Commit Graph

90 Commits

Author SHA1 Message Date
Erik Eckstein
46035305aa Optimizer: improve simplification of alloc_stack
* Reimplement most of the logic in Swift as an Instruction simplification and remove the old code from SILCombine
* support more cases of existential archetype replacements:

For example:
```
  %0 = alloc_stack $any P
  %1 = init_existential_addr %0, $T
  use %1
```
is transformed to
```
  %0 = alloc_stack $T
  use %0
```

Also, if the alloc_stack is already an opened existential and the concrete type is known,
replace it as well:
```
  %0 = metatype $@thick T.Type
  %1 = init_existential_metatype %0, $@thick any P.Type
  %2 = open_existential_metatype %1 : $@thick any P.Type to $@thick (@opened("X", P) Self).Type
  ...
  %3 = alloc_stack $@opened("X", any P) Self
  use %3
```
is transformed to
```
  ...
  %3 = alloc_stack $T
  use %3
```
2025-03-07 15:59:34 +01:00
Erik Eckstein
5572c832e1 SILOptimizer: replace existential archetypes with concrete types in witness_method instructions 2025-03-07 15:59:34 +01:00
Erik Eckstein
71de3d90e0 Optimizer: replace existential archetypes with concrete types in apply instructions
If an apply uses an existential archetype (`@opened("...")`) and the concrete type is known, replace the existential archetype with the concrete type
  1. in the apply's substitution map
  2. in the arguments, e.g. by inserting address casts
For example:
```
  %5 = apply %1<@opend("...")>(%2) : <τ_0_0> (τ_0_0) -> ()
```
->
```
  %4 = unchecked_addr_cast %2 to $*ConcreteType
  %5 = apply %1<ConcreteType>(%4) : <τ_0_0> (τ_0_0) -> ()
```
2025-03-07 15:59:34 +01:00
Erik Eckstein
35097b5a71 Optimizer: simplify unconditional_checked_cast to existential metatypes.
Replace `unconditional_checked_cast` to an existential metatype with an `init_existential_metatype`, it the source is a conforming type.
Note that init_existential_metatype is better than unconditional_checked_cast because it does not need to do any runtime casting.
2025-03-07 15:59:33 +01:00
eeckstein
dff88f968b Merge pull request #79317 from eeckstein/remove-alloc-vector
Remove the experimental FixedArray
2025-02-12 17:03:06 +01:00
Erik Eckstein
5b93eb31bf Optimizer: remove the AllocVectorLowering pass
It's not needed anymore, because the "FixedArray" experimental feature is replaced by inline-arrays.
2025-02-12 10:51:14 +01:00
Erik Eckstein
3f95ce9645 Optimizer: fix spelling of the Simplifiable protocol
Rename `Simplifyable` -> `Simplifiable`

NFC
2025-02-12 09:01:11 +01:00
Erik Eckstein
ce73deebc4 RedundantLoadElimination: add a "mandatory" redundant load elimination pass
And make `eliminateRedundantLoads` callable from other optimizations
2025-02-07 11:30:35 +01:00
Erik Eckstein
9aff288be4 Optimizer: re-implement the pointer_to_address SILCombine peephole optimizations in swift
Which consists of
* removing redundant `address_to_pointer`-`pointer_to_address` pairs
* optimize `index_raw_pointer` of a manually computed stride to `index_addr`
* remove or increase the alignment based on a "assumeAlignment" builtin

This is a big code cleanup but also has some functional differences for the `address_to_pointer`-`pointer_to_address` pair removal:

* It's not done if the resulting SIL would result in a (detectable) use-after-dealloc_stack memory lifetime failure.
* It's not done if `copy_value`s must be inserted or borrow-scopes must be extended to comply with ownership rules (this was the task of the OwnershipRAUWHelper).

Inserting copies is bad anyway.
Extending borrow-scopes would only be required if the original lifetime of the pointer extends a borrow scope - which shouldn't happen in save code. Therefore this is a very rare case which is not worth handling.
2024-12-21 08:28:22 +01:00
Hamish Knight
f728466273 [SwiftCompilerSources] Use interpolation instead of String(describing:)
`String(describing:)` does a bunch of dynamic casts
that can be pretty slow. Use interpolation instead,
which bypasses them.

For `swift-frontend`, this brings the time taken
for type-checking an empty file down from ~100ms
to ~70ms.

For `swift build`, this brings the time taken for
a null build down from ~600ms to ~450ms (the
larger delta is presumably due to the fact that
there's much more Swift code in `swift-package`).
2024-12-19 15:33:39 +00:00
Erik Eckstein
bf496aa4f6 Optimizer: add simplification for fix_lifetime
Canonicalize a `fix_lifetime` from an address to a `load` + `fix_lifetime`:
```
   %1 = alloc_stack $T
   ...
   fix_lifetime %1
```
->
```
   %1 = alloc_stack $T
   ...
   %2 = load %1
   fix_lifetime %2
```

This transformation is done for `alloc_stack` and `store_borrow` (which always has an `alloc_stack` operand).
The benefit of this transformation is that it enables other optimizations, like mem2reg.

This peephole optimization was already done in SILCombine, but it didn't handle store_borrow.
A good opportunity to make an instruction simplification out of it.

This is part of fixing regressions when enabling OSSA modules:
rdar://140229560
2024-12-13 12:06:20 +01:00
Erik Eckstein
6990a195a3 Optimizer: rename GuaranteedPhiUpdater -> PhiUpdater
Because it now has the replacePhisWithIncomingValues utility, which works for all kind of phis.
2024-12-12 09:09:11 +01:00
Erik Eckstein
3e35df0983 Simplification: run begin_borrow simplification in SILCombine 2024-12-11 12:32:34 +01:00
Erik Eckstein
6b38f2aab4 Optimizer: simplify load_borrow
* Remove dead `load_borrow` instructions (replaces the old peephole optimization in SILCombine)
* If the `load_borrow` is followed by a `copy_value`, combine both into a `load [copy]`
2024-12-11 12:32:33 +01:00
eeckstein
81c65758e3 Merge pull request #78059 from eeckstein/destroy-hoisting
Optimizer: add a new destroy-hoisting optimization
2024-12-11 06:18:05 +01:00
Erik Eckstein
5be781a9a0 Optimizer: add a new destroy-hoisting optimization
It hoists `destroy_value` instructions  without shrinking an object's lifetime.
This is done if it can be proved that another copy of a value (either in an SSA value or in memory) keeps the referenced object(s) alive until the original position of the `destroy_value`.
```
  %1 = copy_value %0
  ...
  last_use_of %0
  // other instructions
  destroy_value %0       // %1 is still alive here
```
->
```
  %1 = copy_value %0
  ...
  last_use_of %0
  destroy_value %0
  // other instructions
```

The benefit of this optimization is that it can enable copy-propagation by moving destroys above deinit barries and access scopes.
2024-12-10 16:28:11 +01:00
Erik Eckstein
dd78dc722b Optimizer: add an optimization to remove copy_value of a borrowed value.
It removes a `copy_value` where the source is a guaranteed value, if possible:

```
  %1 = copy_value %0   // %0 = a guaranteed value
  // uses of %1
  destroy_value %1     // borrow scope of %0 is still valid here
```
->
```
  // uses of %0
```

This optimization is very similar to the LoadCopyToBorrow optimization.
Therefore I merged both optimizations into a single file and renamed it to "CopyToBorrowOptimization".
2024-12-09 20:01:07 +01:00
Erik Eckstein
99ef6f727d Optimizer: replace unchecked_enum_data simplification in SILCombine with the corresponding instruction simplification from SwiftCompilerSources
The optimization in SILCombine had a bug (which is already fixed in the instruction simplification).
2024-11-14 09:18:29 +01:00
Erik Eckstein
51e3e5ed80 Optimizer: rename BorrowArgumentsUpdater -> GuaranteedPhiUpdater
NFC
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
Erik Eckstein
b8026d74e6 Revert "Revert "Optimizer: improve the load-copy-to-borrow optimization and implement it in swift""
This reverts commit 0666c446ec.
2024-10-22 08:40:18 +02:00
Erik Eckstein
0666c446ec Revert "Optimizer: improve the load-copy-to-borrow optimization and implement it in swift"
This reverts commit eed8645610.
2024-10-18 10:36:06 +02:00
Erik Eckstein
eed8645610 Optimizer: improve the load-copy-to-borrow optimization and implement it in swift
The optimization replaces a `load [copy]` with a `load_borrow` if possible.

```
  %1 = load [copy] %0
  // no writes to %0
  destroy_value %1
```
->
```
  %1 = load_borrow %0
  // no writes to %0
  end_borrow %1
```

The new implementation uses alias-analysis (instead of a simple def-use walk), which is much more powerful.

rdar://115315849
2024-10-11 09:41:37 +02:00
Erik Eckstein
c97502374b Optimizer: add constant folding of classify_bridge_object
Constant fold `classify_bridge_object` to `(false, false)` if the operand is known to be a swift class.
2024-10-08 16:24:46 +02:00
Erik Eckstein
10782cf42b SwiftCompilerSources: introduce the AST module
As the optimizer uses more and more AST stuff, it's now time to create an "AST" module.
Initially it defines following AST datastructures:
* declarations: `Decl` + derived classes
* `Conformance`
* `SubstitutionMap`
* `Type` and `CanonicalType`

Some of those were already defined in the SIL module and are now moved to the AST module.
This change also cleans up a few things:
* proper definition of `NominalTypeDecl`-related APIs in `SIL.Type`
* rename `ProtocolConformance` to `Conformance`
* use `AST.Type`/`AST.CanonicalType` instead of `BridgedASTType` in SIL and the Optimizer
2024-10-02 07:10:29 +02:00
Alejandro Alonso
b35ac50d3c Optimize TypeValueInst in Swift 2024-09-04 15:13:48 -07:00
Erik Eckstein
f9b524b1cb AliasAnalysis: a complete overhaul of alias- and memory-behavior analysis
The main changes are:

*) Rewrite everything in swift. So far, parts of memory-behavior analysis were already implemented in swift. Now everything is done in swift and lives in `AliasAnalysis.swift`. This is a big code simplification.

*) Support many more instructions in the memory-behavior analysis - especially OSSA instructions, like `begin_borrow`, `end_borrow`, `store_borrow`, `load_borrow`. The computation of end_borrow effects is now much more precise. Also, partial_apply is now handled more precisely.

*) Simplify and reduce type-based alias analysis (TBAA). The complexity of the old TBAA comes from old days where the language and SIL didn't have strict aliasing and exclusivity rules (e.g. for inout arguments). Now TBAA is only needed for code using unsafe pointers. The new TBAA handles this - and not more. Note that TBAA for classes is already done in `AccessBase.isDistinct`.

*) Handle aliasing in `begin_access [modify]` scopes. We already supported truly immutable scopes like `begin_access [read]` or `ref_element_addr [immutable]`. For `begin_access [modify]` we know that there are no other reads or writes to the access-address within the scope.

*) Don't cache memory-behavior results. It turned out that the hit-miss rate was pretty bad (~ 1:7). The overhead of the cache lookup took as long as recomputing the memory behavior.
2024-07-29 17:33:46 +02:00
Kshitij
fd609846ae Makes the swift-based closure-spec pass an experimental frontend feature 2024-05-02 09:14:05 -07:00
Kshitij
c8375c06ae [Autodiff] Adds part of the closure-specialization optimization pass
Changes in this CR add part of the, Swift based, Autodiff specific
closure specialization optimization pass. The pass does not modify any
code nor does it even exist in any of the optimization pipelines. The
rationale for pushing this partially complete optimization pass upstream
is to keep up with the breaking changes in the underlying Swift based
compiler infrastructure.
2024-05-02 09:14:05 -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
Meghana Gupta
7508a90af0 Enable destructure_struct/destructure_tuple simplifications in silcombine 2024-02-26 10:58:08 -08:00
Erik Eckstein
71fcae7fe8 SwiftCompilerSources: add the ability to implement SIL verification in swift 2024-02-22 07:12:10 +01:00
Meghana Gupta
75bd5b08dd Add LifetimeDependenceScopeFixup pass 2024-02-13 16:52:01 -08:00
Andrew Trick
8c092911ef Add the LifetimeDependenceInsertion pass.
Insert mark_dependence [nonescaping] markers at every lifetime
introducer that produces a lifetime-dependent value.
2024-02-12 09:57:14 -08:00
Andrew Trick
ddceffaf3b LifetimeDependenceDiagnostics pass
Initial diagnostic pass to enforce ~Escapable types.
2024-01-30 11:45:55 -08:00
Erik Eckstein
c89df9ec98 Mandatory optimizations: constant fold boolean literals before the DefiniteInitialization pass
Add a new mandatory BooleanLiteralFolding pass which constant folds conditional branches with boolean literals as operands.

```
  %1 = integer_literal -1
  %2 = apply %bool_init(%1)   // Bool.init(_builtinBooleanLiteral:)
  %3 = struct_extract %2, #Bool._value
  cond_br %3, bb1, bb2
```
->
```
  ...
  br bb1
```

This pass is intended to run before DefiniteInitialization, where mandatory inlining and constant folding didn't run, yet (which would perform this kind of optimization).
This optimization is required to let DefiniteInitialization handle boolean literals correctly.
For example in infinite loops:

```
   init() {
     while true {           // DI need to know that there is no loop exit from this while-statement
       if some_condition {
         member_field = init_value
         break
       }
     }
   }
```
2024-01-10 16:15:57 +01:00
Erik Eckstein
ae278058e6 Add an experimental pass to lower allocateVector builtins
By default it lowers the builtin to an `alloc_vector` with a paired `dealloc_stack`.
If the builtin appears in the initializer of a global variable and the vector elements are initialized,
a statically initialized global is created where the initializer is a `vector` instruction.
2023-12-09 18:49:58 +01:00
Erik Eckstein
96e57d62f6 Optimizer: de-virtualize deinits of non-copyable types
In regular swift this is a nice optimization. In embedded swift it's a requirement, because the compiler needs to be able to specialize generic deinits of non-copyable types.
The new de-virtualization utilities are called from two places:

* from the new DeinitDevirtualizer pass. It replaces the old MoveOnlyDeinitDevirtualization, which is very basic and does not fulfill the needs for embedded swift.

* from MandatoryPerformanceOptimizations for embedded swift
2023-11-27 09:21:34 +01:00
Rintaro Ishizaki
47f18d492e [ASTGen] Move regex literal parsing from SwiftCompilerSources to ASTGen
ASTGen always builds with the host Swift compiler, without requiring
bootstrapping, and is enabled in more places. Move the regex literal
parsing logic there so it is enabled in more host environments, and
makes use of CMake's Swift support. Enable all of the regex literal
tests when ASTGen is built, to ensure everything is working.

Remove the "AST" and "Parse" Swift modules from SwiftCompilerSources,
because they are no longer needed.
2023-11-16 10:59:23 -08:00
Erik Eckstein
e718bfe8ed Optimizer: reimplement simplifications for copy_value and destroy_value in swift
So that they can run in the OnoneSimplification pass
2023-10-27 10:47:07 +02:00
Erik Eckstein
2dbd6cc56b SwiftCompilerSources: rework bridging
Introduce two modes of bridging:
* inline mode: this is basically how it worked so far. Using full C++ interop which allows bridging functions to be inlined.
* pure mode: bridging functions are not inlined but compiled in a cpp file. This allows to reduce the C++ interop requirements to a minimum. No std/llvm/swift headers are imported.

This change requires a major refactoring of bridging sources. The implementation of bridging functions go to two separate files: SILBridgingImpl.h and OptimizerBridgingImpl.h.
Depending on the mode, those files are either included in the corresponding header files (inline mode), or included in the c++ file (pure mode).

The mode can be selected with the BRIDGING_MODE cmake variable. By default it is set to the inline mode (= existing behavior). The pure mode is only selected in certain configurations to work around C++ interop issues:
* In debug builds, to workaround a problem with LLDB's `po` command (rdar://115770255).
* On windows to workaround a build problem.
2023-10-09 09:52:52 +02:00
Nate Chandler
ae1f950315 [SwiftCompilerSources] Moved Test into Optimizer.
And changed the type of the context argument to FunctionPassContext.
2023-10-07 21:23:13 -07:00
Nate Chandler
dab8c146a6 [SwiftCompilerSources] Bridged in-IR testing.
Added the bridging types involved and the basic functionality.
2023-09-28 11:33:50 -07:00
Kavon Farvardin
b688a1f4a1 [SILOpt] experimental async demotion pass
For chains of async functions where suspensions can be statically
proven to never be required, this pass removes all suspensions and
turns the functions into synchronous functions.

For example, this function does not actually require any suspensions,
once the correct executor is acquired upon initial entry:

```
func fib(_ n: Int) async -> Int {
  if n <= 1 { return n }
  return await fib(n-1) + fib(n-2)
}
```

So we can turn the above into this for better performance:

```
func fib() async -> Int {
  return fib_sync()
}

func fib_sync(_ n: Int) -> Int {
  if n <= 1 { return n }
  return fib(n-1) + fib(n-2)
}
```

while rewriting callers of `fib` to use the `sync` entry-point
when we can prove that it will be invoked on a compatible executor.

This pass is currently experimental and under development. Thus, it
is disabled by default and you must use
`-enable-experimental-async-demotion` to try it.
2023-09-21 12:21:02 -07:00
Erik Eckstein
5bc036661c SIL optimizer: add the LetPropertyLowering pass
It lowers let property accesses of classes.
Lowering consists of two tasks:

* In class initializers, insert `end_init_let_ref` instructions at places where all let-fields are initialized.
  This strictly separates the life-range of the class into a region where let fields are still written during
  initialization and a region where let fields are truly immutable.

* Add the `[immutable]` flag to all `ref_element_addr` instructions (for let-fields) which are in the "immutable"
  region. This includes the region after an inserted `end_init_let_ref` in an class initializer, but also all
  let-field accesses in other functions than the initializer and the destructor.

This pass should run after DefiniteInitialization but before RawSILInstLowering (because it relies on `mark_uninitialized` still present in the class initializer).
Note that it's not mandatory to run this pass. If it doesn't run, SIL is still correct.

Simplified example (after lowering):

  bb0(%0 : @owned C):                           // = self of the class initializer
    %1 = mark_uninitialized %0
    %2 = ref_element_addr %1, #C.l              // a let-field
    store %init_value to %2
    %3 = end_init_let_ref %1                    // inserted by lowering
    %4 = ref_element_addr [immutable] %3, #C.l  // set to immutable by lowering
    %5 = load %4
2023-09-19 15:10:30 +02:00
Erik Eckstein
4d20423e00 Optimizer: re-implement the RedundantLoadElimination pass in Swift
The new implementation has several benefits compared to the old C++ implementation:

* It is significantly simpler. It optimizes each load separately instead of all at once with bit-field based dataflow.
* It's using alias analysis more accurately which enables more loads to be optimized
* It avoids inserting additional copies in OSSA

The algorithm is a data flow analysis which starts at the original load and searches for preceding stores or loads by following the control flow in backward direction.
The preceding stores and loads provide the "available values" with which the original load can be replaced.
2023-07-21 07:19:56 +02:00
Erik Eckstein
baaf5565b0 Optimizer: reimplement DeadStoreElimination in swift
The old C++ pass didn't catch a few cases.
Also:
* The new pass is significantly simpler: it doesn't perform dataflow for _all_ memory locations at once using bitfields, but handles each store separately. (In both implementations there is a complexity limit in place to avoid quadratic complexity)
* The new pass works with OSSA
2023-07-05 21:33:25 +02:00
Erik Eckstein
55c8c433c0 SILOptimizer: add the StripObjectHeader optimization pass
It sets the `[bare]` attribute for `alloc_ref` and `global_value` instructions if their header (reference count and metatype) is not used throughout the lifetime of the object.
2023-06-29 06:57:05 +02:00
Erik Eckstein
50c23a1640 Optimizer: implement the SILCombine peephole optimizations for retain_value and release_value in Swift 2023-06-07 14:18:38 +02:00
Erik Eckstein
4284dc10d0 Optimizer: implement the ObjectOutliner pass in Swift 2023-05-22 15:34:26 +02:00