Commit Graph

1784 Commits

Author SHA1 Message Date
eeckstein
43ad330bd7 Merge pull request #87373 from eeckstein/optimizer-fixes
Several fixes in the Optimizer
2026-02-21 07:33:00 +01:00
Erik Eckstein
5e2851d36e ClosureSpecialization: fix two problems
* insert borrow scopes for specialized guaranteed arguments: This is needed if the guaranteed argument is replaced by a copied owned value. If the argument has any use which is not compatible with "owned" ownership, we need a borrow scope.

* prevent creating a specialized function with more than one "isolated" parameters
2026-02-20 18:28:14 +01:00
Erik Eckstein
a03ac54643 Cloner: ignore type-dependent operands when recursively cloning instructions
Fixes a crash
2026-02-20 18:28:12 +01:00
Andrew Trick
6a9875c73e LifetimeDependenceUtils: add support for mutable captures
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)
2026-02-20 09:12:38 -08:00
Meghana Gupta
54522799df Revert "Improve bounds checking of local span's with known count"
This reverts commit 1575286943.
2026-02-20 00:40:33 -08:00
Meghana Gupta
403ff70075 Merge pull request #87168 from meg-gupta/simplespanbc
Improve bounds checking of local spans with known count
2026-02-19 16:36:51 -08:00
Erik Eckstein
9cd56f412d embedded: Better diagnostics for missing deinit functions
If value-type deinits cannot be de-virtualized we now print a more useful error message, including call tree information, source location and the type of the struct containing the deinit.
Although this situation should never happen, it's still good to have this error message - just in case.
2026-02-19 09:37:02 +01:00
Meghana Gupta
1575286943 Improve bounds checking of local span's with known count
Look through mark_dependence [nonescaping] instructions while simplifying struct_extract
This is crucial to propagate Span's count field when known.
2026-02-18 11:11:59 -08:00
Erik Eckstein
c8711d132a TempRValueElimination: ignore fix_lifetime instruction in alias analysis
`fix_lifetime` has memory-write effects defined.
However, in TempRValueElimination we don't shrink lifetimes. Therefore we can safely ignore this instruction.

rdar://168840965
2026-02-11 19:10:36 +01:00
Erik Eckstein
ddb0141141 TempRValueElimination: support alloc_stacks with multi-block lifetime
So far the optimization just handled the case where all uses of the alloc_stack are in the same basic block.
Now we can handle arbitrary liveranges of the alloc_stack.
Also, remove the destroy-hoisting part of the algorithm because this is already handle by the dedicated DestroyAddrHoisting pass
2026-02-11 06:43:46 +01:00
Erik Eckstein
95d300c062 SimplifyAllocStack: support non-destructive enums and other improvements
Rewrite the enum -> single payload optimization to handle non-destructive `unchecked_take_enum_data_addr` correctly.
Also, we can handle more cases now.
2026-02-11 06:43:46 +01:00
Erik Eckstein
acd5227639 SIL: add isDestructive and mayBeDestructive to UncheckedTakeEnumDataAddrInst 2026-02-11 06:43:46 +01:00
Erik Eckstein
12a798de3a SIL: improve pushPredecessors and pushSuccessors of InstructionWorklist
* add a `isTransparent` closure argument to speed up iteration for transparent blocks.
* add documentation and tests
2026-02-11 06:43:45 +01:00
Erik Eckstein
a965f825a4 MandatoryPerformanceOptimizations: make sure to break infinite loops and complete lifetimes
Those tasks are already done within the optimization in `runSimplification`. However, afterwards we specialize generics in the function.
And specialization can create unreachable blocks and incomplete lifetimes.

Fixes a compiler crash
rdar://169554780
2026-02-06 15:44:45 +01:00
eeckstein
2bf3979f95 Merge pull request #86974 from eeckstein/simplification-pass
Optimizer: don't remove unused `destructure_struct` of a non-copyable value
2026-02-05 07:26:49 +01:00
Erik Eckstein
9f0a9d77c0 DeinitDevirtualizer: don't devirtualize destroy_value [dead_end]
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
2026-02-04 09:10:24 +01:00
Erik Eckstein
7ea1756fa4 Optimizer: don't remove unused destructure_struct of a non-copyable value
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
2026-02-04 08:47:42 +01:00
Erik Eckstein
6dde4c942e MandatoryPerformanceOptimizations: force de-virtualizing value-type deinits
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
2026-02-02 19:36:48 +01:00
Adrian Prantl
8b7d18ae00 Merge pull request #86902 from adrian-prantl/168622625
[TempRValueElimination] Make AllocStackInst.hasUses analysis global
2026-02-01 18:55:34 -08:00
Adrian Prantl
d7ca6483a0 [TempRValueElimination] Make AllocStackInst.hasUses analysis global
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
2026-01-30 14:01:19 -08:00
Erik Eckstein
5cd70731da LifetimeCompletion/DiagnoseUnreachable: fix a wrong "will never be executed" warning
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.
2026-01-30 11:52:49 +01:00
eeckstein
ad787dc6d4 Merge pull request #86840 from eeckstein/deinit-opts
Optimizer: fix two problems related to value-type deinits
2026-01-29 07:33:36 +01:00
eeckstein
afdaf838a5 Merge pull request #86817 from eeckstein/fix-rle
RedundantLoadElimination: fix a complexity problem when replacing a huge amount of `load`s with a common value
2026-01-28 22:38:44 +01:00
Erik Eckstein
9d0b6c8417 RedundantLoadElimination: fix a complexity problem when replacing a huge amount of loads with a common value
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
2026-01-28 17:54:37 +01:00
Erik Eckstein
f2b55335c4 SIL: add Instruction.set(location:) 2026-01-28 17:54:37 +01:00
Erik Eckstein
bdf5a70258 DeinitDevirtualizer: don't create an invalid function_ref in a serialized function.
Serialized functions may not reference non-public functions.

Fixes a compiler crash
rdar://169022052
2026-01-28 13:10:30 +01:00
Alejandro Alonso
a4fdb7d9ec Simplify the identity for dereference borrows 2026-01-27 11:04:25 -08:00
Alejandro Alonso
9f95dc7695 Simplify init_borrow_addr when the borrow type is statically known
make sure we register as well as add test
2026-01-27 11:03:53 -08:00
Joe Groff
0f3ddfbcc8 Merge pull request #86545 from jckarter/builtin-borrow
`Builtin.Borrow` implementation
2026-01-26 07:32:31 -08:00
Joe Groff
9a417d4c28 Handle Borrow instructions in LifetimeCompletion 2026-01-23 10:03:23 -08:00
Joe Groff
5f49668577 Builtin.dereferenceBorrow should maintain the memory location of addressable values. 2026-01-23 08:02:09 -08:00
Joe Groff
b11e8c985a SIL: Handle dereference_borrow returns in SILGenCleanup.
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`.
2026-01-23 08:02:09 -08:00
Joe Groff
bc53d4ecb4 SIL: Handle Borrow instructions in more utility visitors. 2026-01-23 08:02:09 -08:00
Joe Groff
2f02c7cda3 SIL: Introduce instructions for forming and dereferencing Builtin.Borrow.
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
2026-01-23 08:02:01 -08:00
Erik Eckstein
535a58e3fb SIL: assume a read-effect for unused indirect arguments
Even if an indirect argument is unused, pretend that the function reads from it.
If the unused argument is passed to another generic function and that function is specialized,
the argument may be re-abstracted and the specializer inserts a load from the indirect argument.
Therefore we must be prepared that unused indirect argument might get loaded from at some time.

Fixes a compiler crash
rdar://168623362
2026-01-23 15:58:23 +01:00
eeckstein
ee398d1b18 Merge pull request #86644 from eeckstein/lifetime-completion
Optimizer: enable complete OSSA lifetimes throughout the pass pipeline
2026-01-23 06:14:10 +01:00
Erik Eckstein
7af1e1ffd1 TempRValueElimination: fix a problem with load-store copy elimination
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
2026-01-22 18:03:02 +01:00
Erik Eckstein
5ea44a4a70 CopyToBorrowOptimization: remove special handling of dead-end blocks
Which is now possible with complete OSSA lifetimes
2026-01-22 17:41:48 +01:00
Erik Eckstein
54347dbfd7 SIL Verifier: enable a check for end-borrows
This check requires complete OSSA lifetimes
2026-01-22 17:41:48 +01:00
Erik Eckstein
18063707b5 Optimizer: enable complete OSSA lifetimes throughout the pass pipeline
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
2026-01-22 17:41:48 +01:00
Erik Eckstein
7b6247332f Optimizer: add FunctionPassContext.updateAnalysis()
This is useful if a pass needs an updated analysis after invalidating the SIL of a function.
2026-01-22 17:41:24 +01:00
Erik Eckstein
0f0aa0c17b Optimizer: require that there are no unreachable blocks and infinite loops in OSSA
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
```
2026-01-22 17:41:23 +01:00
Erik Eckstein
34aa3c00d3 Optimizer: move var needFixStackNesting from MutatingContext to FunctionPassContext 2026-01-22 17:41:23 +01:00
Erik Eckstein
16cb80b60d Optimizer: implement all of cond_fail simplification as instruction simplification
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.
2026-01-22 17:41:22 +01:00
Erik Eckstein
2860c2df77 Optimizer: add DominatorTree.getImmediateDominator and DominatorTree.getDominanceOrder 2026-01-22 17:41:22 +01:00
Erik Eckstein
1a4eb8ca7e MandatoryDestroyHoisting: correct handling of destroy_value [dead_end]
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
2026-01-22 17:41:21 +01:00
Erik Eckstein
e2ddbf79f0 SIL: fix the corner case of an empty BasicBlockRange
If no block was ever inserted in a BasicBlockRange, the "inclusive" range should be empty and not contain the "begin" block.
2026-01-22 17:41:21 +01:00
Erik Eckstein
4755aa828e DestroyHoisting: correct handling of destroy_value [dead_end]
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
2026-01-22 17:41:21 +01:00
Erik Eckstein
1e476700af SIL: add Builder.createExtendLifetime and add isDeadEnd to createDestroyValue 2026-01-22 17:41:21 +01:00
Aidan Hall
37e89902a3 Merge pull request #85957 from aidan-hall/fix-debug-info-again
Retain more debug info in SIL optimization passes
2026-01-13 17:13:36 +00:00