Skip to content

[cast-opt] Fix miscompile when we tried to optimize take_on_success t… #25041

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
7 changes: 4 additions & 3 deletions lib/SILOptimizer/Utils/CastOptimizer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -323,11 +323,12 @@ CastOptimizer::optimizeBridgedObjCToSwiftCast(SILDynamicCastInst dynamicCast) {
case CastConsumptionKind::TakeAlways:
Builder.createReleaseValue(Loc, srcOp, Builder.getDefaultAtomicity());
break;
case CastConsumptionKind::TakeOnSuccess:
case CastConsumptionKind::TakeOnSuccess: {
// Insert a release in the success BB.
Builder.setInsertionPoint(SuccessBB->begin());
Builder.createReleaseValue(Loc, srcOp, Builder.getDefaultAtomicity());
SILBuilderWithScope SuccessBuilder(SuccessBB->begin());
SuccessBuilder.emitDestroyValueOperation(Loc, srcOp);
break;
}
case CastConsumptionKind::BorrowAlways:
llvm_unreachable("checked_cast_addr_br never has BorrowAlways");
case CastConsumptionKind::CopyOnSuccess:
Expand Down
222 changes: 222 additions & 0 deletions test/SILOptimizer/constant_propagation_objc.sil
Original file line number Diff line number Diff line change
@@ -0,0 +1,222 @@
// RUN: %target-sil-opt -enable-sil-verify-all %s -diagnostic-constant-propagation | %FileCheck %s
// RUN: %target-sil-opt -enable-sil-verify-all %s -performance-constant-propagation | %FileCheck %s

// REQUIRES: objc_interop

sil_stage canonical

import Swift
import Foundation
import Builtin

sil @$ss11AnyHashableVyABxcSHRzlufC : $@convention(method) <τ_0_0 where τ_0_0 : Hashable> (@in τ_0_0, @thin AnyHashable.Type) -> @out AnyHashable

sil @guaranteed_swift_array_user : $@convention(thin) <τ_0_0> (@guaranteed Array<τ_0_0>) -> ()

// CHECK-LABEL: sil @array_downcast_copyonsuccess : $@convention(thin) (@guaranteed NSArray) -> () {
// CHECK: bb0([[ARG:%.*]] : $NSArray):
// CHECK: [[INPUT:%.*]] = alloc_stack $NSArray
// CHECK: retain_value [[ARG]]
// CHECK: store [[ARG]] to [[INPUT]]
// CHECK: [[OUTPUT:%.*]] = alloc_stack $Array<String>
// CHECK: [[INPUT_VALUE:%.*]] = load [[INPUT]]
// CHECK: br [[BRIDGE_BB:bb[0-9]+]]([[INPUT_VALUE]] :
//
// CHECK: [[SUCCESS_BB:bb[0-9]+]]:
// CHECK: [[SUCCESS_VAL:%.*]] = load [[OUTPUT]]
// CHECK: [[CAST_RESULT:%.*]] = apply {{%.*}}<String>([[SUCCESS_VAL]])
// CHECK-NEXT: release_value [[SUCCESS_VAL]]
// CHECK-NEXT: dealloc_stack [[OUTPUT]]
// CHECK-NEXT: destroy_addr [[INPUT]]
// CHECK-NEXT: dealloc_stack [[INPUT]]
// CHECK-NEXT: br [[EXIT_BB:bb[0-9]+]]
//
// CHECK: [[FAIL_BB:bb[0-9]+]]:
// CHECK-NEXT: dealloc_stack [[CAST_TMP:%.*]]
// CHECK-NEXT: dealloc_stack [[OUTPUT]]
// CHECK-NEXT: destroy_addr [[INPUT]]
// CHECK-NEXT: dealloc_stack [[INPUT]]
// CHECK-NEXT: br [[EXIT_BB]]
//
// CHECK: [[EXIT_BB]]:
// CHECK: return
//
// CHECK: [[BRIDGE_BB]]([[INPUT_VALUE:%.*]] : $NSArray):
// CHECK: [[CAST_TMP:%.*]] = alloc_stack $Optional<Array<String>>
// CHECK: strong_retain [[INPUT_VALUE]]
// CHECK: apply {{%.*}}<Array<String>>([[CAST_TMP]], [[INPUT_VALUE]],
// CHECK: strong_release [[INPUT_VALUE]]
// CHECK: switch_enum_addr [[CAST_TMP]] : $*Optional<Array<String>>, case #Optional.none!enumelt: [[FAIL_BB]], default [[SUCCESS_TRAMPOLINE_BB:bb[0-9]+]]
//
// CHECK: [[SUCCESS_TRAMPOLINE_BB]]:
// CHECK: [[PROJ_ENUM:%.*]] = unchecked_take_enum_data_addr [[CAST_TMP]]
// CHECK: copy_addr [take] [[PROJ_ENUM]] to [initialization] [[OUTPUT]]
// CHECK: dealloc_stack [[CAST_TMP]]
// CHECK: br [[SUCCESS_BB]]
// CHECK: } // end sil function 'array_downcast_copyonsuccess'
sil @array_downcast_copyonsuccess : $@convention(thin) (@guaranteed NSArray) -> () {
bb0(%0 : $NSArray):
%4 = alloc_stack $NSArray
retain_value %0 : $NSArray
store %0 to %4 : $*NSArray
%7 = alloc_stack $Array<String>
checked_cast_addr_br copy_on_success NSArray in %4 : $*NSArray to Array<String> in %7 : $*Array<String>, bb2, bb3

bb2:
%9 = load %7 : $*Array<String>
%10 = function_ref @guaranteed_swift_array_user : $@convention(thin) <τ_0_0> (@guaranteed Array<τ_0_0>) -> ()
apply %10<String>(%9) : $@convention(thin) <τ_0_0> (@guaranteed Array<τ_0_0>) -> ()
release_value %9 : $Array<String>
dealloc_stack %7 : $*Array<String>
destroy_addr %4 : $*NSArray
dealloc_stack %4 : $*NSArray
br bb4

bb3:
dealloc_stack %7 : $*Array<String>
destroy_addr %4 : $*NSArray
dealloc_stack %4 : $*NSArray
br bb4

bb4:
%9999 = tuple()
return %9999 : $()
}

// CHECK-LABEL: sil @array_downcast_takeonsuccess : $@convention(thin) (@guaranteed NSArray) -> () {
// CHECK: bb0([[ARG:%.*]] : $NSArray):
// CHECK: [[INPUT:%.*]] = alloc_stack $NSArray
// CHECK: retain_value [[ARG]]
// CHECK: store [[ARG]] to [[INPUT]]
// CHECK: [[OUTPUT:%.*]] = alloc_stack $Array<String>
// CHECK: [[INPUT_VALUE:%.*]] = load [[INPUT]]
// CHECK: br [[BRIDGE_BB:bb[0-9]+]]([[INPUT_VALUE]] :
//
// CHECK: [[SUCCESS_BB:bb[0-9]+]]:
// CHECK: strong_release [[INPUT_VALUE:%.*]] :
// CHECK: [[SUCCESS_VAL:%.*]] = load [[OUTPUT]]
// CHECK: [[CAST_RESULT:%.*]] = apply {{%.*}}<String>([[SUCCESS_VAL]])
// CHECK-NEXT: release_value [[SUCCESS_VAL]]
// CHECK-NEXT: dealloc_stack [[OUTPUT]]
// CHECK-NEXT: dealloc_stack [[INPUT]]
// CHECK-NEXT: br [[EXIT_BB:bb[0-9]+]]
//
// CHECK: [[FAIL_BB:bb[0-9]+]]:
// CHECK-NEXT: dealloc_stack [[CAST_TMP:%.*]]
// CHECK-NEXT: dealloc_stack [[OUTPUT]]
// CHECK-NEXT: destroy_addr [[INPUT]]
// CHECK-NEXT: dealloc_stack [[INPUT]]
// CHECK-NEXT: br [[EXIT_BB]]
//
// CHECK: [[EXIT_BB]]:
// CHECK: return
//
// CHECK: [[BRIDGE_BB]]([[INPUT_VALUE]] : $NSArray):
// CHECK: [[CAST_TMP:%.*]] = alloc_stack $Optional<Array<String>>
// CHECK: strong_retain [[INPUT_VALUE]]
// CHECK: apply {{%.*}}<Array<String>>([[CAST_TMP]], [[INPUT_VALUE]],
// CHECK: strong_release [[INPUT_VALUE]]
// NOTE: In contrast to with take_always, the release_value is above in SUCCESS_BLOCK
// CHECK: switch_enum_addr [[CAST_TMP]] : $*Optional<Array<String>>, case #Optional.none!enumelt: [[FAIL_BB]], default [[SUCCESS_TRAMPOLINE_BB:bb[0-9]+]]
//
// CHECK: [[SUCCESS_TRAMPOLINE_BB]]:
// CHECK: [[PROJ_ENUM:%.*]] = unchecked_take_enum_data_addr [[CAST_TMP]]
// CHECK: copy_addr [take] [[PROJ_ENUM]] to [initialization] [[OUTPUT]]
// CHECK: dealloc_stack [[CAST_TMP]]
// CHECK: br [[SUCCESS_BB]]
// CHECK: } // end sil function 'array_downcast_takeonsuccess'
sil @array_downcast_takeonsuccess : $@convention(thin) (@guaranteed NSArray) -> () {
bb0(%0 : $NSArray):
%4 = alloc_stack $NSArray
retain_value %0 : $NSArray
store %0 to %4 : $*NSArray
%7 = alloc_stack $Array<String>
checked_cast_addr_br take_on_success NSArray in %4 : $*NSArray to Array<String> in %7 : $*Array<String>, bb2, bb3

bb2:
%9 = load %7 : $*Array<String>
%10 = function_ref @guaranteed_swift_array_user : $@convention(thin) <τ_0_0> (@guaranteed Array<τ_0_0>) -> ()
apply %10<String>(%9) : $@convention(thin) <τ_0_0> (@guaranteed Array<τ_0_0>) -> ()
release_value %9 : $Array<String>
dealloc_stack %7 : $*Array<String>
dealloc_stack %4 : $*NSArray
br bb4

bb3:
dealloc_stack %7 : $*Array<String>
destroy_addr %4 : $*NSArray
dealloc_stack %4 : $*NSArray
br bb4

bb4:
%9999 = tuple()
return %9999 : $()
}

// CHECK-LABEL: sil @array_downcast_takealways : $@convention(thin) (@guaranteed NSArray) -> () {
// CHECK: bb0([[ARG:%.*]] : $NSArray):
// CHECK: [[INPUT:%.*]] = alloc_stack $NSArray
// CHECK: retain_value [[ARG]]
// CHECK: store [[ARG]] to [[INPUT]]
// CHECK: [[OUTPUT:%.*]] = alloc_stack $Array<String>
// CHECK: [[INPUT_VALUE:%.*]] = load [[INPUT]]
// CHECK: br [[BRIDGE_BB:bb[0-9]+]]([[INPUT_VALUE]] :
//
// CHECK: [[SUCCESS_BB:bb[0-9]+]]:
// CHECK: [[SUCCESS_VAL:%.*]] = load [[OUTPUT]]
// CHECK: [[CAST_RESULT:%.*]] = apply {{%.*}}<String>([[SUCCESS_VAL]])
// CHECK-NEXT: release_value [[SUCCESS_VAL]]
// CHECK-NEXT: dealloc_stack [[OUTPUT]]
// CHECK-NEXT: dealloc_stack [[INPUT]]
// CHECK-NEXT: br [[EXIT_BB:bb[0-9]+]]
//
// CHECK: [[FAIL_BB:bb[0-9]+]]:
// CHECK-NEXT: dealloc_stack [[CAST_TMP:%.*]]
// CHECK-NEXT: dealloc_stack [[OUTPUT]]
// CHECK-NEXT: dealloc_stack [[INPUT]]
// CHECK-NEXT: br [[EXIT_BB]]
//
// CHECK: [[EXIT_BB]]:
// CHECK: return
//
// CHECK: [[BRIDGE_BB]]([[INPUT_VALUE:%.*]] : $NSArray):
// CHECK: [[CAST_TMP:%.*]] = alloc_stack $Optional<Array<String>>
// CHECK: strong_retain [[INPUT_VALUE]]
// CHECK: apply {{%.*}}<Array<String>>([[CAST_TMP]], [[INPUT_VALUE]],
// CHECK: strong_release [[INPUT_VALUE]]
// NOTE: When we perform take_always, this is the take of the cast.
// CHECK: release_value [[INPUT_VALUE]]
// CHECK: switch_enum_addr [[CAST_TMP]] : $*Optional<Array<String>>, case #Optional.none!enumelt: [[FAIL_BB]], default [[SUCCESS_TRAMPOLINE_BB:bb[0-9]+]]
//
// CHECK: [[SUCCESS_TRAMPOLINE_BB]]:
// CHECK: [[PROJ_ENUM:%.*]] = unchecked_take_enum_data_addr [[CAST_TMP]]
// CHECK: copy_addr [take] [[PROJ_ENUM]] to [initialization] [[OUTPUT]]
// CHECK: dealloc_stack [[CAST_TMP]]
// CHECK: br [[SUCCESS_BB]]
// CHECK: } // end sil function 'array_downcast_takealways'
sil @array_downcast_takealways : $@convention(thin) (@guaranteed NSArray) -> () {
bb0(%0 : $NSArray):
%4 = alloc_stack $NSArray
retain_value %0 : $NSArray
store %0 to %4 : $*NSArray
%7 = alloc_stack $Array<String>
checked_cast_addr_br take_always NSArray in %4 : $*NSArray to Array<String> in %7 : $*Array<String>, bb2, bb3

bb2:
%9 = load %7 : $*Array<String>
%10 = function_ref @guaranteed_swift_array_user : $@convention(thin) <τ_0_0> (@guaranteed Array<τ_0_0>) -> ()
apply %10<String>(%9) : $@convention(thin) <τ_0_0> (@guaranteed Array<τ_0_0>) -> ()
release_value %9 : $Array<String>
dealloc_stack %7 : $*Array<String>
dealloc_stack %4 : $*NSArray
br bb4

bb3:
dealloc_stack %7 : $*Array<String>
dealloc_stack %4 : $*NSArray
br bb4

bb4:
%9999 = tuple()
return %9999 : $()
}