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-11010] Swift 5.1 infinite compile with optimizations #53400

Closed
keith opened this issue Jun 25, 2019 · 13 comments
Closed

[SR-11010] Swift 5.1 infinite compile with optimizations #53400

keith opened this issue Jun 25, 2019 · 13 comments
Labels
bug A deviation from expected or documented behavior. Also: expected but undesirable behavior. compiler The Swift compiler in itself optimized only Flag: An issue whose reproduction requires optimized compilation SILOptimizer Area → compiler: SIL optimization passes

Comments

@keith
Copy link
Collaborator

keith commented Jun 25, 2019

Previous ID SR-11010
Radar rdar://problem/52131975
Original Reporter @keith
Type Bug
Status Resolved
Resolution Done
Environment

Xcode 11 beta 2 11M337n
macOS 10.14.5 18F203

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

md5: e0eb50be50322b3745bb13f5fad4762b

Issue Description:

With Swift 5.1 and this code:

import UIKit

class SomeView: UIView {
    override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
        var view = super.hitTest(.zero, with: nil)
        while !(view is UIView) {
            view = view?.superview
        }

        return nil
    }
}

If you build it with:

DEVELOPER_DIR=/Applications/Xcode-beta.app xcrun --sdk iphonesimulator swiftc -O -target x86_64-apple-ios13.0-simulator foo.swift

The build never completes (I waited 70 minutes on my non-reduced case before cancelling). If you remove the `-O` it completes as expected. During this time swift also consumes 100% CPU. Here's a sample during this time:

Call graph:
    2840 Thread_8326431   DispatchQueue_1: com.apple.main-thread  (serial)
      2840 start  (in libdyld.dylib) + 1  [0x7fff78a1d3d5]
        2840 main  (in swift) + 1219  [0x10b0cb333]
          2840 swift::performFrontend(llvm::ArrayRef<char const*>, char const*, void*, swift::FrontendObserver*)  (in swift) + 6868  [0x10b13c0a4]
            2840 performCompile(swift::CompilerInstance&, swift::CompilerInvocation&, llvm::ArrayRef<char const*>, int&, swift::FrontendObserver*, swift::UnifiedStatsReporter*)  (in swift) + 34357  [0x10b147fd5]
              2840 swift::CompilerInstance::performSILProcessing(swift::SILModule*, swift::UnifiedStatsReporter*)  (in swift) + 6379  [0x10b4496cb]
                2840 swift::SILPassManager::execute()  (in swift) + 4599  [0x10b7e62f7]
                  2840 (anonymous namespace)::JumpThreadSimplifyCFGPass::run()  (in swift) + 885  [0x10b8d1e95]
                    2840 (anonymous namespace)::SimplifyCFG::run()  (in swift) + 119  [0x10b8a1987]
                      2840 (anonymous namespace)::SimplifyCFG::simplifyBlocks()  (in swift) + 28081,28141,...  [0x10b8af0b1,0x10b8af0ed,...]

Total number in stack (recursive counted multiple, when >=5):

Sort by top of stack, same collapsed (when >= 5):
        (anonymous namespace)::SimplifyCFG::simplifyBlocks()  (in swift)        2840
@belkadan
Copy link
Contributor

Hm, did not reproduce for me in an isolated command-line build. Can you attach a project, so that we know we're seeing the same thing?

@keith
Copy link
Collaborator Author

keith commented Jun 25, 2019

Sorry I changed my example back from `if` -> `while`, there seems to be some flakiness here so I thought that worked but maybe it didn't, can you try with that example?

@belkadan
Copy link
Contributor

Okay, that one reproduces! AppKit version for easier testing:

import AppKit

class SomeView: NSView {
    override func hitTest(_ point: CGPoint) -> NSView? {
        var view = super.hitTest(.zero)
        while !(view is NSView) {
            view = view?.superview
        }

        return nil
    }
}

@keith
Copy link
Collaborator Author

keith commented Jul 2, 2019

Probably expected but still repros the same way with Xcode 11 beta 3

@theblixguy
Copy link
Collaborator

Maybe you can pass `-Xllvm -sil-disable-pass=jumpthread-simplify-cfg` to get around the issue in the meantime.

@keith
Copy link
Collaborator Author

keith commented Jul 8, 2019

It looks like in this case that flag doesn't help

@keith
Copy link
Collaborator Author

keith commented Jul 18, 2019

Looks like this still repros with beta 4, @belkadan do you know if this is on anyone's radar to fix?

@belkadan
Copy link
Contributor

I can't really talk about what people are and aren't working on if they haven't done anything publicly yet, sorry.

@gottesmm
Copy link
Member

I have a fix here for the infinite loop in the compiler, but be aware if the hit test returns none, this is semantically an infinite loop. If one expands out the pattern matching, since hitTest returns an optional, the while loop condition is actually:

![(view != nil) && (view! is UIView)]

This simplifies down to:

(view == nil) || !(view! is UIView)

If we plug that into our example, we get:

class SomeView: UIView {
    override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
        var view: UIView? = super.hitTest(.zero, with: nil)
        while (view == nil) || !(view! is UIView) {
            guard let v: UIView = view else { continue }
            view = v.superview
        }
        return nil
    }
}

Notice how this means that if view is nil, we get an infinite loop.

Interestingly, when we hit the bug in simplify CFG, the compiler has already simplified this more by eliminating the cast.

class SomeView: UIView {
    override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
        var view: UIView? = super.hitTest(.zero, with: nil)
        while view == nil {
            guard let v: UIView = view else { continue }
            view = v.superview
        }
        return nil
    }
}

and then propagating the view == nil information into the while loop:

class SomeView: UIView {
    override func hitTest(_ point: CGPoint, with event: UIEvent?) -> UIView? {
        var view: UIView? = super.hitTest(.zero, with: nil)
        while view == nil {
            continue
        }
        return nil
    }
}

I imagine that isn't what you intended. Is this code you are actually using? Or was this just a code twiddle that you did not expect to work at runtime, but did expect the compiler not infinite loop on.

@gottesmm
Copy link
Member

#26660

@keith
Copy link
Collaborator Author

keith commented Aug 15, 2019

Thanks! Yea it looks like it was some dead code, but I do think because of other implementation details from our case that I simplified out this wouldn't have actually ever resulted in an infinite loop

@belkadan
Copy link
Contributor

Looks like Michael forgot to move this to Done. It should be in 5.1 and master by now, though not any Xcode yet.

@gottesmm
Copy link
Member

Yes. Thanks!

@swift-ci swift-ci transferred this issue from apple/swift-issues Apr 25, 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 optimized only Flag: An issue whose reproduction requires optimized compilation SILOptimizer Area → compiler: SIL optimization passes
Projects
None yet
Development

No branches or pull requests

4 participants