// RUN: %target-swift-frontend  -O -emit-sil -primary-file %s | %FileCheck %s

// Check that values of static let and global let variables are propagated into their uses
// and enable further optimizations like constant propagation, simplifications, etc.

// Define some global let variables.

// Currently GlobalOpt cannot deal with the new alloc_global instruction.

let PI = 3.1415
let ONE = 1.000
let I = 100
let J = 200
let S = "String1"

let VOLUME1 = I * J
let VOLUME2 = J * 2
let VOLUME3 = I + 10
 


struct IntWrapper1 {
  let val: Int
}

struct IntWrapper2 {
  let val: IntWrapper1
}

struct IntWrapper3 {
  let val: IntWrapper2
}

struct IntWrapper4 {
  let val:  IntWrapper2
  let val2: IntWrapper1
}

// Test with an initializer, where a SIL debug_value instruction might block
// analysis of the initializer and inhibit optimization of the let.
struct IntWrapper5 {
  let val: Int
  init(val: Int) { self.val = val }
  static let Five = IntWrapper5(val: 5)
}

var PROP1: Double {
   return PI
}

var PROP2: Int {
   return I * J - I
}

var VPI = 3.1415
var VI = 100
var VS = "String2"

// Define some static let variables inside a struct.
struct B {
 static let PI = 3.1415

 static let ONE = 1.000
 
 static let I = 100
 
 static let J = 200
 
 static let S1 = "String3"

 static let VOLUME1 = I * J

 static let VOLUME2 = J * 2

 static let VOLUME3 = I + 10
 
 static var PROP1: Double { 
   return PI
 }
 
 static var PROP2: Int { 
   return I * J - I
 }

 static func foo() {}

 static let IW3 = IntWrapper3(val: IntWrapper2(val: IntWrapper1(val: 10)))

 static let IW4 = IntWrapper4(val: IntWrapper2(val: IntWrapper1(val: 10)), val2: IntWrapper1(val: 100))

 static let IT1 = ((10, 20), 30, 40)

 static let IT2 = (100, 200, 300)
}

// Define some static let variables inside a class.
class C {
 static let PI = 3.1415

 static let ONE = 1.000
 
 static let I = 100
 
 static let J = 200
 
 static let S1 = "String3"

 static let VOLUME1 = I * J

 static let VOLUME2 = J * 2

 static let VOLUME3 = I + 10
 
 static var PROP1: Double { 
   return PI
 }
 
 static var PROP2: Int { 
   return I * J - I
 }

 static func foo() {}

 static let IW3 = IntWrapper3(val: IntWrapper2(val: IntWrapper1(val: 10)))

 static let IW4 = IntWrapper4(val: IntWrapper2(val: IntWrapper1(val: 10)), val2: IntWrapper1(val: 100))

 static let IT1 = ((10, 20), 30, 40)

 static let IT2 = (100, 200, 300)
}

// CHECK-LABEL: sil [noinline] @_T025globalopt_let_propagation05test_B7_doubleSdyF
// CHECK: bb0:
// CHECK-NEXT: float_literal
// CHECK-NEXT: struct
// CHECK: return
@inline(never)
public func test_let_double() -> Double {
  return PI + 1.0
}

// CHECK-LABEL: sil [noinline] @_T025globalopt_let_propagation05test_B4_intSiyF
// CHECK: bb0:
// CHECK-NEXT: integer_literal
// CHECK-NEXT: struct
// CHECK: return
@inline(never)
public func test_let_int() -> Int {
  return I + 1
}

@inline(never)
public func test_let_string() -> String {
  return S
}

// CHECK-LABEL: sil [noinline] @_T025globalopt_let_propagation05test_B15_double_complexSdyF
// CHECK: bb0:
// CHECK-NEXT: float_literal
// CHECK-NEXT: struct
// CHECK: return
@inline(never)
public func test_let_double_complex() -> Double {
  return PI + ONE + PROP1
}

// CHECK-LABEL: sil [noinline] @_T025globalopt_let_propagation05test_B12_int_complexSiyF
// CHECK: bb0:
// CHECK-NEXT: integer_literal
// CHECK-NEXT: struct
// CHECK: return
@inline(never)
public func test_let_int_complex() -> Int {
  return I + J + VOLUME1 + VOLUME2 + VOLUME3 + PROP2
}

// CHECK-LABEL: sil [noinline] @_T025globalopt_let_propagation019test_static_struct_B7_doubleSdyF
// CHECK: bb0:
// CHECK-NEXT: float_literal
// CHECK-NEXT: struct
// CHECK: return
@inline(never)
public func test_static_struct_let_double() -> Double {
  return B.PI + 1.0
}

// CHECK-LABEL: sil [noinline] @_T025globalopt_let_propagation019test_static_struct_B4_intSiyF
// CHECK: bb0:
// CHECK-NEXT: integer_literal
// CHECK-NEXT: struct
// CHECK: return
@inline(never)
public func test_static_struct_let_int() -> Int {
  return B.I + 1
}

@inline(never)
public func test_static_struct_let_string() -> String {
  return B.S1
}


// CHECK-LABEL: sil [noinline] @_T025globalopt_let_propagation019test_static_struct_B15_double_complexSdyF
// CHECK: bb0:
// CHECK-NEXT: float_literal
// CHECK-NEXT: struct
// CHECK: return
@inline(never)
public func test_static_struct_let_double_complex() -> Double {
  return B.PI + B.ONE + B.PROP1
}

// CHECK-LABEL: sil [noinline] @_T025globalopt_let_propagation019test_static_struct_B12_int_complexSiyF
// CHECK: bb0:
// CHECK-NEXT: integer_literal
// CHECK-NEXT: struct
// CHECK: return
@inline(never)
public func test_static_struct_let_int_complex() -> Int {
  return B.I + B.J + B.VOLUME1 + B.VOLUME2 + B.VOLUME3 + B.PROP2
}


// CHECK-LABEL: sil [noinline] @_T025globalopt_let_propagation018test_static_class_B7_doubleSdyF
// CHECK: bb0:
// CHECK-NEXT: float_literal
// CHECK-NEXT: struct
// CHECK: return
@inline(never)
public func test_static_class_let_double() -> Double {
  return C.PI + 1.0
}

// CHECK-LABEL: sil [noinline] @_T025globalopt_let_propagation018test_static_class_B4_intSiyF
// CHECK: bb0:
// CHECK-NEXT: integer_literal
// CHECK-NEXT: struct
// CHECK: return
@inline(never)
public func test_static_class_let_int() -> Int {
  return C.I + 1
}

@inline(never)
public func test_static_class_let_string() -> String {
  return C.S1
}


// CHECK-LABEL: sil [noinline] @_T025globalopt_let_propagation018test_static_class_B15_double_complexSdyF
// CHECK: bb0:
// CHECK-NEXT: float_literal
// CHECK-NEXT: struct
// CHECK: return
@inline(never)
public func test_static_class_let_double_complex() -> Double {
  return C.PI + C.ONE + C.PROP1
}

// CHECK-LABEL: sil [noinline] @_T025globalopt_let_propagation018test_static_class_B12_int_complexSiyF
// CHECK: bb0:
// CHECK-NEXT: integer_literal
// CHECK-NEXT: struct
// CHECK: return
@inline(never)
public func test_static_class_let_int_complex() -> Int {
  return C.I + C.J + C.VOLUME1 + C.VOLUME2 + C.VOLUME3 + C.PROP2
}

// CHECK-LABEL: sil [noinline] @_T025globalopt_let_propagation15test_var_doubleSdyF
// CHECK: bb0:
// CHECK-NEXT: global_addr
// CHECK-NEXT: struct_element_addr
// CHECK-NEXT: load
@inline(never)
public func test_var_double() -> Double {
  return VPI + 1.0 
}

// CHECK-LABEL: sil [noinline] @_T025globalopt_let_propagation12test_var_intSiyF
// CHECK: bb0: 
// CHECK-NEXT: global_addr
// CHECK-NEXT: struct_element_addr
// CHECK-NEXT: load
@inline(never)
public func test_var_int() -> Int {
  return VI + 1
}

// CHECK-LABEL: sil [noinline] @_T025globalopt_let_propagation018test_static_class_B12_wrapped_intSiyF
// CHECK: bb0:
// CHECK-NEXT: integer_literal
// CHECK-NEXT: struct
// CHECK: return
@inline(never)
public func test_static_class_let_wrapped_int() -> Int {
  return C.IW3.val.val.val + 1
}

// CHECK-LABEL: sil [noinline] @_T025globalopt_let_propagation019test_static_struct_B12_wrapped_intSiyF
// CHECK: bb0:
// CHECK-NEXT: integer_literal
// CHECK-NEXT: struct
// CHECK: return
@inline(never)
public func test_static_struct_let_wrapped_int() -> Int {
  return B.IW3.val.val.val + 1
}

// Test accessing multiple Int fields wrapped into multiple structs, where each struct may have
// multiple fields.
// CHECK-LABEL: sil [noinline] @_T025globalopt_let_propagation019test_static_struct_b1_F22_wrapped_multiple_intsSiyF
// CHECK: bb0:
// CHECK-NOT: global_addr
// CHECK: integer_literal
// CHECK-NOT: global_addr
// CHECK: struct
// CHECK: return
@inline(never)
public func test_static_struct_let_struct_wrapped_multiple_ints() -> Int {
  return B.IW4.val.val.val + B.IW4.val2.val + IntWrapper5.Five.val + 1
}

// Test accessing multiple Int fields wrapped into multiple structs, where each struct may have
// multiple fields.
// CHECK-LABEL: sil [noinline] @_T025globalopt_let_propagation018test_static_class_B29_struct_wrapped_multiple_intsSiyF
// CHECK: bb0:
// CHECK-NOT: global_addr
// CHECK: integer_literal
// CHECK-NOT: global_addr
// CHECK: struct
// CHECK: return
@inline(never)
public func test_static_class_let_struct_wrapped_multiple_ints() -> Int {
  return C.IW4.val.val.val + C.IW4.val2.val + IntWrapper5.Five.val + 1
}

// Test accessing multiple Int fields wrapped into multiple tuples, where each tuple may have
// multiple fields.
// CHECK-LABEL: sil [noinline] @_T025globalopt_let_propagation019test_static_struct_B19_tuple_wrapped_intsSiyF
// CHECK: bb0:
// CHECK-NOT: global_addr
// CHECK: integer_literal
// CHECK: struct
// CHECK: return
@inline(never)
public func test_static_struct_let_tuple_wrapped_ints() -> Int {
  return B.IT1.0.0 + B.IT2.1
}

// Test accessing multiple Int fields wrapped into multiple tuples, where each tuple may have
// multiple fields.
// CHECK-LABEL: sil [noinline] @_T025globalopt_let_propagation018test_static_class_B19_tuple_wrapped_intsSiyF
// CHECK: bb0:
// CHECK-NOT: global_addr
// CHECK: integer_literal
// CHECK: struct
// CHECK: return
@inline(never)
public func test_static_class_let_tuple_wrapped_ints() -> Int {
  return C.IT1.0.0 + C.IT2.1
}
