We would try to take the `metatype` of the integer value, which doesn't work. If
a specialized parameter is an integer generic parameter, emit a code sequence
that compares the integer value. Fixes rdar://176876134.
This check is disabled by default, and enabled with
`-Xllvm -verify-debug-value-expr`, while we fix the remaining problems
that are creating wrongly typed debug info.
Assisted-by: Claude
We cannot use spare bits or other overlapping storage layout tricks with fundamentally
address-only enums, and we can take advantage of this to do borrowing switches or other
in-place projections without copying the value. However, for resilient enums, the
implementation may use spare bit packing, but the type must be handled address-only
outside of its defining module, and we didn't have a way to express that with
borrowing switch. Optimization passes have also been running into problems with the
complexity that we were using `unchecked_take_enum_data_addr` sometimes as a pure
operation. This patch splits the instruction into three:
- `unchecked_inplace_enum_data_addr` represents a nondestructive in-place enum
projection. It is only allowed for enums whose projection operation is
nondestructive.
- `unchecked_take_enum_data_addr` represents a destructive enum projection,
invalidating the enum and leaving the payload to be further consumed.
This matches the current instruction's semantics.
- `unchecked_borrow_enum_data_addr` represents a borrowing enum projection.
The instruction takes a second operand for "scratch" space, which the
enum representation may be copied into in order to avoid invalidating the
enum value, so the result is dependent on the lifetime of both the
original enum and the scratch buffer. This allows for borrowing switches
over resilient enums.
`unchecked_borrow_enum_data_addr` is implemented by taking advantage of the
"address-only enums can't do spare bit optimization" property at runtime.
We inspect the operand type's bitwise-borrowability from its metadata. If
the type is bitwise-borrowable, then we are allowed to bitwise-copy the
enum to the scratch space and apply the projection to the scratch space,
preserving the original value. If the type is not bitwise-borrowable, then
we cannot use spare bit optimization in its layout, so we apply the
projection in-place.
Fixes rdar://174952822.
Previously, the SILVerifier always recomputed DominanceInfo for
every function it verified. This is unnecesscary if previous passes have not
invalidated the analysis.
Query DominanceInfo from SIL's analysis infrastructure and pass that for
verification.
This speeds up -sil-verify-all builds of standard library around 10% and has
the potential to speed up macos CI testing (did not measure).
borrow accessors can be supported in classes when they return 'let' properties, global 'let'.
Remove blanket restriction and add tests for supported cases
Computing dominance relation between instructions in the same block was done with linear search, e.g. when checking if a value-use is before its lifetime ending instruction.
This resulted in quadratic complexity and very long compile times for very large basic blocks.
Now we do the dominance check with pre-computed instruction indices, which is O(0) instead of O(n).
https://github.com/swiftlang/swift/issues/86663
rdar://168511262
In the context of coroutine/yielding accessors, "unwind" means
that the second half of the coroutine (code _after_ the `yield`
statement) will not run if an exception is thrown during the access.
Unwinding was the default behavor for legacy `_read`/`_modify` coroutine
accessors.
For the new `yielding borrow`/`yielding mutate` accessors, unwinding
was optional behind a separate feature flag.
But the final SE-0474 dictates that unwinding is always _disabled_ for
the new yielding accessors. That is, the new yielding accessors always
run to completion, regardless of whether there are thrown exceptions within
the access scope. This was deemed essential to ensure that authors of
data structures could guarantee consistency.
This PR permanently disables unwinding behavior for the new accessors.
The feature flag still exists but has no effect.
A handful of tests that verified the unwinding behavior have been
edited to ensure that unwinding does _not_ happen even when the feature
flag is specified.
Make sure an `end_borrow` is emitted on the `dereference_borrow` so that
SILGenCleanup recognizes this pattern and lowers it to a `return_borrow`
as it does for returned `load_borrow`s. Add support to the Swift implementation
of `BorrowingInstruction` for `DereferenceBorrow`.
Since after address lowering, `Borrow` can remain loadable with a known-
layout address-only referent, we need instructions that handle three
forms:
- borrow and referent are both loadable values
- borrow is a value, but referent is address-only
- borrow and referent are both address-only
This new OSSA invariant simplifies many optimizations because they don't have to take care of the corner case of incomplete lifetimes in dead-end blocks.
The implementation basically consists of these changes:
* add the lifetime completion utility
* add a flag in SILFunction which tells optimization that they need to run the lifetime completion utility
* let all optimizations complete lifetimes if necessary
* enable the ownership verifier to check complete lifetimes
The thing with `optional<VerifierErrorEmitterGuard>` didn't work as expected.
This resulted in a null-pointer de-reference when printing a verifier error.
This has grown large enough and complex enough that it makes sense to go into
its own file.
This is a NFCI change. The only substantial changes is I added a small require
impl based on the one in SILVerifier and I eliminated the namespace
VerifyFlowSensitiveRulesDetails.
We do this since we need to be able to handle instructions that may have two
different results that independently need their scopes lifetime to be ended.
This also required me to change how we handled which instruction/argument we
emit an error about in the verifier. Previously we were using two global
variables that we made nullptr to control which thing we emitted an error about.
This was unnecessary. Instead I added a little helper struct that internally
controls what we will emit an error about and an external "guard" RAII struct
that makes sure we push/pop the instruction/argument we are erroring upon
correctly.
This showed up on and off again on the source-compatibility testsuite project hummingbird.
The gist of the problem is that transformations may not rewrite the
type of an inlined instance of a variable without also createing a
deep copy of the inlined function with a different name (and e.g., a
specialization suffix). Otherwise the modified inlined variable will
cause an inconsistency when later compiler passes try to create the
abstract declaration of that inlined function as there would be
conflicting declarations for that variable.
Since SILDebugScope isn't yet available in the SwiftCompilerSources
this fix just drop these variables, but it would be absolutely
possible to preserve them by using the same mechanism that SILCloner
uses to create a deep copy of the inlined function scopes.
rdar://163167975
This is necessary because we need to model its stack-allocation
behavior, although I'm not yet doing that in this patch because
StackNesting first needs to be taught to not try to move the
deallocation.
I'm not convinced that `async let` *should* be doing a stack allocation,
but it undoubtedly *is* doing a stack allocation, and until we have an
alternative to that, we will need to model it properly.
This should make it easier to add new builtins by "following the warnings" and
prevent us from not handling a builtin in IRGen.
When I did this, I discovered that if I did this naively, we would have
AddressOf show up twice in the switch. This turned out to be because:
1. AddressOf is a SIL builtin that semantically is expected to only result in
SIL being emitted instead of having a builtin "addressof" be emitted.
2. For what ever reason, we actually had code in IRGen to emit an AddressOf
BuiltinInst if we saw it (which we never should have)... but also later code
asserted that we would never see it b/c it is a "SIL only builtin".
3. When I converted the if statements to be case statements, helpfully the
compiler told me I had a duplicate case. After investigation, I found the above
meaning that I was able to just delete the IRGen handling.
So now we properly handle AddressOf by asserting. As an additional tactic to
make "SIL only builtins" even more explicit, I added code to the SIL verifier
that validates we never see a builtin inst that is a "SIL only builtin" and
added some comments to Builtins.def that elaborate on this.
This instruction converts Builtin.ImplicitActor to Optional<any Actor>. In the
process of doing so, it masks out the bits we may have stolen from the witness
table pointer of Builtin.ImplicitActor. The bits that we mask out are the bottom
two bits of the top nibble of the TBI space on platforms that support TBI (that
is bit 60,61 on arm64). On platforms that do not support TBI, we just use the
bottom two tagged pointer bits (0,1).
By using an instruction, we avoid having to represent the bitmasking that we are
performing at the SIL level and can instead just make the emission of the
bitmasking an IRGen detail. It also allows us to move detection if we are
compiling for AArch64 to be an IRGen flag instead of a LangOpts flag.
The instruction is a guaranteed forwarding instruction since we want to treat
its result as a borrowed projection from the Builtin.ImplicitActor.
This is currently not wired up to anything. I am going to wire it up in
subsequent commits.
The reason why we are introducing this new Builtin type is to represent that we
are going to start stealing bits from the protocol witness table pointer of the
Optional<any Actor> that this type is bitwise compatible with. The type will
ensure that this value is only used in places where we know that it will be
properly masked out giving us certainty that this value will not be used in any
manner without it first being bit cleared and transformed back to Optional<any
Actor>.
Allow two identical debug variables have different types if the types are or contain local archetypes.
When cloning SIL (e.g. in LoopUnroll) local archetypes are uniqued and therefore distinct in cloned instructions.
Fixes a SIL verification error
https://github.com/swiftlang/swift/issues/84899
rdar://162660981
edges into dead-end regions.
- Only treat edges *into* dead-end regions as special; edges internal
the region must use the normal rules.
- Conservatively merge information along those edges rather than just
picking one at random. This requires us to not walk into the region
until we've processed all of the edges to it.
- Make sure we prevent *any* stack allocations from being deallocated
if the stack is inconsistent entering a block.
Additionally, fix a bug which was incorrectly treating all blocks that
don't themselves exit the function as ultimately leading to unreachable,
which had inadvertently largely turned off the consistency check.
It's permitted for a `witness_method` instruction to have multiple
type-dependent operands. This can happen when for example when one
local archetype is defined in terms of another.
rdar://159211502