| ; SOFT: | |
| ; RUN: llc < %s -mtriple=arm-none-eabi -float-abi=soft | FileCheck %s --check-prefixes=CHECK,CHECK-SOFT | |
| ; RUN: llc < %s -mtriple=thumb-none-eabi -float-abi=soft | FileCheck %s --check-prefixes=CHECK,CHECK-SOFT | |
| ; SOFTFP: | |
| ; RUN: llc < %s -mtriple=arm-none-eabi -mattr=+vfp3 | FileCheck %s --check-prefixes=CHECK,CHECK-SOFTFP-VFP3 | |
| ; RUN: llc < %s -mtriple=arm-none-eabi -mattr=+vfp4 | FileCheck %s --check-prefixes=CHECK,CHECK-SOFTFP-FP16,CHECK-SOFTFP-FP16-A32 | |
| ; RUN: llc < %s -mtriple=arm-none-eabi -mattr=+fullfp16 | FileCheck %s --check-prefixes=CHECK,CHECK-SOFTFP-FULLFP16 | |
| ; RUN: llc < %s -mtriple=thumbv7-none-eabi -mattr=+vfp3 | FileCheck %s --check-prefixes=CHECK,CHECK-SOFTFP-VFP3 | |
| ; RUN: llc < %s -mtriple=thumbv7-none-eabi -mattr=+vfp4 | FileCheck %s --check-prefixes=CHECK,CHECK-SOFTFP-FP16,CHECK-SOFTFP-FP16-T32 | |
| ; RUN: llc < %s -mtriple=thumbv7-none-eabi -mattr=+fullfp16 | FileCheck %s --check-prefixes=CHECK,CHECK-SOFTFP-FULLFP16 | |
| ; Test fast-isel | |
| ; RUN: llc < %s -mtriple=arm-none-eabi -mattr=+fullfp16 -O0 | FileCheck %s --check-prefixes=CHECK-SPILL-RELOAD | |
| ; RUN: llc < %s -mtriple=thumbv7-none-eabi -mattr=+fullfp16 -O0 | FileCheck %s --check-prefixes=CHECK-SPILL-RELOAD | |
| ; HARD: | |
| ; RUN: llc < %s -mtriple=arm-none-eabihf -mattr=+vfp3 | FileCheck %s --check-prefixes=CHECK,CHECK-HARDFP-VFP3 | |
| ; RUN: llc < %s -mtriple=arm-none-eabihf -mattr=+vfp4 | FileCheck %s --check-prefixes=CHECK,CHECK-HARDFP-FP16 | |
| ; RUN: llc < %s -mtriple=arm-none-eabihf -mattr=+fullfp16 | FileCheck %s --check-prefixes=CHECK,CHECK-HARDFP-FULLFP16 | |
| ; RUN: llc < %s -mtriple=thumbv7-none-eabihf -mattr=+vfp3 | FileCheck %s --check-prefixes=CHECK,CHECK-HARDFP-VFP3 | |
| ; RUN: llc < %s -mtriple=thumbv7-none-eabihf -mattr=+vfp4 | FileCheck %s --check-prefixes=CHECK,CHECK-HARDFP-FP16 | |
| ; RUN: llc < %s -mtriple=thumbv7-none-eabihf -mattr=+fullfp16 | FileCheck %s --check-prefixes=CHECK,CHECK-HARDFP-FULLFP16 | |
| ; FP-CONTRACT=FAST | |
| ; RUN: llc < %s -mtriple=arm-none-eabihf -mattr=+fullfp16 -fp-contract=fast | FileCheck %s --check-prefixes=CHECK,CHECK-HARDFP-FULLFP16-FAST | |
| ; RUN: llc < %s -mtriple=thumbv7-none-eabihf -mattr=+fullfp16 -fp-contract=fast | FileCheck %s --check-prefixes=CHECK,CHECK-HARDFP-FULLFP16-FAST | |
| ; TODO: we can't pass half-precision arguments as "half" types yet. We do | |
| ; that for the time being by passing "float %f.coerce" and the necessary | |
| ; bitconverts/truncates. But when we can pass half types, we do want to use | |
| ; and test that here. | |
| define float @RetValBug(float %A.coerce) { | |
| entry: | |
| ret float undef | |
| ; Check thatLowerReturn can handle undef nodes (i.e. nodes which do not have | |
| ; any operands) when FullFP16 is enabled. | |
| ; | |
| ; CHECK-LABEL: RetValBug: | |
| ; CHECK-HARDFP-FULLFP16: {{.*}} lr | |
| } | |
| ; 1. VABS: TODO | |
| ; 2. VADD | |
| define float @Add(float %a.coerce, float %b.coerce) { | |
| entry: | |
| %0 = bitcast float %a.coerce to i32 | |
| %tmp.0.extract.trunc = trunc i32 %0 to i16 | |
| %1 = bitcast i16 %tmp.0.extract.trunc to half | |
| %2 = bitcast float %b.coerce to i32 | |
| %tmp1.0.extract.trunc = trunc i32 %2 to i16 | |
| %3 = bitcast i16 %tmp1.0.extract.trunc to half | |
| %add = fadd half %1, %3 | |
| %4 = bitcast half %add to i16 | |
| %tmp4.0.insert.ext = zext i16 %4 to i32 | |
| %5 = bitcast i32 %tmp4.0.insert.ext to float | |
| ret float %5 | |
| ; CHECK-LABEL: Add: | |
| ; CHECK-SOFT: bl __aeabi_h2f | |
| ; CHECK-SOFT: bl __aeabi_h2f | |
| ; CHECK-SOFT: bl __aeabi_fadd | |
| ; CHECK-SOFT: bl __aeabi_f2h | |
| ; CHECK-SOFTFP-VFP3: bl __aeabi_h2f | |
| ; CHECK-SOFTFP-VFP3: bl __aeabi_h2f | |
| ; CHECK-SOFTFP-VFP3: vadd.f32 | |
| ; CHECK-SOFTFP-VFP3: bl __aeabi_f2h | |
| ; CHECK-SOFTFP-FP16: vmov [[S2:s[0-9]]], r1 | |
| ; CHECK-SOFTFP-FP16: vmov [[S0:s[0-9]]], r0 | |
| ; CHECK-SOFTFP-FP16: vcvtb.f32.f16 [[S2]], [[S2]] | |
| ; CHECK-SOFTFP-FP16: vcvtb.f32.f16 [[S0]], [[S0]] | |
| ; CHECK-SOFTFP-FP16: vadd.f32 [[S0]], [[S0]], [[S2]] | |
| ; CHECK-SOFTFP-FP16: vcvtb.f16.f32 [[S0]], [[S0]] | |
| ; CHECK-SOFTFP-FP16: vmov r0, s0 | |
| ; CHECK-SOFTFP-FULLFP16: vmov.f16 [[S0:s[0-9]]], r1 | |
| ; CHECK-SOFTFP-FULLFP16: vmov.f16 [[S2:s[0-9]]], r0 | |
| ; CHECK-SOFTFP-FULLFP16: vadd.f16 [[S0]], [[S2]], [[S0]] | |
| ; CHECK-SOFTFP-FULLFP16-NEXT: vmov.f16 r0, s0 | |
| ; CHECK-HARDFP-VFP3: vmov r{{.}}, s0 | |
| ; CHECK-HARDFP-VFP3: vmov{{.*}}, s1 | |
| ; CHECK-HARDFP-VFP3: bl __aeabi_h2f | |
| ; CHECK-HARDFP-VFP3: bl __aeabi_h2f | |
| ; CHECK-HARDFP-VFP3: vadd.f32 | |
| ; CHECK-HARDFP-VFP3: bl __aeabi_f2h | |
| ; CHECK-HARDFP-VFP3: vmov s0, r0 | |
| ; CHECK-HARDFP-FP16: vcvtb.f32.f16 [[S2:s[0-9]]], s1 | |
| ; CHECK-HARDFP-FP16: vcvtb.f32.f16 [[S0:s[0-9]]], s0 | |
| ; CHECK-HARDFP-FP16: vadd.f32 [[S0]], [[S0]], [[S2]] | |
| ; CHECK-HARDFP-FP16: vcvtb.f16.f32 [[S0]], [[S0]] | |
| ; CHECK-HARDFP-FULLFP16: vadd.f16 s0, s0, s1 | |
| } | |
| ; 3. VCMP | |
| define zeroext i1 @VCMP1(float %F.coerce, float %G.coerce) { | |
| entry: | |
| %0 = bitcast float %F.coerce to i32 | |
| %tmp.0.extract.trunc = trunc i32 %0 to i16 | |
| %1 = bitcast i16 %tmp.0.extract.trunc to half | |
| %2 = bitcast float %G.coerce to i32 | |
| %tmp1.0.extract.trunc = trunc i32 %2 to i16 | |
| %3 = bitcast i16 %tmp1.0.extract.trunc to half | |
| %cmp = fcmp une half %1, %3 | |
| ret i1 %cmp | |
| ; CHECK-LABEL: VCMP1: | |
| ; CHECK-SOFT: bl __aeabi_fcmpeq | |
| ; CHECK-SOFTFP-VFP3: bl __aeabi_h2f | |
| ; CHECK-SOFTFP-VFP3: bl __aeabi_h2f | |
| ; CHECK-SOFTFP-VFP3: vcmp.f32 s{{.}}, s{{.}} | |
| ; CHECK-SOFTFP-FP16: vcvtb.f32.f16 s{{.}}, s{{.}} | |
| ; CHECK-SOFTFP-FP16: vcvtb.f32.f16 s{{.}}, s{{.}} | |
| ; CHECK-SOFTFP-FP16: vcmp.f32 s{{.}}, s{{.}} | |
| ; CHECK-SOFTFP-FULLFP16: vmov.f16 [[S2:s[0-9]]], r0 | |
| ; CHECK-SOFTFP-FULLFP16: vmov.f16 [[S0:s[0-9]]], r1 | |
| ; CHECK-SOFTFP-FULLFP16: vcmp.f16 [[S2]], [[S0]] | |
| ; CHECK-HARDFP-FULLFP16-NOT: vmov.f16 s{{.}}, r0 | |
| ; CHECK-HARDFP-FULLFP16-NOT: vmov.f16 s{{.}}, r1 | |
| ; CHECK-HARDFP-FULLFP16: vcmp.f16 s0, s1 | |
| } | |
| ; Check VCMPZH | |
| define zeroext i1 @VCMP2(float %F.coerce) { | |
| entry: | |
| %0 = bitcast float %F.coerce to i32 | |
| %tmp.0.extract.trunc = trunc i32 %0 to i16 | |
| %1 = bitcast i16 %tmp.0.extract.trunc to half | |
| %cmp = fcmp une half %1, 0.000000e+00 | |
| ret i1 %cmp | |
| ; CHECK-LABEL: VCMP2: | |
| ; CHECK-SOFT: bl __aeabi_fcmpeq | |
| ; CHECK-SOFTFP-FP16: vcmp.f32 s0, #0 | |
| ; CHECK-SOFTFP-FULLFP16: vcmp.f16 s0, #0 | |
| ; CHECK-HARDFP-FULLFP16: vcmp.f16 s0, #0 | |
| } | |
| ; 4. VCMPE | |
| define i32 @VCMPE1(float %F.coerce) { | |
| entry: | |
| %0 = bitcast float %F.coerce to i32 | |
| %tmp.0.extract.trunc = trunc i32 %0 to i16 | |
| %1 = bitcast i16 %tmp.0.extract.trunc to half | |
| %tmp = fcmp olt half %1, 0.000000e+00 | |
| %tmp1 = zext i1 %tmp to i32 | |
| ret i32 %tmp1 | |
| ; CHECK-LABEL: VCMPE1: | |
| ; CHECK-SOFT: bl __aeabi_fcmplt | |
| ; CHECK-SOFTFP-FP16: vcmpe.f32 s0, #0 | |
| ; CHECK-SOFTFP-FULLFP16: vcmpe.f16 s0, #0 | |
| ; CHECK-HARDFP-FULLFP16: vcmpe.f16 s0, #0 | |
| } | |
| define i32 @VCMPE2(float %F.coerce, float %G.coerce) { | |
| entry: | |
| %0 = bitcast float %F.coerce to i32 | |
| %tmp.0.extract.trunc = trunc i32 %0 to i16 | |
| %1 = bitcast i16 %tmp.0.extract.trunc to half | |
| %2 = bitcast float %G.coerce to i32 | |
| %tmp.1.extract.trunc = trunc i32 %2 to i16 | |
| %3 = bitcast i16 %tmp.1.extract.trunc to half | |
| %tmp = fcmp olt half %1, %3 | |
| %tmp1 = zext i1 %tmp to i32 | |
| ret i32 %tmp1 | |
| ; CHECK-LABEL: VCMPE2: | |
| ; CHECK-SOFT: bl __aeabi_fcmplt | |
| ; CHECK-SOFTFP-FP16: vcmpe.f32 s{{.}}, s{{.}} | |
| ; CHECK-SOFTFP-FULLFP16: vcmpe.f16 s{{.}}, s{{.}} | |
| ; CHECK-HARDFP-FULLFP16: vcmpe.f16 s{{.}}, s{{.}} | |
| } | |
| ; Test lowering of BR_CC | |
| define hidden i32 @VCMPBRCC() { | |
| entry: | |
| %f = alloca half, align 2 | |
| br label %for.cond | |
| for.cond: | |
| %0 = load half, half* %f, align 2 | |
| %cmp = fcmp nnan ninf nsz ole half %0, 0xH6800 | |
| br i1 %cmp, label %for.body, label %for.end | |
| for.body: | |
| ret i32 1 | |
| for.end: | |
| ret i32 0 | |
| ; CHECK-LABEL: VCMPBRCC: | |
| ; CHECK-SOFT: bl __aeabi_fcmpgt | |
| ; CHECK-SOFT: cmp r0, #0 | |
| ; CHECK-SOFTFP-FP16: vcvtb.f32.f16 [[S2:s[0-9]]], [[S2]] | |
| ; CHECK-SOFTFP-FP16: vcmpe.f32 [[S2]], s0 | |
| ; CHECK-SOFTFP-FP16: vmrs APSR_nzcv, fpscr | |
| ; CHECK-SOFTFP-FULLFP16: vcmpe.f16 s{{.}}, s{{.}} | |
| ; CHECK-SOFTFP-FULLFP16: vmrs APSR_nzcv, fpscr | |
| } | |
| ; 5. VCVT (between floating-point and fixed-point) | |
| ; Only assembly/disassembly support | |
| ; 6. VCVT (between floating-point and integer, both directions) | |
| define i32 @fptosi(i32 %A.coerce) { | |
| entry: | |
| %tmp.0.extract.trunc = trunc i32 %A.coerce to i16 | |
| %0 = bitcast i16 %tmp.0.extract.trunc to half | |
| %conv = fptosi half %0 to i32 | |
| ret i32 %conv | |
| ; CHECK-LABEL: fptosi: | |
| ; CHECK-HARDFP-FULLFP16: vmov.f16 s0, r0 | |
| ; CHECK-HARDFP-FULLFP16-NEXT: vcvt.s32.f16 s0, s0 | |
| ; CHECK-HARDFP-FULLFP16-NEXT: vmov r0, s0 | |
| } | |
| define i32 @fptoui(i32 %A.coerce) { | |
| entry: | |
| %tmp.0.extract.trunc = trunc i32 %A.coerce to i16 | |
| %0 = bitcast i16 %tmp.0.extract.trunc to half | |
| %conv = fptoui half %0 to i32 | |
| ret i32 %conv | |
| ; CHECK-HARDFP-FULLFP16: vcvt.u32.f16 s0, s0 | |
| ; CHECK-HARDFP-FULLFP16-NEXT: vmov r0, s0 | |
| } | |
| define float @UintToH(i32 %a, i32 %b) { | |
| entry: | |
| %0 = uitofp i32 %a to half | |
| %1 = bitcast half %0 to i16 | |
| %tmp0.insert.ext = zext i16 %1 to i32 | |
| %2 = bitcast i32 %tmp0.insert.ext to float | |
| ret float %2 | |
| ; CHECK-LABEL: UintToH: | |
| ; CHECK-HARDFP-FULLFP16: vmov s0, r0 | |
| ; CHECK-HARDFP-FULLFP16-NEXT: vcvt.f16.u32 s0, s0 | |
| } | |
| define float @SintToH(i32 %a, i32 %b) { | |
| entry: | |
| %0 = sitofp i32 %a to half | |
| %1 = bitcast half %0 to i16 | |
| %tmp0.insert.ext = zext i16 %1 to i32 | |
| %2 = bitcast i32 %tmp0.insert.ext to float | |
| ret float %2 | |
| ; CHECK-LABEL: SintToH: | |
| ; CHECK-HARDFP-FULLFP16: vmov s0, r0 | |
| ; CHECK-HARDFP-FULLFP16-NEXT: vcvt.f16.s32 s0, s0 | |
| } | |
| define i32 @f2h(float %f) { | |
| entry: | |
| %conv = fptrunc float %f to half | |
| %0 = bitcast half %conv to i16 | |
| %tmp.0.insert.ext = zext i16 %0 to i32 | |
| ret i32 %tmp.0.insert.ext | |
| ; CHECK-LABEL: f2h: | |
| ; CHECK-HARDFP-FULLFP16: vcvtb.f16.f32 s0, s0 | |
| } | |
| define float @h2f(i32 %h.coerce) { | |
| entry: | |
| %tmp.0.extract.trunc = trunc i32 %h.coerce to i16 | |
| %0 = bitcast i16 %tmp.0.extract.trunc to half | |
| %conv = fpext half %0 to float | |
| ret float %conv | |
| ; CHECK-LABEL: h2f: | |
| ; CHECK-HARDFP-FULLFP16: vcvtb.f32.f16 s0, s0 | |
| } | |
| define double @h2d(i32 %h.coerce) { | |
| entry: | |
| %tmp.0.extract.trunc = trunc i32 %h.coerce to i16 | |
| %0 = bitcast i16 %tmp.0.extract.trunc to half | |
| %conv = fpext half %0 to double | |
| ret double %conv | |
| ; CHECK-LABEL: h2d: | |
| ; CHECK-HARDFP-FULLFP16: vcvtb.f64.f16 d{{.*}}, s{{.}} | |
| } | |
| define i32 @d2h(double %d) { | |
| entry: | |
| %conv = fptrunc double %d to half | |
| %0 = bitcast half %conv to i16 | |
| %tmp.0.insert.ext = zext i16 %0 to i32 | |
| ret i32 %tmp.0.insert.ext | |
| ; CHECK-LABEL: d2h: | |
| ; CHECK-HARDFP-FULLFP16: vcvtb.f16.f64 s0, d{{.*}} | |
| } | |
| ; TODO: | |
| ; 7. VCVTA | |
| ; 8. VCVTM | |
| ; 9. VCVTN | |
| ; 10. VCVTP | |
| ; 11. VCVTR | |
| ; 12. VDIV | |
| define float @Div(float %a.coerce, float %b.coerce) { | |
| entry: | |
| %0 = bitcast float %a.coerce to i32 | |
| %tmp.0.extract.trunc = trunc i32 %0 to i16 | |
| %1 = bitcast i16 %tmp.0.extract.trunc to half | |
| %2 = bitcast float %b.coerce to i32 | |
| %tmp1.0.extract.trunc = trunc i32 %2 to i16 | |
| %3 = bitcast i16 %tmp1.0.extract.trunc to half | |
| %add = fdiv half %1, %3 | |
| %4 = bitcast half %add to i16 | |
| %tmp4.0.insert.ext = zext i16 %4 to i32 | |
| %5 = bitcast i32 %tmp4.0.insert.ext to float | |
| ret float %5 | |
| ; CHECK-LABEL: Div: | |
| ; CHECK-SOFT: bl __aeabi_h2f | |
| ; CHECK-SOFT: bl __aeabi_h2f | |
| ; CHECK-SOFT: bl __aeabi_fdiv | |
| ; CHECK-SOFT: bl __aeabi_f2h | |
| ; CHECK-SOFTFP-VFP3: bl __aeabi_h2f | |
| ; CHECK-SOFTFP-VFP3: bl __aeabi_h2f | |
| ; CHECK-SOFTFP-VFP3: vdiv.f32 | |
| ; CHECK-SOFTFP-VFP3: bl __aeabi_f2h | |
| ; CHECK-SOFTFP-FP16: vmov [[S2:s[0-9]]], r1 | |
| ; CHECK-SOFTFP-FP16: vmov [[S0:s[0-9]]], r0 | |
| ; CHECK-SOFTFP-FP16: vcvtb.f32.f16 [[S2]], [[S2]] | |
| ; CHECK-SOFTFP-FP16: vcvtb.f32.f16 [[S0]], [[S0]] | |
| ; CHECK-SOFTFP-FP16: vdiv.f32 [[S0]], [[S0]], [[S2]] | |
| ; CHECK-SOFTFP-FP16: vcvtb.f16.f32 [[S0]], [[S0]] | |
| ; CHECK-SOFTFP-FP16: vmov r0, s0 | |
| ; CHECK-SOFTFP-FULLFP16: vmov.f16 [[S0:s[0-9]]], r1 | |
| ; CHECK-SOFTFP-FULLFP16: vmov.f16 [[S2:s[0-9]]], r0 | |
| ; CHECK-SOFTFP-FULLFP16: vdiv.f16 [[S0]], [[S2]], [[S0]] | |
| ; CHECK-SOFTFP-FULLFP16-NEXT: vmov.f16 r0, s0 | |
| ; CHECK-HARDFP-VFP3: vmov r{{.}}, s0 | |
| ; CHECK-HARDFP-VFP3: vmov{{.*}}, s1 | |
| ; CHECK-HARDFP-VFP3: bl __aeabi_h2f | |
| ; CHECK-HARDFP-VFP3: bl __aeabi_h2f | |
| ; CHECK-HARDFP-VFP3: vdiv.f32 | |
| ; CHECK-HARDFP-VFP3: bl __aeabi_f2h | |
| ; CHECK-HARDFP-VFP3: vmov s0, r0 | |
| ; CHECK-HARDFP-FP16: vcvtb.f32.f16 [[S2:s[0-9]]], s1 | |
| ; CHECK-HARDFP-FP16: vcvtb.f32.f16 [[S0:s[0-9]]], s0 | |
| ; CHECK-HARDFP-FP16: vdiv.f32 [[S0]], [[S0]], [[S2]] | |
| ; CHECK-HARDFP-FP16: vcvtb.f16.f32 [[S0]], [[S0]] | |
| ; CHECK-HARDFP-FULLFP16: vdiv.f16 s0, s0, s1 | |
| } | |
| ; 13. VFMA | |
| define float @VFMA(float %a.coerce, float %b.coerce, float %c.coerce) { | |
| entry: | |
| %0 = bitcast float %a.coerce to i32 | |
| %tmp.0.extract.trunc = trunc i32 %0 to i16 | |
| %1 = bitcast i16 %tmp.0.extract.trunc to half | |
| %2 = bitcast float %b.coerce to i32 | |
| %tmp1.0.extract.trunc = trunc i32 %2 to i16 | |
| %3 = bitcast i16 %tmp1.0.extract.trunc to half | |
| %4 = bitcast float %c.coerce to i32 | |
| %tmp2.0.extract.trunc = trunc i32 %4 to i16 | |
| %5 = bitcast i16 %tmp2.0.extract.trunc to half | |
| %mul = fmul half %1, %3 | |
| %add = fadd half %mul, %5 | |
| %6 = bitcast half %add to i16 | |
| %tmp4.0.insert.ext = zext i16 %6 to i32 | |
| %7 = bitcast i32 %tmp4.0.insert.ext to float | |
| ret float %7 | |
| ; CHECK-LABEL: VFMA: | |
| ; CHECK-HARDFP-FULLFP16-FAST: vfma.f16 s2, s0, s1 | |
| ; CHECK-HARDFP-FULLFP16-FAST-NEXT: vmov.f32 s0, s2 | |
| } | |
| ; 14. VFMS | |
| define float @VFMS(float %a.coerce, float %b.coerce, float %c.coerce) { | |
| entry: | |
| %0 = bitcast float %a.coerce to i32 | |
| %tmp.0.extract.trunc = trunc i32 %0 to i16 | |
| %1 = bitcast i16 %tmp.0.extract.trunc to half | |
| %2 = bitcast float %b.coerce to i32 | |
| %tmp1.0.extract.trunc = trunc i32 %2 to i16 | |
| %3 = bitcast i16 %tmp1.0.extract.trunc to half | |
| %4 = bitcast float %c.coerce to i32 | |
| %tmp2.0.extract.trunc = trunc i32 %4 to i16 | |
| %5 = bitcast i16 %tmp2.0.extract.trunc to half | |
| %mul = fmul half %1, %3 | |
| %sub = fsub half %5, %mul | |
| %6 = bitcast half %sub to i16 | |
| %tmp4.0.insert.ext = zext i16 %6 to i32 | |
| %7 = bitcast i32 %tmp4.0.insert.ext to float | |
| ret float %7 | |
| ; CHECK-LABEL: VFMS: | |
| ; CHECK-HARDFP-FULLFP16-FAST: vfms.f16 s2, s0, s1 | |
| ; CHECK-HARDFP-FULLFP16-FAST-NEXT: vmov.f32 s0, s2 | |
| } | |
| ; 15. VFNMA | |
| define float @VFNMA(float %a.coerce, float %b.coerce, float %c.coerce) { | |
| entry: | |
| %0 = bitcast float %a.coerce to i32 | |
| %tmp.0.extract.trunc = trunc i32 %0 to i16 | |
| %1 = bitcast i16 %tmp.0.extract.trunc to half | |
| %2 = bitcast float %b.coerce to i32 | |
| %tmp1.0.extract.trunc = trunc i32 %2 to i16 | |
| %3 = bitcast i16 %tmp1.0.extract.trunc to half | |
| %4 = bitcast float %c.coerce to i32 | |
| %tmp2.0.extract.trunc = trunc i32 %4 to i16 | |
| %5 = bitcast i16 %tmp2.0.extract.trunc to half | |
| %mul = fmul half %1, %3 | |
| %sub = fsub half -0.0, %mul | |
| %sub2 = fsub half %sub, %5 | |
| %6 = bitcast half %sub2 to i16 | |
| %tmp4.0.insert.ext = zext i16 %6 to i32 | |
| %7 = bitcast i32 %tmp4.0.insert.ext to float | |
| ret float %7 | |
| ; CHECK-LABEL: VFNMA: | |
| ; CHECK-HARDFP-FULLFP16-FAST: vfnma.f16 s2, s0, s1 | |
| ; CHECK-HARDFP-FULLFP16-FAST-NEXT: vmov.f32 s0, s2 | |
| } | |
| ; 16. VFNMS | |
| define float @VFNMS(float %a.coerce, float %b.coerce, float %c.coerce) { | |
| entry: | |
| %0 = bitcast float %a.coerce to i32 | |
| %tmp.0.extract.trunc = trunc i32 %0 to i16 | |
| %1 = bitcast i16 %tmp.0.extract.trunc to half | |
| %2 = bitcast float %b.coerce to i32 | |
| %tmp1.0.extract.trunc = trunc i32 %2 to i16 | |
| %3 = bitcast i16 %tmp1.0.extract.trunc to half | |
| %4 = bitcast float %c.coerce to i32 | |
| %tmp2.0.extract.trunc = trunc i32 %4 to i16 | |
| %5 = bitcast i16 %tmp2.0.extract.trunc to half | |
| %mul = fmul half %1, %3 | |
| %sub2 = fsub half %mul, %5 | |
| %6 = bitcast half %sub2 to i16 | |
| %tmp4.0.insert.ext = zext i16 %6 to i32 | |
| %7 = bitcast i32 %tmp4.0.insert.ext to float | |
| ret float %7 | |
| ; CHECK-LABEL: VFNMS: | |
| ; CHECK-HARDFP-FULLFP16-FAST: vfnms.f16 s2, s0, s1 | |
| ; CHECK-HARDFP-FULLFP16-FAST-NEXT: vmov.f32 s0, s2 | |
| } | |
| ; 17. VMAXNM | |
| ; 18. VMINNM | |
| ; Tested in fp16-vminmaxnm.ll and fp16-vminmaxnm-safe.ll | |
| ; 19. VMLA | |
| define float @VMLA(float %a.coerce, float %b.coerce, float %c.coerce) { | |
| entry: | |
| %0 = bitcast float %a.coerce to i32 | |
| %tmp.0.extract.trunc = trunc i32 %0 to i16 | |
| %1 = bitcast i16 %tmp.0.extract.trunc to half | |
| %2 = bitcast float %b.coerce to i32 | |
| %tmp1.0.extract.trunc = trunc i32 %2 to i16 | |
| %3 = bitcast i16 %tmp1.0.extract.trunc to half | |
| %4 = bitcast float %c.coerce to i32 | |
| %tmp2.0.extract.trunc = trunc i32 %4 to i16 | |
| %5 = bitcast i16 %tmp2.0.extract.trunc to half | |
| %mul = fmul half %1, %3 | |
| %add = fadd half %5, %mul | |
| %6 = bitcast half %add to i16 | |
| %tmp4.0.insert.ext = zext i16 %6 to i32 | |
| %7 = bitcast i32 %tmp4.0.insert.ext to float | |
| ret float %7 | |
| ; CHECK-LABEL: VMLA: | |
| ; CHECK-HARDFP-FULLFP16: vmla.f16 s2, s0, s1 | |
| ; CHECK-HARDFP-FULLFP16-NEXT: vmov.f32 s0, s2 | |
| } | |
| ; 20. VMLS | |
| define float @VMLS(float %a.coerce, float %b.coerce, float %c.coerce) { | |
| entry: | |
| %0 = bitcast float %a.coerce to i32 | |
| %tmp.0.extract.trunc = trunc i32 %0 to i16 | |
| %1 = bitcast i16 %tmp.0.extract.trunc to half | |
| %2 = bitcast float %b.coerce to i32 | |
| %tmp1.0.extract.trunc = trunc i32 %2 to i16 | |
| %3 = bitcast i16 %tmp1.0.extract.trunc to half | |
| %4 = bitcast float %c.coerce to i32 | |
| %tmp2.0.extract.trunc = trunc i32 %4 to i16 | |
| %5 = bitcast i16 %tmp2.0.extract.trunc to half | |
| %mul = fmul half %1, %3 | |
| %add = fsub half %5, %mul | |
| %6 = bitcast half %add to i16 | |
| %tmp4.0.insert.ext = zext i16 %6 to i32 | |
| %7 = bitcast i32 %tmp4.0.insert.ext to float | |
| ret float %7 | |
| ; CHECK-LABEL: VMLS: | |
| ; CHECK-HARDFP-FULLFP16: vmls.f16 s2, s0, s1 | |
| ; CHECK-HARDFP-FULLFP16-NEXT: vmov.f32 s0, s2 | |
| } | |
| ; TODO: fix immediates. | |
| ; 21. VMOV (between general-purpose register and half-precision register) | |
| ; 22. VMOV (immediate) | |
| define i32 @movi(i32 %a.coerce) { | |
| entry: | |
| %tmp.0.extract.trunc = trunc i32 %a.coerce to i16 | |
| %0 = bitcast i16 %tmp.0.extract.trunc to half | |
| %add = fadd half %0, 0xHC000 | |
| %1 = bitcast half %add to i16 | |
| %tmp2.0.insert.ext = zext i16 %1 to i32 | |
| ret i32 %tmp2.0.insert.ext | |
| ; CHECK-LABEL: movi: | |
| ; CHECK-HARDFP-FULLFP16: vmov.f16 s0, #-2.000000e+00 | |
| } | |
| ; 23. VMUL | |
| define float @Mul(float %a.coerce, float %b.coerce) { | |
| entry: | |
| %0 = bitcast float %a.coerce to i32 | |
| %tmp.0.extract.trunc = trunc i32 %0 to i16 | |
| %1 = bitcast i16 %tmp.0.extract.trunc to half | |
| %2 = bitcast float %b.coerce to i32 | |
| %tmp1.0.extract.trunc = trunc i32 %2 to i16 | |
| %3 = bitcast i16 %tmp1.0.extract.trunc to half | |
| %add = fmul half %1, %3 | |
| %4 = bitcast half %add to i16 | |
| %tmp4.0.insert.ext = zext i16 %4 to i32 | |
| %5 = bitcast i32 %tmp4.0.insert.ext to float | |
| ret float %5 | |
| ; CHECK-LABEL: Mul: | |
| ; CHECK-SOFT: bl __aeabi_h2f | |
| ; CHECK-SOFT: bl __aeabi_h2f | |
| ; CHECK-SOFT: bl __aeabi_fmul | |
| ; CHECK-SOFT: bl __aeabi_f2h | |
| ; CHECK-SOFTFP-VFP3: bl __aeabi_h2f | |
| ; CHECK-SOFTFP-VFP3: bl __aeabi_h2f | |
| ; CHECK-SOFTFP-VFP3: vmul.f32 | |
| ; CHECK-SOFTFP-VFP3: bl __aeabi_f2h | |
| ; CHECK-SOFTFP-FP16: vmov [[S2:s[0-9]]], r1 | |
| ; CHECK-SOFTFP-FP16: vmov [[S0:s[0-9]]], r0 | |
| ; CHECK-SOFTFP-FP16: vcvtb.f32.f16 [[S2]], [[S2]] | |
| ; CHECK-SOFTFP-FP16: vcvtb.f32.f16 [[S0]], [[S0]] | |
| ; CHECK-SOFTFP-FP16: vmul.f32 [[S0]], [[S0]], [[S2]] | |
| ; CHECK-SOFTFP-FP16: vcvtb.f16.f32 [[S0]], [[S0]] | |
| ; CHECK-SOFTFP-FP16: vmov r0, s0 | |
| ; CHECK-SOFTFP-FULLFP16: vmov.f16 [[S0:s[0-9]]], r1 | |
| ; CHECK-SOFTFP-FULLFP16: vmov.f16 [[S2:s[0-9]]], r0 | |
| ; CHECK-SOFTFP-FULLFP16: vmul.f16 [[S0]], [[S2]], [[S0]] | |
| ; CHECK-SOFTFP-FULLFP16-NEXT: vmov.f16 r0, s0 | |
| ; CHECK-HARDFP-VFP3: vmov r{{.}}, s0 | |
| ; CHECK-HARDFP-VFP3: vmov{{.*}}, s1 | |
| ; CHECK-HARDFP-VFP3: bl __aeabi_h2f | |
| ; CHECK-HARDFP-VFP3: bl __aeabi_h2f | |
| ; CHECK-HARDFP-VFP3: vmul.f32 | |
| ; CHECK-HARDFP-VFP3: bl __aeabi_f2h | |
| ; CHECK-HARDFP-VFP3: vmov s0, r0 | |
| ; CHECK-HARDFP-FP16: vcvtb.f32.f16 [[S2:s[0-9]]], s1 | |
| ; CHECK-HARDFP-FP16: vcvtb.f32.f16 [[S0:s[0-9]]], s0 | |
| ; CHECK-HARDFP-FP16: vmul.f32 [[S0]], [[S0]], [[S2]] | |
| ; CHECK-HARDFP-FP16: vcvtb.f16.f32 [[S0]], [[S0]] | |
| ; CHECK-HARDFP-FULLFP16: vmul.f16 s0, s0, s1 | |
| } | |
| ; 24. VNEG | |
| define float @Neg(float %a.coerce) { | |
| entry: | |
| %0 = bitcast float %a.coerce to i32 | |
| %tmp.0.extract.trunc = trunc i32 %0 to i16 | |
| %1 = bitcast i16 %tmp.0.extract.trunc to half | |
| %2 = fsub half -0.000000e+00, %1 | |
| %3 = bitcast half %2 to i16 | |
| %tmp4.0.insert.ext = zext i16 %3 to i32 | |
| %4 = bitcast i32 %tmp4.0.insert.ext to float | |
| ret float %4 | |
| ; CHECK-LABEL: Neg: | |
| ; CHECK-HARDFP-FULLFP16: vneg.f16 s0, s0 | |
| } | |
| ; 25. VNMLA | |
| define float @VNMLA(float %a.coerce, float %b.coerce, float %c.coerce) { | |
| entry: | |
| %0 = bitcast float %a.coerce to i32 | |
| %tmp.0.extract.trunc = trunc i32 %0 to i16 | |
| %1 = bitcast i16 %tmp.0.extract.trunc to half | |
| %2 = bitcast float %b.coerce to i32 | |
| %tmp1.0.extract.trunc = trunc i32 %2 to i16 | |
| %3 = bitcast i16 %tmp1.0.extract.trunc to half | |
| %4 = bitcast float %c.coerce to i32 | |
| %tmp2.0.extract.trunc = trunc i32 %4 to i16 | |
| %5 = bitcast i16 %tmp2.0.extract.trunc to half | |
| %add = fmul half %1, %3 | |
| %add2 = fsub half -0.000000e+00, %add | |
| %add3 = fsub half %add2, %5 | |
| %6 = bitcast half %add3 to i16 | |
| %tmp4.0.insert.ext = zext i16 %6 to i32 | |
| %7 = bitcast i32 %tmp4.0.insert.ext to float | |
| ret float %7 | |
| ; CHECK-LABEL: VNMLA: | |
| ; CHECK-HARDFP-FULLFP16: vnmla.f16 s2, s0, s1 | |
| ; CHECK-HARDFP-FULLFP16: vmov.f32 s0, s2 | |
| } | |
| ; 26. VNMLS | |
| define float @VNMLS(float %a.coerce, float %b.coerce, float %c.coerce) { | |
| entry: | |
| %0 = bitcast float %a.coerce to i32 | |
| %tmp.0.extract.trunc = trunc i32 %0 to i16 | |
| %1 = bitcast i16 %tmp.0.extract.trunc to half | |
| %2 = bitcast float %b.coerce to i32 | |
| %tmp1.0.extract.trunc = trunc i32 %2 to i16 | |
| %3 = bitcast i16 %tmp1.0.extract.trunc to half | |
| %4 = bitcast float %c.coerce to i32 | |
| %tmp2.0.extract.trunc = trunc i32 %4 to i16 | |
| %5 = bitcast i16 %tmp2.0.extract.trunc to half | |
| %add = fmul half %1, %3 | |
| %add2 = fsub half %add, %5 | |
| %6 = bitcast half %add2 to i16 | |
| %tmp4.0.insert.ext = zext i16 %6 to i32 | |
| %7 = bitcast i32 %tmp4.0.insert.ext to float | |
| ret float %7 | |
| ; CHECK-LABEL: VNMLS: | |
| ; CHECK-HARDFP-FULLFP16: vnmls.f16 s2, s0, s1 | |
| ; CHECK-HARDFP-FULLFP16: vmov.f32 s0, s2 | |
| } | |
| ; 27. VNMUL | |
| define float @NMul(float %a.coerce, float %b.coerce) { | |
| entry: | |
| %0 = bitcast float %a.coerce to i32 | |
| %tmp.0.extract.trunc = trunc i32 %0 to i16 | |
| %1 = bitcast i16 %tmp.0.extract.trunc to half | |
| %2 = bitcast float %b.coerce to i32 | |
| %tmp1.0.extract.trunc = trunc i32 %2 to i16 | |
| %3 = bitcast i16 %tmp1.0.extract.trunc to half | |
| %add = fmul half %1, %3 | |
| %add2 = fsub half -0.0, %add | |
| %4 = bitcast half %add2 to i16 | |
| %tmp4.0.insert.ext = zext i16 %4 to i32 | |
| %5 = bitcast i32 %tmp4.0.insert.ext to float | |
| ret float %5 | |
| ; CHECK-LABEL: NMul: | |
| ; CHECK-HARDFP-FULLFP16: vnmul.f16 s0, s0, s1 | |
| } | |
| ; TODO: | |
| ; 28. VRINTA | |
| ; 29. VRINTM | |
| ; 30. VRINTN | |
| ; 31. VRINTP | |
| ; 32. VRINTR | |
| ; 33. VRINTX | |
| ; 34. VRINTZ | |
| ; 35. VSELEQ | |
| define half @select_cc1() { | |
| %1 = fcmp nsz oeq half undef, 0xH0001 | |
| %2 = select i1 %1, half 0xHC000, half 0xH0002 | |
| ret half %2 | |
| ; CHECK-LABEL: select_cc1: | |
| ; CHECK-HARDFP-FULLFP16: vcmp.f16 s0, s0 | |
| ; CHECK-HARDFP-FULLFP16-NEXT: vmrs APSR_nzcv, fpscr | |
| ; CHECK-HARDFP-FULLFP16: vseleq.f16 s0, s{{.}}, s{{.}} | |
| ; CHECK-SOFTFP-FP16-A32: vcmp.f32 s0, s0 | |
| ; CHECK-SOFTFP-FP16-A32-NEXT: vmrs APSR_nzcv, fpscr | |
| ; CHECK-SOFTFP-FP16-A32-NEXT: vmoveq.f32 s{{.}}, s{{.}} | |
| ; CHECK-SOFTFP-FP16-T32: vcmp.f32 s0, s0 | |
| ; CHECK-SOFTFP-FP16-T32: vmrs APSR_nzcv, fpscr | |
| ; CHECK-SOFTFP-FP16-T32: it eq | |
| ; CHECK-SOFTFP-FP16-T32: vmoveq.f32 s{{.}}, s{{.}} | |
| } | |
| ; FIXME: more tests need to be added for VSELGE and VSELGT. | |
| ; That is, more combinations of immediate operands that can or can't | |
| ; be encoded as an FP16 immediate need to be added here. | |
| ; | |
| ; 36. VSELGE | |
| define half @select_cc_ge1() { | |
| %1 = fcmp nsz oge half undef, 0xH0001 | |
| %2 = select i1 %1, half 0xHC000, half 0xH0002 | |
| ret half %2 | |
| ; CHECK-LABEL: select_cc_ge1: | |
| ; CHECK-HARDFP-FULLFP16: vcmpe.f16 s0, s0 | |
| ; CHECK-HARDFP-FULLFP16-NEXT: vmrs APSR_nzcv, fpscr | |
| ; CHECK-HARDFP-FULLFP16-NEXT: vselge.f16 s0, s{{.}}, s{{.}} | |
| ; CHECK-SOFTFP-FP16-A32: vcmpe.f32 s0, s0 | |
| ; CHECK-SOFTFP-FP16-A32-NEXT: vmrs APSR_nzcv, fpscr | |
| ; CHECK-SOFTFP-FP16-A32-NEXT: vmovge.f32 s{{.}}, s{{.}} | |
| ; CHECK-SOFTFP-FP16-T32: vcmpe.f32 s0, s0 | |
| ; CHECK-SOFTFP-FP16-T32-NEXT: vmrs APSR_nzcv, fpscr | |
| ; CHECK-SOFTFP-FP16-T32-NEXT: it ge | |
| ; CHECK-SOFTFP-FP16-T32-NEXT: vmovge.f32 s{{.}}, s{{.}} | |
| } | |
| define half @select_cc_ge2() { | |
| %1 = fcmp nsz ole half undef, 0xH0001 | |
| %2 = select i1 %1, half 0xHC000, half 0xH0002 | |
| ret half %2 | |
| ; CHECK-LABEL: select_cc_ge2: | |
| ; CHECK-HARDFP-FULLFP16: vcmpe.f16 s0, s0 | |
| ; CHECK-HARDFP-FULLFP16-NEXT: vmrs APSR_nzcv, fpscr | |
| ; CHECK-HARDFP-FULLFP16-NEXT: vselge.f16 s0, s{{.}}, s{{.}} | |
| ; CHECK-SOFTFP-FP16-A32: vcmpe.f32 s0, s0 | |
| ; CHECK-SOFTFP-FP16-A32-NEXT: vmrs APSR_nzcv, fpscr | |
| ; CHECK-SOFTFP-FP16-A32-NEXT: vmovls.f32 s{{.}}, s{{.}} | |
| ; CHECK-SOFTFP-FP16-T32: vcmpe.f32 s0, s0 | |
| ; CHECK-SOFTFP-FP16-T32-NEXT: vmrs APSR_nzcv, fpscr | |
| ; CHECK-SOFTFP-FP16-T32-NEXT: it ls | |
| ; CHECK-SOFTFP-FP16-T32-NEXT: vmovls.f32 s{{.}}, s{{.}} | |
| } | |
| define half @select_cc_ge3() { | |
| %1 = fcmp nsz ugt half undef, 0xH0001 | |
| %2 = select i1 %1, half 0xHC000, half 0xH0002 | |
| ret half %2 | |
| ; CHECK-LABEL: select_cc_ge3: | |
| ; CHECK-HARDFP-FULLFP16: vcmpe.f16 s0, s0 | |
| ; CHECK-HARDFP-FULLFP16-NEXT: vmrs APSR_nzcv, fpscr | |
| ; CHECK-HARDFP-FULLFP16-NEXT: vselge.f16 s0, s{{.}}, s{{.}} | |
| ; CHECK-SOFTFP-FP16-A32: vcmpe.f32 s0, s0 | |
| ; CHECK-SOFTFP-FP16-A32-NEXT: vmrs APSR_nzcv, fpscr | |
| ; CHECK-SOFTFP-FP16-A32-NEXT: vmovhi.f32 s{{.}}, s{{.}} | |
| ; CHECK-SOFTFP-FP16-T32: vcmpe.f32 s0, s0 | |
| ; CHECK-SOFTFP-FP16-T32-NEXT: vmrs APSR_nzcv, fpscr | |
| ; CHECK-SOFTFP-FP16-T32-NEXT: it hi | |
| ; CHECK-SOFTFP-FP16-T32-NEXT: vmovhi.f32 s{{.}}, s{{.}} | |
| } | |
| define half @select_cc_ge4() { | |
| %1 = fcmp nsz ult half undef, 0xH0001 | |
| %2 = select i1 %1, half 0xHC000, half 0xH0002 | |
| ret half %2 | |
| ; CHECK-LABEL: select_cc_ge4: | |
| ; CHECK-HARDFP-FULLFP16: vcmpe.f16 s0, s0 | |
| ; CHECK-HARDFP-FULLFP16-NEXT: vmrs APSR_nzcv, fpscr | |
| ; CHECK-HARDFP-FULLFP16-NEXT: vselge.f16 s0, s{{.}}, s{{.}} | |
| ; CHECK-SOFTFP-FP16-A32: vcmpe.f32 s0, s0 | |
| ; CHECK-SOFTFP-FP16-A32-NEXT: vmrs APSR_nzcv, fpscr | |
| ; CHECK-SOFTFP-FP16-A32-NEXT: vmovlt.f32 s{{.}}, s{{.}} | |
| ; CHECK-SOFTFP-FP16-T32: vcmpe.f32 s0, s0 | |
| ; CHECK-SOFTFP-FP16-T32-NEXT: vmrs APSR_nzcv, fpscr | |
| ; CHECK-SOFTFP-FP16-T32-NEXT: it lt | |
| ; CHECK-SOFTFP-FP16-T32-NEXT: vmovlt.f32 s{{.}}, s{{.}} | |
| } | |
| ; 37. VSELGT | |
| define half @select_cc_gt1() { | |
| %1 = fcmp nsz ogt half undef, 0xH0001 | |
| %2 = select i1 %1, half 0xHC000, half 0xH0002 | |
| ret half %2 | |
| ; CHECK-LABEL: select_cc_gt1: | |
| ; CHECK-HARDFP-FULLFP16: vcmpe.f16 s0, s0 | |
| ; CHECK-HARDFP-FULLFP16-NEXT: vmrs APSR_nzcv, fpscr | |
| ; CHECK-HARDFP-FULLFP16-NEXT: vselgt.f16 s0, s{{.}}, s{{.}} | |
| ; CHECK-SOFTFP-FP16-A32: vcmpe.f32 s0, s0 | |
| ; CHECK-SOFTFP-FP16-A32-NEXT: vmrs APSR_nzcv, fpscr | |
| ; CHECK-SOFTFP-FP16-A32-NEXT: vmovgt.f32 s{{.}}, s{{.}} | |
| ; CHECK-SOFTFP-FP16-T32: vcmpe.f32 s0, s0 | |
| ; CHECK-SOFTFP-FP16-T32-NEXT: vmrs APSR_nzcv, fpscr | |
| ; CHECK-SOFTFP-FP16-T32-NEXT: it gt | |
| ; CHECK-SOFTFP-FP16-T32-NEXT: vmovgt.f32 s{{.}}, s{{.}} | |
| } | |
| define half @select_cc_gt2() { | |
| %1 = fcmp nsz uge half undef, 0xH0001 | |
| %2 = select i1 %1, half 0xHC000, half 0xH0002 | |
| ret half %2 | |
| ; CHECK-LABEL: select_cc_gt2: | |
| ; CHECK-HARDFP-FULLFP16: vcmpe.f16 s0, s0 | |
| ; CHECK-HARDFP-FULLFP16-NEXT: vmrs APSR_nzcv, fpscr | |
| ; CHECK-HARDFP-FULLFP16-NEXT: vselgt.f16 s0, s{{.}}, s{{.}} | |
| ; CHECK-SOFTFP-FP16-A32: vcmpe.f32 s0, s0 | |
| ; CHECK-SOFTFP-FP16-A32-NEXT: vmrs APSR_nzcv, fpscr | |
| ; CHECK-SOFTFP-FP16-A32-NEXT: vmovpl.f32 s{{.}}, s{{.}} | |
| ; CHECK-SOFTFP-FP16-T32: vcmpe.f32 s0, s0 | |
| ; CHECK-SOFTFP-FP16-T32-NEXT: vmrs APSR_nzcv, fpscr | |
| ; CHECK-SOFTFP-FP16-T32-NEXT: it pl | |
| ; CHECK-SOFTFP-FP16-T32-NEXT: vmovpl.f32 s{{.}}, s{{.}} | |
| } | |
| define half @select_cc_gt3() { | |
| %1 = fcmp nsz ule half undef, 0xH0001 | |
| %2 = select i1 %1, half 0xHC000, half 0xH0002 | |
| ret half %2 | |
| ; CHECK-LABEL: select_cc_gt3: | |
| ; CHECK-HARDFP-FULLFP16: vcmpe.f16 s0, s0 | |
| ; CHECK-HARDFP-FULLFP16-NEXT: vmrs APSR_nzcv, fpscr | |
| ; CHECK-HARDFP-FULLFP16-NEXT: vselgt.f16 s0, s{{.}}, s{{.}} | |
| ; CHECK-SOFTFP-FP16-A32: vcmpe.f32 s0, s0 | |
| ; CHECK-SOFTFP-FP16-A32-NEXT: vmrs APSR_nzcv, fpscr | |
| ; CHECK-SOFTFP-FP16-A32-NEXT: vmovle.f32 s{{.}}, s{{.}} | |
| ; CHECK-SOFTFP-FP16-T32: vcmpe.f32 s0, s0 | |
| ; CHECK-SOFTFP-FP16-T32-NEXT: vmrs APSR_nzcv, fpscr | |
| ; CHECK-SOFTFP-FP16-T32-NEXT: it le | |
| ; CHECK-SOFTFP-FP16-T32-NEXT: vmovle.f32 s{{.}}, s{{.}} | |
| } | |
| define half @select_cc_gt4() { | |
| %1 = fcmp nsz olt half undef, 0xH0001 | |
| %2 = select i1 %1, half 0xHC000, half 0xH0002 | |
| ret half %2 | |
| ; CHECK-LABEL: select_cc_gt4: | |
| ; CHECK-HARDFP-FULLFP16: vcmpe.f16 s0, s0 | |
| ; CHECK-HARDFP-FULLFP16-NEXT: vmrs APSR_nzcv, fpscr | |
| ; CHECK-HARDFP-FULLFP16-NEXT: vselgt.f16 s0, s{{.}}, s{{.}} | |
| ; CHECK-SOFTFP-FP16-A32: vcmpe.f32 s0, s0 | |
| ; CHECK-SOFTFP-FP16-A32-NEXT: vmrs APSR_nzcv, fpscr | |
| ; CHECK-SOFTFP-FP16-A32-NEXT: vmovmi.f32 s{{.}}, s{{.}} | |
| ; CHECK-SOFTFP-FP16-T32: vcmpe.f32 s0, s0 | |
| ; CHECK-SOFTFP-FP16-T32-NEXT: vmrs APSR_nzcv, fpscr | |
| ; CHECK-SOFTFP-FP16-T32-NEXT: it mi | |
| ; CHECK-SOFTFP-FP16-T32-NEXT: vmovmi.f32 s{{.}}, s{{.}} | |
| } | |
| ; 38. VSELVS | |
| define float @select_cc4(float %a.coerce) { | |
| entry: | |
| %0 = bitcast float %a.coerce to i32 | |
| %tmp.0.extract.trunc = trunc i32 %0 to i16 | |
| %1 = bitcast i16 %tmp.0.extract.trunc to half | |
| %2 = fcmp nsz ueq half %1, 0xH0001 | |
| %3 = select i1 %2, half 0xHC000, half 0xH0002 | |
| %4 = bitcast half %3 to i16 | |
| %tmp4.0.insert.ext = zext i16 %4 to i32 | |
| %5 = bitcast i32 %tmp4.0.insert.ext to float | |
| ret float %5 | |
| ; CHECK-LABEL: select_cc4: | |
| ; CHECK-HARDFP-FULLFP16: vldr.16 [[S2:s[0-9]]], .LCPI{{.*}} | |
| ; CHECK-HARDFP-FULLFP16: vldr.16 [[S4:s[0-9]]], .LCPI{{.*}} | |
| ; CHECK-HARDFP-FULLFP16: vmov.f16 [[S6:s[0-9]]], #-2.000000e+00 | |
| ; CHECK-HARDFP-FULLFP16: vcmp.f16 s0, [[S2]] | |
| ; CHECK-HARDFP-FULLFP16-NEXT: vmrs APSR_nzcv, fpscr | |
| ; CHECK-HARDFP-FULLFP16-NEXT: vseleq.f16 [[S0:s[0-9]]], [[S6]], [[S4]] | |
| ; CHECK-HARDFP-FULLFP16-NEXT: vselvs.f16 s0, [[S6]], [[S0]] | |
| ; CHECK-SOFTFP-FP16-A32: vmov [[S6:s[0-9]]], r0 | |
| ; CHECK-SOFTFP-FP16-A32: vldr s0, .LCP{{.*}} | |
| ; CHECK-SOFTFP-FP16-A32: vcvtb.f32.f16 [[S6]], [[S6]] | |
| ; CHECK-SOFTFP-FP16-A32: vmov.f32 [[S2:s[0-9]]], #-2.000000e+00 | |
| ; CHECK-SOFTFP-FP16-A32: vcmp.f32 [[S6]], s0 | |
| ; CHECK-SOFTFP-FP16-A32: vldr [[S4:s[0-9]]], .LCPI{{.*}} | |
| ; CHECK-SOFTFP-FP16-A32: vmrs APSR_nzcv, fpscr | |
| ; CHECK-SOFTFP-FP16-A32: vmoveq.f32 [[S4]], [[S2]] | |
| ; CHECK-SOFTFP-FP16-A32-NEXT: vmovvs.f32 [[S4]], [[S2]] | |
| ; CHECK-SOFTFP-FP16-A32-NEXT: vcvtb.f16.f32 s0, [[S4]] | |
| ; CHECK-SOFTFP-FP16-T32: vmov [[S6:s[0-9]]], r0 | |
| ; CHECK-SOFTFP-FP16-T32: vldr s0, .LCP{{.*}} | |
| ; CHECK-SOFTFP-FP16-T32: vcvtb.f32.f16 [[S6]], [[S6]] | |
| ; CHECK-SOFTFP-FP16-T32: vmov.f32 [[S2:s[0-9]]], #-2.000000e+00 | |
| ; CHECK-SOFTFP-FP16-T32: vcmp.f32 [[S6]], s0 | |
| ; CHECK-SOFTFP-FP16-T32: vldr [[S4:s[0-9]]], .LCPI{{.*}} | |
| ; CHECK-SOFTFP-FP16-T32: vmrs APSR_nzcv, fpscr | |
| ; CHECK-SOFTFP-FP16-T32: it eq | |
| ; CHECK-SOFTFP-FP16-T32: vmoveq.f32 [[S4]], [[S2]] | |
| ; CHECK-SOFTFP-FP16-T32: it vs | |
| ; CHECK-SOFTFP-FP16-T32-NEXT: vmovvs.f32 [[S4]], [[S2]] | |
| ; CHECK-SOFTFP-FP16-T32-NEXT: vcvtb.f16.f32 s0, [[S4]] | |
| } | |
| ; 39. VSQRT - TODO | |
| ; 40. VSUB | |
| define float @Sub(float %a.coerce, float %b.coerce) { | |
| entry: | |
| %0 = bitcast float %a.coerce to i32 | |
| %tmp.0.extract.trunc = trunc i32 %0 to i16 | |
| %1 = bitcast i16 %tmp.0.extract.trunc to half | |
| %2 = bitcast float %b.coerce to i32 | |
| %tmp1.0.extract.trunc = trunc i32 %2 to i16 | |
| %3 = bitcast i16 %tmp1.0.extract.trunc to half | |
| %add = fsub half %1, %3 | |
| %4 = bitcast half %add to i16 | |
| %tmp4.0.insert.ext = zext i16 %4 to i32 | |
| %5 = bitcast i32 %tmp4.0.insert.ext to float | |
| ret float %5 | |
| ; CHECK-LABEL: Sub: | |
| ; CHECK-SOFT: bl __aeabi_h2f | |
| ; CHECK-SOFT: bl __aeabi_h2f | |
| ; CHECK-SOFT: bl __aeabi_fsub | |
| ; CHECK-SOFT: bl __aeabi_f2h | |
| ; CHECK-SOFTFP-VFP3: bl __aeabi_h2f | |
| ; CHECK-SOFTFP-VFP3: bl __aeabi_h2f | |
| ; CHECK-SOFTFP-VFP3: vsub.f32 | |
| ; CHECK-SOFTFP-VFP3: bl __aeabi_f2h | |
| ; CHECK-SOFTFP-FP16: vmov [[S2:s[0-9]]], r1 | |
| ; CHECK-SOFTFP-FP16: vmov [[S0:s[0-9]]], r0 | |
| ; CHECK-SOFTFP-FP16: vcvtb.f32.f16 [[S2]], [[S2]] | |
| ; CHECK-SOFTFP-FP16: vcvtb.f32.f16 [[S0]], [[S0]] | |
| ; CHECK-SOFTFP-FP16: vsub.f32 [[S0]], [[S0]], [[S2]] | |
| ; CHECK-SOFTFP-FP16: vcvtb.f16.f32 [[S0]], [[S0]] | |
| ; CHECK-SOFTFP-FP16: vmov r0, s0 | |
| ; CHECK-SOFTFP-FULLFP16: vmov.f16 [[S0:s[0-9]]], r1 | |
| ; CHECK-SOFTFP-FULLFP16: vmov.f16 [[S2:s[0-9]]], r0 | |
| ; CHECK-SOFTFP-FULLFP16: vsub.f16 [[S0]], [[S2]], [[S0]] | |
| ; CHECK-SOFTFP-FULLFP16-NEXT: vmov.f16 r0, s0 | |
| ; CHECK-HARDFP-VFP3: vmov r{{.}}, s0 | |
| ; CHECK-HARDFP-VFP3: vmov{{.*}}, s1 | |
| ; CHECK-HARDFP-VFP3: bl __aeabi_h2f | |
| ; CHECK-HARDFP-VFP3: bl __aeabi_h2f | |
| ; CHECK-HARDFP-VFP3: vsub.f32 | |
| ; CHECK-HARDFP-VFP3: bl __aeabi_f2h | |
| ; CHECK-HARDFP-VFP3: vmov s0, r0 | |
| ; CHECK-HARDFP-FP16: vcvtb.f32.f16 [[S2:s[0-9]]], s1 | |
| ; CHECK-HARDFP-FP16: vcvtb.f32.f16 [[S0:s[0-9]]], s0 | |
| ; CHECK-HARDFP-FP16: vsub.f32 [[S0]], [[S0]], [[S2]] | |
| ; CHECK-HARDFP-FP16: vcvtb.f16.f32 [[S0]], [[S0]] | |
| ; CHECK-HARDFP-FULLFP16: vsub.f16 s0, s0, s1 | |
| } | |
| ; Check for VSTRH with a FCONSTH, this checks that addressing mode | |
| ; AddrMode5FP16 is supported. | |
| define i32 @ThumbAddrMode5FP16(i32 %A.coerce) { | |
| entry: | |
| %S = alloca half, align 2 | |
| %tmp.0.extract.trunc = trunc i32 %A.coerce to i16 | |
| %0 = bitcast i16 %tmp.0.extract.trunc to half | |
| %S.0.S.0..sroa_cast = bitcast half* %S to i8* | |
| store volatile half 0xH3C00, half* %S, align 2 | |
| %S.0.S.0. = load volatile half, half* %S, align 2 | |
| %add = fadd half %S.0.S.0., %0 | |
| %1 = bitcast half %add to i16 | |
| %tmp2.0.insert.ext = zext i16 %1 to i32 | |
| ret i32 %tmp2.0.insert.ext | |
| ; CHECK-LABEL: ThumbAddrMode5FP16 | |
| ; CHECK-SOFTFP-FULLFP16: vmov.f16 [[S0:s[0-9]]], #1.000000e+00 | |
| ; CHECK-SOFTFP-FULLFP16: vstr.16 [[S0]], [sp, #{{.}}] | |
| ; CHECK-SOFTFP-FULLFP16: vmov.f16 [[S0_2:s[0-9]]], r0 | |
| ; CHECK-SOFTFP-FULLFP16: vldr.16 [[S2:s[0-9]]], [sp, #{{.}}] | |
| ; CHECK-SOFTFP-FULLFP16: vadd.f16 s{{.}}, [[S2]], [[S0_2]] | |
| } | |
| ; Test function calls to check store/load reg to/from stack | |
| define i32 @fn1() { | |
| entry: | |
| %coerce = alloca half, align 2 | |
| %tmp2 = alloca i32, align 4 | |
| store half 0xH7C00, half* %coerce, align 2 | |
| %0 = load i32, i32* %tmp2, align 4 | |
| %call = call i32 bitcast (i32 (...)* @fn2 to i32 (i32)*)(i32 %0) | |
| store half 0xH7C00, half* %coerce, align 2 | |
| %1 = load i32, i32* %tmp2, align 4 | |
| %call3 = call i32 bitcast (i32 (...)* @fn3 to i32 (i32)*)(i32 %1) | |
| ret i32 %call3 | |
| ; CHECK-SPILL-RELOAD-LABEL: fn1: | |
| ; CHECK-SPILL-RELOAD: vstr.16 s0, [sp, #{{.}}] @ 2-byte Spill | |
| ; CHECK-SPILL-RELOAD-NEXT: bl fn2 | |
| ; CHECK-SPILL-RELOAD-NEXT: vldr.16 s0, [sp, #{{.}}] @ 2-byte Reload | |
| } | |
| declare dso_local i32 @fn2(...) | |
| declare dso_local i32 @fn3(...) |