Files
swift-mirror/test/SILOptimizer/licm_multiend.sil
Andrew Trick 1c12de3241 Fix LICM combined load/store hoisting/sinking optimization.
This loop optimization hoists and sinks a group of loads and stores to
the same address.

Consider this SIL...

PRELOOP:
  %stackAddr = alloc_stack $Index
  %outerAddr1 = struct_element_addr %stackAddr : $*Index, #Index.value
  %innerAddr1 = struct_element_addr %outerAddr1 : $*Int, #Int._value

  %outerAddr2 = struct_element_addr %stackAddr : $*Index, #Index.value
  %innerAddr2 = struct_element_addr %outerAddr2 : $*Int, #Int._value

LOOP:
  %_ = load %innerAddr2 : $*Builtin.Int64
  store %_ to %outerAddr2 : $*Int
  %_ = load %innerAddr1 : $*Builtin.Int64

There are two bugs:

1) LICM miscompiles code during combined load/store hoisting and sinking.

When the loop contains an aliasing load from a difference projection
value, the optimization sinks the store but never replaces the
load. At runtime, the load reads a stale value.

FIX: isOnlyLoadedAndStored needs to check for other load instructions
before hoisting/sinking a seemingly unrelated set of
loads/stores. Checking side effect instructions is insufficient. The
same bug could happen with stores, which also do not produce side
effects.

Fixes <rdar://61246061> LICM miscompile:
Combined load/store hoisting/sinking with aliases

2) The LICM algorithm is not robust with respect to address projection
   because it identifies a projected address by its SILValue. This
   should never be done! It is trivial to represent a project path
   using an IndexTrieNode (there is also an abstraction called
   "ProjectionPath", but it should _never_ actually be stored by an
   analysis because of the time and space complexity of doing so).

The second bug is not necessary to fix for correctness, so will be
fixed in a follow-up commit.
2020-04-03 08:25:20 -07:00

215 lines
8.7 KiB
Plaintext

// RUN: %target-sil-opt -enable-sil-verify-all %s -licm | %FileCheck %s
// REQUIRES: CPU=x86_64
// REQUIRES: OS=macosx
sil_stage canonical
import Builtin
import Swift
import SwiftShims
var x: Int
let reversedArray: ReversedCollection<[Int]>
// x
sil_global hidden @$s3tmp1xSivp : $Int
// reversedArray
sil_global hidden [let] @$s3tmp13reversedArrays18ReversedCollectionVySaySiGGvp : $ReversedCollection<Array<Int>>
// _swiftEmptyArrayStorage
sil_global @_swiftEmptyArrayStorage : $_SwiftEmptyArrayStorage
// CHECK-LABEL: sil hidden @multi_end_licm : $@convention(thin) () -> () {
// CHECK: bb2:
// CHECK: [[GLOBALVAR:%.*]] = global_addr @$s3tmp1xSivp : $*Int
// CHECK: [[BEGINA:%.*]] = begin_access [modify] [dynamic] [no_nested_conflict] [[GLOBALVAR]] : $*Int
// CHECK: br [[LOOPH:bb[0-9]+]]({{.*}} : $Builtin.Int64)
// CHECK: [[LOOPH]]({{.*}} : $Builtin.Int64)
// CHECK: cond_br {{.*}}, [[LOOPCOND1:bb[0-9]+]], [[LOOPCOND2:bb[0-9]+]]
// CHECK: [[LOOPCOND1]]:
// CHECK-NEXT: store
// CHECK-NEXT: cond_br {{.*}}, [[LOOPEXIT1:bb[0-9]+]], [[LOOPCONT1:bb[0-9]+]]
// CHECK: [[LOOPEXIT1]]:
// CHECK-NEXT: end_access [[BEGINA]] : $*Int
// CHECK-NEXT: br [[LOOPAFTEREXIT:bb[0-9]+]]
// CHECK: [[LOOPCOND2]]:
// CHECK-NEXT: struct $Int
// CHECK-NEXT: store
// CHECK-NEXT: cond_br {{.*}}, [[LOOPEXIT2:bb[0-9]+]], [[LOOPCONT1]]
// CHECK: [[LOOPEXIT2]]:
// CHECK-NEXT: end_access [[BEGINA]] : $*Int
// CHECK-NEXT: br [[LOOPAFTEREXIT]]
// CHECK: [[LOOPCONT1]]:
// CHECK-NEXT: br [[LOOPH]]
// CHECK: [[LOOPAFTEREXIT]]:
// CHECK-NEXT: br [[FUNCRET:bb[0-9]+]]
// CHECK: [[FUNCRET]]:
// CHECK-NEXT: tuple
// CHECK-NEXT: return
sil hidden @multi_end_licm : $@convention(thin) () -> () {
bb0:
%0 = global_addr @$s3tmp13reversedArrays18ReversedCollectionVySaySiGGvp : $*ReversedCollection<Array<Int>>
%1 = struct_element_addr %0 : $*ReversedCollection<Array<Int>>, #ReversedCollection._base
%2 = struct_element_addr %1 : $*Array<Int>, #Array._buffer
%3 = struct_element_addr %2 : $*_ArrayBuffer<Int>, #_ArrayBuffer._storage
%4 = struct_element_addr %3 : $*_BridgeStorage<__ContiguousArrayStorageBase>, #_BridgeStorage.rawValue
%5 = load %4 : $*Builtin.BridgeObject
%6 = unchecked_ref_cast %5 : $Builtin.BridgeObject to $__ContiguousArrayStorageBase
%7 = ref_element_addr %6 : $__ContiguousArrayStorageBase, #__ContiguousArrayStorageBase.countAndCapacity
%8 = struct_element_addr %7 : $*_ArrayBody, #_ArrayBody._storage
%9 = struct_element_addr %8 : $*_SwiftArrayBodyStorage, #_SwiftArrayBodyStorage.count
%10 = struct_element_addr %9 : $*Int, #Int._value
%11 = load %10 : $*Builtin.Int64
%12 = builtin "assumeNonNegative_Int64"(%11 : $Builtin.Int64) : $Builtin.Int64
%13 = integer_literal $Builtin.Int64, 0
%14 = integer_literal $Builtin.Int1, 0
%15 = builtin "cmp_eq_Int64"(%12 : $Builtin.Int64, %13 : $Builtin.Int64) : $Builtin.Int1
%16 = builtin "int_expect_Int1"(%15 : $Builtin.Int1, %14 : $Builtin.Int1) : $Builtin.Int1
cond_br %16, bb1, bb2
bb1:
br bb12
bb2:
%19 = global_addr @$s3tmp1xSivp : $*Int
%20 = integer_literal $Builtin.Int64, 1
%21 = integer_literal $Builtin.Int1, -1
%23 = ref_tail_addr %6 : $__ContiguousArrayStorageBase, $Int
br bb4(%12 : $Builtin.Int64)
bb4(%27 : $Builtin.Int64):
%28 = builtin "ssub_with_overflow_Int64"(%27 : $Builtin.Int64, %20 : $Builtin.Int64, %21 : $Builtin.Int1) : $(Builtin.Int64, Builtin.Int1)
%29 = tuple_extract %28 : $(Builtin.Int64, Builtin.Int1), 0
%30 = tuple_extract %28 : $(Builtin.Int64, Builtin.Int1), 1
cond_fail %30 : $Builtin.Int1
%32 = builtin "cmp_slt_Int64"(%29 : $Builtin.Int64, %13 : $Builtin.Int64) : $Builtin.Int1
%33 = load %10 : $*Builtin.Int64
%34 = builtin "assumeNonNegative_Int64"(%33 : $Builtin.Int64) : $Builtin.Int64
%35 = builtin "cmp_slt_Int64"(%29 : $Builtin.Int64, %34 : $Builtin.Int64) : $Builtin.Int1
%36 = builtin "xor_Int1"(%35 : $Builtin.Int1, %21 : $Builtin.Int1) : $Builtin.Int1
%37 = builtin "or_Int1"(%32 : $Builtin.Int1, %36 : $Builtin.Int1) : $Builtin.Int1
cond_fail %37 : $Builtin.Int1
%39 = builtin "truncOrBitCast_Int64_Word"(%29 : $Builtin.Int64) : $Builtin.Word
%40 = index_addr %23 : $*Int, %39 : $Builtin.Word
%41 = struct_element_addr %40 : $*Int, #Int._value
%42 = load %41 : $*Builtin.Int64
%43 = struct $Int (%42 : $Builtin.Int64)
debug_value %43 : $Int, let, name "item"
%global = begin_access [modify] [dynamic] [no_nested_conflict] %19 : $*Int
%46 = builtin "cmp_eq_Int64"(%29 : $Builtin.Int64, %13 : $Builtin.Int64) : $Builtin.Int1
%47 = builtin "int_expect_Int1"(%46 : $Builtin.Int1, %14 : $Builtin.Int1) : $Builtin.Int1
cond_br %47, bb10, bb11
bb10:
store %43 to %global : $*Int
end_access %global : $*Int
cond_br %47, bb6, bb5
bb11:
%otherInt = struct $Int (%27 : $Builtin.Int64)
store %otherInt to %global : $*Int
end_access %global : $*Int
cond_br %47, bb6, bb5
bb5:
br bb4(%29 : $Builtin.Int64)
bb6:
br bb12
bb12:
%25 = tuple ()
return %25 : $()
} // end sil function 'multi_end_licm'
// CHECK-LABEL: sil hidden @multi_end_licm_loop_exit : $@convention(thin) () -> () {
// CHECK: br [[LOOPH:bb[0-9]+]]({{.*}} : $Builtin.Int64)
// CHECK: [[LOOPH]]({{.*}} : $Builtin.Int64)
// CHECK: begin_access [modify] [dynamic] [no_nested_conflict]
// CHECK: cond_br {{.*}}, [[LOOPCOND1:bb[0-9]+]], [[LOOPCOND2:bb[0-9]+]]
// CHECK: [[LOOPCOND1]]
// CHECK-NEXT: store
// CHECK-NEXT: end_access
// CHECK: return
sil hidden @multi_end_licm_loop_exit : $@convention(thin) () -> () {
bb0:
%0 = global_addr @$s3tmp13reversedArrays18ReversedCollectionVySaySiGGvp : $*ReversedCollection<Array<Int>>
%1 = struct_element_addr %0 : $*ReversedCollection<Array<Int>>, #ReversedCollection._base
%2 = struct_element_addr %1 : $*Array<Int>, #Array._buffer
%3 = struct_element_addr %2 : $*_ArrayBuffer<Int>, #_ArrayBuffer._storage
%4 = struct_element_addr %3 : $*_BridgeStorage<__ContiguousArrayStorageBase>, #_BridgeStorage.rawValue
%5 = load %4 : $*Builtin.BridgeObject
%6 = unchecked_ref_cast %5 : $Builtin.BridgeObject to $__ContiguousArrayStorageBase
%7 = ref_element_addr %6 : $__ContiguousArrayStorageBase, #__ContiguousArrayStorageBase.countAndCapacity
%8 = struct_element_addr %7 : $*_ArrayBody, #_ArrayBody._storage
%9 = struct_element_addr %8 : $*_SwiftArrayBodyStorage, #_SwiftArrayBodyStorage.count
%10 = struct_element_addr %9 : $*Int, #Int._value
%11 = load %10 : $*Builtin.Int64
%12 = builtin "assumeNonNegative_Int64"(%11 : $Builtin.Int64) : $Builtin.Int64
%13 = integer_literal $Builtin.Int64, 0
%14 = integer_literal $Builtin.Int1, 0
%15 = builtin "cmp_eq_Int64"(%12 : $Builtin.Int64, %13 : $Builtin.Int64) : $Builtin.Int1
%16 = builtin "int_expect_Int1"(%15 : $Builtin.Int1, %14 : $Builtin.Int1) : $Builtin.Int1
cond_br %16, bb1, bb2
bb1:
br bbRet
bb2:
%19 = global_addr @$s3tmp1xSivp : $*Int
%20 = integer_literal $Builtin.Int64, 1
%21 = integer_literal $Builtin.Int1, -1
%23 = ref_tail_addr %6 : $__ContiguousArrayStorageBase, $Int
br bb4(%12 : $Builtin.Int64)
bb4(%27 : $Builtin.Int64):
%28 = builtin "ssub_with_overflow_Int64"(%27 : $Builtin.Int64, %20 : $Builtin.Int64, %21 : $Builtin.Int1) : $(Builtin.Int64, Builtin.Int1)
%29 = tuple_extract %28 : $(Builtin.Int64, Builtin.Int1), 0
%30 = tuple_extract %28 : $(Builtin.Int64, Builtin.Int1), 1
cond_fail %30 : $Builtin.Int1
%32 = builtin "cmp_slt_Int64"(%29 : $Builtin.Int64, %13 : $Builtin.Int64) : $Builtin.Int1
%33 = load %10 : $*Builtin.Int64
%34 = builtin "assumeNonNegative_Int64"(%33 : $Builtin.Int64) : $Builtin.Int64
%35 = builtin "cmp_slt_Int64"(%29 : $Builtin.Int64, %34 : $Builtin.Int64) : $Builtin.Int1
%36 = builtin "xor_Int1"(%35 : $Builtin.Int1, %21 : $Builtin.Int1) : $Builtin.Int1
%37 = builtin "or_Int1"(%32 : $Builtin.Int1, %36 : $Builtin.Int1) : $Builtin.Int1
cond_fail %37 : $Builtin.Int1
%39 = builtin "truncOrBitCast_Int64_Word"(%29 : $Builtin.Int64) : $Builtin.Word
%40 = index_addr %23 : $*Int, %39 : $Builtin.Word
%41 = struct_element_addr %40 : $*Int, #Int._value
%42 = load %41 : $*Builtin.Int64
%43 = struct $Int (%42 : $Builtin.Int64)
debug_value %43 : $Int, let, name "item"
%global = begin_access [modify] [dynamic] [no_nested_conflict] %19 : $*Int
%46 = builtin "cmp_eq_Int64"(%29 : $Builtin.Int64, %13 : $Builtin.Int64) : $Builtin.Int1
%47 = builtin "int_expect_Int1"(%46 : $Builtin.Int1, %14 : $Builtin.Int1) : $Builtin.Int1
cond_br %47, bbend1, bbend2
bbend1:
store %43 to %global : $*Int
end_access %global : $*Int
cond_br %47, bb6, bb5
bbend2:
%otherInt = struct $Int (%27 : $Builtin.Int64)
store %otherInt to %global : $*Int
cond_br %47, bbOut, bb5
bbOut:
end_access %global : $*Int
br bb6
bb5:
br bb4(%29 : $Builtin.Int64)
bb6:
br bbRet
bbRet:
%25 = tuple ()
return %25 : $()
} // end sil function 'multi_end_licm_loop_exit'