Re: [swift-dev] Semantic ARC and enum projection (Re: Having 64-bit swift_retain/release ignore all negative pointer values)

2016-10-17 Thread Michael Gottesman via swift-dev

> On Oct 17, 2016, at 12:40 PM, John McCall  wrote:
> 
>> On Oct 17, 2016, at 10:19 AM, Joe Groff via swift-dev  
>> wrote:
>>> On Oct 17, 2016, at 9:57 AM, Michael Gottesman  wrote:
>>> 
 
 On Oct 17, 2016, at 9:42 AM, Joe Groff via swift-dev  
 wrote:
 
 
> On Oct 16, 2016, at 1:10 PM, Dave Abrahams via swift-dev 
>  wrote:
> 
> 
> on Thu Oct 13 2016, Joe Groff  wrote:
> 
>>> On Oct 13, 2016, at 1:18 PM, Greg Parker  wrote:
>>> 
>>> 
 On Oct 13, 2016, at 10:46 AM, John McCall via swift-dev 
  wrote:
 
>> 
> On Oct 13, 2016, at 9:04 AM, Joe Groff via swift-dev 
>  wrote:
> 
>> On Mar 1, 2016, at 1:33 PM, Joe Groff via swift-dev 
>>  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 0x  to 0x7fff .
> 
> 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. 
> 
> Well, but we'd like to take advantage of the same kind of optimization
> for the small string optimization.  It doesn't seem like this should be
> handled differently just because the string buffer is a class instance
> and not a closure context.
 
 String is a struct, and small strings don't have to be modeled as class 
 instances. An enum { case Big(StringStorage), Small(Int63) } or similar 
 layout should be able to take advantage of swift_retain/release ignoring 
 negative values too.
>>> 
>>> I need to catch up on this thread, but there is an important thing to 
>>> remember. If you use an enum like this there are a few potential issues:
>>> 
>>> 1. In the implementation, you will /not/ want to use the enum internally. 
>>> This would prevent the optimizer from eliminating all of the Small Case 
>>> reference counting operations. This means you would rewrap the internal 
>>> value when you return one and when you enter into an internal 
>>> implementation code path try to immediately switch to a specialized small 
>>> case path if you can.
>> 
>> This poses an interesting question for the semantic ARC model with enums. I

Re: [swift-dev] Semantic ARC and enum projection (Re: Having 64-bit swift_retain/release ignore all negative pointer values)

2016-10-17 Thread John McCall via swift-dev
> On Oct 17, 2016, at 10:19 AM, Joe Groff via swift-dev  
> wrote:
>> On Oct 17, 2016, at 9:57 AM, Michael Gottesman  wrote:
>> 
>>> 
>>> On Oct 17, 2016, at 9:42 AM, Joe Groff via swift-dev  
>>> wrote:
>>> 
>>> 
 On Oct 16, 2016, at 1:10 PM, Dave Abrahams via swift-dev 
  wrote:
 
 
 on Thu Oct 13 2016, Joe Groff  wrote:
 
>> On Oct 13, 2016, at 1:18 PM, Greg Parker  wrote:
>> 
>> 
>>> On Oct 13, 2016, at 10:46 AM, John McCall via swift-dev 
>>>  wrote:
>>> 
> 
 On Oct 13, 2016, at 9:04 AM, Joe Groff via swift-dev 
  wrote:
 
> On Mar 1, 2016, at 1:33 PM, Joe Groff via swift-dev 
>  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 0x  to 0x7fff .
 
 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. 
 
 Well, but we'd like to take advantage of the same kind of optimization
 for the small string optimization.  It doesn't seem like this should be
 handled differently just because the string buffer is a class instance
 and not a closure context.
>>> 
>>> String is a struct, and small strings don't have to be modeled as class 
>>> instances. An enum { case Big(StringStorage), Small(Int63) } or similar 
>>> layout should be able to take advantage of swift_retain/release ignoring 
>>> negative values too.
>> 
>> I need to catch up on this thread, but there is an important thing to 
>> remember. If you use an enum like this there are a few potential issues:
>> 
>> 1. In the implementation, you will /not/ want to use the enum internally. 
>> This would prevent the optimizer from eliminating all of the Small Case 
>> reference counting operations. This means you would rewrap the internal 
>> value when you return one and when you enter into an internal implementation 
>> code path try to immediately switch to a specialized small case path if you 
>> can.
> 
> This poses an interesting question for the semantic ARC model with enums. It 
> seems to me that, if switching or projecting the payload of an enum was a 
> consuming operation, that we could avoid this optimization pitfall. Switch

Re: [swift-dev] Semantic ARC and enum projection (Re: Having 64-bit swift_retain/release ignore all negative pointer values)

2016-10-17 Thread Michael Gottesman via swift-dev

> On Oct 17, 2016, at 10:19 AM, Joe Groff  wrote:
> 
> 
>> On Oct 17, 2016, at 9:57 AM, Michael Gottesman  wrote:
>> 
>>> 
>>> On Oct 17, 2016, at 9:42 AM, Joe Groff via swift-dev  
>>> wrote:
>>> 
>>> 
 On Oct 16, 2016, at 1:10 PM, Dave Abrahams via swift-dev 
  wrote:
 
 
 on Thu Oct 13 2016, Joe Groff  wrote:
 
>> On Oct 13, 2016, at 1:18 PM, Greg Parker  wrote:
>> 
>> 
>>> On Oct 13, 2016, at 10:46 AM, John McCall via swift-dev 
>>>  wrote:
>>> 
> 
 On Oct 13, 2016, at 9:04 AM, Joe Groff via swift-dev 
  wrote:
 
> On Mar 1, 2016, at 1:33 PM, Joe Groff via swift-dev 
>  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 0x  to 0x7fff .
 
 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. 
 
 Well, but we'd like to take advantage of the same kind of optimization
 for the small string optimization.  It doesn't seem like this should be
 handled differently just because the string buffer is a class instance
 and not a closure context.
>>> 
>>> String is a struct, and small strings don't have to be modeled as class 
>>> instances. An enum { case Big(StringStorage), Small(Int63) } or similar 
>>> layout should be able to take advantage of swift_retain/release ignoring 
>>> negative values too.
>> 
>> I need to catch up on this thread, but there is an important thing to 
>> remember. If you use an enum like this there are a few potential issues:
>> 
>> 1. In the implementation, you will /not/ want to use the enum internally. 
>> This would prevent the optimizer from eliminating all of the Small Case 
>> reference counting operations. This means you would rewrap the internal 
>> value when you return one and when you enter into an internal implementation 
>> code path try to immediately switch to a specialized small case path if you 
>> can.
> 
> This poses an interesting question for the semantic ARC model with enums. It 
> seems to me that, if switching or projecting the payload of an enum was a 
> consuming operation, that we could avoid this optimization pitfall. Switching 
> the

[swift-dev] Semantic ARC and enum projection (Re: Having 64-bit swift_retain/release ignore all negative pointer values)

2016-10-17 Thread Joe Groff via swift-dev

> On Oct 17, 2016, at 9:57 AM, Michael Gottesman  wrote:
> 
>> 
>> On Oct 17, 2016, at 9:42 AM, Joe Groff via swift-dev  
>> wrote:
>> 
>> 
>>> On Oct 16, 2016, at 1:10 PM, Dave Abrahams via swift-dev 
>>>  wrote:
>>> 
>>> 
>>> on Thu Oct 13 2016, Joe Groff  wrote:
>>> 
> On Oct 13, 2016, at 1:18 PM, Greg Parker  wrote:
> 
> 
>> On Oct 13, 2016, at 10:46 AM, John McCall via swift-dev 
>>  wrote:
>> 
 
>>> On Oct 13, 2016, at 9:04 AM, Joe Groff via swift-dev 
>>>  wrote:
>>> 
 On Mar 1, 2016, at 1:33 PM, Joe Groff via swift-dev 
  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 0x  to 0x7fff .
>>> 
>>> 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. 
>>> 
>>> Well, but we'd like to take advantage of the same kind of optimization
>>> for the small string optimization.  It doesn't seem like this should be
>>> handled differently just because the string buffer is a class instance
>>> and not a closure context.
>> 
>> String is a struct, and small strings don't have to be modeled as class 
>> instances. An enum { case Big(StringStorage), Small(Int63) } or similar 
>> layout should be able to take advantage of swift_retain/release ignoring 
>> negative values too.
> 
> I need to catch up on this thread, but there is an important thing to 
> remember. If you use an enum like this there are a few potential issues:
> 
> 1. In the implementation, you will /not/ want to use the enum internally. 
> This would prevent the optimizer from eliminating all of the Small Case 
> reference counting operations. This means you would rewrap the internal value 
> when you return one and when you enter into an internal implementation code 
> path try to immediately switch to a specialized small case path if you can.

This poses an interesting question for the semantic ARC model with enums. It 
seems to me that, if switching or projecting the payload of an enum was a 
consuming operation, that we could avoid this optimization pitfall. Switching 
the enum { case Big(Class), Small(Trivial) } or similar case would semantically 
eliminate the nontrivial enum value and leave only the trivial payload behind.

-Joe
__