; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
; RUN: llc %s -o - -mtriple=avr | FileCheck %s

; Tests for various operations on half precison float. Much of the test is
; copied from test/CodeGen/X86/half.ll.

define void @store(half %x, ptr %p) nounwind {
; CHECK-LABEL: store:
; CHECK:       ; %bb.0:
; CHECK-NEXT:    mov r30, r22
; CHECK-NEXT:    mov r31, r23
; CHECK-NEXT:    std Z+1, r25
; CHECK-NEXT:    st Z, r24
; CHECK-NEXT:    ret
  store half %x, ptr %p
  ret void
}

define half @return(ptr %p) nounwind {
; CHECK-LABEL: return:
; CHECK:       ; %bb.0:
; CHECK-NEXT:    mov r30, r24
; CHECK-NEXT:    mov r31, r25
; CHECK-NEXT:    ld r24, Z
; CHECK-NEXT:    ldd r25, Z+1
; CHECK-NEXT:    ret
  %r = load half, ptr %p
  ret half %r
}

define dso_local double @loadd(ptr nocapture readonly %a) local_unnamed_addr nounwind {
; CHECK-LABEL: loadd:
; CHECK:       ; %bb.0: ; %entry
; CHECK-NEXT:    mov r30, r24
; CHECK-NEXT:    mov r31, r25
; CHECK-NEXT:    ldd r24, Z+2
; CHECK-NEXT:    ldd r25, Z+3
; CHECK-NEXT:    rcall __extendhfsf2
; CHECK-NEXT:    rcall __extendsfdf2
; CHECK-NEXT:    ret
entry:
  %arrayidx = getelementptr inbounds i16, ptr %a, i64 1
  %0 = load i16, ptr %arrayidx, align 2
  %1 = tail call double @llvm.convert.from.fp16.f64(i16 %0)
  ret double %1
}

define dso_local float @loadf(ptr nocapture readonly %a) local_unnamed_addr nounwind {
; CHECK-LABEL: loadf:
; CHECK:       ; %bb.0: ; %entry
; CHECK-NEXT:    mov r30, r24
; CHECK-NEXT:    mov r31, r25
; CHECK-NEXT:    ldd r24, Z+2
; CHECK-NEXT:    ldd r25, Z+3
; CHECK-NEXT:    rcall __extendhfsf2
; CHECK-NEXT:    ret
entry:
  %arrayidx = getelementptr inbounds i16, ptr %a, i64 1
  %0 = load i16, ptr %arrayidx, align 2
  %1 = tail call float @llvm.convert.from.fp16.f32(i16 %0)
  ret float %1
}

define dso_local void @stored(ptr nocapture %a, double %b) local_unnamed_addr nounwind {
; CHECK-LABEL: stored:
; CHECK:       ; %bb.0: ; %entry
; CHECK-NEXT:    push r16
; CHECK-NEXT:    push r17
; CHECK-NEXT:    mov r30, r22
; CHECK-NEXT:    mov r31, r23
; CHECK-NEXT:    mov r22, r20
; CHECK-NEXT:    mov r23, r21
; CHECK-NEXT:    mov r20, r18
; CHECK-NEXT:    mov r21, r19
; CHECK-NEXT:    mov r18, r16
; CHECK-NEXT:    mov r19, r17
; CHECK-NEXT:    mov r16, r24
; CHECK-NEXT:    mov r17, r25
; CHECK-NEXT:    mov r24, r30
; CHECK-NEXT:    mov r25, r31
; CHECK-NEXT:    rcall __truncdfhf2
; CHECK-NEXT:    mov r30, r16
; CHECK-NEXT:    mov r31, r17
; CHECK-NEXT:    std Z+1, r25
; CHECK-NEXT:    st Z, r24
; CHECK-NEXT:    pop r17
; CHECK-NEXT:    pop r16
; CHECK-NEXT:    ret
entry:
  %0 = tail call i16 @llvm.convert.to.fp16.f64(double %b)
  store i16 %0, ptr %a, align 2
  ret void
}

define dso_local void @storef(ptr nocapture %a, float %b) local_unnamed_addr nounwind {
; CHECK-LABEL: storef:
; CHECK:       ; %bb.0: ; %entry
; CHECK-NEXT:    push r16
; CHECK-NEXT:    push r17
; CHECK-NEXT:    mov r18, r22
; CHECK-NEXT:    mov r19, r23
; CHECK-NEXT:    mov r16, r24
; CHECK-NEXT:    mov r17, r25
; CHECK-NEXT:    mov r22, r20
; CHECK-NEXT:    mov r23, r21
; CHECK-NEXT:    mov r24, r18
; CHECK-NEXT:    mov r25, r19
; CHECK-NEXT:    rcall __truncsfhf2
; CHECK-NEXT:    mov r30, r16
; CHECK-NEXT:    mov r31, r17
; CHECK-NEXT:    std Z+1, r25
; CHECK-NEXT:    st Z, r24
; CHECK-NEXT:    pop r17
; CHECK-NEXT:    pop r16
; CHECK-NEXT:    ret
entry:
  %0 = tail call i16 @llvm.convert.to.fp16.f32(float %b)
  store i16 %0, ptr %a, align 2
  ret void
}

define void @test_load_store(ptr %in, ptr %out) nounwind {
; CHECK-LABEL: test_load_store:
; CHECK:       ; %bb.0:
; CHECK-NEXT:    mov r30, r24
; CHECK-NEXT:    mov r31, r25
; CHECK-NEXT:    ld r24, Z
; CHECK-NEXT:    ldd r25, Z+1
; CHECK-NEXT:    mov r30, r22
; CHECK-NEXT:    mov r31, r23
; CHECK-NEXT:    std Z+1, r25
; CHECK-NEXT:    st Z, r24
; CHECK-NEXT:    ret
  %val = load half, ptr %in
  store half %val, ptr %out
  ret void
}

define i16 @test_bitcast_from_half(ptr %addr) nounwind {
; CHECK-LABEL: test_bitcast_from_half:
; CHECK:       ; %bb.0:
; CHECK-NEXT:    mov r30, r24
; CHECK-NEXT:    mov r31, r25
; CHECK-NEXT:    ld r24, Z
; CHECK-NEXT:    ldd r25, Z+1
; CHECK-NEXT:    ret
  %val = load half, ptr %addr
  %val_int = bitcast half %val to i16
  ret i16 %val_int
}

define void @test_bitcast_to_half(ptr %addr, i16 %in) nounwind {
; CHECK-LABEL: test_bitcast_to_half:
; CHECK:       ; %bb.0:
; CHECK-NEXT:    mov r30, r24
; CHECK-NEXT:    mov r31, r25
; CHECK-NEXT:    std Z+1, r23
; CHECK-NEXT:    st Z, r22
; CHECK-NEXT:    ret
  %val_fp = bitcast i16 %in to half
  store half %val_fp, ptr %addr
  ret void
}

define half @from_bits(i16 %x) nounwind {
; CHECK-LABEL: from_bits:
; CHECK:       ; %bb.0:
; CHECK-NEXT:    ret
  %res = bitcast i16 %x to half
  ret half %res
}

define i16 @to_bits(half %x) nounwind {
; CHECK-LABEL: to_bits:
; CHECK:       ; %bb.0:
; CHECK-NEXT:    ret
    %res = bitcast half %x to i16
    ret i16 %res
}

define float @test_extend32(ptr %addr) nounwind {
; CHECK-LABEL: test_extend32:
; CHECK:       ; %bb.0:
; CHECK-NEXT:    mov r30, r24
; CHECK-NEXT:    mov r31, r25
; CHECK-NEXT:    ld r24, Z
; CHECK-NEXT:    ldd r25, Z+1
; CHECK-NEXT:    rcall __extendhfsf2
; CHECK-NEXT:    ret
  %val16 = load half, ptr %addr
  %val32 = fpext half %val16 to float
  ret float %val32
}

define double @test_extend64(ptr %addr) nounwind {
; CHECK-LABEL: test_extend64:
; CHECK:       ; %bb.0:
; CHECK-NEXT:    mov r30, r24
; CHECK-NEXT:    mov r31, r25
; CHECK-NEXT:    ld r24, Z
; CHECK-NEXT:    ldd r25, Z+1
; CHECK-NEXT:    rcall __extendhfsf2
; CHECK-NEXT:    rcall __extendsfdf2
; CHECK-NEXT:    ret
  %val16 = load half, ptr %addr
  %val32 = fpext half %val16 to double
  ret double %val32
}

define void @test_trunc32(float %in, ptr %addr) nounwind {
; CHECK-LABEL: test_trunc32:
; CHECK:       ; %bb.0:
; CHECK-NEXT:    push r16
; CHECK-NEXT:    push r17
; CHECK-NEXT:    mov r16, r20
; CHECK-NEXT:    mov r17, r21
; CHECK-NEXT:    rcall __truncsfhf2
; CHECK-NEXT:    mov r30, r16
; CHECK-NEXT:    mov r31, r17
; CHECK-NEXT:    std Z+1, r25
; CHECK-NEXT:    st Z, r24
; CHECK-NEXT:    pop r17
; CHECK-NEXT:    pop r16
; CHECK-NEXT:    ret
  %val16 = fptrunc float %in to half
  store half %val16, ptr %addr
  ret void
}

define void @test_trunc64(double %in, ptr %addr) nounwind {
; CHECK-LABEL: test_trunc64:
; CHECK:       ; %bb.0:
; CHECK-NEXT:    rcall __truncdfhf2
; CHECK-NEXT:    mov r30, r16
; CHECK-NEXT:    mov r31, r17
; CHECK-NEXT:    std Z+1, r25
; CHECK-NEXT:    st Z, r24
; CHECK-NEXT:    ret
  %val16 = fptrunc double %in to half
  store half %val16, ptr %addr
  ret void
}

define i64 @test_fptosi_i64(ptr %p) nounwind {
; CHECK-LABEL: test_fptosi_i64:
; CHECK:       ; %bb.0:
; CHECK-NEXT:    mov r30, r24
; CHECK-NEXT:    mov r31, r25
; CHECK-NEXT:    ld r24, Z
; CHECK-NEXT:    ldd r25, Z+1
; CHECK-NEXT:    rcall __extendhfsf2
; CHECK-NEXT:    rcall __fixsfdi
; CHECK-NEXT:    ret
  %a = load half, ptr %p, align 2
  %r = fptosi half %a to i64
  ret i64 %r
}

define void @test_sitofp_i64(i64 %a, ptr %p) nounwind {
; CHECK-LABEL: test_sitofp_i64:
; CHECK:       ; %bb.0:
; CHECK-NEXT:    rcall __floatdisf
; CHECK-NEXT:    rcall __truncsfhf2
; CHECK-NEXT:    mov r30, r16
; CHECK-NEXT:    mov r31, r17
; CHECK-NEXT:    std Z+1, r25
; CHECK-NEXT:    st Z, r24
; CHECK-NEXT:    ret
  %r = sitofp i64 %a to half
  store half %r, ptr %p
  ret void
}

define i64 @test_fptoui_i64(ptr %p) nounwind {
; CHECK-LABEL: test_fptoui_i64:
; CHECK:       ; %bb.0:
; CHECK-NEXT:    mov r30, r24
; CHECK-NEXT:    mov r31, r25
; CHECK-NEXT:    ld r24, Z
; CHECK-NEXT:    ldd r25, Z+1
; CHECK-NEXT:    rcall __extendhfsf2
; CHECK-NEXT:    rcall __fixunssfdi
; CHECK-NEXT:    ret
  %a = load half, ptr %p, align 2
  %r = fptoui half %a to i64
  ret i64 %r
}

define void @test_uitofp_i64(i64 %a, ptr %p) nounwind {
; CHECK-LABEL: test_uitofp_i64:
; CHECK:       ; %bb.0:
; CHECK-NEXT:    rcall __floatundisf
; CHECK-NEXT:    rcall __truncsfhf2
; CHECK-NEXT:    mov r30, r16
; CHECK-NEXT:    mov r31, r17
; CHECK-NEXT:    std Z+1, r25
; CHECK-NEXT:    st Z, r24
; CHECK-NEXT:    ret
  %r = uitofp i64 %a to half
  store half %r, ptr %p
  ret void
}

define <2 x float> @test_extend32_vec2(ptr %p) nounwind {
; CHECK-LABEL: test_extend32_vec2:
; CHECK:       ; %bb.0:
; CHECK-NEXT:    push r12
; CHECK-NEXT:    push r13
; CHECK-NEXT:    push r14
; CHECK-NEXT:    push r15
; CHECK-NEXT:    push r16
; CHECK-NEXT:    push r17
; CHECK-NEXT:    mov r30, r24
; CHECK-NEXT:    mov r31, r25
; CHECK-NEXT:    ld r24, Z
; CHECK-NEXT:    ldd r25, Z+1
; CHECK-NEXT:    mov r12, r30
; CHECK-NEXT:    mov r13, r31
; CHECK-NEXT:    rcall __extendhfsf2
; CHECK-NEXT:    mov r16, r22
; CHECK-NEXT:    mov r17, r23
; CHECK-NEXT:    mov r14, r24
; CHECK-NEXT:    mov r15, r25
; CHECK-NEXT:    mov r30, r12
; CHECK-NEXT:    mov r31, r13
; CHECK-NEXT:    ldd r24, Z+2
; CHECK-NEXT:    ldd r25, Z+3
; CHECK-NEXT:    rcall __extendhfsf2
; CHECK-NEXT:    mov r18, r16
; CHECK-NEXT:    mov r19, r17
; CHECK-NEXT:    mov r20, r14
; CHECK-NEXT:    mov r21, r15
; CHECK-NEXT:    pop r17
; CHECK-NEXT:    pop r16
; CHECK-NEXT:    pop r15
; CHECK-NEXT:    pop r14
; CHECK-NEXT:    pop r13
; CHECK-NEXT:    pop r12
; CHECK-NEXT:    ret
  %a = load <2 x half>, ptr %p, align 8
  %b = fpext <2 x half> %a to <2 x float>
  ret <2 x float> %b
}

define <1 x double> @test_extend64_vec1(ptr %p) nounwind {
; CHECK-LABEL: test_extend64_vec1:
; CHECK:       ; %bb.0:
; CHECK-NEXT:    mov r30, r24
; CHECK-NEXT:    mov r31, r25
; CHECK-NEXT:    ld r24, Z
; CHECK-NEXT:    ldd r25, Z+1
; CHECK-NEXT:    rcall __extendhfsf2
; CHECK-NEXT:    rcall __extendsfdf2
; CHECK-NEXT:    ret
  %a = load <1 x half>, ptr %p, align 8
  %b = fpext <1 x half> %a to <1 x double>
  ret <1 x double> %b
}

define void @test_trunc32_vec2(<2 x float> %a, ptr %p) nounwind {
; CHECK-LABEL: test_trunc32_vec2:
; CHECK:       ; %bb.0:
; CHECK-NEXT:    push r12
; CHECK-NEXT:    push r13
; CHECK-NEXT:    push r14
; CHECK-NEXT:    push r15
; CHECK-NEXT:    mov r14, r20
; CHECK-NEXT:    mov r15, r21
; CHECK-NEXT:    mov r12, r18
; CHECK-NEXT:    mov r13, r19
; CHECK-NEXT:    rcall __truncsfhf2
; CHECK-NEXT:    mov r30, r16
; CHECK-NEXT:    mov r31, r17
; CHECK-NEXT:    std Z+3, r25
; CHECK-NEXT:    std Z+2, r24
; CHECK-NEXT:    mov r22, r12
; CHECK-NEXT:    mov r23, r13
; CHECK-NEXT:    mov r24, r14
; CHECK-NEXT:    mov r25, r15
; CHECK-NEXT:    rcall __truncsfhf2
; CHECK-NEXT:    mov r30, r16
; CHECK-NEXT:    mov r31, r17
; CHECK-NEXT:    std Z+1, r25
; CHECK-NEXT:    st Z, r24
; CHECK-NEXT:    pop r15
; CHECK-NEXT:    pop r14
; CHECK-NEXT:    pop r13
; CHECK-NEXT:    pop r12
; CHECK-NEXT:    ret
  %v = fptrunc <2 x float> %a to <2 x half>
  store <2 x half> %v, ptr %p
  ret void
}

define void @test_trunc64_vec1(<1 x double> %a, ptr %p) nounwind {
; CHECK-LABEL: test_trunc64_vec1:
; CHECK:       ; %bb.0:
; CHECK-NEXT:    rcall __truncdfhf2
; CHECK-NEXT:    mov r30, r16
; CHECK-NEXT:    mov r31, r17
; CHECK-NEXT:    std Z+1, r25
; CHECK-NEXT:    st Z, r24
; CHECK-NEXT:    ret
  %v = fptrunc <1 x double> %a to <1 x half>
  store <1 x half> %v, ptr %p
  ret void
}

define float @test_sitofp_fadd_i32(i32 %a, ptr %b) nounwind {
; CHECK-LABEL: test_sitofp_fadd_i32:
; CHECK:       ; %bb.0:
; CHECK-NEXT:    push r12
; CHECK-NEXT:    push r13
; CHECK-NEXT:    push r14
; CHECK-NEXT:    push r15
; CHECK-NEXT:    push r16
; CHECK-NEXT:    push r17
; CHECK-NEXT:    mov r16, r20
; CHECK-NEXT:    mov r17, r21
; CHECK-NEXT:    rcall __floatsisf
; CHECK-NEXT:    rcall __truncsfhf2
; CHECK-NEXT:    mov r14, r24
; CHECK-NEXT:    mov r15, r25
; CHECK-NEXT:    mov r30, r16
; CHECK-NEXT:    mov r31, r17
; CHECK-NEXT:    ld r24, Z
; CHECK-NEXT:    ldd r25, Z+1
; CHECK-NEXT:    rcall __extendhfsf2
; CHECK-NEXT:    mov r16, r22
; CHECK-NEXT:    mov r17, r23
; CHECK-NEXT:    mov r12, r24
; CHECK-NEXT:    mov r13, r25
; CHECK-NEXT:    mov r24, r14
; CHECK-NEXT:    mov r25, r15
; CHECK-NEXT:    rcall __extendhfsf2
; CHECK-NEXT:    mov r18, r22
; CHECK-NEXT:    mov r19, r23
; CHECK-NEXT:    mov r20, r24
; CHECK-NEXT:    mov r21, r25
; CHECK-NEXT:    mov r22, r16
; CHECK-NEXT:    mov r23, r17
; CHECK-NEXT:    mov r24, r12
; CHECK-NEXT:    mov r25, r13
; CHECK-NEXT:    rcall __addsf3
; CHECK-NEXT:    rcall __truncsfhf2
; CHECK-NEXT:    rcall __extendhfsf2
; CHECK-NEXT:    pop r17
; CHECK-NEXT:    pop r16
; CHECK-NEXT:    pop r15
; CHECK-NEXT:    pop r14
; CHECK-NEXT:    pop r13
; CHECK-NEXT:    pop r12
; CHECK-NEXT:    ret
  %tmp0 = load half, ptr %b
  %tmp1 = sitofp i32 %a to half
  %tmp2 = fadd half %tmp0, %tmp1
  %tmp3 = fpext half %tmp2 to float
  ret float %tmp3
}

define half @chained_fp_ops(half %x) {
; CHECK-LABEL: chained_fp_ops:
; CHECK:       ; %bb.0: ; %start
; CHECK-NEXT:    rcall __extendhfsf2
; CHECK-NEXT:    mov r18, r22
; CHECK-NEXT:    mov r19, r23
; CHECK-NEXT:    mov r20, r24
; CHECK-NEXT:    mov r21, r25
; CHECK-NEXT:    rcall __addsf3
; CHECK-NEXT:    rcall __truncsfhf2
; CHECK-NEXT:    rcall __extendhfsf2
; CHECK-NEXT:    ldi r18, 0
; CHECK-NEXT:    ldi r19, 0
; CHECK-NEXT:    ldi r20, 0
; CHECK-NEXT:    ldi r21, 63
; CHECK-NEXT:    rcall __mulsf3
; CHECK-NEXT:    rcall __truncsfhf2
; CHECK-NEXT:    ret
start:
  %y = fmul half %x, 0xH4000
  %z = fdiv half %y, 0xH4000
  ret half %z
}

define half @test_select_cc(half) nounwind {
; CHECK-LABEL: test_select_cc:
; CHECK:       ; %bb.0:
; CHECK-NEXT:    push r16
; CHECK-NEXT:    push r17
; CHECK-NEXT:    rcall __extendhfsf2
; CHECK-NEXT:    ldi r16, 0
; CHECK-NEXT:    ldi r17, 0
; CHECK-NEXT:    mov r18, r16
; CHECK-NEXT:    mov r19, r17
; CHECK-NEXT:    mov r20, r16
; CHECK-NEXT:    mov r21, r17
; CHECK-NEXT:    rcall __nesf2
; CHECK-NEXT:    cpi r24, 0
; CHECK-NEXT:    breq .LBB25_2
; CHECK-NEXT:  ; %bb.1:
; CHECK-NEXT:    ldi r16, 0
; CHECK-NEXT:    ldi r17, 60
; CHECK-NEXT:  .LBB25_2:
; CHECK-NEXT:    mov r24, r16
; CHECK-NEXT:    mov r25, r17
; CHECK-NEXT:    pop r17
; CHECK-NEXT:    pop r16
; CHECK-NEXT:    ret
  %2 = fcmp une half %0, 0xH0000
  %3 = uitofp i1 %2 to half
  ret half %3
}

define half @fabs(half %x) nounwind {
; CHECK-LABEL: fabs:
; CHECK:       ; %bb.0:
; CHECK-NEXT:    andi r25, 127
; CHECK-NEXT:    ret
  %a = call half @llvm.fabs.f16(half %x)
  ret half %a
}

define half @fcopysign(half %x, half %y) nounwind {
; CHECK-LABEL: fcopysign:
; CHECK:       ; %bb.0:
; CHECK-NEXT:    andi r22, 0
; CHECK-NEXT:    andi r23, 128
; CHECK-NEXT:    andi r25, 127
; CHECK-NEXT:    or r24, r22
; CHECK-NEXT:    or r25, r23
; CHECK-NEXT:    ret
  %a = call half @llvm.copysign.f16(half %x, half %y)
  ret half %a
}
