Commit Graph

366 Commits

Author SHA1 Message Date
Aidan Hall 1bd5b40421 LoopUnroll: Clone debug scopes to distinguish pack element types 2026-05-28 18:06:19 +01:00
Erik Eckstein 6a049fe92a ArrayPropertyOpt: fix an ownership error
The optimization can produce illegal SIL in some cases. The fix is to cleanup inserted phis by the SSAUpdater.

https://github.com/swiftlang/swift/issues/89324
rdar://177636712
2026-05-22 17:19:12 +02:00
Meghana Gupta d4cdfad4bc Add a sanity check to ensure we don't have self with address type while optimizing bounds checks for fixed storage types
When the self operand of a fixed_storage.check_index call is an address
type (e.g. @inout Span<Int>), the value at that address can be mutated
between bounds checks. Add a bailout to prevent illegal optimization in such cases.
2026-05-20 11:49:57 -07:00
Joe Groff 097b0d3400 SIL: Split unchecked_*_enum_data_addr according to ownership and effects.
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.
2026-04-27 15:40:37 -07:00
Meghana Gupta 82d99f9776 [BoundsCheckOpts] Fix merging bounds checks with fixed-storage semantics
When merging fixed-storage bounds checks with the same self value,
cloneFixedStorageIndex clones the index computation of a later check
to place it adjacent to an earlier one. The previous implementation could lead to
illegal SIL due to dominance violation in some cases by cloning an instruction
before it's operands were cloned.

Fix this with a recursive DFS that appends instructions in postorder.
Since a node is only added to the worklist after all its operands are recursively
processed, the resulting order is a valid topological sort that guarantees every
cloned instruction's operands are already available.
2026-04-21 03:19:07 -07:00
Meghana Gupta ff0557ab92 Bailout cloning indexes for non-trivial ossa instructions
In cloneFixedStorageIndex, bail out when an instruction to be cloned has
a lifetime-ending operand. Cloning such an instruction (e.g.
destructure_struct/destructure_tuple with an @owned operand, end_borrow of a begin_borrow)
may create a second consume, violating OSSA invariants.
2026-03-20 13:46:24 -07:00
Meghana Gupta c3e35843a9 Hoist bounds checks annotated with fixed storage semantics of the same self to appear together in a block
This change introduces an optimization that hoists bounds checks with fixed storage semantics when they operate on the same self value,
grouping them together within a basic block to enable downstream optimizations like partial vectorization.

Since the language guarantees the validity of underlying storage for types annotated with fixed storage semantics like Span, InlineArray etc,
we don't need to consult alias analysis to perform this optimization.

We have always hoisted cond_fail instructions over side effects in loops. This change enables hoisting over side-effects in blocks.

rdar://164947648 and rdar://166409418
2026-03-20 13:46:23 -07:00
Kavon Farvardin f3f782d65c Merge pull request #87788 from kavon/cbi-as-sil-analysis
SILOptimizer: wrap ColdBlockInfo as a SILAnalysis
2026-03-12 10:37:59 -07:00
Erik Eckstein 3f17264dba ForEachLoopUnroll: don't assume fixed lifetimes when deleting instructions
ForEachLoopUnroll runs in the mandatory pipeline. Therefore it can shrink non-lexical lifetimes.

rdar://169095733
2026-03-12 07:25:19 +01:00
Kavon Farvardin bbe8964995 SILOptimizer: wrap ColdBlockInfo as a SILAnalysis
ColdBlockInfo previously had to be constructed and run manually for each function.
Turning it into a SILAnalysis makes it lazily computed and cached per-function,
with automatic invalidation whenever the control-flow graph changes.

This is otherwise a non-functional change.
2026-03-11 10:52:42 -07:00
Erik Eckstein c20593cd58 Optimizer: better support of OSSA in various optimizations
Mainly:
* look through ownership instructions in more places
* support `load_borrow` and `store_borrow` where `load` and `store`s are expected
* support `destructure_struct` where `struct_extract` is expected
* enable inout-keypath optimization for OSSA
* OSSA support in StringOptimization
2026-03-10 07:56:51 +01:00
Erik Eckstein cbf1c24233 LoopUnroll: avoid iterating over an llvm::DenseMap
Use `llvm::MapVector` instead.
Fixes a potential non-determinism bug.
2026-02-05 13:32:11 +01:00
Meghana Gupta fbaa30b426 Bailout of ForEachLoopUnroll pass when sources of element stores cannot be extended 2026-02-02 16:13:10 -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
Erik Eckstein 02fafc63d6 Optimizer: support the new array literal initialization pattern in the ForEachLoopUnroll pass 2025-10-10 14:20:58 +02:00
Jakub Florek eae7864370 Merge pull request #83988 from MAJKFL/new-sil-licm-pass-copy
New SIL LICM pass
2025-09-01 10:28:17 +01:00
Jakub Florek 85aeb41bd0 Remove old LICM pass. 2025-08-28 22:57:46 +01:00
Jakub Florek bc86fe5a38 Hoist ArrayCallKind to a separate file 2025-08-28 20:50:33 +01:00
Erik Eckstein 87e7250750 Optimizer: fix an ownership violation when duplicating loops
If a guaranteed value is used in a dead-end exit block and the enclosing value is _not_ destroyed in this block, we end up missing the enclosing value as phi-argument after duplicating the loop.
TODO: once we have complete lifetimes we can remove this check again.

rdar://159125605
2025-08-28 18:40:54 +02:00
Anthony Latsis 2bfe2bd587 SIL: Treat -1 as signed when using it to construct an "all bits set" llvm::APInt
This should enable us to revert
https://github.com/swiftlang/swift/pull/81464/commits/73c70ee338c515fba37baf3faff93c6389558a5c.
2025-08-27 15:22:30 +01:00
Anthony Latsis 26540ca439 SIL: Do not use -1 to construct a 1-bit llvm::APInt
A -1 truncated to 1 bit is just 1, so stop overcomplicating things here.
2025-08-27 12:41:10 +01:00
Erik Eckstein 8666297597 BoundsCheckOpts: handle more integer comparison builtins
So far, only negated comparisons (via xor) were handled. Now, also handle simplified comparisons
2025-07-18 07:43:51 +02:00
Arnold Schwaighofer 11942d70bd Merge pull request #81995 from aschwaighofer/fix_looprotate_densemap_subscript_bug
LoopRotate: Fix a by reference map bug under reallocation
2025-06-06 12:56:10 -07:00
Arnold Schwaighofer 1860967a29 Add a comment 2025-06-04 13:27:44 -07:00
Arnold Schwaighofer 59b8813807 LoopRotate: Fix a by reference map bug under reallocation
When using a densemap subscript expression on both sides of an
assignment in the same statement of the same map we run into the issue
that the map can reallocate because of the assignment but we are
referencing the value of the RHS map subscript by reference --
i.e we can reference deallocated memory.

Not good.

My attempts at making a reproducer crash failed.

rdar://151031297
2025-06-04 12:57:00 -07:00
Erik Eckstein cbbf33dc55 LICM: fix a wrong tuple type when splitting loads
When creating a tuple, the type needs to be specified because otherwise, if the original tuple has labels, it will cause a type mismatch verification error.

rdar://152588539
2025-06-04 19:59:54 +02:00
Erik Eckstein 783f90b76c LICM: handle memory dependency for store sinking correctly
Prevent sinking of stores if there are instructions other than `load` which may read from memory.
This kind of memory dependencies were ignored.
Fixes SIL verifier crashes or - in worst case - miscompiles.

rdar://150205299
2025-04-29 17:46:56 +02:00
Evan Wilde bca1378fdb SILOptimizer: Disable invalid passes in C++-only compiler
The SimplifyCFG and LoopRotate passes result in verification failures
when built in a compiler that is not built with Swift sources enabled.

Fixes: rdar://146357242
2025-04-21 12:46:01 -07:00
Andrew Trick d9dd93560d Support mark_dependence_addr in SIL passes. 2025-03-25 23:02:45 -07:00
Pavel Yaskevich 41c88f864a [SILOptimizer] Turn "is self-recursive" check into analysis
The body of a function has to be re-analyzed for every call
site of the function, which is very expensive and if the
body is not changed would produce the same result.

This takes about ~10% from swift-syntax overall build time
in release configuration.
2025-03-24 00:25:13 -07:00
Meghana Gupta 26277c8363 Add support for bounds check optimization of Span and InlineArray 2025-02-28 17:11:52 -08:00
Meghana Gupta 3fe1029ef8 [NFC] Reorganize and rename ArrayBoundsCheckOpts.cpp 2025-02-28 09:50:58 -08:00
Erik Eckstein bfdeb57863 LICM: fix handling of stores of Optional.none in OSSA
Currently we don't support hoisting ownership instructions.
But the check was missing a store of Optional.none because such a value has no ownership even if the optional is not trivial.

Fixes a SIL verifier crash.
https://github.com/swiftlang/swift/issues/79491
2025-02-25 09:34:59 +01:00
Meghana Gupta 03038d201e Fix loop rotate when header has instructions producing ownership results
rdar://145086395
2025-02-18 14:42:28 -08:00
Erik Eckstein 83aaccc188 remove the "array.copy_into_vector" array-semantic
It's not needed anymore, because the "FixedArray" experimental feature is replaced by inline-arrays.
2025-02-12 10:51:14 +01:00
Meghana Gupta 6a47193e0c Fix LICM for begin_access/end_access in OSSA
LICM in OSSA is enabled only for instructions involving trvial values.

begin_access/end_access is eligible for LICM in OSSA. However we need to
collect applies to check for conflicts.

rdar://143835241
2025-01-29 12:54:35 -08:00
Erik Eckstein 5dc80f62dc LoopRotate: don't rotate loops where the header block branches to a dead-end block.
Incomplete liveranges in the dead-end exit block can cause a missing adjacent phi-argument for a re-borrow if there is a borrow-scope is in the loop.
But even when we have complete lifetimes, it's probably not worth rotating a loop where the header block branches to a dead-end block.

Fixes a SIL verifier crash
2025-01-21 19:08:08 +01:00
Erik Eckstein 48b913af4b Optimizer: make the hasOwnershipOperandsOrResults utility available in OwnershipOptUtils 2025-01-02 10:42:01 +01:00
Erik Eckstein 8439d0bc46 LoopRotate: remove redundant phis after ssa-update
Fixes an ownership error
2024-12-12 09:22:20 +01:00
Erik Eckstein 301ab4e112 LoopRotate: remove the replace-arg-with-struct peephole optimization
This does not belong to loop-rotate and does not work with OSSA.
This peephole is covered by other optimizations.
2024-12-12 08:35:48 +01:00
Erik Eckstein 1bd74d1fc3 LICM: (limited) support for OSSA
The first step: allow hoisting instructions which only have trivial operands and results.
2024-12-11 12:32:33 +01:00
Erik Eckstein 074a99cc39 LoopRotate: don't rotate a loop if the new header is loop exiting as well
This doesn't give any performance benefit.
2024-12-11 12:32:33 +01:00
Erik Eckstein 8000802481 LoopRotate: handle copy_value and begin_borrow correctly 2024-12-11 12:32:32 +01:00
Erik Eckstein 7e8410ebc1 COWArrayOpt: handle load_borrow 2024-12-11 12:32:32 +01:00
Erik Eckstein 51e3e5ed80 Optimizer: rename BorrowArgumentsUpdater -> GuaranteedPhiUpdater
NFC
2024-11-12 09:26:59 +01:00
Erik Eckstein 626b40c329 LoopRotate: don't verify the optimized function within the pass
With the new re-borrow flag computation, verification can fail before the re-borrow flags are updated at the end of the pass.
2024-11-12 09:26:59 +01:00
Erik Eckstein 6b8c6a3c3b SIL: rename updateBorrowedFrom to updateBorrowArguments
NFC
2024-11-12 09:26:58 +01:00
Kavon Farvardin 7203a4fa73 ColdBlockInfo: overhaul analysis pass
The old analysis pass doesn't take into account profile data, nor does
it consider post-dominance. It primarily dealt with _fastPath/_slowPath.

A block that is dominated by a cold block is itself cold. That's true
whether it's forwards or backwards dominance.

We can also consider a call to any `Never` returning function as a
cold-exit, though the block(s) leading up to that call may be executed
frequently because of concurrency. For now, I'm ignoring the concurrency
case and assuming it's cold. To make use of this "no return" prediction,
use the `-enable-noreturn-prediction` flag, which is currently off by
default.
2024-09-03 15:41:10 -07:00
Meghana Gupta 49bb19d27e Fix debug info in LICM
Use RegularLocation::getAutoGeneratedLocation() while hoisting a load.
Previously the source location of the preheader's terminator was used,
this need not be a RegularLocationKind triggering verification errors.
2024-08-14 01:49:58 +05:30