[swift-evolution] [Planning][Request] "constexpr" for Swift 5

Daryle Walker darylew at mac.com
Tue Aug 1 08:53:43 CDT 2017


> On Jul 31, 2017, at 4:37 PM, Gor Gyolchanyan <gor.f.gyolchanyan at icloud.com> wrote:
> 
>> 
>> On Jul 31, 2017, at 11:23 PM, John McCall <rjmccall at apple.com <mailto:rjmccall at apple.com>> wrote:
>> 
>>> 
>>> On Jul 31, 2017, at 4:00 PM, Gor Gyolchanyan <gor.f.gyolchanyan at icloud.com <mailto:gor.f.gyolchanyan at icloud.com>> wrote:
>>> 
>>> 
>>>> On Jul 31, 2017, at 10:09 PM, John McCall <rjmccall at apple.com <mailto:rjmccall at apple.com>> wrote:
>>>> 
>>>>> On Jul 31, 2017, at 3:15 AM, Gor Gyolchanyan <gor.f.gyolchanyan at icloud.com <mailto:gor.f.gyolchanyan at icloud.com>> wrote:
>>>>>> On Jul 31, 2017, at 7:10 AM, John McCall via swift-evolution <swift-evolution at swift.org <mailto:swift-evolution at swift.org>> wrote:
>>>>>> 
>>>>>>> On Jul 30, 2017, at 11:43 PM, Daryle Walker <darylew at mac.com <mailto:darylew at mac.com>> wrote:
>>>>>>> The parameters for a fixed-size array type determine the type's size/stride, so how could the bounds not be needed during compile-time? The compiler can't layout objects otherwise. 
>>>>>> 
>>>>>> Swift is not C; it is perfectly capable of laying out objects at run time.  It already has to do that for generic types and types with resilient members.  That does, of course, have performance consequences, and those performance consequences might be unacceptable to you; but the fact that we can handle it means that we don't ultimately require a semantic concept of a constant expression, except inasmuch as we want to allow users to explicitly request guarantees about static layout.
>>>>> 
>>>>> Doesn't this defeat the purpose of generic value parameters? We might as well use a regular parameter if there's no compile-time evaluation involved. In that case, fixed-sized arrays will be useless, because they'll be normal arrays with resizing disabled.
>>>> 
>>>> You're making huge leaps here.  The primary purpose of a fixed-size array feature is to allow the array to be allocated "inline" in its context instead of "out-of-line" using heap-allocated copy-on-write buffers.  There is no reason that that representation would not be supportable just because the array's bound is not statically known; the only thing that matters is whether the bound is consistent for all instances of the container.
>>>> 
>>>> That is, it would not be okay to have a type like:
>>>>  struct Widget {
>>>>    let length: Int
>>>>    var array: [length x Int]
>>>>  }
>>>> because the value of the bound cannot be computed independently of a specific value.
>>>> 
>>>> But it is absolutely okay to have a type like:
>>>>  struct Widget {
>>>>    var array: [(isRunningOnIOS15() ? 20 : 10) x Int]
>>>>  }
>>>> It just means that the bound would get computed at runtime and, presumably, cached.  The fact that this type's size isn't known statically does mean that the compiler has to be more pessimistic, but its values would still get allocated inline into their containers and even on the stack, using pretty much the same techniques as C99 VLAs.
>>> 
>>> I see your point. Dynamically-sized in-place allocation is something that completely escaped me when I was thinking of fixed-size arrays. I can say with confidence that a large portion of private-class-copy-on-write value types would greatly benefit from this and would finally be able to become true value types.
>> 
>> To be clear, it's not obvious that using an inline array is always a good move for performance!  But it would be a tool available for use when people felt it was important.
> 
> That's why I'm trying to push for compile-time execution system. All these problems (among many others) could be designed out of existence and the compiler would be incredibly simple in the light of all the different specific features that the community is asking for. But I do feel your urge to avoid inventing a bulldozer factory just for digging a hole in a sandbox. It doesn't have to be relied upon by the type checker or generic resolution mechanism. It would be purely auxiliary. But that would single-handedly move a large chunk of the compiler into stdlib and a huge portion of various little incidental proposals would fade away because they can now easily be implemented in Swift for specific purposes.
> 
>>>>> As far as I know, the pinnacle of uses for fixed-size arrays is having a compile-time pre-allocated space of the necessary size (either literally at compile-time if that's a static variable, or added to the pre-computed offset of the stack pointer in case of a local variable).
>>>> 
>>>> The difference between having to use dynamic offsets + alloca() and static offsets + a normal stack slot is noticeable but not nearly as extreme as you're imagining.  And again, in most common cases we would absolutely be able to fold a bound statically and fall into the optimal path you're talking about.  The critical guarantee, that the array does not get heap-allocated, is still absolutely intact.
>>> 
>>> Yet again, Swift (specifically - you in this case) is teaching me to trust the compiler to optimize, which is still an alien feeling to me even after all these years of heavy Swift usage. Damn you, C++ for corrupting my brain 😀.
>> 
>> Well.  Trust but verify. 🙂
> 
> The only good way I can think of doing that is hand-crafting a lightning-fast implementation LLVM IR, then doing the same in Swift, decompiling the bitcode and then doing a diff. It's going to be super tedious and painful, but it seems to be the only way to prove that Swift can (hopefully, some day...) replace C++ in sheer performance potential.
> 
>>> In the specific case of having dynamic-sized in-place-allocated value types this will absolutely work. But this raises a chicken-and-the-egg problem: which is built in what: in-place allocated dynamic-sized value types, or specifically fixed-size arrays? On one hand I'm tempted to think that value types should be able to dynamically decide (inside the initializer) the exact size of the allocated memory (no less than the static size) that they occupy (no matter if on the heap, on the stack or anywhere else), after which they'd be able to access the "leftover" memory by a pointer and do whatever they want with it. This approach seems more logical, since this is essentially how fixed-size arrays would be implemented under the hood. But on the other hand, this does make use of unsafe pointers (and no part of Swift currently relies on unsafe pointers to function), so abstracting it away behind a magical fixed-size array seems safer (with a hope that a fixed-size array of UInt8 would be optimized down to exactly the first case).
>> 
>> Representationally, I think we would have a builtin fixed-sized array type that.  But "fixed-size" means "the size is an inherent part of the type", not "we actually know that size statically".  Swift would just be able to use more optimal code-generation patterns for types whose bounds it was actually able to compute statically.
> 
> Well, yeah, knowing its size statically is not a requirement, but having a guarantee of in-place allocation is. As long as non-escaped local fixed-size arrays live on the stack, I'm happy. 🙂

I was neutral on this, but after waking up I realized a problem. I want to use the LLVM type primitives to implement fixed-size arrays. Doing a run-time determination of layout and implementing it with alloca forfeits that (AFAIK). Unless the Swift run-time library comes with LLVM (which I doubt). Which means we do need compile-time constants after all.

— 
Daryle Walker
Mac, Internet, and Video Game Junkie
darylew AT mac DOT com 

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.swift.org/pipermail/swift-evolution/attachments/20170801/357772c7/attachment.html>


More information about the swift-evolution mailing list