> On Oct 20, 2016, at 10:50 AM, Joe Groff <jgr...@apple.com> wrote:
>> On Oct 20, 2016, at 10:45 AM, John McCall <rjmcc...@apple.com
>> <mailto:rjmcc...@apple.com>> wrote:
>>
>>> On Oct 19, 2016, at 8:42 PM, Joe Groff via swift-dev <swift-dev@swift.org
>>> <mailto:swift-dev@swift.org>> wrote:
>>> I had a discussion with Steve this morning about the potential for enum
>>> layout optimization with floating-point payloads. One great thing about
>>> floating-point is that it has NaNs, and lots of them. For the most part,
>>> the only significant semantic difference among these NaNs is whether
>>> they're signaling or not, so it's tempting to reclaim some of these
>>> representations for other purposes. Javascript engines are of course famous
>>> for "NaN-encoding" values, representing JS numbers as native Doubles and
>>> packing pointers to non-number object instances into the same
>>> representation by using NaN bit patterns. In Swift, we could do similar
>>> things for enums with floating-point payloads, making 'Float?' and
>>> 'Double?' take the same amount of space as non-optionals, and automatically
>>> applying Javascript-style layout optimizations when defining enums with
>>> mixed float and object payloads. IEEE 754 is ambivalent about the role of
>>> NaN payloads, and there are libraries that use payloads for interesting
>>> diagnostic purposes, but the standard declares up front that it "does not
>>> interpret the sign of a NaN" (section 6.3). Reserving "negative" NaNs with
>>> the sign bit set as extra inhabitants could let us do enum layout
>>> optimization without interfering with the ability for libraries to freely
>>> use NaN payloads.
>>>
>>> However, the way we usually handle enum optimization with extra inhabitants
>>> is problematic for floats. We normally say that it is undefined behavior
>>> for a value to have an extra inhabitant representation—a class reference
>>> cannot be null, a Bool can only be 0 or 1, and so on. With floats, we need
>>> to interoperate with numerics code not written in Swift, and we want to be
>>> able to read floating-point data out of memory that may use arbitrary bit
>>> patterns. We don't want every double-returning C function or load from
>>> memory to require a check for reserved values afterward. Making it
>>> undefined behavior for floats to have "extra inhabitant" representations is
>>> thus probably not practical.
>>>
>>> Instead of saying that extra inhabitants are undefined behavior, we could
>>> instead continue to allow Floats and Doubles to have arbitrary bit
>>> patterns, and only check for reserved values at the point we construct an
>>> enum that wants to use reserved values for layout. If we reserve negative
>>> NaNs, then for example, constructing a Float? or Double? from a nonoptional
>>> value would check whether the payload value is NaN and if so, clear the
>>> sign bit at that point. That way, we don't have any ABI problems with
>>> Floats and Doubles from foreign sources, but still get the benefits of
>>> layout optimization for Swift types. On the other hand, this would mean
>>> that supposedly-idempotent operations like '.some(x)!' lose the sign
>>> information for NaNs. Since we wouldn't want to prevent the optimizer from
>>> folding those kinds of operations away, we could define Swift's semantics
>>> to say that querying the sign of a NaN value produces an unspecified value.
>>> This matches the intent of IEEE 754, and shouldn't impact most numerics
>>> code in practice. If we were interested in pursuing enum layout
>>> optimization with float payloads, I think this would be the best approach.
>>
>> As an implementation matter, is this going to significantly complicate the
>> "make a T? from an unknown T" path? Currently, I think that logic just asks
>> whether a type has extra inhabitants; it doesn't have any notion of having
>> to rewrite actual values to avoid colliding with the "extra" inhabitants.
>
> It's true that it would no longer be a guaranteed identity operation, but we
> already have the notion of "inject tag" in the abstract access pattern for
> enums, which occurs after the payload has been stored, which normally sets
> the extra tag bits. It seems to me we could also use it to collapse NaN
> representations when the "some" tag is injected over a float (though we would
> need a "evacuateExtraInhabitantRepresentations" value witness to do this
> generically).
This seems reasonable. We could use the same thing to take advantage of spare
bits that the type doesn't promise to initialize, e.g. in a struct with
internal padding.
John.
_______________________________________________
swift-dev mailing list
swift-dev@swift.org
https://lists.swift.org/mailman/listinfo/swift-dev