mirror of
https://github.com/apple/swift.git
synced 2026-01-07 22:52:31 +01:00
Type annotations for instruction operands are omitted, e.g. ``` %3 = struct $S(%1, %2) ``` Operand types are redundant anyway and were only used for sanity checking in the SIL parser. But: operand types _are_ printed if the definition of the operand value was not printed yet. This happens: * if the block with the definition appears after the block where the operand's instruction is located * if a block or instruction is printed in isolation, e.g. in a debugger The old behavior can be restored with `-Xllvm -sil-print-types`. This option is added to many existing test files which check for operand types in their check-lines.
557 lines
19 KiB
Plaintext
557 lines
19 KiB
Plaintext
// RUN: %target-sil-opt -sil-print-types -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 : $()
|
||
}
|