This used to crash because the code storing empty payload enum tag values would
use the bit width of the tag (32 bit) as the minimum unit to store to the
payload even if the actual bits required to store the biggest tag value in the
payload was much smaller.
With payload bit-widths < 32bit we would run out of space crashing looking for
new payload to store the value to ...
Instead pass the maximum size of the bits that need storing down.
rdar://26926035
Instead of hooking into nominal type and extension emission
and walking all conformances of those declarations, let's
just directly hook into the logic for emitting conformances.
This fixes an issue where we would apparently emit duplicate
conformances, as well as unnecessary conformances that are
defined elsewhere.
Rather than collection nominal type and extension decls and emit
reflection metadata records in one go, we can emit them as they
are encountered and instead collection builtin types referenced
by those at the end.
specialization to be separately lowered in IRGen, use the mangling
of the specialized type as the name of the llvm::StructType instead
of the base, unspecialized type.
This tends to produce fewer collisions between IR type names.
LLVM does unique the names on its own, so that's not strictly
necessary, but it's still a good idea because it makes the test
output more reliable and somewhat easier to read (modulo the
impact of bigger type names). Collisions will still occur if
the type is specialized at an archetype, since in this case we
will fall back on the unspecialized type.
Properly lower reference counting SIL instructions with nonatomic attribute as invocations of corresponding non-atomic reference counting runtime functions.
Without this, fixed-sized, address-only enums failed to correctly delegate their `getFixedExtraInhabitantMask` implementation to the enum layout strategy, causing a miscompile where switches would test undefined bits of the enum payload. Fixes rdar://problem/24885747.
- Implement emission of type references for nominal type field
reflection, using a small custom encoder resulting in packed
structs, not strings. This will let us embed 7-bit encoded
32-bit relative offsets directly in the structure (not yet
hooked in).
- Use the AST Mangler for encoding type references
Archetypes and internal references were complicating this before, so we
can take the opportunity to reuse this machinery and avoid unique code
and new ABI.
Next up: Tests for reading the reflection sections and converting the
demangle tree into a tree of type references.
Todo: For concrete types, serialize the types for associated types of
their conformances to bootstrap the typeref substitution process.
rdar://problem/15617914
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.
Decrease the size of nominal type descriptors and make them true-const by relative-addressing the other metadata they need to reference, which should all be included in the same image as the descriptor itself. Relative-referencing string constants exposes a bug in the Apple linker, which crashes when resolving relative relocations to coalesceable symbols (rdar://problem/22674524); work around this for now by revoking the `unnamed_addr`-ness of string constants that we take relative references to. (I haven't tested whether GNU ld or gold also have this problem on Linux; it may be possible to conditionalize the workaround to only apply to Darwin targets for now.)
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.
In a few places, we have to be careful about the distinction between
"empty in this resilience domain" versus "empty in all resilience
domains". Make callers think about this by adding a parameter instead
of relying on them to check isFixedSize() as necessary first.
While making this change I noticed that the code for checking if
types are empty when computing extra inhabitants of structs and enums
might be slightly wrong in the face of resilience; I will revisit
this later.
Instead of categorically forbidding caching within a conditional
scope, permit it but remember how to remove the cache entries.
This means that ad-hoc emission code can now get exactly the
right caching behavior if they use this properly. In keeping
with that, adjust a bunch of code to properly nest scopes
according to the conditional paths they enter.
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.
If an enum case has a payload but the unsubstituted payload type is
zero-sized, we would convert the case into a no-payload case.
This was valid when the only invariant that had to be preserved
is that an enum's layout is the same between all substitutions
of a generic type.
However this is now wrong if the payload type is resiliently-sized,
because other resilience domains may not have knowledge that it is
zero-sized.
The new utility methods will also be used in class layout.
If an enum is public and @_fixed_layout, we can only use fixed-size
optimizations if the payload types are fixed-size in all resilience
domains.
However, if an enum is resilient or internal to a module, other
resilience domains cannot have knowledge of its layout, so we can
still use fixed-size optimizations if the payload types are known
to be fixed-size in the current resilience domain only.
If an enum is fixed-layout in our resilience domain but not
universally fixed-layout, other resilience domains will use
runtime functions to project and inject payloads.
These expect to find the payload size in the metadata, so
emit it if the enum is not universally fixed-layout.
Note that we do know the payload size, so it is constant
for us; there's no runtime call required to initialize
the metadata.
If the payload has insufficient extra inhabitants and there's more than one empty case that requires spilling extra tag bits, we need to check the payload part of the value to discriminate them.
Decrease the size of nominal type descriptors and make them true-const by relative-addressing the other metadata they need to reference, which should all be included in the same image as the descriptor itself. Relative-referencing string constants exposes a bug in the Apple linker, which crashes when resolving relative relocations to coalesceable symbols (rdar://problem/22674524); work around this for now by revoking the `unnamed_addr`-ness of string constants that we take relative references to. (I haven't tested whether GNU ld or gold also have this problem on Linux; it may be possible to conditionalize the workaround to only apply to Darwin targets for now.)
We were checking if the tag was <= numPayloadCases, not <. Fix this
and swap the order of the basic blocks so that the no-tag case is
the fall through for consistency with getEnumTag().
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!
This is a bit of a hodge-podge of related changes that I decided
weren't quite worth teasing apart:
First, rename the weak{Retain,Release} entrypoints to
unowned{Retain,Release} to better reflect their actual use
from generated code.
Second, standardize the names of the rest of the entrypoints around
unowned{operation}.
Third, standardize IRGen's internal naming scheme and API for
reference-counting so that (1) there are generic functions for
emitting operations using a given reference-counting style and
(2) all operations explicitly call out the kind and style of
reference counting.
Finally, implement a number of new entrypoints for unknown unowned
reference-counting. These entrypoints use a completely different
and incompatible scheme for working with ObjC references. The
primary difference is that the new scheme abandons the flawed idea
(which I take responsibility for) that we can simulate an unowned
reference count for ObjC references, and instead moves towards an
address-only scheme when the reference might store an ObjC reference.
(The current implementation is still trivially takable, but that is
not something we should be relying on.) These will be tested in a
follow-up commit. For now, we still rely on the bad assumption of
reference-countability.
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.
The previous patch added an assert, but it turns out we could still
end up in this code path if the payload was fixed-size *and*
address-only. This is not a very common case, so don't try to
optimize it.
In the indirect initializeWithCopy value witness, we had a
terrible hack where the *source* of the initialization was
modified, stripping and emplacing the tag bits before and
after the copy of the payload, respectively.
Since address-only types should not have spare bits at all,
replace the hack with an assertion.
For example, if a @_fixed_layout struct A contains a resilient struct B
from the same module M, then inside M, A can have a fixed size, but
outside, A has a dynamic size because B is opaque. In this case, A is
not "universally fixed-size". This impacts multi-payload enums, because
if A is placed inside a multi-payload enum E which is lowered inside X,
we would get a fixed layout with spare bits, but lowering E outside of
X would yield a dynamic layout. This is incorrect.
Fix this by plumbing through a new predicate IsAlwaysFixedSize, which
is similar to IsPOD and IsBitwiseTakable, where a compound type inherits
the property if all leaf types exhibit it, and only use spare bits if
the original and substituted types have this property.
We were calling hasTypeParameter() on the interface type of the enum
element. Since enum elements are case constructors now, the interface
type was a GenericFunctionType, and since conceptually these cannot
contain free type variables, this would always return to false.
The right fix here is to pass down the unsubstituted type info and
look at the spare bits of that when doing multi-payload enum layout.
Now that this works, we can remove a FIXME that was added to patch
around this.
John and I discussed this and agreed that we only need two cases here,
not four. In the future this may be merged with ResilienceExpansion,
and become a struct with additional availability information, but
we're definitely sure we don't need four levels here.
Replace isSingle{Unknown,Swift}ReferenceCountedObject() with a single
entry point that also returns the reference counting style. Use this
in GenEnum to emit more specific entry points than native and unknown.
This will give a slight performance boost on Darwin, and enable use of
the blocks runtime on Linux.
Progress on <rdar://problem/23315750>.
Fixes two issues with the original patch:
- If numCaseBits was >= 32, the high bits of the tag were shifted
by an incorrect amount. In this case, the high bits were always
zero anyway, but the invalid shift triggered an assertion in
ARM64 codegen. Fixed to not generate the offending instructions
at all, since just loading the payload value is enough.
- When the payload is very small, the number of no-payload cases
could be greater than 2**payloadBits, requiring multiple payload
tags to represent them all. There was an arithmetic error in
this case.
This re-applies r31328.
Swift SVN r31365