Commit Graph

262 Commits

Author SHA1 Message Date
Alejandro Alonso
b35ac50d3c Optimize TypeValueInst in Swift 2024-09-04 15:13:48 -07:00
Ryan Mansfield
908d97dde5 SIL: Fix cl::desc typos. [NFC] 2024-08-29 16:26:56 -04: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
Michael Gottesman
c4f7076baf [region-isolation] When RegionIsolation is enabled, delay the preconcurrency import not used diagnostic to the SIL pipeline.
The reason why I am doing this is that I am going to be adding support for
preconcurrency imports to TransferNonSendable. That implies that we can have
preconcurrency import suppression in the SIL pipeline and thus that emitting the
diagnostic in Sema is too early.

To do this, I introduced a new module pass called
DiagnoseUnnecessaryPreconcurrencyImports that runs after the SILFunction pass
TransferNonSendable. The reason why I use a module pass is to ensure that
TransferNonSendable has run on all functions before we attempt to emit these
diagnostics. Then in that pass, we iterate over all of the modules functions and
construct a uniqued array of SourceFiles for these functions. Then we iterate
over the uniqued SourceFiles and use the already constructed Sema machinery to
emit the diagnostic using the source files.

rdar://126928265
2024-04-23 12:42:43 -05: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
Artem Chikin
69fdc1356c Revert "Revert "Add mandatory SIL pass implementing '@_alwaysEmitConformanceMetadata' protocol attribute"" 2024-04-03 09:29:51 -07:00
Michael Gottesman
3236bc26fa [region-isolation] Refactor out the stubify dead function if no longer used functionality from move only checker into its own pass and put it before region based isolation.
I am doing this since region based isolation hit the same issue that the move
checker did. So it makes sense to refactor the functionality into its own pass
and move it into a helper pass that runs before both.

It is very conservative and only stubifies functions that the specialization
passes explicitly mark as this being ok to be done to.
2024-03-01 13:11:07 -08:00
Meghana Gupta
7508a90af0 Enable destructure_struct/destructure_tuple simplifications in silcombine 2024-02-26 10:58:08 -08:00
Nate Chandler
5a6873b98f [SILOptimizer] Remove DestroyHoisting pass.
It's no longer used.
2024-02-19 11:41:38 -08: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
Michael Gottesman
e29e035ab8 [region-analysis] Add a pass that explicitly invalidates RegionAnalysis.
Over time I am going to be using RegionAnalysis for a series of passes that all
use that same information since I am worried about RegionAnalysis computation
time. With that being said, we want to make sure to eliminate the memory that
RegionAnalysis uses once this series of passes have completed. What this commit
does is create a pass that explicitly invalidates region analysis and explicitly
places it in the pass pipeline after the series of passes. This will ensure that
even if we add an additional pass, there is a strong "rattlesnake" signal to the
new code author that the code needs to be placed before the region analysis
invalidation and will prevent mistakes such as having to recompute the region
analysis in that later pass or the later pass forgeting to invalidate the
analysis.
2024-02-09 11:59:04 -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
17f246e219 Remove the old MoveOnlyDeinitDevirtualization.cpp pass 2023-11-27 09:21:34 +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
Michael Gottesman
d2b5bc33a1 [sil-optimizer] Add a small pass that runs after TransferNonSendable and eliminates tuple addr constructor.
This will limit the number of passes that need to be updated to handle
tuple_addr_constructor.
2023-11-06 15:47:15 -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
Michael Gottesman
0bad8f9b67 [region-isolation] Rename SendNonSendable.cpp -> TransferNonSendable.cpp. 2023-10-26 12:01:44 -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
Kuba Mracek
d0c2a4ccf8 [embedded] Initial support for generic classes in embedded Swift
- VTableSpecializer, a new pass that synthesizes a new vtable per each observed concrete type used
- Don't use full type metadata refs in embedded Swift
- Lazily emit specialized class metadata (LazySpecializedClassMetadata) in IRGen
- Don't emit regular class metadata for a class decl if it's generic (only emit the specialized metadata)
2023-09-12 09:44:54 -07:00
Kuba Mracek
5dac59ce71 Move TargetConstantFolding pass to the simplification passes in Swift, enable using MemoryLayout's .size, .stride, .alignment fields in forced-const global initializers 2023-07-31 10:54:07 -07:00
Erik Eckstein
2384a0c6f4 Optimizer: remove the now unused LSLocation utilities 2023-07-21 07:19:56 +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
nate-chandler
d2c8ede38d Merge pull request #67069 from nate-chandler/test/20230629/2
[Test] Moved SIL function tests next to tested code.
2023-07-04 19:34:50 -07:00
Nate Chandler
e9ab3b4b53 [Test] Refactored SIL "unit" tests.
Renamed UnitTest to FunctionTest.

FunctionTests are now instantiated once as global objects--with their
names and the code they are to run--at which time they are stored by
name in a global registry.

Moved the types to the SIL library.

Together, these changes enable defining unit tests in the source file
containing the code to be tested.
2023-07-04 11:52:11 -07:00
jturcotti
aa9f1a3584 add an experimental feature DeferSendableChecking to defer the sendable checking of some sites. For now, only diagnostics corresponding to non-sendable arguments passed to calls with unsatisfied isolation are deferred. A SIL pass SendNonSendable is added to emit the deferred diagnostics, and ApplyExpr is appropriately enriched to make that deferral possible. 2023-07-03 09:52:11 -07: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
Michael Gottesman
170ba67bd3 [move-only] Eliminate some temporary copies inserted by SILGen when accessing fields of lets.
In the next commit, I am modifying the move only checker to ensure that we
always error when partially invalidating. While doing this I discovered that
these copies were getting in the way of emitting good diagnostics in that case.
2023-06-27 13:41:31 -07: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
Andrew Trick
0bbd92a446 [move-only] Rename MoveOnlyDeinitInsertion
to MoveOnlyDeinitDevirtualization
2023-06-06 09:17:53 -07:00
Nate Chandler
d1ec1e6693 [IRGen] Dealloc metadata packs on dom frontier.
Deallocate dynamic allocas done for metadata/wtable packs.  These
stackrestore calls are inserted on the dominance frontier and then stack
nesting is fixed up.  That was achieved as follows:

Added a new IRGen pass PackMetadataMarkerInserter; it
- determines if there are any instructions which might allocate on-stack
  pack metadata
- if there aren't, no changes are made
- if there are, alloc_pack_metadata just before instructions that could
  allocate pack metadata on the stack and dealloc_pack_metadata on the
  dominance frontier of those instructions
- fixup stack nesting

During IRGen, the allocations done for metadata/wtable packs are
recorded and IRGenSILFunction associates them with the instruction that
lowered.  It must be the instruction after some alloc_pack_metadata
instruction.  Then, when visiting the dealloc_pack_metadata instructions
corresponding to that alloc_pack_metadata, deallocate those packs.
2023-06-05 08:11:28 -07:00
Erik Eckstein
4284dc10d0 Optimizer: implement the ObjectOutliner pass in Swift 2023-05-22 15:34:26 +02:00
Erik Eckstein
f3851f7503 Optimizer: implement load simplification in Swift 2023-05-22 15:34:26 +02:00
Michael Gottesman
3ccda34275 Revert "[move-only] Ensure that we treat captured escaping closure arguments as such even if the closure doesn't actually escape"
This reverts commit 224674cad1.

Originally, I made this change since we were going to follow the AST in a strict
way in terms of what closures are considered escaping or not from a diagnostics
perspective. Upon further investigation I found that we actually do something
different for inout escaping semantics and by treating the AST as the one point
of truth, we are being inconsistent with the rest of the compiler. As an
example, the following code is considered by the compiler to not be an invalid
escaping use of an inout implying that we do not consider the closure to be
escaping:

```
func f(_ x: inout Int) {
  let g = {
    _ = x
  }
}
```

in contrast, a var is always considered to be an escape:

```
func f(_ x: inout Int) {
  var g = {
    _ = x
  }
}

test2.swift:3:13: error: escaping closure captures 'inout' parameter 'x'
    var g = {
            ^
test2.swift:2:10: note: parameter 'x' is declared 'inout'
func f(_ x: inout Int) {
         ^
test2.swift:4:11: note: captured here
        _ = x
          ^
```

Of course, if we store the let into memory, we get the error one would expect:

```
var global: () -> () = {}
func f(_ x: inout Int) {
  let g = {
    _ = x
  }
  global = g
}

test2.swift:4:11: error: escaping closure captures 'inout' parameter 'x'
  let g = {
          ^
test2.swift:3:10: note: parameter 'x' is declared 'inout'
func f(_ x: inout Int) {
         ^
test2.swift:5:7: note: captured here
    _ = x
      ^
```

By reverting to the old behavior where allocbox to stack ran early, noncopyable
types now have the same sort of semantics: let closures that capture a
noncopyable type that do not on the face of it escape are considered
non-escaping, while if the closure is ever stored into memory (e.x.: store into
a global, into a local var) or escapes, we get the appropriate escaping
diagnostics. E.x.:

```
public struct E : ~Copyable {}
public func borrowVal(_ e: borrowing E) {}
public func consumeVal(_ e: consuming E) {}

func f1() {
  var e = E()

  // Mutable borrowing use of e. We can consume e as long as we reinit at end
  // of function. We don't here, so we get an error.
  let c1: () -> () = {
    borrowVal(e)
    consumeVal(e)
  }

  // Mutable borrowing use of e. We can consume e as long as we reinit at end
  // of function. We do do that here, so no error.
  let c2: () -> () = {
    borrowVal(e)
    consumeVal(e)
    e = E()
  }
}

```
2023-05-14 13:34:36 -07:00
Erik Eckstein
13108d69f6 SILOptimizer: remove the obsolete MandatoryGenericSpecializer pass 2023-05-11 08:11:44 +02:00
Erik Eckstein
dc3cb18029 Swift Optimizer: add the MandatoryPerformanceOptimizations pass
As a replacement for the old MandatoryGenericSpecializer

The pass it not enabled yet in the pass pipeline
2023-05-11 08:11:44 +02:00
Erik Eckstein
92a17f8a01 Optimizer: extract the NamedReturnValueOptimization from CopyForwarding to a separate function pass
This allows to run the NamedReturnValueOptimization only late in the pipeline.
The optimization shouldn't be done before serialization, because it might prevent predictable memory optimizations in the caller after inlining.
2023-05-11 08:11:44 +02:00
Erik Eckstein
fa1ecff143 Swift Optimizer: rewrite the MemBehaviorDumper test pass in swift. 2023-05-09 08:25:09 +02:00
Erik Eckstein
df7c71badb Optimizer: remove the now obsolete GlobalOpt pass 2023-05-08 21:23:36 +02:00
Erik Eckstein
960ca70def Swift Optimizer: add the module pass ReadOnlyGlobalVariables
It marks global `var` variables as `let` if they are never written.
2023-05-08 21:23:36 +02:00
Erik Eckstein
6d6b94e430 Swift Optimizer: add the InitializeStaticGlobals function pass
It converts a lazily initialized global to a statically initialized global variable.

When this pass runs on a global initializer `[global_init_once_fn]` it tries to create a static initializer for the initialized global.
```
  sil [global_init_once_fn] @globalinit {
    alloc_global @the_global
    %a = global_addr @the_global
    %i = some_const_initializer_insts
    store %i to %a
  }
```

The pass creates a static initializer for the global:
```
  sil_global @the_global = {
    %initval = some_const_initializer_insts
  }
```

and removes the allocation and store instructions from the initializer function:
```
  sil [global_init_once_fn] @globalinit {
    %a = global_addr @the_global
    %i = some_const_initializer_insts
  }
```

The initializer then becomes a side-effect free function which let's the builtin-simplification remove the `builtin "once"` which calls the initializer.
2023-05-08 21:23:36 +02:00
Erik Eckstein
ee2924fd27 Inliner: don't distinguish between the "mid-level" and "late" inliner
Now that we handle inlined global initializers in LICM, CSE and the StringOptimization, we don't need to have a separate mid-level inliner pass, which treats global accessors specially.
2023-05-08 21:23:36 +02:00
Michael Gottesman
224674cad1 [move-only] Ensure that we treat captured escaping closure arguments as such even if the closure doesn't actually escape
Specifically, we already have the appropriate semantics for arguments captured
by escaping closures but in certain cases allocbox to stack is able to prove
that the closure doesn’t actually escape. This results in the capture being
converted into a non-escaping SIL form. This then causes the move checker to
emit the wrong kind of error.

The solution is to create an early allocbox to stack that doesn’t promote move
only types in boxes from heap -> stack if it is captured by an escaping closure
but does everything else normally. Then once the move checking is completed, we
run alloc box to stack an additional time to ensure that we keep the guarantee
that heap -> stack is performed in those cases.

rdar://108905586
2023-05-04 12:25:19 -07:00
Nate Chandler
1a7c4a4d12 [NFC] Renamed DestroyAddrHoisting.
It was previously named SSADestroyHoisting which is rather misleading
considering that it deals with addresses and hoists destroy_addrs.
2023-03-27 14:15:03 -07:00