mirror of
https://github.com/apple/swift.git
synced 2025-12-14 20:36:38 +01:00
This will e.g. catch violations like ``` %1 = begin_access [read] %0 store %2 to %0 end_access %1 ``` Also, fix all the sil tests which violate that.
1933 lines
80 KiB
Plaintext
1933 lines
80 KiB
Plaintext
// RUN: %target-sil-opt -sil-print-types -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()
|
|
}
|
|
|
|
class Klass {
|
|
|
|
}
|
|
|
|
class NonTrivial {
|
|
@_hasStorage var object: Klass { get set }
|
|
}
|
|
|
|
var globalX: X
|
|
|
|
var globalOtherX: X
|
|
|
|
sil_global hidden @globalX : $X
|
|
|
|
sil_global hidden @globalOtherX : $X
|
|
|
|
sil [ossa] @hasIndirectArg : $@convention(thin) (@inout Klass) -> ()
|
|
|
|
sil hidden [ossa] @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 [trivial] %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 [ossa] @testNestedAccess1 : $@convention(thin) () -> () {
|
|
// CHECK: [[F1:%.*]] = function_ref @testNestedAccessClosure1 : $@convention(thin) () -> ()
|
|
// CHECK: [[C1:%.*]] = convert_function [[F1]] : $@convention(thin) () -> () to $@convention(thin) () -> ()
|
|
// CHECK: [[TF1:%.*]] = thin_to_thick_function [[C1]] : $@convention(thin) () -> () 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 [ossa] @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 [ossa] @testNestedAccess2 : $@convention(thin) () -> () {
|
|
// CHECK: [[F2:%.*]] = function_ref @testNestedAccessClosure2 : $@convention(thin) () -> ()
|
|
// CHECK: [[C2:%.*]] = convert_function [[F2]] : $@convention(thin) () -> () to $@convention(thin) () -> ()
|
|
// CHECK: [[TF2:%.*]] = thin_to_thick_function [[C2]] : $@convention(thin) () -> () 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 [ossa] @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 [ossa] @testNestedAccess3 : $@convention(thin) () -> () {
|
|
// CHECK: [[F3:%.*]] = function_ref @testNestedAccessClosure3 : $@convention(thin) () -> ()
|
|
// CHECK: [[C3:%.*]] = convert_function [[F3]] : $@convention(thin) () -> () to $@convention(thin) () -> ()
|
|
// CHECK: [[TF3:%.*]] = thin_to_thick_function [[C3]] : $@convention(thin) () -> () 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 [ossa] @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 [ossa] @testNestedAccessClosure1 : $@convention(thin) () -> () {
|
|
// CHECK: begin_access [modify] [dynamic] [no_nested_conflict]
|
|
// CHECK-LABEL: // end sil function 'testNestedAccessClosure1'
|
|
sil private [ossa] @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 [trivial] %4 : $*X
|
|
end_access %4 : $*X
|
|
%7 = tuple ()
|
|
return %7 : $()
|
|
}
|
|
|
|
// CHECK-LABEL: sil private [ossa] @testNestedAccessClosure2 : $@convention(thin) () -> () {
|
|
// CHECK: begin_access [read] [dynamic] [no_nested_conflict]
|
|
// CHECK-LABEL: // end sil function 'testNestedAccessClosure2'
|
|
sil private [ossa] @testNestedAccessClosure2 : $@convention(thin) () -> () {
|
|
bb0:
|
|
%0 = global_addr @globalX: $*X
|
|
%1 = begin_access [read] [dynamic] %0 : $*X
|
|
%2 = load [trivial] %1 : $*X
|
|
end_access %1 : $*X
|
|
debug_value %2 : $X, let, name "l"
|
|
%5 = alloc_stack $X
|
|
store %2 to [trivial] %5 : $*X
|
|
dealloc_stack %5 : $*X
|
|
%10 = tuple ()
|
|
return %10 : $()
|
|
}
|
|
|
|
// CHECK-LABEL: sil private [ossa] @testNestedAccessClosure3 : $@convention(thin) () -> () {
|
|
// CHECK: begin_access [modify] [dynamic] [no_nested_conflict]
|
|
// CHECK-LABEL: // end sil function 'testNestedAccessClosure3'
|
|
sil private [ossa] @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 [trivial] %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 [ossa] @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 [ossa] @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 [ossa] @testDisjointAccessClosure1 : $@convention(thin) () -> () {
|
|
// CHECK: begin_access [modify] [dynamic] [no_nested_conflict]
|
|
// CHECK-LABEL: // end sil function 'testDisjointAccessClosure1'
|
|
sil private [ossa] @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 [trivial] %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 [ossa] @testCaptureReadRead : $@convention(thin) () -> () {
|
|
// CHECK: begin_access [read] [static] [no_nested_conflict] %0 : $*X
|
|
// CHECK-LABEL: } // end sil function 'testCaptureReadRead'
|
|
sil [ossa] @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 [trivial] %0 : $*X
|
|
%5 = function_ref @testCaptureReadReadClosure1 : $@convention(thin) (@inout_aliasable X) -> ()
|
|
%6 = partial_apply [callee_guaranteed] %5(%0) : $@convention(thin) (@inout_aliasable X) -> ()
|
|
%7 = copy_value %6 : $@callee_guaranteed () -> ()
|
|
%8 = begin_borrow %7 : $@callee_guaranteed () -> ()
|
|
%9 = convert_escape_to_noescape %8 : $@callee_guaranteed () -> () to $@noescape @callee_guaranteed () -> ()
|
|
%10 = begin_access [read] [dynamic] %0 : $*X
|
|
%12 = apply %9() : $@noescape @callee_guaranteed () -> ()
|
|
destroy_value %9 : $@noescape @callee_guaranteed () -> ()
|
|
end_borrow %8 : $@callee_guaranteed () -> ()
|
|
destroy_value %7 : $@callee_guaranteed () -> ()
|
|
end_access %10 : $*X
|
|
destroy_value %6 : $@callee_guaranteed () -> ()
|
|
dealloc_stack %0 : $*X
|
|
%18 = tuple ()
|
|
return %18 : $()
|
|
}
|
|
|
|
// CHECK-LABEL: sil private [ossa] @testCaptureReadReadClosure1 : $@convention(thin) (@inout_aliasable X) -> () {
|
|
// CHECK: begin_access [read] [dynamic] [no_nested_conflict] %0 : $*X
|
|
// CHECK-LABEL: } // end sil function 'testCaptureReadReadClosure1'
|
|
sil private [ossa] @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 [trivial] %3 : $*Int64
|
|
end_access %2 : $*X
|
|
%6 = alloc_stack $Int64
|
|
store %4 to [trivial] %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 [ossa] @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 [ossa] @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 [trivial] %1 : $*X
|
|
%6 = function_ref @testCaptureBoxReadReadClosure1 : $@convention(thin) (@guaranteed { var X }) -> ()
|
|
%7 = copy_value %0 : ${ var X }
|
|
%8 = partial_apply [callee_guaranteed] %6(%7) : $@convention(thin) (@guaranteed { var X }) -> ()
|
|
%9 = begin_access [read] [dynamic] %1 : $*X
|
|
%12 = apply %8() : $@callee_guaranteed () -> ()
|
|
end_access %9 : $*X
|
|
destroy_value %8 : $@callee_guaranteed () -> ()
|
|
destroy_value %0 : ${ var X }
|
|
%17 = tuple ()
|
|
return %17 : $()
|
|
}
|
|
|
|
// CHECK-LABEL: sil private [ossa] @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 [ossa] @testCaptureBoxReadReadClosure1 : $@convention(thin) (@guaranteed { var X }) -> () {
|
|
bb0(%0 : @guaranteed ${ 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 [trivial] %4 : $*Int64
|
|
end_access %3 : $*X
|
|
%7 = alloc_stack $Int64
|
|
store %5 to [trivial] %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 [ossa] @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 [trivial] %1 : $*Int64
|
|
|
|
%5 = function_ref @testDoubleCaptureClosure1 : $@convention(thin) (@guaranteed { var Int64 }) -> ()
|
|
%6 = copy_value %0 : ${ var Int64 }
|
|
%7 = partial_apply [callee_guaranteed] %5(%6) : $@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 () -> ()
|
|
destroy_value %11 : $@noescape @callee_guaranteed () -> ()
|
|
%17 = apply %15() : $@noescape @callee_guaranteed () -> ()
|
|
destroy_value %15 : $@noescape @callee_guaranteed () -> ()
|
|
destroy_value %7 : $@callee_guaranteed () -> ()
|
|
destroy_value %13 : $@callee_guaranteed () -> ()
|
|
destroy_value %0 : ${ var Int64 }
|
|
%24 = tuple ()
|
|
return %24 : $()
|
|
}
|
|
|
|
// CHECK-LABEL: sil private [ossa] @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 [ossa] @testDoubleCaptureClosure1 : $@convention(thin) (@guaranteed { var Int64 }) -> () {
|
|
bb0(%0 : @guaranteed ${ 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 [trivial] %5 : $*Int64
|
|
end_access %5 : $*Int64
|
|
%8 = tuple ()
|
|
return %8 : $()
|
|
}
|
|
|
|
// CHECK-LABEL: sil private [ossa] @testDoubleCaptureClosure2 : $@convention(thin) (@inout_aliasable Int64) -> () {
|
|
// CHECK: begin_access [modify] [dynamic] [no_nested_conflict] %0 : $*Int64
|
|
// CHECK-LABEL: } // end sil function 'testDoubleCaptureClosure2'
|
|
sil private [ossa] @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 [trivial] %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 [ossa] @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 [ossa] @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 [trivial] %1 : $*Int64
|
|
%5 = function_ref @testInoutReadEscapeReadClosure1 : $@convention(thin) (@guaranteed { var Int64 }) -> ()
|
|
%6 = copy_value %0 : ${ var Int64 }
|
|
%7 = partial_apply [callee_guaranteed] %5(%6) : $@convention(thin) (@guaranteed { var Int64 }) -> ()
|
|
%8 = copy_value %7 : $@callee_guaranteed () -> ()
|
|
%9 = copy_value %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 () -> ()
|
|
destroy_value %11 : $@noescape @callee_guaranteed () -> ()
|
|
destroy_value %9 : $@callee_guaranteed () -> ()
|
|
end_access %12 : $*Int64
|
|
destroy_value %8 : $@callee_guaranteed () -> ()
|
|
%20 = begin_access [read] [dynamic] %1 : $*Int64
|
|
%21 = load [trivial] %20 : $*Int64
|
|
end_access %20 : $*Int64
|
|
%23 = alloc_stack $Int64
|
|
store %21 to [trivial] %23 : $*Int64
|
|
dealloc_stack %23 : $*Int64
|
|
destroy_value %7 : $@callee_guaranteed () -> ()
|
|
destroy_value %0 : ${ var Int64 }
|
|
%30 = tuple ()
|
|
return %30 : $()
|
|
}
|
|
|
|
// CHECK-LABEL: sil private [ossa] @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 [ossa] @testInoutReadEscapeReadClosure1 : $@convention(thin) (@guaranteed { var Int64 }) -> () {
|
|
bb0(%0 : @guaranteed ${ var Int64 }):
|
|
%1 = project_box %0 : ${ var Int64 }, 0
|
|
%3 = begin_access [read] [dynamic] %1 : $*Int64
|
|
%4 = load [trivial] %3 : $*Int64
|
|
end_access %3 : $*Int64
|
|
debug_value %4 : $Int64, let, name "y"
|
|
%7 = alloc_stack $Int64
|
|
store %4 to [trivial] %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 [ossa] @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 [ossa] @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 [trivial] %1 : $*Int64
|
|
%5 = function_ref @testInoutReadEscapeWriteClosure1 : $@convention(thin) (@guaranteed { var Int64 }) -> ()
|
|
%6 = copy_value %0 : ${ var Int64 }
|
|
%7 = partial_apply [callee_guaranteed] %5(%6) : $@convention(thin) (@guaranteed { var Int64 }) -> ()
|
|
%8 = copy_value %7 : $@callee_guaranteed () -> ()
|
|
%9 = copy_value %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 () -> ()
|
|
destroy_value %11 : $@noescape @callee_guaranteed () -> ()
|
|
destroy_value %9 : $@callee_guaranteed () -> ()
|
|
end_access %12 : $*Int64
|
|
destroy_value %8 : $@callee_guaranteed () -> ()
|
|
|
|
%20 = begin_access [read] [dynamic] %1 : $*Int64
|
|
%21 = load [trivial] %20 : $*Int64
|
|
end_access %20 : $*Int64
|
|
%23 = alloc_stack $Int64
|
|
store %21 to [trivial] %23 : $*Int64
|
|
dealloc_stack %23 : $*Int64
|
|
destroy_value %7 : $@callee_guaranteed () -> ()
|
|
destroy_value %0 : ${ var Int64 }
|
|
%30 = tuple ()
|
|
return %30 : $()
|
|
}
|
|
|
|
// CHECK-LABEL: sil private [ossa] @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 [ossa] @testInoutReadEscapeWriteClosure1 : $@convention(thin) (@guaranteed { var Int64 }) -> () {
|
|
bb0(%0 : @guaranteed ${ 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 [trivial] %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 [ossa] @$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 [trivial] [[BEGIN2]]
|
|
// CHECK: end_access [[BEGIN2]]
|
|
// CHECK-LABEL: } // end sil function '$s17enforce_with_opts24testInoutWriteEscapeReadyyF'
|
|
sil [ossa] @$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 [trivial] %1 : $*Int64
|
|
// function_ref closure #1 in testInoutWriteEscapeRead()
|
|
%5 = function_ref @$s17enforce_with_opts24testInoutWriteEscapeReadyyFyycfU_ : $@convention(thin) (@guaranteed { var Int64 }) -> ()
|
|
%6 = copy_value %0 : ${ var Int64 }
|
|
%7 = partial_apply [callee_guaranteed] %5(%6) : $@convention(thin) (@guaranteed { var Int64 }) -> ()
|
|
debug_value %7 : $@callee_guaranteed () -> (), let, name "c"
|
|
%8 = copy_value %7 : $@callee_guaranteed () -> ()
|
|
%9 = copy_value %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 () -> ()
|
|
destroy_value %11 : $@noescape @callee_guaranteed () -> ()
|
|
%16 = tuple ()
|
|
destroy_value %9 : $@callee_guaranteed () -> ()
|
|
end_access %12 : $*Int64
|
|
destroy_value %8 : $@callee_guaranteed () -> ()
|
|
%20 = begin_access [read] [dynamic] %1 : $*Int64
|
|
%21 = load [trivial] %20 : $*Int64
|
|
end_access %20 : $*Int64
|
|
%23 = alloc_stack $Int64
|
|
store %21 to [trivial] %23 : $*Int64
|
|
dealloc_stack %23 : $*Int64
|
|
destroy_value %7 : $@callee_guaranteed () -> ()
|
|
destroy_value %0 : ${ var Int64 }
|
|
%30 = tuple ()
|
|
return %30 : $()
|
|
}
|
|
|
|
// closure #1 in testInoutWriteEscapeRead()
|
|
// CHECK-LABEL: sil private [ossa] @$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 [ossa] @$s17enforce_with_opts24testInoutWriteEscapeReadyyFyycfU_ : $@convention(thin) (@guaranteed { var Int64 }) -> () {
|
|
// %0
|
|
bb0(%0 : @guaranteed ${ var Int64 }):
|
|
%1 = project_box %0 : ${ var Int64 }, 0
|
|
debug_value %1 : $*Int64, var, name "x", argno 1, expr op_deref
|
|
%3 = begin_access [read] [dynamic] %1 : $*Int64
|
|
%4 = load [trivial] %3 : $*Int64
|
|
end_access %3 : $*Int64
|
|
debug_value %4 : $Int64, let, name "y"
|
|
%7 = alloc_stack $Int64
|
|
store %4 to [trivial] %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 [ossa] @$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 [trivial] [[BEGIN2]]
|
|
// CHECK: end_access [[BEGIN2]]
|
|
// CHECK-LABEL: } // end sil function '$s17enforce_with_opts020testInoutWriteEscapeF0yyF'
|
|
sil [ossa] @$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 [trivial] %1 : $*Int64
|
|
// function_ref closure #1 in testInoutWriteEscapeWrite()
|
|
%5 = function_ref @$s17enforce_with_opts020testInoutWriteEscapeF0yyFyycfU_ : $@convention(thin) (@guaranteed { var Int64 }) -> ()
|
|
%6 = copy_value %0 : ${ var Int64 }
|
|
%7 = partial_apply [callee_guaranteed] %5(%6) : $@convention(thin) (@guaranteed { var Int64 }) -> ()
|
|
debug_value %7 : $@callee_guaranteed () -> (), let, name "c"
|
|
%8 = copy_value %7 : $@callee_guaranteed () -> ()
|
|
%9 = copy_value %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 () -> ()
|
|
destroy_value %11 : $@noescape @callee_guaranteed () -> ()
|
|
%16 = tuple ()
|
|
destroy_value %9 : $@callee_guaranteed () -> ()
|
|
end_access %12 : $*Int64
|
|
destroy_value %8 : $@callee_guaranteed () -> ()
|
|
%20 = begin_access [read] [dynamic] %1 : $*Int64
|
|
%21 = load [trivial] %20 : $*Int64
|
|
end_access %20 : $*Int64
|
|
%23 = alloc_stack $Int64
|
|
store %21 to [trivial] %23 : $*Int64
|
|
dealloc_stack %23 : $*Int64
|
|
destroy_value %7 : $@callee_guaranteed () -> ()
|
|
destroy_value %0 : ${ var Int64 }
|
|
%30 = tuple ()
|
|
return %30 : $()
|
|
}
|
|
|
|
// CHECK-LABEL: sil private [ossa] @$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 [ossa] @$s17enforce_with_opts020testInoutWriteEscapeF0yyFyycfU_ : $@convention(thin) (@guaranteed { var Int64 }) -> () {
|
|
// %0
|
|
bb0(%0 : @guaranteed ${ var Int64 }):
|
|
%1 = project_box %0 : ${ var Int64 }, 0
|
|
debug_value %1 : $*Int64, var, name "x", argno 1, expr op_deref
|
|
%3 = integer_literal $Builtin.Int64, 42
|
|
%4 = struct $Int64 (%3 : $Builtin.Int64)
|
|
%5 = begin_access [modify] [dynamic] %1 : $*Int64
|
|
store %4 to [trivial] %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 [ossa] @$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 [trivial] %1 : $*Int64
|
|
// function_ref closure #1 in testInoutReadNoescapeRead()
|
|
%5 = function_ref @$s23enforce_with_opts_nob2s021testInoutReadNoescapeG0yyFyycfU_ : $@convention(thin) (@guaranteed { var Int64 }) -> ()
|
|
%6 = copy_value %0 : ${ var Int64 }
|
|
%7 = partial_apply [callee_guaranteed] %5(%6) : $@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 () -> ()) -> ()
|
|
%10 = copy_value %7 : $@callee_guaranteed () -> ()
|
|
%11 = partial_apply [callee_guaranteed] %9(%1, %10) : $@convention(thin) (@inout_aliasable Int64, @guaranteed @callee_guaranteed () -> ()) -> ()
|
|
%12 = copy_value %11 : $@callee_guaranteed () -> ()
|
|
%13 = convert_escape_to_noescape %12 : $@callee_guaranteed () -> () to $@noescape @callee_guaranteed () -> ()
|
|
%14 = apply %13() : $@noescape @callee_guaranteed () -> ()
|
|
destroy_value %13 : $@noescape @callee_guaranteed () -> ()
|
|
%15 = tuple ()
|
|
destroy_value %12 : $@callee_guaranteed () -> ()
|
|
destroy_value %11 : $@callee_guaranteed () -> ()
|
|
destroy_value %7 : $@callee_guaranteed () -> ()
|
|
destroy_value %0 : ${ var Int64 }
|
|
%20 = tuple ()
|
|
return %20 : $()
|
|
}
|
|
|
|
// closure #1 in testInoutReadNoescapeRead()
|
|
// CHECK-LABEL: sil private [ossa] @$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 [ossa] @$s23enforce_with_opts_nob2s021testInoutReadNoescapeG0yyFyycfU_ : $@convention(thin) (@guaranteed { var Int64 }) -> () {
|
|
// %0
|
|
bb0(%0 : @guaranteed ${ var Int64 }):
|
|
%1 = project_box %0 : ${ var Int64 }, 0
|
|
debug_value %1 : $*Int64, var, name "x", argno 1, expr op_deref
|
|
%3 = begin_access [read] [dynamic] %1 : $*Int64
|
|
%4 = load [trivial] %3 : $*Int64
|
|
end_access %3 : $*Int64
|
|
debug_value %4 : $Int64, let, name "y"
|
|
%7 = alloc_stack $Int64
|
|
store %4 to [trivial] %7 : $*Int64
|
|
dealloc_stack %7 : $*Int64
|
|
%12 = tuple ()
|
|
return %12 : $()
|
|
}
|
|
|
|
// closure #2 in testInoutReadNoescapeRead()
|
|
// CHECK-LABEL: sil private [ossa] @$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 [ossa] @$s23enforce_with_opts_nob2s021testInoutReadNoescapeG0yyFyyXEfU0_ : $@convention(thin) (@inout_aliasable Int64, @guaranteed @callee_guaranteed () -> ()) -> () {
|
|
// %0
|
|
// %1
|
|
bb0(%0 : $*Int64, %1 : @guaranteed $@callee_guaranteed () -> ()):
|
|
debug_value %0 : $*Int64, var, name "x", argno 1, expr op_deref
|
|
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 [ossa] @$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 [trivial] %1 : $*Int64
|
|
// function_ref closure #1 in testInoutReadNoescapeWrite()
|
|
%5 = function_ref @$s23enforce_with_opts_nob2s26testInoutReadNoescapeWriteyyFyycfU_ : $@convention(thin) (@guaranteed { var Int64 }) -> ()
|
|
%6 = copy_value %0 : ${ var Int64 }
|
|
%7 = partial_apply [callee_guaranteed] %5(%6) : $@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 () -> ()) -> ()
|
|
%10 = copy_value %7 : $@callee_guaranteed () -> ()
|
|
%11 = partial_apply [callee_guaranteed] %9(%1, %10) : $@convention(thin) (@inout_aliasable Int64, @guaranteed @callee_guaranteed () -> ()) -> ()
|
|
%12 = copy_value %11 : $@callee_guaranteed () -> ()
|
|
%13 = convert_escape_to_noescape %12 : $@callee_guaranteed () -> () to $@noescape @callee_guaranteed () -> ()
|
|
%14 = apply %13() : $@noescape @callee_guaranteed () -> ()
|
|
destroy_value %13 : $@noescape @callee_guaranteed () -> ()
|
|
%15 = tuple ()
|
|
destroy_value %12 : $@callee_guaranteed () -> ()
|
|
destroy_value %11 : $@callee_guaranteed () -> ()
|
|
destroy_value %7 : $@callee_guaranteed () -> ()
|
|
destroy_value %0 : ${ var Int64 }
|
|
%20 = tuple ()
|
|
return %20 : $()
|
|
}
|
|
|
|
// closure #1 in testInoutReadNoescapeWrite()
|
|
// CHECK-LABEL: sil private [ossa] @$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 [ossa] @$s23enforce_with_opts_nob2s26testInoutReadNoescapeWriteyyFyycfU_ : $@convention(thin) (@guaranteed { var Int64 }) -> () {
|
|
// %0
|
|
bb0(%0 : @guaranteed ${ var Int64 }):
|
|
%1 = project_box %0 : ${ var Int64 }, 0
|
|
debug_value %1 : $*Int64, var, name "x", argno 1, expr op_deref
|
|
%3 = integer_literal $Builtin.Int64, 7
|
|
%4 = struct $Int64 (%3 : $Builtin.Int64)
|
|
%5 = begin_access [modify] [dynamic] %1 : $*Int64
|
|
store %4 to [trivial] %5 : $*Int64
|
|
end_access %5 : $*Int64
|
|
%8 = tuple ()
|
|
return %8 : $()
|
|
}
|
|
|
|
// closure #2 in testInoutReadNoescapeWrite()
|
|
// CHECK-LABEL: sil private [ossa] @$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 [ossa] @$s23enforce_with_opts_nob2s26testInoutReadNoescapeWriteyyFyyXEfU0_ : $@convention(thin) (@inout_aliasable Int64, @guaranteed @callee_guaranteed () -> ()) -> () {
|
|
bb0(%0 : $*Int64, %1 : @guaranteed $@callee_guaranteed () -> ()):
|
|
debug_value %0 : $*Int64, var, name "x", argno 1, expr op_deref
|
|
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 [ossa] @$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 [trivial] %1 : $*Int64
|
|
// function_ref closure #1 in testInoutWriteNoescapeReadClosure()
|
|
%5 = function_ref @$s23enforce_with_opts_nob2s33testInoutWriteNoescapeReadClosureyyFyycfU_ : $@convention(thin) (@guaranteed { var Int64 }) -> ()
|
|
%6 = copy_value %0 : ${ var Int64 }
|
|
%7 = partial_apply [callee_guaranteed] %5(%6) : $@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 () -> ()) -> ()
|
|
%10 = copy_value %7 : $@callee_guaranteed () -> ()
|
|
%11 = partial_apply [callee_guaranteed] %9(%1, %10) : $@convention(thin) (@inout_aliasable Int64, @guaranteed @callee_guaranteed () -> ()) -> ()
|
|
%12 = copy_value %11 : $@callee_guaranteed () -> ()
|
|
%13 = convert_escape_to_noescape %12 : $@callee_guaranteed () -> () to $@noescape @callee_guaranteed () -> ()
|
|
%14 = apply %13() : $@noescape @callee_guaranteed () -> ()
|
|
destroy_value %13 : $@noescape @callee_guaranteed () -> ()
|
|
%15 = tuple ()
|
|
destroy_value %12 : $@callee_guaranteed () -> ()
|
|
destroy_value %11 : $@callee_guaranteed () -> ()
|
|
destroy_value %7 : $@callee_guaranteed () -> ()
|
|
destroy_value %0 : ${ var Int64 }
|
|
%20 = tuple ()
|
|
return %20 : $()
|
|
}
|
|
|
|
// closure #1 in testInoutWriteNoescapeReadClosure()
|
|
// CHECK-LABEL: sil private [ossa] @$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 [ossa] @$s23enforce_with_opts_nob2s33testInoutWriteNoescapeReadClosureyyFyycfU_ : $@convention(thin) (@guaranteed { var Int64 }) -> () {
|
|
// %0
|
|
bb0(%0 : @guaranteed ${ var Int64 }):
|
|
%1 = project_box %0 : ${ var Int64 }, 0
|
|
debug_value %1 : $*Int64, var, name "x", argno 1, expr op_deref
|
|
%3 = begin_access [read] [dynamic] %1 : $*Int64
|
|
%4 = load [trivial] %3 : $*Int64
|
|
end_access %3 : $*Int64
|
|
debug_value %4 : $Int64, let, name "y"
|
|
%7 = alloc_stack $Int64
|
|
store %4 to [trivial] %7 : $*Int64
|
|
dealloc_stack %7 : $*Int64
|
|
%12 = tuple ()
|
|
return %12 : $()
|
|
}
|
|
|
|
// closure #2 in testInoutWriteNoescapeReadClosure()
|
|
// CHECK-LABEL: sil private [ossa] @$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 [ossa] @$s23enforce_with_opts_nob2s33testInoutWriteNoescapeReadClosureyyFyyXEfU0_ : $@convention(thin) (@inout_aliasable Int64, @guaranteed @callee_guaranteed () -> ()) -> () {
|
|
// %0
|
|
// %1
|
|
bb0(%0 : $*Int64, %1 : @guaranteed $@callee_guaranteed () -> ()):
|
|
debug_value %0 : $*Int64, var, name "x", argno 1, expr op_deref
|
|
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 [ossa] @$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 [trivial] %1 : $*Int64
|
|
// function_ref closure #1 in testInoutWriteNoescapeWriteClosure()
|
|
%5 = function_ref @$s23enforce_with_opts_nob2s022testInoutWriteNoescapeG7ClosureyyFyycfU_ : $@convention(thin) (@guaranteed { var Int64 }) -> ()
|
|
%6 = copy_value %0 : ${ var Int64 }
|
|
%7 = partial_apply [callee_guaranteed] %5(%6) : $@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 () -> ()) -> ()
|
|
%10 = copy_value %7 : $@callee_guaranteed () -> ()
|
|
%11 = partial_apply [callee_guaranteed] %9(%1, %10) : $@convention(thin) (@inout_aliasable Int64, @guaranteed @callee_guaranteed () -> ()) -> ()
|
|
%12 = copy_value %11 : $@callee_guaranteed () -> ()
|
|
%13 = convert_escape_to_noescape %12 : $@callee_guaranteed () -> () to $@noescape @callee_guaranteed () -> ()
|
|
%14 = apply %13() : $@noescape @callee_guaranteed () -> ()
|
|
destroy_value %13 : $@noescape @callee_guaranteed () -> ()
|
|
%15 = tuple ()
|
|
destroy_value %12 : $@callee_guaranteed () -> ()
|
|
destroy_value %11 : $@callee_guaranteed () -> ()
|
|
destroy_value %7 : $@callee_guaranteed () -> ()
|
|
destroy_value %0 : ${ var Int64 }
|
|
%20 = tuple ()
|
|
return %20 : $()
|
|
}
|
|
|
|
// closure #1 in testInoutWriteNoescapeWriteClosure()
|
|
// CHECK-LABEL: sil private [ossa] @$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 [ossa] @$s23enforce_with_opts_nob2s022testInoutWriteNoescapeG7ClosureyyFyycfU_ : $@convention(thin) (@guaranteed { var Int64 }) -> () {
|
|
// %0
|
|
bb0(%0 : @guaranteed ${ var Int64 }):
|
|
%1 = project_box %0 : ${ var Int64 }, 0
|
|
debug_value %1 : $*Int64, var, name "x", argno 1, expr op_deref
|
|
%3 = integer_literal $Builtin.Int64, 7
|
|
%4 = struct $Int64 (%3 : $Builtin.Int64)
|
|
%5 = begin_access [modify] [dynamic] %1 : $*Int64
|
|
store %4 to [trivial] %5 : $*Int64
|
|
end_access %5 : $*Int64
|
|
%8 = tuple ()
|
|
return %8 : $()
|
|
}
|
|
|
|
// closure #2 in testInoutWriteNoescapeWriteClosure()
|
|
// CHECK-LABEL: sil private [ossa] @$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 [ossa] @$s23enforce_with_opts_nob2s022testInoutWriteNoescapeG7ClosureyyFyyXEfU0_ : $@convention(thin) (@inout_aliasable Int64, @guaranteed @callee_guaranteed () -> ()) -> () {
|
|
bb0(%0 : $*Int64, %1 : @guaranteed $@callee_guaranteed () -> ()):
|
|
debug_value %0 : $*Int64, var, name "x", argno 1, expr op_deref
|
|
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 [ossa] @testOldToNewMapRead : $@convention(thin) () -> () {
|
|
// CHECK: [[GLOBAL:%.*]] = global_addr @globalX : $*X
|
|
// CHECK-NEXT: [[BEGIN:%.*]] = begin_access [read] [dynamic] [no_nested_conflict] [[GLOBAL]] : $*X
|
|
// CHECK-NEXT: load [trivial] [[BEGIN]] : $*X
|
|
// CHECK-NEXT: load [trivial] [[BEGIN]] : $*X
|
|
// CHECK-NEXT: load [trivial] [[BEGIN]] : $*X
|
|
// CHECK-NEXT: end_access [[BEGIN]] : $*X
|
|
// CHECK-NOT: begin_access
|
|
// CHECK-LABEL: } // end sil function 'testOldToNewMapRead'
|
|
sil [ossa] @testOldToNewMapRead : $@convention(thin) () -> () {
|
|
bb0:
|
|
%0 = global_addr @globalX: $*X
|
|
%1 = begin_access [read] [dynamic] %0 : $*X
|
|
%2 = load [trivial] %1 : $*X
|
|
end_access %1 : $*X
|
|
%4 = begin_access [read] [dynamic] %0 : $*X
|
|
%5 = load [trivial] %4 : $*X
|
|
end_access %4 : $*X
|
|
%7 = begin_access [read] [dynamic] %0 : $*X
|
|
%8 = load [trivial] %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 [ossa] @testOldToNewMapReadMayRelease : $@convention(thin) () -> () {
|
|
// CHECK: [[GLOBAL:%.*]] = global_addr @globalX : $*X
|
|
// CHECK-NEXT: [[BEGIN:%.*]] = begin_access [read] [dynamic] [no_nested_conflict] [[GLOBAL]] : $*X
|
|
// CHECK-NEXT: load [trivial] [[BEGIN]] : $*X
|
|
// CHECK-NEXT: load [trivial] [[BEGIN]] : $*X
|
|
// CHECK-NEXT: destroy_value
|
|
// CHECK-NEXT: load [trivial] [[BEGIN]] : $*X
|
|
// CHECK-NEXT: end_access [[BEGIN]] : $*X
|
|
// CHECK-LABEL: } // end sil function 'testOldToNewMapReadMayRelease'
|
|
sil [ossa] @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 [trivial] %1 : $*X
|
|
end_access %1 : $*X
|
|
%4 = begin_access [read] [dynamic] %0 : $*X
|
|
%5 = load [trivial] %4 : $*X
|
|
end_access %4 : $*X
|
|
destroy_value %alloc : ${ var Int32 }
|
|
%7 = begin_access [read] [dynamic] %0 : $*X
|
|
%8 = load [trivial] %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 [ossa] @testOldToNewMapWrite : $@convention(thin) () -> () {
|
|
// CHECK: [[GLOBAL:%.*]] = global_addr @globalX : $*X
|
|
// CHECK-NEXT: [[BEGIN:%.*]] = begin_access [read] [dynamic] [no_nested_conflict] [[GLOBAL]] : $*X
|
|
// CHECK-NEXT: load [trivial] [[BEGIN]] : $*X
|
|
// CHECK-NEXT: end_access [[BEGIN]] : $*X
|
|
// CHECK: [[BEGIN2:%.*]] = begin_access [modify] [dynamic] [no_nested_conflict] [[GLOBAL]] : $*X
|
|
// CHECK-NEXT: store {{.*}} to [trivial] [[BEGIN2]] : $*X
|
|
// CHECK-NEXT: end_access [[BEGIN2]] : $*X
|
|
// CHECK: [[BEGIN3:%.*]] = begin_access [read] [dynamic] [no_nested_conflict] [[GLOBAL]] : $*X
|
|
// CHECK-NEXT: load [trivial] [[BEGIN3]] : $*X
|
|
// CHECK-NEXT: end_access [[BEGIN3]] : $*X
|
|
// CHECK-LABEL: } // end sil function 'testOldToNewMapWrite'
|
|
sil [ossa] @testOldToNewMapWrite : $@convention(thin) () -> () {
|
|
bb0:
|
|
%0 = global_addr @globalX: $*X
|
|
%1 = begin_access [read] [dynamic] %0 : $*X
|
|
%2 = load [trivial] %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 [trivial] %7 : $*X
|
|
end_access %7 : $*X
|
|
%10 = begin_access [read] [dynamic] %0 : $*X
|
|
%11 = load [trivial] %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 [ossa] @testDataFlowAcrossBBs : $@convention(thin) () -> () {
|
|
// CHECK: [[GLOBAL:%.*]] = global_addr @globalX : $*X
|
|
// CHECK-NEXT: [[BEGIN:%.*]] = begin_access [modify] [dynamic] [no_nested_conflict] [[GLOBAL]] : $*X
|
|
// CHECK-NEXT: load [trivial] [[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 [trivial] [[BEGIN2]] : $*X
|
|
// CHECK-NEXT: end_access [[BEGIN2]] : $*X
|
|
// CHECK-LABEL: } // end sil function 'testDataFlowAcrossBBs'
|
|
sil [ossa] @testDataFlowAcrossBBs : $@convention(thin) () -> () {
|
|
bb0:
|
|
%0 = global_addr @globalX: $*X
|
|
%1 = begin_access [modify] [dynamic] %0 : $*X
|
|
%2 = load [trivial] %1 : $*X
|
|
end_access %1 : $*X
|
|
br bb1
|
|
|
|
bb1:
|
|
br bb2
|
|
|
|
bb2:
|
|
%4 = begin_access [read] [dynamic] %0 : $*X
|
|
%5 = load [trivial] %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 [ossa] @testDataFlowAcrossInnerLoop : $@convention(thin) () -> () {
|
|
// CHECK: [[GLOBAL:%.*]] = global_addr @globalX : $*X
|
|
// CHECK-NEXT: [[BEGIN:%.*]] = begin_access [modify] [dynamic] [no_nested_conflict] [[GLOBAL]] : $*X
|
|
// CHECK-NEXT: load [trivial] [[BEGIN]] : $*X
|
|
// CHECK-NEXT: end_access [[BEGIN]] : $*X
|
|
// CHECK-NEXT: br bb1
|
|
// CHECK: cond_br {{.*}}, bb2, bb3
|
|
// CHECK: bb3:
|
|
// CHECK: [[BEGIN2:%.*]] = begin_access [read] [dynamic] [no_nested_conflict] [[GLOBAL]] : $*X
|
|
// CHECK-NEXT: load [trivial] [[BEGIN2]] : $*X
|
|
// CHECK-NEXT: end_access [[BEGIN2]] : $*X
|
|
// CHECK-LABEL: } // end sil function 'testDataFlowAcrossInnerLoop'
|
|
sil [ossa] @testDataFlowAcrossInnerLoop : $@convention(thin) () -> () {
|
|
bb0:
|
|
%0 = global_addr @globalX: $*X
|
|
%1 = begin_access [modify] [dynamic] %0 : $*X
|
|
%2 = load [trivial] %1 : $*X
|
|
end_access %1 : $*X
|
|
br bb1
|
|
|
|
bb1:
|
|
%cond = integer_literal $Builtin.Int1, 1
|
|
cond_br %cond, bb2, bb3
|
|
|
|
bb2:
|
|
br bb1
|
|
|
|
bb3:
|
|
%4 = begin_access [read] [dynamic] %0 : $*X
|
|
%5 = load [trivial] %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 [ossa] @testConflictInInnerLoop : $@convention(thin) () -> () {
|
|
// CHECK: [[GLOBAL:%.*]] = global_addr @globalX : $*X
|
|
// CHECK-NEXT: [[BEGIN:%.*]] = begin_access [modify] [dynamic] [no_nested_conflict] [[GLOBAL]] : $*X
|
|
// CHECK-NEXT: load [trivial] [[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 [trivial] [[BEGIN2]] : $*X
|
|
// CHECK-NEXT: end_access [[BEGIN2]] : $*X
|
|
// CHECK: cond_br {{.*}}, bb2, bb3
|
|
// CHECK: bb3:
|
|
// CHECK: [[BEGIN3:%.*]] = begin_access [read] [dynamic] [no_nested_conflict] [[GLOBAL]] : $*X
|
|
// CHECK-NEXT: load [trivial] [[BEGIN3]] : $*X
|
|
// CHECK-NEXT: end_access [[BEGIN3]] : $*X
|
|
// CHECK-NOT: begin_access
|
|
// CHECK-LABEL: } // end sil function 'testConflictInInnerLoop'
|
|
sil [ossa] @testConflictInInnerLoop : $@convention(thin) () -> () {
|
|
bb0:
|
|
%0 = global_addr @globalX: $*X
|
|
%1 = begin_access [modify] [dynamic] %0 : $*X
|
|
%2 = load [trivial] %1 : $*X
|
|
end_access %1 : $*X
|
|
br bb1
|
|
|
|
bb1:
|
|
%4 = begin_access [read] [dynamic] %0 : $*X
|
|
%5 = load [trivial] %4 : $*X
|
|
end_access %4 : $*X
|
|
%cond = integer_literal $Builtin.Int1, 1
|
|
cond_br %cond, bb2, bb3
|
|
|
|
bb2:
|
|
br bb1
|
|
|
|
bb3:
|
|
%7 = begin_access [read] [dynamic] %0 : $*X
|
|
%8 = load [trivial] %7 : $*X
|
|
end_access %7 : $*X
|
|
%10 = tuple ()
|
|
return %10 : $()
|
|
}
|
|
|
|
sil hidden_external [ossa] [global_init] @globalAddressor : $@convention(thin) () -> Builtin.RawPointer
|
|
|
|
// public func testUnidentifiedAccessInLoop() {
|
|
// Tests Unidentified Accesses detection + propagation
|
|
//
|
|
// CHECK-LABEL: sil [ossa] @testUnidentifiedAccessInLoop : $@convention(thin) () -> () {
|
|
// CHECK: [[GLOBAL:%.*]] = global_addr @globalX : $*X
|
|
// CHECK-NEXT: [[BEGIN:%.*]] = begin_access [modify] [dynamic] [no_nested_conflict] [[GLOBAL]] : $*X
|
|
// CHECK-NEXT: load [trivial] [[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 [trivial] [[BEGIN2]] : $*Int64
|
|
// CHECK-NEXT: end_access [[BEGIN2]] : $*Int64
|
|
// CHECK: cond_br {{.*}}, bb2, bb3
|
|
// CHECK: bb3:
|
|
// CHECK: [[BEGIN3:%.*]] = begin_access [read] [dynamic] [no_nested_conflict] [[GLOBAL]] : $*X
|
|
// CHECK-NEXT: load [trivial] [[BEGIN3]] : $*X
|
|
// CHECK-NEXT: end_access [[BEGIN3]] : $*X
|
|
// CHECK-NOT: begin_access
|
|
// CHECK-LABEL: } // end sil function 'testUnidentifiedAccessInLoop'
|
|
sil [ossa] @testUnidentifiedAccessInLoop : $@convention(thin) () -> () {
|
|
bb0:
|
|
%0 = global_addr @globalX: $*X
|
|
%1 = begin_access [modify] [dynamic] %0 : $*X
|
|
%2 = load [trivial] %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 [trivial] %u3 : $*Int64
|
|
end_access %u3 : $*Int64
|
|
%cond = integer_literal $Builtin.Int1, 1
|
|
cond_br %cond, bb2, bb3
|
|
|
|
bb2:
|
|
br bb1
|
|
|
|
bb3:
|
|
%7 = begin_access [read] [dynamic] %0 : $*X
|
|
%8 = load [trivial] %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 [ossa] @testIrreducibleGraph : $@convention(thin) () -> () {
|
|
// CHECK: [[GLOBAL:%.*]] = global_addr @globalX : $*X
|
|
// CHECK-NEXT: [[BEGIN:%.*]] = begin_access [read] [dynamic] [no_nested_conflict] [[GLOBAL]] : $*X
|
|
// CHECK-NEXT: load [trivial] [[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 [trivial] [[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 [trivial] [[BEGIN3]] : $*X
|
|
// CHECK-NEXT: end_access [[BEGIN3]] : $*X
|
|
// CHECK: cond_br {{.*}}, bb5, bb9
|
|
// CHECK: bb6:
|
|
// CHECK: [[BEGIN4:%.*]] = begin_access [read] [dynamic] [no_nested_conflict] [[GLOBAL]] : $*X
|
|
// CHECK-NEXT: load [trivial] [[BEGIN4]] : $*X
|
|
// CHECK-NEXT: end_access [[BEGIN4]] : $*X
|
|
// CHECK: cond_br {{.*}}
|
|
// CHECK: [[BEGIN5:%.*]] = begin_access [read] [dynamic] [no_nested_conflict] [[GLOBAL]] : $*X
|
|
// CHECK-NEXT: load [trivial] [[BEGIN5]] : $*X
|
|
// CHECK-NEXT: end_access [[BEGIN5]] : $*X
|
|
// CHECK-NOT: begin_access
|
|
// CHECK-LABEL: } // end sil function 'testIrreducibleGraph'
|
|
sil [ossa] @testIrreducibleGraph : $@convention(thin) () -> () {
|
|
bb0:
|
|
%0 = global_addr @globalX: $*X
|
|
%1 = begin_access [read] [dynamic] %0 : $*X
|
|
%2 = load [trivial] %1 : $*X
|
|
end_access %1 : $*X
|
|
br bb1
|
|
|
|
bb1:
|
|
%4 = begin_access [modify] [dynamic] %0 : $*X
|
|
%5 = load [trivial] %4 : $*X
|
|
end_access %4 : $*X
|
|
%cond1 = integer_literal $Builtin.Int1, 1
|
|
cond_br %cond1, bb2, bb3
|
|
|
|
bb2:
|
|
br bb4
|
|
|
|
bb3:
|
|
br bb6
|
|
|
|
bb4:
|
|
%6 = begin_access [read] [dynamic] %0 : $*X
|
|
%7 = load [trivial] %6 : $*X
|
|
end_access %6 : $*X
|
|
%cond2 = integer_literal $Builtin.Int1, 1
|
|
cond_br %cond2, bb5, bb9
|
|
|
|
bb5:
|
|
br bb6
|
|
|
|
bb6:
|
|
%8 = begin_access [read] [dynamic] %0 : $*X
|
|
%9 = load [trivial] %8 : $*X
|
|
end_access %8 : $*X
|
|
%cond3 = integer_literal $Builtin.Int1, 1
|
|
cond_br %cond3, bb7, bb8
|
|
|
|
bb7:
|
|
br bb4
|
|
|
|
bb8:
|
|
br bb1
|
|
|
|
bb9:
|
|
%10 = begin_access [read] [dynamic] %0 : $*X
|
|
%11 = load [trivial] %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 [ossa] @testIrreducibleGraph2 : $@convention(thin) () -> () {
|
|
// CHECK: [[GLOBAL:%.*]] = global_addr @globalX : $*X
|
|
// CHECK-NEXT: [[BEGIN:%.*]] = begin_access [read] [dynamic] [no_nested_conflict] [[GLOBAL]] : $*X
|
|
// CHECK-NEXT: load [trivial] [[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 [trivial] [[BEGIN1]] : $*X
|
|
// CHECK-NEXT: end_access [[BEGIN1]] : $*X
|
|
// CHECK: cond_br {{.*}}
|
|
// CHECK: [[BEGIN2:%.*]] = begin_access [read] [dynamic] [no_nested_conflict] [[GLOBAL]] : $*X
|
|
// CHECK-NEXT: load [trivial] [[BEGIN2]] : $*X
|
|
// CHECK-NEXT: end_access [[BEGIN2]] : $*X
|
|
// CHECK-NEXT: br
|
|
// CHECK: [[BEGIN3:%.*]] = begin_access [read] [dynamic] [no_nested_conflict] [[GLOBAL]] : $*X
|
|
// CHECK-NEXT: load [trivial] [[BEGIN3]] : $*X
|
|
// CHECK-NEXT: br
|
|
// CHECK: load [trivial] [[BEGIN3]] : $*X
|
|
// CHECK-NEXT: end_access [[BEGIN3]] : $*X
|
|
// CHECK: cond_br {{.*}}
|
|
// CHECK: [[BEGIN5:%.*]] = begin_access [read] [dynamic] [no_nested_conflict] [[GLOBAL]] : $*X
|
|
// CHECK-NEXT: load [trivial] [[BEGIN5]] : $*X
|
|
// CHECK-NEXT: end_access [[BEGIN5]] : $*X
|
|
// CHECK-NOT: begin_access
|
|
// CHECK-LABEL: } // end sil function 'testIrreducibleGraph2'
|
|
sil [ossa] @testIrreducibleGraph2 : $@convention(thin) () -> () {
|
|
bb0:
|
|
%0 = global_addr @globalX: $*X
|
|
%1 = begin_access [read] [dynamic] %0 : $*X
|
|
%2 = load [trivial] %1 : $*X
|
|
end_access %1 : $*X
|
|
br bb1
|
|
|
|
bb1:
|
|
%4 = begin_access [modify] [dynamic] %0 : $*X
|
|
%5 = load [trivial] %4 : $*X
|
|
end_access %4 : $*X
|
|
%cond1 = integer_literal $Builtin.Int1, 1
|
|
cond_br %cond1, bb2, bb3
|
|
|
|
bb2:
|
|
br bb4
|
|
|
|
bb3:
|
|
br bb6
|
|
|
|
bb4:
|
|
%6 = begin_access [read] [dynamic] %0 : $*X
|
|
%7 = load [trivial] %6 : $*X
|
|
end_access %6 : $*X
|
|
br bb5
|
|
|
|
bb5:
|
|
br bb6
|
|
|
|
bb6:
|
|
%8 = begin_access [read] [dynamic] %0 : $*X
|
|
%9 = load [trivial] %8 : $*X
|
|
end_access %8 : $*X
|
|
br bb7
|
|
|
|
bb7:
|
|
%10 = begin_access [read] [dynamic] %0 : $*X
|
|
%11 = load [trivial] %10 : $*X
|
|
end_access %10 : $*X
|
|
%cond2 = integer_literal $Builtin.Int1, 1
|
|
cond_br %cond2, bb8, bb9
|
|
|
|
bb8:
|
|
br bb4
|
|
|
|
bb9:
|
|
%13 = begin_access [read] [dynamic] %0 : $*X
|
|
%14 = load [trivial] %13 : $*X
|
|
end_access %13 : $*X
|
|
%16 = tuple ()
|
|
return %16 : $()
|
|
}
|
|
|
|
class RefElemClass {
|
|
var x : BitfieldOne
|
|
var y : Int32
|
|
init()
|
|
}
|
|
|
|
// Merge access overlapping scopes.
|
|
//
|
|
// CHECK-LABEL: sil [ossa] @ref_elem_c : $@convention(thin) (@owned RefElemClass) -> () {
|
|
// CHECK: [[GLOBAL:%.*]] = global_addr @globalX : $*X
|
|
// CHECK-NEXT: [[BEGIN:%.*]] = begin_access [read] [dynamic] [no_nested_conflict] [[GLOBAL]] : $*X
|
|
// CHECK-NEXT: load [trivial] [[BEGIN]] : $*X
|
|
// CHECK-NEXT: end_access [[BEGIN]] : $*X
|
|
// CHECK-NEXT: [[BORROW:%.*]] = begin_borrow %0 : $RefElemClass
|
|
// CHECK-NEXT: [[REFX:%.*]] = ref_element_addr [[BORROW]] : $RefElemClass, #RefElemClass.x
|
|
// CHECK-NEXT: [[REFY:%.*]] = ref_element_addr [[BORROW]] : $RefElemClass, #RefElemClass.y
|
|
// CHECK-NEXT: [[BEGINX:%.*]] = begin_access [modify] [dynamic] [[REFX]] : $*BitfieldOne
|
|
// CHECK: [[BEGINY:%.*]] = begin_access [modify] [dynamic] [no_nested_conflict] [[REFY]] : $*Int32
|
|
// CHECK: end_access [[BEGINX]] : $*BitfieldOne
|
|
// CHECK-NEXT: end_access [[BEGINY]] : $*Int32
|
|
// CHECK-NEXT: end_borrow [[BORROW]] : $RefElemClass
|
|
// CHECK-LABEL: } // end sil function 'ref_elem_c'
|
|
sil [ossa] @ref_elem_c : $@convention(thin) (@owned RefElemClass) -> () {
|
|
bb0(%0 : @owned $RefElemClass):
|
|
%b0 = global_addr @globalX: $*X
|
|
%1 = begin_access [read] [dynamic] %b0 : $*X
|
|
%2 = load [trivial] %1 : $*X
|
|
end_access %1 : $*X
|
|
%borrow = begin_borrow %0 : $RefElemClass
|
|
%x = ref_element_addr %borrow : $RefElemClass, #RefElemClass.x
|
|
%y = ref_element_addr %borrow : $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
|
|
end_borrow %borrow : $RefElemClass
|
|
destroy_value %0 : $RefElemClass
|
|
%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 [ossa] @ref_elem_no_alias : $@convention(thin) (@owned RefElemNoConflictClass) -> () {
|
|
// CHECK: [[BORROW:%.*]] = begin_borrow %0 : $RefElemNoConflictClass
|
|
// CHECK: [[REFX:%.*]] = ref_element_addr [[BORROW]] : $RefElemNoConflictClass, #RefElemNoConflictClass.x
|
|
// CHECK-NEXT: [[REFY:%.*]] = ref_element_addr [[BORROW]] : $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-NEXT: end_borrow [[BORROW]] : $RefElemNoConflictClass
|
|
// CHECK-LABEL: } // end sil function 'ref_elem_no_alias'
|
|
sil [ossa] @ref_elem_no_alias : $@convention(thin) (@owned RefElemNoConflictClass) -> () {
|
|
bb0(%0 : @owned $RefElemNoConflictClass):
|
|
%borrow = begin_borrow %0 : $RefElemNoConflictClass
|
|
%x = ref_element_addr %borrow : $RefElemNoConflictClass, #RefElemNoConflictClass.x
|
|
%y = ref_element_addr %borrow : $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
|
|
end_borrow %borrow : $RefElemNoConflictClass
|
|
destroy_value %0 : $RefElemNoConflictClass
|
|
%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 [ossa] @testStronglyConnectedComponent : $@convention(thin) () -> () {
|
|
// CHECK: [[GLOBAL:%.*]] = global_addr @globalX : $*X
|
|
// CHECK-NEXT: [[BEGIN:%.*]] = begin_access [read] [dynamic] [no_nested_conflict] [[GLOBAL]] : $*X
|
|
// CHECK-NEXT: load [trivial] [[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 [trivial] [[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 [trivial] [[BEGIN2]] : $*X
|
|
// CHECK-NEXT: end_access [[BEGIN2]] : $*X
|
|
// CHECK-NEXT: br
|
|
// CHECK: [[BEGIN3:%.*]] = begin_access [read] [dynamic] [no_nested_conflict] [[GLOBAL]] : $*X
|
|
// CHECK-NEXT: load [trivial] [[BEGIN3]] : $*X
|
|
// CHECK: br
|
|
// CHECK: load [trivial] [[BEGIN3]] : $*X
|
|
// CHECK-NEXT: end_access [[BEGIN3]] : $*X
|
|
// CHECK: cond_br {{.*}}
|
|
// CHECK: [[BEGIN4:%.*]] = begin_access [read] [dynamic] [no_nested_conflict] [[GLOBAL]] : $*X
|
|
// CHECK-NEXT: load [trivial] [[BEGIN4]] : $*X
|
|
// CHECK-NEXT: end_access [[BEGIN4]] : $*X
|
|
// CHECK: br
|
|
// CHECK: [[BEGIN5:%.*]] = begin_access [read] [dynamic] [no_nested_conflict] [[GLOBAL]] : $*X
|
|
// CHECK-NEXT: load [trivial] [[BEGIN5]] : $*X
|
|
// CHECK-NEXT: end_access [[BEGIN5]] : $*X
|
|
// CHECK-NOT: begin_access
|
|
// CHECK-LABEL: } // end sil function 'testStronglyConnectedComponent'
|
|
sil [ossa] @testStronglyConnectedComponent : $@convention(thin) () -> () {
|
|
bb0:
|
|
%0 = global_addr @globalX: $*X
|
|
%1 = begin_access [read] [dynamic] %0 : $*X
|
|
%2 = load [trivial] %1 : $*X
|
|
end_access %1 : $*X
|
|
br bb1
|
|
|
|
bb1:
|
|
%4 = begin_access [modify] [dynamic] %0 : $*X
|
|
%5 = load [trivial] %4 : $*X
|
|
end_access %4 : $*X
|
|
%cond1 = integer_literal $Builtin.Int1, 1
|
|
cond_br %cond1, bb2, bb3
|
|
|
|
bb2:
|
|
br bb4
|
|
|
|
bb3:
|
|
br bb6
|
|
|
|
bb4:
|
|
%6 = begin_access [read] [dynamic] %0 : $*X
|
|
%7 = load [trivial] %6 : $*X
|
|
end_access %6 : $*X
|
|
br bb5
|
|
|
|
bb5:
|
|
br bb6
|
|
|
|
bb6:
|
|
%8 = begin_access [read] [dynamic] %0 : $*X
|
|
%9 = load [trivial] %8 : $*X
|
|
end_access %8 : $*X
|
|
br bb7
|
|
|
|
bb7:
|
|
%10 = begin_access [read] [dynamic] %0 : $*X
|
|
%11 = load [trivial] %10 : $*X
|
|
end_access %10 : $*X
|
|
%cond2 = integer_literal $Builtin.Int1, 1
|
|
cond_br %cond2, bb8, bb9
|
|
|
|
bb8:
|
|
%13 = begin_access [read] [dynamic] %0 : $*X
|
|
%14 = load [trivial] %13 : $*X
|
|
end_access %13 : $*X
|
|
br bb4
|
|
|
|
bb9:
|
|
%16 = begin_access [read] [dynamic] %0 : $*X
|
|
%17 = load [trivial] %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 [ossa] @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 [ossa] @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 [trivial] %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 [ossa] @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 [ossa] @testMergeWithSecondConflict : $@convention(thin) () -> () {
|
|
bb0:
|
|
%0 = global_addr @globalX: $*X
|
|
%1 = begin_access [read] [dynamic] %0 : $*X
|
|
%2 = load [trivial] %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 : $()
|
|
}
|
|
|
|
// --- 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] [ossa] @readFlags : $@convention(method) (Int64, @guaranteed TestClass) -> Bool {
|
|
// CHECK: bb0(%0 : $Int64, %1 : @guaranteed $TestClass):
|
|
// CHECK: [[ADR:%.*]] = ref_element_addr %1 : $TestClass, #TestClass.flags
|
|
// CHECK: begin_access [read] [dynamic] [no_nested_conflict] [[ADR]] : $*Int64
|
|
// CHECK: load [trivial] %4 : $*Builtin.Int64
|
|
// CHECK: end_access
|
|
// CHECK-LABEL: } // end sil function 'readFlags'
|
|
sil hidden [noinline] [ossa] @readFlags : $@convention(method) (Int64, @guaranteed TestClass) -> Bool {
|
|
bb0(%0 : $Int64, %1 : @guaranteed $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 [trivial] %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 [ossa] @testReadModifyConflictPair : $@convention(method) (@guaranteed TestClass) -> () {
|
|
// CHECK: bb0(%0 : @guaranteed $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 [ossa] @testReadModifyConflictPair : $@convention(method) (@guaranteed TestClass) -> () {
|
|
bb0(%0 : @guaranteed $TestClass):
|
|
%1 = ref_element_addr %0 : $TestClass, #TestClass.flags
|
|
%2 = begin_access [read] [dynamic] %1 : $*Int64
|
|
%3 = load [trivial] %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 [trivial] %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 [ossa] @testNestedReadMerging : $@convention(thin) () -> () {
|
|
// CHECK: [[GLOBAL:%.*]] = global_addr @globalX : $*X
|
|
// CHECK-NEXT: [[BEGIN:%.*]] = begin_access [read] [dynamic] [no_nested_conflict] [[GLOBAL]] : $*X
|
|
// CHECK-NEXT: load [trivial] [[BEGIN]] : $*X
|
|
// CHECK-NEXT: load [trivial] [[BEGIN]] : $*X
|
|
// CHECK-NEXT: [[BEGIN2:%.*]] = begin_access [read] [dynamic] [no_nested_conflict] [[GLOBAL]] : $*X
|
|
// CHECK-NEXT: load [trivial] [[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 [ossa] @testNestedReadMerging : $@convention(thin) () -> () {
|
|
bb0:
|
|
%0 = global_addr @globalX: $*X
|
|
%1 = begin_access [read] [dynamic] %0 : $*X
|
|
%2 = load [trivial] %1 : $*X
|
|
end_access %1 : $*X
|
|
%4 = begin_access [read] [dynamic] %0 : $*X
|
|
%5 = load [trivial] %4 : $*X
|
|
%7 = begin_access [read] [dynamic] %0 : $*X
|
|
%8 = load [trivial] %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 [ossa] @testNestedWriteBailout : $@convention(thin) () -> () {
|
|
// CHECK: [[GLOBAL:%.*]] = global_addr @globalX : $*X
|
|
// CHECK-NEXT: [[BEGIN:%.*]] = begin_access [modify] [dynamic] [[GLOBAL]] : $*X
|
|
// CHECK-NEXT: load [trivial] [[BEGIN]] : $*X
|
|
// CHECK-NEXT: load [trivial] [[BEGIN]] : $*X
|
|
// CHECK-NEXT: [[BEGIN2:%.*]] = begin_access [modify] [dynamic] [no_nested_conflict] [[GLOBAL]] : $*X
|
|
// CHECK-NEXT: load [trivial] [[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 [ossa] @testNestedWriteBailout : $@convention(thin) () -> () {
|
|
bb0:
|
|
%0 = global_addr @globalX: $*X
|
|
%1 = begin_access [modify] [dynamic] %0 : $*X
|
|
%2 = load [trivial] %1 : $*X
|
|
end_access %1 : $*X
|
|
%4 = begin_access [modify] [dynamic] %0 : $*X
|
|
%5 = load [trivial] %4 : $*X
|
|
%7 = begin_access [modify] [dynamic] %0 : $*X
|
|
%8 = load [trivial] %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 [ossa] @testTransformArgumentCaller : $@convention(thin) (Int64, @guaranteed { var Int64 }, @guaranteed { var Int64 }) -> Int64 {
|
|
// CHECK: begin_access [modify] [dynamic] [no_nested_conflict]
|
|
// CHECK-LABEL: } // end sil function 'testTransformArgumentCaller'
|
|
sil [ossa] @testTransformArgumentCaller : $@convention(thin) (Int64, @guaranteed { var Int64 }, @guaranteed { var Int64 }) -> Int64 {
|
|
bb0(%0 : $Int64, %1 : @guaranteed ${ var Int64 }, %2 : @guaranteed ${ var Int64 }):
|
|
%boxadr = project_box %1 : $ { var Int64 }, 0
|
|
%access = begin_access [modify] [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 [trivial] %access : $*Int64
|
|
end_access %access : $*Int64
|
|
return %call : $Int64
|
|
}
|
|
|
|
// CHECK-LABEL: sil [ossa] @testTransformArgumentCallee : $@convention(thin) (@guaranteed { var Int64 }) -> Int64 {
|
|
// CHECK: begin_access [read] [dynamic] [no_nested_conflict]
|
|
// CHECK-LABEL: } // end sil function 'testTransformArgumentCallee'
|
|
sil [ossa] @testTransformArgumentCallee : $@convention(thin) (@guaranteed { var Int64 }) -> Int64 {
|
|
bb0(%0 : @guaranteed ${ var Int64 }):
|
|
%boxadr = project_box %0 : $ { var Int64 }, 0
|
|
%access = begin_access [read] [dynamic] [no_nested_conflict] %boxadr : $*Int64
|
|
%val = load [trivial] %access : $*Int64
|
|
end_access %access : $*Int64
|
|
return %val : $Int64
|
|
}
|
|
|
|
|
|
// Test that SILInstruction::mayRelease recognizes
|
|
// unmanaged_release_value without asserting.
|
|
// CHECK-LABEL: sil [transparent] [serialized] [ossa] @testUnmanagedRelease : $@convention(method) <T where T : AnyObject> (@inout T) -> () {
|
|
// CHECK: begin_access
|
|
// CHECK: unmanaged_release_value
|
|
// CHECK-LABEL: } // end sil function 'testUnmanagedRelease'
|
|
sil [transparent] [serialized] [ossa] @testUnmanagedRelease : $@convention(method) <T where T : AnyObject> (@inout T) -> () {
|
|
bb0(%0 : $*T):
|
|
%access = begin_access [modify] [dynamic] %0 : $*T
|
|
%load = load [copy] %access : $*T
|
|
end_access %access : $*T
|
|
%unmanaged1 = ref_to_unmanaged %load : $T to $@sil_unmanaged T
|
|
%copy1 = strong_copy_unmanaged_value %unmanaged1 : $@sil_unmanaged T
|
|
unmanaged_retain_value %copy1 : $T
|
|
%cast1 = unchecked_bitwise_cast %copy1 : $T to $UnsafeRawPointer
|
|
destroy_value %copy1 : $T
|
|
%cast2 = unchecked_bitwise_cast %cast1 : $UnsafeRawPointer to $T
|
|
%copy2 = copy_value %cast2 : $T
|
|
%unmanaged2 = ref_to_unmanaged %copy2 : $T to $@sil_unmanaged T
|
|
destroy_value %copy2 : $T
|
|
%copy3 = strong_copy_unmanaged_value %unmanaged2 : $@sil_unmanaged T
|
|
unmanaged_release_value %copy3 : $T
|
|
destroy_value %copy3 : $T
|
|
destroy_value %load : $T
|
|
%108 = tuple ()
|
|
return %108 : $()
|
|
}
|
|
|
|
// Test that SILInstruction::mayRelease recognizes
|
|
// unchecked_ownership_conversion without asserting.
|
|
// CHECK-LABEL: sil hidden [ossa] @$testUncheckedOwnershipConversion :
|
|
// CHECK: begin_access
|
|
// CHECK: unchecked_ownership_conversion
|
|
// CHECK-LABEL: } // end sil function '$testUncheckedOwnershipConversion'
|
|
sil hidden [ossa] @$testUncheckedOwnershipConversion : $@convention(method) (@guaranteed Klass, @in Int64) -> @owned Builtin.NativeObject {
|
|
bb0(%0 : @guaranteed $Klass, %1 : $*Int64):
|
|
%2 = begin_access [modify] [dynamic] %1 : $*Int64
|
|
%3 = struct_element_addr %2 : $*Int64, #Int64._value
|
|
%4 = load [trivial] %3 : $*Builtin.Int64
|
|
%5 = integer_literal $Builtin.Int1, -1
|
|
%6 = builtin "ssub_with_overflow_Int64"(%4 : $Builtin.Int64, %4 : $Builtin.Int64, %5 : $Builtin.Int1) : $(Builtin.Int64, Builtin.Int1)
|
|
(%7, %8) = destructure_tuple %6 : $(Builtin.Int64, Builtin.Int1)
|
|
cond_fail %8 : $Builtin.Int1, "arithmetic overflow"
|
|
%10 = struct $Int64 (%7 : $Builtin.Int64)
|
|
store %10 to [trivial] %2 : $*Int64
|
|
end_access %2 : $*Int64
|
|
%13 = unchecked_ref_cast %0 : $Klass to $Builtin.NativeObject
|
|
%14 = unchecked_ownership_conversion %13 : $Builtin.NativeObject, @guaranteed to @owned
|
|
return %14 : $Builtin.NativeObject
|
|
}
|
|
|
|
class Bar {
|
|
func foo()
|
|
}
|
|
|
|
// Nothing happens here. I guess we check that the analysis does not crash.
|
|
//
|
|
// CHECK-LABEL: sil [ossa] @$testEndLifetime : $@convention(method) (@owned Bar, @in Bar) -> () {
|
|
// CHECK-LABEL: } // end sil function '$testEndLifetime'
|
|
sil [ossa] @$testEndLifetime : $@convention(method) (@owned Bar, @in Bar) -> () {
|
|
bb0(%0 : @owned $Bar, %1 : $*Bar):
|
|
%b = begin_access [modify] [dynamic] %1 : $*Bar
|
|
end_access %b : $*Bar
|
|
%f = objc_method %0 : $Bar, #Bar.foo!foreign : (Bar) -> () -> (), $@convention(objc_method) (Bar) -> ()
|
|
%c = apply %f(%0) : $@convention(objc_method) (Bar) -> ()
|
|
end_lifetime %0 : $Bar
|
|
destroy_addr %1 : $*Bar
|
|
%r = tuple ()
|
|
return %r : $()
|
|
}
|
|
|
|
// CHECK-LABEL: sil [ossa] @testBorrowedAccess : $@convention(thin) (@owned NonTrivial) -> () {
|
|
// CHECK: bb0(%0 : @owned $NonTrivial):
|
|
// CHECK: [[BORROW:%.*]] = begin_borrow %0 : $NonTrivial
|
|
// CHECK: [[ADR:%.*]] = ref_element_addr [[BORROW]] : $NonTrivial, #NonTrivial.object
|
|
// CHECK: [[ACCESS:%.*]] = begin_access [modify] [dynamic] [[ADR]] : $*Klass
|
|
// CHECK-NOT: access
|
|
// CHECK-NOT: borrow
|
|
// CHECK: apply %{{.*}}([[ACCESS]]) : $@convention(thin) (@inout Klass) -> ()
|
|
// CHECK: apply %{{.*}}([[ACCESS]]) : $@convention(thin) (@inout Klass) -> ()
|
|
// CHECK: end_access [[ACCESS]] : $*Klass
|
|
// CHECK: end_borrow [[BORROW]] : $NonTrivial
|
|
// CHECK: destroy_value %0 : $NonTrivial
|
|
// CHECK-LABEL: } // end sil function 'testBorrowedAccess'
|
|
sil [ossa] @testBorrowedAccess : $@convention(thin) (@owned NonTrivial) -> () {
|
|
bb0(%0 : @owned $NonTrivial):
|
|
%f = function_ref @hasIndirectArg : $@convention(thin) (@inout Klass) -> ()
|
|
%b1 = begin_borrow %0 : $NonTrivial
|
|
%e1 = ref_element_addr %b1 : $NonTrivial, #NonTrivial.object
|
|
%a1 = begin_access [modify] [dynamic] %e1 : $*Klass
|
|
%c1 = apply %f(%a1) : $@convention(thin) (@inout Klass) -> ()
|
|
end_access %a1 : $*Klass
|
|
end_borrow %b1 : $NonTrivial
|
|
%b2 = begin_borrow %0 : $NonTrivial
|
|
%e2 = ref_element_addr %b2 : $NonTrivial, #NonTrivial.object
|
|
%a2 = begin_access [modify] [dynamic] %e2 : $*Klass
|
|
%c2 = apply %f(%a2) : $@convention(thin) (@inout Klass) -> ()
|
|
end_access %a2 : $*Klass
|
|
end_borrow %b2 : $NonTrivial
|
|
destroy_value %0 : $NonTrivial
|
|
return undef : $()
|
|
}
|
|
|
|
sil shared [clang _swift_stdlib_has_malloc_size] @_swift_stdlib_has_malloc_size : $@convention(c) () -> Bool
|
|
sil shared [clang _swift_stdlib_malloc_size] @_swift_stdlib_malloc_size : $@convention(c) (UnsafeRawPointer) -> Int
|
|
|
|
// CHECK-LABEL: sil [ossa] @ignoreMallocSize :
|
|
// CHECK: begin_access [read] [dynamic] [no_nested_conflict]
|
|
// CHECK-LABEL: } // end sil function 'ignoreMallocSize'
|
|
sil [ossa] @ignoreMallocSize : $@convention(thin) (UnsafeRawPointer) -> () {
|
|
bb0(%0 : $UnsafeRawPointer):
|
|
%2 = global_addr @globalX: $*X
|
|
%3 = begin_access [read] [dynamic] %2 : $*X
|
|
%4 = function_ref @_swift_stdlib_has_malloc_size : $@convention(c) () -> Bool
|
|
%5 = apply %4() : $@convention(c) () -> Bool
|
|
%6 = function_ref @_swift_stdlib_malloc_size : $@convention(c) (UnsafeRawPointer) -> Int
|
|
%7 = apply %6(%0) : $@convention(c) (UnsafeRawPointer) -> Int
|
|
end_access %3 : $*X
|
|
%9 = tuple ()
|
|
return %9 : $()
|
|
}
|
|
|
|
|
|
// CHECK-LABEL: sil [ossa] @merge_3_pairs_and_escaping_pointer :
|
|
// CHECK: ref_element_addr
|
|
// CHECK-NEXT: begin_access
|
|
// CHECK-NEXT: end_access
|
|
// CHECK-NEXT: ref_element_addr
|
|
// CHECK-NEXT: begin_access
|
|
// CHECK-NEXT: address_to_pointer
|
|
// CHECK-NEXT: end_access
|
|
// CHECK-NEXT: tuple
|
|
// CHECK-LABEL: } // end sil function 'merge_3_pairs_and_escaping_pointer'
|
|
sil [ossa] @merge_3_pairs_and_escaping_pointer : $@convention(thin) (@guaranteed RefElemClass) -> () {
|
|
bb0(%1 : @guaranteed $RefElemClass):
|
|
%2 = ref_element_addr %1, #RefElemClass.x
|
|
%3 = begin_access [modify] [dynamic] %2
|
|
end_access %3
|
|
%5 = begin_access [modify] [dynamic] %2
|
|
end_access %5
|
|
%7 = ref_element_addr %1, #RefElemClass.x
|
|
%8 = begin_access [modify] [dynamic] %7
|
|
%9 = address_to_pointer %8 to $Builtin.RawPointer
|
|
end_access %8
|
|
%11 = tuple ()
|
|
return %11
|
|
}
|