Skip to content
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

[SR-12180] Crash in app runtime due to Swift compiler optimization -O (Optimize for speed) #54605

Closed
swift-ci opened this issue Feb 11, 2020 · 2 comments
Labels
bug A deviation from expected or documented behavior. Also: expected but undesirable behavior. compiler The Swift compiler in itself crash Bug: A crash, i.e., an abnormal termination of software optimized only Flag: An issue whose reproduction requires optimized compilation

Comments

@swift-ci
Copy link
Collaborator

Previous ID SR-12180
Radar rdar://problem/59496027
Original Reporter kamils (JIRA User)
Type Bug
Status Resolved
Resolution Done
Environment

Apple Swift version 5.1 (swiftlang-1100.0.270.13)

Additional Detail from JIRA
Votes 0
Component/s Compiler
Labels Bug, CompilerCrash, OptimizedOnly
Assignee None
Priority Medium

md5: 0edb6e6fee44600a3b53619a01df402a

Issue Description:

On production AppStore build we did face a 100% reproducible deterministic app crash which I was able reduce to be caused by following code. This happens only with `-O (Optimize for speed)`, `-Onone` is unaffected.

swiftc --version                   
Apple Swift version 5.1 (swiftlang-1100.0.270.13 clang-1100.0.33.7)
Target: x86_64-apple-darwin19.0.0

This happens both on X86-64 and ARM so clearly it's a Swift compiler issue.

import Foundation

public class UserDefaultsStore<T>: Storing where T: Codable {
    public typealias Element = T
    private var userDefaults: UserDefaults

    @inline(__always) //this will be inlined by default, will crash
    public func read(for url: String) -> T? {
        print(userDefaults) //� Crash will happen here
        return nil
    }
    //This code is essential for crash to happen (even though it's not called)
    public func save(_ element: T, for url: String) {
    }
    public init(userDefaults: UserDefaults) {
        self.userDefaults = userDefaults
    }
}


public protocol Storing {
    associatedtype Element
    func read(for url: String) -> Element?
    func save(_ element: Element, for key: String)
}


public struct AnyStoring<T>: Storing {
    public typealias Element = T
    private let readHandler: (String) -> T?
    private let saveHandler: (T, String) -> Void
    
    //This is essential to cause the crash
    @inline(never)
    public func read(for key: String) -> T? {
        return readHandler(key)
    }
    
    public func save(_ element: T, for key: String) {
        saveHandler(element, key)
    }


    public init<C: Storing>(_ cache: C) where C.Element == Element {
        readHandler = cache.read
        saveHandler = cache.save
    }
}


//This can be class as well
public struct SomeModel: Codable {
}

//Call to crash
AnyStoring<SomeModel>(UserDefaultsStore<SomeModel>(userDefaults: UserDefaults.standard)).read(for: "")

We found four possible workarounds not to trigger the crash:

  1. Define both `AnyStoring` & `UserDefaultsStore` as a class
  2. Define both `AnyStoring` & `UserDefaultsStore` as a struct
  3. Define `AnyStoring` as a class & `UserDefaultsStore` as a struct (so effectively exchanging their definition types)
  4. Mark the crashing method
    public func read(for url: String) -> T? {
    with `@inline(never)`

The SIL of inlined and nonline variant for crashing `public func read(for url: String) -> T? {` looks identical to me (apart from the mangled signature).

// UserDefaultsStore.read(for:)
sil [always_inline] [ossa] @$s11main_always17UserDefaultsStoreC4read3forxSgSS_tF : $@convention(method) <T where T : Decodable, T : Encodable> (@guaranteed String, @guaranteed UserDefaultsStore<T>) -> @out Optional<T> {
// %0                                             // user: %25
// %1                                             // user: %3
// %2                                             // users: %12, %11, %4
bb0(%0 : $*Optional<T>, %1 : @guaranteed $String, %2 : @guaranteed $UserDefaultsStore<T>):
  debug_value %1 : $String, let, name "url", argno 1 // id: %3
  debug_value %2 : $UserDefaultsStore<T>, let, name "self", argno 2 // id: %4
  %5 = integer_literal $Builtin.Word, 1           // user: %7
  // function_ref _allocateUninitializedArray<A>(_:)
  %6 = function_ref @$ss27_allocateUninitializedArrayySayxG_BptBwlF : $@convention(thin) <τ_0_0> (Builtin.Word) -> (@owned Array<τ_0_0>, Builtin.RawPointer) // user: %7
  %7 = apply %6<Any>(%5) : $@convention(thin) <τ_0_0> (Builtin.Word) -> (@owned Array<τ_0_0>, Builtin.RawPointer) // user: %8
  (%8, %9) = destructure_tuple %7 : $(Array<Any>, Builtin.RawPointer) // users: %23, %20, %10
  %10 = pointer_to_address %9 : $Builtin.RawPointer to [strict] $*Any // user: %13
  %11 = class_method %2 : $UserDefaultsStore<T>, #UserDefaultsStore.userDefaults!getter.1 : <T where T : Decodable, T : Encodable> (UserDefaultsStore<T>) -> () -> UserDefaults, $@convention(method) <τ_0_0 where τ_0_0 : Decodable, τ_0_0 : Encodable> (@guaranteed UserDefaultsStore<τ_0_0>) -> @owned UserDefaults // user: %12
  %12 = apply %11<T>(%2) : $@convention(method) <τ_0_0 where τ_0_0 : Decodable, τ_0_0 : Encodable> (@guaranteed UserDefaultsStore<τ_0_0>) -> @owned UserDefaults // user: %14
  %13 = init_existential_addr %10 : $*Any, $UserDefaults // user: %14
  store %12 to [init] %13 : $*UserDefaults        // id: %14
  // function_ref default argument 1 of print(_:separator:terminator:)
  %15 = function_ref @$ss5print_9separator10terminatoryypd_S2StFfA0_ : $@convention(thin) () -> @owned String // user: %16
  %16 = apply %15() : $@convention(thin) () -> @owned String // users: %22, %20
  // function_ref default argument 2 of print(_:separator:terminator:)
  %17 = function_ref @$ss5print_9separator10terminatoryypd_S2StFfA1_ : $@convention(thin) () -> @owned String // user: %18
  %18 = apply %17() : $@convention(thin) () -> @owned String // users: %21, %20
  // function_ref print(_:separator:terminator:)
  %19 = function_ref @$ss5print_9separator10terminatoryypd_S2StF : $@convention(thin) (@guaranteed Array<Any>, @guaranteed String, @guaranteed String) -> () // user: %20
  %20 = apply %19(%8, %16, %18) : $@convention(thin) (@guaranteed Array<Any>, @guaranteed String, @guaranteed String) -> ()
  destroy_value %18 : $String                     // id: %21
  destroy_value %16 : $String                     // id: %22
  destroy_value %8 : $Array<Any>                  // id: %23
  %24 = metatype $@thin Optional<T>.Type
  inject_enum_addr %0 : $*Optional<T>, #Optional.none!enumelt // id: %25
  %26 = tuple ()                                  // user: %27
  return %26 : $()                                // id: %27
} // end sil function '$s11main_always17UserDefaultsStoreC4read3forxSgSS_tF'
// UserDefaultsStore.read(for:)
sil [noinline] [ossa] @$s4main17UserDefaultsStoreC4read3forxSgSS_tF : $@convention(method) <T where T : Decodable, T : Encodable> (@guaranteed String, @guaranteed UserDefaultsStore<T>) -> @out Optional<T> {
// %0                                             // user: %25
// %1                                             // user: %3
// %2                                             // users: %12, %11, %4
bb0(%0 : $*Optional<T>, %1 : @guaranteed $String, %2 : @guaranteed $UserDefaultsStore<T>):
  debug_value %1 : $String, let, name "url", argno 1 // id: %3
  debug_value %2 : $UserDefaultsStore<T>, let, name "self", argno 2 // id: %4
  %5 = integer_literal $Builtin.Word, 1           // user: %7
  // function_ref _allocateUninitializedArray<A>(_:)
  %6 = function_ref @$ss27_allocateUninitializedArrayySayxG_BptBwlF : $@convention(thin) <τ_0_0> (Builtin.Word) -> (@owned Array<τ_0_0>, Builtin.RawPointer) // user: %7
  %7 = apply %6<Any>(%5) : $@convention(thin) <τ_0_0> (Builtin.Word) -> (@owned Array<τ_0_0>, Builtin.RawPointer) // user: %8
  (%8, %9) = destructure_tuple %7 : $(Array<Any>, Builtin.RawPointer) // users: %23, %20, %10
  %10 = pointer_to_address %9 : $Builtin.RawPointer to [strict] $*Any // user: %13
  %11 = class_method %2 : $UserDefaultsStore<T>, #UserDefaultsStore.userDefaults!getter.1 : <T where T : Decodable, T : Encodable> (UserDefaultsStore<T>) -> () -> UserDefaults, $@convention(method) <τ_0_0 where τ_0_0 : Decodable, τ_0_0 : Encodable> (@guaranteed UserDefaultsStore<τ_0_0>) -> @owned UserDefaults // user: %12
  %12 = apply %11<T>(%2) : $@convention(method) <τ_0_0 where τ_0_0 : Decodable, τ_0_0 : Encodable> (@guaranteed UserDefaultsStore<τ_0_0>) -> @owned UserDefaults // user: %14
  %13 = init_existential_addr %10 : $*Any, $UserDefaults // user: %14
  store %12 to [init] %13 : $*UserDefaults        // id: %14
  // function_ref default argument 1 of print(_:separator:terminator:)
  %15 = function_ref @$ss5print_9separator10terminatoryypd_S2StFfA0_ : $@convention(thin) () -> @owned String // user: %16
  %16 = apply %15() : $@convention(thin) () -> @owned String // users: %22, %20
  // function_ref default argument 2 of print(_:separator:terminator:)
  %17 = function_ref @$ss5print_9separator10terminatoryypd_S2StFfA1_ : $@convention(thin) () -> @owned String // user: %18
  %18 = apply %17() : $@convention(thin) () -> @owned String // users: %21, %20
  // function_ref print(_:separator:terminator:)
  %19 = function_ref @$ss5print_9separator10terminatoryypd_S2StF : $@convention(thin) (@guaranteed Array<Any>, @guaranteed String, @guaranteed String) -> () // user: %20
  %20 = apply %19(%8, %16, %18) : $@convention(thin) (@guaranteed Array<Any>, @guaranteed String, @guaranteed String) -> ()
  destroy_value %18 : $String                     // id: %21
  destroy_value %16 : $String                     // id: %22
  destroy_value %8 : $Array<Any>                  // id: %23
  %24 = metatype $@thin Optional<T>.Type
  inject_enum_addr %0 : $*Optional<T>, #Optional.none!enumelt // id: %25
  %26 = tuple ()                                  // user: %27
  return %26 : $()                                // id: %27
} // end sil function '$s4main17UserDefaultsStoreC4read3forxSgSS_tF'
@beccadax
Copy link
Contributor

@swift-ci create

@eeckstein
Copy link
Member

master: #29882
5.2: #29890

@swift-ci swift-ci transferred this issue from apple/swift-issues Apr 25, 2022
@AnthonyLatsis AnthonyLatsis added the crash Bug: A crash, i.e., an abnormal termination of software label Dec 12, 2022
This issue was closed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug A deviation from expected or documented behavior. Also: expected but undesirable behavior. compiler The Swift compiler in itself crash Bug: A crash, i.e., an abnormal termination of software optimized only Flag: An issue whose reproduction requires optimized compilation
Projects
None yet
Development

No branches or pull requests

4 participants