Recent changes added support for resiliently-sized enums, and
enums resilient to changes in implementation strategy.
This patch adds resilient case numbering, fixing the problem
where adding new payload cases would break existing code by
changing the numbering of no-payload cases.
The problem is that internally, enum cases are numbered with payload
cases coming first, followed by no-payload cases. While each list
is itself in declaration order, with new additions coming at the
end, we need to partition it to give us a fast runtime test for
"is this a payload or no-payload case index."
The resilient numbering strategy used here is that the getEnumTag
and destructiveInjectEnumTag value witness functions now take a
tag index in the range [-ElementsWithPayload..ElementsWithNoPayload-1].
Payload elements are numbered in *reverse* declaration order, so
adding new payload cases yields decreasing tag indices, and adding
new no-payload cases yields increasing tag indices, allowing use
sites to be resilient.
This adds the adjustment between 'fragile' and 'resilient' tag
indices in a somewhat unsatisfying manner, because the calculation
could be pushed down further into EnumImplStrategy, simplifying
both the IRGen code and the generated IR. I'll clean this up later.
In the meantime, clean up some other stuff in GenEnum.cpp, mostly
abstracting code that walks cases.
Use them to generate value witnesses when the type has dynamic packing.
Regularize the interface for calling value witnesses.
Not a huge difference yet, although we do re-use local type data
a little more effectively now.
There are several interesting new features here.
The first is that, when emitting a SILFunction, we're now able to
cache type data according to the full dominance structure of the
original function. For example, if we ask for type metadata, and
we've already computed it in a dominating position, we're now able
to re-use that value; previously, we were limited to only doing this
if the value was from the entry block or the LLVM basic block
matched exactly. Since this tracks the SIL dominance relationship,
things in IRGen which add their own control flow must be careful
to suppress caching within blocks that may not dominate the
fallthrough; this mechanism is currently very crude, but could be
made to allow a limited amount of caching within the
conditionally-executed blocks.
This query is done using a proper dominator tree analysis, even at -O0.
I do not expect that we will frequently need to actually build the
tree, and I expect that the code-size benefits of doing a real
analysis will be significant, especially as we move towards making
more metadata lazily computed.
The second feature is that this adds support for "abstract"
cache entries, which indicate that we know how to derive the metadata
but haven't actually done so. This code isn't yet tested, but
it's going to be the basis of making a lot of things much lazier.
This value witness function takes an address of an enum value where the
payload has already been initialized, together with a case index, and
forms the enum value.
The formal behavior can be thought of as satisfying an identity in
relation to the existing two enum value witnesses. For any enum
value, the following is to leave the value unchanged:
tag = getEnumTag(value)
destructiveProjectEnumData(value)
destructiveInjectEnumData(value, tag)
This is the last missing piece for the inject_enum_addr SIL instruction
to handle resilient enums, allowing the implementation of an enum to be
decoupled from its uses. Also, it should be useful for dynamically
constructing enum cases with write reflection, once we get around to
doing such a thing.
The body of the value witness is emitted by a new emitStoreTag() method
on EnumImplStrategy. This is similar to the existing storeTag(), except
the case index is a value instead of a contant.
This is implemented as follows for the different enum strategies:
1) For enums consisting of a single case, this is trivial.
2) For enums where all cases are empty, stores the case index into the
payload area.
3) For enums with a single payload case, emits a call to a runtime
function. Note that for non-generic single payload enums, this could
be open-coded more efficiently, but the function still has the
correct behavior since it supports extra inhabitants and so on.
A follow-up patch will make this more efficient.
4) For multi-payload enums, there are two cases:
a) If one of the payloads is generic or resilient, the enum is
dynamically-sized, and a call to a runtime function is emitted.
b) If the entire enum is fixed-size, the value witness checks if
the case is empty or not.
If the case has a payload, the case index is swizzled into
spare bits of the payload, if any, with remaining bits going
into the extra tag area.
If the case is empty, the case index is swizzled into the
spare bits of the payload, the remaining bits of the payload,
and the extra tag area.
The implementations of emitStoreTag() duplicate existing logic in the
enum strategies, in particular case 4)b) is rather complicated.
Code cleanups are welcome here!
Resilient enums are manipulated as opaque values.
Clients are still allowed to assume physical case indices and case
payload types for now -- we might add a level of indirection here,
which would require designing a new case dispatch mechanism.
Resilient enums are never constructed directly, only by calling
case constructor functions. Case constructors already get emitted,
however they're [transparent] -- this will change in a subsequent
patch.
We could save on code size by emitting an InjectEnumTag value
witness function that can construct any case given a physical case
number, rather than emitting constructors for each case, but for
now going through case constructor functions will suffice.
These will be used for reflection, and eventually to speed up generic
operations on single payload enums as well.
Progress on <rdar://problem/21739870>.
Swift SVN r30214
This nicely gathers all the layout information together in one contiguous bundle we can potentially emit independently for use in generic type layout. A step on the way to rdar://problem/19898165.
Swift SVN r30128
Don't project every value witness from the metadata every time we need one; this wastes code size in a way LLVM can't really optimize since it doesn't know the metadata is immutable. The code size wins on the standard library are disappointingly small (stdlib only shrinks by 4KB), but this makes generic IR a lot more compact and easier to read.
Swift SVN r28095
We've had a rash of bugs due to inconsistencies between how IRGen and the runtime think types are laid out. Add a '-verify-type-layout' mode to the frontend that causes IRGen to emit a bunch of code that compares its static assumptions against what the runtime value witness does.
Swift SVN r24918
initializeBufferWithTakeOfBuffer value witness.
Attempt to use initializeBufferWithTakeOfBuffer in
some appropriate places.
There are some changes enabled by this which are
coming in a follow-up patch.
Swift SVN r20741
Add Builtin.destroyArray, .copyArray, .takeArrayFrontToBack, and .takeArrayBackToFront, which perform bulk destroy/copy/take operations using memcpy/memmove, a loop, or a generic value witness.
Swift SVN r17009
Add value witnesses for destroyArray, initializeArrayWithCopy, and initializeArrayWithTake{FrontToBack,BackToFront}, and fill out the runtime value witness table implementations. Stub out the IRGen ones for now.
Swift SVN r16772
If a single-payload union doesn't use up all of its payload's extra inhabitants, it can claim the remaining ones as its own. While we're here, specialize the extra inhabitants implementation for no-payload unions to generously (and more efficiently) give out all integer values within its storage type above the largest discriminator as extra inhabitants, instead of relying entirely on the spare bits mask.
Swift SVN r8470
These value witness table entries will be conditionally available for types that support specialized union representation through extra inhabitants and/or spare bits and for union value witnesses:
- storeExtraInhabitant, to store an extra inhabitant representation;
- getExtraInhabitantIndex, to recognize an extra inhabitant representation;
- getUnionTag, to get a union's discriminator; and
- inplaceProjectUnionData, to extract the value in place from a union.
This just sets up the enumerators and related IR types and mangling; nothing emits these witnesses yet.
Swift SVN r7234
The value witnesses are always available through type metadata (through an extra indirection). Saving that indirection costs 16 words (and growing!) in every witness table, and when we start instantiating conformances for generic instances, would require us to instantiate practically every generic witness table. Removing the value witnesses from the protocol witness table means we will only need to instantiate witness tables when associated types are dependent on the conforming type's type variables.
This is an ABI break, but should have no user-visible functional change.
Swift SVN r6651
Provide a lowering for the DeinitExistential instruction to deallocate the buffer in an existential container with an uninitialized value, using its deallocateBuffer witness.
Swift SVN r5623
storage in a flags word in the value witness table. Pack the
alignment into only 16 bits of this word. Optimize tuple value
witnesses based on whether they're POD and inline.
Swift SVN r5138
To be able to get the dynamic type of a generic value, the 'typeof' operation needs to be part of the value witness for the type. Add 'typeof' to the value witness table layout, and in the runtime, provide standard typeof witnesses for static, Swift class, and ObjC class values.
Swift SVN r5013