Files
swift-mirror/test/SILOptimizer/alias-analysis.sil
Erik Eckstein f9b524b1cb AliasAnalysis: a complete overhaul of alias- and memory-behavior analysis
The main changes are:

*) Rewrite everything in swift. So far, parts of memory-behavior analysis were already implemented in swift. Now everything is done in swift and lives in `AliasAnalysis.swift`. This is a big code simplification.

*) Support many more instructions in the memory-behavior analysis - especially OSSA instructions, like `begin_borrow`, `end_borrow`, `store_borrow`, `load_borrow`. The computation of end_borrow effects is now much more precise. Also, partial_apply is now handled more precisely.

*) Simplify and reduce type-based alias analysis (TBAA). The complexity of the old TBAA comes from old days where the language and SIL didn't have strict aliasing and exclusivity rules (e.g. for inout arguments). Now TBAA is only needed for code using unsafe pointers. The new TBAA handles this - and not more. Note that TBAA for classes is already done in `AccessBase.isDistinct`.

*) Handle aliasing in `begin_access [modify]` scopes. We already supported truly immutable scopes like `begin_access [read]` or `ref_element_addr [immutable]`. For `begin_access [modify]` we know that there are no other reads or writes to the access-address within the scope.

*) Don't cache memory-behavior results. It turned out that the hit-miss rate was pretty bad (~ 1:7). The overhead of the cache lookup took as long as recomputing the memory behavior.
2024-07-29 17:33:46 +02:00

102 lines
3.7 KiB
Plaintext

// RUN: %target-sil-opt %s -dump-alias-info -o /dev/null | %FileCheck %s
// General black-box alias analysis tests. White-box unit tests are
// specific to the aa-kind, such as basic-aa.sil and typed-access-tb-aa.sil.
// REQUIRES: asserts
sil_stage canonical
import Swift
import SwiftShims
import Builtin
struct MyStruct {
@_hasStorage @_hasInitialValue var i: Int64 { get set }
@_hasStorage @_hasInitialValue var j: Int64 { get set }
}
struct OuterStruct {
@_hasStorage @_hasInitialValue var s: MyStruct { get set }
}
// Test overlapping access on a different path; the user has misused an index offset
// CHECK-LABEL: @testOffsetBehindProjection
// CHECK: PAIR #23.
// CHECK-NEXT: %3 = index_addr %1 : $*Int64, %2 : $Builtin.Word
// CHECK-NEXT: %5 = struct_element_addr %0 : $*MyStruct, #MyStruct.j
// CHECK-NEXT: NoAlias
sil shared @testOffsetBehindProjection : $@convention(thin) (@inout MyStruct) -> () {
bb0(%0 : $*MyStruct):
%1 = struct_element_addr %0 : $*MyStruct, #MyStruct.i
%2 = integer_literal $Builtin.Word, 1
%3 = index_addr %1 : $*Int64, %2 : $Builtin.Word
%4 = load %3 : $*Int64
// load from a different subobject overlaps
%5 = struct_element_addr %0 : $*MyStruct, #MyStruct.j
%6 = load %5 : $*Int64
%99 = tuple ()
return %99 : $()
}
// CHECK-LABEL: @testOffsetsBehindProjectionOverlap
// CHECK: PAIR #21.
// CHECK-NEXT: %2 = struct_element_addr %1 : $*MyStruct, #MyStruct.i
// CHECK-NEXT: %6 = struct_element_addr %5 : $*MyStruct, #MyStruct.i
// CHECK-NEXT: NoAlias
sil shared @testOffsetsBehindProjectionOverlap : $@convention(thin) (@inout OuterStruct) -> () {
bb0(%0 : $*OuterStruct):
%1 = struct_element_addr %0 : $*OuterStruct, #OuterStruct.s
%2 = struct_element_addr %1 : $*MyStruct, #MyStruct.i
%3 = load %2 : $*Int64
// Loading from a different index within the same subobject still appears to overlap.
// Indexing from a subobject is always considered an unknown index.
%4 = integer_literal $Builtin.Word, 1
%5 = index_addr %1 : $*MyStruct, %4 : $Builtin.Word
%6 = struct_element_addr %5 : $*MyStruct, #MyStruct.i
%7 = load %6 : $*Int64
%99 = tuple ()
return %99 : $()
}
// CHECK-LABEL: @testIndexOf0
// CHECK: PAIR #8.
// CHECK-NEXT: %1 = struct_element_addr %0 : $*MyStruct, #MyStruct.i
// CHECK-NEXT: %3 = index_addr %0 : $*MyStruct, %2 : $Builtin.Word
// CHECK-NEXT: MayAlias
// CHECK: PAIR #9.
// CHECK-NEXT: %1 = struct_element_addr %0 : $*MyStruct, #MyStruct.i
// CHECK-NEXT: %4 = struct_element_addr %3 : $*MyStruct, #MyStruct.i
// CHECK-NEXT: MayAlias
sil @testIndexOf0 : $@convention(thin) (@inout MyStruct) -> () {
bb0(%0 : $*MyStruct):
%1 = struct_element_addr %0 : $*MyStruct, #MyStruct.i
%2 = integer_literal $Builtin.Word, 0
%3 = index_addr %0 : $*MyStruct, %2 : $Builtin.Word
%4 = struct_element_addr %3 : $*MyStruct, #MyStruct.i
fix_lifetime %1 : $*Int64
fix_lifetime %4 : $*Int64
%99 = tuple ()
return %99 : $()
}
// CHECK-LABEL: @testUnknownIndex
// CHECK: PAIR #12.
// CHECK-NEXT: %2 = struct_element_addr %0 : $*MyStruct, #MyStruct.i
// CHECK-NEXT: %3 = index_addr %0 : $*MyStruct, %1 : $Builtin.Word
// CHECK-NEXT: MayAlias
// CHECK: PAIR #13.
// CHECK-NEXT: %2 = struct_element_addr %0 : $*MyStruct, #MyStruct.i
// CHECK-NEXT: %4 = struct_element_addr %3 : $*MyStruct, #MyStruct.i
// CHECK-NEXT: MayAlias
sil @testUnknownIndex : $@convention(thin) (@inout MyStruct, Builtin.Word) -> () {
bb0(%0 : $*MyStruct, %1 : $Builtin.Word):
%2 = struct_element_addr %0 : $*MyStruct, #MyStruct.i
%3 = index_addr %0 : $*MyStruct, %1 : $Builtin.Word
%4 = struct_element_addr %3 : $*MyStruct, #MyStruct.i
fix_lifetime %2 : $*Int64
fix_lifetime %4 : $*Int64
%99 = tuple ()
return %99 : $()
}