// RUN: %target-swift-frontend -assume-parsing-unqualified-ownership-sil -emit-ir -primary-file %s -module-name test_v7k | %FileCheck %s
// RUN: %target-swift-frontend -assume-parsing-unqualified-ownership-sil -S -primary-file %s -module-name test_v7k | %FileCheck -check-prefix=V7K %s

// REQUIRES: CPU=armv7k
// REQUIRES: OS=watchos

// CHECK-LABEL: define hidden swiftcc float @_T08test_v7k9addFloats{{.*}}(float, float)
// CHECK: fadd float %0, %1
// CHECK ret float
// V7K-LABEL: __T08test_v7k9addFloats{{.*}}
// V7K: vadd.f32 s0, s0, s1
func addFloats(x: Float, y : Float) -> Float {
  return x+y
}

// CHECK-LABEL: define hidden swiftcc double @_T08test_v7k10addDoubles{{.*}}(double, double, double)
// CHECK: fadd double %0, %1
// CHECK: fadd double
// CHECK: ret double
// V7K-LABEL: __T08test_v7k10addDoubles
// V7K: vadd.f64 d0, d0, d1
// V7K: vadd.f64 d0, d0, d2
func addDoubles(x: Double, y: Double, z: Double) -> Double {
  return x+y+z
}

// CHECK-LABEL: define hidden swiftcc float @_T08test_v7k6addFDF{{.*}}(float, double, float)
// CHECK: fmul float
// CHECK: ret float
// V7K-LABEL: __T08test_v7k6addFDF
// V7K: vmul.f32 s0, s0, s1
// z is back-filled to s1
func addFDF(x: Float, y: Double, z: Float) -> Float {
  return x*z
}

// CHECK-LABEL: define hidden swiftcc double @_T08test_v7k8addStackS{{.*}}(double, double, double, double, double, double, double, float, double)
// CHECK: fadd double
// CHECK: ret double
// V7K-LABEL: __T08test_v7k8addStackS
// V7K: vldr d16, [sp]
// V7K: vadd.f64 d0, d6, d16
// a is assigned to d6, c is passed via stack
func addStack(d0: Double, d1: Double, d2: Double, d3: Double, d4: Double,
              d5: Double, a: Double, b: Float, c: Double) -> Double {
  return a+c
}

// CHECK-LABEL: define hidden swiftcc float @_T08test_v7k9addStack2{{.*}}(double, double, double, double, double, double, double, float, double, float)
// CHECK: fadd float
// V7K-LABEL: __T08test_v7k9addStack2
// V7K: vldr s0, [sp, #8]
// V7K: vadd.f32 s0, s14, s0
// a is assigned to s14, b is via stack, c is via stack since it can't be back-filled to s15
func addStack2(d0: Double, d1: Double, d2: Double, d3: Double, d4: Double, 
               d5: Double, d6: Double, a: Float, b: Double, c: Float) -> Float {
  return a+c
}

// Passing various enums:
// CHECK-LABEL: define hidden swiftcc void @_T08test_v7k0A5Empty{{.*}}()
// V7K-LABEL: __T08test_v7k0A5Empty
enum Empty {}
func testEmpty(x: Empty) -> Empty {
  return x
}

// CHECK-LABEL: define hidden swiftcc i32 @_T08test_v7k0A6Single{{.*}}()
// CHECK: ret i32 1
// V7K-LABEL: __T08test_v7k0A6Single
// V7K: movw    r0, #1
enum SingleCase { case X }
func testSingle(x: SingleCase) -> Int32{
  switch x {
  case SingleCase.X:
    return 1
  }
}

// CHECK-LABEL: define hidden swiftcc double @_T08test_v7k0A4Data{{.*}}(i32, double)
// CHECK: ret double
// V7K-LABEL: __T08test_v7k0A4Data
// V7K: vstr d0
// V7K: vmov.f64 d0
enum DataCase { case Y(Int, Double) }
func testData(x: DataCase) -> Double {
  switch x {
  case let .Y(i, d):
    return d
  }
}

// CHECK-LABEL: define hidden swiftcc i32 @_T08test_v7k0A6Clike2{{.*}}(i8)
// CHECK: [[ID:%[0-9]+]] = phi i32 [ 2, {{.*}} ], [ 1, {{.*}} ]
// CHECK: ret i32 [[ID]]
// V7K-LABEL: __T08test_v7k0A6Clike2
// V7K: tst r0, #1
// V7K: movw r0, #1
// V7K: movw r0, #2
enum CLike2 {
  case A
  case B
}
func testClike2(x: CLike2) -> Int {
  switch x {
  case CLike2.A:
    return 1
  case CLike2.B:
    return 2
  }
}

// CHECK-LABEL: define hidden swiftcc i32 @_T08test_v7k0A6Clike8{{.*}}(i32, i8)
// CHECK: [[ID:%[0-9]+]] = phi i32 [ -1, {{.*}} ], [ 1, {{.*}} ]
// CHECK: ret i32 [[ID]]
// V7K-LABEL: __T08test_v7k0A6Clike8
// V7K: sxtb r1, r1
// V7K: cmp r1, #0
// V7K: movw r0, #1
// V7K: mvn r0, #0
enum CLike8 {
  case A
  case B
  case C
  case D
  case E
  case F
  case G
  case H
}
func testClike8(t: Int, x: CLike8) -> Int {
  switch x {
  case CLike8.A:
    return 1
  default:
    return -1
  }
}

// layout of the enum: the tag bit is set for the no-data cases, which are then
// assigned values in the data area of the enum in declaration order
// CHECK-LABEL: define hidden swiftcc double @_T08test_v7k0A7SingleP{{.*}}(i32, i32, i8)
// CHECK: br i1
// CHECK: switch i32 [[ID:%[0-9]+]]
// CHECK: [[FIRST:%[0-9]+]] = zext i32 %0 to i64
// CHECK: [[SECOND:%[0-9]+]] = zext i32 %1 to i64
// CHECK: [[TEMP:%[0-9]+]] = shl i64 [[SECOND]], 32
// CHECK: [[RESULT:%[0-9]+]] = or i64 [[FIRST]], [[TEMP]]
// CHECK: bitcast i64 [[RESULT]] to double
// CHECK: phi double [ 0.000000e+00, {{.*}} ]
// V7K-LABEL: __T08test_v7k0A7SingleP
// V7K: tst     r2, #1
// V7K: vmov.f64 d0
enum SinglePayload {
  case Paragraph
  case Char(Double)
  case Chapter
}
func testSingleP(x: SinglePayload) -> Double {
  switch x {
  case let .Char(d):
    return d
  default:
    return 0.0
  }
}

// CHECK-LABEL: define hidden swiftcc double @_T08test_v7k0A6MultiP{{.*}}(i32, i32, i8)
// CHECK: [[FIRST:%[0-9]+]] = zext i32 %0 to i64
// CHECK: [[SECOND:%[0-9]+]] = zext i32 %1 to i64
// CHECK: [[TEMP:%[0-9]+]] = shl i64 [[SECOND]], 32
// CHECK: [[RESULT:%[0-9]+]] = or i64 [[FIRST]], [[TEMP]]
// CHECK: bitcast i64 [[RESULT]] to double
// CHECK: sitofp i32 {{.*}} to double 
// CHECK: phi double [ 0.000000e+00, {{.*}} ]
// CHECK: ret double
// V7K-LABEL: __T08test_v7k0A6MultiP
// V7K: vldr d0
// Backend will assign r0, r1 and r2 for input parameters and d0 for return values.
class Bignum {}
enum MultiPayload {
  case X(Int)
  case Y(Double)
  case Z(Bignum)
}
func testMultiP(x: MultiPayload) -> Double {
  switch x {
  case let .X(i):
    return Double(i)
  case let .Y(d):
    return d
  default:
    return 0.0
  }
}

// CHECK-LABEL: define hidden swiftcc float @_T08test_v7k0A3Opt{{.*}}(i32, i8)
// CHECK: entry:
// CHECK: [[TR:%.*]] = trunc i8 %1
// CHECK: br i1 [[TR]], {{.*}}, label %[[PAYLOADLABEL:.*]]
// CHECK: <label>:[[PAYLOADLABEL]]
// CHECK: [[ID:%[0-9]+]] = bitcast i32 %0 to float
// CHECK: ret float
// V7K-LABEL: __T08test_v7k0A3Opt
// V7K:         tst     r1, #1
// V7K:         str     r0, [r7, [[SLOT:#-[0-9]+]]
// V7K:         ldr     r0, [r7, [[SLOT]]
// V7K:         vmov    s0, r0
// V7K:         vstr    s0, [r7, [[SLOT2:#-[0-9]+]]
// V7K:         vldr    s0, [r7, [[SLOT2]]
// V7K:         pop     {{{.*}}, pc}
func testOpt(x: Float?) -> Float {
  return x!
}

// Returning tuple: (Int, Int)
// CHECK-LABEL: define hidden swiftcc { i32, i32 } @_T08test_v7k6minMaxS{{.*}}(i32, i32)
// V7K-LABEL: __T08test_v7k6minMaxS
// V7K: ldr r0
// V7K: ldr r1
func minMax(x : Int, y : Int) -> (min: Int, max: Int) {
    var currentMin = x
    var currentMax = y
    if y < x {
      currentMin = y
      currentMax = x
    }
    return (currentMin, currentMax)
}

// Returning struct: Double x 4; Int8, Double, Double;
struct MyRect {
  var x : Double
  var y : Double
  var w : Double
  var h : Double
}
struct MyPoint {
var x: Double
var y: Double
}

struct MySize {
var w: Double
var h: Double
}
struct MyRect2 {
  var t: Int8
  var p : MyPoint
  init() {
    t = 1
    p = MyPoint(x : 0.0, y: 0.0)
  }
}
struct MyRect4 {
  var t: Int8
  var p : MyPoint
  var s: MySize
  init() {
   t = 1
   p = MyPoint(x : 0.0, y: 0.0)
   s = MySize(w: 1.0, h: 2.0)
  }
}
// CHECK-LABEL: define hidden swiftcc { double, double, double, double } @_T08test_v7k0A4Ret2{{.*}}(double, i32)
// V7K-LABEL: __T08test_v7k0A4Ret2
// double in d0, i32 in r0, return in d0,...,d3
// V7K: vmov [[ID:s[0-9]+]], r0
// V7K: vcvt.f64.s32 [[ID2:d[0-9]+]], [[ID]]
// V7K: vstr d0, [sp]
// V7K: vmov.f64 d0, [[ID2]]
// V7K: bl
// V7K: vldr [[ID3:d[0-9]+]], [sp]
// V7K: vmov.f64 d2, [[ID3]]
func testRet2(w : Double, i : Int) -> MyRect {
  var r = MyRect(x : Double(i), y : 2.0, w : 3.0, h : 4.0)
  r.w = w
  return r
}
// CHECK-LABEL: define hidden swiftcc { i8, double, double } @_T08test_v7k0A4Ret3{{.*}}()
// V7K-LABEL: __T08test_v7k0A4Ret3
func testRet3() -> MyRect2 {
  var r = MyRect2()
  return r
}

// Returning tuple?: (Int x 6)?
// CHECK-LABEL: define hidden swiftcc void @_T08test_v7k7minMax2{{.*}}({{%TSi.*}} noalias nocapture sret, i32, i32)
// V7K-LABEL: __T08test_v7k7minMax2
// We will indirectly return an optional with the address in r0, input parameters will be in r1 and r2
// V7K: cmp r1, r2
// V7K: str r0, [sp, [[IDX:#[0-9]+]]]
// V7K: ldr [[R0_RELOAD:r[0-9]+]], [sp, [[IDX]]]
// V7K: str {{.*}}, [{{.*}}[[R0_RELOAD]]]
// V7K: str {{.*}}, [{{.*}}[[R0_RELOAD]], #4]
// V7K: str {{.*}}, [{{.*}}[[R0_RELOAD]], #8]
// V7K: str {{.*}}, [{{.*}}[[R0_RELOAD]], #12]
// V7K: str {{.*}}, [{{.*}}[[R0_RELOAD]], #16]
// V7K: str {{.*}}, [{{.*}}[[R0_RELOAD]], #20]
// V7K: and {{.*}}, {{.*}}, #1
// V7K: strb {{.*}}, [{{.*}}[[R0_RELOAD]], #24]
func minMax2(x : Int, y : Int) -> (min: Int, max: Int, min2: Int, max2: Int, min3: Int, max3: Int)? {
    if x == y {
      return nil
    }
    var currentMin = x
    var currentMax = y
    if y < x {
      currentMin = y
      currentMax = x
    }
    return (currentMin, currentMax, currentMin, currentMax, currentMin, currentMax)
}

// Returning struct?: {Int x 6}?
// CHECK-LABEL: define hidden swiftcc void @_T08test_v7k7minMax3{{.*}}({{%T.*}} noalias nocapture sret, i32, i32)
// V7K-LABEL: __T08test_v7k7minMax3
struct Ret {
  var min:Int
  var max:Int
  var min2, max2 : Int
  var min3, max3 : Int
}
func minMax3(x : Int, y : Int) -> Ret? {
    if x == y {
      return nil
    }
    var currentMin = x
    var currentMax = y
    if y < x {
      currentMin = y
      currentMax = x
    }
    var r = Ret(min:currentMin, max:currentMax, min2:currentMin, max2:currentMax, min3:currentMin, max3:currentMax)
    return r
}

// Passing struct: Int8, MyPoint x 10, MySize * 10
// CHECK-LABEL: define hidden swiftcc double @_T08test_v7k0A4Ret5{{.*}}(%T8test_v7k7MyRect3V* noalias nocapture dereferenceable(328))
// V7K-LABEL: __T08test_v7k0A4Ret5
// V7K:   ldrb    r1, [r0]
// V7K:   vldr    d16, [r0, #8]
// V7K:   add     r0, r0, #296
// V7K:   vldr    d18, [r0]
// V7K:   sxtb    r0, r1
// V7K:   vmov    s0, r0
// V7K:   vcvt.f64.s32    d20, s0
// V7K:   vadd.f64        d16, d20, d16
// V7K:   vadd.f64        d0, d16, d18
// V7K:   bx      lr

struct MyRect3 {
  var t: Int8
  var p: MyPoint
  var p2: MyPoint
  var s: MySize
  var s2: MySize
  var p3: MyPoint
  var p4: MyPoint
  var s3: MySize
  var s4: MySize
  var p5: MyPoint
  var p6: MyPoint
  var s5: MySize
  var s6: MySize
  var p7: MyPoint
  var p8: MyPoint
  var s7: MySize
  var s8: MySize
  var p9: MyPoint
  var p10: MyPoint
  var s9: MySize
  var s10: MySize
}
func testRet5(r: MyRect3) -> Double {
  return Double(r.t) + r.p.x + r.s9.w
}
