Skip to content

[pmo] Assigns should result in PMO not eliminating an allocation. #22018

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions lib/SILOptimizer/Mandatory/PredictableMemOpt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1287,6 +1287,9 @@ bool AllocOptimize::tryToRemoveDeadAllocation() {

switch (u.Kind) {
case PMOUseKind::Assign:
// Until we can promote the value being destroyed by the assign, we can
// not remove deallocations with such assigns.
return false;
case PMOUseKind::InitOrAssign:
break; // These don't prevent removal.
case PMOUseKind::Initialization:
Expand Down
172 changes: 172 additions & 0 deletions test/SILOptimizer/predictable_deadalloc_elim.sil
Original file line number Diff line number Diff line change
Expand Up @@ -93,3 +93,175 @@ bb0(%0 : $Builtin.NativeObject):
%9999 = tuple()
return %9999 : $()
}

//////////////////
// Assign Tests //
//////////////////

// Make sure that we do eliminate this allocation
// CHECK-LABEL: sil @simple_assign_take_trivial : $@convention(thin) (Builtin.Int32, @in Builtin.Int32) -> () {
// CHECK-NOT: alloc_stack
// CHECK: } // end sil function 'simple_assign_take_trivial'
sil @simple_assign_take_trivial : $@convention(thin) (Builtin.Int32, @in Builtin.Int32) -> () {
bb0(%0 : $Builtin.Int32, %1 : $*Builtin.Int32):
%2 = alloc_stack $Builtin.Int32
store %0 to %2 : $*Builtin.Int32
copy_addr [take] %1 to %2 : $*Builtin.Int32
dealloc_stack %2 : $*Builtin.Int32
%9999 = tuple()
return %9999 : $()
}

// In this case, we perform an init, copy. Since we do not want to lose the +1
// on the argument, we do not eliminate this (even though with time perhaps we
// could).
// CHECK-LABEL: sil @simple_init_copy : $@convention(thin) (@owned Builtin.NativeObject, @in_guaranteed Builtin.NativeObject) -> () {
// CHECK: alloc_stack
// CHECK: copy_addr
// CHECK: } // end sil function 'simple_init_copy'
sil @simple_init_copy : $@convention(thin) (@owned Builtin.NativeObject, @in_guaranteed Builtin.NativeObject) -> () {
bb0(%0 : $Builtin.NativeObject, %1 : $*Builtin.NativeObject):
%2 = alloc_stack $Builtin.NativeObject
store %0 to %2 : $*Builtin.NativeObject
destroy_addr %2 : $*Builtin.NativeObject
copy_addr %1 to [initialization] %2 : $*Builtin.NativeObject
destroy_addr %2 : $*Builtin.NativeObject
dealloc_stack %2 : $*Builtin.NativeObject
%9999 = tuple()
return %9999 : $()
}

// This we can promote successfully.
// CHECK-LABEL: sil @simple_init_take : $@convention(thin) (@owned Builtin.NativeObject, @in Builtin.NativeObject) -> () {
// CHECK: bb0([[ARG0:%.*]] : $Builtin.NativeObject, [[ARG1:%.*]] : $*Builtin.NativeObject):
// CHECK-NOT: alloc_stack
// CHECK: strong_release [[ARG0]]
// CHECK: [[ARG1_LOADED:%.*]] = load [[ARG1]]
// CHECK: strong_release [[ARG1_LOADED]]
// CHECK: } // end sil function 'simple_init_take'
sil @simple_init_take : $@convention(thin) (@owned Builtin.NativeObject, @in Builtin.NativeObject) -> () {
bb0(%0 : $Builtin.NativeObject, %1 : $*Builtin.NativeObject):
%2 = alloc_stack $Builtin.NativeObject
store %0 to %2 : $*Builtin.NativeObject
destroy_addr %2 : $*Builtin.NativeObject
copy_addr [take] %1 to [initialization] %2 : $*Builtin.NativeObject
destroy_addr %2 : $*Builtin.NativeObject
dealloc_stack %2 : $*Builtin.NativeObject
%9999 = tuple()
return %9999 : $()
}

// Since we are copying the input argument, we can not get rid of the copy_addr,
// meaning we shouldn't eliminate the allocation here.
// CHECK-LABEL: sil @simple_assign_no_take : $@convention(thin) (@owned Builtin.NativeObject, @in_guaranteed Builtin.NativeObject) -> () {
// CHECK: alloc_stack
// CHECK: copy_addr
// CHECK: } // end sil function 'simple_assign_no_take'
sil @simple_assign_no_take : $@convention(thin) (@owned Builtin.NativeObject, @in_guaranteed Builtin.NativeObject) -> () {
bb0(%0 : $Builtin.NativeObject, %1 : $*Builtin.NativeObject):
%2 = alloc_stack $Builtin.NativeObject
store %0 to %2 : $*Builtin.NativeObject
copy_addr %1 to %2 : $*Builtin.NativeObject
destroy_addr %2 : $*Builtin.NativeObject
dealloc_stack %2 : $*Builtin.NativeObject
%9999 = tuple()
return %9999 : $()
}

// If PMO understood how to promote assigns, we should be able to handle this
// case.
// CHECK-LABEL: sil @simple_assign_take : $@convention(thin) (@owned Builtin.NativeObject, @in Builtin.NativeObject) -> () {
// CHECK: alloc_stack
// CHECK: copy_addr
// CHECK: } // end sil function 'simple_assign_take'
sil @simple_assign_take : $@convention(thin) (@owned Builtin.NativeObject, @in Builtin.NativeObject) -> () {
bb0(%0 : $Builtin.NativeObject, %1 : $*Builtin.NativeObject):
%2 = alloc_stack $Builtin.NativeObject
store %0 to %2 : $*Builtin.NativeObject
copy_addr [take] %1 to %2 : $*Builtin.NativeObject
destroy_addr %2 : $*Builtin.NativeObject
dealloc_stack %2 : $*Builtin.NativeObject
%9999 = tuple()
return %9999 : $()
}

// CHECK-LABEL: sil @simple_diamond_without_assign : $@convention(thin) (@owned Builtin.NativeObject) -> () {
// CHECK: bb0([[ARG:%.*]] :
// CHECK-NOT: alloc_stack
// CHECK-NOT: store
// CHECK: bb3:
// CHECK-NEXT: strong_release [[ARG]]
// CHECK: } // end sil function 'simple_diamond_without_assign'
sil @simple_diamond_without_assign : $@convention(thin) (@owned Builtin.NativeObject) -> () {
bb0(%0 : $Builtin.NativeObject):
%1 = alloc_stack $Builtin.NativeObject
store %0 to %1 : $*Builtin.NativeObject
cond_br undef, bb1, bb2

bb1:
br bb3

bb2:
br bb3

bb3:
destroy_addr %1 : $*Builtin.NativeObject
dealloc_stack %1 : $*Builtin.NativeObject
%9999 = tuple()
return %9999 : $()
}

// We should not promote this due to this being an assign to %2.
// CHECK-LABEL: sil @simple_diamond_with_assign : $@convention(thin) (@owned Builtin.NativeObject, @in_guaranteed Builtin.NativeObject) -> () {
// CHECK: alloc_stack
// CHECK: copy_addr
// CHECK: } // end sil function 'simple_diamond_with_assign'
sil @simple_diamond_with_assign : $@convention(thin) (@owned Builtin.NativeObject, @in_guaranteed Builtin.NativeObject) -> () {
bb0(%0 : $Builtin.NativeObject, %1 : $*Builtin.NativeObject):
%2 = alloc_stack $Builtin.NativeObject
store %0 to %2 : $*Builtin.NativeObject
cond_br undef, bb1, bb2

bb1:
copy_addr [take] %1 to %2 : $*Builtin.NativeObject
br bb3

bb2:
br bb3

bb3:
destroy_addr %2 : $*Builtin.NativeObject
dealloc_stack %2 : $*Builtin.NativeObject
%9999 = tuple()
return %9999 : $()
}

// Today PMO can not handle different available values coming from different
// BBs. With time it can be taught to do that if necessary. That being said,
// this test shows that we /tried/ and failed with the available value test
// instead of failing earlier due to the copy_addr being an assign since we
// explode the copy_addr.
// CHECK-LABEL: sil @simple_diamond_with_assign_remove : $@convention(thin) (@owned Builtin.NativeObject, @in_guaranteed Builtin.NativeObject) -> () {
// CHECK: alloc_stack
// CHECK-NOT: copy_addr
// CHECK: } // end sil function 'simple_diamond_with_assign_remove'
sil @simple_diamond_with_assign_remove : $@convention(thin) (@owned Builtin.NativeObject, @in_guaranteed Builtin.NativeObject) -> () {
bb0(%0 : $Builtin.NativeObject, %1 : $*Builtin.NativeObject):
%2 = alloc_stack $Builtin.NativeObject
store %0 to %2 : $*Builtin.NativeObject
cond_br undef, bb1, bb2

bb1:
destroy_addr %2 : $*Builtin.NativeObject
copy_addr [take] %1 to [initialization] %2 : $*Builtin.NativeObject
br bb3

bb2:
br bb3

bb3:
destroy_addr %2 : $*Builtin.NativeObject
dealloc_stack %2 : $*Builtin.NativeObject
%9999 = tuple()
return %9999 : $()
}