Commit Graph

663 Commits

Author SHA1 Message Date
eeckstein
6bf39cccd6 Merge pull request #70787 from eeckstein/boolean-literal-folding
Mandatory optimizations: constant fold boolean literals before the DefiniteInitialization pass
2024-01-11 11:39:44 +01:00
Erik Eckstein
04a6bbbb7a docs: add documentation for the -sil-pass-count-config-file option in DebuggingTheCompiler
Also, add this option in swift-autocomplete.bash.
Unrelated: support aliased commands in swift-autocomplete.bash
2024-01-11 11:10:05 +01:00
Meghana Gupta
5d2454f3e3 Merge pull request #68180 from meg-gupta/lowerb4inline
Lower OwnershipModelEliminator to just before inlining
2024-01-10 13:53:34 -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
4aa51ffeab PassManager: add the -sil-pass-count-config-file for easier bisecting pass counts in large projects
This is useful for bisecting passes in large projects:
  1. create a config file from a full build log. E.g. with
        ```
        grep -e '-module-name' build.log  | sed -e 's/.*-module-name \([^ ]*\) .*/\1:10000000/' | sort | uniq > config.txt
        ```
  2. add the `-Xllvm -sil-pass-count-config-file config.txt` option to the project settings
  3. bisect by modifying the counts in the config file
  4. clean-rebuild after each bisecting step
2024-01-09 12:12:33 +01:00
Meghana Gupta
79cc6547a1 Lower OwnershipModelEliminator to just before inlining 2024-01-05 13:20:52 -08:00
Erik Eckstein
b3cccb3ef5 PassManager: correctly pass the CalleeCache to SILModule::verify
Fixes a compiler warning and improves verification.
2023-12-13 13:45:53 +01:00
Kuba (Brecka) Mracek
b9f7acb8b5 Merge pull request #70309 from kubamracek/embedded-dfe
[embedded] Run DeadFunctionAndGlobalElimination after MandatoryPerformanceOptimizations
2023-12-12 10:21:11 -08:00
Kuba Mracek
df638b0e63 [embedded] Add explaining comment for running DeadFunctionAndGlobalElimination in embedded Swift 2023-12-11 08:48:13 -08:00
Kuba Mracek
bb1ba7c95c [embedded] Run DeadFunctionAndGlobalElimination after MandatoryPerformanceOptimizations 2023-12-09 22:39:55 -08: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
Dario Rexin
406fe3eed2 [SILOpt] Allow pre-specializations for _Trivial of known size (#70256)
* [SILOpt] Allow pre-specializations for _Trivial of known size

rdar://119224542

This allows pre-specializations to be generated and applied for trivial types of a shared size.
2023-12-08 19:42:49 -08:00
Erik Eckstein
0897d8a720 MemoryLifetimeVerifier: use CalleeCache instead of AliasAnalysis
To verify if a function may read from an indirect argument, don't use AliasAnalysis.
Instead use the CalleeCache to get the list of callees of an apply instruction.
Then use a simple call-back into the swift Function to check if a callee has any relevant memory effect set.

This avoids a dependency from SIL to the Optimizer.
It fixes a linker error when building some unit tests in debug.
2023-12-01 19:20:18 +01:00
Erik Eckstein
57b345035e SILPassManager: make -sil-print-function more convenient
Also match a function name if the specified function doesn't contain the $-prefix.
This is convenient when invoking the compiler from a shell, where a `$`-function name has to be single-quoted.
2023-11-30 14:24:36 +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
Hamish Knight
5d99fe63e9 Rename get() -> unbridged() on bridging wrappers 2023-10-31 11:06:39 +00:00
Hamish Knight
5853304da1 Remove BridgingUtils.h headers
These are now empty, and it seems like generally
we ought to prefer putting the bridging logic on
the BridgedXXX wrappers.
2023-10-30 23:50:00 +00:00
Michael Gottesman
0bad8f9b67 [region-isolation] Rename SendNonSendable.cpp -> TransferNonSendable.cpp. 2023-10-26 12:01:44 -07:00
Erik Eckstein
3a0f635830 Move the optimizer bridging back to PassManager.cpp
Putting it into a separate source file OptimizerBridging.cpp caused linker errors for sourcekitd-test in sourcekit tests on linux.
2023-10-09 10:10:20 +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
Kuba Mracek
28a3d583c8 [embedded] Re-enable ReleaseDevirtualizer and teach it to look for specialized destructors 2023-09-29 16:28:59 -07:00
Kuba Mracek
b2e43f325a [embedded] Avoid a late VTableSpecializer pass in favor of disabling -O/-Osize passes that break embedded Swift assumptions 2023-09-28 10:33:00 -07:00
Kuba Mracek
16f6ca907f [embedded] Fix a compiler crash when using generic classes in -O/-Osize 2023-09-28 09:55:17 -07:00
Kuba Mracek
ff1d177a7c [embedded] Move the DFE pass to getOnonePassPipeline 2023-09-27 12:46:56 -07:00
Kuba Mracek
8b1aca9ff5 [embedded] Only run the late DeadFunctionElimination if embedded mode is on 2023-09-26 22:55:53 -07:00
Kuba Mracek
b049319487 [embedded] Remove unspecialized functions before running IRGen 2023-09-26 22:25:37 -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
Kuba Mracek
89df0426e0 [embedded] Specialize vtables when processing instructions, not bulk 2023-09-20 09:41:09 -07:00
Kuba Mracek
03f927eca1 [embedded] Perform VTable specialization iteratively as part of MandatoryPerformanceOptimizations 2023-09-20 09:41:09 -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
56085205d8 [embedded] specialize generic classes based on MetatypeInst too, fix lazy emit assert 2023-09-12 21:24:14 -07: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
25eb997a28 [embedded] Add basics of module serialization, importing and validation in embedded Swift.
- Add a flag to the serialized module (IsEmbeddedSwiftModule)
- Check on import that the mode matches (don't allow importing non-embedded module in embedded mode and vice versa)
- Drop TBD support, it's not expected to work in embedded Swift for now
- Drop auto-linking backdeploy libraries, it's not expected to backdeploy embedded Swift for now
- Drop prespecializations, not expected to work in embedded Swift for now
- Use CMO to serialize everything when emitting an embedded Swift module
- Change SILLinker to deserialize/import everything when importing an embedded Swift module
- Add an IR test for importing modules
- Add a deserialization validation test
2023-09-06 20:06:36 -07:00
Erik Eckstein
8223b3e210 MandatoryPerformanceOptimizations: fix some problems with inilning
* don't inline functions if it's not possible - by checking `SILInliner::canInlineApplySite`
* fix stack nesting after inlining a `begin_apply`
2023-08-10 15:01:47 +02:00
Meghana Gupta
848466acbe Ensure no module transform is added in the function pipeline 2023-08-08 11:04:46 -07:00
Kuba Mracek
ae6129cf69 Cache the IRGenModule even between SIL passes 2023-07-31 11:57:28 -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
2e9de24e2a Swift Optimizer: add the SSAUpdater utility 2023-07-21 07:19:12 +02: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
Evan Wilde
f3ff561c6f [NFC] add llvm namespace to Optional and None
This is phase-1 of switching from llvm::Optional to std::optional in the
next rebranch. llvm::Optional was removed from upstream LLVM, so we need
to migrate off rather soon. On Darwin, std::optional, and llvm::Optional
have the same layout, so we don't need to be as concerned about ABI
beyond the name mangling. `llvm::Optional` is only returned from one
function in
```
getStandardTypeSubst(StringRef TypeName,
                     bool allowConcurrencyManglings);
```
It's the return value, so it should not impact the mangling of the
function, and the layout is the same as `std::optional`, so it should be
mostly okay. This function doesn't appear to have users, and the ABI was
already broken 2 years ago for concurrency and no one seemed to notice
so this should be "okay".

I'm doing the migration incrementally so that folks working on main can
cherry-pick back to the release/5.9 branch. Once 5.9 is done and locked
away, then we can go through and finish the replacement. Since `None`
and `Optional` show up in contexts where they are not `llvm::None` and
`llvm::Optional`, I'm preparing the work now by going through and
removing the namespace unwrapping and making the `llvm` namespace
explicit. This should make it fairly mechanical to go through and
replace llvm::Optional with std::optional, and llvm::None with
std::nullopt. It's also a change that can be brought onto the
release/5.9 with minimal impact. This should be an NFC change.
2023-06-27 09:03:52 -07:00
Erik Eckstein
0bf643d0c6 Optimizer: add an additional DeadObjectElimination at the end of the pipeline
The last dead-store-elimination pass can expose opportunities for dead object elimination.

rdar://110846405
2023-06-16 18:14:45 +02:00
Andrew Trick
62b0899500 [move-only] Disable move-only devirtualization.
It is not needed for correctness and hides most of the deinit related
optimizer bugs.
2023-06-06 09:17:53 -07: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
585395b67c Swift Optimizer: add Context.lookupStdlibFunction 2023-05-22 15:34:26 +02:00
Erik Eckstein
38de5b1ab5 Swift SIL/Optimizer: implement cloning of static init values of globals in Swift
* add the StaticInitCloner utility
* remove bridging of `copyStaticInitializer` and `createStaticInitializer`
* add `Context.mangleOutlinedVariable` and `Context.createGlobalVariable`
2023-05-22 15:34:26 +02:00
Erik Eckstein
b707b5a595 Swift SIL: improve the Builder
* add new create-functions for instructions
* allow the Builder to build static initializer instructions for global variables
* some refactoring to simplify the implementation
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