mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
Change the connection graph builder to map an existential address and its opened address onto the same node. Fixes <rdar://59559805> miscompile; use-after-free Immediate bug: EscapeAnalysis::mayReleaseContent incorrectly returns 'false' when comparing a store into an existential's value with a release of the existential. How this was exposed: mayReleaseContent was made more aggressive so that only the connection graph node representing the released object is considered to be "released". This fails for existentials because a separate connection graph node is created for the value within the existential. This is just one manifestation of a broader bug. Representing an existential as two logically distinct objects could also result in incorrect escaping information and incorrect alias analysis. Note that copying directly into an existential address and storing into the opened value in fact modify the same memory. Furthermore, when opening an existential, no local reference count is used to keep the opened value "alive" independent from the existential (this is another assumption made by mayReleaseContent). This is always how existentials were represented in the connection graph, but issues had never been exposed.
596 lines
33 KiB
Plaintext
596 lines
33 KiB
Plaintext
// RUN: %target-sil-opt %s -escapes-dump -escapes-internal-verify -o /dev/null | %FileCheck %s
|
|
|
|
// REQUIRES: asserts
|
|
// REQUIRES: OS=macosx
|
|
// REQUIRES: PTRSIZE=64
|
|
|
|
sil_stage canonical
|
|
|
|
import Builtin
|
|
import Swift
|
|
import SwiftShims
|
|
|
|
// =============================================================================
|
|
// Test call to array.uninitialized that has "extra" release_value uses
|
|
|
|
class C {
|
|
var c: C
|
|
}
|
|
|
|
class DummyArrayStorage<Element> {
|
|
@_hasStorage var count: Int { get }
|
|
@_hasStorage var capacity: Int { get }
|
|
init()
|
|
}
|
|
|
|
// init_any_array_with_buffer
|
|
sil [_semantics "array.uninitialized"] @init_any_array_with_buffer : $@convention(thin) (@owned DummyArrayStorage<AnyObject>, Int32, @thin Array<AnyObject>.Type) -> (@owned Array<AnyObject>, UnsafeMutablePointer<AnyObject>)
|
|
|
|
// CHECK-LABEL: CG of testBadArrayUninit
|
|
// CHECK-NEXT: Val [ref] %2 Esc: , Succ: (%2.1)
|
|
// CHECK-NEXT: Con [int] %2.1 Esc: G, Succ: (%2.2)
|
|
// CHECK-NEXT: Con [ref] %2.2 Esc: G, Succ:
|
|
// CHECK-NEXT: Val %5 Esc: , Succ: (%5.1)
|
|
// CHECK-NEXT: Con %5.1 Esc: G, Succ: %10
|
|
// CHECK-NEXT: Val [ref] %10 Esc: G, Succ: (%10.1)
|
|
// CHECK-NEXT: Con %10.1 Esc: G, Succ:
|
|
// CHECK-LABEL: End
|
|
sil hidden @testBadArrayUninit : $@convention(thin) (Builtin.Word, Int32) -> () {
|
|
bb0(%0 : $Builtin.Word, %1 : $Int32):
|
|
// create an array
|
|
%2 = alloc_ref [tail_elems $AnyObject * %0 : $Builtin.Word] $DummyArrayStorage<AnyObject>
|
|
%3 = metatype $@thin Array<AnyObject>.Type
|
|
%4 = function_ref @init_any_array_with_buffer : $@convention(thin) (@owned DummyArrayStorage<AnyObject>, Int32, @thin Array<AnyObject>.Type) -> (@owned Array<AnyObject>, UnsafeMutablePointer<AnyObject>)
|
|
%5 = apply %4(%2, %1, %3) : $@convention(thin) (@owned DummyArrayStorage<AnyObject>, Int32, @thin Array<AnyObject>.Type) -> (@owned Array<AnyObject>, UnsafeMutablePointer<AnyObject>)
|
|
%6 = tuple_extract %5 : $(Array<AnyObject>, UnsafeMutablePointer<AnyObject>), 0
|
|
%7 = tuple_extract %5 : $(Array<AnyObject>, UnsafeMutablePointer<AnyObject>), 1
|
|
%8 = struct_extract %7 : $UnsafeMutablePointer<AnyObject>, #UnsafeMutablePointer._rawValue
|
|
%9 = pointer_to_address %8 : $Builtin.RawPointer to [strict] $*AnyObject
|
|
|
|
// store an elt
|
|
%10 = alloc_ref $C
|
|
%11 = init_existential_ref %10 : $C : $C, $AnyObject
|
|
store %11 to %9 : $*AnyObject
|
|
|
|
// extra use of the call
|
|
release_value %5 : $(Array<AnyObject>, UnsafeMutablePointer<AnyObject>) // id: %228
|
|
%13 = tuple ()
|
|
return %13 : $()
|
|
}
|
|
|
|
// =============================================================================
|
|
// testArrayEscapeToBox: test that an array is marked escaping when
|
|
// assigned to a box. When multiple arrays are merged into the same
|
|
// box, ensure that a previous mapping from the project_box address to
|
|
// the box's content is not lost during the merge.
|
|
|
|
class ElementClass {
|
|
init()
|
|
}
|
|
|
|
class StagedContext {
|
|
init()
|
|
}
|
|
|
|
class VFSStagedContext : StagedContext {
|
|
override init()
|
|
}
|
|
|
|
// specialized Array.init()
|
|
sil @$sS2ayxGycfCSo12ElementClassC_Tg5 : $@convention(method) (@thin Array<ElementClass>.Type) -> @owned Array<ElementClass>
|
|
|
|
// specialized Array._getCount()
|
|
sil @$sSa9_getCountSiyFSo12ElementClassC_Tg5 : $@convention(method) (@guaranteed Array<ElementClass>) -> Int
|
|
|
|
// specialized static Array._adoptStorage(_:count:)
|
|
sil shared [_semantics "array.uninitialized"] @$sSa13_adoptStorage_5countSayxG_SpyxGts016_ContiguousArrayB0CyxGn_SitFZSo12ElementClassC_Tg5 : $@convention(method) (@owned _ContiguousArrayStorage<ElementClass>, Int, @thin Array<ElementClass>.Type) -> (@owned Array<ElementClass>, UnsafeMutablePointer<ElementClass>) {
|
|
// %0 // users: %13, %3
|
|
// %1 // users: %9, %4
|
|
bb0(%0 : $_ContiguousArrayStorage<ElementClass>, %1 : $Int, %2 : $@thin Array<ElementClass>.Type):
|
|
%3 = upcast %0 : $_ContiguousArrayStorage<ElementClass> to $__ContiguousArrayStorageBase // users: %17, %11
|
|
%4 = struct_extract %1 : $Int, #Int._value // user: %6
|
|
%5 = integer_literal $Builtin.Int64, 1 // users: %7, %6
|
|
%6 = builtin "shl_Int64"(%4 : $Builtin.Int64, %5 : $Builtin.Int64) : $Builtin.Int64 // user: %7
|
|
%7 = builtin "or_Int64"(%6 : $Builtin.Int64, %5 : $Builtin.Int64) : $Builtin.Int64 // user: %8
|
|
%8 = struct $UInt (%7 : $Builtin.Int64) // user: %9
|
|
%9 = struct $_SwiftArrayBodyStorage (%1 : $Int, %8 : $UInt) // user: %10
|
|
%10 = struct $_ArrayBody (%9 : $_SwiftArrayBodyStorage) // user: %12
|
|
%11 = ref_element_addr %3 : $__ContiguousArrayStorageBase, #__ContiguousArrayStorageBase.countAndCapacity // user: %12
|
|
store %10 to %11 : $*_ArrayBody // id: %12
|
|
%13 = unchecked_ref_cast %0 : $_ContiguousArrayStorage<ElementClass> to $Builtin.BridgeObject // user: %14
|
|
%14 = struct $_BridgeStorage<__ContiguousArrayStorageBase> (%13 : $Builtin.BridgeObject) // user: %15
|
|
%15 = struct $_ArrayBuffer<ElementClass> (%14 : $_BridgeStorage<__ContiguousArrayStorageBase>) // user: %16
|
|
%16 = struct $Array<ElementClass> (%15 : $_ArrayBuffer<ElementClass>) // user: %20
|
|
%17 = ref_tail_addr %3 : $__ContiguousArrayStorageBase, $ElementClass // user: %18
|
|
%18 = address_to_pointer %17 : $*ElementClass to $Builtin.RawPointer // user: %19
|
|
%19 = struct $UnsafeMutablePointer<ElementClass> (%18 : $Builtin.RawPointer) // user: %20
|
|
%20 = tuple (%16 : $Array<ElementClass>, %19 : $UnsafeMutablePointer<ElementClass>) // user: %21
|
|
return %20 : $(Array<ElementClass>, UnsafeMutablePointer<ElementClass>) // id: %21
|
|
}
|
|
|
|
// testArrayUsePointsClosure1
|
|
sil @testArrayUsePointsClosure1 : $@convention(thin) (@guaranteed { var Optional<VFSStagedContext> }, @guaranteed @callee_guaranteed (@guaranteed Optional<StagedContext>, @guaranteed Optional<Error>) -> ()) -> ()
|
|
|
|
// testArrayUsePointsClosure2
|
|
sil @testArrayUsePointsClosure2 : $@convention(thin) (@guaranteed @callee_guaranteed (@guaranteed Optional<StagedContext>, @guaranteed Optional<Error>) -> (), @guaranteed Optional<VFSStagedContext>, @guaranteed { var Array<ElementClass> }) -> ()
|
|
|
|
// Make sure both locally allocated array's are globally escaping.
|
|
//
|
|
// CHECK-LABEL: CG of testArrayEscapeToBox
|
|
// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: (%21)
|
|
// CHECK-NEXT: Val [ref] %1 Esc: , Succ: (%21)
|
|
// CHECK-NEXT: Val [ref] %4 Esc: , Succ: %0, %1
|
|
// CHECK-NEXT: Val [ref] %8 Esc: , Succ: (%21)
|
|
// CHECK-NEXT: Val [ref] %12 Esc: G, Succ: (%21)
|
|
// CHECK-NEXT: Val [ref] %17 Esc: G, Succ: (%21)
|
|
// CHECK-NEXT: Val [ref] %20 Esc: G, Succ: %17
|
|
// CHECK-NEXT: Con [int] %21 Esc: G, Succ: (%39)
|
|
// CHECK-NEXT: Val [ref] %31 Esc: G, Succ: (%21)
|
|
// CHECK-NEXT: Val [ref] %34 Esc: G, Succ: %31
|
|
// CHECK-NEXT: Con %39 Esc: G, Succ: (%21), %12, %20, %34
|
|
// CHECK-NEXT: Val [ref] %45 Esc: , Succ: %0, %8, %39
|
|
// CHECK-NEXT: End
|
|
sil private @testArrayEscapeToBox : $@convention(thin) (@guaranteed @callee_guaranteed (@guaranteed Optional<StagedContext>, @guaranteed Optional<Error>) -> ()) -> () {
|
|
// %0 // users: %54, %51, %47, %45, %5, %4
|
|
bb0(%0 : $@callee_guaranteed (@guaranteed Optional<StagedContext>, @guaranteed Optional<Error>) -> ()):
|
|
%1 = alloc_box ${ var Optional<VFSStagedContext> }, var, name "context" // users: %59, %6, %4, %2
|
|
%2 = project_box %1 : ${ var Optional<VFSStagedContext> }, 0 // user: %44
|
|
// function_ref testArrayUsePointsClosure1
|
|
%3 = function_ref @testArrayUsePointsClosure1 : $@convention(thin) (@guaranteed { var Optional<VFSStagedContext> }, @guaranteed @callee_guaranteed (@guaranteed Optional<StagedContext>, @guaranteed Optional<Error>) -> ()) -> () // user: %4
|
|
%4 = partial_apply [callee_guaranteed] %3(%1, %0) : $@convention(thin) (@guaranteed { var Optional<VFSStagedContext> }, @guaranteed @callee_guaranteed (@guaranteed Optional<StagedContext>, @guaranteed Optional<Error>) -> ()) -> ()
|
|
strong_retain %0 : $@callee_guaranteed (@guaranteed Optional<StagedContext>, @guaranteed Optional<Error>) -> () // id: %5
|
|
strong_retain %1 : ${ var Optional<VFSStagedContext> } // id: %6
|
|
br bb1 // id: %7
|
|
|
|
bb1: // Preds: bb0
|
|
%8 = alloc_box ${ var Array<ElementClass> }, var, name "intents" // users: %58, %56, %52, %48, %45, %9
|
|
%9 = project_box %8 : ${ var Array<ElementClass> }, 0 // users: %41, %36, %27, %22, %13
|
|
%10 = metatype $@thin Array<ElementClass>.Type // users: %33, %19, %12
|
|
// function_ref specialized Array.init()
|
|
%11 = function_ref @$sS2ayxGycfCSo12ElementClassC_Tg5 : $@convention(method) (@thin Array<ElementClass>.Type) -> @owned Array<ElementClass> // user: %12
|
|
%12 = apply %11(%10) : $@convention(method) (@thin Array<ElementClass>.Type) -> @owned Array<ElementClass> // user: %13
|
|
store %12 to %9 : $*Array<ElementClass> // id: %13
|
|
cond_br undef, bb2, bb3 // id: %14
|
|
|
|
bb2: // Preds: bb1
|
|
%15 = integer_literal $Builtin.Int64, 1 // user: %16
|
|
%16 = struct $Int (%15 : $Builtin.Int64) // user: %19
|
|
%17 = alloc_ref [tail_elems $ElementClass * undef : $Builtin.Word] $_ContiguousArrayStorage<ElementClass> // user: %19
|
|
// function_ref specialized static Array._adoptStorage(_:count:)
|
|
%18 = function_ref @$sSa13_adoptStorage_5countSayxG_SpyxGts016_ContiguousArrayB0CyxGn_SitFZSo12ElementClassC_Tg5 : $@convention(method) (@owned _ContiguousArrayStorage<ElementClass>, Int, @thin Array<ElementClass>.Type) -> (@owned Array<ElementClass>, UnsafeMutablePointer<ElementClass>) // user: %19
|
|
%19 = apply %18(%17, %16, %10) : $@convention(method) (@owned _ContiguousArrayStorage<ElementClass>, Int, @thin Array<ElementClass>.Type) -> (@owned Array<ElementClass>, UnsafeMutablePointer<ElementClass>) // users: %21, %20
|
|
%20 = tuple_extract %19 : $(Array<ElementClass>, UnsafeMutablePointer<ElementClass>), 0 // user: %27
|
|
%21 = tuple_extract %19 : $(Array<ElementClass>, UnsafeMutablePointer<ElementClass>), 1
|
|
%22 = struct_element_addr %9 : $*Array<ElementClass>, #Array._buffer // user: %23
|
|
%23 = struct_element_addr %22 : $*_ArrayBuffer<ElementClass>, #_ArrayBuffer._storage // user: %24
|
|
%24 = struct_element_addr %23 : $*_BridgeStorage<__ContiguousArrayStorageBase>, #_BridgeStorage.rawValue // user: %25
|
|
%25 = load %24 : $*Builtin.BridgeObject // user: %26
|
|
strong_release %25 : $Builtin.BridgeObject // id: %26
|
|
store %20 to %9 : $*Array<ElementClass> // id: %27
|
|
br bb4 // id: %28
|
|
|
|
bb3: // Preds: bb1
|
|
%29 = integer_literal $Builtin.Int64, 1 // user: %30
|
|
%30 = struct $Int (%29 : $Builtin.Int64) // user: %33
|
|
%31 = alloc_ref [tail_elems $ElementClass * undef : $Builtin.Word] $_ContiguousArrayStorage<ElementClass> // user: %33
|
|
// function_ref specialized static Array._adoptStorage(_:count:)
|
|
%32 = function_ref @$sSa13_adoptStorage_5countSayxG_SpyxGts016_ContiguousArrayB0CyxGn_SitFZSo12ElementClassC_Tg5 : $@convention(method) (@owned _ContiguousArrayStorage<ElementClass>, Int, @thin Array<ElementClass>.Type) -> (@owned Array<ElementClass>, UnsafeMutablePointer<ElementClass>) // user: %33
|
|
%33 = apply %32(%31, %30, %10) : $@convention(method) (@owned _ContiguousArrayStorage<ElementClass>, Int, @thin Array<ElementClass>.Type) -> (@owned Array<ElementClass>, UnsafeMutablePointer<ElementClass>) // users: %35, %34
|
|
%34 = tuple_extract %33 : $(Array<ElementClass>, UnsafeMutablePointer<ElementClass>), 0 // user: %41
|
|
%35 = tuple_extract %33 : $(Array<ElementClass>, UnsafeMutablePointer<ElementClass>), 1
|
|
%36 = struct_element_addr %9 : $*Array<ElementClass>, #Array._buffer // user: %37
|
|
%37 = struct_element_addr %36 : $*_ArrayBuffer<ElementClass>, #_ArrayBuffer._storage // user: %38
|
|
%38 = struct_element_addr %37 : $*_BridgeStorage<__ContiguousArrayStorageBase>, #_BridgeStorage.rawValue // user: %39
|
|
%39 = load %38 : $*Builtin.BridgeObject // user: %40
|
|
strong_release %39 : $Builtin.BridgeObject // id: %40
|
|
store %34 to %9 : $*Array<ElementClass> // id: %41
|
|
br bb4 // id: %42
|
|
|
|
bb4: // Preds: bb3 bb2
|
|
// function_ref testArrayUsePointsClosure2
|
|
%43 = function_ref @testArrayUsePointsClosure2 : $@convention(thin) (@guaranteed @callee_guaranteed (@guaranteed Optional<StagedContext>, @guaranteed Optional<Error>) -> (), @guaranteed Optional<VFSStagedContext>, @guaranteed { var Array<ElementClass> }) -> () // user: %45
|
|
%44 = load %2 : $*Optional<VFSStagedContext> // users: %55, %53, %49, %45
|
|
%45 = partial_apply [callee_guaranteed] %43(%0, %44, %8) : $@convention(thin) (@guaranteed @callee_guaranteed (@guaranteed Optional<StagedContext>, @guaranteed Optional<Error>) -> (), @guaranteed Optional<VFSStagedContext>, @guaranteed { var Array<ElementClass> }) -> () // user: %57
|
|
// function_ref specialized Array._getCount()
|
|
%46 = function_ref @$sSa9_getCountSiyFSo12ElementClassC_Tg5 : $@convention(method) (@guaranteed Array<ElementClass>) -> Int
|
|
strong_retain %0 : $@callee_guaranteed (@guaranteed Optional<StagedContext>, @guaranteed Optional<Error>) -> () // id: %47
|
|
strong_retain %8 : ${ var Array<ElementClass> } // id: %48
|
|
retain_value %44 : $Optional<VFSStagedContext> // id: %49
|
|
br bb5 // id: %50
|
|
|
|
bb5: // Preds: bb4
|
|
strong_retain %0 : $@callee_guaranteed (@guaranteed Optional<StagedContext>, @guaranteed Optional<Error>) -> () // id: %51
|
|
strong_retain %8 : ${ var Array<ElementClass> } // id: %52
|
|
retain_value %44 : $Optional<VFSStagedContext> // id: %53
|
|
strong_release %0 : $@callee_guaranteed (@guaranteed Optional<StagedContext>, @guaranteed Optional<Error>) -> () // id: %54
|
|
release_value %44 : $Optional<VFSStagedContext> // id: %55
|
|
strong_release %8 : ${ var Array<ElementClass> } // id: %56
|
|
strong_release %45 : $@callee_guaranteed () -> () // id: %57
|
|
strong_release %8 : ${ var Array<ElementClass> } // id: %58
|
|
strong_release %1 : ${ var Optional<VFSStagedContext> } // id: %59
|
|
br bb6 // id: %60
|
|
|
|
bb6: // Preds: bb5
|
|
%61 = tuple () // user: %62
|
|
return %61 : $() // id: %62
|
|
}
|
|
|
|
// =============================================================================
|
|
// Test merging an interior node with a non-interior
|
|
// node. Verification can fail in the time between when the properties
|
|
// are merged and when the edges are merged.
|
|
|
|
struct Poly {
|
|
@_hasStorage var points: Array<Int64> { get set }
|
|
@_hasStorage var length: Int64 { get set }
|
|
init(points: Array<Int64>, length: Int64)
|
|
}
|
|
|
|
// CHECK-LABEL: CG of testMergeInteriorPointsTo
|
|
// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: (%6.2)
|
|
// CHECK-NEXT: Val %1 Esc: , Succ: (%1.1)
|
|
// CHECK-NEXT: Con [ref] %1.1 Esc: , Succ: %0, %12.1
|
|
// CHECK-NEXT: Val %6 Esc: , Succ: (%6.1)
|
|
// CHECK-NEXT: Con [ref] %6.1 Esc: , Succ: (%6.2)
|
|
// CHECK-NEXT: Con [int] %6.2 Esc: G, Succ: (%6.3)
|
|
// CHECK-NEXT: Con %6.3 Esc: G, Succ: (%6.4)
|
|
// CHECK-NEXT: Con [int] %6.4 Esc: G, Succ: (%6.5)
|
|
// CHECK-NEXT: Con %6.5 Esc: G, Succ: (%6.6)
|
|
// CHECK-NEXT: Con %6.6 Esc: G, Succ:
|
|
// CHECK-NEXT: Val %8 Esc: , Succ: (%8.1)
|
|
// CHECK-NEXT: Con [ref] %8.1 Esc: , Succ: %6.1
|
|
// CHECK-NEXT: Val %12 Esc: , Succ: (%12.1)
|
|
// CHECK-NEXT: Con [ref] %12.1 Esc: , Succ: %6.1
|
|
// CHECK-LABEL: End
|
|
sil [serialized] @testMergeInteriorPointsTo : $@convention(method) (@owned Array<Int64>) -> () {
|
|
bb0(%0 : $Array<Int64>):
|
|
%1 = alloc_stack $Poly, var, name "self"
|
|
%2 = struct_element_addr %1 : $*Poly, #Poly.points
|
|
retain_value %0 : $Array<Int64>
|
|
retain_value %0 : $Array<Int64>
|
|
store %0 to %2 : $*Array<Int64>
|
|
%6 = alloc_stack $Int64, var, name "length"
|
|
release_value %0 : $Array<Int64>
|
|
%8 = alloc_stack $Int64
|
|
copy_addr %6 to [initialization] %8 : $*Int64
|
|
destroy_addr %8 : $*Int64
|
|
dealloc_stack %8 : $*Int64
|
|
%12 = alloc_stack $Int64
|
|
copy_addr %6 to [initialization] %12 : $*Int64
|
|
%14 = struct_element_addr %1 : $*Poly, #Poly.length
|
|
copy_addr [take] %12 to [initialization] %14 : $*Int64
|
|
dealloc_stack %12 : $*Int64
|
|
destroy_addr %6 : $*Int64
|
|
dealloc_stack %6 : $*Int64
|
|
dealloc_stack %1 : $*Poly
|
|
%20 = tuple ()
|
|
return %20 : $()
|
|
}
|
|
|
|
// =============================================================================
|
|
// Test merging a callee graph such that when merging pointsTo nodes
|
|
// for defer edges in the caller graph, the new pointsTo node has
|
|
// already been merged and the deferred node has both a defer edge and
|
|
// pointsTo edge to the same node.
|
|
|
|
public protocol ProceduralOp : AnyObject {}
|
|
|
|
public class OptypeBase {
|
|
@_hasStorage var name : String { get set }
|
|
@_hasStorage var id : Int { get set }
|
|
}
|
|
|
|
public class OptypeDependent : OptypeBase {
|
|
@_hasStorage var _enable : Bool { get set }
|
|
@_hasStorage var _ops : Array<ProceduralOp>
|
|
}
|
|
|
|
public class CombineAnyOp : OptypeDependent {
|
|
@_hasStorage var _sources : Array<(ProceduralOp, Bool)>
|
|
}
|
|
|
|
sil_global @_swiftEmptyArrayStorage : $_SwiftEmptyArrayStorage
|
|
|
|
sil @$sSa28_allocateBufferUninitialized15minimumCapacitys06_ArrayB0VyxGSi_tFZ : $@convention(method) <τ_0_0> (Int, @thin Array<τ_0_0>.Type) -> @owned _ArrayBuffer<τ_0_0>
|
|
|
|
// specialized static Array._allocateUninitialized(_:)
|
|
sil shared [_semantics "array.uninitialized"] @$sSa22_allocateUninitializedySayxG_SpyxGtSiFZ12ProceduralOp_p_Tg5 : $@convention(method) (Int, @thin Array<ProceduralOp>.Type) -> (@owned Array<ProceduralOp>, UnsafeMutablePointer<ProceduralOp>) {
|
|
// %0
|
|
bb0(%0 : $Int, %1 : $@thin Array<ProceduralOp>.Type):
|
|
%2 = global_addr @_swiftEmptyArrayStorage : $*_SwiftEmptyArrayStorage
|
|
%3 = address_to_pointer %2 : $*_SwiftEmptyArrayStorage to $Builtin.RawPointer
|
|
%4 = raw_pointer_to_ref %3 : $Builtin.RawPointer to $__EmptyArrayStorage
|
|
%5 = unchecked_ref_cast %4 : $__EmptyArrayStorage to $Builtin.BridgeObject
|
|
%6 = integer_literal $Builtin.Int64, 0
|
|
%7 = struct_extract %0 : $Int, #Int._value
|
|
%8 = builtin "cmp_slt_Int64"(%6 : $Builtin.Int64, %7 : $Builtin.Int64) : $Builtin.Int1
|
|
cond_br %8, bb1, bb2
|
|
|
|
bb1:
|
|
%10 = metatype $@thin Array<ProceduralOp>.Type
|
|
// function_ref static Array._allocateBufferUninitialized(minimumCapacity:)
|
|
%11 = function_ref @$sSa28_allocateBufferUninitialized15minimumCapacitys06_ArrayB0VyxGSi_tFZ : $@convention(method) <τ_0_0> (Int, @thin Array<τ_0_0>.Type) -> @owned _ArrayBuffer<τ_0_0>
|
|
%12 = apply %11<ProceduralOp>(%0, %10) : $@convention(method) <τ_0_0> (Int, @thin Array<τ_0_0>.Type) -> @owned _ArrayBuffer<τ_0_0>
|
|
%13 = struct_extract %12 : $_ArrayBuffer<ProceduralOp>, #_ArrayBuffer._storage
|
|
%14 = struct_extract %13 : $_BridgeStorage<__ContiguousArrayStorageBase>, #_BridgeStorage.rawValue
|
|
%15 = unchecked_ref_cast %14 : $Builtin.BridgeObject to $__ContiguousArrayStorageBase
|
|
%16 = ref_element_addr %15 : $__ContiguousArrayStorageBase, #__ContiguousArrayStorageBase.countAndCapacity
|
|
%17 = struct_element_addr %16 : $*_ArrayBody, #_ArrayBody._storage
|
|
%18 = struct_element_addr %17 : $*_SwiftArrayBodyStorage, #_SwiftArrayBodyStorage.count
|
|
store %0 to %18 : $*Int
|
|
br bb3(%14 : $Builtin.BridgeObject)
|
|
|
|
bb2:
|
|
strong_retain %4 : $__EmptyArrayStorage
|
|
br bb3(%5 : $Builtin.BridgeObject)
|
|
|
|
// %23
|
|
bb3(%23 : $Builtin.BridgeObject):
|
|
%24 = struct $_BridgeStorage<__ContiguousArrayStorageBase> (%23 : $Builtin.BridgeObject)
|
|
%25 = struct $_ArrayBuffer<ProceduralOp> (%24 : $_BridgeStorage<__ContiguousArrayStorageBase>)
|
|
%26 = struct $Array<ProceduralOp> (%25 : $_ArrayBuffer<ProceduralOp>)
|
|
%27 = unchecked_ref_cast %23 : $Builtin.BridgeObject to $__ContiguousArrayStorageBase
|
|
%28 = ref_tail_addr %27 : $__ContiguousArrayStorageBase, $ProceduralOp
|
|
%29 = address_to_pointer %28 : $*ProceduralOp to $Builtin.RawPointer
|
|
%30 = struct $UnsafeMutablePointer<ProceduralOp> (%29 : $Builtin.RawPointer)
|
|
%31 = tuple (%26 : $Array<ProceduralOp>, %30 : $UnsafeMutablePointer<ProceduralOp>)
|
|
return %31 : $(Array<ProceduralOp>, UnsafeMutablePointer<ProceduralOp>)
|
|
}
|
|
|
|
sil shared [_semantics "array.uninitialized"] @$sSa22_allocateUninitializedySayxG_SpyxGtSiFZ12ProceduralOp_p_Sbt_Tg5 : $@convention(method) (Int, @thin Array<(ProceduralOp, Bool)>.Type) -> (@owned Array<(ProceduralOp, Bool)>, UnsafeMutablePointer<(ProceduralOp, Bool)>) {
|
|
// %0
|
|
bb0(%0 : $Int, %1 : $@thin Array<(ProceduralOp, Bool)>.Type):
|
|
%2 = global_addr @_swiftEmptyArrayStorage : $*_SwiftEmptyArrayStorage
|
|
%3 = address_to_pointer %2 : $*_SwiftEmptyArrayStorage to $Builtin.RawPointer
|
|
%4 = raw_pointer_to_ref %3 : $Builtin.RawPointer to $__EmptyArrayStorage
|
|
%5 = unchecked_ref_cast %4 : $__EmptyArrayStorage to $Builtin.BridgeObject
|
|
%6 = integer_literal $Builtin.Int64, 0
|
|
%7 = struct_extract %0 : $Int, #Int._value
|
|
%8 = builtin "cmp_slt_Int64"(%6 : $Builtin.Int64, %7 : $Builtin.Int64) : $Builtin.Int1
|
|
cond_br %8, bb1, bb2
|
|
|
|
bb1:
|
|
%10 = metatype $@thin Array<(ProceduralOp, Bool)>.Type
|
|
// function_ref static Array._allocateBufferUninitialized(minimumCapacity:)
|
|
%11 = function_ref @$sSa28_allocateBufferUninitialized15minimumCapacitys06_ArrayB0VyxGSi_tFZ : $@convention(method) <τ_0_0> (Int, @thin Array<τ_0_0>.Type) -> @owned _ArrayBuffer<τ_0_0>
|
|
%12 = apply %11<(ProceduralOp, Bool)>(%0, %10) : $@convention(method) <τ_0_0> (Int, @thin Array<τ_0_0>.Type) -> @owned _ArrayBuffer<τ_0_0>
|
|
%13 = struct_extract %12 : $_ArrayBuffer<(ProceduralOp, Bool)>, #_ArrayBuffer._storage
|
|
%14 = struct_extract %13 : $_BridgeStorage<__ContiguousArrayStorageBase>, #_BridgeStorage.rawValue
|
|
%15 = unchecked_ref_cast %14 : $Builtin.BridgeObject to $__ContiguousArrayStorageBase
|
|
%16 = ref_element_addr %15 : $__ContiguousArrayStorageBase, #__ContiguousArrayStorageBase.countAndCapacity
|
|
%17 = struct_element_addr %16 : $*_ArrayBody, #_ArrayBody._storage
|
|
%18 = struct_element_addr %17 : $*_SwiftArrayBodyStorage, #_SwiftArrayBodyStorage.count
|
|
store %0 to %18 : $*Int
|
|
br bb3(%14 : $Builtin.BridgeObject)
|
|
|
|
bb2:
|
|
strong_retain %4 : $__EmptyArrayStorage
|
|
br bb3(%5 : $Builtin.BridgeObject)
|
|
|
|
// %23
|
|
bb3(%23 : $Builtin.BridgeObject):
|
|
%24 = struct $_BridgeStorage<__ContiguousArrayStorageBase> (%23 : $Builtin.BridgeObject)
|
|
%25 = struct $_ArrayBuffer<(ProceduralOp, Bool)> (%24 : $_BridgeStorage<__ContiguousArrayStorageBase>)
|
|
%26 = struct $Array<(ProceduralOp, Bool)> (%25 : $_ArrayBuffer<(ProceduralOp, Bool)>)
|
|
%27 = unchecked_ref_cast %23 : $Builtin.BridgeObject to $__ContiguousArrayStorageBase
|
|
%28 = ref_tail_addr %27 : $__ContiguousArrayStorageBase, $(ProceduralOp, Bool)
|
|
%29 = address_to_pointer %28 : $*(ProceduralOp, Bool) to $Builtin.RawPointer
|
|
%30 = struct $UnsafeMutablePointer<(ProceduralOp, Bool)> (%29 : $Builtin.RawPointer)
|
|
%31 = tuple (%26 : $Array<(ProceduralOp, Bool)>, %30 : $UnsafeMutablePointer<(ProceduralOp, Bool)>)
|
|
return %31 : $(Array<(ProceduralOp, Bool)>, UnsafeMutablePointer<(ProceduralOp, Bool)>)
|
|
}
|
|
|
|
sil @testMergePointsTo_OptypeDependent_init : $@convention(method) (@owned String, @owned OptypeDependent) -> @owned OptypeDependent {
|
|
bb0(%0 : $String, %1 : $OptypeDependent):
|
|
%3 = integer_literal $Builtin.Int64, 0
|
|
%4 = struct $Int (%3 : $Builtin.Int64)
|
|
%5 = metatype $@thin Array<ProceduralOp>.Type
|
|
// function_ref specialized static Array._allocateUninitialized(_:)
|
|
%6 = function_ref @$sSa22_allocateUninitializedySayxG_SpyxGtSiFZ12ProceduralOp_p_Tg5 : $@convention(method) (Int, @thin Array<ProceduralOp>.Type) -> (@owned Array<ProceduralOp>, UnsafeMutablePointer<ProceduralOp>)
|
|
strong_retain %1 : $OptypeDependent // id: %7
|
|
%8 = apply %6(%4, %5) : $@convention(method) (Int, @thin Array<ProceduralOp>.Type) -> (@owned Array<ProceduralOp>, UnsafeMutablePointer<ProceduralOp>)
|
|
%9 = tuple_extract %8 : $(Array<ProceduralOp>, UnsafeMutablePointer<ProceduralOp>), 0
|
|
%10 = ref_element_addr %1 : $OptypeDependent, #OptypeDependent._ops
|
|
store %9 to %10 : $*Array<ProceduralOp>
|
|
%12 = integer_literal $Builtin.Int1, -1
|
|
%13 = struct $Bool (%12 : $Builtin.Int1)
|
|
%14 = ref_element_addr %1 : $OptypeDependent, #OptypeDependent._enable
|
|
store %13 to %14 : $*Bool
|
|
strong_release %1 : $OptypeDependent
|
|
%17 = upcast %1 : $OptypeDependent to $OptypeBase
|
|
%19 = ref_element_addr %17 : $OptypeBase, #OptypeBase.name
|
|
store %0 to %19 : $*String
|
|
return %1 : $OptypeDependent
|
|
}
|
|
|
|
// CHECK-LABEL: CG of testMergePointsTo_CombineAnyOp_init
|
|
// CHECK-NEXT: Arg [ref] %0 Esc: A, Succ: (%7.1)
|
|
// CHECK-NEXT: Arg [ref] %1 Esc: A, Succ: (%9)
|
|
// CHECK-NEXT: Val %7 Esc: A, Succ: (%7.1), %7.1
|
|
// CHECK-NEXT: Con [int] %7.1 Esc: G, Succ: (%7.1)
|
|
// CHECK-NEXT: Con [int] %9 Esc: A, Succ: (%9.1)
|
|
// CHECK-NEXT: Con [ref] %9.1 Esc: A, Succ: %0, %7, %7.1
|
|
// CHECK-NEXT: Val [ref] %18 Esc: , Succ: (%9), %1
|
|
// CHECK-NEXT: Ret [ref] return Esc: , Succ: %18
|
|
// CHECK-LABEL: End
|
|
sil @testMergePointsTo_CombineAnyOp_init : $@convention(method) (@owned String, @owned CombineAnyOp) -> @owned CombineAnyOp {
|
|
bb0(%0 : $String, %1 : $CombineAnyOp):
|
|
strong_retain %1 : $CombineAnyOp
|
|
%4 = integer_literal $Builtin.Int64, 0
|
|
%5 = struct $Int (%4 : $Builtin.Int64)
|
|
%6 = metatype $@thin Array<(ProceduralOp, Bool)>.Type
|
|
// function_ref specialized static Array._allocateUninitialized(_:)
|
|
%7 = function_ref @$sSa22_allocateUninitializedySayxG_SpyxGtSiFZ12ProceduralOp_p_Sbt_Tg5 : $@convention(method) (Int, @thin Array<(ProceduralOp, Bool)>.Type) -> (@owned Array<(ProceduralOp, Bool)>, UnsafeMutablePointer<(ProceduralOp, Bool)>)
|
|
%8 = apply %7(%5, %6) : $@convention(method) (Int, @thin Array<(ProceduralOp, Bool)>.Type) -> (@owned Array<(ProceduralOp, Bool)>, UnsafeMutablePointer<(ProceduralOp, Bool)>)
|
|
%9 = tuple_extract %8 : $(Array<(ProceduralOp, Bool)>, UnsafeMutablePointer<(ProceduralOp, Bool)>), 0
|
|
%10 = ref_element_addr %1 : $CombineAnyOp, #CombineAnyOp._sources
|
|
store %9 to %10 : $*Array<(ProceduralOp, Bool)>
|
|
strong_release %1 : $CombineAnyOp
|
|
%13 = struct_extract %0 : $String, #String._guts
|
|
%14 = struct_extract %13 : $_StringGuts, #_StringGuts._object
|
|
%15 = struct_extract %14 : $_StringObject, #_StringObject._object
|
|
strong_retain %15 : $Builtin.BridgeObject
|
|
%18 = upcast %1 : $CombineAnyOp to $OptypeDependent
|
|
%19 = function_ref @testMergePointsTo_OptypeDependent_init : $@convention(method) (@owned String, @owned OptypeDependent) -> @owned OptypeDependent
|
|
%20 = apply %19(%0, %18) : $@convention(method) (@owned String, @owned OptypeDependent) -> @owned OptypeDependent
|
|
%21 = unchecked_ref_cast %20 : $OptypeDependent to $CombineAnyOp
|
|
strong_release %15 : $Builtin.BridgeObject
|
|
return %21 : $CombineAnyOp
|
|
}
|
|
|
|
// =============================================================================
|
|
// Test calling ConnectionGraph::initializePointsTo from
|
|
// mergeAllScheduledNodes when multiple merges are pending. In this
|
|
// case, initializePointsTo is called and a node in the
|
|
// to-be-initialized defer web already pointsTo a node that has been
|
|
// marked for merging.
|
|
|
|
public protocol ASTNode {}
|
|
|
|
public struct AssignmentNode : ASTNode {
|
|
@_hasStorage public let variable: ASTNode { get }
|
|
@_hasStorage public let value: ASTNode { get }
|
|
@_hasStorage public let range: Range<Int>? { get }
|
|
@_hasStorage public let documentation: String? { get }
|
|
public init(variable: ASTNode, value: ASTNode, range: Range<Int>?, documentation: String?) throws
|
|
public var childNodes: [ASTNode] { get }
|
|
}
|
|
|
|
public struct VariableNode : ASTNode {
|
|
@_hasStorage public let name: String { get }
|
|
@_hasStorage public let range: Range<Int>? { get }
|
|
public init(name: String, range: Range<Int>?)
|
|
public var childNodes: [ASTNode] { get }
|
|
public var description: String { get }
|
|
public var nodeDescription: String? { get }
|
|
}
|
|
|
|
// CHECK-LABEL: CG of testPendingMergeHelper
|
|
// CHECK-NEXT: Arg %0 Esc: A, Succ: (%0.1)
|
|
// CHECK-NEXT: Con %0.1 Esc: A, Succ: %3.1
|
|
// CHECK-NEXT: Arg %1 Esc: A, Succ: (%1.1)
|
|
// CHECK-NEXT: Con %1.1 Esc: A, Succ: (%2.1)
|
|
// CHECK-NEXT: Arg [ref] %2 Esc: A, Succ: (%2.1)
|
|
// CHECK-NEXT: Con %2.1 Esc: G, Succ:
|
|
// CHECK-NEXT: Val %3 Esc: , Succ: (%3.1)
|
|
// CHECK-NEXT: Con %3.1 Esc: A, Succ: %1.1, %2
|
|
// CHECK-LABEL: End
|
|
sil @testPendingMergeHelper : $@convention(method) (@in ASTNode, @owned Optional<String>) -> (@out AssignmentNode, @error Error) {
|
|
bb0(%0 : $*AssignmentNode, %1 : $*ASTNode, %4 : $Optional<String>):
|
|
%6 = alloc_stack $AssignmentNode, var, name "self"
|
|
cond_br undef, bb1, bb2
|
|
|
|
bb1:
|
|
destroy_addr %1 : $*ASTNode
|
|
dealloc_stack %6 : $*AssignmentNode
|
|
throw undef : $Error
|
|
|
|
bb2:
|
|
%160 = struct_element_addr %6 : $*AssignmentNode, #AssignmentNode.variable
|
|
copy_addr [take] %1 to [initialization] %160 : $*ASTNode
|
|
%162 = struct_element_addr %6 : $*AssignmentNode, #AssignmentNode.value
|
|
%164 = struct_element_addr %6 : $*AssignmentNode, #AssignmentNode.range
|
|
%166 = struct_element_addr %6 : $*AssignmentNode, #AssignmentNode.documentation
|
|
store %4 to %166 : $*Optional<String>
|
|
copy_addr [take] %6 to [initialization] %0 : $*AssignmentNode
|
|
dealloc_stack %6 : $*AssignmentNode
|
|
%171 = tuple ()
|
|
return %171 : $()
|
|
}
|
|
|
|
// CHECK-LABEL: CG of testPendingMerge
|
|
// CHECK-NEXT: Arg %0 Esc: A, Succ: (%0.1)
|
|
// CHECK-NEXT: Con %0.1 Esc: A, Succ: %1, %4.1
|
|
// CHECK-NEXT: Arg [ref] %1 Esc: A, Succ: (%9.1)
|
|
// CHECK-NEXT: Val %2 Esc: , Succ: (%9)
|
|
// CHECK-NEXT: Val %4 Esc: , Succ: (%4.1)
|
|
// CHECK-NEXT: Con %4.1 Esc: A, Succ: (%9.1), %5.1, %9
|
|
// CHECK-NEXT: Val %5 Esc: , Succ: (%5.1)
|
|
// CHECK-NEXT: Con %5.1 Esc: A, Succ: %1
|
|
// CHECK-NEXT: Con [ref] %9 Esc: A, Succ: (%9.1)
|
|
// CHECK-NEXT: Con [int] %9.1 Esc: G, Succ: (%9.2)
|
|
// CHECK-NEXT: Con %9.2 Esc: G, Succ: (%9.3)
|
|
// CHECK-NEXT: Con %9.3 Esc: G, Succ:
|
|
// CHECK-LABEL: End
|
|
sil private @testPendingMerge : $@convention(thin) (@owned VariableNode) -> (@out ASTNode, @error Error) {
|
|
bb0(%0 : $*ASTNode, %1 : $VariableNode):
|
|
%9 = alloc_stack $Optional<String>, var, name "documentation"
|
|
cond_br undef, bb12, bb14
|
|
|
|
bb12:
|
|
%113 = alloc_stack $AssignmentNode, let, name "assign"
|
|
%115 = alloc_stack $ASTNode
|
|
retain_value %1 : $VariableNode
|
|
%117 = init_existential_addr %115 : $*ASTNode, $VariableNode
|
|
store %1 to %117 : $*VariableNode
|
|
%122 = load %9 : $*Optional<String>
|
|
|
|
%123 = function_ref @testPendingMergeHelper : $@convention(method) (@in ASTNode, @owned Optional<String>) -> (@out AssignmentNode, @error Error)
|
|
try_apply %123(%113, %115, %122) : $@convention(method) (@in ASTNode, @owned Optional<String>) -> (@out AssignmentNode, @error Error), normal bb13, error bb65
|
|
|
|
bb13(%125 : $()):
|
|
dealloc_stack %115 : $*ASTNode
|
|
%128 = init_existential_addr %0 : $*ASTNode, $AssignmentNode
|
|
copy_addr %113 to [initialization] %128 : $*AssignmentNode
|
|
destroy_addr %113 : $*AssignmentNode
|
|
dealloc_stack %113 : $*AssignmentNode
|
|
release_value %1 : $VariableNode
|
|
dealloc_stack %9 : $*Optional<String>
|
|
br bb61
|
|
|
|
bb14:
|
|
destroy_addr %9 : $*Optional<String>
|
|
%584 = init_existential_addr %0 : $*ASTNode, $VariableNode
|
|
store %1 to %584 : $*VariableNode
|
|
dealloc_stack %9 : $*Optional<String>
|
|
br bb61
|
|
|
|
bb61:
|
|
%589 = tuple ()
|
|
return %589 : $()
|
|
|
|
|
|
bb65(%614 : $Error):
|
|
dealloc_stack %115 : $*ASTNode
|
|
dealloc_stack %113 : $*AssignmentNode
|
|
dealloc_stack %9 : $*Optional<String>
|
|
br bb72(undef : $Error)
|
|
|
|
|
|
bb72(%681 : $Error):
|
|
throw %681 : $Error
|
|
}
|
|
|
|
//=============================================================================
|
|
// Test an "array.uninitialized" with multiple tuple_extract's but no
|
|
// other unusual uses. Make sure canOptimizeArrayUninitializedCall
|
|
// returns false; otherwise graph verification can fail because only
|
|
// one of the tuple_extracts is mapped to a node.
|
|
|
|
// specialized static Array._adoptStorage(_:count:)
|
|
sil [_semantics "array.uninitialized"] @$sSa13_adoptStorage_5countSayxG_SpyxGts016_ContiguousArrayB0CyxGn_SitFZSo5Int64V_Tg5 : $@convention(method) (@owned _ContiguousArrayStorage<Int64>, Int, @thin Array<Int64>.Type) -> (@owned Array<Int64>, UnsafeMutablePointer<Int64>)
|
|
|
|
// CHECK-LABEL: CG of testArrayUninitResultMapping
|
|
// CHECK-NEXT: [ref] %0 Esc: , Succ: (%0.1)
|
|
// CHECK-NEXT: Con [int] %0.1 Esc: G, Succ: (%0.2)
|
|
// CHECK-NEXT: Con [ref] %0.2 Esc: G, Succ:
|
|
// CHECK-NEXT: Val %2 Esc: , Succ: (%2.1)
|
|
// CHECK-NEXT: Con %2.1 Esc: G, Succ:
|
|
// CHECK-LABEL: End
|
|
sil hidden @testArrayUninitResultMapping : $@convention(thin) () -> () {
|
|
bb0:
|
|
%0 = alloc_ref [tail_elems $Int64 * undef : $Builtin.Word] $_ContiguousArrayStorage<Int64>
|
|
// function_ref specialized static Array._adoptStorage(_:count:)
|
|
%1 = function_ref @$sSa13_adoptStorage_5countSayxG_SpyxGts016_ContiguousArrayB0CyxGn_SitFZSo5Int64V_Tg5 : $@convention(method) (@owned _ContiguousArrayStorage<Int64>, Int, @thin Array<Int64>.Type) -> (@owned Array<Int64>, UnsafeMutablePointer<Int64>)
|
|
%2 = apply %1(%0, undef, undef) : $@convention(method) (@owned _ContiguousArrayStorage<Int64>, Int, @thin Array<Int64>.Type) -> (@owned Array<Int64>, UnsafeMutablePointer<Int64>)
|
|
%3 = tuple_extract %2 : $(Array<Int64>, UnsafeMutablePointer<Int64>), 0
|
|
%4 = tuple_extract %2 : $(Array<Int64>, UnsafeMutablePointer<Int64>), 1
|
|
%5 = tuple_extract %2 : $(Array<Int64>, UnsafeMutablePointer<Int64>), 0
|
|
%6 = struct_extract %5 : $Array<Int64>, #Array._buffer
|
|
%7 = struct_extract %6 : $_ArrayBuffer<Int64>, #_ArrayBuffer._storage
|
|
%8 = struct_extract %7 : $_BridgeStorage<__ContiguousArrayStorageBase>, #_BridgeStorage.rawValue
|
|
strong_retain %8 : $Builtin.BridgeObject
|
|
%10 = tuple ()
|
|
return %10 : $()
|
|
}
|