| // RUN: %target-swift-frontend -O -emit-sil %s | %FileCheck %s |
| // RUN: %target-swift-frontend -O -wmo -emit-sil %s | %FileCheck -check-prefix=CHECK-WMO %s |
| |
| // Check that values of internal and private global variables, which are provably assigned only |
| // once, are propagated into their uses and enable further optimizations like constant |
| // propagation, simplifications, etc. |
| |
| // Define some global variables. |
| |
| public var VD = 3.1415 |
| public var VI = 100 |
| |
| private var PVD = 3.1415 |
| private var PVI = 100 |
| private var PVIAssignTwice = 1 |
| private var PVITakenAddress = 1 |
| |
| |
| internal var IVD = 3.1415 |
| internal var IVI = 100 |
| internal var IVIAssignTwice = 1 |
| internal var IVITakenAddress = 1 |
| |
| // Taking the address of a global should prevent from performing the propagation of its value. |
| @inline(never) |
| @_optimize(none) |
| public func takeInout<T>(_ x: inout T) { |
| } |
| |
| // Compiler should detect that we assign a global here as well and prevent a global optimization. |
| public func assignSecondTime() { |
| PVIAssignTwice = 2 |
| IVIAssignTwice = 2 |
| } |
| |
| // Having multiple assignments to a global should prevent from performing the propagation of its value. |
| |
| // Loads from private global variables can be removed, |
| // because they cannot be changed outside of this source file. |
| // CHECK-LABEL: sil [noinline] @$S28globalopt_global_propagation013test_private_B11_var_doubleSdyF |
| // CHECK: bb0: |
| // CHECK-NOT: global_addr |
| // CHECK: float_literal |
| // CHECK: struct |
| // CHECK: return |
| @inline(never) |
| public func test_private_global_var_double() -> Double { |
| return PVD + 1.0 |
| } |
| |
| // Loads from private global variables can be removed, |
| // because they cannot be changed outside of this source file. |
| // CHECK-LABEL: sil [noinline] @$S28globalopt_global_propagation013test_private_B8_var_intSiyF |
| // CHECK: bb0: |
| // CHECK-NOT: global_addr |
| // CHECK: integer_literal |
| // CHECK: struct |
| // CHECK: return |
| @inline(never) |
| public func test_private_global_var_int() -> Int { |
| return PVI + 1 |
| } |
| |
| // Loads from internal global variables can be removed if this is a WMO compilation, because |
| // they cannot be changed outside of this module. |
| // CHECK-WMO-LABEL: sil [noinline] @$S28globalopt_global_propagation014test_internal_B11_var_doubleSdyF |
| // CHECK-WMO: bb0: |
| // CHECK-WMO-NOT: global_addr |
| // CHECK-WMO: float_literal |
| // CHECK-WMO: struct |
| // CHECK-WMO: return |
| @inline(never) |
| public func test_internal_global_var_double() -> Double { |
| return IVD + 1.0 |
| } |
| |
| // Loads from internal global variables can be removed if this is a WMO compilation, because |
| // they cannot be changed outside of this module. |
| // CHECK-WMO-LABEL: sil [noinline] @$S28globalopt_global_propagation014test_internal_B8_var_intSiyF |
| // CHECK_WMO: bb0: |
| // CHECK-WMO-NOT: global_addr |
| // CHECK-WMO: integer_literal |
| // CHECK-WMO: struct |
| // CHECK_WMO: return |
| @inline(never) |
| public func test_internal_global_var_int() -> Int { |
| return IVI + 1 |
| } |
| |
| // Loads from public global variables cannot be removed, because their values could be changed elsewhere. |
| // CHECK-WMO-LABEL: sil [noinline] @$S28globalopt_global_propagation012test_public_B11_var_doubleSdyF |
| // CHECK-WMO: bb0: |
| // CHECK-WMO-NEXT: global_addr |
| // CHECK-WMO-NEXT: struct_element_addr |
| // CHECK-WMO-NEXT: load |
| @inline(never) |
| public func test_public_global_var_double() -> Double { |
| return VD + 1.0 |
| } |
| |
| |
| // Loads from public global variables cannot be removed, because their values could be changed elsewhere. |
| // CHECK-LABEL: sil [noinline] @$S28globalopt_global_propagation012test_public_B8_var_intSiyF |
| // CHECK: bb0: |
| // CHECK-NEXT: global_addr |
| // CHECK-NEXT: struct_element_addr |
| // CHECK-NEXT: load |
| @inline(never) |
| public func test_public_global_var_int() -> Int { |
| return VI + 1 |
| } |
| |
| // Values of globals cannot be propagated as there are multiple assignments to it. |
| // CHECK-WMO-LABEL: sil [noinline] @$S28globalopt_global_propagation026test_internal_and_private_B25_var_with_two_assignmentsSiyF |
| // CHECK-WMO: bb0: |
| // CHECK-WMO: global_addr |
| // CHECK-WMO: global_addr |
| // CHECK-WMO: struct_element_addr |
| // CHECK-WMO: load |
| // CHECK-WMO: struct_element_addr |
| // CHECK-WMO: load |
| // CHECK-WMO: return |
| @inline(never) |
| public func test_internal_and_private_global_var_with_two_assignments() -> Int { |
| return IVIAssignTwice + PVIAssignTwice |
| } |
| |
| // Values of globals cannot be propagated as their address was taken and |
| // therefore their value could have been changed elsewhere. |
| // CHECK-WMO-LABEL: sil @$S28globalopt_global_propagation05test_B13_take_addressSiyF |
| // CHECK-WMO: bb0: |
| // CHECK-WMO: global_addr |
| // CHECK-WMO: global_addr |
| // CHECK-WMO: struct_element_addr |
| // CHECK-WMO: load |
| // CHECK-WMO: struct_element_addr |
| // CHECK-WMO: load |
| // CHECK-WMO: return |
| public func test_global_take_address() -> Int { |
| takeInout(&PVITakenAddress) |
| takeInout(&IVITakenAddress) |
| return IVITakenAddress + PVITakenAddress |
| } |
| |
| struct IntWrapper1 { |
| let val: Int |
| } |
| |
| struct IntWrapper2 { |
| let val: IntWrapper1 |
| } |
| |
| struct IntWrapper3 { |
| let val: IntWrapper2 |
| } |
| |
| struct IntWrapper4 { |
| let val: IntWrapper2 |
| let val2: IntWrapper1 |
| } |
| |
| let IW3 = IntWrapper3(val: IntWrapper2(val: IntWrapper1(val: 10))) |
| |
| |
| let IW4 = IntWrapper4(val: IntWrapper2(val: IntWrapper1(val: 10)), val2: IntWrapper1(val: 100)) |
| |
| // Test accessing single Int wrapped into multiple structs, where each struct has only one field. |
| // CHECK-LABEL: sil [noinline] @$S28globalopt_global_propagation34test_let_struct_wrapped_single_intSiyF |
| // CHECK: bb0: |
| // CHECK-NOT: global_addr |
| // CHECK: integer_literal |
| // CHECK: struct |
| // CHECK: return |
| |
| // CHECK-WMO-LABEL: sil [noinline] @$S28globalopt_global_propagation34test_let_struct_wrapped_single_intSiyF |
| // CHECK-WMO: bb0: |
| // CHECK-WMO-NOT: global_addr |
| // CHECK-WMO: integer_literal |
| // CHECK-WMO: struct |
| // CHECK-WMO: return |
| @inline(never) |
| public func test_let_struct_wrapped_single_int() -> Int { |
| return 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] @$S28globalopt_global_propagation37test_let_struct_wrapped_multiple_intsSiyF |
| // CHECK: bb0: |
| // CHECK-NOT: global_addr |
| // CHECK: integer_literal |
| // CHECK: struct |
| // CHECK: return |
| |
| // CHECK-WMO-LABEL: sil [noinline] @$S28globalopt_global_propagation37test_let_struct_wrapped_multiple_intsSiyF |
| // CHECK-WMO: bb0: |
| // CHECK-WMO-NOT: global_addr |
| // CHECK-WMO: integer_literal |
| // CHECK-WMO: struct |
| // CHECK-WMO: return |
| @inline(never) |
| public func test_let_struct_wrapped_multiple_ints() -> Int { |
| return IW4.val.val.val + IW4.val2.val + 1 |
| } |
| |
| |
| let IT1 = ((10, 20), 30, 40) |
| |
| let IT2 = (100, 200, 300) |
| |
| // Test accessing multiple Int fields wrapped into multiple tuples, where each tuple may have |
| // multiple fields. |
| // CHECK-LABEL: sil [noinline] @$S28globalopt_global_propagation27test_let_tuple_wrapped_intsSiyF |
| // CHECK: bb0: |
| // CHECK-NOT: global_addr |
| // CHECK: integer_literal |
| // CHECK: struct |
| // CHECK: return |
| |
| // CHECK-WMO-LABEL: sil [noinline] @$S28globalopt_global_propagation27test_let_tuple_wrapped_intsSiyF |
| // CHECK-WMO: bb0: |
| // CHECK-WMO-NOT: global_addr |
| // CHECK-WMO: integer_literal |
| // CHECK-WMO: struct |
| // CHECK-WMO: return |
| @inline(never) |
| public func test_let_tuple_wrapped_ints() -> Int { |
| return IT1.0.0 + IT2.1 |
| } |