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
Wihtout this change an alloc_stack instruction that is defined in a different
basic block than its use could result in instructions that are not dominated by
its operands. In an asserts build this is caught by the SIL verifier, but in a
non-asserts build it can crash the compiler.
Thanks to Erik Eckstein for the actual implementation of the fix!
rdar://168622625
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.
As RedundantLoadElimination processing the loads in reverse control flow order, the replaced loads might accumulate quite a lot of users.
This happens if the are many loads from the same location in a row.
To void quadratic complexity in `uses.replaceAll`, we swap both load instructions and move the uses from the `existingLoad` (which usually has a small number of uses) to this load - and delete the `existingLoad`.
This came up in the Sema/large_int_array.swift.gyb test, which tests a 64k large Int array.
With this fix, the compile time gets down from 3 minutes to 5 seconds.
I also changed the test:
* run the compiler with the timeout script to detect build time regressions
* re-enabled the SIL verifier because the problem there is already fixed (https://github.com/swiftlang/swift/pull/86781)
* run the compiler with -O to also test the whole optimizer pipeline and not only the mandatory pipeline
* assigned the array to a real variable (instead of `_`) to not let the optimizer remove the whole array too early
* run the compiler with and without `-parse-as-library` because this makes a huge difference how the global array is being generated
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`.
If a value is "copied" to the stack location via a `load` and `store` instruction pair and the source location is written or de-allocated between both instructions,
the optimization generated wrong SIL.
The fix is to make sure that writes to the source locations are always checked between the `load` and `store`.
rdar://168595700
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
```
Inserts an unreachable after an unconditional fail:
```
%0 = integer_literal 1
cond_fail %0, "message"
// following instructions
```
->
```
%0 = integer_literal 1
cond_fail %0, "message"
unreachable
deadblock:
// following instructions
```
Remove the old SILCombine implementation because it's not working well with OSSA lifetime completion.
This also required to move the `shouldRemoveCondFail` utility function from SILCombine to InstOptUtils.
Make sure to create `destroy_value` with correct dead_end flags.
Especially, in a dead-end region where the original value's lifetime was ended with a `destroy_value [dead_end]`, the optimization must not re-create a destroy without the dead_end flag.
^ Conflicts:
^ test/SILOptimizer/mandatory-destroy-hoisting.sil
Make sure to create `destroy_value` with correct dead_end flags.
Especially, in a dead-end region where the original value's lifetime was ended with a `destroy_value [dead_end]`, the optimization must not re-create a destroy without the dead_end flag.
^ Conflicts:
^ test/SILOptimizer/destroy-hoisting.sil
This can cause several problems, e.g. false performance errors or even mis-compiles.
Instead, just ignore dead-end destroys as we don't want to "move" them away from `unreachable` instructions, anyway.
rdar://167553623
This reverts commit b1eb70bf45.
The original PR (#85244) was reverted (#85836) due to rdar://165667449
This version fixes the aforementioned issue, and (potentially) improves overall
debug info retention by salvaging info in erase(instructionIncludingDebugUses:),
rather than inserting many explicit salvageDebugInfo calls throughout the code
to make the test cases pass.
Bridging: Make salvageDebugInfo a method of MutatingContext
This feels more consistent than making it a method of Instruction.
DebugInfo: Salvage from trivially dead instructions
This handles the case we were checking in constantFoldBuiltin, so we do not need to salvage debug info there any more.
Usually `load [take]` is not hoisted anyway, because there must be another instruction in the loop which re-initializes the loaded memory location.
However, this is not necessarily true for alloc_stack locations with dynamic lifetime, e.g. a pack-loop which is specialized for a single pack element (at runtime the loop is only executed once).
Fixes a SIL ownership verifier crash
rdar://167504916
The conformances `Type: Comparable` and `EnumCase: Equatable` were
previously introduced in #85757 for implementing AutoDiff closure
specialization logic. This patch moves the latter directly to the SIL
module. The conformance `Type: Comparable` is deleted, and `sort(by:)`
is used instead of `sort` to mimic the same behavior in tests. These
changes address warnings mentioned in the comment:
https://github.com/swiftlang/swift/pull/85757#issuecomment-3681636278
* remove borrow scopes which are borrowing an already guaranteed value
* allow optimizing lexical `begin_borrows` outside the mandatory pipeline
* fix: don't remove `begin_borrow [lexical]` of a `thin_to_thick_function` in the mandatory pipeline
When trying to remove a borrow scope by replacing all guaranteed uses with owned uses, don't bail for values which have debug_value uses.
Also make sure that the debug_value instructions are located within the owned value's lifetime.