; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
; RUN: llc < %s -mtriple=thumbv8.1-m-none-eabi -mattr=+fullfp16 -fp-contract=fast | FileCheck %s
; RUN: llc < %s -mtriple=thumbv8.1-m-none-eabi -mattr=+fullfp16,+slowfpvfmx -fp-contract=fast | FileCheck %s -check-prefix=DONT-FUSE
; Check generated fp16 fused MAC and MLS.
define arm_aapcs_vfpcc void @fusedMACTest2(ptr %a1, ptr %a2, ptr %a3) {
; CHECK-LABEL: fusedMACTest2:
; CHECK: @ %bb.0:
; CHECK-NEXT: vldr.16 s0, [r1]
; CHECK-NEXT: vldr.16 s2, [r0]
; CHECK-NEXT: vldr.16 s4, [r2]
; CHECK-NEXT: vfma.f16 s4, s2, s0
; CHECK-NEXT: vstr.16 s4, [r0]
; CHECK-NEXT: bx lr
;
; DONT-FUSE-LABEL: fusedMACTest2:
; DONT-FUSE: @ %bb.0:
; DONT-FUSE-NEXT: vldr.16 s0, [r1]
; DONT-FUSE-NEXT: vldr.16 s2, [r0]
; DONT-FUSE-NEXT: vmul.f16 s0, s2, s0
; DONT-FUSE-NEXT: vldr.16 s2, [r2]
; DONT-FUSE-NEXT: vadd.f16 s0, s0, s2
; DONT-FUSE-NEXT: vstr.16 s0, [r0]
; DONT-FUSE-NEXT: bx lr
%f1 = load half, ptr %a1, align 2
%f2 = load half, ptr %a2, align 2
%f3 = load half, ptr %a3, align 2
%1 = fmul half %f1, %f2
%2 = fadd half %1, %f3
store half %2, ptr %a1, align 2
ret void
}
define arm_aapcs_vfpcc void @fusedMACTest4(ptr %a1, ptr %a2, ptr %a3) {
; CHECK-LABEL: fusedMACTest4:
; CHECK: @ %bb.0:
; CHECK-NEXT: vldr.16 s0, [r2]
; CHECK-NEXT: vldr.16 s2, [r1]
; CHECK-NEXT: vldr.16 s4, [r0]
; CHECK-NEXT: vfms.f16 s4, s2, s0
; CHECK-NEXT: vstr.16 s4, [r0]
; CHECK-NEXT: bx lr
;
; DONT-FUSE-LABEL: fusedMACTest4:
; DONT-FUSE: @ %bb.0:
; DONT-FUSE-NEXT: vldr.16 s0, [r2]
; DONT-FUSE-NEXT: vldr.16 s2, [r1]
; DONT-FUSE-NEXT: vmul.f16 s0, s2, s0
; DONT-FUSE-NEXT: vldr.16 s2, [r0]
; DONT-FUSE-NEXT: vsub.f16 s0, s2, s0
; DONT-FUSE-NEXT: vstr.16 s0, [r0]
; DONT-FUSE-NEXT: bx lr
%f1 = load half, ptr %a1, align 2
%f2 = load half, ptr %a2, align 2
%f3 = load half, ptr %a3, align 2
%1 = fmul half %f2, %f3
%2 = fsub half %f1, %1
store half %2, ptr %a1, align 2
ret void
}
define arm_aapcs_vfpcc void @fusedMACTest6(ptr %a1, ptr %a2, ptr %a3) {
; CHECK-LABEL: fusedMACTest6:
; CHECK: @ %bb.0:
; CHECK-NEXT: vldr.16 s0, [r1]
; CHECK-NEXT: vldr.16 s2, [r0]
; CHECK-NEXT: vldr.16 s4, [r2]
; CHECK-NEXT: vfnma.f16 s4, s2, s0
; CHECK-NEXT: vstr.16 s4, [r0]
; CHECK-NEXT: bx lr
;
; DONT-FUSE-LABEL: fusedMACTest6:
; DONT-FUSE: @ %bb.0:
; DONT-FUSE-NEXT: vldr.16 s0, [r1]
; DONT-FUSE-NEXT: vldr.16 s2, [r0]
; DONT-FUSE-NEXT: vnmul.f16 s0, s2, s0
; DONT-FUSE-NEXT: vldr.16 s2, [r2]
; DONT-FUSE-NEXT: vsub.f16 s0, s0, s2
; DONT-FUSE-NEXT: vstr.16 s0, [r0]
; DONT-FUSE-NEXT: bx lr
%f1 = load half, ptr %a1, align 2
%f2 = load half, ptr %a2, align 2
%f3 = load half, ptr %a3, align 2
%1 = fmul half %f1, %f2
%2 = fsub half -0.0, %1
%3 = fsub half %2, %f3
store half %3, ptr %a1, align 2
ret void
}
define arm_aapcs_vfpcc void @fusedMACTest8(ptr %a1, ptr %a2, ptr %a3) {
; CHECK-LABEL: fusedMACTest8:
; CHECK: @ %bb.0:
; CHECK-NEXT: vldr.16 s0, [r1]
; CHECK-NEXT: vldr.16 s2, [r0]
; CHECK-NEXT: vldr.16 s4, [r2]
; CHECK-NEXT: vfnms.f16 s4, s2, s0
; CHECK-NEXT: vstr.16 s4, [r0]
; CHECK-NEXT: bx lr
;
; DONT-FUSE-LABEL: fusedMACTest8:
; DONT-FUSE: @ %bb.0:
; DONT-FUSE-NEXT: vldr.16 s0, [r1]
; DONT-FUSE-NEXT: vldr.16 s2, [r0]
; DONT-FUSE-NEXT: vmul.f16 s0, s2, s0
; DONT-FUSE-NEXT: vldr.16 s2, [r2]
; DONT-FUSE-NEXT: vsub.f16 s0, s0, s2
; DONT-FUSE-NEXT: vstr.16 s0, [r0]
; DONT-FUSE-NEXT: bx lr
%f1 = load half, ptr %a1, align 2
%f2 = load half, ptr %a2, align 2
%f3 = load half, ptr %a3, align 2
%1 = fmul half %f1, %f2
%2 = fsub half %1, %f3
store half %2, ptr %a1, align 2
ret void
}
define arm_aapcs_vfpcc void @test_fma_f16(ptr %aa, ptr %bb, ptr %cc) nounwind readnone ssp {
; CHECK-LABEL: test_fma_f16:
; CHECK: @ %bb.0: @ %entry
; CHECK-NEXT: vldr.16 s0, [r1]
; CHECK-NEXT: vldr.16 s2, [r0]
; CHECK-NEXT: vldr.16 s4, [r2]
; CHECK-NEXT: vfma.f16 s4, s2, s0
; CHECK-NEXT: vstr.16 s4, [r0]
; CHECK-NEXT: bx lr
;
; DONT-FUSE-LABEL: test_fma_f16:
; DONT-FUSE: @ %bb.0: @ %entry
; DONT-FUSE-NEXT: vldr.16 s0, [r1]
; DONT-FUSE-NEXT: vldr.16 s2, [r0]
; DONT-FUSE-NEXT: vldr.16 s4, [r2]
; DONT-FUSE-NEXT: vfma.f16 s4, s2, s0
; DONT-FUSE-NEXT: vstr.16 s4, [r0]
; DONT-FUSE-NEXT: bx lr
entry:
%a = load half, ptr %aa, align 2
%b = load half, ptr %bb, align 2
%c = load half, ptr %cc, align 2
%tmp1 = tail call half @llvm.fma.f16(half %a, half %b, half %c) nounwind readnone
store half %tmp1, ptr %aa, align 2
ret void
}
define arm_aapcs_vfpcc void @test_fnms_f16(ptr %aa, ptr %bb, ptr %cc) nounwind readnone ssp {
; CHECK-LABEL: test_fnms_f16:
; CHECK: @ %bb.0:
; CHECK-NEXT: vldr.16 s0, [r1]
; CHECK-NEXT: vldr.16 s2, [r0]
; CHECK-NEXT: vldr.16 s4, [r2]
; CHECK-NEXT: vfma.f16 s4, s2, s0
; CHECK-NEXT: vstr.16 s4, [r0]
; CHECK-NEXT: bx lr
;
; DONT-FUSE-LABEL: test_fnms_f16:
; DONT-FUSE: @ %bb.0:
; DONT-FUSE-NEXT: vldr.16 s0, [r1]
; DONT-FUSE-NEXT: vldr.16 s2, [r0]
; DONT-FUSE-NEXT: vldr.16 s4, [r2]
; DONT-FUSE-NEXT: vfma.f16 s4, s2, s0
; DONT-FUSE-NEXT: vstr.16 s4, [r0]
; DONT-FUSE-NEXT: bx lr
%a = load half, ptr %aa, align 2
%b = load half, ptr %bb, align 2
%c = load half, ptr %cc, align 2
%tmp2 = fsub half -0.0, %c
%tmp3 = tail call half @llvm.fma.f16(half %a, half %b, half %c) nounwind readnone
store half %tmp3, ptr %aa, align 2
ret void
}
define arm_aapcs_vfpcc void @test_fma_const_fold(ptr %aa, ptr %bb) nounwind {
; CHECK-LABEL: test_fma_const_fold:
; CHECK: @ %bb.0:
; CHECK-NEXT: vldr.16 s0, [r1]
; CHECK-NEXT: vldr.16 s2, [r0]
; CHECK-NEXT: vadd.f16 s0, s2, s0
; CHECK-NEXT: vstr.16 s0, [r0]
; CHECK-NEXT: bx lr
;
; DONT-FUSE-LABEL: test_fma_const_fold:
; DONT-FUSE: @ %bb.0:
; DONT-FUSE-NEXT: vldr.16 s0, [r1]
; DONT-FUSE-NEXT: vldr.16 s2, [r0]
; DONT-FUSE-NEXT: vadd.f16 s0, s2, s0
; DONT-FUSE-NEXT: vstr.16 s0, [r0]
; DONT-FUSE-NEXT: bx lr
%a = load half, ptr %aa, align 2
%b = load half, ptr %bb, align 2
%ret = call half @llvm.fma.f16(half %a, half 1.0, half %b)
store half %ret, ptr %aa, align 2
ret void
}
define arm_aapcs_vfpcc void @test_fma_canonicalize(ptr %aa, ptr %bb) nounwind {
; CHECK-LABEL: test_fma_canonicalize:
; CHECK: @ %bb.0:
; CHECK-NEXT: vldr.16 s0, [r0]
; CHECK-NEXT: vldr.16 s2, [r1]
; CHECK-NEXT: vmov.f16 s4, #2.000000e+00
; CHECK-NEXT: vfma.f16 s2, s0, s4
; CHECK-NEXT: vstr.16 s2, [r0]
; CHECK-NEXT: bx lr
;
; DONT-FUSE-LABEL: test_fma_canonicalize:
; DONT-FUSE: @ %bb.0:
; DONT-FUSE-NEXT: vldr.16 s0, [r0]
; DONT-FUSE-NEXT: vldr.16 s2, [r1]
; DONT-FUSE-NEXT: vmov.f16 s4, #2.000000e+00
; DONT-FUSE-NEXT: vfma.f16 s2, s0, s4
; DONT-FUSE-NEXT: vstr.16 s2, [r0]
; DONT-FUSE-NEXT: bx lr
%a = load half, ptr %aa, align 2
%b = load half, ptr %bb, align 2
%ret = call half @llvm.fma.f16(half 2.0, half %a, half %b)
store half %ret, ptr %aa, align 2
ret void
}
define arm_aapcs_vfpcc void @fms1(ptr %a1, ptr %a2, ptr %a3) {
; CHECK-LABEL: fms1:
; CHECK: @ %bb.0:
; CHECK-NEXT: vldr.16 s0, [r1]
; CHECK-NEXT: vldr.16 s2, [r0]
; CHECK-NEXT: vldr.16 s4, [r2]
; CHECK-NEXT: vfms.f16 s4, s2, s0
; CHECK-NEXT: vstr.16 s4, [r0]
; CHECK-NEXT: bx lr
;
; DONT-FUSE-LABEL: fms1:
; DONT-FUSE: @ %bb.0:
; DONT-FUSE-NEXT: vldr.16 s0, [r1]
; DONT-FUSE-NEXT: vldr.16 s2, [r0]
; DONT-FUSE-NEXT: vldr.16 s4, [r2]
; DONT-FUSE-NEXT: vfms.f16 s4, s2, s0
; DONT-FUSE-NEXT: vstr.16 s4, [r0]
; DONT-FUSE-NEXT: bx lr
%f1 = load half, ptr %a1, align 2
%f2 = load half, ptr %a2, align 2
%f3 = load half, ptr %a3, align 2
%s = fsub half -0.0, %f1
%ret = call half @llvm.fma.f16(half %s, half %f2, half %f3)
store half %ret, ptr %a1, align 2
ret void
}
define arm_aapcs_vfpcc void @fms2(ptr %a1, ptr %a2, ptr %a3) {
; CHECK-LABEL: fms2:
; CHECK: @ %bb.0:
; CHECK-NEXT: vldr.16 s0, [r1]
; CHECK-NEXT: vldr.16 s2, [r0]
; CHECK-NEXT: vldr.16 s4, [r2]
; CHECK-NEXT: vfms.f16 s4, s2, s0
; CHECK-NEXT: vstr.16 s4, [r0]
; CHECK-NEXT: bx lr
;
; DONT-FUSE-LABEL: fms2:
; DONT-FUSE: @ %bb.0:
; DONT-FUSE-NEXT: vldr.16 s0, [r1]
; DONT-FUSE-NEXT: vldr.16 s2, [r0]
; DONT-FUSE-NEXT: vldr.16 s4, [r2]
; DONT-FUSE-NEXT: vfms.f16 s4, s2, s0
; DONT-FUSE-NEXT: vstr.16 s4, [r0]
; DONT-FUSE-NEXT: bx lr
%f1 = load half, ptr %a1, align 2
%f2 = load half, ptr %a2, align 2
%f3 = load half, ptr %a3, align 2
%s = fsub half -0.0, %f1
%ret = call half @llvm.fma.f16(half %f2, half %s, half %f3)
store half %ret, ptr %a1, align 2
ret void
}
define arm_aapcs_vfpcc void @fnma1(ptr %a1, ptr %a2, ptr %a3) {
; CHECK-LABEL: fnma1:
; CHECK: @ %bb.0:
; CHECK-NEXT: vldr.16 s0, [r1]
; CHECK-NEXT: vldr.16 s2, [r0]
; CHECK-NEXT: vldr.16 s4, [r2]
; CHECK-NEXT: vfnma.f16 s4, s2, s0
; CHECK-NEXT: vstr.16 s4, [r0]
; CHECK-NEXT: bx lr
;
; DONT-FUSE-LABEL: fnma1:
; DONT-FUSE: @ %bb.0:
; DONT-FUSE-NEXT: vldr.16 s0, [r1]
; DONT-FUSE-NEXT: vldr.16 s2, [r0]
; DONT-FUSE-NEXT: vldr.16 s4, [r2]
; DONT-FUSE-NEXT: vfnma.f16 s4, s2, s0
; DONT-FUSE-NEXT: vstr.16 s4, [r0]
; DONT-FUSE-NEXT: bx lr
%f1 = load half, ptr %a1, align 2
%f2 = load half, ptr %a2, align 2
%f3 = load half, ptr %a3, align 2
%fma = call half @llvm.fma.f16(half %f1, half %f2, half %f3)
%n1 = fsub half -0.0, %fma
store half %n1, ptr %a1, align 2
ret void
}
define arm_aapcs_vfpcc void @fnma2(ptr %a1, ptr %a2, ptr %a3) {
; CHECK-LABEL: fnma2:
; CHECK: @ %bb.0:
; CHECK-NEXT: vldr.16 s0, [r1]
; CHECK-NEXT: vldr.16 s2, [r0]
; CHECK-NEXT: vldr.16 s4, [r2]
; CHECK-NEXT: vfnma.f16 s4, s2, s0
; CHECK-NEXT: vstr.16 s4, [r0]
; CHECK-NEXT: bx lr
;
; DONT-FUSE-LABEL: fnma2:
; DONT-FUSE: @ %bb.0:
; DONT-FUSE-NEXT: vldr.16 s0, [r1]
; DONT-FUSE-NEXT: vldr.16 s2, [r0]
; DONT-FUSE-NEXT: vldr.16 s4, [r2]
; DONT-FUSE-NEXT: vfnma.f16 s4, s2, s0
; DONT-FUSE-NEXT: vstr.16 s4, [r0]
; DONT-FUSE-NEXT: bx lr
%f1 = load half, ptr %a1, align 2
%f2 = load half, ptr %a2, align 2
%f3 = load half, ptr %a3, align 2
%n1 = fsub half -0.0, %f1
%n3 = fsub half -0.0, %f3
%ret = call half @llvm.fma.f16(half %n1, half %f2, half %n3)
store half %ret, ptr %a1, align 2
ret void
}
define arm_aapcs_vfpcc void @fnms1(ptr %a1, ptr %a2, ptr %a3) {
; CHECK-LABEL: fnms1:
; CHECK: @ %bb.0:
; CHECK-NEXT: vldr.16 s0, [r1]
; CHECK-NEXT: vldr.16 s2, [r0]
; CHECK-NEXT: vldr.16 s4, [r2]
; CHECK-NEXT: vfnms.f16 s4, s2, s0
; CHECK-NEXT: vstr.16 s4, [r0]
; CHECK-NEXT: bx lr
;
; DONT-FUSE-LABEL: fnms1:
; DONT-FUSE: @ %bb.0:
; DONT-FUSE-NEXT: vldr.16 s0, [r1]
; DONT-FUSE-NEXT: vldr.16 s2, [r0]
; DONT-FUSE-NEXT: vldr.16 s4, [r2]
; DONT-FUSE-NEXT: vfnms.f16 s4, s2, s0
; DONT-FUSE-NEXT: vstr.16 s4, [r0]
; DONT-FUSE-NEXT: bx lr
%f1 = load half, ptr %a1, align 2
%f2 = load half, ptr %a2, align 2
%f3 = load half, ptr %a3, align 2
%n3 = fsub half -0.0, %f3
%ret = call half @llvm.fma.f16(half %f1, half %f2, half %n3)
store half %ret, ptr %a1, align 2
ret void
}
define arm_aapcs_vfpcc void @fnms2(ptr %a1, ptr %a2, ptr %a3) {
; CHECK-LABEL: fnms2:
; CHECK: @ %bb.0:
; CHECK-NEXT: vldr.16 s0, [r1]
; CHECK-NEXT: vldr.16 s2, [r0]
; CHECK-NEXT: vldr.16 s4, [r2]
; CHECK-NEXT: vfnms.f16 s4, s2, s0
; CHECK-NEXT: vstr.16 s4, [r0]
; CHECK-NEXT: bx lr
;
; DONT-FUSE-LABEL: fnms2:
; DONT-FUSE: @ %bb.0:
; DONT-FUSE-NEXT: vldr.16 s0, [r1]
; DONT-FUSE-NEXT: vldr.16 s2, [r0]
; DONT-FUSE-NEXT: vldr.16 s4, [r2]
; DONT-FUSE-NEXT: vfnms.f16 s4, s2, s0
; DONT-FUSE-NEXT: vstr.16 s4, [r0]
; DONT-FUSE-NEXT: bx lr
%f1 = load half, ptr %a1, align 2
%f2 = load half, ptr %a2, align 2
%f3 = load half, ptr %a3, align 2
%n1 = fsub half -0.0, %f1
%fma = call half @llvm.fma.f16(half %n1, half %f2, half %f3)
%n = fsub half -0.0, %fma
store half %n, ptr %a1, align 2
ret void
}
define arm_aapcs_vfpcc void @fnms3(ptr %a1, ptr %a2, ptr %a3) {
; CHECK-LABEL: fnms3:
; CHECK: @ %bb.0:
; CHECK-NEXT: vldr.16 s0, [r0]
; CHECK-NEXT: vldr.16 s2, [r1]
; CHECK-NEXT: vldr.16 s4, [r2]
; CHECK-NEXT: vfnms.f16 s4, s2, s0
; CHECK-NEXT: vstr.16 s4, [r0]
; CHECK-NEXT: bx lr
;
; DONT-FUSE-LABEL: fnms3:
; DONT-FUSE: @ %bb.0:
; DONT-FUSE-NEXT: vldr.16 s0, [r0]
; DONT-FUSE-NEXT: vldr.16 s2, [r1]
; DONT-FUSE-NEXT: vldr.16 s4, [r2]
; DONT-FUSE-NEXT: vfnms.f16 s4, s2, s0
; DONT-FUSE-NEXT: vstr.16 s4, [r0]
; DONT-FUSE-NEXT: bx lr
%f1 = load half, ptr %a1, align 2
%f2 = load half, ptr %a2, align 2
%f3 = load half, ptr %a3, align 2
%n2 = fsub half -0.0, %f2
%fma = call half @llvm.fma.f16(half %f1, half %n2, half %f3)
%n1 = fsub half -0.0, %fma
store half %n1, ptr %a1, align 2
ret void
}
declare half @llvm.fma.f16(half, half, half) nounwind readnone