Commit Graph

7977 Commits

Author SHA1 Message Date
Andrew Trick
d609889e87 Merge pull request #85934 from atrick/fix-closure-liveness
Fix InteriorUseWalker: consider partial_apply [on_stack] an escape
2025-12-11 15:05:21 -08:00
Joe Groff
79225fa841 Add missing feature flags to borrow_accessor_address_only_non_escapable.swift 2025-12-10 13:40:34 -08:00
Joe Groff
4479b49fd8 SIL: Handle address ReturnInsts from borrow accessors as yielding uses.
This avoids a spurious lifetime error when an address-only borrow accessor returns a
non-`Escapable` value from a non-`Escapable` aggregate.
2025-12-10 11:44:41 -08:00
Andrew Trick
f7c3c6f437 Fix InteriorUseWalker: consider partial_apply [on_stack] an escape
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?!)
2025-12-09 14:38:04 -08:00
eeckstein
e987c925f7 Merge pull request #85855 from eeckstein/fix-silcombine
DeadCodeElimination,SILCombine: don't insert destroys for removed values in dead-end blocks
2025-12-05 22:04:07 +01:00
Ben Cohen
0f99458900 Allow Comparable: ~Copyable (#85747)
Builds on #85746 which covers Equatable
2025-12-05 08:37:36 -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
953d692e53 SILCombine: don't remove dead Array adoptStorage call
If followed by a dead infinite loop, the array initialization might have beed removed.
Therefore when inserting a compensating destroy of the array buffer can lead to a crash.

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
98805c9141 Optimizer: let the InstructionDeleter respect deinit-barriers by default
The InstructionDeleter 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. Therefore it's not done for lexical lifetimes.
However, since https://github.com/swiftlang/swift/pull/85334, the optimizer should treat _all_ lifetimes as fixed and not only lexical lifetimes.

This change adds a `assumeFixedLifetimes` flag to InstructionDeleter which is on by default.
Only mandatory passes (like OSLogOptimization) should turn this off.
2025-12-03 15:53:56 +01:00
Erik Eckstein
8aa911ba2f Optimizer: add simplifications for destroy_value
Attempt to optimize by forwarding the destroy to operands of forwarding instructions.

```
  %3 = struct $S (%1, %2)
  destroy_value %3         // the only use of %3
```
->
```
  destroy_value %1
  destroy_value %2
```

The benefit of this transformation is that the forwarding instruction can be removed.

Also, handle `destroy_value` for phi arguments.
This is a more complex case where the destroyed value comes from different predecessors via a phi argument.
The optimization moves the `destroy_value` to each predecessor block.

```
bb1:
  br bb3(%0)
bb2:
  br bb3(%1)
bb3(%3 : @owned T):
  ...                // no deinit-barriers
  destroy_value %3   // the only use of %3
```
->
```
bb1:
  destroy_value %0
  br bb3
bb2:
  destroy_value %1
  br bb3
bb3:
  ...
```
2025-12-03 15:53:55 +01:00
Aidan Hall
90e12147e0 Merge pull request #85456 from aidan-hall/pack-opt-fix-weather-swb
PackSpecialization: Fix result & type parameter handling
2025-12-02 15:38:32 +00:00
Michael Gottesman
24c69c674d Merge pull request #85604 from gottesmm/alloc_stack_non_nested
[irgen] Implement support for alloc_stack non_nested.
2025-12-01 09:38:06 -08:00
eeckstein
9304ce951c Merge pull request #85707 from eeckstein/embedded-witness-method-specialization
embedded: change the function representation of directly called witness methods
2025-12-01 09:36:45 +01:00
eeckstein
62a13962a9 Merge pull request #85724 from eeckstein/fix-ossa-canonicalization
Optimizer: correctly handle bit-wise escaping values in OSSA canonicalization
2025-12-01 09:36:17 +01:00
eeckstein
46c69e40c1 Merge pull request #85533 from eeckstein/fix-access-simplification
SILOptimizer: don't remove empty conflicting access scopes
2025-12-01 09:35:48 +01:00
eeckstein
e18ba6d1bb Merge pull request #85691 from eeckstein/fix-sil-combine
SILCombine: fix propagation of concrete existentials in enums
2025-12-01 09:34:55 +01:00
Aidan Hall
97b7c35647 PackSpecialization: Fix result & type parameter handling
Refactor certain functions to make them simpler. and avoid calling
AST.Type.loweredType, which can fail. Instead, access the types of the
function's (SIL) arguments directly.

Correctly handle exploding packs that contain generic or opaque types by using
AST.Type.mapOutOfEnvironment().

@substituted types cause the shouldExplode predicate to be unreliable for AST
types, so restrict it to just SIL.Type. Add test cases for functions that have
@substituted types.

Re-enable PackSpecialization in FunctionPass pipeline.

Add a check to avoid emitting a destructure_tuple of the original function's
return tuple when it is void/().
2025-11-28 17:39:41 +00:00
Erik Eckstein
633d42b1b4 Optimizer: correctly handle bit-wise escaping values in OSSA canonicalization
We cannot compute the liverange of a value if it bit-wise escapes.
This fixes a mis-compile in copy-propagation which hoists a destroy_value over a use of the escaped value when lexical liveranges are disabled.
The test case is a simplified SIL sequence from the stdlib core where this problem shows up, because we build the stdlib core with disabled lexical liveranges.
2025-11-27 22:07:11 +01:00
Erik Eckstein
d7b75b6819 Optimizer: register explicit-copy instruction simplifications for SILCombine
Usually `explicit_copy_addr` and `explicit_copy_value` don't survive until the first SILCombine pass run anyway.
But if they do, the simplifications need to be registered, otherwise SILCombine will complain.
2025-11-27 19:40:31 +01:00
Erik Eckstein
64dd574bea embedded: change the function representation of directly called witness methods
This is needed in Embedded Swift because the `witness_method` convention requires passing the witness table to the callee.
However, the witness table is not necessarily available.
A witness table is only generated if an existential value of a protocol is created.

This is a rare situation because only witness thunks have `witness_method` convention and those thunks are created as "transparent" functions, which means they are always inlined (after de-virtualization of a witness method call).
However, inlining - even of transparent functions - can fail for some reasons.

This change adds a new EmbeddedWitnessCallSpecialization pass:
If a function with `witness_method` convention is directly called, the function is specialized by changing the convention to `method` and the call is replaced by a call to the specialized function:

```
  %1 = function_ref @callee : $@convention(witness_method: P) (@guaranteed C) -> ()
  %2 = apply %1(%0) : $@convention(witness_method: P) (@guaranteed C) -> ()
...
sil [ossa] @callee : $@convention(witness_method: P) (@guaranteed C) -> () {
  ...
}
```
->
```
  %1 = function_ref @$e6calleeTfr9 : $@convention(method) (@guaranteed C) -> ()
  %2 = apply %1(%0) : $@convention(method) (@guaranteed C) -> ()
...
// specialized callee
sil shared [ossa] @$e6calleeTfr9 : $@convention(method) (@guaranteed C) -> () {
  ...
}
```

Fixes a compiler crash
rdar://165184147
2025-11-26 16:23:47 +01:00
Erik Eckstein
2ac69f3c55 SILCombine: fix propagation of concrete existentials in enums
This peephole optimization didn't consider that an alloc_stack of an enum can be overridden by another value.
The fix is to remove this peephole optimization at all because it is already covered by `optimizeEnum` in alloc_stack simplification.

Fixes a miscompile
https://github.com/swiftlang/swift/issues/85687
rdar://165374568
2025-11-25 09:42:19 +01:00
Erik Eckstein
9a124742b0 Optimizer: add the DeadAccessScopeElimination optimization pass
It eliminates dead access scopes if they are not conflicting with other scopes.

Removes:
```
  %2 = begin_access [modify] [dynamic] %1
  ...                                       // no uses of %2
  end_access %2
```

However, dead _conflicting_ access scopes are not removed.
If a conflicting scope becomes dead because an optimization e.g. removed a load, it is still important to get an access violation at runtime.
Even a propagated value of a redundant load from a conflicting scope is undefined.

```
  %2 = begin_access [modify] [dynamic] %1
  store %x to %2
  %3 = begin_access [read] [dynamic] %1    // conflicting with %2!
  %y = load %3
  end_access %3
  end_access %2
  use(%y)
```
After redundant-load-elimination:
```
  %2 = begin_access [modify] [dynamic] %1
  store %x to %2
  %3 = begin_access [read] [dynamic] %1    // now dead, but still conflicting with %2
  end_access %3
  end_access %2
  use(%x)                                  // propagated from the store, but undefined here!
```
In this case the scope `%3` is not removed because it's important to get an access violation error at runtime before the undefined value `%x` is used.

This pass considers potential conflicting access scopes in called functions.
But it does not consider potential conflicting access in callers (because it can't!).
However, optimizations, like redundant-load-elimination, can only do such transformations if the outer access scope is within the function, e.g.

```
bb0(%0 : $*T):     // an inout from a conflicting scope in the caller
  store %x to %0
  %3 = begin_access [read] [dynamic] %1
  %y = load %3     // cannot be propagated because it cannot be proved that %1 is the same address as %0
  end_access %3
```

All those checks are only done for dynamic access scopes, because they matter for runtime exclusivity checking.
Dead static scopes are removed unconditionally.
2025-11-24 14:49:45 +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
Erik Eckstein
0ff2caea12 SimplifyInstructions: 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:05:15 +01:00
Michael Gottesman
c876c1ee88 [sil-verifier] Split SILVerifier verificationFailure into separate functions for Instructions, Arguments, and add a variant for Values.
This also required me to change how we handled which instruction/argument we
emit an error about in the verifier. Previously we were using two global
variables that we made nullptr to control which thing we emitted an error about.
This was unnecessary. Instead I added a little helper struct that internally
controls what we will emit an error about and an external "guard" RAII struct
that makes sure we push/pop the instruction/argument we are erroring upon
correctly.
2025-11-21 11:21:15 -08:00
Michael Gottesman
682ef268d2 [optimizer] Teach SIL optimizer that stack nesting should ignore nested stack allocations. 2025-11-21 11:21:15 -08:00
Henrik G. Olsson
7d6acaeddb Merge pull request #84941 from ramonasuncion/test-siloptimizer-fix-conditional-destroy
[Test][SILOptimizer] Fix di-conditional-destroy-scope test
2025-11-19 23:41:32 -08:00
Kavon Farvardin
6ab3a03c1d Merge pull request #85578 from kavon/opaque-values/fixes-4 2025-11-19 16:02:09 -08:00
Ramon Asuncion
ea38612066 [Test][SILOptimizer] Quote filter function name 2025-11-19 18:25:04 -05:00
Erik Eckstein
50c299e0bf LoopInvariantCodeMotion: don't reuse existing instructions in the loop pre-header
This is wrong for hoisted load instructions because we don't check for aliasing in the pre-header.
And for side-effect-free instructions it's not really necessary, because that can cleanup CSE afterwards.

Fixes a miscompile
rdar://164034503
2025-11-18 21:23:13 +01:00
Kavon Farvardin
e5f0ac6398 OpaqueValues: ensure no-implicit-copy captures are correct 2025-11-18 09:21:57 -08:00
Aidan Hall
d945dc4e0b Merge pull request #85491 from aidan-hall/disable-pack-specialization
SILOptimizer: Disable PackSpecialization pass until it has been properly fixed.

rdar://164515160
2025-11-18 15:34:22 +00:00
Evan Wilde
a3f9e0a766 SILOptimizer: Fix Int64 assumption for armv7 (#85512)
Fixing test failure on the armv7 Android builds.
2025-11-17 13:31:31 -08:00
Aidan Hall
305a5efaef SILOptimizer: Disable PackSpecialization pass to unblock SWBs
rdar://164515160
2025-11-17 17:38:13 +00:00
Yuta Saito
8748213d52 Merge pull request #85524 from kateinoigakukun/yt/fix-non-reg2mem-rle-crash 2025-11-16 23:10:20 +09:00
Yuta Saito
e259a257e6 RedundantLoadElim: Fix invalid destructure_struct emission without aggressive reg2mem
The `shouldExpand` in `OptUtils.swift` was incorrectly returning `true`
unconditionally when `useAggressiveReg2MemForCodeSize` was disabled. The
expansion might be invalid for types with addr-only types and structs
with deinit, but we didn't check them before. This could lead to invalid
`destructure_struct` instructions without `drop_deinit` being emitted.
2025-11-15 19:22:33 +00:00
Ramon Asuncion
4426bd584f [Test][SILOptimizer] Fix di-conditional-destroy-scope test 2025-11-15 03:18:10 -05:00
Meghana Gupta
4ebc087bef Merge pull request #85481 from meg-gupta/mutablespanbitwisecopyable
Update MutableSpan and OutputSpan
2025-11-13 22:51:28 -08:00
Meghana Gupta
827c30f13c Merge pull request #85482 from meg-gupta/reduceendcowmutation
Avoid inserting end_cow_mutation_addr in some common cases
2025-11-13 19:07:45 -08:00
Meghana Gupta
c93163cada Add _assumeNonNegative to OutputSpan's count as well 2025-11-13 13:48:27 -08:00
Meghana Gupta
404173de83 [NFC] Add MutableSpan bounds check test 2025-11-13 10:45:01 -08:00
Meghana Gupta
f0839be1cb Update MutableSpan bounds check tests 2025-11-13 08:58:24 -08:00
Meghana Gupta
b8376fd432 Add some read only tests for MutableSpan 2025-11-13 08:58:22 -08:00
Meghana Gupta
2365d12989 Update check lines after using standard library's MutableSpan and OutputSpan 2025-11-12 21:33:02 -08:00
Meghana Gupta
c5fd69d3f6 Update bounds check tests to use stdlib's MutableSpan and OutputSpan 2025-11-12 20:13:26 -08:00
Andrew Trick
b28cd59dfc [NFC] TypeLowering: add CustomDeinit.
Teach SIL type lowering to recursively track custom vs. default deinit status.

Determine whether each type recursively only has default deinitialization. This
includes any recursive deinitializers that may be invoked by releasing a
reference held by this type.

If a type only has default deinitialization, then the deinitializer cannot
have any semantically-visible side effects. It cannot write to any memory
2025-11-11 17:29:45 -08:00
Adrian Prantl
11356acd44 Merge pull request #85397 from adrian-prantl/163167975
[SILOptimzer] Fix a crash caused by SILCombine mishandling inlined variables
2025-11-10 18:41:07 -08:00
Meghana Gupta
e2123e1b3b Merge pull request #85362 from meg-gupta/moreborrow
Add SIL verification for borrow and mutate accessors and some other minor fixes
2025-11-10 09:04:28 -08:00
Adrian Prantl
99cf35cdce [SILOptimzer] Fix a crash caused by SILCombine mishandling inlined variables
This showed up on and off again on the source-compatibility testsuite project hummingbird.

The gist of the problem is that transformations may not rewrite the
type of an inlined instance of a variable without also createing a
deep copy of the inlined function with a different name (and e.g., a
specialization suffix). Otherwise the modified inlined variable will
cause an inconsistency when later compiler passes try to create the
abstract declaration of that inlined function as there would be
conflicting declarations for that variable.

Since SILDebugScope isn't yet available in the SwiftCompilerSources
this fix just drop these variables, but it would be absolutely
possible to preserve them by using the same mechanism that SILCloner
uses to create a deep copy of the inlined function scopes.

rdar://163167975
2025-11-07 17:06:33 -08:00