Skip to content

IRGen: fast casting to final classes #41784

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
merged 1 commit into from
Apr 8, 2022

Conversation

eeckstein
Copy link
Contributor

@eeckstein eeckstein commented Mar 11, 2022

When casting a class instance to a final class, we can directly compare the isa-pointer with address of the metadata.
This avoids a call to swift_dynamicCastClass.
It also avoids a call to the metadata accessor of the class (which calls swift_getInitializedObjCClass).
For comparing the metadata pointers it's not required that the metadata is fully initialized.

It results in a significant performance improvement for class-casting intensive code. I measured 30% speedup for the new stack promotion + escape analysis, where we do a lot of dynamic instruction casting (#39438).

@eeckstein
Copy link
Contributor Author

@swift-ci test

@eeckstein
Copy link
Contributor Author

@swift-ci benchmark

@mikeash
Copy link
Contributor

mikeash commented Mar 11, 2022

Does emitHeapMetadataRefForHeapObject handle non-pointer isa and artificial subclasses?

@eeckstein
Copy link
Contributor Author

As discussed offline, ObjectiveC classes are excluded from this optimization anyway

@eeckstein
Copy link
Contributor Author

@swift-ci test

@eeckstein
Copy link
Contributor Author

@swift-ci benchmark

@tbkka
Copy link
Contributor

tbkka commented Mar 14, 2022

This looks like it should apply to is tests as well?

@atrick
Copy link
Contributor

atrick commented Mar 14, 2022

@tbkka When this exact check fails, do we then also need to check that the source's type is not __SwiftValue? If it is, then a dynamic call is needed to bridge to the final class.

Trying to answer my question: as long as this code is only reached when the static type of the cast's source is a class, then the dynamic type cannot be __SwiftValue. The static type of __SwiftValue is always a protocol.

@eeckstein this optimization only happens when the source's static type is a class right?

@eeckstein
Copy link
Contributor Author

This looks like it should apply to is tests as well?

is is done with the same SIL instruction as as: check_cast_br. So, yes, it applies to is as well

@eeckstein
Copy link
Contributor Author

this optimization only happens when the source's static type is a class right?

class or class existential, but not AnyObject

@atrick
Copy link
Contributor

atrick commented Mar 14, 2022

It turns out that casting an existential to a class may return a different object than the source of the cast. Once we fix CheckedCastInst::directlyForwarding, this case will be pessimized:

func castProtocolToFinal(_ p: Classes.P) -> Classes.Final? {
   return p as? Classes.Final
 }

Not sure how much that will affect the performance results until we get a runtime fix: rdar://90269352 (Always unwrap AnyHashable during dynamic casting)

@tbkka
Copy link
Contributor

tbkka commented Mar 15, 2022

@atrick and I looked at the current runtime casting implementation and this optimization is not currently safe if the source is any kind of existential (including AnyObject). It's currently only safe for casting between concrete class types. I think AnyHashable may be the only real problem here; I'm going to fix that shortly.

@eeckstein
Copy link
Contributor Author

I'm not sure about this. As far as I understand, this can only happen if the source is an AnyObject, but not another class existential.Also, isn't this only the case for Linux (non-objc interop runtime)?

@tbkka
Copy link
Contributor

tbkka commented Mar 15, 2022

As far as I understand, this can only happen if the source is an AnyObject, but not another class existential.Also, isn't this only the case for Linux (non-objc interop runtime)?

I need to do a little more verification, but studying the casting code, it looks like this is a problem for all class existentials on all platforms.

When casting a class instance to a final class, we can directly compare the isa-pointer with address of the metadata.
This avoids a call to `swift_dynamicCastClass`.
It also avoids a call to the metadata accessor of the class (which calls `swift_getInitializedObjCClass`).
For comparing the metadata pointers it's not required that the metadata is fully initialized.
@eeckstein
Copy link
Contributor Author

@atrick I'm using the new doesCastPreserveOwnershipForTypes check now

@eeckstein
Copy link
Contributor Author

@swift-ci test

2 similar comments
@eeckstein
Copy link
Contributor Author

@swift-ci test

@eeckstein
Copy link
Contributor Author

@swift-ci test

Copy link
Contributor

@atrick atrick left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@eeckstein eeckstein merged commit fb04b1c into swiftlang:main Apr 8, 2022
@eeckstein eeckstein deleted the fast-class-casts branch April 8, 2022 05:33
@compnerd
Copy link
Member

compnerd commented Apr 8, 2022

This seems to have caused a pretty big regression on Foundation on Windows (CC: @millenomi) https://ci-external.swift.org/job/oss-swift-windows-toolchain-x86_64-vs2019/447/console

@eeckstein
Copy link
Contributor Author

Thanks, I'll disable this optimization for Windows.

slavapestov added a commit to slavapestov/swift that referenced this pull request Jul 12, 2022
…imization

This is a regression from swiftlang#41784.

Fixes rdar://problem/93822961.
slavapestov added a commit to slavapestov/swift that referenced this pull request Jul 13, 2022
…imization

This is a regression from swiftlang#41784.

Fixes rdar://problem/93822961.
hborla pushed a commit to hborla/swift that referenced this pull request Jul 21, 2022
…imization

This is a regression from swiftlang#41784.

Fixes rdar://problem/93822961.
Catfish-Man pushed a commit to Catfish-Man/swift that referenced this pull request Jul 28, 2022
…imization

This is a regression from swiftlang#41784.

Fixes rdar://problem/93822961.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants