Files
swift-mirror/test/SILOptimizer/predictable_memopt_dependence.sil
2026-01-16 18:08:13 +00:00

608 lines
27 KiB
Plaintext

// RUN: %target-sil-opt -sil-print-types -enable-sil-verify-all %s -mandatory-redundant-load-elimination -predictable-deadalloc-elim | %FileCheck %s
sil_stage raw
import Swift
import Builtin
class SomeClass {}
struct SomeClassPair {
var x: SomeClass
var y: SomeClass
}
///////////////////////////////
// mark_dependence semantics //
///////////////////////////////
// Test mark_dependence base: box promotion
//
// Eliminate the load [copy]
// Update the mark_dependence base to the available value.
//
// TODO: box promotion is unsupported:
// - Eliminate the allocation and store
// - Update the mark_dependence base to the available value
//
// CHECK-LABEL: sil [ossa] @markdep_base_promote_box : $@convention(thin) (@owned SomeClass, UnsafeMutablePointer<Int>) -> @owned SomeClass {
// CHECK: [[ST:%.*]] = copy_value %0 : $SomeClass
// CHECK: store %0 to [init]
// CHECK: return [[ST]]
// CHECK-LABEL: } // end sil function 'markdep_base_promote_box'
sil [ossa] @markdep_base_promote_box : $@convention(thin) (@owned SomeClass, UnsafeMutablePointer<Int>) -> @owned SomeClass {
bb0(%0 : @owned $SomeClass, %1 : $UnsafeMutablePointer<Int>):
%2 = alloc_box $<τ_0_0> { var τ_0_0 } <SomeClass>
%2a = project_box %2 : $<τ_0_0> { var τ_0_0 } <SomeClass>, 0
store %0 to [init] %2a : $*SomeClass
%md = mark_dependence %1 : $UnsafeMutablePointer<Int> on %2 : $<τ_0_0> { var τ_0_0 } <SomeClass>
%ld = load [copy] %2a : $*SomeClass
%rawp = struct_extract %md : $UnsafeMutablePointer<Int>, #UnsafeMutablePointer._rawValue
%addr = pointer_to_address %rawp : $Builtin.RawPointer to [strict] $*Int
%acc = begin_access [read] [unsafe] %addr : $*Int
%ldp = load [trivial] %acc : $*Int
end_access %acc : $*Int
destroy_value %2 : $<τ_0_0> { var τ_0_0 } <SomeClass>
return %ld : $SomeClass
}
// Test mark_dependence base: stack promotion
//
// Eliminate the allocation, store, and load.
// Update the mark_dependence base to the available value.
//
// CHECK-LABEL: sil [ossa] @markdep_base_promote_stack : $@convention(thin) (@owned SomeClass, UnsafeMutablePointer<Int>) -> @owned SomeClass {
// CHECK: [[ST:%.*]] = copy_value %0 : $SomeClass
// CHECK: [[MD:%.*]] = mark_dependence %1 : $UnsafeMutablePointer<Int> on %0 : $SomeClass
// CHECK: struct_extract [[MD]] : $UnsafeMutablePointer<Int>, #UnsafeMutablePointer._rawValue
// CHECK: return [[ST]] : $SomeClass
// CHECK-LABEL: } // end sil function 'markdep_base_promote_stack'
sil [ossa] @markdep_base_promote_stack : $@convention(thin) (@owned SomeClass, UnsafeMutablePointer<Int>) -> @owned SomeClass {
bb0(%0 : @owned $SomeClass, %1 : $UnsafeMutablePointer<Int>):
%2 = alloc_stack $SomeClass
store %0 to [init] %2 : $*SomeClass
%md = mark_dependence %1 : $UnsafeMutablePointer<Int> on %2 : $*SomeClass
%ld = load [copy] %2 : $*SomeClass
%rawp = struct_extract %md : $UnsafeMutablePointer<Int>, #UnsafeMutablePointer._rawValue
%addr = pointer_to_address %rawp : $Builtin.RawPointer to [strict] $*Int
%acc = begin_access [read] [unsafe] %addr : $*Int
%ldp = load [trivial] %acc : $*Int
end_access %acc : $*Int
destroy_addr %2 : $*SomeClass
dealloc_stack %2 : $*SomeClass
return %ld : $SomeClass
}
// Test mark_dependence_addr base: stack promotion
//
// Eliminate the allocation, store, and load.
// Update the mark_dependence_addr base to the available value.
//
// CHECK-LABEL: sil [ossa] @markdep_addr_base_promote_stack : $@convention(thin) (@owned SomeClass, UnsafeMutablePointer<Int>) -> @owned SomeClass {
// CHECK: [[ST:%.*]] = copy_value %0 : $SomeClass
// CHECK: mark_dependence_addr %1 : $UnsafeMutablePointer<Int> on %0 : $SomeClass
// CHECK: struct_extract %1 : $UnsafeMutablePointer<Int>, #UnsafeMutablePointer._rawValue
// CHECK: return [[ST]] : $SomeClass
// CHECK-LABEL: } // end sil function 'markdep_addr_base_promote_stack'
sil [ossa] @markdep_addr_base_promote_stack : $@convention(thin) (@owned SomeClass, UnsafeMutablePointer<Int>) -> @owned SomeClass {
bb0(%0 : @owned $SomeClass, %1 : $UnsafeMutablePointer<Int>):
%2 = alloc_stack $SomeClass
store %0 to [init] %2 : $*SomeClass
mark_dependence_addr %1 : $UnsafeMutablePointer<Int> on %2 : $*SomeClass
%ld = load [copy] %2 : $*SomeClass
%rawp = struct_extract %1 : $UnsafeMutablePointer<Int>, #UnsafeMutablePointer._rawValue
%addr = pointer_to_address %rawp : $Builtin.RawPointer to [strict] $*Int
%acc = begin_access [read] [unsafe] %addr : $*Int
%ldp = load [trivial] %acc : $*Int
end_access %acc : $*Int
destroy_addr %2 : $*SomeClass
dealloc_stack %2 : $*SomeClass
return %ld : $SomeClass
}
// Test mark_dependence base: stack promotion on trivial allocation
//
// CHECK-LABEL: sil [ossa] @markdep_trivial_base_promote_stack : $@convention(thin) (Int, UnsafeMutablePointer<Int>) -> Int {
// CHECK: bb0(%0 : $Int, %1 : $UnsafeMutablePointer<Int>):
// CHECK-NOT: alloc_stack
// CHECK: mark_dependence %1 : $UnsafeMutablePointer<Int> on %0 : $Int
// CHECK: return %0 : $Int
// CHECK: } // end sil function 'markdep_trivial_base_promote_stack'
sil [ossa] @markdep_trivial_base_promote_stack : $@convention(thin) (Int, UnsafeMutablePointer<Int>) -> Int {
bb0(%0 : $Int, %1 : $UnsafeMutablePointer<Int>):
%2 = alloc_stack $Int
store %0 to [trivial] %2 : $*Int
%md = mark_dependence %1 : $UnsafeMutablePointer<Int> on %2 : $*Int
%ld = load [trivial] %2 : $*Int
%rawp = struct_extract %md : $UnsafeMutablePointer<Int>, #UnsafeMutablePointer._rawValue
%addr = pointer_to_address %rawp : $Builtin.RawPointer to [strict] $*Int
%acc = begin_access [read] [unsafe] %addr : $*Int
%ldp = load [trivial] %acc : $*Int
end_access %acc : $*Int
destroy_addr %2 : $*Int
dealloc_stack %2 : $*Int
return %ld : $Int
}
// Test mark_dependence source: box promotion
//
// Eliminate the load and promote the mark_dependence.
//
// Update the mark_dependence source to the available value.
//
// TODO: box promotion is unsupported:
// - Eliminate the allocation and store
//
// CHECK-LABEL: sil [ossa] @markdep_source_promote_box : $@convention(thin) (@owned SomeClass, @guaranteed SomeClass) -> @owned SomeClass {
// CHECK: [[CP1:%.*]] = copy_value %0 : $SomeClass
// CHECK: store %0 to [init]
// CHECK: [[MD:%.*]] = mark_dependence [[CP1]] : $SomeClass on %1 : $SomeClass
// CHECK: destroy_value %{{.*}} : $<τ_0_0> { var τ_0_0 } <SomeClass>
// CHECK: return [[MD]] : $SomeClass
// CHECK-LABEL: } // end sil function 'markdep_source_promote_box'
sil [ossa] @markdep_source_promote_box : $@convention(thin) (@owned SomeClass, @guaranteed SomeClass) -> @owned SomeClass {
bb0(%0 : @owned $SomeClass, %1 : @guaranteed $SomeClass):
%2 = alloc_box $<τ_0_0> { var τ_0_0 } <SomeClass>
%2a = project_box %2 : $<τ_0_0> { var τ_0_0 } <SomeClass>, 0
store %0 to [init] %2a : $*SomeClass
%md = mark_dependence %2a : $*SomeClass on %1 : $SomeClass
%ld = load [copy] %md : $*SomeClass
destroy_value %2 : $<τ_0_0> { var τ_0_0 } <SomeClass>
return %ld : $SomeClass
}
// Test mark_dependence source: stack promotion
//
// Eliminate the allocation, store, and load.
// Update the mark_dependence source to the available value.
//
// CHECK-LABEL: sil [ossa] @markdep_source_promote_stack : $@convention(thin) (@owned SomeClass, @guaranteed SomeClass) -> @owned SomeClass {
// CHECK: [[CP1:%.*]] = copy_value %0 : $SomeClass
// CHECK: [[MD_OLD:%.*]] = mark_dependence %0 : $SomeClass on %1 : $SomeClass
// CHECK: [[MD:%.*]] = mark_dependence [[CP1]] : $SomeClass on %1 : $SomeClass
// CHECK: destroy_value [[MD_OLD]] : $SomeClass
// CHECK: return [[MD]] : $SomeClass
// CHECK-LABEL: } // end sil function 'markdep_source_promote_stack'
sil [ossa] @markdep_source_promote_stack: $@convention(thin) (@owned SomeClass, @guaranteed SomeClass) -> @owned SomeClass {
bb0(%0 : @owned $SomeClass, %1 : @guaranteed $SomeClass):
%2 = alloc_stack $SomeClass
store %0 to [init] %2 : $*SomeClass
%md = mark_dependence %2 : $*SomeClass on %1 : $SomeClass
%ld = load [copy] %md : $*SomeClass
destroy_addr %2 : $*SomeClass
dealloc_stack %2 : $*SomeClass
return %ld : $SomeClass
}
// Test mark_dependence source: stack promotion on trivial allocation.
//
// CHECK-LABEL: sil [ossa] @markdep_trivial_source_promote_stack : $@convention(thin) (Int, @guaranteed SomeClass) -> Int {
// CHECK: bb0(%0 : $Int, %1 : @guaranteed $SomeClass):
// CHECK-NEXT: [[MD:%.*]] = mark_dependence %0 : $Int on %1 : $SomeClass
// CHECK-NEXT: return [[MD]] : $Int
// CHECK-LABEL: } // end sil function 'markdep_trivial_source_promote_stack'
sil [ossa] @markdep_trivial_source_promote_stack: $@convention(thin) (Int, @guaranteed SomeClass) -> Int {
bb0(%0 : $Int, %1 : @guaranteed $SomeClass):
%2 = alloc_stack $Int
store %0 to [trivial] %2 : $*Int
%md = mark_dependence %2 : $*Int on %1 : $SomeClass
%ld = load [trivial] %md : $*Int
destroy_addr %2 : $*Int
dealloc_stack %2 : $*Int
return %ld : $Int
}
// Test mark_dependence base: dead box
//
// Currently, no optimization here.
//
// TODO: box promotion is unsupported:
// - Eliminate the allocation and store.
// - Promote the mark_dependence base.
// - The stored value must not be destroyed until after the end_access
//
// CHECK-LABEL: sil [ossa] @markdep_base_dead_box : $@convention(thin) (@owned SomeClass, UnsafeMutablePointer<Int>) -> Int {
// CHECK: store %0 to [init] %{{.*}} : $*SomeClass
// CHECK: %{{.*}} = mark_dependence %1 : $UnsafeMutablePointer<Int> on %{{.*}} : $<τ_0_0> { var τ_0_0 } <SomeClass>
// CHECK: end_access %{{.*}} : $*Int
// CHECK: destroy_value %{{.*}} : $<τ_0_0> { var τ_0_0 } <SomeClass>
// CHECK-LABEL: } // end sil function 'markdep_base_dead_box'
sil [ossa] @markdep_base_dead_box : $@convention(thin) (@owned SomeClass, UnsafeMutablePointer<Int>) -> Int {
bb0(%0 : @owned $SomeClass, %1 : $UnsafeMutablePointer<Int>):
%2 = alloc_box $<τ_0_0> { var τ_0_0 } <SomeClass>
%2a = project_box %2 : $<τ_0_0> { var τ_0_0 } <SomeClass>, 0
store %0 to [init] %2a : $*SomeClass
%md = mark_dependence %1 : $UnsafeMutablePointer<Int> on %2 : $<τ_0_0> { var τ_0_0 } <SomeClass>
%rawp = struct_extract %md : $UnsafeMutablePointer<Int>, #UnsafeMutablePointer._rawValue
%addr = pointer_to_address %rawp : $Builtin.RawPointer to [strict] $*Int
%acc = begin_access [read] [unsafe] %addr : $*Int
%ldp = load [trivial] %acc : $*Int
end_access %acc : $*Int
destroy_value %2 : $<τ_0_0> { var τ_0_0 } <SomeClass>
return %ldp : $Int
}
// Test mark_dependence base: dead stack
//
// Eliminate the allocation and store.
// Update the mark_dependence to the stored value.
// The stored value must not be destroyed until after the end_access
//
// CHECK-LABEL: sil [ossa] @markdep_base_dead_stack : $@convention(thin) (@owned SomeClass, UnsafeMutablePointer<Int>) -> Int {
// CHECK-NOT: alloc_stack
// CHECK-NOT: store
// CHECK: mark_dependence %1 : $UnsafeMutablePointer<Int> on %0 : $SomeClass
// CHECK: end_access %{{.*}} : $*Int
// CHECK: destroy_value %0 : $SomeClass
// CHECK-LABEL: } // end sil function 'markdep_base_dead_stack'
sil [ossa] @markdep_base_dead_stack : $@convention(thin) (@owned SomeClass, UnsafeMutablePointer<Int>) -> Int {
bb0(%0 : @owned $SomeClass, %1 : $UnsafeMutablePointer<Int>):
%2 = alloc_stack $SomeClass
store %0 to [init] %2 : $*SomeClass
%md = mark_dependence %1 : $UnsafeMutablePointer<Int> on %2 : $*SomeClass
%rawp = struct_extract %md : $UnsafeMutablePointer<Int>, #UnsafeMutablePointer._rawValue
%addr = pointer_to_address %rawp : $Builtin.RawPointer to [strict] $*Int
%acc = begin_access [read] [unsafe] %addr : $*Int
%ldp = load [trivial] %acc : $*Int
end_access %acc : $*Int
destroy_addr %2 : $*SomeClass
dealloc_stack %2: $*SomeClass
return %ldp : $Int
}
// Test mark_dependence_addr base: dead stack
//
// Eliminate the allocation and store.
// Update the mark_dependence_addr to the stored value.
// The stored value must not be destroyed until after the end_access
//
// CHECK-LABEL: sil [ossa] @markdep_addr_base_dead_stack : $@convention(thin) (@owned SomeClass, UnsafeMutablePointer<Int>) -> Int {
// CHECK-NOT: alloc_stack
// CHECK-NOT: store
// CHECK: mark_dependence_addr %1 : $UnsafeMutablePointer<Int> on %0 : $SomeClass
// CHECK: end_access %{{.*}} : $*Int
// CHECK: destroy_value %0 : $SomeClass
// CHECK-LABEL: } // end sil function 'markdep_addr_base_dead_stack'
sil [ossa] @markdep_addr_base_dead_stack : $@convention(thin) (@owned SomeClass, UnsafeMutablePointer<Int>) -> Int {
bb0(%0 : @owned $SomeClass, %1 : $UnsafeMutablePointer<Int>):
%2 = alloc_stack $SomeClass
store %0 to [init] %2 : $*SomeClass
mark_dependence_addr %1 : $UnsafeMutablePointer<Int> on %2 : $*SomeClass
%rawp = struct_extract %1 : $UnsafeMutablePointer<Int>, #UnsafeMutablePointer._rawValue
%addr = pointer_to_address %rawp : $Builtin.RawPointer to [strict] $*Int
%acc = begin_access [read] [unsafe] %addr : $*Int
%ldp = load [trivial] %acc : $*Int
end_access %acc : $*Int
destroy_addr %2 : $*SomeClass
dealloc_stack %2: $*SomeClass
return %ldp : $Int
}
// Test mark_dependence source: dead box
//
// Promote the mark_dependence and eliminate the load.
//
// TODO: box promotion is unsupported:
// - Eliminate the allocation and store.
// - The stored value must not be destroyed until after the end_access
//
// CHECK-LABEL: sil [ossa] @markdep_source_dead_box : $@convention(thin) (@owned SomeClass, @guaranteed SomeClass) -> () {
// CHECK: [[CP1:%.*]] = copy_value %0 : $SomeClass
// CHECK: store %0 to [init]
// CHECK: [[MD:%.*]] = mark_dependence [[CP1]] : $SomeClass on %1 : $SomeClass
// CHECK: destroy_value %{{.*}} : $<τ_0_0> { var τ_0_0 } <SomeClass>
// CHECK: destroy_value [[MD]] : $SomeClass
// CHECK-LABEL: } // end sil function 'markdep_source_dead_box'
sil [ossa] @markdep_source_dead_box : $@convention(thin) (@owned SomeClass, @guaranteed SomeClass) -> () {
bb0(%0 : @owned $SomeClass, %1 : @guaranteed $SomeClass):
%2 = alloc_box $<τ_0_0> { var τ_0_0 } <SomeClass>
%2a = project_box %2 : $<τ_0_0> { var τ_0_0 } <SomeClass>, 0
store %0 to [init] %2a : $*SomeClass
%md = mark_dependence %2a : $*SomeClass on %1 : $SomeClass
%ld = load [copy] %md : $*SomeClass
destroy_value %2 : $<τ_0_0> { var τ_0_0 } <SomeClass>
destroy_value %ld : $SomeClass
%99 = tuple ()
return %99 : $()
}
// Test mark_dependence source: dead stack
//
// Eliminate the allocation, store, mark_dependence, and load.
//
// CHECK-LABEL: sil [ossa] @markdep_source_dead_stack : $@convention(thin) (@owned SomeClass, @guaranteed SomeClass) -> () {
// CHECK: bb0(%0 : @owned $SomeClass, %1 : @guaranteed $SomeClass):
// CHECK-NEXT: [[CP1:%.*]] = copy_value %0 : $SomeClass
// CHECK-NEXT: [[MD_OLD:%.*]] = mark_dependence %0 : $SomeClass on %1 : $SomeClass
// CHECK-NEXT: [[MD:%.*]] = mark_dependence [[CP1]] : $SomeClass on %1 : $SomeClass
// CHECK-NEXT: destroy_value [[MD_OLD]] : $SomeClass
// CHECK-NEXT: destroy_value [[MD]] : $SomeClass
// CHECK-LABEL: } // end sil function 'markdep_source_dead_stack'
sil [ossa] @markdep_source_dead_stack: $@convention(thin) (@owned SomeClass, @guaranteed SomeClass) -> () {
bb0(%0 : @owned $SomeClass, %1 : @guaranteed $SomeClass):
%2 = alloc_stack $SomeClass
store %0 to [init] %2 : $*SomeClass
%md = mark_dependence %2 : $*SomeClass on %1 : $SomeClass
%ld = load [copy] %md : $*SomeClass
destroy_addr %2 : $*SomeClass
dealloc_stack %2 : $*SomeClass
destroy_value %ld : $SomeClass
%99 = tuple ()
return %99 : $()
}
// Test mark_dependence source: fully available before a partial init
//
// Elimimate the alocation and stores.
//
// CHECK-LABEL: sil [ossa] @markdep_source_before_partial : $@convention(thin) (@owned SomeClass, @owned SomeClass, @guaranteed SomeClass) -> () {
// CHECK: bb0(%0 : @owned $SomeClass, %1 : @owned $SomeClass, %2 : @guaranteed $SomeClass):
// CHECK-NEXT: struct $SomeClassPair (%0 : $SomeClass, %1 : $SomeClass)
// CHECK-NEXT: destroy_value
// CHECK-LABEL: } // end sil function 'markdep_source_before_partial'
sil [ossa] @markdep_source_before_partial : $@convention(thin) (@owned SomeClass, @owned SomeClass, @guaranteed SomeClass) -> () {
bb0(%0 : @owned $SomeClass, %1 : @owned $SomeClass, %2 : @guaranteed $SomeClass):
%stack = alloc_stack $SomeClassPair
%md = mark_dependence %stack : $*SomeClassPair on %2 : $SomeClass
%stack0 = struct_element_addr %stack : $*SomeClassPair, #SomeClassPair.x
%stack1 = struct_element_addr %stack : $*SomeClassPair, #SomeClassPair.y
store %0 to [init] %stack0 : $*SomeClass
store %1 to [init] %stack1 : $*SomeClass
destroy_addr %stack : $*SomeClassPair
dealloc_stack %stack : $*SomeClassPair
%9999 = tuple()
return %9999 : $()
}
// Test mark_dependence source: partially available before init
//
// CHECK-LABEL: sil [ossa] @markdep_source_partial_preinit : $@convention(thin) (@owned SomeClass, @owned SomeClass, @guaranteed SomeClass) -> () {
// CHECK: bb0(%0 : @owned $SomeClass, %1 : @owned $SomeClass, %2 : @guaranteed $SomeClass):
// CHECK-NEXT: [[PAIR:%.*]] = struct $SomeClassPair (%0 : $SomeClass, %1 : $SomeClass)
// CHECK-NEXT: destroy_value [[PAIR]] : $SomeClassPair
// CHECK-NOT: destroy_addr
// CHECK-LABEL: } // end sil function 'markdep_source_partial_preinit'
sil [ossa] @markdep_source_partial_preinit : $@convention(thin) (@owned SomeClass, @owned SomeClass, @guaranteed SomeClass) -> () {
bb0(%0 : @owned $SomeClass, %1 : @owned $SomeClass, %2 : @guaranteed $SomeClass):
%stack = alloc_stack $SomeClassPair
%stack0 = struct_element_addr %stack : $*SomeClassPair, #SomeClassPair.x
%stack1 = struct_element_addr %stack : $*SomeClassPair, #SomeClassPair.y
%md0 = mark_dependence %stack0 : $*SomeClass on %2 : $SomeClass
store %0 to [init] %md0 : $*SomeClass
store %1 to [init] %stack1 : $*SomeClass
destroy_addr %stack : $*SomeClassPair
dealloc_stack %stack : $*SomeClassPair
%9999 = tuple()
return %9999 : $()
}
// Test mark_dependence source: partially available after init
//
// TODO: Partially available values are not handled by dependent dataflow.
//
// CHECK-LABEL: sil [ossa] @markdep_source_partial_postinit : $@convention(thin) (@owned SomeClass, @owned SomeClass, @guaranteed SomeClass) -> () {
// CHECK: [[STK:%.*]] = alloc_stack $SomeClassPair
// CHECK: [[STK1:%.*]] = struct_element_addr [[STK]] : $*SomeClassPair, #SomeClassPair.y
// CHECK: store %1 to [init] [[STK1]] : $*SomeClass
// CHECK: [[MD:%.*]] = mark_dependence [[STK1]] : $*SomeClass on %2 : $SomeClass
// CHECK: destroy_addr [[STK]] : $*SomeClassPair
// CHECK-LABEL: } // end sil function 'markdep_source_partial_postinit'
sil [ossa] @markdep_source_partial_postinit : $@convention(thin) (@owned SomeClass, @owned SomeClass, @guaranteed SomeClass) -> () {
bb0(%0 : @owned $SomeClass, %1 : @owned $SomeClass, %2 : @guaranteed $SomeClass):
%stack = alloc_stack $SomeClassPair
%stack0 = struct_element_addr %stack : $*SomeClassPair, #SomeClassPair.x
%stack1 = struct_element_addr %stack : $*SomeClassPair, #SomeClassPair.y
store %0 to [init] %stack0 : $*SomeClass
store %1 to [init] %stack1 : $*SomeClass
%md1 = mark_dependence %stack1 : $*SomeClass on %2 : $SomeClass
destroy_addr %stack : $*SomeClassPair
dealloc_stack %stack : $*SomeClassPair
%9999 = tuple()
return %9999 : $()
}
// Test mark_dependence source: load from dependent phi value.
//
// CHECK-LABEL: sil [ossa] @markdep_source_load_phi :
// CHECK: bb0(%0 : @guaranteed $SomeClass):
// CHECK-NEXT: [[CP0:%.*]] = copy_value %0 : $SomeClass
// CHECK: bb1:
// CHECK-NEXT: [[CP1:%.*]] = copy_value [[CP0]]
// CHECK-NEXT: br bb3([[CP1]] : $SomeClass)
// CHECK: bb2:
// CHECK-NEXT: [[CP2:%.*]] = copy_value [[CP0]]
// CHECK-NEXT: br bb3([[CP2]] : $SomeClass)
// CHECK: bb3([[PHI:%.*]] : @owned $SomeClass):
// CHECK: [[MD:%.*]] = mark_dependence [[PHI]] : $SomeClass on %0 : $SomeClass
// CHECK: return [[MD]]
// CHECK-LABEL: } // end sil function 'markdep_source_load_phi'
sil [ossa] @markdep_source_load_phi : $@convention(thin) (@guaranteed SomeClass) -> @owned SomeClass {
bb0(%0 : @guaranteed $SomeClass):
%1 = alloc_stack $SomeClass
%copy = copy_value %0 : $SomeClass
cond_br undef, bb1, bb2
bb1:
store %copy to [init] %1 : $*SomeClass
br bb3
bb2:
store %copy to [init] %1 : $*SomeClass
br bb3
bb3:
%md = mark_dependence %1 : $*SomeClass on %0 : $SomeClass
%ld = load [copy] %md : $*SomeClass
destroy_addr %1 : $*SomeClass
dealloc_stack %1 : $*SomeClass
return %ld : $SomeClass
}
// Test mark_dependence source: dead dependent phi.
//
// Eliminate the loads, allocation, and stores.
//
// CHECK-LABEL: sil [ossa] @markdep_source_dead_phi : $@convention(thin) (@guaranteed SomeClass) -> () {
// CHECK: bb0(%0 : @guaranteed $SomeClass):
// CHECK-NOT: alloc_stack
// CHECK: cond_br undef, bb1, bb2
// CHECK: bb1:
// CHECK-NOT: store
// CHECK-NOT: load
// CHECK: bb2:
// CHECK-NOT: store
// CHECK-NOT: load
// CHECK: bb3:
// CHECK-NEXT: [[MD:%.*]] = mark_dependence %1 : $SomeClass on %0 : $SomeClass
// CHECK-NEXT: destroy_value [[MD]] : $SomeClass
// CHECK-LABEL: } // end sil function 'markdep_source_dead_phi'
sil [ossa] @markdep_source_dead_phi : $@convention(thin) (@guaranteed SomeClass) -> () {
bb0(%0 : @guaranteed $SomeClass):
%1 = alloc_stack $SomeClass
%copy = copy_value %0 : $SomeClass
cond_br undef, bb1, bb2
bb1:
store %copy to [init] %1 : $*SomeClass
%ld1 = load [copy] %1 : $*SomeClass
destroy_value %ld1 : $SomeClass
br bb3
bb2:
store %copy to [init] %1 : $*SomeClass
%ld2 = load [copy] %1 : $*SomeClass
destroy_value %ld2 : $SomeClass
br bb3
bb3:
%md = mark_dependence %1 : $*SomeClass on %0 : $SomeClass
destroy_addr %1 : $*SomeClass
dealloc_stack %1 : $*SomeClass
%9999 = tuple()
return %9999 : $()
}
// Test mark_dependence source: conflicting available values.
//
// No optimization.
//
// TODO: the current algorithm can't handle this. With the current
// approach, we would need to run a completely separate dataflow for
// each mark_dependence.
// CHECK-LABEL: sil [ossa] @markdep_source_conflicting_load : $@convention(thin) (@guaranteed SomeClass) -> () {
// CHECK: bb0(%0 : @guaranteed $SomeClass):
// CHECK: %1 = alloc_stack $SomeClass
// CHECK: store %{{.*}} to [init] %1 : $*SomeClass
// CHECK: bb1:
// CHECK: %{{.*}} = mark_dependence %1 : $*SomeClass on %0 : $SomeClass
// CHECK: bb2:
// CHECK: load [take] %1 : $*SomeClass
// CHECK: store %{{.*}} to [init] %1 : $*SomeClass
// CHECK: bb3:
// CHECK: load [copy] %1 : $*SomeClass
// CHECK: destroy_addr %1 : $*SomeClass
// CHECK: dealloc_stack %1 : $*SomeClass
// CHECK-LABEL: } // end sil function 'markdep_source_conflicting_load'
sil [ossa] @markdep_source_conflicting_load : $@convention(thin) (@guaranteed SomeClass) -> () {
bb0(%0 : @guaranteed $SomeClass):
%1 = alloc_stack $SomeClass
%copy0 = copy_value %0 : $SomeClass
store %copy0 to [init] %1 : $*SomeClass
cond_br undef, bb1, bb2
bb1:
%md = mark_dependence %1 : $*SomeClass on %0 : $SomeClass
br bb3
bb2:
%ld2 = load [take] %1 : $*SomeClass
destroy_value %ld2 : $SomeClass
%copy2 = copy_value %0 : $SomeClass
store %copy2 to [init] %1 : $*SomeClass
br bb3
bb3:
%ld3 = load [copy] %1 : $*SomeClass
destroy_value %ld3 : $SomeClass
destroy_addr %1 : $*SomeClass
dealloc_stack %1 : $*SomeClass
%9999 = tuple()
return %9999 : $()
}
// Test mark_dependence source with a mark_dependence base use.
//
// Promote both mark_dependence instructions during dead allocatio eliminiation,
// reusing the promoted instruction as the available value.
//
// CHECK-LABEL: sil [ossa] @markdep_source_base_dead_stack : $@convention(thin) (@owned SomeClass, @guaranteed SomeClass, UnsafeMutablePointer<Int>) -> Int {
// CHECK: bb0(%0 : @owned $SomeClass, %1 : @guaranteed $SomeClass, %2 : $UnsafeMutablePointer<Int>):
// CHECK-NEXT: [[MD1:%.*]] = mark_dependence %0 : $SomeClass on %1 : $SomeClass
// CHECK-NEXT: [[MD2:%.*]] = mark_dependence %2 : $UnsafeMutablePointer<Int> on [[MD1]] : $SomeClass
// CHECK-NOT: mark_dependence
// CHECK: struct_extract [[MD2]] : $UnsafeMutablePointer<Int>, #UnsafeMutablePointer._rawValue
// CHECK: destroy_value [[MD1]] : $SomeClass
// CHECK-LABEL: } // end sil function 'markdep_source_base_dead_stack'
sil [ossa] @markdep_source_base_dead_stack : $@convention(thin) (@owned SomeClass, @guaranteed SomeClass, UnsafeMutablePointer<Int>) -> Int {
bb0(%0 : @owned $SomeClass, %1 : @guaranteed $SomeClass, %2 : $UnsafeMutablePointer<Int>):
%stk = alloc_stack $SomeClass
store %0 to [init] %stk : $*SomeClass
%md1 = mark_dependence %stk : $*SomeClass on %1 : $SomeClass
%md2 = mark_dependence %2 : $UnsafeMutablePointer<Int> on %md1 : $*SomeClass
%rawp = struct_extract %md2 : $UnsafeMutablePointer<Int>, #UnsafeMutablePointer._rawValue
%addr = pointer_to_address %rawp : $Builtin.RawPointer to [strict] $*Int
%acc = begin_access [read] [unsafe] %addr : $*Int
%ldp = load [trivial] %acc : $*Int
end_access %acc : $*Int
destroy_addr %md1 : $*SomeClass
dealloc_stack %stk: $*SomeClass
return %ldp : $Int
}
// Test mark_dependence source with a load [take] use.
//
// Promote the mark_dependence and eliminate the load.
//
// CHECK-LABEL: sil [ossa] @markdep_source_load_take : $@convention(thin) (@owned SomeClass, @guaranteed SomeClass) -> @owned SomeClass {
// CHECK: bb0(%0 : @owned $SomeClass, %1 : @guaranteed $SomeClass):
// CHECK-NEXT: [[MD:%.*]] = mark_dependence %0 : $SomeClass on %1 : $SomeClass
// CHECK-NEXT: return [[MD]] : $SomeClass
// CHECK-LABEL: } // end sil function 'markdep_source_load_take'
sil [ossa] @markdep_source_load_take : $@convention(thin) (@owned SomeClass, @guaranteed SomeClass) -> @owned SomeClass {
bb0(%0 : @owned $SomeClass, %1 : @guaranteed $SomeClass):
%stk = alloc_stack $SomeClass
store %0 to [init] %stk : $*SomeClass
%md1 = mark_dependence %stk : $*SomeClass on %1 : $SomeClass
%ld = load [take] %md1 : $*SomeClass
dealloc_stack %stk: $*SomeClass
return %ld : $SomeClass
}
// Test a chain of mark_dependence that are promoted during dataflow,
// but need to be deleted after a conflict is detected.
//
// CHECK-LABEL: sil [ossa] @markdep_source_chain_cleanup : $@convention(thin) (@owned SomeClass, @owned SomeClass, @guaranteed SomeClass) -> () {
// CHECK: bb0(%0 : @owned $SomeClass, %1 : @owned $SomeClass, %2 : @guaranteed $SomeClass):
// CHECK: [[STK:%.*]] = alloc_stack $SomeClassPair
// CHECK: [[Y:%.*]] = struct_element_addr [[STK]] : $*SomeClassPair, #SomeClassPair.y
// CHECK: store %1 to [init] [[Y]] : $*SomeClass
// CHECK-NOT: mark_dependence %{{.*}} : $SomeClass
// CHECK: [[MD1:%.*]] = mark_dependence [[Y]] : $*SomeClass on %2 : $SomeClass
// CHECK: mark_dependence [[MD1]] : $*SomeClass on %2 : $SomeClass
// CHECK: destroy_addr [[STK]] : $*SomeClassPair
// CHECK-LABEL: } // end sil function 'markdep_source_chain_cleanup'
sil [ossa] @markdep_source_chain_cleanup : $@convention(thin) (@owned SomeClass, @owned SomeClass, @guaranteed SomeClass) -> () {
bb0(%0 : @owned $SomeClass, %1 : @owned $SomeClass, %2 : @guaranteed $SomeClass):
%stack = alloc_stack $SomeClassPair
%stack0 = struct_element_addr %stack : $*SomeClassPair, #SomeClassPair.x
%stack1 = struct_element_addr %stack : $*SomeClassPair, #SomeClassPair.y
store %0 to [init] %stack0 : $*SomeClass
store %1 to [init] %stack1 : $*SomeClass
%md1 = mark_dependence %stack1 : $*SomeClass on %2 : $SomeClass
%md2 = mark_dependence %md1 : $*SomeClass on %2 : $SomeClass
destroy_addr %stack : $*SomeClassPair
dealloc_stack %stack : $*SomeClassPair
%9999 = tuple()
return %9999 : $()
}