mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
It eliminates dead access scopes if they are not conflicting with other scopes. Removes: ``` %2 = begin_access [modify] [dynamic] %1 ... // no uses of %2 end_access %2 ``` However, dead _conflicting_ access scopes are not removed. If a conflicting scope becomes dead because an optimization e.g. removed a load, it is still important to get an access violation at runtime. Even a propagated value of a redundant load from a conflicting scope is undefined. ``` %2 = begin_access [modify] [dynamic] %1 store %x to %2 %3 = begin_access [read] [dynamic] %1 // conflicting with %2! %y = load %3 end_access %3 end_access %2 use(%y) ``` After redundant-load-elimination: ``` %2 = begin_access [modify] [dynamic] %1 store %x to %2 %3 = begin_access [read] [dynamic] %1 // now dead, but still conflicting with %2 end_access %3 end_access %2 use(%x) // propagated from the store, but undefined here! ``` In this case the scope `%3` is not removed because it's important to get an access violation error at runtime before the undefined value `%x` is used. This pass considers potential conflicting access scopes in called functions. But it does not consider potential conflicting access in callers (because it can't!). However, optimizations, like redundant-load-elimination, can only do such transformations if the outer access scope is within the function, e.g. ``` bb0(%0 : $*T): // an inout from a conflicting scope in the caller store %x to %0 %3 = begin_access [read] [dynamic] %1 %y = load %3 // cannot be propagated because it cannot be proved that %1 is the same address as %0 end_access %3 ``` All those checks are only done for dynamic access scopes, because they matter for runtime exclusivity checking. Dead static scopes are removed unconditionally.
310 lines
8.2 KiB
Plaintext
310 lines
8.2 KiB
Plaintext
// RUN: %target-sil-opt %s -dead-access-scope-elimination | %FileCheck %s
|
|
|
|
sil_stage canonical
|
|
|
|
import Swift
|
|
import Builtin
|
|
|
|
sil_global @g1: $Int
|
|
sil_global @g2: $Int
|
|
|
|
sil @unknown : $@convention(thin) () -> ()
|
|
sil @readonly : $@convention(thin) () -> () {
|
|
[global: read]
|
|
}
|
|
sil @pure : $@convention(thin) () -> () {
|
|
[global: ]
|
|
}
|
|
|
|
// CHECK-LABEL: sil [ossa] @simple_dead :
|
|
// CHECK-NOT: begin_access
|
|
// CHECK-NOT: end_access
|
|
// CHECK: } // end sil function 'simple_dead'
|
|
sil [ossa] @simple_dead : $@convention(thin) () -> () {
|
|
bb0:
|
|
%0 = global_addr @g1 : $*Int
|
|
%2 = begin_access [modify] [dynamic] %0
|
|
fix_lifetime %2
|
|
debug_value %2 : $*Int, name "g1"
|
|
end_access %2
|
|
%r = tuple ()
|
|
return %r
|
|
}
|
|
|
|
// CHECK-LABEL: sil [ossa] @simple_alive :
|
|
// CHECK: [[A:%.*]] = begin_access
|
|
// CHECK: [[L:%.*]] = load [trivial] [[A]]
|
|
// CHECK: return [[L]]
|
|
// CHECK: } // end sil function 'simple_alive'
|
|
sil [ossa] @simple_alive : $@convention(thin) () -> Int {
|
|
bb0:
|
|
%0 = global_addr @g1 : $*Int
|
|
%2 = begin_access [modify] [dynamic] %0
|
|
%3 = load [trivial] %2
|
|
end_access %2
|
|
return %3
|
|
}
|
|
|
|
// CHECK-LABEL: sil [ossa] @conflicting :
|
|
// CHECK: %1 = begin_access [modify]
|
|
// CHECK: %2 = begin_access [read]
|
|
// CHECK: end_access %2
|
|
// CHECK: end_access %1
|
|
// CHECK: } // end sil function 'conflicting'
|
|
sil [ossa] @conflicting : $@convention(thin) () -> () {
|
|
bb0:
|
|
%0 = global_addr @g1 : $*Int
|
|
%1 = begin_access [modify] [dynamic] %0
|
|
%2 = begin_access [read] [dynamic] %0
|
|
end_access %2
|
|
end_access %1
|
|
%r = tuple ()
|
|
return %r
|
|
}
|
|
|
|
// CHECK-LABEL: sil [ossa] @not_conflicting :
|
|
// CHECK-NOT: begin_access
|
|
// CHECK-NOT: end_access
|
|
// CHECK: } // end sil function 'not_conflicting'
|
|
sil [ossa] @not_conflicting : $@convention(thin) () -> () {
|
|
bb0:
|
|
%0 = global_addr @g1 : $*Int
|
|
%1 = begin_access [read] [dynamic] %0
|
|
%2 = begin_access [read] [dynamic] %0
|
|
end_access %2
|
|
end_access %1
|
|
%r = tuple ()
|
|
return %r
|
|
}
|
|
|
|
// CHECK-LABEL: sil [ossa] @not_aliasing :
|
|
// CHECK-NOT: begin_access
|
|
// CHECK-NOT: end_access
|
|
// CHECK: } // end sil function 'not_aliasing'
|
|
sil [ossa] @not_aliasing : $@convention(thin) () -> () {
|
|
bb0:
|
|
%0 = global_addr @g1 : $*Int
|
|
%1 = global_addr @g2 : $*Int
|
|
%2 = begin_access [modify] [dynamic] %0
|
|
%3 = begin_access [read] [dynamic] %1
|
|
end_access %3
|
|
end_access %2
|
|
%r = tuple ()
|
|
return %r
|
|
}
|
|
|
|
// CHECK-LABEL: sil [ossa] @call_with_potential_access :
|
|
// CHECK: %2 = begin_access [read]
|
|
// CHECK: %3 = begin_access [read]
|
|
// CHECK: end_access %3
|
|
// CHECK: end_access %2
|
|
// CHECK: } // end sil function 'call_with_potential_access'
|
|
sil [ossa] @call_with_potential_access : $@convention(thin) () -> () {
|
|
bb0:
|
|
%0 = global_addr @g1 : $*Int
|
|
%1 = global_addr @g2 : $*Int
|
|
%2 = begin_access [read] [dynamic] %0
|
|
%3 = begin_access [read] [dynamic] %1
|
|
%4 = function_ref @unknown : $@convention(thin) () -> ()
|
|
%5 = apply %4() : $@convention(thin) () -> ()
|
|
end_access %3
|
|
end_access %2
|
|
%r = tuple ()
|
|
return %r
|
|
}
|
|
|
|
// CHECK-LABEL: sil [ossa] @call_pure :
|
|
// CHECK-NOT: begin_access
|
|
// CHECK-NOT: end_access
|
|
// CHECK: } // end sil function 'call_pure'
|
|
sil [ossa] @call_pure : $@convention(thin) () -> () {
|
|
bb0:
|
|
%0 = global_addr @g1 : $*Int
|
|
%1 = global_addr @g2 : $*Int
|
|
%2 = begin_access [read] [dynamic] %0
|
|
%3 = begin_access [read] [dynamic] %1
|
|
%4 = function_ref @pure : $@convention(thin) () -> ()
|
|
%5 = apply %4() : $@convention(thin) () -> ()
|
|
end_access %3
|
|
end_access %2
|
|
%r = tuple ()
|
|
return %r
|
|
}
|
|
|
|
// CHECK-LABEL: sil [ossa] @call_readonly :
|
|
// CHECK: %3 = begin_access
|
|
// CHECK-NEXT: apply
|
|
// CHECK-NEXT: end_access %3
|
|
// CHECK-NEXT: apply
|
|
// CHECK-NEXT: tuple
|
|
// CHECK: } // end sil function 'call_readonly'
|
|
sil [ossa] @call_readonly : $@convention(thin) () -> () {
|
|
bb0:
|
|
%0 = global_addr @g1 : $*Int
|
|
%1 = global_addr @g2 : $*Int
|
|
%2 = function_ref @readonly : $@convention(thin) () -> ()
|
|
%3 = begin_access [modify] [dynamic] %0
|
|
%4 = apply %2() : $@convention(thin) () -> ()
|
|
end_access %3
|
|
%6 = begin_access [read] [dynamic] %0
|
|
%7 = apply %2() : $@convention(thin) () -> ()
|
|
end_access %6
|
|
%r = tuple ()
|
|
return %r
|
|
}
|
|
|
|
// CHECK-LABEL: sil [ossa] @unknown_call_via_destructor :
|
|
// CHECK: %2 = begin_access [read]
|
|
// CHECK: end_access %2
|
|
// CHECK: } // end sil function 'unknown_call_via_destructor'
|
|
sil [ossa] @unknown_call_via_destructor : $@convention(thin) (@owned AnyObject) -> () {
|
|
bb0(%0: @owned $AnyObject):
|
|
%1 = global_addr @g1 : $*Int
|
|
%2 = begin_access [read] [dynamic] %1
|
|
destroy_value %0
|
|
end_access %2
|
|
%r = tuple ()
|
|
return %r
|
|
}
|
|
|
|
// CHECK-LABEL: sil [ossa] @two_inner_accesses :
|
|
// CHECK: %1 = begin_access [read]
|
|
// CHECK-NEXT: %2 = begin_access [modify]
|
|
// CHECK-NEXT: end_access %2
|
|
// CHECK-NEXT: end_access %1
|
|
// CHECK: } // end sil function 'two_inner_accesses'
|
|
sil [ossa] @two_inner_accesses : $@convention(thin) () -> () {
|
|
bb0:
|
|
%0 = global_addr @g1 : $*Int
|
|
%1 = begin_access [read] [dynamic] %0
|
|
%2 = begin_access [modify] [dynamic] %0
|
|
end_access %2
|
|
%4 = begin_access [read] [dynamic] %0
|
|
end_access %4
|
|
end_access %1
|
|
%r = tuple ()
|
|
return %r
|
|
}
|
|
|
|
// CHECK-LABEL: sil [ossa] @partially_overlapping_accesses :
|
|
// CHECK: %2 = begin_access [read] [dynamic] %1
|
|
// CHECK: apply
|
|
// CHECK-NEXT: end_access %2
|
|
// CHECK-NEXT: tuple
|
|
// CHECK: } // end sil function 'partially_overlapping_accesses'
|
|
sil [ossa] @partially_overlapping_accesses : $@convention(thin) () -> () {
|
|
bb0:
|
|
%0 = global_addr @g1 : $*Int
|
|
%1 = global_addr @g2 : $*Int
|
|
%2 = begin_access [read] [dynamic] %0
|
|
%3 = begin_access [read] [dynamic] %1
|
|
end_access %2
|
|
%4 = function_ref @unknown : $@convention(thin) () -> ()
|
|
%5 = apply %4() : $@convention(thin) () -> ()
|
|
%6 = begin_access [read] [dynamic] %0
|
|
end_access %3
|
|
end_access %6
|
|
%r = tuple ()
|
|
return %r
|
|
}
|
|
|
|
// CHECK-LABEL: sil [ossa] @multi_block1 :
|
|
// CHECK: bb0:
|
|
// CHECK: %1 = begin_access
|
|
// CHECK: bb1:
|
|
// CHECK: %3 = begin_access
|
|
// CHECK: bb2:
|
|
// CHECK: %6 = begin_access
|
|
// CHECK: bb3:
|
|
// CHECK: %9 = begin_access
|
|
// CHECK: } // end sil function 'multi_block1'
|
|
sil [ossa] @multi_block1 : $@convention(thin) () -> () {
|
|
bb0:
|
|
%0 = global_addr @g1 : $*Int
|
|
%1 = begin_access [modify] [dynamic] %0
|
|
cond_br undef, bb1, bb2
|
|
|
|
bb1:
|
|
%3 = begin_access [read] [dynamic] %0
|
|
end_access %3
|
|
br bb3
|
|
|
|
bb2:
|
|
%6 = begin_access [read] [dynamic] %0
|
|
end_access %6
|
|
br bb3
|
|
|
|
bb3:
|
|
%9 = begin_access [read] [dynamic] %0
|
|
end_access %9
|
|
end_access %1
|
|
%r = tuple ()
|
|
return %r
|
|
}
|
|
|
|
// CHECK-LABEL: sil [ossa] @multi_block2 :
|
|
// CHECK: bb0:
|
|
// CHECK: %1 = begin_access
|
|
// CHECK: bb1:
|
|
// CHECK: %3 = begin_access
|
|
// CHECK: bb2:
|
|
// CHECK: end_access %1
|
|
// CHECK-NEXT: br bb3
|
|
// CHECK: bb3:
|
|
// CHECK-NEXT: tuple
|
|
// CHECK: } // end sil function 'multi_block2'
|
|
sil [ossa] @multi_block2 : $@convention(thin) () -> () {
|
|
bb0:
|
|
%0 = global_addr @g1 : $*Int
|
|
%1 = begin_access [modify] [dynamic] %0
|
|
cond_br undef, bb1, bb2
|
|
|
|
bb1:
|
|
%3 = begin_access [read] [dynamic] %0
|
|
end_access %3
|
|
end_access %1
|
|
br bb3
|
|
|
|
bb2:
|
|
end_access %1
|
|
%8 = begin_access [read] [dynamic] %0
|
|
end_access %8
|
|
br bb3
|
|
|
|
bb3:
|
|
%11 = begin_access [read] [dynamic] %0
|
|
end_access %11
|
|
%r = tuple ()
|
|
return %r
|
|
}
|
|
|
|
// CHECK-LABEL: sil [ossa] @nested_static :
|
|
// CHECK-NOT: begin_access [modify] [static]
|
|
// CHECK: } // end sil function 'nested_static'
|
|
sil [ossa] @nested_static : $@convention(thin) () -> () {
|
|
bb0:
|
|
%0 = global_addr @g1 : $*Int
|
|
%1 = begin_access [modify] [dynamic] %0
|
|
%2 = begin_access [modify] [static] %1
|
|
end_access %2
|
|
end_access %1
|
|
%r = tuple ()
|
|
return %r
|
|
}
|
|
|
|
// CHECK-LABEL: sil [ossa] @non_dead_inside_dead :
|
|
// CHECK: begin_access [modify]
|
|
// CHECK: begin_access [read]
|
|
// CHECK: } // end sil function 'non_dead_inside_dead'
|
|
sil [ossa] @non_dead_inside_dead : $@convention(thin) () -> Int {
|
|
bb0:
|
|
%0 = global_addr @g1 : $*Int
|
|
%1 = begin_access [modify] [dynamic] %0
|
|
%2 = begin_access [read] [dynamic] %0
|
|
%3 = load [trivial] %2
|
|
end_access %2
|
|
end_access %1
|
|
return %3
|
|
}
|
|
|