blob: 8c6d5836716813ec6da65e4b0feb3411528fb420 [file] [log] [blame]
// RUN: %target-sil-opt -access-enforcement-dom %s -enable-sil-verify-all | %FileCheck %s
//
// Test the AccessEnforcementDom pass in isolation. This ensures that
// no upstream passes have removed SIL-level access markers that are
// required to ensure the pass is not overly optimistic.
sil_stage canonical
import Builtin
import Swift
import SwiftShims
struct X {
@_hasStorage var i: Int64 { get set }
init(i: Int64)
init()
}
var globalX: X
var globalOtherX: X
sil_global hidden @globalX : $X
sil_global hidden @globalOtherX : $X
sil hidden @Xinit : $@convention(method) (@thin X.Type) -> X {
bb0(%0 : $@thin X.Type):
%1 = alloc_stack $X, var, name "self"
%2 = integer_literal $Builtin.Int64, 7
%3 = struct $Int64 (%2 : $Builtin.Int64)
%4 = struct_element_addr %1 : $*X, #X.i
store %3 to %4 : $*Int64
%6 = struct $X (%3 : $Int64)
dealloc_stack %1 : $*X
return %6 : $X
}
// public func testDomSimpleRead() {
// Checks 3 scopes, two of which are dominated and access the same storage
//
// CHECK-LABEL: sil @testDomSimpleRead : $@convention(thin) () -> () {
// CHECK: [[GLOBAL:%.*]] = global_addr @globalX : $*X
// CHECK-NEXT: [[BEGIN:%.*]] = begin_access [read] [dynamic] [[GLOBAL]] : $*X
// CHECK-NEXT: load [[BEGIN]] : $*X
// CHECK-NEXT: end_access [[BEGIN]] : $*X
// CHECK-NEXT: [[BEGIN:%.*]] = begin_access [read] [static] [no_nested_conflict] [[GLOBAL]] : $*X
// CHECK-NEXT: load [[BEGIN]] : $*X
// CHECK-NEXT: end_access [[BEGIN]] : $*X
// CHECK-NEXT: [[BEGIN:%.*]] = begin_access [read] [static] [no_nested_conflict] [[GLOBAL]] : $*X
// CHECK-NEXT: load [[BEGIN]] : $*X
// CHECK-NEXT: end_access [[BEGIN]] : $*X
// CHECK-NOT: begin_access
// CHECK-LABEL: } // end sil function 'testDomSimpleRead'
sil @testDomSimpleRead : $@convention(thin) () -> () {
bb0:
%0 = global_addr @globalX: $*X
%1 = begin_access [read] [dynamic] %0 : $*X
%2 = load %1 : $*X
end_access %1 : $*X
%4 = begin_access [read] [dynamic] [no_nested_conflict] %0 : $*X
%5 = load %4 : $*X
end_access %4 : $*X
%7 = begin_access [read] [dynamic] [no_nested_conflict] %0 : $*X
%8 = load %7 : $*X
end_access %7 : $*X
%10 = tuple ()
return %10 : $()
}
// public func testDomSimpleWrite() {
// Checks 3 scopes, two of which are dominated and access the same storage
//
// CHECK-LABEL: sil @testDomSimpleWrite : $@convention(thin) () -> () {
// CHECK: [[GLOBAL:%.*]] = global_addr @globalX : $*X
// CHECK-NEXT: [[BEGIN:%.*]] = begin_access [modify] [dynamic] [[GLOBAL]] : $*X
// CHECK-NEXT: load [[BEGIN]] : $*X
// CHECK-NEXT: end_access [[BEGIN]] : $*X
// CHECK: [[BEGIN:%.*]] = begin_access [modify] [static] [no_nested_conflict] [[GLOBAL]] : $*X
// CHECK: store {{.*}} to [[BEGIN]] : $*X
// CHECK-NEXT: end_access [[BEGIN]] : $*X
// CHECK-NEXT: [[BEGIN:%.*]] = begin_access [read] [static] [no_nested_conflict] [[GLOBAL]] : $*X
// CHECK-NEXT: load [[BEGIN]] : $*X
// CHECK-NEXT: end_access [[BEGIN]] : $*X
// CHECK-NOT: begin_access
// CHECK-LABEL: } // end sil function 'testDomSimpleWrite'
sil @testDomSimpleWrite : $@convention(thin) () -> () {
bb0:
%0 = global_addr @globalX: $*X
%1 = begin_access [modify] [dynamic] %0 : $*X
%2 = load %1 : $*X
end_access %1 : $*X
%4 = metatype $@thin X.Type
// function_ref X.init()
%5 = function_ref @Xinit : $@convention(method) (@thin X.Type) -> X
%6 = apply %5(%4) : $@convention(method) (@thin X.Type) -> X
%7 = begin_access [modify] [dynamic] [no_nested_conflict] %0 : $*X
store %6 to %7 : $*X
end_access %7 : $*X
%10 = begin_access [read] [dynamic] [no_nested_conflict] %0 : $*X
%11 = load %10 : $*X
end_access %10 : $*X
%12 = tuple ()
return %12 : $()
}
// public func testDomAcrossBBs() {
// Checks static-setting of scopes across basic blocks
//
// CHECK-LABEL: sil @testDomAcrossBBs : $@convention(thin) () -> () {
// CHECK: [[GLOBAL:%.*]] = global_addr @globalX : $*X
// CHECK-NEXT: [[BEGIN:%.*]] = begin_access [modify] [dynamic] [[GLOBAL]] : $*X
// CHECK-NEXT: load [[BEGIN]] : $*X
// CHECK-NEXT: end_access [[BEGIN]] : $*X
// CHECK-NEXT: br bb1
// CHECK: br bb2
// CHECK: bb2:
// CHECK-NEXT: [[BEGIN:%.*]] = begin_access [modify] [static] [no_nested_conflict] [[GLOBAL]] : $*X
// CHECK: load [[BEGIN]] : $*X
// CHECK-NEXT: end_access [[BEGIN]] : $*X
// CHECK-NOT: begin_access
// CHECK-LABEL: } // end sil function 'testDomAcrossBBs'
sil @testDomAcrossBBs : $@convention(thin) () -> () {
bb0:
%0 = global_addr @globalX: $*X
%1 = begin_access [modify] [dynamic] %0 : $*X
%2 = load %1 : $*X
end_access %1 : $*X
br bb1
bb1:
br bb2
bb2:
%4 = begin_access [modify] [dynamic] [no_nested_conflict] %0 : $*X
%5 = load %4 : $*X
end_access %4 : $*X
%7 = tuple ()
return %7 : $()
}
// public func testDomAcrossInnerLoop() {
// Checksstatic-setting of scopes across an inner loop
//
// CHECK-LABEL: sil @testDomAcrossInnerLoop : $@convention(thin) () -> () {
// CHECK: [[GLOBAL:%.*]] = global_addr @globalX : $*X
// CHECK-NEXT: [[BEGIN:%.*]] = begin_access [modify] [dynamic] [[GLOBAL]] : $*X
// CHECK-NEXT: load [[BEGIN]] : $*X
// CHECK-NEXT: end_access [[BEGIN]] : $*X
// CHECK-NEXT: br bb1
// CHECK: cond_br {{.*}}, bb1, bb2
// CHECK: bb2:
// CHECK-NEXT: [[BEGIN:%.*]] = begin_access [modify] [static] [no_nested_conflict] [[GLOBAL]] : $*X
// CHECK: load [[BEGIN]] : $*X
// CHECK-NEXT: end_access [[BEGIN]] : $*X
// CHECK-NOT: begin_access
// CHECK-LABEL: } // end sil function 'testDomAcrossInnerLoop'
sil @testDomAcrossInnerLoop : $@convention(thin) () -> () {
bb0:
%0 = global_addr @globalX: $*X
%1 = begin_access [modify] [dynamic] %0 : $*X
%2 = load %1 : $*X
end_access %1 : $*X
br bb1
bb1:
%cond = integer_literal $Builtin.Int1, 1
cond_br %cond, bb1, bb2
bb2:
%4 = begin_access [modify] [dynamic] [no_nested_conflict] %0 : $*X
%5 = load %4 : $*X
end_access %4 : $*X
%7 = tuple ()
return %7 : $()
}
// public func testIrreducibleGraph() {
// Checks domination in an irreducible control flow
//
// CHECK-LABEL: sil @testIrreducibleGraph : $@convention(thin) () -> () {
// CHECK: [[GLOBAL:%.*]] = global_addr @globalX : $*X
// CHECK-NEXT: [[BEGIN:%.*]] = begin_access [modify] [dynamic] [[GLOBAL]] : $*X
// CHECK-NEXT: load [[BEGIN]] : $*X
// CHECK-NEXT: end_access [[BEGIN]] : $*X
// CHECK-NEXT: br bb1
// CHECK: [[BEGIN2:%.*]] = begin_access [modify] [static] [no_nested_conflict] [[GLOBAL]] : $*X
// CHECK-NEXT: load [[BEGIN2]] : $*X
// CHECK-NEXT: end_access [[BEGIN2]] : $*X
// CHECK: cond_br {{.*}}, bb2, bb3
// CHECK: [[BEGIN3:%.*]] = begin_access [read] [static] [no_nested_conflict] [[GLOBAL]] : $*X
// CHECK-NEXT: load [[BEGIN3]] : $*X
// CHECK-NEXT: end_access [[BEGIN3]] : $*X
// CHECK: cond_br {{.*}}, bb3, bb4
// CHECK: [[BEGIN4:%.*]] = begin_access [read] [static] [no_nested_conflict] [[GLOBAL]] : $*X
// CHECK-NEXT: load [[BEGIN4]] : $*X
// CHECK-NEXT: end_access [[BEGIN4]] : $*X
// CHECK: cond_br {{.*}}, bb2, bb1
// CHECK: [[BEGIN5:%.*]] = begin_access [read] [static] [no_nested_conflict] [[GLOBAL]] : $*X
// CHECK-NEXT: load [[BEGIN5]] : $*X
// CHECK-NEXT: end_access [[BEGIN5]] : $*X
// CHECK-NOT: begin_access
// CHECK-LABEL: } // end sil function 'testIrreducibleGraph'
sil @testIrreducibleGraph : $@convention(thin) () -> () {
bb0:
%0 = global_addr @globalX: $*X
%1 = begin_access [read] [dynamic] %0 : $*X
%2 = load %1 : $*X
end_access %1 : $*X
br bb1
bb1:
%4 = begin_access [modify] [dynamic] [no_nested_conflict] %0 : $*X
%5 = load %4 : $*X
end_access %4 : $*X
%cond1 = integer_literal $Builtin.Int1, 1
cond_br %cond1, bb2, bb3
bb2:
%6 = begin_access [read] [dynamic] [no_nested_conflict] %0 : $*X
%7 = load %6 : $*X
end_access %6 : $*X
%cond2 = integer_literal $Builtin.Int1, 1
cond_br %cond2, bb3, bb4
bb3:
%8 = begin_access [read] [dynamic] [no_nested_conflict] %0 : $*X
%9 = load %8 : $*X
end_access %8 : $*X
%cond3 = integer_literal $Builtin.Int1, 1
cond_br %cond3, bb2, bb1
bb4:
%10 = begin_access [read] [dynamic] [no_nested_conflict] %0 : $*X
%11 = load %10 : $*X
end_access %10 : $*X
%12 = tuple ()
return %12 : $()
}
// public func testIrreducibleGraph2() {
// Checks detection of irreducible control flow / bail for *some* of them
//
// CHECK-LABEL: sil @testIrreducibleGraph2 : $@convention(thin) () -> () {
// CHECK: [[GLOBAL:%.*]] = global_addr @globalX : $*X
// CHECK-NEXT: br bb1
// CHECK: cond_br {{.*}}, bb2, bb3
// CHECK: bb2:
// CHECK: [[BEGIN2:%.*]] = begin_access [read] [dynamic] [no_nested_conflict] [[GLOBAL]] : $*X
// CHECK-NEXT: load [[BEGIN2]] : $*X
// CHECK-NEXT: end_access [[BEGIN2]] : $*X
// CHECK-NEXT: br bb3
// CHECK: bb3:
// CHECK: [[BEGIN3:%.*]] = begin_access [read] [dynamic] [no_nested_conflict] [[GLOBAL]] : $*X
// CHECK-NEXT: load [[BEGIN3]] : $*X
// CHECK-NEXT: end_access [[BEGIN3]] : $*X
// CHECK: br bb4
// CHECK: [[BEGIN4:%.*]] = begin_access [read] [static] [no_nested_conflict] [[GLOBAL]] : $*X
// CHECK-NEXT: load [[BEGIN4]] : $*X
// CHECK-NEXT: end_access [[BEGIN4]] : $*X
// CHECK: cond_br {{.*}}, bb2, bb5
// CHECK: [[BEGIN5:%.*]] = begin_access [read] [static] [no_nested_conflict] [[GLOBAL]] : $*X
// CHECK-NEXT: load [[BEGIN5]] : $*X
// CHECK-NEXT: end_access [[BEGIN5]] : $*X
// CHECK-NOT: begin_access
// CHECK-LABEL: } // end sil function 'testIrreducibleGraph2'
sil @testIrreducibleGraph2 : $@convention(thin) () -> () {
bb0:
%0 = global_addr @globalX: $*X
br bb1
bb1:
%cond1 = integer_literal $Builtin.Int1, 1
cond_br %cond1, bb2, bb3
bb2:
%6 = begin_access [read] [dynamic] [no_nested_conflict] %0 : $*X
%7 = load %6 : $*X
end_access %6 : $*X
br bb3
bb3:
%8 = begin_access [read] [dynamic] [no_nested_conflict] %0 : $*X
%9 = load %8 : $*X
end_access %8 : $*X
br bb4
bb4:
%10 = begin_access [read] [dynamic] [no_nested_conflict] %0 : $*X
%11 = load %10 : $*X
end_access %10 : $*X
%cond2 = integer_literal $Builtin.Int1, 1
cond_br %cond2, bb2, bb5
bb5:
%13 = begin_access [read] [dynamic] [no_nested_conflict] %0 : $*X
%14 = load %13 : $*X
end_access %13 : $*X
%16 = tuple ()
return %16 : $()
}
// public func testDomUnpaired() {
// Checks 3 scopes, two of which are dominated and access the same storage
// However, The second access is unpaired - we can’t optimized
//
// CHECK-LABEL: sil @testDomUnpaired : $@convention(thin) () -> () {
// CHECK: [[GLOBAL:%.*]] = global_addr @globalX : $*X
// CHECK-NEXT: [[BEGIN:%.*]] = begin_access [read] [dynamic] [[GLOBAL]] : $*X
// CHECK-NEXT: load [[BEGIN]] : $*X
// CHECK-NEXT: end_access [[BEGIN]] : $*X
// CHECK-NEXT: [[ALLOC:%.*]] = alloc_stack $Builtin.UnsafeValueBuffer
// CHECK-NEXT: begin_unpaired_access [read] [dynamic] [[GLOBAL]] : $*X, [[ALLOC]] : $*Builtin.UnsafeValueBuffer
// CHECK-NEXT: end_unpaired_access [dynamic] [[ALLOC]] : $*Builtin.UnsafeValueBuffer
// CHECK-NEXT: [[BEGIN:%.*]] = begin_access [read] [dynamic] [no_nested_conflict] [[GLOBAL]] : $*X
// CHECK-NEXT: load [[BEGIN]] : $*X
// CHECK-NEXT: end_access [[BEGIN]] : $*X
// CHECK-NOT: begin_access
// CHECK-LABEL: } // end sil function 'testDomUnpaired'
sil @testDomUnpaired : $@convention(thin) () -> () {
bb0:
%0 = global_addr @globalX: $*X
%1 = begin_access [read] [dynamic] %0 : $*X
%2 = load %1 : $*X
end_access %1 : $*X
%buffer = alloc_stack $Builtin.UnsafeValueBuffer
begin_unpaired_access [read] [dynamic] %0 : $*X, %buffer : $*Builtin.UnsafeValueBuffer
end_unpaired_access [dynamic] %buffer : $*Builtin.UnsafeValueBuffer
%7 = begin_access [read] [dynamic] [no_nested_conflict] %0 : $*X
%8 = load %7 : $*X
end_access %7 : $*X
dealloc_stack %buffer : $*Builtin.UnsafeValueBuffer
%10 = tuple ()
return %10 : $()
}
// public func testLoopDominatingAccessAdderSimple() {
// Checks creation of new scope in loop preheader
//
// CHECK-LABEL: sil @testLoopDominatingAccessAdderSimple : $@convention(thin) () -> () {
// CHECK: [[GLOBAL:%.*]] = global_addr @globalX : $*X
// CHECK: bb1:
// CHECK-NEXT: [[BEGIN:%.*]] = begin_access [read] [dynamic] [no_nested_conflict] [[GLOBAL]] : $*X
// CHECK-NEXT: end_access [[BEGIN]] : $*X
// CHECK: bb3:
// CHECK: [[BEGIN2:%.*]] = begin_access [read] [static] [no_nested_conflict] [[GLOBAL]] : $*X
// CHECK-NEXT: load [[BEGIN2]] : $*X
// CHECK-NEXT: end_access [[BEGIN2]] : $*X
// CHECK: cond_br {{.*}}, bb2, bb4
// CHECK-LABEL: } // end sil function 'testLoopDominatingAccessAdderSimple'
sil @testLoopDominatingAccessAdderSimple : $@convention(thin) () -> () {
bb0:
%0 = global_addr @globalX: $*X
br bb1
bb1:
br bb2
bb2:
br bb3
bb3:
%4 = begin_access [read] [dynamic] [no_nested_conflict] %0 : $*X
%5 = load %4 : $*X
end_access %4 : $*X
%cond = integer_literal $Builtin.Int1, 1
cond_br %cond, bb2, bb4
bb4:
%10 = tuple ()
return %10 : $()
}
// public func testLoopDominatingAccessAdderBailSimple() {
// Checks bailing on the creation of new scope in loop preheader if the access has a nested conflict
//
// CHECK-LABEL: sil @testLoopDominatingAccessAdderBailSimple : $@convention(thin) () -> () {
// CHECK: [[GLOBAL:%.*]] = global_addr @globalX : $*X
// CHECK: bb1:
// CHECK-NEXT: br bb2
// CHECK: bb3:
// CHECK: [[BEGIN2:%.*]] = begin_access [read] [dynamic] [[GLOBAL]] : $*X
// CHECK-NEXT: load [[BEGIN2]] : $*X
// CHECK-NEXT: end_access [[BEGIN2]] : $*X
// CHECK: cond_br {{.*}}, bb2, bb4
// CHECK-LABEL: } // end sil function 'testLoopDominatingAccessAdderBailSimple'
sil @testLoopDominatingAccessAdderBailSimple : $@convention(thin) () -> () {
bb0:
%0 = global_addr @globalX: $*X
br bb1
bb1:
br bb2
bb2:
br bb3
bb3:
%4 = begin_access [read] [dynamic] %0 : $*X
%5 = load %4 : $*X
end_access %4 : $*X
%cond = integer_literal $Builtin.Int1, 1
cond_br %cond, bb2, bb4
bb4:
%10 = tuple ()
return %10 : $()
}
// public func testLoopDominatingAccessAdderMulti() {
// Checks creation of a single new scope in loop preheader if we have multi-access in loop
//
// CHECK-LABEL: sil @testLoopDominatingAccessAdderMulti : $@convention(thin) () -> () {
// CHECK: [[GLOBAL:%.*]] = global_addr @globalX : $*X
// CHECK: bb1:
// CHECK-NEXT: [[BEGIN:%.*]] = begin_access [read] [dynamic] [no_nested_conflict] [[GLOBAL]] : $*X
// CHECK-NEXT: end_access [[BEGIN]] : $*X
// CHECK: bb3:
// CHECK: [[BEGIN2:%.*]] = begin_access [read] [static] [no_nested_conflict] [[GLOBAL]] : $*X
// CHECK-NEXT: load [[BEGIN2]] : $*X
// CHECK-NEXT: end_access [[BEGIN2]] : $*X
// CHECK: [[BEGIN3:%.*]] = begin_access [read] [static] [no_nested_conflict] [[GLOBAL]] : $*X
// CHECK-NEXT: load [[BEGIN3]] : $*X
// CHECK-NEXT: end_access [[BEGIN3]] : $*X
// CHECK: cond_br {{.*}}, bb2, bb4
// CHECK-LABEL: } // end sil function 'testLoopDominatingAccessAdderMulti'
sil @testLoopDominatingAccessAdderMulti : $@convention(thin) () -> () {
bb0:
%0 = global_addr @globalX: $*X
br bb1
bb1:
br bb2
bb2:
br bb3
bb3:
%4 = begin_access [read] [dynamic] [no_nested_conflict] %0 : $*X
%5 = load %4 : $*X
end_access %4 : $*X
%6 = begin_access [read] [dynamic] [no_nested_conflict] %0 : $*X
%7 = load %6 : $*X
end_access %6 : $*X
%cond = integer_literal $Builtin.Int1, 1
cond_br %cond, bb2, bb4
bb4:
%10 = tuple ()
return %10 : $()
}
// public func testLoopDominatingAccessAdderMultiChangeKind() {
// Checks creation of a single new scope in loop preheader with [modify]
// if one of the accesses changes the kind we first encounter
//
// CHECK-LABEL: sil @testLoopDominatingAccessAdderMultiChangeKind : $@convention(thin) () -> () {
// CHECK: [[GLOBAL:%.*]] = global_addr @globalX : $*X
// CHECK: bb1:
// CHECK-NEXT: [[BEGIN:%.*]] = begin_access [modify] [dynamic] [no_nested_conflict] [[GLOBAL]] : $*X
// CHECK-NEXT: end_access [[BEGIN]] : $*X
// CHECK: bb3:
// CHECK: [[BEGIN2:%.*]] = begin_access [read] [static] [no_nested_conflict] [[GLOBAL]] : $*X
// CHECK-NEXT: load [[BEGIN2]] : $*X
// CHECK-NEXT: end_access [[BEGIN2]] : $*X
// CHECK: [[BEGIN3:%.*]] = begin_access [modify] [static] [no_nested_conflict] [[GLOBAL]] : $*X
// CHECK-NEXT: load [[BEGIN3]] : $*X
// CHECK-NEXT: end_access [[BEGIN3]] : $*X
// CHECK: [[BEGIN4:%.*]] = begin_access [read] [static] [no_nested_conflict] [[GLOBAL]] : $*X
// CHECK-NEXT: load [[BEGIN4]] : $*X
// CHECK-NEXT: end_access [[BEGIN4]] : $*X
// CHECK: cond_br {{.*}}, bb2, bb4
// CHECK-LABEL: } // end sil function 'testLoopDominatingAccessAdderMultiChangeKind'
sil @testLoopDominatingAccessAdderMultiChangeKind : $@convention(thin) () -> () {
bb0:
%0 = global_addr @globalX: $*X
br bb1
bb1:
br bb2
bb2:
br bb3
bb3:
%4 = begin_access [read] [dynamic] [no_nested_conflict] %0 : $*X
%5 = load %4 : $*X
end_access %4 : $*X
%6 = begin_access [modify] [dynamic] [no_nested_conflict] %0 : $*X
%7 = load %6 : $*X
end_access %6 : $*X
%8 = begin_access [read] [dynamic] [no_nested_conflict] %0 : $*X
%9 = load %8 : $*X
end_access %8 : $*X
%cond = integer_literal $Builtin.Int1, 1
cond_br %cond, bb2, bb4
bb4:
%10 = tuple ()
return %10 : $()
}
// public func testBailOnNestedDomWrite() {
// Checks that we bail when the dominated begin comes before the dominating begin
//
// CHECK-LABEL: sil @testBailOnNestedDomWrite : $@convention(thin) () -> () {
// CHECK: bb0:
// CHECK: [[GLOBAL:%.*]] = global_addr @globalX : $*X
// CHECK: [[BEGIN:%.*]] = begin_access [modify] [dynamic] [[GLOBAL]] : $*X
// CHECK: [[BEGIN2:%.*]] = begin_access [modify] [dynamic] [no_nested_conflict] [[GLOBAL]] : $*X
// CHECK-LABEL: } // end sil function 'testBailOnNestedDomWrite'
sil @testBailOnNestedDomWrite : $@convention(thin) () -> () {
bb0:
%0 = global_addr @globalX: $*X
%4 = begin_access [modify] [dynamic] %0 : $*X
%5 = load %4 : $*X
%7 = begin_access [modify] [dynamic] [no_nested_conflict] %0 : $*X
%8 = load %7 : $*X
end_access %7 : $*X
end_access %4 : $*X
%10 = tuple ()
return %10 : $()
}
// public func testOptOnNestedDomRead() {
//
// Bail on a nested access. The dominator-based algorithm does not
// remove nested access scopes because those could generally be
// conflicts. This one just happens to be a read-read, so it isn't a real
// conflict.
//
// FIXME: simple nested dynamic reads to identical storage could very
// easily be removed via separate logic, but it does not fit with the
// dominator-based algorithm. For example, during the analysis phase, we
// could simply mark the "fully nested" read scopes, then convert them to
// [static] right after the analysis, removing them from the result
// map. I didn't do this because I don't know if it happens in practice.
//
// CHECK-LABEL: sil @testOptOnNestedDomRead : $@convention(thin) () -> () {
// CHECK: bb0:
// CHECK: [[GLOBAL:%.*]] = global_addr @globalX : $*X
// CHECK: [[BEGIN:%.*]] = begin_access [read] [dynamic] [[GLOBAL]] : $*X
// CHECK: [[BEGIN2:%.*]] = begin_access [read] [dynamic] [no_nested_conflict] [[GLOBAL]] : $*X
// CHECK-LABEL: } // end sil function 'testOptOnNestedDomRead'
sil @testOptOnNestedDomRead : $@convention(thin) () -> () {
bb0:
%0 = global_addr @globalX: $*X
%4 = begin_access [read] [dynamic] %0 : $*X
%5 = load %4 : $*X
%7 = begin_access [read] [dynamic] [no_nested_conflict] %0 : $*X
%8 = load %7 : $*X
end_access %7 : $*X
end_access %4 : $*X
%10 = tuple ()
return %10 : $()
}