[swift-dev] Having 64-bit swift_retain/release ignore all negative pointer values

Joe Groff jgroff at apple.com
Thu Oct 13 15:25:53 CDT 2016


> On Oct 13, 2016, at 1:18 PM, Greg Parker <gparker at apple.com> wrote:
> 
> 
>> On Oct 13, 2016, at 10:46 AM, John McCall via swift-dev <swift-dev at swift.org> wrote:
>> 
>>> On Oct 13, 2016, at 9:04 AM, Joe Groff via swift-dev <swift-dev at swift.org> wrote:
>>> 
>>>> On Mar 1, 2016, at 1:33 PM, Joe Groff via swift-dev <swift-dev at swift.org> wrote:
>>>> 
>>>> In swift_retain/release, we have an early-exit check to pass through a nil pointer. Since we're already burning branch, I'm thinking we could pass through not only zero but negative pointer values too on 64-bit systems, since negative pointers are never valid userspace pointers on our 64-bit targets. This would give us room for tagged-pointer-like optimizations, for instance to avoid allocations for tiny closure contexts.
>>> 
>>> I'd like to resurrect this thread as we look to locking down the ABI. There were portability concerns about doing this unilaterally for all 64-bit targets, but AFAICT it should be safe for x86-64 and Apple AArch64 targets. The x86-64 ABI limits the userland address space, per section 3.3.2:
>>> 
>>> Although the AMD64 architecture uses 64-bit pointers, implementations are only required to handle 48-bit addresses. Therefore, conforming processes may only use addresses from 0x00000000 00000000 to 0x00007fff ffffffff.
>>> 
>>> Apple's ARM64 platforms always enable the top-byte-ignore architectural feature, restricting the available address space to the low 56 bits of the full 64-bit address space in practice. Therefore, "negative" values should never be valid user-space references to Swift-refcountable objects. Taking advantage of this fact would enable us to optimize small closure contexts, Error objects, and, if we move to a reference-counted COW model for existentials, small `Any` values, which need to be refcountable for ABI reasons but don't semantically promise a unique identity like class instances do.
>> 
>> This makes sense to me.  if (x <= 0) return; should be just as cheap as is (x == 0) return;
> 
> Conversely, I wanted to try to remove such nil checks. Currently they look haphazard: some functions have them and some do not.
> 
> Allowing ABI space for tagged pointer objects is a much bigger problem than the check in swift_retain/release. For example, all vtable and witness table dispatch sites to AnyObject or any other type that might someday have a tagged pointer subclass would need to compile in a fallback path now. You can't dereference a tagged pointer to get its class pointer. 

True. I don't think we'd want to use this optimization for class types; I was specifically thinking of other things for which we use nullable refcounted representations, particularly closure contexts. The ABI for function types requires the context to be refcountable by swift_retain/release, but it doesn't necessarily have to be a valid pointer, if the closure formation site and invocation function agree on a tagged-pointer representation. We could also do interesting things with enums; if one payload type is a class reference and the rest are trivial, we could lay the enum out in such a way that we can use swift_retain/release on it by setting the high bit when tagging the trivial representations, saving us the need to emit a switch. We wouldn't actually dereference the pointer representation without checking it first.

I know we've discussed taking the nil check out of swift_retain/release, and possibly having separate variants that do include the null check for when we know we're working with Optionals. How much of difference would that really make, though? I'd expect it to be a fairly easily predictable branch, since most objects are likely to be nonnull in practice.

-Joe


More information about the swift-dev mailing list