| // 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 : $() |
| } |