Files
swift-mirror/test/SILOptimizer/outliner.swift
Andrew Trick 7205e46f41 Disable -O copy propagation (lifetime shortening) by default
Problem: We continue to uncover code that assumes either precise local
variable lifetimes (to the end of the lexical scope) or extended
temporary lifetimes (to the end of the statement). These bugs require
heroic debugging to find the root cause. Because they only show up in
Release builds, they often manifest just before the affected project
“ships” under an impending deadline.

We now have enough information from projects that have been tested
with copy propagation that we can both understand common patterns and
identify some specific APIs that may cause trouble. We know what API
annotations the compiler will need for helpful warnings and can begin
adding those annotations.

Disabling copy propagation now is only a temporary deferral, we will
still need to bring it back by default. However, by then we should
have:

- LLDB and runtime support for debugging deinitialized objects

- A variant of lifetime sortening that can run in Debug builds to
  catch problems before code ships

- Static compiler warnings for likely invalid lifetime assumptions

- Source annotations that allow those warnings to protect programmers
  against existing dangerous APIs

In the meantime...

Projects can experiment with the behavior and gradually migrate.

Copy propagation will automatically be enabled in -enable-ossa-modules
mode. It is important to work toward a single performance
target. Supporting full OSSA and improving ARC performance without
copy propagation would be prohibitively complicated.

rdar://76438920 (Temporarily disable -O copy propagation by default)
2021-04-09 00:12:09 -07:00

261 lines
12 KiB
Swift

// RUN: %target-swift-frontend -Osize -import-objc-header %S/Inputs/Outliner.h %s -emit-sil -enforce-exclusivity=unchecked -enable-copy-propagation | %FileCheck %s
// RUN: %target-swift-frontend -Osize -g -import-objc-header %S/Inputs/Outliner.h %s -emit-sil -enforce-exclusivity=unchecked -enable-copy-propagation | %FileCheck %s
// REQUIRES: objc_interop
// REQUIRES: optimized_stdlib
import Foundation
public class MyGizmo {
private var gizmo : Gizmo
private var optionalGizmo : Gizmo?
init() {
gizmo = Gizmo()
}
// CHECK-LABEL: sil @$s8outliner7MyGizmoC11usePropertyyyF :
// CHECK: [[A_FUN:%.*]] = function_ref @$sSo5GizmoC14stringPropertySSSgvgToTeab_
// CHECK: apply [[A_FUN]]({{.*}}) : $@convention(thin) (@in_guaranteed Gizmo) -> @owned Optional<String>
// CHECK-NOT: return
// CHECK: [[P_FUN:%.*]] = function_ref @$sSo5GizmoC14stringPropertySSSgvgToTepb_
// CHECK: apply [[P_FUN]]({{.*}}) : $@convention(thin) (Gizmo) -> @owned Optional<String>
// CHECK: return
// CHECK: } // end sil function '$s8outliner7MyGizmoC11usePropertyyyF'
public func useProperty() {
print(gizmo.stringProperty)
print(optionalGizmo!.stringProperty)
}
}
// CHECK-LABEL: sil @$s8outliner13testOutliningyyF :
// CHECK: [[FUN:%.*]] = function_ref @$sSo5GizmoC14stringPropertySSSgvgToTepb_
// CHECK: apply [[FUN]](%{{.*}}) : $@convention(thin) (Gizmo) -> @owned Optional<String>
// CHECK: apply [[FUN]](%{{.*}}) : $@convention(thin) (Gizmo) -> @owned Optional<String>
// CHECK: [[FUN:%.*]] = function_ref @$sSo5GizmoC14stringPropertySSSgvsToTembnn_
// CHECK: apply [[FUN]]({{.*}}) : $@convention(thin) (@owned String, Gizmo) -> ()
// CHECK: apply [[FUN]]({{.*}}) : $@convention(thin) (@owned String, Gizmo) -> ()
// CHECK: [[FUN:%.*]] = function_ref @$sSo5GizmoC12modifyString_10withNumber0D6FoobarSSSgAF_SiypSgtFToTembnnnb_
// CHECK: apply [[FUN]]({{.*}}) : $@convention(thin) (@owned String, Int, Optional<AnyObject>, Gizmo) -> @owned Optional<String>
// CHECK: apply [[FUN]]({{.*}}) : $@convention(thin) (@owned String, Int, Optional<AnyObject>, Gizmo) -> @owned Optional<String>
// CHECK: [[FUN:%.*]] = function_ref @$sSo5GizmoC11doSomethingyypSgSaySSGSgFToTembgnn_
// CHECK: apply [[FUN]]({{.*}}) : $@convention(thin) (@guaranteed Array<String>, Gizmo) -> @owned Optional<AnyObject>
// CHECK: [[FUN:%.*]] = function_ref @$sSo5GizmoC11doSomethingyypSgSaySSGSgFToTembnn_
// CHECK: apply [[FUN]]({{.*}}) : $@convention(thin) (@owned Array<String>, Gizmo) -> @owned Optional<AnyObject>
// CHECK: return
// CHECK: } // end sil function '$s8outliner13testOutliningyyF'
public func testOutlining() {
let gizmo = Gizmo()
let foobar = Gizmo()
print(gizmo.stringProperty)
print(gizmo.stringProperty)
gizmo.stringProperty = "foobar"
gizmo.stringProperty = "foobar2"
gizmo.modifyString("hello", withNumber:1, withFoobar: foobar)
gizmo.modifyString("hello", withNumber:1, withFoobar: foobar)
let arr = [ "foo", "bar"]
gizmo.doSomething(arr)
gizmo.doSomething(arr)
}
// CHECK-LABEL: sil @$s8outliner9dontCrash1ayyp_tF : $@convention(thin) (@in_guaranteed Any) -> () {
// CHECK: [[OBJ:%.*]] = open_existential_ref {{.*}} : $AnyObject to $@opened("{{.*}}") (AnyObject)
// CHECK: [[METH:%.*]] = objc_method [[OBJ]] : $@opened("{{.*}}") (AnyObject), #Treeish.treeishChildren!foreign : <Self where Self : Treeish> (Self) -> () -> [Any]?
// CHECK: [[RES:%.*]] = apply [[METH]]([[OBJ]]) : $@convention(objc_method)
// CHECK: switch_enum [[RES]]
// CHECK: } // end sil function '$s8outliner9dontCrash1ayyp_tF'
// CHECK-LABEL: sil shared [noinline] @$sSo5GizmoC14stringPropertySSSgvgToTeab_ : $@convention(thin) (@in_guaranteed Gizmo) -> @owned Optional<String>
// CHECK: bb0(%0 : $*Gizmo):
// CHECK: %1 = load %0 : $*Gizmo
// CHECK: %2 = objc_method %1 : $Gizmo, #Gizmo.stringProperty!getter.foreign : (Gizmo) -> () -> String?
// CHECK: %3 = apply %2(%1) : $@convention(objc_method) (Gizmo) -> @autoreleased Optional<NSString>
// CHECK: switch_enum %3 : $Optional<NSString>, case #Optional.some!enumelt: bb1, case #Optional.none!enumelt: bb2
// CHECK: bb1(%5 : $NSString):
// CHECK: %6 = function_ref @$sSS10FoundationE36_unconditionallyBridgeFromObjectiveCySSSo8NSStringCSgFZ : $@convention(method) (@guaranteed Optional<NSString>, @thin String.Type) -> @owned String
// CHECK: %7 = metatype $@thin String.Type
// CHECK: %8 = apply %6(%3, %7) : $@convention(method) (@guaranteed Optional<NSString>, @thin String.Type) -> @owned String
// CHECK: release_value %3 : $Optional<NSString>
// CHECK: %10 = enum $Optional<String>, #Optional.some!enumelt, %8 : $String
// CHECK: br bb3(%10 : $Optional<String>)
// CHECK: bb2:
// CHECK: %12 = enum $Optional<String>, #Optional.none!enumelt
// CHECK: br bb3(%12 : $Optional<String>)
// CHECK: bb3(%14 : $Optional<String>):
// CHECK: return %14 : $Optional<String>
// CHECK: } // end sil function '$sSo5GizmoC14stringPropertySSSgvgToTeab_'
// CHECK-LABEL: sil shared [noinline] @$sSo5GizmoC14stringPropertySSSgvgToTepb_ : $@convention(thin) (Gizmo) -> @owned Optional<String>
// CHECK: bb0(%0 : $Gizmo):
// CHECK: %1 = objc_method %0 : $Gizmo, #Gizmo.stringProperty!getter.foreign : (Gizmo) -> () -> String?
// CHECK: %2 = apply %1(%0) : $@convention(objc_method) (Gizmo) -> @autoreleased Optional<NSString>
// CHECK: switch_enum %2 : $Optional<NSString>, case #Optional.some!enumelt: bb1, case #Optional.none!enumelt: bb2
// CHECK:bb1(%4 : $NSString):
// CHECK: %5 = function_ref @$sSS10FoundationE36_unconditionallyBridgeFromObjectiveCySSSo8NSStringCSgFZ : $@convention(method) (@guaranteed Optional<NSString>, @thin String.Type) -> @owned String
// CHECK: %6 = metatype $@thin String.Type
// CHECK: %7 = apply %5(%2, %6) : $@convention(method) (@guaranteed Optional<NSString>, @thin String.Type) -> @owned String
// CHECK: release_value %2 : $Optional<NSString>
// CHECK: %9 = enum $Optional<String>, #Optional.some!enumelt, %7 : $String
// CHECK: br bb3(%9 : $Optional<String>)
// CHECK:bb2:
// CHECK: %11 = enum $Optional<String>, #Optional.none!enumelt
// CHECK: br bb3(%11 : $Optional<String>)
// CHECK:bb3(%13 : $Optional<String>):
// CHECK: return %13 : $Optional<String>
// CHECK: } // end sil function '$sSo5GizmoC14stringPropertySSSgvgToTepb_'
// CHECK-LABEL: sil shared [noinline] @$sSo5GizmoC14stringPropertySSSgvsToTembnn_ : $@convention(thin) (@owned String, Gizmo) -> () {
// CHECK: bb0(%0 : $String, %1 : $Gizmo):
// CHECK: %2 = objc_method %1 : $Gizmo, #Gizmo.stringProperty!setter.foreign : (Gizmo) -> (String?) -> ()
// CHECK: %3 = function_ref @$sSS10FoundationE19_bridgeToObjectiveCSo8NSStringCyF : $@convention(method) (@guaranteed String) -> @owned NSString
// CHECK: %4 = apply %3(%0) : $@convention(method) (@guaranteed String) -> @owned NSString
// CHECK: release_value %0 : $String
// CHECK: %6 = enum $Optional<NSString>, #Optional.some!enumelt, %4 : $NSString
// CHECK: %7 = apply %2(%6, %1) : $@convention(objc_method) (Optional<NSString>, Gizmo) -> ()
// CHECK: strong_release %4 : $NSString
// CHECK: return %7 : $()
// CHECK: } // end sil function '$sSo5GizmoC14stringPropertySSSgvsToTembnn_'
// CHECK-LABEL: sil shared [noinline] @$sSo5GizmoC12modifyString_10withNumber0D6FoobarSSSgAF_SiypSgtFToTembnnnb_ : $@convention(thin) (@owned String, Int, Optional<AnyObject>, Gizmo) -> @owned Optional<String> {
// CHECK: bb0(%0 : $String, %1 : $Int, %2 : $Optional<AnyObject>, %3 : $Gizmo):
// CHECK: %4 = objc_method %3 : $Gizmo, #Gizmo.modifyString!foreign : (Gizmo) -> (String?, Int, Any?) -> String?
// CHECK: %5 = function_ref @$sSS10FoundationE19_bridgeToObjectiveCSo8NSStringCyF : $@convention(method) (@guaranteed String) -> @owned NSString
// CHECK: %6 = apply %5(%0) : $@convention(method) (@guaranteed String) -> @owned NSString
// CHECK: release_value %0 : $String
// CHECK: %8 = enum $Optional<NSString>, #Optional.some!enumelt, %6 : $NSString
// CHECK: %9 = apply %4(%8, %1, %2, %3) : $@convention(objc_method) (Optional<NSString>, Int, Optional<AnyObject>, Gizmo) -> @autoreleased Optional<NSString>
// CHECK: strong_release %6 : $NSString
// CHECK: switch_enum %9 : $Optional<NSString>, case #Optional.some!enumelt: bb2, case #Optional.none!enumelt: bb1
//
// CHECK: bb1:
// CHECK: %12 = enum $Optional<String>, #Optional.none!enumelt
// CHECK: br bb3(%12 : $Optional<String>)
//
// CHECK: bb2(%14 : $NSString):
// CHECK: %15 = function_ref @$sSS10FoundationE36_unconditionallyBridgeFromObjectiveCySSSo8NSStringCSgFZ : $@convention(method) (@guaranteed Optional<NSString>, @thin String.Type) -> @owned String
// CHECK: %16 = metatype $@thin String.Type
// CHECK: %17 = apply %15(%9, %16) : $@convention(method) (@guaranteed Optional<NSString>, @thin String.Type) -> @owned String
// CHECK: release_value %9 : $Optional<NSString> // id: %18
// CHECK: %19 = enum $Optional<String>, #Optional.some!enumelt, %17 : $String
// CHECK: br bb3(%19 : $Optional<String>)
//
// CHECK: bb3(%21 : $Optional<String>):
// CHECK: return %21 : $Optional<String>
// CHECK: } // end sil function '$sSo5GizmoC12modifyString_10withNumber0D6FoobarSSSgAF_SiypSgtFToTembnnnb_'
// CHECK-LABEL: sil shared [noinline] @$sSo5GizmoC11doSomethingyypSgSaySSGSgFToTembgnn_ : $@convention(thin) (@guaranteed Array<String>, Gizmo) -> @owned Optional<AnyObject> {
// CHECK: bb0(%0 : $Array<String>, %1 : $Gizmo):
// CHECK: %2 = objc_method %1 : $Gizmo, #Gizmo.doSomething!foreign : (Gizmo) -> ([String]?) -> Any?
// CHECK: %3 = function_ref @$sSa10FoundationE19_bridgeToObjectiveCSo7NSArrayCyF : $@convention(method) <{{.*}}> (@guaranteed Array<{{.*}}>) -> @owned NSArray
// CHECK: %4 = apply %3<String>(%0) : $@convention(method) <{{.*}}> (@guaranteed Array<{{.*}}>) -> @owned NSArray
// CHECK-NOT: release_value
// CHECK: %5 = enum $Optional<NSArray>, #Optional.some!enumelt, %4 : $NSArray
// CHECK: %6 = apply %2(%5, %1) : $@convention(objc_method) (Optional<NSArray>, Gizmo) -> @autoreleased Optional<AnyObject>
// CHECK: strong_release %4 : $NSArray
// CHECK: return %6 : $Optional<AnyObject>
// CHECK: } // end sil function '$sSo5GizmoC11doSomethingyypSgSaySSGSgFToTembgnn_'
public func dontCrash<T: Proto>(x : Gizmo2<T>) {
let s = x.doSomething()
print(s)
}
public func dontCrash2(_ c: SomeGenericClass) -> Bool {
guard let str = c.version else {
return false
}
guard let str2 = c.doSomething() else {
return false
}
let arr = [ "foo", "bar"]
c.doSomething2(arr)
return true
}
func dontCrash3() -> String? {
let bundle = Bundle.main
let resource = "common parameter"
return bundle.path(forResource: resource, ofType: "png")
?? bundle.path(forResource: resource, ofType: "apng")
}
extension Operation {
func dontCrash4() {
if completionBlock != nil {
completionBlock = { }
}
}
}
public func dontCrash(a: Any) {
(a as AnyObject).treeishChildren()
}
public class Foo : NSObject {
var x: MyView? = nil
public func dontCrash(_ pt: MyPoint) -> Bool {
guard let somView = x,
let window = somView.window else {
return false
}
guard let event = MyEvent.mouseEvent(with: .A,
location: pt,
windowNumber: window.windowNumber,
context: nil,
eventNumber: 0,
clickCount: 1,
pressure:1) else {
print("failure")
return false
}
print(event)
return true
}
}
public func testCalendar() {
let formatter = DateFormatter()
formatter.calendar = Calendar(identifier: .gregorian)
}
open class Test
{
@inline(never)
public func getWindow() -> MyWindow
{
return MyWindow()
}
public func testDontCrash() -> MyView
{
let view = MyView()
view.window2 = getWindow()
return view
}
}
internal extension NSError {
convenience init(myError code: Int) {
self.init(domain: "error", code: code, userInfo: [:])
}
}
public class AnotherTest {
public let obj: MyObject
public init(obj: MyObject) {
self.obj = obj
}
public func dontCrash() {
self.obj.error = NSError(myError: 10)
}
}