The pass merges to adjacent borrow scopes in a basic block.
```
%2 = begin_borrow %1
use(%2)
end_borrow %2
...
%6 = begin_borrow %1
use(%6)
end_borrow %6
```
->
```
%2 = begin_borrow %1
use(%2)
...
use(%2)
end_borrow %2
```
This helps other optimizations, like common-subexpression-elimination, because the borrow liveranges are larger and not split.
The `projection` flag indicates that `index_addr` projects an element address from an array base address, as opposed to being used for general pointer arithmetic.
When this flag is set, the result address can only reach the single element at the given index — it is not possible to chain multiple `index_addr` instructions to reach other array elements from the result.
Without this flag, the result may be used as the base of another `index_addr`, allowing arithmetic across element boundaries (e.g. an `index_addr` with index 1 followed by an `index_addr` with index 2 reaches the element at offset 3).
An `index_addr [projection]` is mandatory to go from an array base address to an element - even if it's the first element, i.e. the index is zero.
This means that the optimizer must not remove `index_addr [projection]` with a zero index.
Handle a protocol that has a default implementation that returns a ~Escapable
value and also has a typed throw that defaults to Never.
I believe this "regression" appeared when typed throws were adopted in more
APIs.
Fixes rdar://178685863 (CxxIterableIterator escapes its scope)
When the operand was changed by a pass but has a different type from
what the undef type should be, pass the correct type to killOperand
so that an undef with the right type is emitted.
When the value of an enum discriminator is known but the payload unknown
(for example, an Optional that we know is non-nil), salvage the value
using a debug reconstruction block that recreates the enum using the
payload and the known discriminator.
Replaces a sequence which is commonly found in generated class destructors:
```
%1 = some_owned_value
%2 = begin_borrow %1 // the only use of %1
%3 = unchecked_ref_cast %2 to $C
%4 = unchecked_ownership_conversion %3, @guaranteed to @owned
end_borrow %2
end_lifetime %1
```
->
```
%1 = some_owned_value
%2 = begin_borrow %1 // now dead
end_borrow %2
%4 = unchecked_ref_cast %1 to $C
```
In LifetimeDependenceDiagnostics, disable diagnostics of function arguments that
have a noescape function type. Diagosing noescape function-type arguments is
meant to be handled by a separate pass, DiagnoseInvalidEscapingCaptures. In some
distant future, we could consider merging these diagnostics. For now, they
should be independent.
This fixes a bug introduced with:
commit e3d55f64c2
Date: Wed Apr 29 10:37:32 2026 +0100
Lifetimes: Treat noescape function types as ~Escapable
After that change, lifetime diagnostics inadvertantly kicked on for noescape
function-type arg. The diagnostic would then fail when importing ObjC API with
an optional noescape function such as:
+ (instancetype)takeNoEscapeBlock:(void(NS_NOESCAPE ^)(void))block;
// error: lifetime-dependent variable 'block' escapes its scope)
Here the compiler generates a thunk to unwrap the optional and generates a
mark_dependence on the Optional's inner value.
Fixes rdar://177381648 - error: lifetime-dependent variable escapes its scope
The LICM pass's hoistAndSinkLoadAndStore incorrectly determined load/store
ownership based on firstStore.storeOwnership rather than the address type.
This PR fixes it by determining ownership based on the address type's triviality:
Immortal foreign reference types never need release or retain operations
in Swift, they are represented as trival types, i.e, they can be copied/loaded
without any refcount operations. Some peepholes introducing upcast or
unchecked_ref_cast on the archetype which is not trivial. As a result,
we ended up with SIL that failed to pass verification, we tried to do
trivial operations on a non-trivial type.
This PR disables the peephole optimizations when the triviality of the
address type differs from the triviality of the type after the
transformation.
rdar://177549159
- Canonicalize dynamic_pack_index and pack_pack_index to scalar_pack_index.
- Replace opened Pack Element types with concrete types when statically known (i.e. when open_pack_element uses a scalar_pack_index) in SILCloner.
- Add a simplification for tuple_pack_element_addr to replace it with tuple_element_addr when it uses a scalar_pack_index. This is near-identical to the existing SILCombine visitor for tuple_pack_element_addr (which worked for dynamic_pack_index).
TODO: Fix or remove old SILCombine visitors for pack instructions that are broken or made obsolete by this change.
The patch enhances sil-combiner handling of `differentiable_function` by
adding support for `convert_function` which is further used in
`differentiable_function_extract`:
```
%0 = differentiable_function ... %x
%1 = begin_borrow %0
%2 = convert_function %1 to ...
%3 = differentiable_function_extract [xxx] %2
// use of %3
```
-->
```
%0 = differentiable_function ... %x
// use of %x
```
If a tuple_pack_element_addr uses a scalar_pack_index rather than a
dynamic_pack_index, we can replace it with tuple_element_addr, since the
specific tuple element accessed is statically known.
If `load [trivial]` only loads a "part" of a stored value, which itself is non-trivial, the pass crashes with an ownership error.
The fix is to wrap the projection instructions (e.g. `struct_extract`) inside a borrow scope. This is correctly done in `createProjectionAndCopy`.
https://github.com/swiftlang/swift/issues/89255
rdar://177430359
So far we supported hoisting a `load [take]` which "takes" just a part of a stored value (i.e. a projected value).
This works as long as no other struct/tuple field is also loaded in the loop.
If this is the case it causes an ownership error.
The fix is to disallow hoisting projected `load [take]` instructions.
The C++ `SILFunctionType` exposes both `getResults()` (formal results only)
and `getResultsWithError()` (formal + error). The Swift mirror previously
only had `results`, bridging to the with-error variant. Add `formalResults`
for the formal-only view, matching the C++ split.
Switch PackSpecialization's three result-iteration sites to `formalResults`.
The bridged `createSpecializedFunctionDeclaration` preserves the error
result on its own, so iterating with-error included it twice in the new
function's signature.
Also forward the original apply's `nothrow`/`noasync` flags to the
specialized apply, required for SIL verification of a plain apply calling
a function with an error result.
The patch implements proper sil-combiner handling of
`differentiable_function` for cases when extractee has non-trivial
ownership. In such casese, it is consumed by the differentiable_function
instruction. We must copy the extractee before the consumption point so
the copy remains live afterward.
Fixes#88816
Fix lifetime diagnostics to consider an implicit initializer of a ~Escapable
type to be implicitly immortal. Required to handle Optional<~Escapable> stored
properties, such as:
struct Foo<Element: ~Escapable>: ~Escapable {
var element: Element?
@_lifetime(borrow c)
init<C>(c: borrowing C) {
// error: Lifetime-dependent variable 'self' escapes its scope
}
}
The fix is simply to remove a temporary safeguard that I put in place to
compensate for our incomplete closure lifetimes. We now have the complete
representation of lifetimes on closures, so don't need the safeguard.
Representationally, a function that returns a ~Escapable value but has no
dependendencies is immortal. This was always the intended design, but the
temporary safeguard treated these cases as implicitly bound to some local scope.
Removing this safeguard has the effect of:
- Variable intialization is immortal (it cannot depend on anything by
definition). The safety of the initializer is checked inside the implementation
of those expressions rather than the caller.
- Empty ~Escapable types have an implicit immortal initializer (why not?)
- Calls to a function with @_unsafeNonescapableResult but no @_lifetime
annotation produce an immortal value. This is reasonable, and we want to
deprecate this attribute as soon as possible anyway. It is not for general use.
This is currently blocking usage of BorrowingSequence, such as a hypothetical BorrowingSequenceMapSequenceIterator.
Fixes rdar://176561897 ([nonescapable] initialization of Optional fields reports
a lifetime escape)
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)
Currently, AutoDiff Closure Specialization pass iterates over all VJP
instructions and checks each of them against a set of conditions which
`partial_apply` of pullback must satisfy.
This logic could be re-implemented w/o loop, checking conditions in
opposite direction, starting from `return` instruction and transitively
going to defining instructions of operands (`tuple` and `partial_apply`
for the desired pullback case).
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.