Commit Graph

658 Commits

Author SHA1 Message Date
Nate Chandler
ed1b3f2968 [SILOpt] Put DestroyHoisting behind flag.
Tested that import enum case is optimized appropriately without the
classic DestroyHoisting pass.
2022-02-18 10:10:18 -08:00
Nate Chandler
014876d4ca [SILOpt] Enabled SSADestroyHoisting.
The new destroy_addr hoisting respects lexical lifetimes.  It will
replace all other destory hoisting.
2022-02-10 20:17:57 -08:00
Erik Eckstein
8fe36eedd8 SILPassManager: improve bisecting for debugging the optimizer
* Add the possibility to bisect the individual transforms of SILCombine and SimplifyCFG.
   To do so, the `-sil-opt-pass-count` option now accepts the format `<n>.<m>`, where `m` is the sub-pass number.
   The sub-pass number limits the number of individual transforms in SILCombine or SimplifyCFG.

* Add an option `-sil-print-last` to print the SIL of the currently optimized function before and after the last pass, which is specified with `-sil-opt-pass-count`.
2022-02-09 13:25:30 +01:00
Kavon Farvardin
4f28b87de9 basic implementation of flow-isolation for SE-327
Flow-isolation is a diagnostic SIL pass that finds
unsafe accesses to properties in initializers and
deinitializers that cannot gain isolation to otherwise
protect those accesses from concurrent modifications.
See SE-327 for more details about how and why it exists.

This commit includes changes and features like:

- The removal of the escaping-use restriction
- Flow-isolation that works properly with `defer` statements
- Flow-isolation with an emphasis on helpful diagnostics.

It also includes known issues like:

- Local / nonescaping functions are not analyzed by
  flow-isolation, despite it being technically possible.
  The main challenge in supporting it efficiently is that
  such functions do not have a single exit-point, like
  a `defer`. In particular, arbitrary functions can throw
  so there are points where nonisolation should _not_ flow
  out of the function at a call-site in the initializer, etc.

- The implementation of the flow-isolation pass is not
  particularly memory efficient; it relies on BitDataflow
  even though the particular flow problem is simple.
  So, a more efficient implementation would be specialized for
  this particular problem, etc.

There are also some changes to the Swift language itself: defer
will respect its context when deciding its property access kind.

Previously, a defer in an initializer would always access a stored
property through its accessor methods, instead of doing so directly
like its enclosing function might. This inconsistency is unfortunate,
so for Swift 6+ we make this consistent. For Swift 5, only a defer
in a function that is a member of the following kinds of types
will gain this consistency:

- an actor type
- any nominal type that is actor-isolated, excluding UnsafeGlobalActor.

These types are still rather new, so there is much less of a chance of
breaking expected behaviors around defer. In particular, the danger is
that users are relying on the behavior of defer triggering a property
observer within an init or deinit, when it would not be triggering it
without the defer.
2022-02-02 13:31:14 -07:00
Erik Eckstein
79e9b9a088 SIL optimizer: add the ability to disable swift instruction passes with the -sil-disable-pass option. 2022-02-01 18:30:05 +01:00
Michael Gottesman
0032b3818c [move-only] Move move function kills values checking and move only value lifetime checking before diagnostic constant prop.
This is part of a larger piece of work that is going to introduce the ability in
SIL for us to wrap trivial values in a move only type wrapper and thus perform
ownership based diagnostics upon those trivial values. This is being done now so
that we can perform SSA based ownership diagnostics on trivial types. This will
allow us to eventually be able to do things like no escape analysis on
UnsafePointer.

That all sounds great, but also we must consider how this effects the rest of
the optimizer. For instance, what if we want to have a no escape integer and
have overflow checks used upon it! To ensure that we can do this, the authors
realized that we did not need to persist the ownership information so late in
that part of the pipeline and we can just do the ownership checking earlier than
constant propagation and then lower. This is safe to do since the rest of the
optimizer will not introduce escapes of a pointer or extra copies unlike if the
underlying non move only type variant was also non-trivial.

With that in hand, this PR moves these two move only passes earlier than
constant propagation for this purpose. The reason they were put in the current
position vs earlier is that I wanted them to run after predictable dead
allocation elimination since it cleaned up the SIL I was reading as I designed
it. There isn't any reason that they can't run earlier. Once I bring in the new
SIL move only type wrapper, after these run, the trivial type lowering will then
run.
2022-01-28 14:02:47 -08:00
Erik Eckstein
603e837a8f Swift optimizations: make isSwift51RuntimeAvailable sensitive to the resilience domain of the function. 2022-01-27 13:20:38 +01:00
Max Desiatov
b964dba177 libswift: implement ReleaseDevirtualizer in Swift 2022-01-19 18:51:19 +00:00
Max Desiatov
7d961001d3 libswift: bridge more functions from SILBuilder 2022-01-19 18:51:18 +00:00
Max Desiatov
0b34baa80f libswift: bridge RCIdentityAnalysis code 2022-01-19 18:51:17 +00:00
Erik Eckstein
e028239cc2 Swift Optimizer: add AllocRefInstBase.setIsStackAllocatable 2022-01-12 15:47:16 +01:00
Erik Eckstein
e152b2cb4a Swift Optimizer: add the fixStackNesting utility in PassContext
This bridges to the StackNesting utility in C++
2022-01-12 15:47:16 +01:00
Erik Eckstein
a24b17a333 Swift Optimizer: add the dominator and post-dominator tree analysis 2022-01-12 15:47:16 +01:00
Erik Eckstein
4440beb555 Swift Optimizer: add bridging to the DeadEndBlocksAnalysis 2022-01-12 15:47:16 +01:00
Erik Eckstein
40200d6544 Swift Optimizer: add BasicBlock utility data structures and rename StackList
* add `BasicBlockSet`
* add `BasicBlockWorklist`
* add `BasicBlockRange`, which defines a range of blocks from a common dominating “begin” block to a set of “end” blocks.
* add  `InstructionRange`, which is similar to `BasicBlockRange`, just on instruction level. It can be used for value lifetime analysis.
* rename `StackList` -> `Stack` and move it to `Optimizer/DataStructures`
* rename `PassContext.passContext` to `PassContext._bridged`
* add notify-functions to PassContext
2022-01-12 15:47:16 +01:00
Max Desiatov
42e6fac1ea libswift: reimplement AssumeSingleThreaded pass 2022-01-10 08:47:43 +00:00
Meghana Gupta
7386444664 Merge pull request #39776 from meg-gupta/moveb4mem2reg
Move non-transparent OME to just before Mem2Reg
2022-01-05 18:05:32 -08:00
Erik Eckstein
3522ba1521 SILOptimizer: rename LibswiftPassInvocation -> SwiftPassInvocation
And a few other small related changes:
* remove libswiftPassInvocation from SILInstructionWorklist (because it's not needed)
* replace start/finishPassRun with start/finishFunction/InstructionPassRun

NFC
2022-01-05 10:15:56 +01:00
Meghana Gupta
a52b8966c6 Move non-transparent OME to just before Mem2Reg 2021-12-23 17:42:31 -08:00
Erik Eckstein
3540c01125 rename initializeLibSwift -> InitializeSwiftModules
and some updates in comments.
2021-12-22 11:31:52 +01:00
Erik Eckstein
408cf02bc8 rework cross-module-optimization
* rename the CrossModuleSerializationSetup pass to simply CrossModuleOptimization
* remove the CMO specific serializer pass. Instead run the CrossModuleSerializationSetup pass directly before the standard serializer pass.
* correctly handle shared functions (e.g. specializations)
* refactoring
2021-12-20 11:33:02 +01:00
Nate Chandler
59ae5180ba [NFC] Used SILOption field for copy propagation.
Replaced the quad-state (of state which one was illegal) of two booleans
(EnableCopyPropagation and DisableCopyPropagation) with an enum.
2021-12-13 17:44:28 -08:00
Nate Chandler
1464c1b1ff [NFC] Renamed LexicalLifetimesOption cases.
The cases' new names more accurately reflect what behavior occurs when
SILOptions::LexicalLifetimes is assigned that case.
2021-12-10 18:36:28 -08:00
Michael Gottesman
aeceaf7d78 Merge pull request #40478 from gottesmm/pr-cfbd8a7087ef5ca3ec578da8993a686ad7a50077
[move-function] Add support for loadable vars
2021-12-09 16:19:39 -08:00
swift-ci
6a752615d6 Merge pull request #40477 from nate-chandler/lexical-lifetimes/reenable-destroy-hoisting 2021-12-09 15:42:02 -08:00
Michael Gottesman
4a9c26ff96 [move-function] Make MandatoryInlining always convert builtin.move -> mark_unresolved_move_addr.
To give a bit more information, currently the way the move function is
implemented is that:

1. SILGen emits a builtin "move" that is called within the function _move in the
   stdlib.

2. Mandatory Inlining today if the final inlined type is address only, inlines
   builtin "move" as mark_unresolved_move_addr. Otherwise, if the inlined type
   is loadable, it performs a load [take] + move [diagnostic] + store [init].

3. In the diagnostic pipeline before any mem optimizations have run, we run the
   move checker for addresses. This eliminates /all/ mark_unresolved_move_addr
   as part of emitting diagnostics. In order to make this work, we perform a
   small optimization before the checker runs that moves the
   mark_unresolved_move_addr from being on temporary alloc_stacks to the true
   base underlying address we are trying to move. This optimization is necessary
   since _move is generic and often times SILGen will emit this temporary that
   we do not want.

4. Then after we have run the guaranteed mem optimizations, we run the object
   based move checker emitting diagnostics.

This PR changes the scheme above to the following:

1. SILGen emits a builtin "move" that is called within the function _move in the
   stdlib.

2. Mandatory Inlining inlines builtin "move" as mark_unresolved_move_addr.

3. In the diagnostic pipeline before we have run any mem optimizations and
   before we have run the actual move address checker, we massage the IR as we
   do above but in a separate pass where in addition we try to match this pattern:

     ```
     %temporary = alloc_stack $LoadableType
     store %1 to [init] %temporary : $*LoadableType
     mark_unresolved_move_addr %temporary to %otherAddr : $*LoadableType
     destroy_addr %temporary : $*LoadableType
     ```

   and transform it to:

     ```
     %temporary = alloc_stack $LoadableType
     %2 = move_value [allows_diagnostics] %1 : $*LoadableType
     store %2 to [init] %temporary : $*LoadableType
     destroy_addr %temporary : $*LoadableType
     ```

   ensuring that the object move checker will handle this.

4. Then after we have run the guaranteed mem optimizations, we run the object
   based move checker emitting diagnostics.
2021-12-09 12:48:29 -08:00
Nate Chandler
59f3b37c35 [SIL] Reenable destroy hoisting under lexical lifetimes.
Until https://github.com/apple/swift/pull/40392 lands, run destroy
hoisting with lexical lifetimes.
2021-12-09 10:42:36 -08:00
Nate Chandler
fece6ef67d [SILOptimizer] Add flag to print pre-OSSA lowering.
The new Xllvm flag prints the module to stdout just before the OSSA
lowering pass runs.
2021-12-08 17:53:01 -08:00
Michael Gottesman
e3f9eaca58 [move-function] Implement the move-function checker for address only lets. 2021-12-06 12:47:31 -08:00
Michael Gottesman
72eb5e2eec [move-operator] Specify if LexicalLifetimes is enabled using an enum instead of a bool.
The reason why I am doing this is that we are going to be enabling lexical
lifetimes early in the pipeline so that I can use it for the move operator's
diagnostics.

To make it easy for passes to know whether or not they should support lexical
lifetimes, I included a query on SILOptions called
supportsLexicalLifetimes. This will return true if the pass (given the passed in
option) should insert the lexical lifetime flag. This ensures that passes that
run in both pipelines (e.x.: AllocBoxToStack) know whether or not to set the
lexical lifetime flag without having to locally reason about it.

This is just chopping off layers of a larger patch I am upstreaming.

NOTE: This is technically NFC since it leaves the default alone of not inserting
lexical lifetimes at all.
2021-11-15 13:47:22 -08:00
Michael Gottesman
d526f36dc0 [moveOnly] Add a new pass called the LexicalLifetimeEliminator that runs after we finish lexical diagnostics.
NOTE: This pass is disabled when -enable-experimental-lexical-lifetimes is
enabled.

When that flag is disabled, this removes the lexical flag from begin_borrow and
alloc_stack. This ensures that we can begin using begin_borrow [lexical] and
friends to emit diagnostics without impacting performance. I am going to be
preparing a subsequent patch that causes us to emit lexical lifetimes by
default. Due to this pass, I am not expecting any issues around perf.
2021-11-12 13:17:02 -08:00
Michael Gottesman
4aa4ac3027 [moveOnly] Add checker that validates that if a let/param copyable value has _move applied to it, the let doesn't have any later uses.
This is just an initial prototype for people to play with. It is as always
behind the -enable-experimental-move-only flag.

NOTE: In this PR I implemented this only for 'local let' like things (local
lets/params). I did not implement in this PR support for local var and haven't
done anything with class ivars or globals.

rdar://83957028
2021-11-04 17:13:29 -07:00
Erik Eckstein
c578c937c8 SILOptimizer: run the GlobalOpt pass in the mandatory pipeline.
Replace the dynamic initialization of trivial globals with statically initialized globals, even in -Onone.
This is required to be able to use global variables in performance-annotated functions.
Also, it's a small performance improvement for -Onone.
2021-10-29 22:35:57 +02:00
eeckstein
d5ae51130d Merge pull request #39902 from eeckstein/performance-annotations
First prototype of Performance Annotations
2021-10-29 07:39:41 +02:00
Michael Gottesman
e5d0f386e4 [moveOnly] Add a new experimental move only checker that checks noImplicitCopy variables.
NOTE: This is only available when the flag -enable-experimental-move-only. There
are no effects when the flag is disabled.

The way that this works is that it takes advantage of the following changes to
SILGen emission:

* When SILGen initializes a let with NoImplicitCopyAttribute, SILGen now emits
a begin_borrow [lexical] + copy + move_only. This is a pattern that we can check
and know that we are processing a move only value. When performing move
checking, we check move_only as a move only value and that it isn't consumed
multiple times.

* The first point works well for emitting all diagnostics except for
initializing an additional let var. To work around that I changed let
initialization to always bind to an owned value to a move of that owned
value. There is no semantic difference since that value is going to be consumed
by the binding operation anyways so we effectively just move the cleanup from
the original value we wanted to bind to the move. We still then actually borrow
the new let value with a begin_borrow [lexical] for the new let value. This
ensures that an initialization of a let value appears to be a consuming use to
the move only value checker while ensuring that the value has a proper
begin_borrow [lexical].

Some notes on functionality:

1. This attribute can only be applied to local 'let'.

2. "print" due to how we call it today with a vararg array is treated as a
   consuming use (unfortunately).

3. I have not added the builtin copy operator yet, but I recently added a _move
   skeleton attribute so one can end the lifetimes of these values early.

4. This supports all types that are not address only types (similar to
   _move). To support full on address only types we need opaque values.

rdar://83957088
2021-10-28 11:32:22 -07:00
Erik Eckstein
9f8b155c6c PerformanceDiagnostics: a pass to print errors for performance violations in annotated functions.
The PerformanceDiagnostics pass issues performance diagnostics for functions which are annotated with performance annotations, like @_noLocks, @_noAllocation.
This is done recursively for all functions which are called from performance-annotated functions.

rdar://83882635
2021-10-28 18:44:46 +02:00
Erik Eckstein
569a520db2 SILOptimizer: add a mandatory generic-specialization pass.
This pass is only used for functions with performance annotations (@_noLocks, @_noAllocation).
It runs in the mandatory pipeline and specializes all function calls in performance-annotated functions and functions which are called from such functions.
In addition, the pass also does some other related optimizations: devirtualization, constant-folding Builtin.canBeClass, inlining of transparent functions and memory access optimizations.
2021-10-28 18:43:14 +02:00
Nate Chandler
339ef007ed Disabled destroy hoisting for lexical lifetimes.
When -enable-experimental-lexical-lifetimes is passed, disable the
destroy hoisting pass.
2021-10-13 13:47:59 -07:00
Meghana Gupta
e63a68d715 Add a flag to disable late non-transparent OME when -enable-ossa-modules is turned off
Since we are now moving non-transparent OME down the pipeline by default, add a flag to
turn off this behaviour.

When turned on, non-transparent OME will run early, just before PerformanceSILLinker.
2021-10-12 08:54:42 -07:00
Meghana Gupta
c9d124cf82 Move OME lower for non-transparent functions
Move it just before outliner which has not yet been migrated
2021-10-12 08:54:42 -07:00
Meghana Gupta
2d7e20e599 Remove unused mode in OME 2021-10-05 14:19:58 -07:00
Meghana Gupta
a32bfb0039 Now that -enable-ossa-modules has been fixed, remove redundant -sil-enable-late-ome 2021-10-05 12:54:15 -07:00
Nate Chandler
4ff34aab31 [SIL] Enabled printing canonical module.
To print the module, use the new llvm flag -sil-print-canonical-module
which parallels the existing flag -sil-view-canonical-cfg.  When that
flag is passed, the new pass ModulePrinter is added to the diagnostic
pass pipeline after mandatory diagnostics have run.  The new pass just
prints the module to stdout.
2021-09-22 12:35:41 -07:00
Nate Chandler
11f2650bb6 [SIL] Renamed flag for viewing canonical cfg.
Previously, the flag was named -sil-view-guaranteed-cfg.  Here, it's
renamed to -sil-view-canonical-cfg.
2021-09-22 12:35:39 -07:00
Andrew Trick
e85228491d Rename AccessedStorage to AccessStorage
to be consistent with AccessPath and AccessBase.

Otherwise, the arbitrary name difference adds constant friction.
2021-09-21 23:18:24 -07:00
Meghana Gupta
d7281a1001 Enable late OME via an experimental flag (#39363) 2021-09-20 10:25:30 -07:00
Erik Eckstein
50195e7e7d libswift: fix a few bugs in StackList
Use MemoryLayout.stride instead pf MemoryLayout.size. This fixes a buffer overflow bug in case of unaligned elements.
Plus some other bug fixes for stacklists which get larger than a single slab.
Also, add the `append(contentsOf:)` method.
2021-09-20 07:56:46 +02:00
Erik Eckstein
90c71ad002 libswift: improve and simplify pass invocation
* unify FunctionPassContext and InstructionPassContext
* add a modification API: PassContext.setOperand
* automatic invalidation notifications when the SIL is modified
2021-08-23 10:21:12 +02:00
Erik Eckstein
88a74a288c StringOptimization: optimize interpolated C strings.
Optimize code like:

   puts("\(String.self)")

Optimizing string interpolation and optimizing C-strings are both done in StringOptimization.
A second run of the StringOptimization is needed in the pipeline to optimize such code, because the result of the interpolation-optimization must be cleaned up so that the C-String optimization can kick in.

Also, StringOptimization must handle struct_extract(struct(literal)), where the struct_extract may be in a called function.
To solve a phase ordering problem with inlining String semantics and inlining the `String(stringInterpolation: DefaultStringInterpolation)` constructor, we do a simple analysis of the callee. Doing this simple "interprocedural" analysis avoids relying on inlining that String constructor.

rdar://74941849
2021-07-06 16:25:03 +02:00
eeckstein
aa3ede1538 Merge pull request #38196 from eeckstein/libswift-infrastructure
libswift: add instructions and some analysis
2021-07-02 08:52:16 +02:00