// RUN: %target-sil-opt -enforce-exclusivity=none -enable-sil-verify-all -cowarray-opt %s | %FileCheck %s // Declare this SIL to be canonical because some tests break raw SIL // conventions. e.g. address-type block args. -enforce-exclusivity=none is also // required to allow address-type block args in canonical SIL. sil_stage canonical import Builtin import Swift ///////////// // Utility // ///////////// struct ArrayIntBuffer { var storage : Builtin.NativeObject } struct MyArray { var buffer : ArrayIntBuffer } struct MyStruct { } class MyArrayContainer { var array: MyArray init() deinit } struct Container { var array: MyArray } struct ContainerContainer { var container: Container } class MyArrayStorage { @_hasStorage var header: Int init() deinit } sil [_semantics "array.make_mutable"] @array_make_mutable : $@convention(method) (@inout MyArray) -> () sil [_semantics "array.get_count"] @guaranteed_array_get_count : $@convention(method) (@guaranteed MyArray) -> Int sil [_semantics "array.get_capacity"] @guaranteed_array_get_capacity : $@convention(method) (@guaranteed MyArray) -> Int sil [_semantics "array.mutate_unknown"] @array_unknown_mutate : $@convention(method) (@inout MyArray) -> () // An unknown user sil @unknown : $@convention(thin) () -> () /////////// // Tests // /////////// // CHECK-LABEL: sil @simple_hoist // CHECK: bb0([[ARRAY:%[0-9]+]] // CHECK: [[FUN:%[0-9]+]] = function_ref @array_make_mutable // CHECK: apply [[FUN]]([[ARRAY]] // CHECK: bb1 // CHECK-NOT: array_make_mutable // CHECK-NOT: apply [[FUN]] sil @simple_hoist : $@convention(thin) (@inout MyArray, @inout Builtin.Int1) -> () { bb0(%0 : $*MyArray, %1 : $*Builtin.Int1): debug_value_addr %0 : $*MyArray %2 = load %0 : $*MyArray br bb1 bb1: %5 = function_ref @array_make_mutable : $@convention(method) (@inout MyArray) -> () %6 = apply %5(%0) : $@convention(method) (@inout MyArray) -> () cond_br undef, bb1, bb2 bb2: %7 = tuple() return %7 : $() } // CHECK-LABEL: sil @hoist_ignoring_paired_retain_release_and_hoist // CHECK: bb0( // CHECK-NOT: br bb // CHECK: [[MM:%.*]] = function_ref @array_make_mutable // CHECK-NOT: br bb // CHECK: apply [[MM]] // CHECK: br bb1 // CHECK: bb1: // CHECK-NOT: apply // CHECK: cond_br {{.*}}, bb1 sil @hoist_ignoring_paired_retain_release_and_hoist : $@convention(thin) (@inout MyArray, @inout Builtin.Int1) -> () { bb0(%0 : $*MyArray, %1 : $*Builtin.Int1): %2 = load %0 : $*MyArray br bb1 bb1: retain_value %2 : $MyArray %3 = load %1 : $*Builtin.Int1 %4 = load %0 : $*MyArray release_value %2 : $MyArray %5 = function_ref @array_make_mutable : $@convention(method) (@inout MyArray) -> () %6 = apply %5(%0) : $@convention(method) (@inout MyArray) -> () cond_br %3, bb1, bb2 bb2: %7 = tuple() return %7 : $() } // CHECK-LABEL: sil @hoist_blocked_by_unpaired_retain_release_1 // CHECK: bb0( // CHECK-NOT: apply // CHECK: bb1: // CHECK: retain_value // CHECK: [[MM:%.*]] = function_ref @array_make_mutable // CHECK: apply [[MM]] // CHECK: cond_br {{.*}}, bb1 sil @hoist_blocked_by_unpaired_retain_release_1 : $@convention(thin) (@inout MyArray, @inout Builtin.Int1) -> () { bb0(%0 : $*MyArray, %1 : $*Builtin.Int1): %2 = load %0 : $*MyArray br bb1 bb1: retain_value %2 : $MyArray %3 = load %1 : $*Builtin.Int1 %4 = load %0 : $*MyArray %5 = function_ref @array_make_mutable : $@convention(method) (@inout MyArray) -> () %6 = apply %5(%0) : $@convention(method) (@inout MyArray) -> () cond_br %3, bb1, bb2 bb2: %7 = tuple() return %7 : $() } // CHECK-LABEL: sil @hoist_blocked_by_unpaired_retain_release_2 // CHECK: bb0( // CHECK-NOT: apply // CHECK: bb1: // CHECK: retain_value // CHECK: [[MM:%.*]] = function_ref @array_make_mutable // CHECK: apply [[MM]] // CHECK: cond_br {{.*}}, bb1 sil @hoist_blocked_by_unpaired_retain_release_2 : $@convention(thin) (@inout MyArray, @inout Builtin.Int1) -> () { bb0(%0 : $*MyArray, %1 : $*Builtin.Int1): br bb1 bb1: %10 = load %0 : $*MyArray %11 = load %0 : $*MyArray %8 = struct_extract %10 : $MyArray, #MyArray.buffer %9 = struct_extract %11 : $MyArray, #MyArray.buffer retain_value %8 : $ArrayIntBuffer retain_value %9 : $ArrayIntBuffer release_value %9 : $ArrayIntBuffer %3 = load %1 : $*Builtin.Int1 %4 = load %0 : $*MyArray %5 = function_ref @array_make_mutable : $@convention(method) (@inout MyArray) -> () %6 = apply %5(%0) : $@convention(method) (@inout MyArray) -> () cond_br %3, bb1, bb2 bb2: %7 = tuple() return %7 : $() } // CHECK-LABEL: sil @hoist_not_blocked_by_unpaired_release // CHECK: bb0( // CHECK-NOT: br bb // CHECK: [[MM:%.*]] = function_ref @array_make_mutable // CHECK-NOT: br bb // CHECK: apply [[MM]] // CHECK: br bb1 // CHECK: bb1: // CHECK-NOT: apply // CHECK: cond_br {{.*}}, bb1 sil @hoist_not_blocked_by_unpaired_release : $@convention(thin) (@inout MyArray, @inout Builtin.Int1) -> () { bb0(%0 : $*MyArray, %1 : $*Builtin.Int1): %2 = load %0 : $*MyArray br bb1 bb1: %3 = load %1 : $*Builtin.Int1 %4 = load %0 : $*MyArray release_value %2 : $MyArray %5 = function_ref @array_make_mutable : $@convention(method) (@inout MyArray) -> () %6 = apply %5(%0) : $@convention(method) (@inout MyArray) -> () cond_br %3, bb1, bb2 bb2: %7 = tuple() return %7 : $() } // CHECK-LABEL: sil @dont_hoist_if_executed_conditionally // CHECK: bb0({{.*}}): // CHECK-NOT: apply // CHECK: bb1({{.*}}): // CHECK: bb2: // CHECK: apply // CHECK: bb3: sil @dont_hoist_if_executed_conditionally : $@convention(thin) (@inout MyArray, @inout Builtin.Int1) -> MyArray { bb0(%0 : $*MyArray, %1 : $*Builtin.Int1): debug_value_addr %0 : $*MyArray %2 = load %0 : $*MyArray br bb1(%2 : $MyArray) bb1(%p1 : $MyArray): cond_br undef, bb2, bb3 bb2: // If this block is never taken, then hoisting to bb0 would change the value of %p3. %5 = function_ref @array_make_mutable : $@convention(method) (@inout MyArray) -> () %6 = apply %5(%0) : $@convention(method) (@inout MyArray) -> () %7 = load %0 : $*MyArray br bb4(%7 : $MyArray) bb3: br bb4(%p1 : $MyArray) bb4(%p2 : $MyArray): cond_br undef, bb1(%p2 : $MyArray), bb5(%p2 : $MyArray) bb5(%p3 : $MyArray): return %p3 : $MyArray } // CHECK-LABEL: sil @cow_should_ignore_mark_dependence_addrproj_use : $@convention(thin) (@inout MyArray, @inout Builtin.Int1) -> () { // CHECK: bb0( // CHECK-NOT: br bb // CHECK: [[MM:%.*]] = function_ref @array_make_mutable // CHECK-NOT: br bb // CHECK: apply [[MM]] // CHECK: br bb1 // CHECK: bb1: // CHECK-NOT: apply // CHECK: cond_br {{.*}}, bb1 sil @cow_should_ignore_mark_dependence_addrproj_use : $@convention(thin) (@inout MyArray, @inout Builtin.Int1) -> () { bb0(%0 : $*MyArray, %1 : $*Builtin.Int1): %999 = struct_element_addr %0 : $*MyArray, #MyArray.buffer %9999 = struct_element_addr %999 : $*ArrayIntBuffer, #ArrayIntBuffer.storage %99999 = load %9999 : $*Builtin.NativeObject %2 = load %0 : $*MyArray br bb1 bb1: retain_value %2 : $MyArray %3 = load %1 : $*Builtin.Int1 %4 = load %0 : $*MyArray release_value %2 : $MyArray %5 = function_ref @array_make_mutable : $@convention(method) (@inout MyArray) -> () %6 = apply %5(%0) : $@convention(method) (@inout MyArray) -> () mark_dependence %1 : $*Builtin.Int1 on %99999 : $Builtin.NativeObject cond_br %3, bb1, bb2 bb2: %7 = tuple() return %7 : $() } // CHECK-LABEL: sil @cow_should_ignore_mark_dependence_value : $@convention(thin) (@inout MyArray, @inout Builtin.Int1) -> () { // CHECK: bb0( // CHECK-NOT: br bb // CHECK: [[MM:%.*]] = function_ref @array_make_mutable // CHECK-NOT: br bb // CHECK: apply [[MM]] // CHECK: br bb1 // CHECK: bb1: // CHECK-NOT: apply // CHECK: cond_br {{.*}}, bb1 sil @cow_should_ignore_mark_dependence_value : $@convention(thin) (@inout MyArray, @inout Builtin.Int1) -> () { bb0(%0 : $*MyArray, %1 : $*Builtin.Int1): %2 = load %0 : $*MyArray br bb1 bb1: retain_value %2 : $MyArray %3 = load %1 : $*Builtin.Int1 %4 = load %0 : $*MyArray release_value %2 : $MyArray %5 = function_ref @array_make_mutable : $@convention(method) (@inout MyArray) -> () %6 = apply %5(%0) : $@convention(method) (@inout MyArray) -> () mark_dependence %1 : $*Builtin.Int1 on %2 : $MyArray cond_br %3, bb1, bb2 bb2: %7 = tuple() return %7 : $() } // CHECK-LABEL: sil @cow_should_ignore_enum : $@convention(thin) (@inout MyArray, @inout Builtin.Int1) -> () { // CHECK: bb0( // CHECK-NOT: br bb // CHECK: [[MM:%.*]] = function_ref @array_make_mutable // CHECK-NOT: br bb // CHECK: apply [[MM]] // CHECK: br bb1 // CHECK: bb1: // CHECK-NOT: apply // CHECK: cond_br {{.*}}, bb1 sil @cow_should_ignore_enum : $@convention(thin) (@inout MyArray, @inout Builtin.Int1) -> () { bb0(%0 : $*MyArray, %1 : $*Builtin.Int1): %2 = load %0 : $*MyArray br bb1 bb1: retain_value %2 : $MyArray %3 = load %1 : $*Builtin.Int1 %4 = load %0 : $*MyArray release_value %2 : $MyArray %5 = function_ref @array_make_mutable : $@convention(method) (@inout MyArray) -> () %6 = apply %5(%0) : $@convention(method) (@inout MyArray) -> () %8 = enum $Optional>, #Optional.some!enumelt, %2 : $MyArray mark_dependence %1 : $*Builtin.Int1 on %8 : $Optional> cond_br %3, bb1, bb2 bb2: %7 = tuple() return %7 : $() } // CHECK-LABEL: sil @cow_should_ignore_guaranteed_semantic_call_sequence : $@convention(thin) (@guaranteed MyArrayContainer, Builtin.NativeObject) -> () { // CHECK: bb0 // CHECK: [[F:%.*]] = function_ref @array_make_mutable : $@convention(method) (@inout MyArray) -> () // CHECK: apply [[F]]( // CHECK: bb1: // CHECK: bb2: // CHECK-NOT: apply [[F]]( // CHECK: bb3: // CHECK: bb4: // CHECK-NOT: apply [[F]]( // CHECK: bb5: // CHECK: bb6: // CHECK-NOT: apply [[F]]( // CHECK: bb7: // CHECK: bb8: // CHECK-NOT: apply [[F]]( // CHECK: bb9: sil @cow_should_ignore_guaranteed_semantic_call_sequence : $@convention(thin) (@guaranteed MyArrayContainer, Builtin.NativeObject) -> () { bb0(%0 : $MyArrayContainer, %00 : $Builtin.NativeObject): %1 = ref_element_addr %0 : $MyArrayContainer, #MyArrayContainer.array %2 = load %1 : $*MyArray %3 = function_ref @guaranteed_array_get_count : $@convention(method) (@guaranteed MyArray) -> Int %4 = function_ref @guaranteed_array_get_capacity : $@convention(method) (@guaranteed MyArray) -> Int %5 = function_ref @unknown : $@convention(thin) () -> () %6 = function_ref @array_make_mutable : $@convention(method) (@inout MyArray) -> () br bb1 bb1: // Simple case. This should hoist. retain_value %2 : $MyArray apply %3(%2) : $@convention(method) (@guaranteed MyArray) -> Int apply %4(%2) : $@convention(method) (@guaranteed MyArray) -> Int release_value %2 : $MyArray apply %6(%1) : $@convention(method) (@inout MyArray) -> () cond_br undef, bb1, bb2 bb2: br bb3 bb3: // Failure case b/c of use in between release and call. retain_value %2 : $MyArray apply %3(%2) : $@convention(method) (@guaranteed MyArray) -> Int apply %4(%2) : $@convention(method) (@guaranteed MyArray) -> Int fix_lifetime %0 : $MyArrayContainer release_value %2 : $MyArray apply %6(%1) : $@convention(method) (@inout MyArray) -> () cond_br undef, bb4, bb3 bb4: br bb5 bb5: // Failure case b/c of use in between calls. retain_value %2 : $MyArray apply %3(%2) : $@convention(method) (@guaranteed MyArray) -> Int fix_lifetime %0 : $MyArrayContainer apply %4(%2) : $@convention(method) (@guaranteed MyArray) -> Int release_value %2 : $MyArray apply %6(%1) : $@convention(method) (@inout MyArray) -> () cond_br undef, bb5, bb6 bb6: br bb7 bb7: // Failure b/c use is in between apply and retain. retain_value %2 : $MyArray fix_lifetime %0 : $MyArrayContainer apply %3(%2) : $@convention(method) (@guaranteed MyArray) -> Int apply %4(%2) : $@convention(method) (@guaranteed MyArray) -> Int release_value %2 : $MyArray apply %6(%1) : $@convention(method) (@inout MyArray) -> () cond_br undef, bb7, bb8 bb8: br bb9 bb9: // Failure b/c of release_value retain_value %00 : $Builtin.NativeObject retain_value %2 : $MyArray apply %3(%2) : $@convention(method) (@guaranteed MyArray) -> Int apply %4(%2) : $@convention(method) (@guaranteed MyArray) -> Int release_value %00 : $Builtin.NativeObject release_value %2 : $MyArray apply %6(%1) : $@convention(method) (@inout MyArray) -> () cond_br undef, bb9, bb10 bb10: %7 = tuple() return %7 : $() } // CHECK: sil @cow_handle_array_address_load // CHECK: bb0({{.*}}): // CHECK: apply // CHECK: br bb1 // CHECK: bb1: // CHECK-NOT: apply // CHECK: bb2 sil @cow_handle_array_address_load : $@convention(thin) (@inout MyArray, @inout Builtin.Int1) -> () { bb0(%0 : $*MyArray, %1 : $*Builtin.Int1): %2 = load %0 : $*MyArray %3 = struct_element_addr %0 : $*MyArray, #MyArray.buffer %4 = struct_element_addr %3 : $*ArrayIntBuffer, #ArrayIntBuffer.storage br bb1 bb1: %6 = load %4 : $*Builtin.NativeObject strong_retain %6 : $Builtin.NativeObject %8 = load %1 : $*Builtin.Int1 strong_release %6 : $Builtin.NativeObject %10 = function_ref @array_make_mutable : $@convention(method) (@inout MyArray) -> () %11 = apply %10(%0) : $@convention(method) (@inout MyArray) -> () %12 = enum $Optional>, #Optional.some!enumelt, %2 : $MyArray %13 = mark_dependence %1 : $*Builtin.Int1 on %12 : $Optional> cond_br %8, bb1, bb2 bb2: %15 = tuple () return %15 : $() } // CHECK-LABEL: sil @cow_type_based_hoisting_retain_release_matching : $@convention(thin) (@guaranteed MyArrayContainer, Builtin.NativeObject) -> () { // CHECK: bb0 // CHECK: [[F:%.*]] = function_ref @array_make_mutable : $@convention(method) (@inout MyArray) -> () // CHECK-NOT: apply [[F]]( // CHECK: bb1: // CHECK: apply [[F]]( // CHECK: bb2: // CHECK-NOT: apply [[F]]( // CHECK: bb3: // CHECK: apply [[F]]( // CHECK: bb4: sil @cow_type_based_hoisting_retain_release_matching : $@convention(thin) (@guaranteed MyArrayContainer, Builtin.NativeObject) -> () { bb0(%0 : $MyArrayContainer, %00 : $Builtin.NativeObject): %1 = ref_element_addr %0 : $MyArrayContainer, #MyArrayContainer.array %2 = load %1 : $*MyArray %3 = function_ref @guaranteed_array_get_count : $@convention(method) (@guaranteed MyArray) -> Int %4 = function_ref @guaranteed_array_get_capacity : $@convention(method) (@guaranteed MyArray) -> Int %5 = function_ref @unknown : $@convention(thin) () -> () %6 = function_ref @array_make_mutable : $@convention(method) (@inout MyArray) -> () %7 = function_ref @array_unknown_mutate : $@convention(method) (@inout MyArray) -> () br bb1 bb1: retain_value %2 : $MyArray retain_value %00 : $Builtin.NativeObject release_value %00: $Builtin.NativeObject apply %3(%2) : $@convention(method) (@guaranteed MyArray) -> Int apply %4(%2) : $@convention(method) (@guaranteed MyArray) -> Int release_value %2 : $MyArray apply %6(%1) : $@convention(method) (@inout MyArray) -> () cond_br undef, bb1, bb2 bb2: br bb3 bb3: retain_value %2 : $MyArray apply %7(%1) : $@convention(method) (@inout MyArray) -> () apply %3(%2) : $@convention(method) (@guaranteed MyArray) -> Int apply %4(%2) : $@convention(method) (@guaranteed MyArray) -> Int release_value %2 : $MyArray apply %6(%1) : $@convention(method) (@inout MyArray) -> () cond_br undef, bb3, bb4 bb4: %8 = tuple() return %8 : $() } struct _MyBridgeStorage { var rawValue : Builtin.BridgeObject } struct _My2dArrayBuffer { var _storage : _MyBridgeStorage } struct My2dArray { var _buffer : _My2dArrayBuffer } struct MyInt { @_hasStorage var _value: Builtin.Int64 init(_ value: Int16) } // CHECK-LABEL: sil @hoist_projections // CHECK: bb0([[CONTAINER:%[0-9]+]] // CHECK: [[CONTAINER2:%.*]] = struct_element_addr [[CONTAINER]] : $*ContainerContainer // CHECK: [[ARRAY:%.*]] = struct_element_addr [[CONTAINER2]] : $*Container, // CHECK: [[FUN:%[0-9]+]] = function_ref @array_make_mutable // CHECK: apply [[FUN]]([[ARRAY]] // CHECK: bb1 // CHECK-NOT: array_make_mutable // CHECK-NOT: apply [[FUN]] sil @hoist_projections : $@convention(thin) (@inout ContainerContainer, @inout Builtin.Int1) -> () { bb0(%0 : $*ContainerContainer, %1 : $*Builtin.Int1): br bb1 bb1: %2 = struct_element_addr %0 : $*ContainerContainer, #ContainerContainer.container br bb3(%2 : $*Container) bb3(%3: $*Container): %4 = struct_element_addr %3 : $*Container, #Container.array %5 = function_ref @array_make_mutable : $@convention(method) (@inout MyArray) -> () %6 = apply %5(%4) : $@convention(method) (@inout MyArray) -> () cond_br undef, bb1, bb2 bb2: %7 = tuple() return %7 : $() } // CHECK-LABEL: sil @hoist_non_unary_projections // CHECK: index_addr // CHECK: struct_element_addr // CHECK: bb1: // CHECK-NOT: index_addr // CHECK-NOT: struct_element_addr sil @hoist_non_unary_projections : $@convention(thin) (@inout ContainerContainer, @inout Builtin.Int1) -> () { bb0(%0 : $*ContainerContainer, %1 : $*Builtin.Int1): %i = integer_literal $Builtin.Int32, 0 br bb1 bb1: %2 = struct_element_addr %0 : $*ContainerContainer, #ContainerContainer.container br bb2(%2 : $*Container) bb2(%3: $*Container): %3i = index_addr %3 : $*Container, %i : $Builtin.Int32 %4 = struct_element_addr %3i : $*Container, #Container.array %5 = function_ref @array_make_mutable : $@convention(method) (@inout MyArray) -> () %6 = apply %5(%4) : $@convention(method) (@inout MyArray) -> () cond_br undef, bb1, bb3 bb3: %7 = tuple() return %7 : $() } // CHECK-LABEL: sil @hoist_projections2 // CHECK: bb0([[CONTAINER:%[0-9]+]] // CHECK: [[CONTAINER2:%.*]] = struct_element_addr [[CONTAINER]] : $*ContainerContainer // CHECK: [[ARRAY:%.*]] = struct_element_addr [[CONTAINER2]] : $*Container, // CHECK: [[FUN:%[0-9]+]] = function_ref @array_make_mutable // CHECK: apply [[FUN]]([[ARRAY]] // CHECK: bb1 // CHECK-NOT: array_make_mutable // CHECK-NOT: apply [[FUN]] sil @hoist_projections2 : $@convention(thin) (@inout ContainerContainer, @inout Builtin.Int1) -> () { bb0(%0 : $*ContainerContainer, %1 : $*Builtin.Int1): br bb1 bb1: %2 = struct_element_addr %0 : $*ContainerContainer, #ContainerContainer.container %3 = struct_element_addr %2 : $*Container, #Container.array br bb3(%3 : $*MyArray) bb3(%4 : $*MyArray): %5 = function_ref @array_make_mutable : $@convention(method) (@inout MyArray) -> () %6 = apply %5(%4) : $@convention(method) (@inout MyArray) -> () cond_br undef, bb1, bb2 bb2: %7 = tuple() return %7 : $() } // CHECK-LABEL: sil @hoist_projections3 // CHECK: bb0([[CONTAINER:%[0-9]+]] // CHECK: [[CONTAINER2:%.*]] = struct_element_addr [[CONTAINER]] : $*ContainerContainer // CHECK: [[ARRAY:%.*]] = struct_element_addr [[CONTAINER2]] : $*Container, // CHECK: [[FUN:%[0-9]+]] = function_ref @array_make_mutable // CHECK: apply [[FUN]]([[ARRAY]] // CHECK: bb1 // CHECK-NOT: array_make_mutable // CHECK-NOT: apply [[FUN]] sil @hoist_projections3 : $@convention(thin) (@inout ContainerContainer, @inout Builtin.Int1) -> () { bb0(%0 : $*ContainerContainer, %1 : $*Builtin.Int1): br bb1 bb1: %2 = struct_element_addr %0 : $*ContainerContainer, #ContainerContainer.container %3 = struct_element_addr %2 : $*Container, #Container.array %5 = function_ref @array_make_mutable : $@convention(method) (@inout MyArray) -> () %6 = apply %5(%3) : $@convention(method) (@inout MyArray) -> () cond_br undef, bb1, bb2 bb2: %7 = tuple() return %7 : $() } // CHECK-LABEL: sil @hoist_array2d // CHECK: bb0({{.*}}): // CHECK: apply // CHECK-NEXT: load // CHECK-NEXT: struct_extract // CHECK-NEXT: struct_extract // CHECK-NEXT: unchecked_ref_cast // CHECK-NEXT: ref_tail_addr // CHECK-NEXT: index_addr // CHECK-NEXT: apply // CHECK-NEXT: br bb1 // CHECK: bb1: // CHECK-NOT: apply sil @hoist_array2d : $@convention(thin) (@inout MyArray) -> () { bb0(%0 : $*MyArray): %2 = load %0 : $*MyArray %3 = integer_literal $Builtin.Word, 1 br bb1 bb1: %5 = function_ref @array_make_mutable : $@convention(method) (@inout MyArray) -> () %6 = apply %5(%0) : $@convention(method) (@inout MyArray) -> () %7 = load %0 : $*MyArray %8 = struct_extract %7 : $MyArray, #MyArray.buffer %9 = struct_extract %8 : $ArrayIntBuffer, #ArrayIntBuffer.storage %10 = unchecked_ref_cast %9 : $Builtin.NativeObject to $MyArrayStorage %11 = ref_tail_addr %10 : $MyArrayStorage, $MyArray %12 = index_addr %11 : $*MyArray, %3 : $Builtin.Word %13 = apply %5(%12) : $@convention(method) (@inout MyArray) -> () cond_br undef, bb1, bb2 bb2: %r = tuple() return %r : $() } // CHECK-LABEL: sil @dont_hoist_inner_mutating_outer // CHECK: bb0({{.*}}): // CHECK: apply // CHECK-NOT: apply // CHECK: bb1: // CHECK: apply // CHECK: apply // CHECK-NOT: apply // CHECK: return sil @dont_hoist_inner_mutating_outer : $@convention(thin) (@inout MyArray) -> () { bb0(%0 : $*MyArray): %2 = load %0 : $*MyArray %3 = integer_literal $Builtin.Word, 1 %4 = function_ref @array_unknown_mutate : $@convention(method) (@inout MyArray) -> () br bb1 bb1: %5 = function_ref @array_make_mutable : $@convention(method) (@inout MyArray) -> () %6 = apply %5(%0) : $@convention(method) (@inout MyArray) -> () %7 = load %0 : $*MyArray %8 = struct_extract %7 : $MyArray, #MyArray.buffer %9 = struct_extract %8 : $ArrayIntBuffer, #ArrayIntBuffer.storage %10 = unchecked_ref_cast %9 : $Builtin.NativeObject to $MyArrayStorage %11 = ref_tail_addr %10 : $MyArrayStorage, $MyArray %12 = index_addr %11 : $*MyArray, %3 : $Builtin.Word %13 = apply %5(%12) : $@convention(method) (@inout MyArray) -> () apply %4(%0) : $@convention(method) (@inout MyArray) -> () cond_br undef, bb1, bb2 bb2: %r = tuple() return %r : $() } // CHECK-LABEL: sil @dont_hoist_inner_variant_index // CHECK: bb0({{.*}}): // CHECK: apply // CHECK-NOT: apply // CHECK: bb1: // CHECK: apply // CHECK-NOT: apply // CHECK: return sil @dont_hoist_inner_variant_index : $@convention(thin) (@inout MyArray, @inout Builtin.Word) -> () { bb0(%0 : $*MyArray, %1 : $*Builtin.Word): %2 = load %0 : $*MyArray br bb1 bb1: %4 = load %1 : $*Builtin.Word %5 = function_ref @array_make_mutable : $@convention(method) (@inout MyArray) -> () %6 = apply %5(%0) : $@convention(method) (@inout MyArray) -> () %7 = load %0 : $*MyArray %8 = struct_extract %7 : $MyArray, #MyArray.buffer %9 = struct_extract %8 : $ArrayIntBuffer, #ArrayIntBuffer.storage %10 = unchecked_ref_cast %9 : $Builtin.NativeObject to $MyArrayStorage %11 = ref_tail_addr %10 : $MyArrayStorage, $MyArray %12 = index_addr %11 : $*MyArray, %4 : $Builtin.Word %13 = apply %5(%12) : $@convention(method) (@inout MyArray) -> () cond_br undef, bb1, bb2 bb2: %r = tuple() return %r : $() }