Code like this currently looks like a lifetime escape:
struct Ref<T: ~Copyable & ~Escapable>: ~Escapable {
private let ref: Builtin.Borrow<T>
@_lifetime(borrow target)
init(_ target: borrowing T) {
self.ref = Builtin.makeBorrow(target)
}
}
This is blocking the implementation of `Ref<~Escapable>`.
Fixes rdar://176564359 ([nonescapable] support Builtin.makeBorrow in
lifetime diagnostics)
This is especially important for Embedded Swift because non de-virtualized deinits result in IRGen crashes.
Fixes a compiler crash in embedded
rdar://175984319
Rather than doing a standard swift runtime cast to an existential, explicitly check for the conforming instruction classes, which is much faster.
The new `isFullApplySite` and `isReturnInstruction` casting utilities are used in the (very few) time critical places in the optimizer.
After toolchain builders are upgraded to a compiler version which includes the fix for this problem (https://github.com/swiftlang/swift/pull/88270), we don't need this workaround anymore and the regular `as`/`is` casts can be used again.
Now the runtime casts doesn't show up prominently in compile-time profiling data anymore - even with a host compiler which doesn't implement fast type checks, yet.
rdar://173916206
Passing a C++ object to the TSanInOutAccess builtin resulted in an extra
temporary copy. This copy was not optimized out because the semantics of this
builtin was not understood by the optimizer. Teaching the utils that this
intrinsic does not actually modify the object, does not escape it,
and does not read it lets the optimizer eliminate this copy.
Strictly speaking, the test code that uses interop is not safe/correct,
this is why it had a lifetime issue.
rdar://173921363
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.
When a `pointer_to_address` reinterprets the type (i.e. the address type
differs from the originating `address_to_pointer`), the accumulated
projection path is no longer meaningful. Use `anyValueFields` to be
conservative in both the walk-down and walk-up directions.
Without this fix, escape analysis could incorrectly conclude that a store
to an `alloc_stack` does not escape when it is read back through a
type-reinterpreting `pointer_to_address`, causing DeadStoreElimination
to illegally remove the store.
rdar://171841812
Builtin.Borrow<T> does not introduce a new borrow scope at the level of
SILValue. A SILValue that introduces a borrow scope is expected to be tracked by
OSSA--it cannot produce a trivial value. That borrow scope is expected to have a
scope-ending operation on all paths.
It's not clear how OSSA utilities and passes will handle the previous
implementation which doesn't honor those invariants. Fixing this is needed to
rely on Builtin.Borrow<T> more extensively.
Instead, treat the make_borrow instruction like an instantenous use. It produces
a Builtin.Borrow<T> value with ownership None. That value is completely ignored
by OSSA and does not contribute to liveness of the incoming value.
This is at least a coherent representation, but still incredibly dangerous. The
validity of SIL depends on the programmer using Builtin.makeBorrow
correctly. Add a TODO for implementing verification.
Fixes rdar://173800290 (Fix Builtin.Borrow<T> OSSA representation)
Avoid passing an invalid substitution map into createApply.
Required conditions (from the bug report):
1. `~Copyable` struct with user-defined `deinit`
2. Nested inside a constrained extension where all generic parameters are concrete
3. A `destroy_value` or `destroy_addr` of the struct's value exists in some function
4. DeinitDevirtualizer runs (release-mode optimization pipeline)
Fixes rdar://173803881 ([GH:#88213] [SILOptimizer] DeinitDevirtualizer assertion
failure with ~Copyable type in all-concrete constrained extension)
Suggested by: coenttb
* All SIL modifications must go through a `MutatingContext`. Therefore replace the simple setter for `isNested` with `set(isNested:, context)`
* It's better to add a `isNested` parameter for `Builder.createPartialApply` than to set it after each construction of a `partial_apply`, which can easily be missed.
A mutable capture is always escaping. Naturally, you can't mutably capture a
non-Escapable value. Nonetheless, code may override the lifetime of the captured
value as immortal, knowing that the closure does not actually escape.
'_overrideLifetime(capturedThing, copying: ())' quiets the diagnostics
on the caller side. But, when diagnosing the closure body itself, lifetime
analysis is upset by the boxed non-Escaping value. Simply add a case
to LocalVariableAccessMap to recognize this as a valid case so that diagnostics
can ignore it.
Fixes rdar://170592353 (error: lifetime-dependent variable 'overriddenLifetime'
escapes its scope)
It doesn't make sense to de-virtualize `destroy_value [dead_end]` because such operations are no-ops anyway.
This is especially important for Embedded Swift, because introducing calls to generic deinit functions after the mandatory pipeline can result in IRGen crashes.
rdar://168171608
Because `destructure_struct` acts as lifetime-ending use. Removing this instruction results in an ownership error.
This can happen if a non-copyable struct only has trivial fields.
Fixes a verifier crash
https://github.com/swiftlang/swift/issues/86901
rdar://169285436
Do this even if the function then contains references to other functions with wrong linkage.
MandatoryPerformanceOptimization fixes the linkage afterwards.
This is similar to what we already do with de-virtualizing class and witness methods: https://github.com/swiftlang/swift/pull/76032
rdar://168171608
Make sure that debug locations are correct so that instruction which are inserted by DI after a no-return call doesn't cause DiagnoseUnreachable to print a wrong warning.
Also, add `extend_lifetime` to the list of excluded instructions in DiagnoseUnreachable.
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`.
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
These two new invariants eliminate corner cases which caused bugs if optimization didn't handle them.
Also, it will significantly simplify lifetime completion.
The implementation basically consists of these changes:
* add a flag in SILFunction which tells optimization if they need to take care of infinite loops
* add a utility to break infinite loops
* let all optimizations remove unreachable blocks and break infinite loops if necessary
* add verification to check the new SIL invariants
The new `breakIfniniteLoops` utility breaks infinite loops in the control flow by inserting an "artificial" loop exit to a new dead-end block with an `unreachable`.
It inserts a `cond_br` with a `builtin "infinite_loop_true_condition"`:
```
bb0:
br bb1
bb1:
br bb1 // back-end branch
```
->
```
bb0:
br bb1
bb1:
%1 = builtin "infinite_loop_true_condition"() // always true, but the compiler doesn't know
cond_br %1, bb2, bb3
bb2: // new back-end block
br bb1
bb3: // new dead-end block
unreachable
```
Fixes a bug in MandatoryDestroyHoisting where a captured value is destroyed
before a copy of the closure.
On-stack closures can be copied, and all copied uses must be within the borrow
scope of the captured operand. This is just like any other non-Escapable value,
so treat it as such by checking `Value.mayEscape` rather than `Type.Escapable`.
Originally, I wanted to make it illegal to copy of partial_apply [on_stack], but
it looks like we still allow it.
I would rather not complicate any logic yet with special handling for this
case. To fix any performance concerns, we might be able to simplify the
representation instead by banning copy_value on on-stack closures.
Fixes rdar://165850554 swift-frontend crash: While running "CopyPropagation" -
Invalid SIL provided to OSSACompleteLifetime?!)
For example:
```
public class C: MyClass {
public init(p: some P) {
// ...
}
}
```
Constructors are not called via the vtable (except "required" constructors).
Therefore we can allow generic constructors.
https://github.com/swiftlang/swift/issues/78150
rdar://138576752