mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
AccessPath was treating init_enum_data_addr as an address base, which is not ideal. It should be able to identify the underlying enum object as the base. This issue was caught by LoadBorrowImmutabilityChecker during SIL verification. Instead handle init_enum_data_addr as a access projection that does not affect the access path. I expect this SIL pattern to disappear with SIL opaque values, but it still needs to be handled properly after lowering addresses. Functionality changes: - any user of AccessPath now sees enum initialization stores as writes to the underlying enum object - SILGen now generates begin/end access markers for enum initialization patterns. (Originally, we did not "see through" init_enum_data_addr because we didn't want to generate these markers, but that behavior was inconsistent and problematic). Fixes rdar://70725514 fatal error encountered during compilation; Unknown instruction: init_enum_data_addr)
920 lines
35 KiB
Plaintext
920 lines
35 KiB
Plaintext
// RUN: %target-sil-opt %s -accessed-storage-dump -enable-accessed-storage-dump-uses -enable-sil-verify-all -o /dev/null | %FileCheck %s
|
|
// RUN: %target-sil-opt %s -access-path-verification -o /dev/null
|
|
|
|
// REQUIRES: PTRSIZE=64
|
|
|
|
sil_stage canonical
|
|
|
|
import Builtin
|
|
import Swift
|
|
import SwiftShims
|
|
|
|
struct MyStruct {
|
|
@_hasStorage @_hasInitialValue var i: Int64 { get set }
|
|
@_hasStorage @_hasInitialValue var j: Int64 { get set }
|
|
}
|
|
|
|
// CHECK-LABEL: @testInnerUse
|
|
// CHECK: ###For MemOp: %{{.*}} = load [trivial] %{{.*}} : $*MyStruct
|
|
// CHECK: Argument %0 = argument of bb0 : $Builtin.RawPointer
|
|
// CHECK: Storage: Argument %0 = argument of bb0 : $Builtin.RawPointer
|
|
// CHECK: Path: ()
|
|
// CHECK: Exact Uses {
|
|
// CHECK-NEXT: %{{.*}} = load [trivial] %{{.*}} : $*MyStruct
|
|
// CHECK-NEXT: Path: ()
|
|
// CHECK-NEXT: }
|
|
// CHECK: Inner Uses {
|
|
// CHECK-NEXT: %{{.*}} = load [trivial] %{{.*}} : $*MyStruct
|
|
// CHECK-NEXT: Path: ()
|
|
// CHECK-NEXT: %{{.*}} = load [trivial] %{{.*}} : $*Int64
|
|
// CHECK-NEXT: Path: (#0)
|
|
// CHECK-NEXT: }
|
|
// CHECK: Overlapping Uses {
|
|
// CHECK-NEXT: %{{.*}} = load [trivial] %{{.*}} : $*MyStruct
|
|
// CHECK-NEXT: Path: ()
|
|
// CHECK-NEXT: %{{.*}} = load [trivial] %{{.*}} : $*Int64
|
|
// CHECK-NEXT: Path: (#0)
|
|
// CHECK: }
|
|
// CHECK: ###For MemOp: %{{.*}} = load [trivial] %{{.*}} : $*Int64
|
|
// CHECK: Argument %0 = argument of bb0 : $Builtin.RawPointer
|
|
// CHECK: Storage: Argument %0 = argument of bb0 : $Builtin.RawPointer
|
|
// CHECK: Path: (#0)
|
|
// CHECK: Exact Uses {
|
|
// CHECK-NEXT: %{{.*}} = load [trivial] %{{.*}} : $*Int64
|
|
// CHECK-NEXT: Path: (#0)
|
|
// CHECK-NEXT: }
|
|
// CHECK: Inner Uses {
|
|
// CHECK-NEXT: %{{.*}} = load [trivial] %{{.*}} : $*Int64
|
|
// CHECK-NEXT: Path: (#0)
|
|
// CHECK-NEXT: }
|
|
// CHECK: Overlapping Uses {
|
|
// CHECK-NEXT: %{{.*}} = load [trivial] %{{.*}} : $*MyStruct
|
|
// CHECK-NEXT: Path: ()
|
|
// CHECK-NEXT: %{{.*}} = load [trivial] %{{.*}} : $*Int64
|
|
// CHECK-NEXT: Path: (#0)
|
|
// CHECK: }
|
|
sil [serialized] [ossa] @testInnerUse : $@convention(thin) (Builtin.RawPointer, Builtin.Word) -> () {
|
|
bb0(%0 : $Builtin.RawPointer, %1 : $Builtin.Word):
|
|
%2 = pointer_to_address %0 : $Builtin.RawPointer to [strict] $*MyStruct
|
|
%3 = load [trivial] %2 : $*MyStruct
|
|
%4 = struct_element_addr %2 : $*MyStruct, #MyStruct.i
|
|
%5 = load [trivial] %4 : $*Int64
|
|
%6 = tuple ()
|
|
return %6 : $()
|
|
}
|
|
|
|
// unknown offset contains subobject indices
|
|
// CHECK-LABEL: @testDynamicIndexWithSubObject
|
|
// CHECK: ###For MemOp: %{{.*}} = load [trivial] %{{.*}} : $*Int64
|
|
// CHECK: Path: (#0)
|
|
// CHECK: Exact Uses {
|
|
// CHECK-NEXT: %{{.*}} = load [trivial] %{{.*}} : $*Int64
|
|
// CHECK-NEXT: Path: (#0)
|
|
// CHECK-NEXT: }
|
|
// CHECK: Inner Uses {
|
|
// CHECK-NEXT: %{{.*}} = load [trivial] %{{.*}} : $*Int64
|
|
// CHECK-NEXT: Path: (#0)
|
|
// CHECK-NEXT: }
|
|
// CHECK: Overlapping Uses {
|
|
// CHECK-NEXT: %{{.*}} = load [trivial] %{{.*}} : $*Int64
|
|
// CHECK-NEXT: Path: (#0)
|
|
// CHECK-NEXT: %{{.*}} = load [trivial] %{{.*}} : $*MyStruct
|
|
// CHECK-NEXT: Path: (@Unknown)
|
|
// CHECK-NEXT: }
|
|
// CHECK: ###For MemOp: %{{.*}} = load [trivial] %{{.*}} : $*MyStruct
|
|
// CHECK: Path: (@Unknown)
|
|
// CHECK-NEXT: Exact Uses {
|
|
// CHECK-NEXT: }
|
|
// CHECK-NEXT: Inner Uses {
|
|
// CHECK-NEXT: }
|
|
// CHECK-NEXT: Overlapping Uses {
|
|
// CHECK-NEXT: %{{.*}} = load [trivial] %3 : $*Int64
|
|
// CHECK-NEXT: Path: (#0)
|
|
// CHECK-NEXT: %{{.*}} = load [trivial] %{{.*}} : $*MyStruct
|
|
// CHECK-NEXT: Path: (@Unknown)
|
|
// CHECK-NEXT: }
|
|
sil [serialized] [ossa] @testDynamicIndexWithSubObject : $@convention(thin) (Builtin.RawPointer, Builtin.Word) -> () {
|
|
bb0(%0 : $Builtin.RawPointer, %1 : $Builtin.Word):
|
|
%2 = pointer_to_address %0 : $Builtin.RawPointer to [strict] $*MyStruct
|
|
%3 = struct_element_addr %2 : $*MyStruct, #MyStruct.i
|
|
%4 = load [trivial] %3 : $*Int64
|
|
%5 = index_addr %2 : $*MyStruct, %1 : $Builtin.Word
|
|
%6 = load [trivial] %5 : $*MyStruct
|
|
%7 = tuple ()
|
|
return %7 : $()
|
|
}
|
|
|
|
// The index load should be reported as an overlapping uses, not an
|
|
// exact or inner use.
|
|
// CHECK: ###For MemOp: %3 = load [trivial] %2 : $*MyStruct
|
|
// CHECK: Argument %0 = argument of bb0 : $Builtin.RawPointer
|
|
// CHECK: Base: %0 = argument of bb0 : $Builtin.RawPointer
|
|
// CHECK: Storage: Argument %0 = argument of bb0 : $Builtin.RawPointer
|
|
// CHECK: Path: ()
|
|
// CHECK: Exact Uses {
|
|
// CHECK-NEXT: %{{.*}} = load [trivial] %{{.*}} : $*MyStruct
|
|
// CHECK-NEXT: Path: ()
|
|
// CHECK-NEXT: }
|
|
// CHECK-NEXT: Inner Uses {
|
|
// CHECK-NEXT: %{{.*}} = load [trivial] %{{.*}} : $*MyStruct
|
|
// CHECK-NEXT: Path: ()
|
|
// CHECK-NEXT: }
|
|
// CHECK-NEXT: Overlapping Uses {
|
|
// CHECK-NEXT: %{{.*}} = load [trivial] %{{.*}} : $*MyStruct
|
|
// CHECK-NEXT: Path: ()
|
|
// CHECK-NEXT: %{{.*}} = load [trivial] %{{.*}} : $*Int64
|
|
// CHECK-NEXT: Path: INVALID
|
|
// CHECK-NEXT: }
|
|
// CHECK: ###For MemOp: %{{.*}} = load [trivial] %{{.*}} : $*Int64
|
|
// CHECK: Argument %0 = argument of bb0 : $Builtin.RawPointer
|
|
// CHECK: INVALID
|
|
sil [serialized] [ossa] @testDynamicIndexInsideSubObject : $@convention(thin) (Builtin.RawPointer, Builtin.Word) -> () {
|
|
bb0(%0 : $Builtin.RawPointer, %1 : $Builtin.Word):
|
|
%2 = pointer_to_address %0 : $Builtin.RawPointer to [strict] $*MyStruct
|
|
%3 = load [trivial] %2 : $*MyStruct
|
|
%4 = struct_element_addr %2 : $*MyStruct, #MyStruct.i
|
|
%5 = index_addr %4 : $*Int64, %1 : $Builtin.Word
|
|
%6 = load [trivial] %5 : $*Int64
|
|
%7 = tuple ()
|
|
return %7 : $()
|
|
}
|
|
|
|
// An unknown offset contains known offsets.
|
|
// CHECK-LABEL: @testDynamicIndexWithStaticIndex
|
|
// CHECK: ###For MemOp: %{{.*}} = load [trivial] %{{.*}} : $*MyStruct
|
|
// CHECK: Path: (@1)
|
|
// CHECK: Exact Uses {
|
|
// CHECK-NEXT: %{{.*}} = load [trivial] %{{.*}} : $*MyStruct
|
|
// CHECK-NEXT: Path: (@1)
|
|
// CHECK-NEXT: }
|
|
// CHECK: Inner Uses {
|
|
// CHECK-NEXT: %{{.*}} = load [trivial] %{{.*}} : $*MyStruct
|
|
// CHECK-NEXT: Path: (@1)
|
|
// CHECK-NEXT: }
|
|
// CHECK: Overlapping Uses {
|
|
// CHECK-NEXT: %{{.*}} = load [trivial] %{{.*}} : $*MyStruct
|
|
// CHECK-NEXT: Path: (@1)
|
|
// CHECK-NEXT: %{{.*}} = load [trivial] %{{.*}} : $*MyStruct
|
|
// CHECK-NEXT: Path: (@Unknown)
|
|
// CHECK-NEXT: }
|
|
// CHECK: ###For MemOp: %{{.*}} = load [trivial] %{{.*}} : $*MyStruct
|
|
// CHECK: Path: (@Unknown)
|
|
// CHECK: Exact Uses {
|
|
// CHECK-NEXT: }
|
|
// CHECK: Inner Uses {
|
|
// CHECK-NEXT: }
|
|
// CHECK: Overlapping Uses {
|
|
// CHECK-NEXT: %{{.*}} = load [trivial] %{{.*}} : $*MyStruct
|
|
// CHECK-NEXT: Path: (@1)
|
|
// CHECK-NEXT: %{{.*}} = load [trivial] %{{.*}} : $*MyStruct
|
|
// CHECK-NEXT: Path: (@Unknown)
|
|
// CHECK-NEXT: }
|
|
sil [serialized] [ossa] @testDynamicIndexWithStaticIndex : $@convention(thin) (Builtin.RawPointer, Builtin.Word) -> () {
|
|
bb0(%0 : $Builtin.RawPointer, %1 : $Builtin.Word):
|
|
%2 = pointer_to_address %0 : $Builtin.RawPointer to [strict] $*MyStruct
|
|
%3 = integer_literal $Builtin.Word, 1
|
|
%4 = index_addr %2 : $*MyStruct, %3 : $Builtin.Word
|
|
%5 = load [trivial] %4 : $*MyStruct
|
|
%6 = index_addr %2 : $*MyStruct, %1 : $Builtin.Word
|
|
%7 = load [trivial] %6 : $*MyStruct
|
|
%8 = tuple ()
|
|
return %8 : $()
|
|
}
|
|
|
|
// Even though the static offset does not match the first load, the
|
|
// dynamic offset should cancel it.
|
|
// CHECK-LABEL: @testChainedStaticDynamicIndex
|
|
// CHECK: ###For MemOp: %3 = load [trivial] %2 : $*MyStruct
|
|
// CHECK: Exact Uses {
|
|
// CHECK-NEXT: %3 = load [trivial] %2 : $*MyStruct
|
|
// CHECK-NEXT: Path: ()
|
|
// CHECK-NEXT: }
|
|
// CHECK: Overlapping Uses {
|
|
// CHECK-NEXT: %3 = load [trivial] %2 : $*MyStruct
|
|
// CHECK-NEXT: Path: ()
|
|
// CHECK-NEXT: %8 = load [trivial] %7 : $*MyStruct
|
|
// CHECK-NEXT: Path: (@Unknown)
|
|
// CHECK-NEXT: }
|
|
// CHECK: ###For MemOp: %6 = load [trivial] %5 : $*MyStruct
|
|
// CHECK: Storage: Argument %0 = argument of bb0 : $Builtin.RawPointer
|
|
// CHECK: Path: (@1)
|
|
// CHECK: Exact Uses {
|
|
// CHECK-NEXT: %6 = load [trivial] %5 : $*MyStruct
|
|
// CHECK-NEXT: Path: (@1)
|
|
// CHECK-NEXT: }
|
|
// CHECK: Overlapping Uses {
|
|
// CHECK-NEXT: %6 = load [trivial] %5 : $*MyStruct
|
|
// CHECK-NEXT: Path: (@1)
|
|
// CHECK-NEXT: %8 = load [trivial] %7 : $*MyStruct
|
|
// CHECK-NEXT: Path: (@Unknown)
|
|
// CHECK-NEXT: }
|
|
// CHECK: ###For MemOp: %8 = load [trivial] %7 : $*MyStruct
|
|
// CHECK: Storage: Argument %0 = argument of bb0 : $Builtin.RawPointer
|
|
// CHECK: Path: (@Unknown)
|
|
// CHECK: Exact Uses {
|
|
// CHECK-NEXT: }
|
|
// CHECK: Inner Uses {
|
|
// CHECK-NEXT: }
|
|
// CHECK: Overlapping Uses {
|
|
// CHECK-NEXT: %3 = load [trivial] %2 : $*MyStruct
|
|
// CHECK-NEXT: Path: ()
|
|
// CHECK-NEXT: %6 = load [trivial] %5 : $*MyStruct
|
|
// CHECK-NEXT: Path: (@1)
|
|
// CHECK-NEXT: %8 = load [trivial] %7 : $*MyStruct
|
|
// CHECK-NEXT: Path: (@Unknown)
|
|
// CHECK-NEXT: }
|
|
sil [serialized] [ossa] @testChainedStaticDynamicIndex : $@convention(thin) (Builtin.RawPointer, Builtin.Word) -> () {
|
|
bb0(%0 : $Builtin.RawPointer, %1 : $Builtin.Word):
|
|
%2 = pointer_to_address %0 : $Builtin.RawPointer to [strict] $*MyStruct
|
|
%3 = load [trivial] %2 : $*MyStruct
|
|
%4 = integer_literal $Builtin.Word, 1
|
|
%5 = index_addr %2 : $*MyStruct, %4 : $Builtin.Word
|
|
%6 = load [trivial] %5 : $*MyStruct
|
|
%7 = index_addr %5 : $*MyStruct, %1 : $Builtin.Word
|
|
%8 = load [trivial] %7 : $*MyStruct
|
|
%9 = tuple ()
|
|
return %9 : $()
|
|
}
|
|
|
|
// Static indices cancel.
|
|
// Unknown offset overlaps with subobject projections.
|
|
//
|
|
// CHECK-LABEL: @staticIndexAddrCancel
|
|
// CHECK: ###For MemOp: %3 = load [trivial] %2 : $*Int64
|
|
// CHECK: Storage: Argument %0 = argument of bb0 : $Builtin.RawPointer
|
|
// CHECK: Path: (#0)
|
|
// CHECK: Exact Uses {
|
|
// CHECK-NEXT: %3 = load [trivial] %2 : $*Int64
|
|
// CHECK-NEXT: Path: (#0)
|
|
// CHECK-NEXT: %9 = load [trivial] %8 : $*Int64
|
|
// CHECK-NEXT: Path: (#0)
|
|
// CHECK-NEXT: }
|
|
// CHECK: Overlapping Uses {
|
|
// CHECK-NEXT: %3 = load [trivial] %2 : $*Int64
|
|
// CHECK-NEXT: Path: (#0)
|
|
// CHECK-NEXT: %9 = load [trivial] %8 : $*Int64
|
|
// CHECK-NEXT: Path: (#0)
|
|
// CHECK-NEXT: }
|
|
// CHECK: ###For MemOp: %9 = load [trivial] %8 : $*Int64
|
|
// CHECK: Storage: Argument %0 = argument of bb0 : $Builtin.RawPointer
|
|
// CHECK: Path: (#0)
|
|
// CHECK: Exact Uses {
|
|
// CHECK-NEXT: %3 = load [trivial] %2 : $*Int64
|
|
// CHECK-NEXT: Path: (#0)
|
|
// CHECK-NEXT: %9 = load [trivial] %8 : $*Int64
|
|
// CHECK-NEXT: Path: (#0)
|
|
// CHECK-NEXT: }
|
|
// CHECK: Overlapping Uses {
|
|
// CHECK-NEXT: %3 = load [trivial] %2 : $*Int64
|
|
// CHECK-NEXT: Path: (#0)
|
|
// CHECK-NEXT: %9 = load [trivial] %8 : $*Int64
|
|
// CHECK-NEXT: Path: (#0)
|
|
// CHECK-NEXT: }
|
|
sil [ossa] @staticIndexAddrCancel : $@convention(thin) (Builtin.RawPointer) -> () {
|
|
bb0(%0 : $Builtin.RawPointer):
|
|
%1 = pointer_to_address %0 : $Builtin.RawPointer to $*MyStruct
|
|
%2 = struct_element_addr %1 : $*MyStruct, #MyStruct.i
|
|
%3 = load [trivial] %2 : $*Int64
|
|
%4 = integer_literal $Builtin.Word, 1
|
|
%5 = index_addr %1 : $*MyStruct, %4 : $Builtin.Word
|
|
%6 = integer_literal $Builtin.Word, -1
|
|
%7 = index_addr %5 : $*MyStruct, %6 : $Builtin.Word
|
|
%8 = struct_element_addr %7 : $*MyStruct, #MyStruct.i
|
|
%9 = load [trivial] %8 : $*Int64
|
|
%99 = tuple ()
|
|
return %99 : $()
|
|
}
|
|
|
|
// Increment an indexable address by one in a loop, with subobject
|
|
// uses inside and outside the loop.
|
|
//
|
|
// CHECK-LABEL: @testIndexLoop
|
|
// CHECK: ###For MemOp: %3 = load %2 : $*AnyObject
|
|
// CHECK: Storage: Unidentified %1 = struct_extract %0 : $UnsafeMutablePointer<AnyObject>, #UnsafeMutablePointer._rawValue
|
|
// CHECK: Path: ()
|
|
// CHECK: Exact Uses {
|
|
// CHECK-NEXT: %3 = load %2 : $*AnyObject
|
|
// CHECK-NEXT: Path: ()
|
|
// CHECK-NEXT: }
|
|
// CHECK: Overlapping Uses {
|
|
// CHECK-NEXT: %3 = load %2 : $*AnyObject
|
|
// CHECK-NEXT: Path: ()
|
|
// CHECK-NEXT: %7 = load %6 : $*AnyObject
|
|
// CHECK-NEXT: Path: (@Unknown)
|
|
// CHECK-NEXT: store %7 to %6 : $*AnyObject
|
|
// CHECK-NEXT: Path: (@Unknown)
|
|
// CHECK-NEXT: store %7 to %10 : $*AnyObject
|
|
// CHECK-NEXT: Path: (@Unknown)
|
|
// CHECK-NEXT: cond_br undef, bb2, bb3(%12 : $Builtin.RawPointer)
|
|
// CHECK-NEXT: Path: (@Unknown)
|
|
// CHECK-NEXT: }
|
|
// CHECK: ###For MemOp: %7 = load %6 : $*AnyObject
|
|
// CHECK: Storage: Unidentified %1 = struct_extract %0 : $UnsafeMutablePointer<AnyObject>, #UnsafeMutablePointer._rawValue
|
|
// CHECK: Path: (@Unknown)
|
|
// CHECK: Exact Uses {
|
|
// CHECK-NEXT: }
|
|
// CHECK: Overlapping Uses {
|
|
// CHECK-NEXT: %3 = load %2 : $*AnyObject
|
|
// CHECK-NEXT: Path: ()
|
|
// CHECK-NEXT: %7 = load %6 : $*AnyObject
|
|
// CHECK-NEXT: Path: (@Unknown)
|
|
// CHECK-NEXT: store %7 to %6 : $*AnyObject
|
|
// CHECK-NEXT: Path: (@Unknown)
|
|
// CHECK-NEXT: store %7 to %10 : $*AnyObject
|
|
// CHECK-NEXT: Path: (@Unknown)
|
|
// CHECK-NEXT: cond_br undef, bb2, bb3(%12 : $Builtin.RawPointer)
|
|
// CHECK-NEXT: Path: (@Unknown)
|
|
// CHECK-NEXT: }
|
|
// CHECK: ###For MemOp: store %7 to %6 : $*AnyObject
|
|
// CHECK: Storage: Unidentified %1 = struct_extract %0 : $UnsafeMutablePointer<AnyObject>, #UnsafeMutablePointer._rawValue
|
|
// CHECK: Path: (@Unknown)
|
|
// CHECK: Exact Uses {
|
|
// CHECK-NEXT: }
|
|
// CHECK: Overlapping Uses {
|
|
// CHECK-NEXT: %3 = load %2 : $*AnyObject
|
|
// CHECK-NEXT: Path: ()
|
|
// CHECK-NEXT: %7 = load %6 : $*AnyObject
|
|
// CHECK-NEXT: Path: (@Unknown)
|
|
// CHECK-NEXT: store %7 to %6 : $*AnyObject
|
|
// CHECK-NEXT: Path: (@Unknown)
|
|
// CHECK-NEXT: store %7 to %10 : $*AnyObject
|
|
// CHECK-NEXT: Path: (@Unknown)
|
|
// CHECK-NEXT: cond_br undef, bb2, bb3(%12 : $Builtin.RawPointer)
|
|
// CHECK-NEXT: Path: (@Unknown)
|
|
// CHECK-NEXT: }
|
|
// CHECK: ###For MemOp: store %7 to %10 : $*AnyObject
|
|
// CHECK: Storage: Unidentified %1 = struct_extract %0 : $UnsafeMutablePointer<AnyObject>, #UnsafeMutablePointer._rawValue
|
|
// CHECK: Path: (@Unknown)
|
|
// CHECK: Exact Uses {
|
|
// CHECK-NEXT: }
|
|
// CHECK: Overlapping Uses {
|
|
// CHECK-NEXT: %3 = load %2 : $*AnyObject
|
|
// CHECK-NEXT: Path: ()
|
|
// CHECK-NEXT: %7 = load %6 : $*AnyObject
|
|
// CHECK-NEXT: Path: (@Unknown)
|
|
// CHECK-NEXT: store %7 to %6 : $*AnyObject
|
|
// CHECK-NEXT: Path: (@Unknown)
|
|
// CHECK-NEXT: store %7 to %10 : $*AnyObject
|
|
// CHECK-NEXT: Path: (@Unknown)
|
|
// CHECK-NEXT: cond_br undef, bb2, bb3(%12 : $Builtin.RawPointer)
|
|
// CHECK-NEXT: Path: (@Unknown)
|
|
// CHECK-NEXT: }
|
|
// CHECK: ###For MemOp: %17 = load %16 : $*AnyObject
|
|
// CHECK: Storage: Unidentified %1 = struct_extract %0 : $UnsafeMutablePointer<AnyObject>, #UnsafeMutablePointer._rawValue
|
|
// CHECK: Path: (@Unknown)
|
|
// CHECK: Exact Uses {
|
|
// CHECK-NEXT: }
|
|
// CHECK: Overlapping Uses {
|
|
// CHECK-NEXT: %3 = load %2 : $*AnyObject
|
|
// CHECK-NEXT: Path: ()
|
|
// CHECK-NEXT: %7 = load %6 : $*AnyObject
|
|
// CHECK-NEXT: Path: (@Unknown)
|
|
// CHECK-NEXT: store %7 to %6 : $*AnyObject
|
|
// CHECK-NEXT: Path: (@Unknown)
|
|
// CHECK-NEXT: store %7 to %10 : $*AnyObject
|
|
// CHECK-NEXT: Path: (@Unknown)
|
|
// CHECK-NEXT: cond_br undef, bb2, bb3(%12 : $Builtin.RawPointer)
|
|
// CHECK-NEXT: Path: (@Unknown)
|
|
// CHECK-NEXT: }
|
|
sil shared @testIndexLoop : $@convention(thin) (UnsafeMutablePointer<AnyObject>) -> AnyObject {
|
|
bb0(%0 : $UnsafeMutablePointer<AnyObject>):
|
|
%1 = struct_extract %0 : $UnsafeMutablePointer<AnyObject>, #UnsafeMutablePointer._rawValue
|
|
%2 = pointer_to_address %1 : $Builtin.RawPointer to [strict] $*AnyObject
|
|
%3 = load %2 : $*AnyObject
|
|
br bb1(%1 : $Builtin.RawPointer)
|
|
|
|
bb1(%5 : $Builtin.RawPointer):
|
|
%6 = pointer_to_address %5 : $Builtin.RawPointer to [strict] $*AnyObject
|
|
%7 = load %6 : $*AnyObject
|
|
store %7 to %6 : $*AnyObject
|
|
%9 = integer_literal $Builtin.Word, 1
|
|
%10 = index_addr %6 : $*AnyObject, %9 : $Builtin.Word
|
|
store %7 to %10 : $*AnyObject
|
|
%12 = address_to_pointer %10 : $*AnyObject to $Builtin.RawPointer
|
|
cond_br undef, bb2, bb3(%12 : $Builtin.RawPointer)
|
|
|
|
bb2:
|
|
br bb1(%12 : $Builtin.RawPointer)
|
|
|
|
bb3(%15 : $Builtin.RawPointer):
|
|
%16 = pointer_to_address %15 : $Builtin.RawPointer to [strict] $*AnyObject
|
|
%17 = load %16 : $*AnyObject
|
|
return %17 : $AnyObject
|
|
}
|
|
|
|
enum IntTEnum<T> {
|
|
indirect case int(Int)
|
|
case other(T)
|
|
|
|
var getValue: Int {get }
|
|
}
|
|
|
|
// CHECK-LABEL: @testEnumUses
|
|
// CHECK: ###For MemOp: copy_addr %0 to [initialization] %2 : $*IntTEnum<T>
|
|
// CHECK: Storage: Argument %0 = argument of bb0 : $*IntTEnum<T>
|
|
// CHECK: Path: ()
|
|
// CHECK: Exact Uses {
|
|
// CHECK-NEXT: debug_value_addr %0 : $*IntTEnum<T>, let, name "self", argno 1
|
|
// CHECK-NEXT: Path: ()
|
|
// CHECK-NEXT: copy_addr %0 to [initialization] %2 : $*IntTEnum<T>
|
|
// CHECK-NEXT: Path: ()
|
|
// CHECK-NEXT: }
|
|
// CHECK: Overlapping Uses {
|
|
// CHECK-NEXT: debug_value_addr %0 : $*IntTEnum<T>, let, name "self", argno 1
|
|
// CHECK-NEXT: Path: ()
|
|
// CHECK-NEXT: copy_addr %0 to [initialization] %2 : $*IntTEnum<T>
|
|
// CHECK-NEXT: Path: ()
|
|
// CHECK-NEXT: }
|
|
// CHECK: Storage: Stack %2 = alloc_stack $IntTEnum<T>
|
|
// CHECK: Path: ()
|
|
// CHECK: Exact Uses {
|
|
// CHECK-NEXT: copy_addr %0 to [initialization] %2 : $*IntTEnum<T>
|
|
// CHECK-NEXT: Path: ()
|
|
// CHECK-NEXT: switch_enum_addr %2 : $*IntTEnum<T>, case #IntTEnum.int!enumelt: bb1, case #IntTEnum.other!enumelt: bb2
|
|
// CHECK-NEXT: Path: ()
|
|
// CHECK-NEXT: %5 = unchecked_take_enum_data_addr %2 : $*IntTEnum<T>, #IntTEnum.int!enumelt
|
|
// CHECK-NEXT: Path: ()
|
|
// CHECK-NEXT: %6 = load [take] %5 : $*<τ_0_0> { var Int } <T>
|
|
// CHECK-NEXT: Path: ()
|
|
// CHECK-NEXT: %8 = load [trivial] %7 : $*Int
|
|
// CHECK-NEXT: Path: ()
|
|
// CHECK-NEXT: dealloc_stack %2 : $*IntTEnum<T>
|
|
// CHECK-NEXT: Path: ()
|
|
// CHECK-NEXT: %13 = unchecked_take_enum_data_addr %2 : $*IntTEnum<T>, #IntTEnum.other!enumelt
|
|
// CHECK-NEXT: Path: ()
|
|
// CHECK-NEXT: destroy_addr %13 : $*T
|
|
// CHECK-NEXT: Path: ()
|
|
// CHECK-NEXT: dealloc_stack %2 : $*IntTEnum<T>
|
|
// CHECK-NEXT: Path: ()
|
|
// CHECK-NEXT: }
|
|
// CHECK: Overlapping Uses {
|
|
// CHECK-NEXT: copy_addr %0 to [initialization] %2 : $*IntTEnum<T>
|
|
// CHECK-NEXT: Path: ()
|
|
// CHECK-NEXT: switch_enum_addr %2 : $*IntTEnum<T>, case #IntTEnum.int!enumelt: bb1, case #IntTEnum.other!enumelt: bb2
|
|
// CHECK-NEXT: Path: ()
|
|
// CHECK-NEXT: %5 = unchecked_take_enum_data_addr %2 : $*IntTEnum<T>, #IntTEnum.int!enumelt
|
|
// CHECK-NEXT: Path: ()
|
|
// CHECK-NEXT: %6 = load [take] %5 : $*<τ_0_0> { var Int } <T>
|
|
// CHECK-NEXT: Path: ()
|
|
// CHECK-NEXT: %8 = load [trivial] %7 : $*Int
|
|
// CHECK-NEXT: Path: ()
|
|
// CHECK-NEXT: dealloc_stack %2 : $*IntTEnum<T>
|
|
// CHECK-NEXT: Path: ()
|
|
// CHECK-NEXT: %13 = unchecked_take_enum_data_addr %2 : $*IntTEnum<T>, #IntTEnum.other!enumelt
|
|
// CHECK-NEXT: Path: ()
|
|
// CHECK-NEXT: destroy_addr %13 : $*T
|
|
// CHECK-NEXT: Path: ()
|
|
// CHECK-NEXT: dealloc_stack %2 : $*IntTEnum<T>
|
|
// CHECK-NEXT: Path: ()
|
|
// CHECK-NEXT: }
|
|
// CHECK: ###For MemOp: switch_enum_addr %2 : $*IntTEnum<T>, case #IntTEnum.int!enumelt: bb1, case #IntTEnum.other!enumelt: bb2
|
|
// CHECK: Storage: Stack %2 = alloc_stack $IntTEnum<T>
|
|
// CHECK: Path: ()
|
|
// CHECK: Exact Uses {
|
|
// CHECK: copy_addr %0 to [initialization] %2 : $*IntTEnum<T>
|
|
// CHECK: switch_enum_addr %2 : $*IntTEnum<T>, case #IntTEnum.int!enumelt: bb1, case #IntTEnum.other!enumelt: bb2
|
|
// CHECK: %5 = unchecked_take_enum_data_addr %2 : $*IntTEnum<T>, #IntTEnum.int!enumelt
|
|
// CHECK: %6 = load [take] %5 : $*<τ_0_0> { var Int } <T>
|
|
// CHECK: %8 = load [trivial] %7 : $*Int
|
|
// CHECK: %13 = unchecked_take_enum_data_addr %2 : $*IntTEnum<T>, #IntTEnum.other!enumelt
|
|
// CHECK: }
|
|
// CHECK: Overlapping Uses {
|
|
// CHECK: copy_addr %0 to [initialization] %2 : $*IntTEnum<T>
|
|
// CHECK: switch_enum_addr %2 : $*IntTEnum<T>, case #IntTEnum.int!enumelt: bb1, case #IntTEnum.other!enumelt: bb2
|
|
// CHECK: %5 = unchecked_take_enum_data_addr %2 : $*IntTEnum<T>, #IntTEnum.int!enumelt
|
|
// CHECK: %6 = load [take] %5 : $*<τ_0_0> { var Int } <T>
|
|
// CHECK: %8 = load [trivial] %7 : $*Int
|
|
// CHECK: %13 = unchecked_take_enum_data_addr %2 : $*IntTEnum<T>, #IntTEnum.other!enumelt
|
|
// CHECK: }
|
|
// CHECK: ###For MemOp: %5 = unchecked_take_enum_data_addr %2 : $*IntTEnum<T>, #IntTEnum.int!enumelt
|
|
// CHECK: Storage: Stack %2 = alloc_stack $IntTEnum<T>
|
|
// CHECK: Path: ()
|
|
// CHECK: Exact Uses {
|
|
// CHECK: copy_addr %0 to [initialization] %2 : $*IntTEnum<T>
|
|
// CHECK: switch_enum_addr %2 : $*IntTEnum<T>, case #IntTEnum.int!enumelt: bb1, case #IntTEnum.other!enumelt: bb2
|
|
// CHECK: %5 = unchecked_take_enum_data_addr %2 : $*IntTEnum<T>, #IntTEnum.int!enumelt
|
|
// CHECK: %6 = load [take] %5 : $*<τ_0_0> { var Int } <T>
|
|
// CHECK: %8 = load [trivial] %7 : $*Int
|
|
// CHECK: %13 = unchecked_take_enum_data_addr %2 : $*IntTEnum<T>, #IntTEnum.other!enumelt
|
|
// CHECK: }
|
|
// CHECK: Overlapping Uses {
|
|
// CHECK: copy_addr %0 to [initialization] %2 : $*IntTEnum<T>
|
|
// CHECK: switch_enum_addr %2 : $*IntTEnum<T>, case #IntTEnum.int!enumelt: bb1, case #IntTEnum.other!enumelt: bb2
|
|
// CHECK: %5 = unchecked_take_enum_data_addr %2 : $*IntTEnum<T>, #IntTEnum.int!enumelt
|
|
// CHECK: %6 = load [take] %5 : $*<τ_0_0> { var Int } <T>
|
|
// CHECK: %8 = load [trivial] %7 : $*Int
|
|
// CHECK: %13 = unchecked_take_enum_data_addr %2 : $*IntTEnum<T>, #IntTEnum.other!enumelt
|
|
// CHECK: }
|
|
// CHECK: ###For MemOp: %6 = load [take] %5 : $*<τ_0_0> { var Int } <T>
|
|
// CHECK: Storage: Stack %2 = alloc_stack $IntTEnum<T>
|
|
// CHECK: Path: ()
|
|
// CHECK: Exact Uses {
|
|
// CHECK: copy_addr %0 to [initialization] %2 : $*IntTEnum<T>
|
|
// CHECK: switch_enum_addr %2 : $*IntTEnum<T>, case #IntTEnum.int!enumelt: bb1, case #IntTEnum.other!enumelt: bb2
|
|
// CHECK: %5 = unchecked_take_enum_data_addr %2 : $*IntTEnum<T>, #IntTEnum.int!enumelt
|
|
// CHECK: %6 = load [take] %5 : $*<τ_0_0> { var Int } <T>
|
|
// CHECK: %8 = load [trivial] %7 : $*Int
|
|
// CHECK: %13 = unchecked_take_enum_data_addr %2 : $*IntTEnum<T>, #IntTEnum.other!enumelt
|
|
// CHECK: }
|
|
// CHECK: Overlapping Uses {
|
|
// CHECK: copy_addr %0 to [initialization] %2 : $*IntTEnum<T>
|
|
// CHECK: switch_enum_addr %2 : $*IntTEnum<T>, case #IntTEnum.int!enumelt: bb1, case #IntTEnum.other!enumelt: bb2
|
|
// CHECK: %5 = unchecked_take_enum_data_addr %2 : $*IntTEnum<T>, #IntTEnum.int!enumelt
|
|
// CHECK: %6 = load [take] %5 : $*<τ_0_0> { var Int } <T>
|
|
// CHECK: %8 = load [trivial] %7 : $*Int
|
|
// CHECK: %13 = unchecked_take_enum_data_addr %2 : $*IntTEnum<T>, #IntTEnum.other!enumelt
|
|
// CHECK: }
|
|
// CHECK: ###For MemOp: %8 = load [trivial] %7 : $*Int
|
|
// CHECK: Storage: Stack %2 = alloc_stack $IntTEnum<T>
|
|
// CHECK: Path: ()
|
|
// CHECK: Exact Uses {
|
|
// CHECK: copy_addr %0 to [initialization] %2 : $*IntTEnum<T>
|
|
// CHECK: switch_enum_addr %2 : $*IntTEnum<T>, case #IntTEnum.int!enumelt: bb1, case #IntTEnum.other!enumelt: bb2
|
|
// CHECK: %5 = unchecked_take_enum_data_addr %2 : $*IntTEnum<T>, #IntTEnum.int!enumelt
|
|
// CHECK: %6 = load [take] %5 : $*<τ_0_0> { var Int } <T>
|
|
// CHECK: %8 = load [trivial] %7 : $*Int
|
|
// CHECK: %13 = unchecked_take_enum_data_addr %2 : $*IntTEnum<T>, #IntTEnum.other!enumelt
|
|
// CHECK: }
|
|
// CHECK: Overlapping Uses {
|
|
// CHECK: copy_addr %0 to [initialization] %2 : $*IntTEnum<T>
|
|
// CHECK: switch_enum_addr %2 : $*IntTEnum<T>, case #IntTEnum.int!enumelt: bb1, case #IntTEnum.other!enumelt: bb2
|
|
// CHECK: %5 = unchecked_take_enum_data_addr %2 : $*IntTEnum<T>, #IntTEnum.int!enumelt
|
|
// CHECK: %6 = load [take] %5 : $*<τ_0_0> { var Int } <T>
|
|
// CHECK: %8 = load [trivial] %7 : $*Int
|
|
// CHECK: %13 = unchecked_take_enum_data_addr %2 : $*IntTEnum<T>, #IntTEnum.other!enumelt
|
|
// CHECK: }
|
|
// CHECK: ###For MemOp: %13 = unchecked_take_enum_data_addr %2 : $*IntTEnum<T>, #IntTEnum.other!enumelt
|
|
// CHECK: Storage: Stack %2 = alloc_stack $IntTEnum<T>
|
|
// CHECK: Path: ()
|
|
// CHECK: Exact Uses {
|
|
// CHECK: copy_addr %0 to [initialization] %2 : $*IntTEnum<T>
|
|
// CHECK: switch_enum_addr %2 : $*IntTEnum<T>, case #IntTEnum.int!enumelt: bb1, case #IntTEnum.other!enumelt: bb2
|
|
// CHECK: %5 = unchecked_take_enum_data_addr %2 : $*IntTEnum<T>, #IntTEnum.int!enumelt
|
|
// CHECK: %6 = load [take] %5 : $*<τ_0_0> { var Int } <T>
|
|
// CHECK: %8 = load [trivial] %7 : $*Int
|
|
// CHECK: %13 = unchecked_take_enum_data_addr %2 : $*IntTEnum<T>, #IntTEnum.other!enumelt
|
|
// CHECK: }
|
|
// CHECK: Overlapping Uses {
|
|
// CHECK: copy_addr %0 to [initialization] %2 : $*IntTEnum<T>
|
|
// CHECK: switch_enum_addr %2 : $*IntTEnum<T>, case #IntTEnum.int!enumelt: bb1, case #IntTEnum.other!enumelt: bb2
|
|
// CHECK: %5 = unchecked_take_enum_data_addr %2 : $*IntTEnum<T>, #IntTEnum.int!enumelt
|
|
// CHECK: %6 = load [take] %5 : $*<τ_0_0> { var Int } <T>
|
|
// CHECK: %8 = load [trivial] %7 : $*Int
|
|
// CHECK: %13 = unchecked_take_enum_data_addr %2 : $*IntTEnum<T>, #IntTEnum.other!enumelt
|
|
// CHECK: }
|
|
sil hidden [ossa] @testEnumUses : $@convention(method) <T> (@in_guaranteed IntTEnum<T>) -> Int {
|
|
bb0(%0 : $*IntTEnum<T>):
|
|
debug_value_addr %0 : $*IntTEnum<T>, let, name "self", argno 1
|
|
%2 = alloc_stack $IntTEnum<T>
|
|
copy_addr %0 to [initialization] %2 : $*IntTEnum<T>
|
|
switch_enum_addr %2 : $*IntTEnum<T>, case #IntTEnum.int!enumelt: bb1, case #IntTEnum.other!enumelt: bb2
|
|
|
|
bb1:
|
|
%5 = unchecked_take_enum_data_addr %2 : $*IntTEnum<T>, #IntTEnum.int!enumelt
|
|
%6 = load [take] %5 : $*<τ_0_0> { var Int } <T>
|
|
%7 = project_box %6 : $<τ_0_0> { var Int } <T>, 0
|
|
%8 = load [trivial] %7 : $*Int
|
|
debug_value %8 : $Int, let, name "x"
|
|
destroy_value %6 : $<τ_0_0> { var Int } <T>
|
|
dealloc_stack %2 : $*IntTEnum<T>
|
|
br bb3(%8 : $Int)
|
|
|
|
bb2:
|
|
%13 = unchecked_take_enum_data_addr %2 : $*IntTEnum<T>, #IntTEnum.other!enumelt
|
|
%14 = integer_literal $Builtin.Int64, 0
|
|
%15 = struct $Int (%14 : $Builtin.Int64)
|
|
destroy_addr %13 : $*T
|
|
dealloc_stack %2 : $*IntTEnum<T>
|
|
br bb3(%15 : $Int)
|
|
|
|
bb3(%19 : $Int):
|
|
return %19 : $Int
|
|
}
|
|
|
|
class Storage {}
|
|
|
|
// Test that tail_addr is always an unknown offset.
|
|
//
|
|
// CHECK-LABEL: @inlinedArrayProp
|
|
// CHECK: ###For MemOp: %{{.*}} = load [trivial] %{{.*}} : $*Int
|
|
// CHECK: Tail %0 = argument of bb0 : $Storage
|
|
// CHECK: Base: %{{.*}} = ref_tail_addr %0 : $Storage, $UInt
|
|
// CHECK: Storage: Tail %0 = argument of bb0 : $Storage
|
|
// CHECK: Path: (@Unknown)
|
|
// CHECK: Exact Uses {
|
|
// CHECK-NEXT: }
|
|
// CHECK: Overlapping Uses {
|
|
// CHECK-NEXT: %8 = load [trivial] %7 : $*Int
|
|
// CHECK-NEXT: Path: (@Unknown)
|
|
// CHECK-NEXT: end_access %7 : $*Int
|
|
// CHECK-NEXT: Path: (@Unknown)
|
|
// CHECK-NEXT: end_access %5 : $*Int
|
|
// CHECK-NEXT: Path: (@Unknown)
|
|
// CHECK-NEXT: end_access %2 : $*UInt
|
|
// CHECK-NEXT: Path: ()
|
|
// CHECK-NEXT: }
|
|
sil hidden [ossa] @inlinedArrayProp : $@convention(thin) (@guaranteed Storage) -> Int {
|
|
bb0(%0 : @guaranteed $Storage):
|
|
%1 = ref_tail_addr %0 : $Storage, $UInt
|
|
%2 = begin_access [read] [static] %1 : $*UInt
|
|
%3 = integer_literal $Builtin.Word, 1
|
|
%4 = tail_addr %2 : $*UInt, %3 : $Builtin.Word, $Int
|
|
%5 = begin_access [read] [static] %4 : $*Int
|
|
%6 = index_addr %5 : $*Int, %3 : $Builtin.Word
|
|
%7 = begin_access [read] [static] %6 : $*Int
|
|
%8 = load [trivial] %7 : $*Int
|
|
end_access %7 : $*Int
|
|
end_access %5 : $*Int
|
|
end_access %2 : $*UInt
|
|
return %8 : $Int
|
|
}
|
|
|
|
// CHECK-LABEL: @testBeginAccessUses
|
|
// CHECK: ###For MemOp: copy_addr [take] %1 to [initialization] %{{.*}} : $*T
|
|
// CHECK: Storage: Argument %1 = argument of bb0 : $*T
|
|
// CHECK: Path: ()
|
|
// CHECK: Exact Uses {
|
|
// CHECK-NEXT: copy_addr [take] %1 to [initialization] %{{.*}} : $*T
|
|
// CHECK-NEXT: Path: ()
|
|
// CHECK-NEXT: }
|
|
// CHECK: Overlapping Uses {
|
|
// CHECK-NEXT: copy_addr [take] %1 to [initialization] %{{.*}} : $*T
|
|
// CHECK-NEXT: Path: ()
|
|
// CHECK-NEXT: }
|
|
// CHECK: Storage: Stack %{{.*}} = alloc_stack $T, var, name "$value"
|
|
// CHECK: Path: ()
|
|
// CHECK: Exact Uses {
|
|
// CHECK-NEXT: copy_addr [take] %1 to [initialization] %{{.*}} : $*T
|
|
// CHECK-NEXT: Path: ()
|
|
// CHECK-NEXT: copy_addr %{{.*}} to [initialization] %0 : $*T
|
|
// CHECK-NEXT: Path: ()
|
|
// CHECK-NEXT: end_access %{{.*}} : $*T
|
|
// CHECK-NEXT: Path: ()
|
|
// CHECK-NEXT: destroy_addr %{{.*}} : $*T
|
|
// CHECK-NEXT: Path: ()
|
|
// CHECK-NEXT: dealloc_stack %{{.*}} : $*T
|
|
// CHECK-NEXT: Path: ()
|
|
// CHECK-NEXT: }
|
|
// CHECK: Overlapping Uses {
|
|
// CHECK-NEXT: copy_addr [take] %1 to [initialization] %{{.*}} : $*T
|
|
// CHECK-NEXT: Path: ()
|
|
// CHECK-NEXT: copy_addr %{{.*}} to [initialization] %0 : $*T
|
|
// CHECK-NEXT: Path: ()
|
|
// CHECK-NEXT: end_access %{{.*}} : $*T
|
|
// CHECK-NEXT: Path: ()
|
|
// CHECK-NEXT: destroy_addr %{{.*}} : $*T
|
|
// CHECK-NEXT: Path: ()
|
|
// CHECK-NEXT: dealloc_stack %{{.*}} : $*T
|
|
// CHECK-NEXT: Path: ()
|
|
// CHECK-NEXT: }
|
|
// CHECK: ###For MemOp: copy_addr %{{.*}} to [initialization] %0 : $*T
|
|
// CHECK: Storage: Stack %{{.*}} = alloc_stack $T, var, name "$value"
|
|
// CHECK: Path: ()
|
|
// CHECK: Exact Uses {
|
|
// CHECK-NEXT: copy_addr [take] %1 to [initialization] %{{.*}} : $*T
|
|
// CHECK-NEXT: Path: ()
|
|
// CHECK-NEXT: copy_addr %{{.*}} to [initialization] %0 : $*T
|
|
// CHECK-NEXT: Path: ()
|
|
// CHECK-NEXT: end_access %{{.*}} : $*T
|
|
// CHECK-NEXT: Path: ()
|
|
// CHECK-NEXT: destroy_addr %{{.*}} : $*T
|
|
// CHECK-NEXT: Path: ()
|
|
// CHECK-NEXT: dealloc_stack %{{.*}} : $*T
|
|
// CHECK-NEXT: Path: ()
|
|
// CHECK-NEXT: }
|
|
// CHECK: Overlapping Uses {
|
|
// CHECK-NEXT: copy_addr [take] %1 to [initialization] %{{.*}} : $*T
|
|
// CHECK-NEXT: Path: ()
|
|
// CHECK-NEXT: copy_addr %{{.*}} to [initialization] %0 : $*T
|
|
// CHECK-NEXT: Path: ()
|
|
// CHECK-NEXT: end_access %{{.*}} : $*T
|
|
// CHECK-NEXT: Path: ()
|
|
// CHECK-NEXT: destroy_addr %{{.*}} : $*T
|
|
// CHECK-NEXT: Path: ()
|
|
// CHECK-NEXT: dealloc_stack %{{.*}} : $*T
|
|
// CHECK-NEXT: Path: ()
|
|
// CHECK-NEXT: }
|
|
sil [ossa] @testBeginAccessUses : $@convention(thin) <T where T : Comparable> (@in T) -> @out T {
|
|
bb0(%0 : $*T, %1 : $*T):
|
|
%2 = alloc_stack $T, var, name "$value"
|
|
copy_addr [take] %1 to [initialization] %2 : $*T
|
|
%4 = begin_access [modify] [static] %2 : $*T
|
|
copy_addr %4 to [initialization] %0 : $*T
|
|
end_access %4 : $*T
|
|
destroy_addr %2 : $*T
|
|
dealloc_stack %2 : $*T
|
|
%10 = tuple ()
|
|
return %10 : $()
|
|
}
|
|
|
|
// Test reference root casts.
|
|
class A {
|
|
var prop0: Int64
|
|
}
|
|
class B : A {
|
|
var alsoProp0: Int64
|
|
}
|
|
|
|
// CHECK-LABEL: @testReferenceRootCast
|
|
// CHECK: ###For MemOp: store %0 to [[ADR0:%.*]] : $*Int64
|
|
// CHECK: Class %{{.*}} = alloc_ref $B
|
|
// CHECK: Field: var prop0: Int64 Index: 0
|
|
// CHECK: Base: [[ADR0]] = ref_element_addr %{{.*}} : $A, #A.prop0
|
|
// CHECK: Storage: Class %1 = alloc_ref $B
|
|
// CHECK: Field: var prop0: Int64 Index: 0
|
|
// CHECK: Path: ()
|
|
// CHECK: Exact Uses {
|
|
// CHECK-NEXT: store %0 to [[ADR0]] : $*Int64
|
|
// CHECK-NEXT: Path: ()
|
|
// CHECK-NEXT: }
|
|
// CHECK: Overlapping Uses {
|
|
// CHECK-NEXT: store %0 to [[ADR0]] : $*Int64
|
|
// CHECK-NEXT: Path: ()
|
|
// CHECK-NEXT: }
|
|
// CHECK: ###For MemOp: store %0 to [[ADR1:%.*]] : $*Int64
|
|
// CHECK-NEXT: Class %{{.*}} = alloc_ref $B
|
|
// CHECK-NEXT: Field: var alsoProp0: Int64 Index: 1
|
|
// CHECK-NEXT: Base: [[ADR1]] = ref_element_addr %10 : $B, #B.alsoProp0
|
|
// CHECK-NEXT: Storage: Class %{{.*}} = alloc_ref $B
|
|
// CHECK-NEXT: Field: var alsoProp0: Int64 Index: 1
|
|
// CHECK-NEXT: Path: ()
|
|
// CHECK-NEXT: Exact Uses {
|
|
// CHECK-NEXT: store %0 to [[ADR1]] : $*Int64
|
|
// CHECK-NEXT: Path: ()
|
|
// CHECK-NEXT: }
|
|
// CHECK: Overlapping Uses {
|
|
// CHECK-NEXT: store %0 to [[ADR1]] : $*Int64
|
|
// CHECK-NEXT: Path: ()
|
|
// CHECK-NEXT: }
|
|
sil @testReferenceRootCast : $@convention(thin) (Int64) -> () {
|
|
bb0(%0 : $Int64):
|
|
%1 = alloc_ref $B
|
|
cond_br undef, bb1, bb2
|
|
|
|
bb1:
|
|
%3 = upcast %1 : $B to $A
|
|
br bb3(%3 : $A)
|
|
|
|
bb2:
|
|
%5 = upcast %1 : $B to $A
|
|
br bb3(%5 : $A)
|
|
|
|
bb3(%7 : $A):
|
|
%8 = ref_element_addr %7 : $A, #A.prop0
|
|
store %0 to %8 : $*Int64
|
|
%10 = unchecked_ref_cast %7 : $A to $B
|
|
%11 = ref_element_addr %10 : $B, #B.alsoProp0
|
|
store %0 to %11 : $*Int64
|
|
%99 = tuple ()
|
|
return %99 : $()
|
|
}
|
|
|
|
// Test a phi cycle where the phi itself is the root (there is no common phi reference).
|
|
// CHECK-LABEL: @testRootPhiCycle
|
|
// CHECK: ###For MemOp: %{{.*}} = load %{{.*}} : $*Double
|
|
// CHECK: Storage: Tail %{{.*}} = argument of bb3 : $Builtin.BridgeObject
|
|
// CHECK: Path: ()
|
|
// CHECK: Exact Uses {
|
|
// CHECK-NEXT: %{{.*}} = load %{{.*}} : $*Double
|
|
// CHECK-NEXT: Path: ()
|
|
// CHECK-NEXT: %{{.*}} = load %{{.*}} : $*Double
|
|
// CHECK-NEXT: Path: ()
|
|
// CHECK-NEXT: }
|
|
// CHECK: ###For MemOp: %{{.*}} = load %{{.*}} : $*Double
|
|
// CHECK: Storage: Tail %{{.*}} = argument of bb3 : $Builtin.BridgeObject
|
|
// CHECK: Path: ()
|
|
// CHECK: Exact Uses {
|
|
// CHECK-NEXT: %{{.*}} = load %{{.*}} : $*Double
|
|
// CHECK-NEXT: Path: ()
|
|
// CHECK-NEXT: %{{.*}} = load %{{.*}} : $*Double
|
|
// CHECK-NEXT: Path: ()
|
|
// CHECK-NEXT: }
|
|
sil @testRootPhiCycle : $@convention(thin) (Builtin.BridgeObject, Builtin.BridgeObject) -> () {
|
|
bb0(%0 : $Builtin.BridgeObject, %1 : $Builtin.BridgeObject):
|
|
cond_br undef, bb1, bb2
|
|
|
|
bb1:
|
|
br bb3(%0 : $Builtin.BridgeObject)
|
|
|
|
bb2:
|
|
br bb3(%1 : $Builtin.BridgeObject)
|
|
|
|
bb3(%820 : $Builtin.BridgeObject):
|
|
%834 = unchecked_ref_cast %820 : $Builtin.BridgeObject to $C
|
|
%844 = ref_tail_addr [immutable] %834 : $C, $Double
|
|
%853 = load %844 : $*Double
|
|
%943 = load %844 : $*Double
|
|
cond_br undef, bb4, bb5
|
|
|
|
bb4:
|
|
br bb3(%820 : $Builtin.BridgeObject)
|
|
|
|
bb5:
|
|
%999 = tuple ()
|
|
return %999 : $()
|
|
}
|
|
|
|
class C {}
|
|
|
|
// Test a CoW mutation followed by a phi cycle.
|
|
//
|
|
// Note: FindReferenceRoot currently does not see past CoW mutation,
|
|
// but probably should.
|
|
// CHECK: @testCowMutationPhi
|
|
// CHECK: ###For MemOp: (%{{.*}}, %{{.*}}) = begin_cow_mutation [native] %0 : $Builtin.BridgeObject
|
|
// CHECK-NEXT: ###For MemOp: %{{.*}} = load %{{.*}} : $*Double
|
|
// CHECK: Storage: Tail %{{.*}} = argument of bb3 : $Builtin.BridgeObject
|
|
// CHECK: Path: ()
|
|
// CHECK: Exact Uses {
|
|
// CHECK-NEXT: %{{.*}} = load %{{.*}} : $*Double
|
|
// CHECK-NEXT: Path: ()
|
|
// CHECK-NEXT: %{{.*}} = load %{{.*}} : $*Double
|
|
// CHECK-NEXT: Path: ()
|
|
// CHECK-NEXT: }
|
|
// CHECK: ###For MemOp: %{{.*}} = load %{{.*}} : $*Double
|
|
// CHECK: Storage: Tail %{{.*}} = argument of bb3 : $Builtin.BridgeObject
|
|
// CHECK: Path: ()
|
|
// CHECK: Exact Uses {
|
|
// CHECK-NEXT: %{{.*}} = load %{{.*}} : $*Double
|
|
// CHECK-NEXT: Path: ()
|
|
// CHECK-NEXT: %{{.*}} = load %{{.*}} : $*Double
|
|
// CHECK-NEXT: Path: ()
|
|
// CHECK-NEXT: }
|
|
sil @testCowMutationPhi : $@convention(thin) (Builtin.BridgeObject) -> () {
|
|
bb0(%0 : $Builtin.BridgeObject):
|
|
cond_br undef, bb1, bb2
|
|
|
|
bb1:
|
|
br bb3(%0 : $Builtin.BridgeObject)
|
|
|
|
bb2:
|
|
(%3, %4) = begin_cow_mutation [native] %0 : $Builtin.BridgeObject
|
|
%5 = end_cow_mutation [keep_unique] %4 : $Builtin.BridgeObject
|
|
br bb3(%5 : $Builtin.BridgeObject)
|
|
|
|
bb3(%820 : $Builtin.BridgeObject):
|
|
%834 = unchecked_ref_cast %820 : $Builtin.BridgeObject to $C
|
|
%844 = ref_tail_addr [immutable] %834 : $C, $Double
|
|
%853 = load %844 : $*Double
|
|
%943 = load %844 : $*Double
|
|
cond_br undef, bb4, bb5
|
|
|
|
bb4:
|
|
br bb3(%820 : $Builtin.BridgeObject)
|
|
|
|
bb5:
|
|
%999 = tuple ()
|
|
return %999 : $()
|
|
}
|
|
|
|
// CHECK-LABEL: @test_init_enum_addr
|
|
// CHECK: ###For MemOp: store %0 to [init] %2 : $*AnyObject
|
|
// CHECK: Stack %1 = alloc_stack $Optional<AnyObject>
|
|
// CHECK: Path: ()
|
|
// CHECK: Exact Uses {
|
|
// CHECK-NEXT: store %0 to [init] %{{.*}} : $*AnyObject
|
|
// CHECK-NEXT: Path: ()
|
|
// CHECK-NEXT: inject_enum_addr %1 : $*Optional<AnyObject>, #Optional.some!enumelt
|
|
// CHECK-NEXT: Path: ()
|
|
// CHECK-NEXT: %{{.*}} = load [copy] %1 : $*Optional<AnyObject>
|
|
// CHECK-NEXT: Path: ()
|
|
// CHECK-NEXT: dealloc_stack %1 : $*Optional<AnyObject>
|
|
// CHECK-NEXT: Path: ()
|
|
// CHECK-NEXT: }
|
|
// CHECK: ###For MemOp: inject_enum_addr %1 : $*Optional<AnyObject>, #Optional.some!enumelt
|
|
// CHECK: Stack %1 = alloc_stack $Optional<AnyObject>
|
|
// CHECK: Path: ()
|
|
// CHECK: Exact Uses {
|
|
// CHECK-NEXT: store %0 to [init] %{{.*}} : $*AnyObject
|
|
// CHECK-NEXT: Path: ()
|
|
// CHECK-NEXT: inject_enum_addr %1 : $*Optional<AnyObject>, #Optional.some!enumelt
|
|
// CHECK-NEXT: Path: ()
|
|
// CHECK-NEXT: %{{.*}} = load [copy] %1 : $*Optional<AnyObject>
|
|
// CHECK-NEXT: Path: ()
|
|
// CHECK-NEXT: dealloc_stack %1 : $*Optional<AnyObject>
|
|
// CHECK-NEXT: Path: ()
|
|
// CHECK-NEXT: }
|
|
// CHECK: ###For MemOp: %5 = load [copy] %1 : $*Optional<AnyObject>
|
|
// CHECK: Stack %1 = alloc_stack $Optional<AnyObject>
|
|
// CHECK: Path: ()
|
|
// CHECK: Exact Uses {
|
|
// CHECK-NEXT: store %0 to [init] %{{.*}} : $*AnyObject
|
|
// CHECK-NEXT: Path: ()
|
|
// CHECK-NEXT: inject_enum_addr %1 : $*Optional<AnyObject>, #Optional.some!enumelt
|
|
// CHECK-NEXT: Path: ()
|
|
// CHECK-NEXT: %{{.*}} = load [copy] %1 : $*Optional<AnyObject>
|
|
// CHECK-NEXT: Path: ()
|
|
// CHECK-NEXT: dealloc_stack %1 : $*Optional<AnyObject>
|
|
// CHECK-NEXT: Path: ()
|
|
// CHECK-NEXT: }
|
|
sil [ossa] @test_init_enum_addr : $@convention(thin) (@owned AnyObject) -> @owned Optional<AnyObject> {
|
|
bb0(%0 : @owned $AnyObject):
|
|
%1 = alloc_stack $Optional<AnyObject>
|
|
%2 = init_enum_data_addr %1 : $*Optional<AnyObject>, #Optional.some!enumelt
|
|
store %0 to [init] %2 : $*AnyObject
|
|
inject_enum_addr %1 : $*Optional<AnyObject>, #Optional.some!enumelt
|
|
%5 = load [copy] %1 : $*Optional<AnyObject>
|
|
dealloc_stack %1 : $*Optional<AnyObject>
|
|
return %5 : $Optional<AnyObject>
|
|
}
|