blob: c119c9c3772bf3db5fe1c5ff5ed72abfdc232c8d [file] [log] [blame]
// RUN: %target-sil-opt -access-enforcement-opts -I %S/Inputs/abi %s | %FileCheck %s
sil_stage canonical
import Builtin
import Swift
import SwiftShims
import c_layout
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
}
// func testNestedAccess() {
// readAndPerform(&globalX) {
// globalX = X()
// }
// modifyAndPerform(&globalX) {
// let l = globalX
// _blackHole(l)
// }
// modifyAndPerform(&globalX) {
// globalX.i = 12
// }
// }
// Preserve begin/end scope for nested conflicts,
// after inlining (read|modify)AndPerform.
// need to split it into 3 SIL functions else merging would kick in and we would be testing something else
//
// CHECK-LABEL: sil hidden @testNestedAccess1 : $@convention(thin) () -> () {
// CHECK: [[F1:%.*]] = function_ref @testNestedAccessClosure1 : $@convention(thin) () -> ()
// CHECK: [[C1:%.*]] = convert_function [[F1]] : $@convention(thin) () -> () to $@convention(thin) @noescape () -> ()
// CHECK: [[TF1:%.*]] = thin_to_thick_function [[C1]] : $@convention(thin) @noescape () -> () to $@noescape @callee_guaranteed ()
// CHECK: [[A1:%.*]] = begin_access [read] [dynamic] %0 : $*X
// CHECK: apply [[TF1]]() : $@noescape @callee_guaranteed () -> ()
// CHECK: end_access [[A1]] : $*X
// CHECK-LABEL: } // end sil function 'testNestedAccess1'
sil hidden @testNestedAccess1 : $@convention(thin) () -> () {
bb0:
%2 = global_addr @globalX: $*X
%3 = function_ref @testNestedAccessClosure1 : $@convention(thin) () -> ()
%4 = convert_function %3 : $@convention(thin) () -> () to $@convention(thin) @noescape () -> ()
%5 = thin_to_thick_function %4 : $@convention(thin) @noescape () -> () to $@noescape @callee_guaranteed () -> ()
%6 = begin_access [read] [dynamic] %2 : $*X
%9 = apply %5() : $@noescape @callee_guaranteed () -> ()
end_access %6 : $*X
%36 = tuple ()
return %36 : $()
}
// CHECK-LABEL: sil hidden @testNestedAccess2 : $@convention(thin) () -> () {
// CHECK: [[F2:%.*]] = function_ref @testNestedAccessClosure2 : $@convention(thin) () -> ()
// CHECK: [[C2:%.*]] = convert_function [[F2]] : $@convention(thin) () -> () to $@convention(thin) @noescape () -> ()
// CHECK: [[TF2:%.*]] = thin_to_thick_function [[C2]] : $@convention(thin) @noescape () -> () to $@noescape @callee_guaranteed () -> ()
// CHECK: [[A2:%.*]] = begin_access [modify] [dynamic] %0 : $*X
// CHECK: apply [[TF2]]() : $@noescape @callee_guaranteed () -> ()
// CHECK: end_access [[A2]] : $*X
// CHECK-LABEL: } // end sil function 'testNestedAccess2'
sil hidden @testNestedAccess2 : $@convention(thin) () -> () {
bb0:
%2 = global_addr @globalX: $*X
%15 = function_ref @testNestedAccessClosure2 : $@convention(thin) () -> ()
%16 = convert_function %15 : $@convention(thin) () -> () to $@convention(thin) @noescape () -> ()
%17 = thin_to_thick_function %16 : $@convention(thin) @noescape () -> () to $@noescape @callee_guaranteed () -> ()
%18 = begin_access [modify] [dynamic] %2 : $*X
%21 = apply %17() : $@noescape @callee_guaranteed () -> ()
end_access %18 : $*X
%36 = tuple ()
return %36 : $()
}
// CHECK-LABEL: sil hidden @testNestedAccess3 : $@convention(thin) () -> () {
// CHECK: [[F3:%.*]] = function_ref @testNestedAccessClosure3 : $@convention(thin) () -> ()
// CHECK: [[C3:%.*]] = convert_function [[F3]] : $@convention(thin) () -> () to $@convention(thin) @noescape () -> ()
// CHECK: [[TF3:%.*]] = thin_to_thick_function [[C3]] : $@convention(thin) @noescape () -> () to $@noescape @callee_guaranteed () -> ()
// CHECK: [[A3:%.*]] = begin_access [modify] [dynamic] %0 : $*X
// CHECK: apply [[TF3]]() : $@noescape @callee_guaranteed () -> ()
// CHECK: end_access [[A3]] : $*X
// CHECK-LABEL: } // end sil function 'testNestedAccess3'
sil hidden @testNestedAccess3 : $@convention(thin) () -> () {
bb0:
%2 = global_addr @globalX: $*X
%27 = function_ref @testNestedAccessClosure3 : $@convention(thin) () -> ()
%28 = convert_function %27 : $@convention(thin) () -> () to $@convention(thin) @noescape () -> ()
%29 = thin_to_thick_function %28 : $@convention(thin) @noescape () -> () to $@noescape @callee_guaranteed () -> ()
%30 = begin_access [modify] [dynamic] %2 : $*X
%33 = apply %29() : $@noescape @callee_guaranteed () -> ()
end_access %30 : $*X
%36 = tuple ()
return %36 : $()
}
// CHECK-LABEL: sil private @testNestedAccessClosure1 : $@convention(thin) () -> () {
// CHECK: begin_access [modify] [dynamic] [no_nested_conflict]
// CHECK-LABEL: // end sil function 'testNestedAccessClosure1'
sil private @testNestedAccessClosure1 : $@convention(thin) () -> () {
bb0:
%0 = global_addr @globalX: $*X
%1 = metatype $@thin X.Type
// function_ref X.init()
%2 = function_ref @Xinit : $@convention(method) (@thin X.Type) -> X
%3 = apply %2(%1) : $@convention(method) (@thin X.Type) -> X
%4 = begin_access [modify] [dynamic] %0 : $*X
store %3 to %4 : $*X
end_access %4 : $*X
%7 = tuple ()
return %7 : $()
}
// CHECK-LABEL: sil private @testNestedAccessClosure2 : $@convention(thin) () -> () {
// CHECK: begin_access [read] [dynamic] [no_nested_conflict]
// CHECK-LABEL: // end sil function 'testNestedAccessClosure2'
sil private @testNestedAccessClosure2 : $@convention(thin) () -> () {
bb0:
%0 = global_addr @globalX: $*X
%1 = begin_access [read] [dynamic] %0 : $*X
%2 = load %1 : $*X
end_access %1 : $*X
debug_value %2 : $X, let, name "l"
%5 = alloc_stack $X
store %2 to %5 : $*X
dealloc_stack %5 : $*X
%10 = tuple ()
return %10 : $()
}
// CHECK-LABEL: sil private @testNestedAccessClosure3 : $@convention(thin) () -> () {
// CHECK: begin_access [modify] [dynamic] [no_nested_conflict]
// CHECK-LABEL: // end sil function 'testNestedAccessClosure3'
sil private @testNestedAccessClosure3 : $@convention(thin) () -> () {
bb0:
%0 = global_addr @globalX: $*X
%1 = integer_literal $Builtin.Int64, 12
%2 = struct $Int64 (%1 : $Builtin.Int64)
%3 = begin_access [modify] [dynamic] %0 : $*X
%4 = struct_element_addr %3 : $*X, #X.i
store %2 to %4 : $*Int64
end_access %3 : $*X
%7 = tuple ()
return %7 : $()
}
// func testDisjointAccess() {
// modifyAndPerform(&globalOtherX) {
// globalX.i = 12 // no-trap
// }
// }
// Demote disjoint global access to non-nested access.
//
// CHECK-LABEL: sil hidden @testDisjointAccess : $@convention(thin) () -> () {
// CHECK: bb0:
// CHECK: [[GLOBALX:%.*]] = global_addr @globalOtherX : $*X
// CHECK: function_ref @testDisjointAccessClosure1 : $@convention(thin) () -> ()
// CHECK: [[ACCESS:%.*]] = begin_access [modify] [dynamic] [no_nested_conflict] [[GLOBALX]] : $*X
// CHECK: apply %{{.*}}() : $@noescape @callee_guaranteed () -> ()
// CHECK: end_access [[ACCESS]] : $*X
// CHECK-LABEL: } // end sil function 'testDisjointAccess'
sil hidden @testDisjointAccess : $@convention(thin) () -> () {
bb0:
%2 = global_addr @globalOtherX: $*X
%3 = function_ref @testDisjointAccessClosure1 : $@convention(thin) () -> ()
%4 = convert_function %3 : $@convention(thin) () -> () to $@convention(thin) @noescape () -> ()
%5 = thin_to_thick_function %4 : $@convention(thin) @noescape () -> () to $@noescape @callee_guaranteed () -> ()
%6 = begin_access [modify] [dynamic] %2 : $*X
%9 = apply %5() : $@noescape @callee_guaranteed () -> ()
end_access %6 : $*X
%12 = tuple ()
return %12 : $()
}
// CHECK-LABEL: sil private @testDisjointAccessClosure1 : $@convention(thin) () -> () {
// CHECK: begin_access [modify] [dynamic] [no_nested_conflict]
// CHECK-LABEL: // end sil function 'testDisjointAccessClosure1'
sil private @testDisjointAccessClosure1 : $@convention(thin) () -> () {
bb0:
%0 = global_addr @globalX: $*X
%1 = integer_literal $Builtin.Int64, 12
%2 = struct $Int64 (%1 : $Builtin.Int64)
%3 = begin_access [modify] [dynamic] %0 : $*X
%4 = struct_element_addr %3 : $*X, #X.i
store %2 to %4 : $*Int64
end_access %3 : $*X
%7 = tuple ()
return %7 : $()
}
// public func testCaptureReadRead() {
// var x = X()
// readAndPerform(&x) {
// _blackHole(x.i) // no-trap
// }
// }
// Demote both read accesses to non_nested, then remove the local outer access.
//
// CHECK-LABEL: sil @testCaptureReadRead : $@convention(thin) () -> () {
// CHECK: begin_access [read] [static] [no_nested_conflict] %0 : $*X
// CHECK-LABEL: } // end sil function 'testCaptureReadRead'
sil @testCaptureReadRead : $@convention(thin) () -> () {
bb0:
%0 = alloc_stack $X, var, name "x"
%1 = metatype $@thin X.Type
%2 = function_ref @Xinit : $@convention(method) (@thin X.Type) -> X
%3 = apply %2(%1) : $@convention(method) (@thin X.Type) -> X
store %3 to %0 : $*X
%5 = function_ref @testCaptureReadReadClosure1 : $@convention(thin) (@inout_aliasable X) -> ()
%6 = partial_apply [callee_guaranteed] %5(%0) : $@convention(thin) (@inout_aliasable X) -> ()
strong_retain %6 : $@callee_guaranteed () -> ()
%8 = convert_escape_to_noescape %6 : $@callee_guaranteed () -> () to $@noescape @callee_guaranteed () -> ()
%9 = begin_access [read] [dynamic] %0 : $*X
%12 = apply %8() : $@noescape @callee_guaranteed () -> ()
strong_release %6 : $@callee_guaranteed () -> ()
end_access %9 : $*X
strong_release %6 : $@callee_guaranteed () -> ()
dealloc_stack %0 : $*X
%18 = tuple ()
return %18 : $()
}
// CHECK-LABEL: sil private @testCaptureReadReadClosure1 : $@convention(thin) (@inout_aliasable X) -> () {
// CHECK: begin_access [read] [dynamic] [no_nested_conflict] %0 : $*X
// CHECK-LABEL: } // end sil function 'testCaptureReadReadClosure1'
sil private @testCaptureReadReadClosure1 : $@convention(thin) (@inout_aliasable X) -> () {
bb0(%0 : $*X):
%2 = begin_access [read] [dynamic] %0 : $*X
%3 = struct_element_addr %2 : $*X, #X.i
%4 = load %3 : $*Int64
end_access %2 : $*X
%6 = alloc_stack $Int64
store %4 to %6 : $*Int64
dealloc_stack %6 : $*Int64
%11 = tuple ()
return %11 : $()
}
// Without allocBoxToStack:
// public func testCaptureBoxReadRead() {
// var x = X()
// readAndPerform(&x) {
// _blackHole(x.i) // no-trap
// }
// }
// Demote both read accesses to non_nested, and remove the local outer access.
//
// CHECK-LABEL: sil @testCaptureBoxReadRead : $@convention(thin) () -> () {
// CHECK: [[BOX:%.*]] = alloc_box ${ var X }, var, name "x"
// CHECK: [[BOXADR:%.*]] = project_box [[BOX]] : ${ var X }, 0
// CHECK: begin_access [read] [static] [no_nested_conflict] [[BOXADR]] : $*X
// CHECK-LABEL: } // end sil function 'testCaptureBoxReadRead'
sil @testCaptureBoxReadRead : $@convention(thin) () -> () {
bb0:
%0 = alloc_box ${ var X }, var, name "x"
%1 = project_box %0 : ${ var X }, 0
%2 = metatype $@thin X.Type
%3 = function_ref @Xinit : $@convention(method) (@thin X.Type) -> X
%4 = apply %3(%2) : $@convention(method) (@thin X.Type) -> X
store %4 to %1 : $*X
%6 = function_ref @testCaptureBoxReadReadClosure1 : $@convention(thin) (@guaranteed { var X }) -> ()
strong_retain %0 : ${ var X }
%8 = partial_apply [callee_guaranteed] %6(%0) : $@convention(thin) (@guaranteed { var X }) -> ()
%9 = begin_access [read] [dynamic] %1 : $*X
%12 = apply %8() : $@callee_guaranteed () -> ()
end_access %9 : $*X
strong_release %8 : $@callee_guaranteed () -> ()
strong_release %0 : ${ var X }
%17 = tuple ()
return %17 : $()
}
// CHECK-LABEL: sil private @testCaptureBoxReadReadClosure1 : $@convention(thin) (@guaranteed { var X }) -> () {
// CHECK: [[BOXADR:%.*]] = project_box %0 : ${ var X }, 0
// CHECK: begin_access [read] [dynamic] [no_nested_conflict] %1 : $*X
// CHECK-LABEL: } // end sil function 'testCaptureBoxReadReadClosure1'
sil private @testCaptureBoxReadReadClosure1 : $@convention(thin) (@guaranteed { var X }) -> () {
bb0(%0 : ${ var X }):
%1 = project_box %0 : ${ var X }, 0
%3 = begin_access [read] [dynamic] %1 : $*X
%4 = struct_element_addr %3 : $*X, #X.i
%5 = load %4 : $*Int64
end_access %3 : $*X
%7 = alloc_stack $Int64
store %5 to %7 : $*Int64
dealloc_stack %7 : $*Int64
%12 = tuple ()
return %12 : $()
}
// testDoubleCapture()
// public func testDoubleCapture() {
// var x = 3
// let c = { x = 7 }
// // Inside may-escape closure `c`: [read] [dynamic]
// // Inside never-escape closure: [modify] [dynamic]
// doTwo(c, { x = 42 })
// }
// Demote each closure access to non_nested.
sil @testDoubleCapture : $@convention(thin) () -> () {
bb0:
%0 = alloc_box ${ var Int64 }, var, name "x"
%1 = project_box %0 : ${ var Int64 }, 0
%2 = integer_literal $Builtin.Int64, 3
%3 = struct $Int64 (%2 : $Builtin.Int64)
store %3 to %1 : $*Int64
%5 = function_ref @testDoubleCaptureClosure1 : $@convention(thin) (@guaranteed { var Int64 }) -> ()
strong_retain %0 : ${ var Int64 }
%7 = partial_apply [callee_guaranteed] %5(%0) : $@convention(thin) (@guaranteed { var Int64 }) -> ()
%11 = convert_escape_to_noescape %7 : $@callee_guaranteed () -> () to $@noescape @callee_guaranteed () -> ()
%12 = function_ref @testDoubleCaptureClosure2 : $@convention(thin) (@inout_aliasable Int64) -> ()
%13 = partial_apply [callee_guaranteed] %12(%1) : $@convention(thin) (@inout_aliasable Int64) -> ()
%15 = convert_escape_to_noescape %13 : $@callee_guaranteed () -> () to $@noescape @callee_guaranteed () -> ()
%16 = apply %11() : $@noescape @callee_guaranteed () -> ()
%17 = apply %15() : $@noescape @callee_guaranteed () -> ()
strong_release %13 : $@callee_guaranteed () -> ()
strong_release %7 : $@callee_guaranteed () -> ()
strong_release %0 : ${ var Int64 }
%24 = tuple ()
return %24 : $()
}
// CHECK-LABEL: sil private @testDoubleCaptureClosure1 : $@convention(thin) (@guaranteed { var Int64 }) -> () {
// CHECK: [[BOXADR:%.*]] = project_box %0 : ${ var Int64 }, 0
// CHECK: begin_access [modify] [dynamic] [no_nested_conflict] %1 : $*Int64
// CHECK-LABEL: } // end sil function 'testDoubleCaptureClosure1'
sil private @testDoubleCaptureClosure1 : $@convention(thin) (@guaranteed { var Int64 }) -> () {
bb0(%0 : ${ var Int64 }):
%1 = project_box %0 : ${ var Int64 }, 0
%3 = integer_literal $Builtin.Int64, 7
%4 = struct $Int64 (%3 : $Builtin.Int64)
%5 = begin_access [modify] [dynamic] %1 : $*Int64
store %4 to %5 : $*Int64
end_access %5 : $*Int64
%8 = tuple ()
return %8 : $()
}
// CHECK-LABEL: sil private @testDoubleCaptureClosure2 : $@convention(thin) (@inout_aliasable Int64) -> () {
// CHECK: begin_access [modify] [dynamic] [no_nested_conflict] %0 : $*Int64
// CHECK-LABEL: } // end sil function 'testDoubleCaptureClosure2'
sil private @testDoubleCaptureClosure2 : $@convention(thin) (@inout_aliasable Int64) -> () {
bb0(%0 : $*Int64):
%2 = integer_literal $Builtin.Int64, 42
%3 = struct $Int64 (%2 : $Builtin.Int64)
%4 = begin_access [modify] [dynamic] %0 : $*Int64
store %3 to %4 : $*Int64
end_access %4 : $*Int64
%7 = tuple ()
return %7 : $()
}
// public func testInoutReadEscapeRead() {
// var x = 3
// let c = { let y = x; _blackHole(y) }
// readAndPerform(&x, closure: c)
// _blackHole(x)
// }
// Demote read/read access to [no_nested_conflict].
//
// CHECK-LABEL: sil @testInoutReadEscapeRead : $@convention(thin) () -> () {
// CHECK: [[BOX:%.*]] = alloc_box ${ var Int64 }, var, name "x"
// CHECK: [[BOXADR:%.*]] = project_box [[BOX]] : ${ var Int64 }, 0
// CHECK: begin_access [read] [static] [no_nested_conflict] [[BOXADR]] : $*Int64
// CHECK-NOT: begin_access
// CHECK-LABEL: } // end sil function 'testInoutReadEscapeRead'
sil @testInoutReadEscapeRead : $@convention(thin) () -> () {
bb0:
%0 = alloc_box ${ var Int64 }, var, name "x"
%1 = project_box %0 : ${ var Int64 }, 0
%2 = integer_literal $Builtin.Int64, 3
%3 = struct $Int64 (%2 : $Builtin.Int64)
store %3 to %1 : $*Int64
%5 = function_ref @testInoutReadEscapeReadClosure1 : $@convention(thin) (@guaranteed { var Int64 }) -> ()
strong_retain %0 : ${ var Int64 }
%7 = partial_apply [callee_guaranteed] %5(%0) : $@convention(thin) (@guaranteed { var Int64 }) -> ()
strong_retain %7 : $@callee_guaranteed () -> ()
strong_retain %7 : $@callee_guaranteed () -> ()
%11 = convert_escape_to_noescape %7 : $@callee_guaranteed () -> () to $@noescape @callee_guaranteed () -> ()
%12 = begin_access [read] [dynamic] %1 : $*Int64
%15 = apply %11() : $@noescape @callee_guaranteed () -> ()
strong_release %7 : $@callee_guaranteed () -> ()
end_access %12 : $*Int64
strong_release %7 : $@callee_guaranteed () -> ()
%20 = begin_access [read] [dynamic] %1 : $*Int64
%21 = load %20 : $*Int64
end_access %20 : $*Int64
%23 = alloc_stack $Int64
store %21 to %23 : $*Int64
dealloc_stack %23 : $*Int64
strong_release %7 : $@callee_guaranteed () -> ()
strong_release %0 : ${ var Int64 }
%30 = tuple ()
return %30 : $()
}
// CHECK-LABEL: sil private @testInoutReadEscapeReadClosure1 : $@convention(thin) (@guaranteed { var Int64 }) -> () {
// CHECK: [[BOXADR:%.*]] = project_box %0 : ${ var Int64 }, 0
// CHECK: begin_access [read] [dynamic] [no_nested_conflict] [[BOXADR]] : $*Int64
// CHECK-LABEL: } // end sil function 'testInoutReadEscapeReadClosure1'
sil private @testInoutReadEscapeReadClosure1 : $@convention(thin) (@guaranteed { var Int64 }) -> () {
bb0(%0 : ${ var Int64 }):
%1 = project_box %0 : ${ var Int64 }, 0
%3 = begin_access [read] [dynamic] %1 : $*Int64
%4 = load %3 : $*Int64
end_access %3 : $*Int64
debug_value %4 : $Int64, let, name "y"
%7 = alloc_stack $Int64
store %4 to %7 : $*Int64
dealloc_stack %7 : $*Int64
%12 = tuple ()
return %12 : $()
}
// public func testInoutReadEscapeWrite() {
// var x = 3
// let c = { x = 42 }
// readAndPerform(&x, closure: c)
// _blackHole(x)
// }
// Preserve the scope of the outer inout access. Runtime trap expected.
//
// CHECK-LABEL: sil @testInoutReadEscapeWrite : $@convention(thin) () -> () {
// CHECK: [[BOX:%.*]] = alloc_box ${ var Int64 }, var, name "x"
// CHECK: [[BOXADR:%.*]] = project_box [[BOX]] : ${ var Int64 }, 0
// CHECK: [[BEGIN:%.*]] = begin_access [read] [dynamic] [[BOXADR]] : $*Int64
// CHECK: apply
// CHECK-NOT: begin_access
// CHECK: end_access [[BEGIN]]
// CHECK-NOT: begin_access
// CHECK-LABEL: } // end sil function 'testInoutReadEscapeWrite'
sil @testInoutReadEscapeWrite : $@convention(thin) () -> () {
bb0:
%0 = alloc_box ${ var Int64 }, var, name "x"
%1 = project_box %0 : ${ var Int64 }, 0
%2 = integer_literal $Builtin.Int64, 3
%3 = struct $Int64 (%2 : $Builtin.Int64)
store %3 to %1 : $*Int64
%5 = function_ref @testInoutReadEscapeWriteClosure1 : $@convention(thin) (@guaranteed { var Int64 }) -> ()
strong_retain %0 : ${ var Int64 }
%7 = partial_apply [callee_guaranteed] %5(%0) : $@convention(thin) (@guaranteed { var Int64 }) -> ()
strong_retain %7 : $@callee_guaranteed () -> ()
strong_retain %7 : $@callee_guaranteed () -> ()
%11 = convert_escape_to_noescape %7 : $@callee_guaranteed () -> () to $@noescape @callee_guaranteed () -> ()
%12 = begin_access [read] [dynamic] %1 : $*Int64
%15 = apply %11() : $@noescape @callee_guaranteed () -> ()
strong_release %7 : $@callee_guaranteed () -> ()
end_access %12 : $*Int64
strong_release %7 : $@callee_guaranteed () -> ()
%20 = begin_access [read] [dynamic] %1 : $*Int64
%21 = load %20 : $*Int64
end_access %20 : $*Int64
%23 = alloc_stack $Int64
store %21 to %23 : $*Int64
dealloc_stack %23 : $*Int64
strong_release %7 : $@callee_guaranteed () -> ()
strong_release %0 : ${ var Int64 }
%30 = tuple ()
return %30 : $()
}
// CHECK-LABEL: sil private @testInoutReadEscapeWriteClosure1 : $@convention(thin) (@guaranteed { var Int64 }) -> () {
// CHECK: [[BOXADR:%.*]] = project_box %0 : ${ var Int64 }, 0
// CHECK: begin_access [modify] [dynamic] [no_nested_conflict] %1 : $*Int64
// CHECK-LABEL: } // end sil function 'testInoutReadEscapeWriteClosure1'
sil private @testInoutReadEscapeWriteClosure1 : $@convention(thin) (@guaranteed { var Int64 }) -> () {
bb0(%0 : ${ var Int64 }):
%1 = project_box %0 : ${ var Int64 }, 0
%3 = integer_literal $Builtin.Int64, 42
%4 = struct $Int64 (%3 : $Builtin.Int64)
%5 = begin_access [modify] [dynamic] %1 : $*Int64
store %4 to %5 : $*Int64
end_access %5 : $*Int64
%8 = tuple ()
return %8 : $()
}
// public func testInoutWriteEscapeRead() {
// var x = 3
// let c = { let y = x; _blackHole(y) }
// modifyAndPerform(&x, closure: c)
// _blackHole(x)
// }
// Preserve the scope of the outer inout access. Runtime trap expected.
//
// FIXME: The optimization should be able to merge these accesses, but
// it must first prove that no other conflicting read accesses occur
// within the existing read access scopes.
//
// CHECK-LABEL: sil @$s17enforce_with_opts24testInoutWriteEscapeReadyyF : $@convention(thin) () -> () {
// CHECK: [[BOX:%.*]] = alloc_box ${ var Int64 }, var, name "x"
// CHECK: [[BOXADR:%.*]] = project_box [[BOX]] : ${ var Int64 }, 0
// CHECK: [[BEGIN:%.*]] = begin_access [modify] [dynamic] [[BOXADR]] : $*Int64
// CHECK: apply
// CHECK: end_access [[BEGIN]]
// CHECK: [[BEGIN2:%.*]] = begin_access [read] [dynamic] [no_nested_conflict] [[BOXADR]] : $*Int64
// CHECK: load [[BEGIN2]]
// CHECK: end_access [[BEGIN2]]
// CHECK-LABEL: } // end sil function '$s17enforce_with_opts24testInoutWriteEscapeReadyyF'
sil @$s17enforce_with_opts24testInoutWriteEscapeReadyyF : $@convention(thin) () -> () {
bb0:
%0 = alloc_box ${ var Int64 }, var, name "x"
%1 = project_box %0 : ${ var Int64 }, 0
%2 = integer_literal $Builtin.Int64, 3
%3 = struct $Int64 (%2 : $Builtin.Int64)
store %3 to %1 : $*Int64
// function_ref closure #1 in testInoutWriteEscapeRead()
%5 = function_ref @$s17enforce_with_opts24testInoutWriteEscapeReadyyFyycfU_ : $@convention(thin) (@guaranteed { var Int64 }) -> ()
strong_retain %0 : ${ var Int64 }
%7 = partial_apply [callee_guaranteed] %5(%0) : $@convention(thin) (@guaranteed { var Int64 }) -> ()
debug_value %7 : $@callee_guaranteed () -> (), let, name "c"
strong_retain %7 : $@callee_guaranteed () -> ()
strong_retain %7 : $@callee_guaranteed () -> ()
%11 = convert_escape_to_noescape %7 : $@callee_guaranteed () -> () to $@noescape @callee_guaranteed () -> ()
%12 = begin_access [modify] [dynamic] %1 : $*Int64
%13 = tuple ()
%14 = tuple ()
%15 = apply %11() : $@noescape @callee_guaranteed () -> ()
%16 = tuple ()
strong_release %7 : $@callee_guaranteed () -> ()
end_access %12 : $*Int64
strong_release %7 : $@callee_guaranteed () -> ()
%20 = begin_access [read] [dynamic] %1 : $*Int64
%21 = load %20 : $*Int64
end_access %20 : $*Int64
%23 = alloc_stack $Int64
store %21 to %23 : $*Int64
dealloc_stack %23 : $*Int64
strong_release %7 : $@callee_guaranteed () -> ()
strong_release %0 : ${ var Int64 }
%30 = tuple ()
return %30 : $()
}
// closure #1 in testInoutWriteEscapeRead()
// CHECK-LABEL: sil private @$s17enforce_with_opts24testInoutWriteEscapeReadyyFyycfU_ : $@convention(thin) (@guaranteed { var Int64 }) -> () {
// CHECK: [[BOXADR:%.*]] = project_box %0 : ${ var Int64 }, 0
// CHECK: begin_access [read] [dynamic] [no_nested_conflict] %1
// CHECK-LABEL: } // end sil function '$s17enforce_with_opts24testInoutWriteEscapeReadyyFyycfU_'
sil private @$s17enforce_with_opts24testInoutWriteEscapeReadyyFyycfU_ : $@convention(thin) (@guaranteed { var Int64 }) -> () {
// %0
bb0(%0 : ${ var Int64 }):
%1 = project_box %0 : ${ var Int64 }, 0
debug_value_addr %1 : $*Int64, var, name "x", argno 1
%3 = begin_access [read] [dynamic] %1 : $*Int64
%4 = load %3 : $*Int64
end_access %3 : $*Int64
debug_value %4 : $Int64, let, name "y"
%7 = alloc_stack $Int64
store %4 to %7 : $*Int64
dealloc_stack %7 : $*Int64
%12 = tuple ()
return %12 : $()
}
// public func testInoutWriteEscapeWrite() {
// var x = 3
// let c = { x = 42 }
// modifyAndPerform(&x, closure: c)
// _blackHole(x)
// }
// Preserve the scope of the outer inout access. Runtime trap expected.
//
// FIXME: The optimization should be able to merge these accesses, but
// it must first prove that no other conflicting read accesses occur
// within the existing read access scopes.
//
// CHECK-LABEL: sil @$s17enforce_with_opts020testInoutWriteEscapeF0yyF : $@convention(thin) () -> () {
// CHECK: [[BOX:%.*]] = alloc_box ${ var Int64 }, var, name "x"
// CHECK: [[BOXADR:%.*]] = project_box [[BOX]] : ${ var Int64 }, 0
// CHECK: [[BEGIN:%.*]] = begin_access [modify] [dynamic] [[BOXADR]] : $*Int64
// CHECK: apply
// CHECK: end_access [[BEGIN]]
// CHECK: [[BEGIN2:%.*]] = begin_access [read] [dynamic] [no_nested_conflict] [[BOXADR]] : $*Int64
// CHECK: load [[BEGIN2]]
// CHECK: end_access [[BEGIN2]]
// CHECK-LABEL: } // end sil function '$s17enforce_with_opts020testInoutWriteEscapeF0yyF'
sil @$s17enforce_with_opts020testInoutWriteEscapeF0yyF : $@convention(thin) () -> () {
bb0:
%0 = alloc_box ${ var Int64 }, var, name "x"
%1 = project_box %0 : ${ var Int64 }, 0
%2 = integer_literal $Builtin.Int64, 3
%3 = struct $Int64 (%2 : $Builtin.Int64)
store %3 to %1 : $*Int64
// function_ref closure #1 in testInoutWriteEscapeWrite()
%5 = function_ref @$s17enforce_with_opts020testInoutWriteEscapeF0yyFyycfU_ : $@convention(thin) (@guaranteed { var Int64 }) -> ()
strong_retain %0 : ${ var Int64 }
%7 = partial_apply [callee_guaranteed] %5(%0) : $@convention(thin) (@guaranteed { var Int64 }) -> ()
debug_value %7 : $@callee_guaranteed () -> (), let, name "c"
strong_retain %7 : $@callee_guaranteed () -> ()
strong_retain %7 : $@callee_guaranteed () -> ()
%11 = convert_escape_to_noescape %7 : $@callee_guaranteed () -> () to $@noescape @callee_guaranteed () -> ()
%12 = begin_access [modify] [dynamic] %1 : $*Int64
%13 = tuple ()
%14 = tuple ()
%15 = apply %11() : $@noescape @callee_guaranteed () -> ()
%16 = tuple ()
strong_release %7 : $@callee_guaranteed () -> ()
end_access %12 : $*Int64
strong_release %7 : $@callee_guaranteed () -> ()
%20 = begin_access [read] [dynamic] %1 : $*Int64
%21 = load %20 : $*Int64
end_access %20 : $*Int64
%23 = alloc_stack $Int64
store %21 to %23 : $*Int64
dealloc_stack %23 : $*Int64
strong_release %7 : $@callee_guaranteed () -> ()
strong_release %0 : ${ var Int64 }
%30 = tuple ()
return %30 : $()
}
// CHECK-LABEL: sil private @$s17enforce_with_opts020testInoutWriteEscapeF0yyFyycfU_ : $@convention(thin) (@guaranteed { var Int64 }) -> () {
// CHECK: [[BOXADR:%.*]] = project_box %0 : ${ var Int64 }, 0
// CHECK: begin_access [modify] [dynamic] [no_nested_conflict] [[BOXADR]] : $*Int64
// CHECK-LABEL: } // end sil function '$s17enforce_with_opts020testInoutWriteEscapeF0yyFyycfU_'
sil private @$s17enforce_with_opts020testInoutWriteEscapeF0yyFyycfU_ : $@convention(thin) (@guaranteed { var Int64 }) -> () {
// %0
bb0(%0 : ${ var Int64 }):
%1 = project_box %0 : ${ var Int64 }, 0
debug_value_addr %1 : $*Int64, var, name "x", argno 1
%3 = integer_literal $Builtin.Int64, 42
%4 = struct $Int64 (%3 : $Builtin.Int64)
%5 = begin_access [modify] [dynamic] %1 : $*Int64
store %4 to %5 : $*Int64
end_access %5 : $*Int64
%8 = tuple ()
return %8 : $()
}
// testInoutReadNoescapeRead()
// public func testInoutReadNoescapeRead() {
// var x = 3
// let c = { let y = x; _blackHole(y) }
// doOne { readAndPerform(&x, closure: c) }
// }
// The outer inout access is not folded because we the optimizer can't resolve
// the call to the inner closure from the outer closure.
sil @$s23enforce_with_opts_nob2s021testInoutReadNoescapeG0yyF : $@convention(thin) () -> () {
bb0:
%0 = alloc_box ${ var Int64 }, var, name "x"
%1 = project_box %0 : ${ var Int64 }, 0
%2 = integer_literal $Builtin.Int64, 3
%3 = struct $Int64 (%2 : $Builtin.Int64)
store %3 to %1 : $*Int64
// function_ref closure #1 in testInoutReadNoescapeRead()
%5 = function_ref @$s23enforce_with_opts_nob2s021testInoutReadNoescapeG0yyFyycfU_ : $@convention(thin) (@guaranteed { var Int64 }) -> ()
strong_retain %0 : ${ var Int64 }
%7 = partial_apply [callee_guaranteed] %5(%0) : $@convention(thin) (@guaranteed { var Int64 }) -> ()
debug_value %7 : $@callee_guaranteed () -> (), let, name "c"
// function_ref closure #2 in testInoutReadNoescapeRead()
%9 = function_ref @$s23enforce_with_opts_nob2s021testInoutReadNoescapeG0yyFyyXEfU0_ : $@convention(thin) (@inout_aliasable Int64, @guaranteed @callee_guaranteed () -> ()) -> ()
strong_retain %7 : $@callee_guaranteed () -> ()
%11 = partial_apply [callee_guaranteed] %9(%1, %7) : $@convention(thin) (@inout_aliasable Int64, @guaranteed @callee_guaranteed () -> ()) -> ()
strong_retain %11 : $@callee_guaranteed () -> ()
%13 = convert_escape_to_noescape %11 : $@callee_guaranteed () -> () to $@noescape @callee_guaranteed () -> ()
%14 = apply %13() : $@noescape @callee_guaranteed () -> ()
%15 = tuple ()
strong_release %11 : $@callee_guaranteed () -> ()
strong_release %11 : $@callee_guaranteed () -> ()
strong_release %7 : $@callee_guaranteed () -> ()
strong_release %0 : ${ var Int64 }
%20 = tuple ()
return %20 : $()
}
// closure #1 in testInoutReadNoescapeRead()
// CHECK-LABEL: sil private @$s23enforce_with_opts_nob2s021testInoutReadNoescapeG0yyFyycfU_ : $@convention(thin) (@guaranteed { var Int64 }) -> () {
// CHECK: [[BOXADR:%.*]] = project_box %0 : ${ var Int64 }, 0
// CHECK: begin_access [read] [dynamic] [no_nested_conflict] [[BOXADR]] : $*Int64
// CHECK-LABEL: } // end sil function '$s23enforce_with_opts_nob2s021testInoutReadNoescapeG0yyFyycfU_'
sil private @$s23enforce_with_opts_nob2s021testInoutReadNoescapeG0yyFyycfU_ : $@convention(thin) (@guaranteed { var Int64 }) -> () {
// %0
bb0(%0 : ${ var Int64 }):
%1 = project_box %0 : ${ var Int64 }, 0
debug_value_addr %1 : $*Int64, var, name "x", argno 1
%3 = begin_access [read] [dynamic] %1 : $*Int64
%4 = load %3 : $*Int64
end_access %3 : $*Int64
debug_value %4 : $Int64, let, name "y"
%7 = alloc_stack $Int64
store %4 to %7 : $*Int64
dealloc_stack %7 : $*Int64
%12 = tuple ()
return %12 : $()
}
// closure #2 in testInoutReadNoescapeRead()
// CHECK-LABEL: sil private @$s23enforce_with_opts_nob2s021testInoutReadNoescapeG0yyFyyXEfU0_ : $@convention(thin) (@inout_aliasable Int64, @guaranteed @callee_guaranteed () -> ()) -> () {
// CHECK: begin_access [read] [dynamic] %0 : $*Int64
// CHECK-LABEL: } // end sil function '$s23enforce_with_opts_nob2s021testInoutReadNoescapeG0yyFyyXEfU0_'
sil private @$s23enforce_with_opts_nob2s021testInoutReadNoescapeG0yyFyyXEfU0_ : $@convention(thin) (@inout_aliasable Int64, @guaranteed @callee_guaranteed () -> ()) -> () {
// %0
// %1
bb0(%0 : $*Int64, %1 : $@callee_guaranteed () -> ()):
debug_value_addr %0 : $*Int64, var, name "x", argno 1
debug_value %1 : $@callee_guaranteed () -> (), let, name "c", argno 2
%4 = begin_access [read] [dynamic] %0 : $*Int64
%5 = tuple ()
%6 = tuple ()
%7 = apply %1() : $@callee_guaranteed () -> ()
%8 = tuple ()
end_access %4 : $*Int64
%10 = tuple ()
return %10 : $()
}
// testInoutReadNoescapeWrite()
// public func testInoutReadNoescapeWrite() {
// var x = 3
// let c = { x = 7 }
// doOne { readAndPerform(&x, closure: c) }
// }
// The outer inout access scope (closure #2) must be preserved.
// Expected to trap at runtime.
sil @$s23enforce_with_opts_nob2s26testInoutReadNoescapeWriteyyF : $@convention(thin) () -> () {
bb0:
%0 = alloc_box ${ var Int64 }, var, name "x"
%1 = project_box %0 : ${ var Int64 }, 0
%2 = integer_literal $Builtin.Int64, 3
%3 = struct $Int64 (%2 : $Builtin.Int64)
store %3 to %1 : $*Int64
// function_ref closure #1 in testInoutReadNoescapeWrite()
%5 = function_ref @$s23enforce_with_opts_nob2s26testInoutReadNoescapeWriteyyFyycfU_ : $@convention(thin) (@guaranteed { var Int64 }) -> ()
strong_retain %0 : ${ var Int64 }
%7 = partial_apply [callee_guaranteed] %5(%0) : $@convention(thin) (@guaranteed { var Int64 }) -> ()
debug_value %7 : $@callee_guaranteed () -> (), let, name "c"
// function_ref closure #2 in testInoutReadNoescapeWrite()
%9 = function_ref @$s23enforce_with_opts_nob2s26testInoutReadNoescapeWriteyyFyyXEfU0_ : $@convention(thin) (@inout_aliasable Int64, @guaranteed @callee_guaranteed () -> ()) -> ()
strong_retain %7 : $@callee_guaranteed () -> ()
%11 = partial_apply [callee_guaranteed] %9(%1, %7) : $@convention(thin) (@inout_aliasable Int64, @guaranteed @callee_guaranteed () -> ()) -> ()
strong_retain %11 : $@callee_guaranteed () -> ()
%13 = convert_escape_to_noescape %11 : $@callee_guaranteed () -> () to $@noescape @callee_guaranteed () -> ()
%14 = apply %13() : $@noescape @callee_guaranteed () -> ()
%15 = tuple ()
strong_release %11 : $@callee_guaranteed () -> ()
strong_release %11 : $@callee_guaranteed () -> ()
strong_release %7 : $@callee_guaranteed () -> ()
strong_release %0 : ${ var Int64 }
%20 = tuple ()
return %20 : $()
}
// closure #1 in testInoutReadNoescapeWrite()
// CHECK-LABEL: sil private @$s23enforce_with_opts_nob2s26testInoutReadNoescapeWriteyyFyycfU_ : $@convention(thin) (@guaranteed { var Int64 }) -> () {
// CHECK: begin_access [modify] [dynamic] [no_nested_conflict] %1 : $*Int64
// CHECK-LABEL: } // end sil function '$s23enforce_with_opts_nob2s26testInoutReadNoescapeWriteyyFyycfU_'
sil private @$s23enforce_with_opts_nob2s26testInoutReadNoescapeWriteyyFyycfU_ : $@convention(thin) (@guaranteed { var Int64 }) -> () {
// %0
bb0(%0 : ${ var Int64 }):
%1 = project_box %0 : ${ var Int64 }, 0
debug_value_addr %1 : $*Int64, var, name "x", argno 1
%3 = integer_literal $Builtin.Int64, 7
%4 = struct $Int64 (%3 : $Builtin.Int64)
%5 = begin_access [modify] [dynamic] %1 : $*Int64
store %4 to %5 : $*Int64
end_access %5 : $*Int64
%8 = tuple ()
return %8 : $()
}
// closure #2 in testInoutReadNoescapeWrite()
// CHECK-LABEL: sil private @$s23enforce_with_opts_nob2s26testInoutReadNoescapeWriteyyFyyXEfU0_ : $@convention(thin) (@inout_aliasable Int64, @guaranteed @callee_guaranteed () -> ()) -> () {
// CHECK: begin_access [read] [dynamic] %0 : $*Int64
// CHECK-LABEL: } // end sil function '$s23enforce_with_opts_nob2s26testInoutReadNoescapeWriteyyFyyXEfU0_'
sil private @$s23enforce_with_opts_nob2s26testInoutReadNoescapeWriteyyFyyXEfU0_ : $@convention(thin) (@inout_aliasable Int64, @guaranteed @callee_guaranteed () -> ()) -> () {
bb0(%0 : $*Int64, %1 : $@callee_guaranteed () -> ()):
debug_value_addr %0 : $*Int64, var, name "x", argno 1
debug_value %1 : $@callee_guaranteed () -> (), let, name "c", argno 2
%4 = begin_access [read] [dynamic] %0 : $*Int64
%5 = tuple ()
%6 = tuple ()
%7 = apply %1() : $@callee_guaranteed () -> ()
%8 = tuple ()
end_access %4 : $*Int64
%10 = tuple ()
return %10 : $()
}
// testInoutWriteEscapeReadClosure()
// public func testInoutWriteNoescapeReadClosure() {
// var x = 3
// let c = { let y = x; _blackHole(y) }
// doOne { modifyAndPerform(&x, closure: c) }
// }
// The outer inout access scope (closure #2) must be preserved.
// Expected to trap at runtime.
sil @$s23enforce_with_opts_nob2s33testInoutWriteNoescapeReadClosureyyF : $@convention(thin) () -> () {
bb0:
%0 = alloc_box ${ var Int64 }, var, name "x"
%1 = project_box %0 : ${ var Int64 }, 0
%2 = integer_literal $Builtin.Int64, 3
%3 = struct $Int64 (%2 : $Builtin.Int64)
store %3 to %1 : $*Int64
// function_ref closure #1 in testInoutWriteNoescapeReadClosure()
%5 = function_ref @$s23enforce_with_opts_nob2s33testInoutWriteNoescapeReadClosureyyFyycfU_ : $@convention(thin) (@guaranteed { var Int64 }) -> ()
strong_retain %0 : ${ var Int64 }
%7 = partial_apply [callee_guaranteed] %5(%0) : $@convention(thin) (@guaranteed { var Int64 }) -> ()
debug_value %7 : $@callee_guaranteed () -> (), let, name "c"
// function_ref closure #2 in testInoutWriteNoescapeReadClosure()
%9 = function_ref @$s23enforce_with_opts_nob2s33testInoutWriteNoescapeReadClosureyyFyyXEfU0_ : $@convention(thin) (@inout_aliasable Int64, @guaranteed @callee_guaranteed () -> ()) -> ()
strong_retain %7 : $@callee_guaranteed () -> ()
%11 = partial_apply [callee_guaranteed] %9(%1, %7) : $@convention(thin) (@inout_aliasable Int64, @guaranteed @callee_guaranteed () -> ()) -> ()
strong_retain %11 : $@callee_guaranteed () -> ()
%13 = convert_escape_to_noescape %11 : $@callee_guaranteed () -> () to $@noescape @callee_guaranteed () -> ()
%14 = apply %13() : $@noescape @callee_guaranteed () -> ()
%15 = tuple ()
strong_release %11 : $@callee_guaranteed () -> ()
strong_release %11 : $@callee_guaranteed () -> ()
strong_release %7 : $@callee_guaranteed () -> ()
strong_release %0 : ${ var Int64 }
%20 = tuple ()
return %20 : $()
}
// closure #1 in testInoutWriteNoescapeReadClosure()
// CHECK-LABEL: sil private @$s23enforce_with_opts_nob2s33testInoutWriteNoescapeReadClosureyyFyycfU_ : $@convention(thin) (@guaranteed { var Int64 }) -> () {
// CHECK: begin_access [read] [dynamic] [no_nested_conflict] %1 : $*Int64
// CHECK-LABEL: } // end sil function '$s23enforce_with_opts_nob2s33testInoutWriteNoescapeReadClosureyyFyycfU_'
sil private @$s23enforce_with_opts_nob2s33testInoutWriteNoescapeReadClosureyyFyycfU_ : $@convention(thin) (@guaranteed { var Int64 }) -> () {
// %0
bb0(%0 : ${ var Int64 }):
%1 = project_box %0 : ${ var Int64 }, 0
debug_value_addr %1 : $*Int64, var, name "x", argno 1
%3 = begin_access [read] [dynamic] %1 : $*Int64
%4 = load %3 : $*Int64
end_access %3 : $*Int64
debug_value %4 : $Int64, let, name "y"
%7 = alloc_stack $Int64
store %4 to %7 : $*Int64
dealloc_stack %7 : $*Int64
%12 = tuple ()
return %12 : $()
}
// closure #2 in testInoutWriteNoescapeReadClosure()
// CHECK-LABEL: sil private @$s23enforce_with_opts_nob2s33testInoutWriteNoescapeReadClosureyyFyyXEfU0_ : $@convention(thin) (@inout_aliasable Int64, @guaranteed @callee_guaranteed () -> ()) -> () {
// CHECK: begin_access [modify] [dynamic] %0 : $*Int64
// CHECK-LABEL: } // end sil function '$s23enforce_with_opts_nob2s33testInoutWriteNoescapeReadClosureyyFyyXEfU0_'
sil private @$s23enforce_with_opts_nob2s33testInoutWriteNoescapeReadClosureyyFyyXEfU0_ : $@convention(thin) (@inout_aliasable Int64, @guaranteed @callee_guaranteed () -> ()) -> () {
// %0
// %1
bb0(%0 : $*Int64, %1 : $@callee_guaranteed () -> ()):
debug_value_addr %0 : $*Int64, var, name "x", argno 1
debug_value %1 : $@callee_guaranteed () -> (), let, name "c", argno 2
%4 = begin_access [modify] [dynamic] %0 : $*Int64
%5 = tuple ()
%6 = tuple ()
%7 = apply %1() : $@callee_guaranteed () -> ()
%8 = tuple ()
end_access %4 : $*Int64
%10 = tuple ()
return %10 : $()
}
// testInoutWriteNoescapeWriteClosure()
// public func testInoutWriteNoescapeWriteClosure() {
// var x = 3
// let c = { x = 7 }
// doOne { modifyAndPerform(&x, closure: c) }
// }
sil @$s23enforce_with_opts_nob2s022testInoutWriteNoescapeG7ClosureyyF : $@convention(thin) () -> () {
bb0:
%0 = alloc_box ${ var Int64 }, var, name "x"
%1 = project_box %0 : ${ var Int64 }, 0
%2 = integer_literal $Builtin.Int64, 3
%3 = struct $Int64 (%2 : $Builtin.Int64)
store %3 to %1 : $*Int64
// function_ref closure #1 in testInoutWriteNoescapeWriteClosure()
%5 = function_ref @$s23enforce_with_opts_nob2s022testInoutWriteNoescapeG7ClosureyyFyycfU_ : $@convention(thin) (@guaranteed { var Int64 }) -> ()
strong_retain %0 : ${ var Int64 }
%7 = partial_apply [callee_guaranteed] %5(%0) : $@convention(thin) (@guaranteed { var Int64 }) -> ()
debug_value %7 : $@callee_guaranteed () -> (), let, name "c"
// function_ref closure #2 in testInoutWriteNoescapeWriteClosure()
%9 = function_ref @$s23enforce_with_opts_nob2s022testInoutWriteNoescapeG7ClosureyyFyyXEfU0_ : $@convention(thin) (@inout_aliasable Int64, @guaranteed @callee_guaranteed () -> ()) -> ()
strong_retain %7 : $@callee_guaranteed () -> ()
%11 = partial_apply [callee_guaranteed] %9(%1, %7) : $@convention(thin) (@inout_aliasable Int64, @guaranteed @callee_guaranteed () -> ()) -> ()
strong_retain %11 : $@callee_guaranteed () -> ()
%13 = convert_escape_to_noescape %11 : $@callee_guaranteed () -> () to $@noescape @callee_guaranteed () -> ()
%14 = apply %13() : $@noescape @callee_guaranteed () -> ()
%15 = tuple ()
strong_release %11 : $@callee_guaranteed () -> ()
strong_release %11 : $@callee_guaranteed () -> ()
strong_release %7 : $@callee_guaranteed () -> ()
strong_release %0 : ${ var Int64 }
%20 = tuple ()
return %20 : $()
}
// closure #1 in testInoutWriteNoescapeWriteClosure()
// CHECK-LABEL: sil private @$s23enforce_with_opts_nob2s022testInoutWriteNoescapeG7ClosureyyFyycfU_ : $@convention(thin) (@guaranteed { var Int64 }) -> () {
// CHECK: begin_access [modify] [dynamic] [no_nested_conflict] %1 : $*Int64
// CHECK-LABEL: } // end sil function '$s23enforce_with_opts_nob2s022testInoutWriteNoescapeG7ClosureyyFyycfU_'
sil private @$s23enforce_with_opts_nob2s022testInoutWriteNoescapeG7ClosureyyFyycfU_ : $@convention(thin) (@guaranteed { var Int64 }) -> () {
// %0
bb0(%0 : ${ var Int64 }):
%1 = project_box %0 : ${ var Int64 }, 0
debug_value_addr %1 : $*Int64, var, name "x", argno 1
%3 = integer_literal $Builtin.Int64, 7
%4 = struct $Int64 (%3 : $Builtin.Int64)
%5 = begin_access [modify] [dynamic] %1 : $*Int64
store %4 to %5 : $*Int64
end_access %5 : $*Int64
%8 = tuple ()
return %8 : $()
}
// closure #2 in testInoutWriteNoescapeWriteClosure()
// CHECK-LABEL: sil private @$s23enforce_with_opts_nob2s022testInoutWriteNoescapeG7ClosureyyFyyXEfU0_ : $@convention(thin) (@inout_aliasable Int64, @guaranteed @callee_guaranteed () -> ()) -> () {
// CHECK: begin_access [modify] [dynamic] %0 : $*Int64
// CHECK-LABEL: } // end sil function '$s23enforce_with_opts_nob2s022testInoutWriteNoescapeG7ClosureyyFyyXEfU0_'
sil private @$s23enforce_with_opts_nob2s022testInoutWriteNoescapeG7ClosureyyFyyXEfU0_ : $@convention(thin) (@inout_aliasable Int64, @guaranteed @callee_guaranteed () -> ()) -> () {
bb0(%0 : $*Int64, %1 : $@callee_guaranteed () -> ()):
debug_value_addr %0 : $*Int64, var, name "x", argno 1
debug_value %1 : $@callee_guaranteed () -> (), let, name "c", argno 2
%4 = begin_access [modify] [dynamic] %0 : $*Int64
%5 = tuple ()
%6 = tuple ()
%7 = apply %1() : $@callee_guaranteed () -> ()
%8 = tuple ()
end_access %4 : $*Int64
%10 = tuple ()
return %10 : $()
}
// public func testOldToNewMapRead() {
// Checks merging of 3 scopes resulting in a larger read scope
//
// CHECK-LABEL: sil @testOldToNewMapRead : $@convention(thin) () -> () {
// CHECK: [[GLOBAL:%.*]] = global_addr @globalX : $*X
// CHECK-NEXT: [[BEGIN:%.*]] = begin_access [read] [dynamic] [no_nested_conflict] [[GLOBAL]] : $*X
// CHECK-NEXT: load [[BEGIN]] : $*X
// CHECK-NEXT: load [[BEGIN]] : $*X
// CHECK-NEXT: load [[BEGIN]] : $*X
// CHECK-NEXT: end_access [[BEGIN]] : $*X
// CHECK-NOT: begin_access
// CHECK-LABEL: } // end sil function 'testOldToNewMapRead'
sil @testOldToNewMapRead : $@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] %0 : $*X
%5 = load %4 : $*X
end_access %4 : $*X
%7 = begin_access [read] [dynamic] %0 : $*X
%8 = load %7 : $*X
end_access %7 : $*X
%10 = tuple ()
return %10 : $()
}
// public func testOldToNewMapReadMayRelease() {
// Checks merging 2 out of 3 scopes resulting in a larger read scope
// Due to a MayRelease instruction before the 3rd scope
//
// CHECK-LABEL: sil @testOldToNewMapReadMayRelease : $@convention(thin) () -> () {
// CHECK: [[GLOBAL:%.*]] = global_addr @globalX : $*X
// CHECK-NEXT: [[BEGIN:%.*]] = begin_access [read] [dynamic] [no_nested_conflict] [[GLOBAL]] : $*X
// CHECK-NEXT: load [[BEGIN]] : $*X
// CHECK-NEXT: load [[BEGIN]] : $*X
// CHECK-NEXT: end_access [[BEGIN]] : $*X
// CHECK-NEXT: strong_release
// CHECK-NEXT: [[BEGIN:%.*]] = begin_access [read] [dynamic] [no_nested_conflict] [[GLOBAL]] : $*X
// CHECK-LABEL: } // end sil function 'testOldToNewMapReadMayRelease'
sil @testOldToNewMapReadMayRelease : $@convention(thin) () -> () {
bb0:
%alloc = alloc_box ${ var Int32 }, var, name "y"
%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] %0 : $*X
%5 = load %4 : $*X
end_access %4 : $*X
strong_release %alloc : ${ var Int32 }
%7 = begin_access [read] [dynamic] %0 : $*X
%8 = load %7 : $*X
end_access %7 : $*X
%10 = tuple ()
return %10 : $()
}
// public func testOldToNewMapWrite) {
// Checks merging of 3 scopes resulting in a larger modify scope
//
// FIXME: The optimization should be able to merge these accesses, but
// it must first prove that no other conflicting read accesses occur
// within the existing read access scopes.
//
// CHECK-LABEL: sil @testOldToNewMapWrite : $@convention(thin) () -> () {
// CHECK: [[GLOBAL:%.*]] = global_addr @globalX : $*X
// CHECK-NEXT: [[BEGIN:%.*]] = begin_access [read] [dynamic] [no_nested_conflict] [[GLOBAL]] : $*X
// CHECK-NEXT: load [[BEGIN]] : $*X
// CHECK-NEXT: end_access [[BEGIN]] : $*X
// CHECK: [[BEGIN2:%.*]] = begin_access [modify] [dynamic] [no_nested_conflict] [[GLOBAL]] : $*X
// CHECK-NEXT: store {{.*}} to [[BEGIN2]] : $*X
// CHECK-NEXT: end_access [[BEGIN2]] : $*X
// CHECK: [[BEGIN3:%.*]] = begin_access [read] [dynamic] [no_nested_conflict] [[GLOBAL]] : $*X
// CHECK-NEXT: load [[BEGIN3]] : $*X
// CHECK-NEXT: end_access [[BEGIN3]] : $*X
// CHECK-LABEL: } // end sil function 'testOldToNewMapWrite'
sil @testOldToNewMapWrite : $@convention(thin) () -> () {
bb0:
%0 = global_addr @globalX: $*X
%1 = begin_access [read] [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] %0 : $*X
store %6 to %7 : $*X
end_access %7 : $*X
%10 = begin_access [read] [dynamic] %0 : $*X
%11 = load %10 : $*X
end_access %10 : $*X
%12 = tuple ()
return %12 : $()
}
// public func testDataFlowAcrossBBs() {
// Checks merging of scopes across basic blocks - propagating that information
//
// FIXME: The optimization should be able to merge these accesses, but
// it must first prove that no other conflicting read accesses occur
// within the existing read access scopes.
//
// CHECK-LABEL: sil @testDataFlowAcrossBBs : $@convention(thin) () -> () {
// CHECK: [[GLOBAL:%.*]] = global_addr @globalX : $*X
// CHECK-NEXT: [[BEGIN:%.*]] = begin_access [modify] [dynamic] [no_nested_conflict] [[GLOBAL]] : $*X
// CHECK-NEXT: load [[BEGIN]] : $*X
// CHECK-NEXT: end_access [[BEGIN]] : $*X
// CHECK-NEXT: br bb1
// CHECK: br bb2
// CHECK: [[BEGIN2:%.*]] = begin_access [read] [dynamic] [no_nested_conflict] [[GLOBAL]] : $*X
// CHECK-NEXT: load [[BEGIN2]] : $*X
// CHECK-NEXT: end_access [[BEGIN2]] : $*X
// CHECK-LABEL: } // end sil function 'testDataFlowAcrossBBs'
sil @testDataFlowAcrossBBs : $@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 [read] [dynamic] %0 : $*X
%5 = load %4 : $*X
end_access %4 : $*X
%7 = tuple ()
return %7 : $()
}
// public func testDataFlowAcrossInnerLoop() {
// Checks merging of scopes across an inner loop
//
// FIXME: The optimization should be able to merge these accesses, but
// it must first prove that no other conflicting read accesses occur
// within the existing read access scopes.
//
// CHECK-LABEL: sil @testDataFlowAcrossInnerLoop : $@convention(thin) () -> () {
// CHECK: [[GLOBAL:%.*]] = global_addr @globalX : $*X
// CHECK-NEXT: [[BEGIN:%.*]] = begin_access [modify] [dynamic] [no_nested_conflict] [[GLOBAL]] : $*X
// CHECK-NEXT: load [[BEGIN]] : $*X
// CHECK-NEXT: end_access [[BEGIN]] : $*X
// CHECK-NEXT: br bb1
// CHECK: cond_br {{.*}}, bb1, bb2
// CHECK: [[BEGIN2:%.*]] = begin_access [read] [dynamic] [no_nested_conflict] [[GLOBAL]] : $*X
// CHECK-NEXT: load [[BEGIN2]] : $*X
// CHECK-NEXT: end_access [[BEGIN2]] : $*X
// CHECK-LABEL: } // end sil function 'testDataFlowAcrossInnerLoop'
sil @testDataFlowAcrossInnerLoop : $@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 [read] [dynamic] %0 : $*X
%5 = load %4 : $*X
end_access %4 : $*X
%7 = tuple ()
return %7 : $()
}
// public func testConflictInInnerLoop() {
// Checks summary propagation and conflict detection in sub-regions
//
// CHECK-LABEL: sil @testConflictInInnerLoop : $@convention(thin) () -> () {
// CHECK: [[GLOBAL:%.*]] = global_addr @globalX : $*X
// CHECK-NEXT: [[BEGIN:%.*]] = begin_access [modify] [dynamic] [no_nested_conflict] [[GLOBAL]] : $*X
// CHECK-NEXT: load [[BEGIN]] : $*X
// CHECK-NEXT: end_access [[BEGIN]] : $*X
// CHECK-NEXT: br bb1
// CHECK: [[BEGIN2:%.*]] = begin_access [read] [dynamic] [no_nested_conflict] [[GLOBAL]] : $*X
// CHECK-NEXT: load [[BEGIN2]] : $*X
// CHECK-NEXT: end_access [[BEGIN2]] : $*X
// CHECK: cond_br {{.*}}, bb1, bb2
// CHECK: [[BEGIN3:%.*]] = begin_access [read] [dynamic] [no_nested_conflict] [[GLOBAL]] : $*X
// CHECK-NEXT: load [[BEGIN3]] : $*X
// CHECK-NEXT: end_access [[BEGIN3]] : $*X
// CHECK-NOT: begin_access
// CHECK-LABEL: } // end sil function 'testConflictInInnerLoop'
sil @testConflictInInnerLoop : $@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:
%4 = begin_access [read] [dynamic] %0 : $*X
%5 = load %4 : $*X
end_access %4 : $*X
%cond = integer_literal $Builtin.Int1, 1
cond_br %cond, bb1, bb2
bb2:
%7 = begin_access [read] [dynamic] %0 : $*X
%8 = load %7 : $*X
end_access %7 : $*X
%10 = tuple ()
return %10 : $()
}
sil hidden_external [global_init] @globalAddressor : $@convention(thin) () -> Builtin.RawPointer
// public func testUnidentifiedAccessInLoop() {
// Tests Unidentified Accesses detection + propagation
//
// CHECK-LABEL: sil @testUnidentifiedAccessInLoop : $@convention(thin) () -> () {
// CHECK: [[GLOBAL:%.*]] = global_addr @globalX : $*X
// CHECK-NEXT: [[BEGIN:%.*]] = begin_access [modify] [dynamic] [no_nested_conflict] [[GLOBAL]] : $*X
// CHECK-NEXT: load [[BEGIN]] : $*X
// CHECK-NEXT: end_access [[BEGIN]] : $*X
// CHECK-NEXT: br bb1
// CHECK: [[BEGIN2:%.*]] = begin_access [read] [dynamic] [no_nested_conflict] {{.*}} : $*Int64
// CHECK-NEXT: load [[BEGIN2]] : $*Int64
// CHECK-NEXT: end_access [[BEGIN2]] : $*Int64
// CHECK: cond_br {{.*}}, bb1, bb2
// CHECK: [[BEGIN3:%.*]] = begin_access [read] [dynamic] [no_nested_conflict] [[GLOBAL]] : $*X
// CHECK-NEXT: load [[BEGIN3]] : $*X
// CHECK-NEXT: end_access [[BEGIN3]] : $*X
// CHECK-NOT: begin_access
// CHECK-LABEL: } // end sil function 'testUnidentifiedAccessInLoop'
sil @testUnidentifiedAccessInLoop : $@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:
%u0 = function_ref @globalAddressor : $@convention(thin) () -> Builtin.RawPointer
%u1 = apply %u0() : $@convention(thin) () -> Builtin.RawPointer
%u2 = pointer_to_address %u1 : $Builtin.RawPointer to [strict] $*Int64
%u3 = begin_access [read] [dynamic] %u2 : $*Int64
%u4 = load %u3 : $*Int64
end_access %u3 : $*Int64
%cond = integer_literal $Builtin.Int1, 1
cond_br %cond, bb1, bb2
bb2:
%7 = begin_access [read] [dynamic] %0 : $*X
%8 = load %7 : $*X
end_access %7 : $*X
%10 = tuple ()
return %10 : $()
}
// public func testIrreducibleGraph() {
// Checks detection of irreducible control flow / bail
// See mergePredAccesses in the algorithm
//
// CHECK-LABEL: sil @testIrreducibleGraph : $@convention(thin) () -> () {
// CHECK: [[GLOBAL:%.*]] = global_addr @globalX : $*X
// CHECK-NEXT: [[BEGIN:%.*]] = begin_access [read] [dynamic] [no_nested_conflict] [[GLOBAL]] : $*X
// CHECK-NEXT: load [[BEGIN]] : $*X
// CHECK-NEXT: end_access [[BEGIN]] : $*X
// CHECK-NEXT: br bb1
// CHECK: [[BEGIN2:%.*]] = begin_access [modify] [dynamic] [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] [dynamic] [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] [dynamic] [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] [dynamic] [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] %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] %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] %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] %0 : $*X
%11 = load %10 : $*X
end_access %10 : $*X
%12 = tuple ()
return %12 : $()
}
// public func testIrreducibleGraph2() {
// Checks detection of irreducible control flow / bail + parts that we *can* merge
// See disableCrossBlock in the algorithm: this detects this corner case
//
// FIXME: The optimization should be able to merge these accesses, but
// it must first prove that no other conflicting read accesses occur
// within the existing read access scopes.
//
// CHECK-LABEL: sil @testIrreducibleGraph2 : $@convention(thin) () -> () {
// CHECK: [[GLOBAL:%.*]] = global_addr @globalX : $*X
// CHECK-NEXT: [[BEGIN:%.*]] = begin_access [read] [dynamic] [no_nested_conflict] [[GLOBAL]] : $*X
// CHECK-NEXT: load [[BEGIN]] : $*X
// CHECK-NEXT: end_access [[BEGIN]] : $*X
// CHECK-NEXT: br bb1
// CHECK: [[BEGIN1:%.*]] = begin_access [modify] [dynamic] [no_nested_conflict] [[GLOBAL]] : $*X
// CHECK-NEXT: load [[BEGIN1]] : $*X
// CHECK-NEXT: end_access [[BEGIN1]] : $*X
// CHECK: cond_br {{.*}}, bb2, bb3
// 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: [[BEGIN3:%.*]] = begin_access [read] [dynamic] [no_nested_conflict] [[GLOBAL]] : $*X
// CHECK-NEXT: load [[BEGIN3]] : $*X
// CHECK: br bb4
// CHECK: load [[BEGIN3]] : $*X
// CHECK-NEXT: end_access [[BEGIN3]] : $*X
// CHECK: cond_br {{.*}}, bb2, bb5
// CHECK: [[BEGIN5:%.*]] = begin_access [read] [dynamic] [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
%1 = begin_access [read] [dynamic] %0 : $*X
%2 = load %1 : $*X
end_access %1 : $*X
br bb1
bb1:
%4 = begin_access [modify] [dynamic] %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] %0 : $*X
%7 = load %6 : $*X
end_access %6 : $*X
br bb3
bb3:
%8 = begin_access [read] [dynamic] %0 : $*X
%9 = load %8 : $*X
end_access %8 : $*X
br bb4
bb4:
%10 = begin_access [read] [dynamic] %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] %0 : $*X
%14 = load %13 : $*X
end_access %13 : $*X
%16 = tuple ()
return %16 : $()
}
class RefElemClass {
var x : BitfieldOne
var y : Int32
init()
}
// Merge access overlapping scopes. Scope nesting does not need to be
// preserved unless the nested accesses are to identical storage.
//
// CHECK-LABEL: sil @ref_elem_c : $@convention(thin) (RefElemClass) -> () {
// CHECK: [[GLOBAL:%.*]] = global_addr @globalX : $*X
// CHECK-NEXT: [[BEGIN:%.*]] = begin_access [read] [dynamic] [no_nested_conflict] [[GLOBAL]] : $*X
// CHECK-NEXT: load [[BEGIN]] : $*X
// CHECK-NEXT: end_access [[BEGIN]] : $*X
// CHECK-NEXT: [[REFX:%.*]] = ref_element_addr %0 : $RefElemClass, #RefElemClass.x
// CHECK-NEXT: [[REFY:%.*]] = ref_element_addr %0 : $RefElemClass, #RefElemClass.y
// CHECK-NEXT: [[BEGINX:%.*]] = begin_access [modify] [dynamic] [[REFX]] : $*BitfieldOne
// CHECK: [[BEGINY:%.*]] = begin_access [modify] [dynamic] [[REFY]] : $*Int32
// CHECK: end_access [[BEGINX]] : $*BitfieldOne
// CHECK-NEXT: end_access [[BEGINY]] : $*Int32
// CHECK-LABEL: } // end sil function 'ref_elem_c'
sil @ref_elem_c : $@convention(thin) (RefElemClass) -> () {
bb0(%0 : $RefElemClass):
%b0 = global_addr @globalX: $*X
%1 = begin_access [read] [dynamic] %b0 : $*X
%2 = load %1 : $*X
end_access %1 : $*X
%x = ref_element_addr %0 : $RefElemClass, #RefElemClass.x
%y = ref_element_addr %0 : $RefElemClass, #RefElemClass.y
%b1 = begin_access [modify] [dynamic] %x : $*BitfieldOne
%u0 = function_ref @globalAddressor : $@convention(thin) () -> Builtin.RawPointer
%u1 = apply %u0() : $@convention(thin) () -> Builtin.RawPointer
end_access %b1 : $*BitfieldOne
%b2 = begin_access [modify] [dynamic] %y : $*Int32
%b3 = begin_access [modify] [dynamic] %x : $*BitfieldOne
end_access %b3 : $*BitfieldOne
end_access %b2 : $*Int32
%10 = tuple ()
return %10 : $()
}
class RefElemNoConflictClass {
var x : Int32
var y : Int32
init()
}
// Checks that we can detect not having a nested conflict in class when we are accessing
// fields that do no alias
// CHECK-LABEL: sil @ref_elem_no_alias : $@convention(thin) (RefElemNoConflictClass) -> () {
// CHECK: [[REFX:%.*]] = ref_element_addr %0 : $RefElemNoConflictClass, #RefElemNoConflictClass.x
// CHECK-NEXT: [[REFY:%.*]] = ref_element_addr %0 : $RefElemNoConflictClass, #RefElemNoConflictClass.y
// CHECK: [[BEGINY:%.*]] = begin_access [modify] [dynamic] [no_nested_conflict] [[REFY]] : $*Int32
// CHECK: [[BEGINX:%.*]] = begin_access [modify] [dynamic] [no_nested_conflict] [[REFX]] : $*Int32
// CHECK-NEXT: end_access [[BEGINX]] : $*Int32
// CHECK-NEXT: end_access [[BEGINY]] : $*Int32
// CHECK-LABEL: } // end sil function 'ref_elem_no_alias'
sil @ref_elem_no_alias : $@convention(thin) (RefElemNoConflictClass) -> () {
bb0(%0 : $RefElemNoConflictClass):
%x = ref_element_addr %0 : $RefElemNoConflictClass, #RefElemNoConflictClass.x
%y = ref_element_addr %0 : $RefElemNoConflictClass, #RefElemNoConflictClass.y
%b2 = begin_access [modify] [dynamic] %y : $*Int32
%b3 = begin_access [modify] [dynamic] %x : $*Int32
end_access %b3 : $*Int32
end_access %b2 : $*Int32
%10 = tuple ()
return %10 : $()
}
// public func testStronglyConnectedComponent() {
// During the merge optimization,
// Check that we don't merge cross strongly component boundaries for now
//
// FIXME: The optimization should be able to merge these accesses, but
// it must first prove that no other conflicting read accesses occur
// within the existing read access scopes.
//
// CHECK-LABEL: sil @testStronglyConnectedComponent : $@convention(thin) () -> () {
// CHECK: [[GLOBAL:%.*]] = global_addr @globalX : $*X
// CHECK-NEXT: [[BEGIN:%.*]] = begin_access [read] [dynamic] [no_nested_conflict] [[GLOBAL]] : $*X
// CHECK-NEXT: load [[BEGIN]] : $*X
// CHECK-NEXT: end_access [[BEGIN]] : $*X
// CHECK-NEXT: br bb1
// CHECK: [[BEGIN1:%.*]] = begin_access [modify] [dynamic] [no_nested_conflict] [[GLOBAL]] : $*X
// CHECK-NEXT: load [[BEGIN1]] : $*X
// CHECK-NEXT: end_access [[BEGIN1]] : $*X
// CHECK: cond_br {{.*}}, bb2, bb3
// 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: [[BEGIN3:%.*]] = begin_access [read] [dynamic] [no_nested_conflict] [[GLOBAL]] : $*X
// CHECK-NEXT: load [[BEGIN3]] : $*X
// CHECK: br bb4
// CHECK: load [[BEGIN3]] : $*X
// CHECK-NEXT: end_access [[BEGIN3]] : $*X
// CHECK: cond_br {{.*}}, bb5, bb6
// CHECK: [[BEGIN4:%.*]] = begin_access [read] [dynamic] [no_nested_conflict] [[GLOBAL]] : $*X
// CHECK-NEXT: load [[BEGIN4]] : $*X
// CHECK-NEXT: end_access [[BEGIN4]] : $*X
// CHECK: br bb2
// CHECK: [[BEGIN5:%.*]] = begin_access [read] [dynamic] [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 'testStronglyConnectedComponent'
sil @testStronglyConnectedComponent : $@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] %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] %0 : $*X
%7 = load %6 : $*X
end_access %6 : $*X
br bb3
bb3:
%8 = begin_access [read] [dynamic] %0 : $*X
%9 = load %8 : $*X
end_access %8 : $*X
br bb4
bb4:
%10 = begin_access [read] [dynamic] %0 : $*X
%11 = load %10 : $*X
end_access %10 : $*X
%cond2 = integer_literal $Builtin.Int1, 1
cond_br %cond2, bb5, bb6
bb5:
%13 = begin_access [read] [dynamic] %0 : $*X
%14 = load %13 : $*X
end_access %13 : $*X
br bb2
bb6:
%16 = begin_access [read] [dynamic] %0 : $*X
%17 = load %16 : $*X
end_access %16 : $*X
%19 = tuple ()
return %19 : $()
}
// public func testMergeWithFirstConflict() {
// Check that we can merge scopes even if the first one of them has conflicts within it
//
// CHECK-LABEL: sil @testMergeWithFirstConflict : $@convention(thin) () -> () {
// CHECK: [[GLOBAL:%.*]] = global_addr @globalX : $*X
// CHECK-NEXT: [[BEGIN:%.*]] = begin_access [read] [dynamic] [[GLOBAL]] : $*X
// CHECK: apply
// CHECK-NEXT: load
// CHECK-NEXT: end_access [[BEGIN]] : $*X
// CHECK-NOT: begin_access
// CHECK-LABEL: } // end sil function 'testMergeWithFirstConflict'
sil @testMergeWithFirstConflict : $@convention(thin) () -> () {
bb0:
%0 = global_addr @globalX: $*X
%4 = begin_access [read] [dynamic] %0 : $*X
%u0 = function_ref @globalAddressor : $@convention(thin) () -> Builtin.RawPointer
%u1 = apply %u0() : $@convention(thin) () -> Builtin.RawPointer
end_access %4 : $*X
%1 = begin_access [read] [dynamic] %0 : $*X
%2 = load %1 : $*X
end_access %1 : $*X
%10 = tuple ()
return %10 : $()
}
// public func testMergeWithSecondConflict() {
// Check that we can merge scopes even if the 2nd one of them has conflicts within it
//
// CHECK-LABEL: sil @testMergeWithSecondConflict : $@convention(thin) () -> () {
// CHECK: [[GLOBAL:%.*]] = global_addr @globalX : $*X
// CHECK-NEXT: [[BEGIN:%.*]] = begin_access [read] [dynamic] [[GLOBAL]] : $*X
// CHECK: load
// CHECK: apply
// CHECK-NEXT: end_access [[BEGIN]] : $*X
// CHECK-NOT: begin_access
// CHECK-LABEL: } // end sil function 'testMergeWithSecondConflict'
sil @testMergeWithSecondConflict : $@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] %0 : $*X
%u0 = function_ref @globalAddressor : $@convention(thin) () -> Builtin.RawPointer
%u1 = apply %u0() : $@convention(thin) () -> Builtin.RawPointer
end_access %4 : $*X
%10 = tuple ()
return %10 : $()
}
// public func testPhiArgs() {
// Check that we can merge scopes with Phi args - avoiding a crash we've seen in the past
//
// CHECK-LABEL: sil @testPhiArgs : $@convention(thin) () -> () {
// CHECK: [[GLOBAL:%.*]] = global_addr @globalX : $*X
// CHECK: cond_br {{.*}}, bb1, bb2
// CHECK: bb1
// CHECK: br bb3([[GLOBAL]] : $*X)
// CHECK: bb2
// CHECK: br bb3([[GLOBAL]] : $*X)
// CHECK: bb3([[GLOBALPHI:%.*]] : $*X):
// CHECK: [[BEGIN:%.*]] = begin_access [read] [dynamic] [no_nested_conflict] [[GLOBALPHI]] : $*X
// CHECK-NEXT: load
// CHECK-NEXT: load
// CHECK-NEXT: end_access [[BEGIN]] : $*X
// CHECK-NOT: begin_access
// CHECK-LABEL: } // end sil function 'testPhiArgs'
sil @testPhiArgs : $@convention(thin) () -> () {
bb0:
%0 = global_addr @globalX: $*X
%cond = integer_literal $Builtin.Int1, 1
cond_br %cond, bb1, bb2
bb1:
br bb3(%0 : $*X)
bb2:
br bb3(%0 : $*X)
bb3(%1 : $*X):
%2 = begin_access [read] [dynamic] %1 : $*X
%3 = load %2 : $*X
end_access %2 : $*X
%4 = begin_access [read] [dynamic] %0 : $*X
%5 = load %4 : $*X
end_access %4 : $*X
%10 = tuple ()
return %10 : $()
}
// --- rdar://48239213: Fatal access conflict detected.
//
// The read/modify pair of accesses in testReadModifyConflictPair
// cannot be merged without introducing a false conflict.
public class TestClass {
@_hasStorage @_hasInitialValue var flags: Int64 { get set }
}
// CHECK-LABEL: sil hidden [noinline] @readFlags : $@convention(method) (Int64, @guaranteed TestClass) -> Bool {
// CHECK: bb0(%0 : $Int64, %1 : $TestClass):
// CHECK: [[ADR:%.*]] = ref_element_addr %1 : $TestClass, #TestClass.flags
// CHECK: begin_access [read] [dynamic] [no_nested_conflict] [[ADR]] : $*Int64
// CHECK: load %4 : $*Builtin.Int64
// CHECK: end_access
// CHECK-LABEL: } // end sil function 'readFlags'
sil hidden [noinline] @readFlags : $@convention(method) (Int64, @guaranteed TestClass) -> Bool {
bb0(%0 : $Int64, %1 : $TestClass):
%2 = ref_element_addr %1 : $TestClass, #TestClass.flags
%3 = begin_access [read] [dynamic] [no_nested_conflict] %2 : $*Int64
%4 = struct_element_addr %3 : $*Int64, #Int64._value
%5 = load %4 : $*Builtin.Int64
end_access %3 : $*Int64
%7 = struct_extract %0 : $Int64, #Int64._value
%8 = builtin "cmp_eq_Int64"(%5 : $Builtin.Int64, %7 : $Builtin.Int64) : $Builtin.Int1
%9 = struct $Bool (%8 : $Builtin.Int1)
return %9 : $Bool
}
// CHECK-LABEL: sil @testReadModifyConflictPair : $@convention(method) (@guaranteed TestClass) -> () {
// CHECK: bb0(%0 : $TestClass):
// CHECK: [[ADR:%.*]] = ref_element_addr %0 : $TestClass, #TestClass.flags
// CHECK: begin_access [read] [dynamic] [no_nested_conflict] [[ADR]] : $*Int64
// CHECK: load
// CHECK: end_access
// CHECK: apply {{.*}} : $@convention(method) (Int64, @guaranteed TestClass) -> Bool
// CHECK: begin_access [modify] [dynamic] [no_nested_conflict] [[ADR]] : $*Int64
// CHECK: store
// CHECK: end_access
// CHECK-LABEL: } // end sil function 'testReadModifyConflictPair'
sil @testReadModifyConflictPair : $@convention(method) (@guaranteed TestClass) -> () {
bb0(%0 : $TestClass):
%1 = ref_element_addr %0 : $TestClass, #TestClass.flags
%2 = begin_access [read] [dynamic] %1 : $*Int64
%3 = load %2 : $*Int64
end_access %2 : $*Int64
%5 = function_ref @readFlags : $@convention(method) (Int64, @guaranteed TestClass) -> Bool
%6 = apply %5(%3, %0) : $@convention(method) (Int64, @guaranteed TestClass) -> Bool
%7 = integer_literal $Builtin.Int64, 3
%8 = struct $Int64 (%7 : $Builtin.Int64)
%9 = begin_access [modify] [dynamic] %1 : $*Int64
store %8 to %9 : $*Int64
end_access %9 : $*Int64
%12 = tuple ()
return %12 : $()
}
// public func testNestedReadMerging() {
// Checks bailing on nested read scopes
// We will create two scopes - nested conflict
//
// CHECK-LABEL: sil @testNestedReadMerging : $@convention(thin) () -> () {
// CHECK: [[GLOBAL:%.*]] = global_addr @globalX : $*X
// CHECK-NEXT: [[BEGIN:%.*]] = begin_access [read] [dynamic] [no_nested_conflict] [[GLOBAL]] : $*X
// CHECK-NEXT: load [[BEGIN]] : $*X
// CHECK-NEXT: load [[BEGIN]] : $*X
// CHECK-NEXT: [[BEGIN2:%.*]] = begin_access [read] [dynamic] [no_nested_conflict] [[GLOBAL]] : $*X
// CHECK-NEXT: load [[BEGIN2]] : $*X
// CHECK-NEXT: end_access [[BEGIN2]] : $*X
// CHECK-NEXT: end_access [[BEGIN]] : $*X
// CHECK-NOT: begin_access
// CHECK-LABEL: } // end sil function 'testNestedReadMerging'
sil @testNestedReadMerging : $@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] %0 : $*X
%5 = load %4 : $*X
%7 = begin_access [read] [dynamic] %0 : $*X
%8 = load %7 : $*X
end_access %7 : $*X
end_access %4 : $*X
%10 = tuple ()
return %10 : $()
}
// public func testNestedWriteBailout() {
// Same as testNestedReadBailout but with modify
// We will create two scopes - the nested conflict must be preserved.
//
// CHECK-LABEL: sil @testNestedWriteBailout : $@convention(thin) () -> () {
// CHECK: [[GLOBAL:%.*]] = global_addr @globalX : $*X
// CHECK-NEXT: [[BEGIN:%.*]] = begin_access [modify] [dynamic] [[GLOBAL]] : $*X
// CHECK-NEXT: load [[BEGIN]] : $*X
// CHECK-NEXT: load [[BEGIN]] : $*X
// CHECK-NEXT: [[BEGIN2:%.*]] = begin_access [modify] [dynamic] [no_nested_conflict] [[GLOBAL]] : $*X
// CHECK-NEXT: load [[BEGIN2]] : $*X
// CHECK-NEXT: end_access [[BEGIN2]] : $*X
// CHECK-NEXT: end_access [[BEGIN]] : $*X
// CHECK-NOT: begin_access
// CHECK-LABEL: } // end sil function 'testNestedWriteBailout'
sil @testNestedWriteBailout : $@convention(thin) () -> () {
bb0:
%0 = global_addr @globalX: $*X
%1 = begin_access [modify] [dynamic] %0 : $*X
%2 = load %1 : $*X
end_access %1 : $*X
%4 = begin_access [modify] [dynamic] %0 : $*X
%5 = load %4 : $*X
%7 = begin_access [modify] [dynamic] %0 : $*X
%8 = load %7 : $*X
end_access %7 : $*X
end_access %4 : $*X
%10 = tuple ()
return %10 : $()
}
// Test transformCalleeStorage when the callee storage is an Argument
// and the caller storage is an Argument at a different position.
//
// CHECK-LABEL: sil @testTransformArgumentCaller : $@convention(thin) (Int64, @guaranteed { var Int64 }, @guaranteed { var Int64 }) -> Int64 {
// CHECK: begin_access [read] [dynamic] [no_nested_conflict]
// CHECK-LABEL: } // end sil function 'testTransformArgumentCaller'
sil @testTransformArgumentCaller : $@convention(thin) (Int64, @guaranteed { var Int64 }, @guaranteed { var Int64 }) -> Int64 {
bb0(%0 : $Int64, %1 : ${ var Int64 }, %2 : ${ var Int64 }):
%boxadr = project_box %1 : $ { var Int64 }, 0
%access = begin_access [read] [dynamic] [no_nested_conflict] %boxadr : $*Int64
%f = function_ref @testTransformArgumentCallee : $@convention(thin) (@guaranteed { var Int64 }) -> Int64
%call = apply %f(%2) : $@convention(thin) (@guaranteed { var Int64 }) -> Int64
store %0 to %access : $*Int64
end_access %access : $*Int64
return %call : $Int64
}
// CHECK-LABEL: sil @testTransformArgumentCallee : $@convention(thin) (@guaranteed { var Int64 }) -> Int64 {
// CHECK: begin_access [read] [dynamic] [no_nested_conflict]
// CHECK-LABEL: } // end sil function 'testTransformArgumentCallee'
sil @testTransformArgumentCallee : $@convention(thin) (@guaranteed { var Int64 }) -> Int64 {
bb0(%0 : ${ var Int64 }):
%boxadr = project_box %0 : $ { var Int64 }, 0
%access = begin_access [read] [dynamic] [no_nested_conflict] %boxadr : $*Int64
%val = load %access : $*Int64
end_access %access : $*Int64
return %val : $Int64
}