Remove expensive availability checks from Data replaceSubrange #1571
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
The funnel point for replacing subranges in
Datafor non-empty/inline representations is__DataStorage.replaceBytes(in:with:length:). Originally on Darwin, this function was declared usingNSRangeas the range parameter, and since it is@usableFromInlineit is never inlined and is part of our ABI.NSRangeexists in Foundation.framework but not in swift-foundation and other lower libraries, so usingNSRangehere is problematic.In macOS 14 aligned releases, we added a new
@usableFromInlineABI entry point that usesRange<Int>instead. This is the entry point that we use today on Linux/Windows, and in Foundation.framework the calling code has an availability check to call theRange<Int>variant on newer OS versions and theNSRangevariant on older OS versions. This solution isn't great and these availability checks are quite expensive and can show up as heavy parts of traces.To resolve the performance issue and make this possible to represent without a dependency on
NSRange, I replaced theNSRangevariant's parameter with a tuple. This tuple is the same layout asNSRange(so it is ABI compatible) and I used@_silgen_nameto preserve the symbol name despite the change in type. This means that we can reliably call this function in modules whereNSRangedoes not exist without performing an availability check. TheRange<Int>variant now just calls the tuple variant, and all call sites unconditionally call the tuple variant. I confirmed that building a binary with this change (that I validated makes a direct call to the__DataStorage.replaceBytesfunction) runs successfully on an OS without this change (i.e. the back deployment scenario) and forward deployment should be equivalent.