Commit Graph

129 Commits

Author SHA1 Message Date
Meghana Gupta 1e7ee5f784 Add a new utility canDeleteDeadMoveOnlyOwnedDestructureInst and explicitly handle forwarding operations that destructure 2026-04-21 16:27:52 -07:00
Meghana Gupta 607e1f3a6e Don't delete dead instructions that forward move-only values in ossa
A dead forwarding operation (e.g. `unchecked_enum_data`,
`unchecked_value_cast`, `destructure_struct`) with an owned move-only operand
must not be deleted because it ends the lifetime of its operand.

Previously, only `destructure_struct` was guarded against deletion. This
change uses `ForwardingOperation` and `getSingleForwardingOperand()` to
cover all single-operand forwarding instructions uniformly.

Resolves rdar://175150849
2026-04-21 11:28:45 -07:00
Meghana Gupta 6e647a5b92 Don't delete dead destructure_struct in DCE for ~Copyable structs
This was needed from the time we called OSSACompleteLifetimes from DCE, since we don't
do that anymore, delete this code.
2026-02-26 10:53:00 -08:00
Meghana Gupta 05124d9cbf Remove unnecessary pointer escape checking in DCE 2026-02-26 10:47:24 -08:00
Joe Groff 0f3ddfbcc8 Merge pull request #86545 from jckarter/builtin-borrow
`Builtin.Borrow` implementation
2026-01-26 07:32:31 -08:00
Meghana Gupta bfdfe8f948 Remove DCE support for end_lifetime 2026-01-23 14:04:12 -08:00
Meghana Gupta 13a044dc05 [NFC] Remove unused variable 2026-01-23 09:07:05 -08:00
Meghana Gupta db77c83cad Remove unnecessary calls to DCE::endLifetimeOfLiveValue 2026-01-23 09:01:49 -08:00
Joe Groff bc53d4ecb4 SIL: Handle Borrow instructions in more utility visitors. 2026-01-23 08:02:09 -08:00
Meghana Gupta 02d0ce598d Remove unnecessary code from DeadCodeElimination
DeadCodeElimination stopped deleting dead copy_value and hoisting destroy_value
instructions.

LifetimeCompletion of valuesToComplete was necessary since DCE could delete blocks
that contain only destroy_value instructions or forwarding instructions with
destroy_value uses only.

Since this is not done in DeadCodeElimination, remove this support.
2026-01-23 06:50:49 -08: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 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
Meghana Gupta b467c5ec3c [NFC] Remove unused DominanceInfo from OSSACompleteLifetime and InteriorLiveness 2025-12-17 13:30:06 -08:00
Erik Eckstein c0017a2701 DeadCodeElimination: don't insert destroys for removed values in dead-end blocks
When an owned value has no lifetime ending uses it means that it is in a dead-end region.
We must not remove and inserting compensating destroys for it because that would potentially destroy the value too early.
Initialization of an object might be cut off and removed after a dead-end loop or an `unreachable`.
In this case a class destructor would see uninitialized fields.

Fixes a mis-compile
https://github.com/swiftlang/swift/issues/85851
rdar://165876726
2025-12-05 17:08:51 +01:00
Erik Eckstein e603f52757 DeadCodeElimination: don't hoist destroy_value instructions
DeadCodeElimination can remove instructions including their destroys and then insert compensating destroys at a new place.
This is effectively destroy-hoisting which doesn't respect deinit-barriers.
Disable removing and re-creating `destroy_value` instructions. This is done by other optimizations.
2025-12-03 15:53:56 +01:00
Erik Eckstein dbc50633b9 DeadCodeElimination: don't remove empty access scopes
Empty access scopes can be a result of e.g. redundant-load-elimination.
It's still important to keep those access scopes to detect access violations.
Even if the load is physically not done anymore, in case of a conflicting access a propagated load is still wrong and must be detected.

rdar://164571252
2025-11-24 14:49:45 +01:00
Meghana Gupta e116df3628 Introduce return_borrow instruction 2025-10-23 05:18:59 -07:00
Nate Chandler aa85694237 [NFC] OSSACompleteLifetime: Renamed. 2025-08-18 09:45:19 -07:00
Nate Chandler fdd896e021 Revert "[DCE] Verify liveness of completed lifetimes."
This reverts commit 3ec9b269f5.

rdar://149896608
2025-04-23 16:59:26 -07:00
Nate Chandler 3ec9b269f5 [DCE] Verify liveness of completed lifetimes. 2025-04-11 14:58:34 -07:00
Nate Chandler b405c8b23c [DCE] Keep insts which consume escaping values.
When DCE deletes instructions as dead, if the instruction ends one of
its operands lifetimes, it must insert a compensating lifetime end.
When the def block of the value and the parent block of the instruction
are different, it uses lifetime completion.  Lifetime completion relies
on complete liveness, which doesn't and can't exist for values with
pointer escapes.  The result is ending lifetimes too early.

Avoid this scenario by marking such instructions live.

In the fullness of time, it may be possible to track the deleted
instruction's "location" even in the face of deletions of adjacent
instructions and parent blocks and to insert the lifetime end at that
location.

rdar://149007151
2025-04-11 14:58:33 -07:00
Nate Chandler 6d9ae19629 [DCE] Don't complete lifetimes of erased values.
DCE may enqueue a value for lifetime completion and later on erase the
instruction that defines that value.  When erasing an instruction, erase
each of its results from the collection of values to complete.

rdar://141560546
2024-12-19 15:16:32 -08:00
Nate Chandler f75f12b681 [DCE] Add delete handler for phi erasure.
Set CallsChanged when appropriate and increment NumDeletedInsts.
2024-12-19 15:12:24 -08:00
Nate Chandler 00116edcfb [NFC] DCE: TermInsts increment NumDeletedInsts. 2024-12-19 15:12:24 -08:00
Nate Chandler 1851e712f8 [Gardening] DCE: Use bound variable in branch.
Rather than reusing the source of the dyn_cast.
2024-12-19 15:12:24 -08:00
Meghana Gupta 50bde829b4 Look through borrowed from instructions before calling lifetime completion
Lifetime completion will insert end_borrows only on borrow introducers.

Look through "borrowed from" instructions before calling it.

rdar://141490551
2024-12-15 01:53:19 -08:00
Meghana Gupta 3701f01de4 Mark ownership fixup instructions as live 2024-12-15 01:53:17 -08:00
Meghana Gupta daa9b161d0 Fix DCE for ownership forwarding instructions
DCE deletes ownership forwarding instructions when it doesn’t have useful users.
It inserts destroy_value/end_borrow for its operands to compensate their lifetimes.

DCE also deletes branches when its successor blocks does not have useful instructions.
It deletes blocks and creates a jump to the nearest post dominating block.

When DCE needs to delete a forwarding instruction in a dead block, it cannot just create
lifetime ends of its operands at its position. Use LifetimeCompletion utility in such cases.

rdar://140428721
2024-12-09 03:22:57 -08:00
Meghana Gupta adf1eae287 Bring in DominanceInfo and DeadEndBlocks to DCE 2024-12-09 03:00:18 -08:00
Meghana Gupta 434461d8f4 Fix DCE of load_borrow when it is derived from another borrow scope
Fixes rdar://138663452
2024-11-14 12:09:11 -08:00
eeckstein 6d4db0e649 Merge pull request #77583 from eeckstein/fix-dce
DeadCodeElimination: don't remove end_lifetime instructions with address operands
2024-11-13 18:51:45 +01:00
Erik Eckstein 64698ca6bb DeadCodeElimination: don't remove end_lifetime instructions with address operands
DCE cannot reason about values in memory.

Fixes a memory lifetime verification error
rdar://139779406
2024-11-13 12:04:41 +01:00
Meghana Gupta fd6d29d10b Insert end_borrow for dead phi operands only when required
DCE inserts end_borrow at phi operands when a guaranteed phi becomes dead.
This should be done for reborrows which end the lifetime of the incoming value.
The existing check was not accurate and ended up inserting end_borrow for forwarded values as well.

Fixes rdar://139283745
2024-11-12 14:05:23 -08:00
Meghana Gupta 359f6d14bf DCE unconditional_checked_cast 2024-10-16 16:55:22 -07:00
Tim Kientzle 1098054291 Merge branch 'main' into tbkka-assertions2 2024-06-18 17:52:00 -07:00
Tim Kientzle 1d961ba22d Add #include "swift/Basic/Assertions.h" to a lot of source files
Although I don't plan to bring over new assertions wholesale
into the current qualification branch, it's entirely possible
that various minor changes in main will use the new assertions;
having this basic support in the release branch will simplify that.
(This is why I'm adding the includes as a separate pass from
rewriting the individual assertions)
2024-06-05 19:37:30 -07:00
Nate Chandler 224d36c576 [DCE] Insts that define lexical values seem useful
Don't delete as dead instructions that define lexical values such as
`move_value [lexical]`.

rdar://129299803
2024-06-05 13:04:43 -07:00
Meghana Gupta 505d84fe74 Don't delete allocations in DCE 2024-05-23 12:59:00 -07:00
Emil Pedersen c2c16f53dd [DebugInfo] Fix undef debug values being removed 2024-04-26 16:31:16 -07:00
Erik Eckstein e14c1d1f62 SIL, Optimizer: update and handle borrowed-from instructions
Compute, update and handle borrowed-from instruction in various utilities and passes.
Also, used borrowed-from to simplify `gatherBorrowIntroducers` and `gatherEnclosingValues`.
Replace those utilities by `Value.getBorrowIntroducers` and `Value.getEnclosingValues`, which return a lazily computed Sequence of borrowed/enclosing values.
2024-04-10 13:38:10 +02:00
Erik Eckstein 44f4ea9099 DeadCodeElimination: fix debug logging of to-instruction when adding a reverse dependency 2024-04-10 13:38:10 +02:00
Michael Gottesman 11f0ff6e32 [sil] Ensure that all SILValues have a parent function by making it so that SILUndef is uniqued at the function instead of module level.
For years, optimizer engineers have been hitting a common bug caused by passes
assuming all SILValues have a parent function only to be surprised by SILUndef.
Generally we see SILUndef not that often so we see this come up later in
testing. This patch eliminates that problem by making SILUndef uniqued at the
function level instead of the module level. This ensures that it makes sense for
SILUndef to have a parent function, eliminating this possibility since we can
define an API to get its parent function.

rdar://123484595
2024-02-27 13:14:47 -08:00
Nate Chandler ae6868a296 [DCE] Process instructions added during deletion.
In cea0f00598, `InstructionDeleter` began
deleting `load [take]` instructions.  Analogous to how it creates a
`destroy_value` when deleting an instruction which consumes a value, in
the case of deleting a `load [take]` the `InstructionDeleter` inserts a
compensating `destroy_addr`.

Previously, `DeadCodeElimination` did not observe the creation of any
instructions created by the `InstructionDeleter`.  In the case of the
newly created `destroy_addr`, DCE didn't mark that the `destroy_addr`
was live and so deleted it.  The result was a leak.

Here, this is fixed by passing an `InstModCallbacks`--with an
`onCreateNewInst` implementation--down into `erasePhiArgument` that
eventually invokes the `InstructionDeleter`.  When the
`InstructionDeleter` creates a new instruction, DCE marks it live.
2023-11-15 15:03:24 -08:00
Slava Pestov 05ccd9734c SIL: Introduce ThrowAddrInst 2023-10-31 16:58:54 -04:00
Meghana Gupta 031255c2d8 Rename hasPointerEscape -> findPointerEscape 2023-06-20 21:13:55 -07:00
Nate Chandler 38cbc6fde1 [DCE] Destroys of live values are live.
As is done for `end_borrow`s, add a dependency from a `destroy_value`s
to its operand so that it is not eliminated if its operand isn't.
2023-04-06 13:43:57 -07:00
Nate Chandler 5f5692c3ba [Gardening] Separated debug output lines. 2023-04-06 13:35:01 -07:00
swift-ci be620b8ada Merge pull request #64709 from meg-gupta/dcefix
Don't DCE @owned phis that may have escaped
2023-03-29 14:26:45 -07:00
Meghana Gupta 246e9c819b Don't DCE @owned phis that may have escaped 2023-03-29 11:38:13 -07:00
Meghana Gupta a135f4fc50 Check lexicality at reborrow instead of borrow while disabling DCE 2023-03-28 20:31:32 -07:00