Commit Graph

973 Commits

Author SHA1 Message Date
Andrew Trick
cb1ed89c9a [NFC] Add support for a mandatory-copy-propagation pass.
It is currently disabled so this commit is NFC.

MandatoryCopyPropagation canonicalizes all all OSSA lifetimes with
either CopyValue or DestroyValue operations. While regular
CopyPropagation only canonicalizes lifetimes with copies. This ensures
that more lifetime program bugs are found in debug builds. Eventually,
regular CopyPropagation will also canonicalize all lifetimes, but for
now, we don't want to expose optimized code to more behavior change
than necessary.

Add frontend flags for developers to easily control copy propagation:

-enable-copy-propagation: enables whatever form of copy propagation
 the current pipeline runs (mandatory-copy-propagation at -Onone,
 regular copy-propation at -O).

-disable-copy-propagation: similarly disables any form of copy
 propagation in the current pipelien.

To control a specific variant of the passes, use
   -Xllvm -disable-pass=mandatory-copy-propagation
or -Xllvm -disable-pass=copy-propagation instead.

The meaning of these flags will stay the same as we adjust the
defaults. Soon mandatory-copy-propagation will be enabled by
default. There are two reasons to do this, both related to predictable
behavior across Debug and Release builds.

1. Shortening object lifetimes can cause observable changes in program
   behavior in the presense of weak/unowned reference and
   deinitializer side effects.

2. Programmers need to know reliably whether a given code pattern will
   copy the storage for copy-on-write types (Array, Set). Eliminating
   the "unexpected" copies the same way at -Onone and -O both makes
   debugging tractable and provides assurance that the code isn't
   relying on the luck of the optimizer in a particular compiler
   release.
2021-03-02 22:19:47 -08:00
swift-ci
44ed14e646 Merge pull request #36138 from atrick/comment-deleteinst 2021-02-24 14:25:02 -08:00
Andrew Trick
717e1a1005 Comment the eliminateDeadInstruction and related APIs.
When the underlying utility was changed for OSSA, it changed the
semantics of the callback, which breaks the way I've always used a
deletion callback to update iterators.

/// \p callback is called on each deleted instruction before deleting any
/// instructions. This way, the SIL is valid in the callback. However, the
/// callback cannot be used to update instruction iterators since other
/// instructions to be deleted remain in the instruction list.
2021-02-24 11:23:31 -08:00
Andrew Trick
ba9f52071b OSSA: simplify-cfg support for trivial block arguments.
Enable most simplify-cfg optimizations as long as the block arguments
have trivial types. Enable most simplify CFG unit tests cases.

This massively reduces the size of the CFG during OSSA passes.

Test cases that aren't supported in OSSA yet have been moved to a
separate test file for disabled OSSA tests,

Full simplify-cfg support is currently blocked on OSSA utilities which
I haven't checked in yet.
2021-02-23 22:47:59 -08:00
Andrew Trick
dd525d02e7 Add requiresOSSACleanup API. 2021-02-23 22:47:59 -08:00
Andrew Trick
0c09d42447 Comment OSSA APIs makeNewValueAvailable & endLifetimeAtLeakingBlocks 2021-02-23 16:54:37 -08:00
Andrew Trick
d50f220018 Add a makeUserIteratorRange API
To allow a value's use-list to be pased into an LLVM API that expects
an llvm::iterator_range<SILInstruction *>.
2021-02-23 16:54:37 -08:00
Erik Eckstein
a17f8c2f3f SILOptimizer: add a diagnostics pass to warn about lifetime issues with weak references.
The DiagnoseLifetimeIssuesPass pass prints a warning if an object is stored to a weak property (or is weakly captured) and destroyed before the property (or captured reference) is ever used again.
This can happen if the programmer relies on the lexical scope to keep an object alive, but copy-propagation can shrink the object's lifetime to its last use.
For example:

  func test() {
    let k = Klass()
      // k is deallocated immediately after the closure capture (a store_weak).
      functionWithClosure({ [weak k] in
                            // crash!
                            k!.foo()
                          })
    }

Unfortunately this pass can only catch simple cases, but it's better than nothing.

rdar://73910632
2021-02-15 11:11:35 +01:00
swift-ci
d33c79edaf Merge pull request #35847 from rxwei/72666310-mangle-ad-thunks 2021-02-13 05:07:32 -08:00
Richard Wei
f9ddecf459 [AutoDiff] Mangle linear map self-reordering thunks and subset parameters thunks.
Add the following new mangling rules.
```
global ::= from-type to-type 'TJO' AUTODIFF-FUNCTION-KIND // autodiff self-reordering reabstraction thunk
global ::= from-type 'TJS' AUTODIFF-FUNCTION-KIND INDEX-SUBSET 'p' INDEX-SUBSET 'r' INDEX-SUBSET 'P' // autodiff linear map subset parameters thunk
global ::= global to-type 'TJS' AUTODIFF-FUNCTION-KIND INDEX-SUBSET 'p' INDEX-SUBSET 'r' INDEX-SUBSET 'P' // autodiff derivative function subset parameters thunk
```

Example:
```console
$s13TangentVector16_Differentiation14DifferentiablePQzAaDQy_SdAFIegnnnr_TJSdSSSpSrSUSP ---> autodiff subset parameters thunk for differential from @escaping @callee_guaranteed (@in_guaranteed A._Differentiation.Differentiable.TangentVector, @in_guaranteed B._Differentiation.Differentiable.TangentVector, @in_guaranteed Swift.Double) -> (@out B._Differentiation.Differentiable.TangentVector) with respect to parameters {0, 1, 2} and results {0} to parameters {0, 2}
$sS2f8mangling3FooV13TangentVectorVIegydd_SfAESfIegydd_TJOp ---> autodiff self-reordering reabstraction thunk for pullback from @escaping @callee_guaranteed (@unowned Swift.Float) -> (@unowned Swift.Float, @unowned mangling.Foo.TangentVector) to @escaping @callee_guaranteed (@unowned Swift.Float) -> (@unowned mangling.Foo.TangentVector, @unowned Swift.Float)
```

Resolves rdar://72666310 / SR-13508.

Also fix a bug in `AutoDiffFunction` mangling where the original may be a global that contains more than 1 node (rdar://74151229 / SR-14106).
2021-02-13 01:38:35 -08:00
Erik Eckstein
fe10f98cf0 SIL: rename the SILBitfield.h header file to BasicBlockBits.h
NFC
2021-02-12 11:15:55 +01:00
Erik Eckstein
65b03f9815 SILOptimizer: prevent illegal load forwarding of function references in global variables.
We cannot replace a load from a global let-variable with a function_ref, if the referenced function would violate the resilience rules.
That means if a non-public function_ref would be inlined into a function which is serialized.
2021-02-09 19:56:43 +01:00
Meghana Gupta
ecb5d65d6d Fix lifetime of intermediate phis created by RLE
We were adjusting the lifetime of the final phi created by the
SSAUpdater. The intermediate phi's lifetime needs to be adjusted as
well.
2021-02-02 21:54:09 -08:00
Meghana Gupta
f5d47c3ba4 Fix debug location while creating instructions in RLE
The insert point may not have RegularKind debug location.
So, use an auto-generated RegularKind debug location.
2021-02-02 21:40:48 -08:00
Michael Gottesman
b16f340595 Merge pull request #35684 from eeckstein/fix-find-jpds
SIL: fix problems in findJointPostDominatingSet and some refactoring
2021-02-02 14:18:22 -08:00
Erik Eckstein
d33ea9f350 SIL: remove the JointPostDominanceSetComputer helper struct.
Instead make `findJointPostDominatingSet` a stand-alone function.
There is no need to keep the temporary SmallVector alive across multiple calls of findJointPostDominatingSet for the purpose of re-using malloc'ed memory. The worklist usually contains way less elements than its small size.
2021-02-02 10:20:35 +01:00
Andrew Trick
b3e76ae8e5 Add PhiOperand and PhiValue types.
This is necessitated by the SILArgument representation. It has the
tragic property that adding unrelated phis invalidates existing
phis. Therefore, the optimizer can't do book-keeping of phi values by
refering directly to SILValues or Operands. Instead, it must only
refer to SILBasicBlocks and argument indices.
2021-02-01 20:15:05 -08:00
Michael Gottesman
2fad943df0 [sil-combine] Update convert_function canonicalization for ownership.
Some notes:

1. I moved the identity round-trip case to InstSimplify since that is where
   optimizations like that are.

2. I did not update in this commit the code that eliminates convert_function
   when it is only destroyed. In a subsequent commit I am going to implement
   that in a general way and apply it to all forwarding instructions.

3. I implemented eliminating convert_function with ownership only uses in a
   utility so that I can reuse it for other similar optimizations in SILCombine.
2021-01-28 12:10:16 -08:00
Erik Eckstein
011358edd6 SIL: let SingleValueInstruction only inherit from a single SILNode.
This removes the ambiguity when casting from a SingleValueInstruction to SILNode, which makes the code simpler. E.g. the "isRepresentativeSILNode" logic is not needed anymore.
Also, it reduces the size of the most used instruction class - SingleValueInstruction - by one pointer.

Conceptually, SILInstruction is still a SILNode. But implementation-wise SILNode is not a base class of SILInstruction anymore.
Only the two sub-classes of SILInstruction - SingleValueInstruction and NonSingleValueInstruction - inherit from SILNode. SingleValueInstruction's SILNode is embedded into a ValueBase and its relative offset in the class is the same as in NonSingleValueInstruction (see SILNodeOffsetChecker).
This makes it possible to cast from a SILInstruction to a SILNode without knowing which SILInstruction sub-class it is.
Casting to SILNode cannot be done implicitly, but only with an LLVM `cast` or with SILInstruction::asSILNode(). But this is a rare case anyway.
2021-01-27 16:40:15 +01:00
Erik Eckstein
e98c527319 SILOptimizer: remove the unused findApplyFromDevirtualizedResult utility function 2021-01-27 16:40:14 +01:00
Meghana Gupta
3e330abf44 Fix use-after-free in ownership rauw
Instead of saving BorrowingOperand on the context save the SILBasicBlock
and index of the terminator operand.
This avoids the use-after-free in eliminateReborrowsOfRecursiveBorrows.
Previously, eliminateReborrowsOfRecursiveBorrows called helper
insertOwnedBaseValueAlongBranchEdge, which can delete a branch
instruction (reborrow) that could have been cached in
recursiveReborrows.
2021-01-26 20:21:16 -08:00
Michael Gottesman
fdbe872889 Merge pull request #35601 from gottesmm/ossa-sil-combine-3
[sil-combine] More ownership updates
2021-01-26 17:43:26 -08:00
Eric Miotto
8e7f9c9cbd Revert "SIL: let SingleValueInstruction only inherit from a single SILNode." 2021-01-26 10:02:24 -08:00
Michael Gottesman
44fdc746ea [ownership] Allow the user of OwnershipRAUWHelper to insert forwarding transforms of the new value at oldValue in between checking and RAUWing. 2021-01-25 17:18:31 -08:00
Erik Eckstein
ff1991740a SIL: let SingleValueInstruction only inherit from a single SILNode.
This removes the ambiguity when casting from a SingleValueInstruction to SILNode, which makes the code simpler. E.g. the "isRepresentativeSILNode" logic is not needed anymore.
Also, it reduces the size of the most used instruction class - SingleValueInstruction - by one pointer.

Conceptually, SILInstruction is still a SILNode. But implementation-wise SILNode is not a base class of SILInstruction anymore.
Only the two sub-classes of SILInstruction - SingleValueInstruction and NonSingleValueInstruction - inherit from SILNode. SingleValueInstruction's SILNode is embedded into a ValueBase and its relative offset in the class is the same as in NonSingleValueInstruction (see SILNodeOffsetChecker).
This makes it possible to cast from a SILInstruction to a SILNode without knowing which SILInstruction sub-class it is.
Casting to SILNode cannot be done implicitly, but only with an LLVM `cast` or with SILInstruction::asSILNode(). But this is a rare case anyway.
2021-01-25 09:30:04 +01:00
Erik Eckstein
5ad332f6cd SILOptimizer: remove the unused findApplyFromDevirtualizedResult utility function 2021-01-25 09:30:04 +01:00
Andrew Trick
a2fac95f9f OSSA ownership optimization RAUW utility fixes.
Verify that the OwnershipRAUWUtility always preserves the original
borrow scope by exhaustively switching over OperandOwnership.

And related cleanup.
2021-01-23 18:15:58 -08:00
eeckstein
a0884baa3c Merge pull request #35521 from eeckstein/sil-bitfields
SIL: add a utility which can manage per-block bitfields and flags efficiently.
2021-01-22 08:39:13 +01:00
Meghana Gupta
845e63f901 Support ownershipKind in SILSSAUpdater 2021-01-21 16:27:50 -08:00
Erik Eckstein
3e8612b0d3 SILOptimizer: use the BasicBlockFlag utility in ValueLifetimeAnalysis 2021-01-21 21:31:41 +01:00
Erik Eckstein
763bfed6a4 SILOptimizer: use the BasicBlockFlag utility in ReachableBlocks 2021-01-21 21:31:41 +01:00
Michael Gottesman
2243ffefa4 Merge pull request #35461 from gottesmm/ossa-interior-ptr-fixup
[ownership] Implement Interior Pointer handling API for RAUWing addresses
2021-01-20 09:53:48 -08:00
Erik Eckstein
cf17fd4df4 SILOptimizer: Use BasicBlockData in the StackNesting utility 2021-01-20 16:09:01 +01:00
Andrew Trick
f509d67e7c Fix -canonical-ossa-rewrite-borrows but leave it disabled.
Add support for interleaved borrow scopes:

%b1 = begin_borrow %a
%c = copy
%b2 = begin_borrow %b1
end_borrow %b1
use %c
end_borrow %b2

Will be transformed to:

%c = copy %a
%b1 = begin_borrow %a
%b2 = begin_borrow %b1
end_borrow %b1
use %c
end_borrow %b2

This was the original intention but the implementation was incomplete.

This option can be enabled as soon as we need it for performance.

The intention is also to handle multi-block borrows, but that hasn't
been implemented.
2021-01-19 19:07:54 -08:00
Meghana Gupta
e5a6de6c01 Merge pull request #34996 from meg-gupta/ossarle
Enable RLE on OSSA
2021-01-19 14:10:42 -08:00
Nate Chandler
d2e5fa7f42 Tweaked comment.
Matched the comment on the definition of NonLocalAccessBlocks::compute()
to that on the declaration.
2021-01-19 09:34:15 -08:00
Meghana Gupta
66ef200105 Enable RLE on OSSA 2021-01-17 23:39:03 -08:00
Michael Gottesman
73ba521e56 [ownership] Add a new API to OwnershipFixupContext::replaceAllAddressUsesFixingInteriorPointerOwnership.
In OSSA, we enforce that addresses from interior pointer instructions are scoped
within a borrow scope. This means that it is invalid to use such an address
outside of its parent borrow scope and as a result one can not just RAUW an
address value by a dominating address value since the latter may be invalid at
the former. I foresee that I am going to have to solve this problem and so I
decided to write this API to handle the vast majority of cases.

The way this API works is that it:

1. Computes an access path with base for the new value. If we do not have a base
value and a valid access path with root, we bail.

2. Then we check if our base value is the result of an interior pointer
instruction. If it isn't, we are immediately done and can RAUW without further
delay.

3. If we do have an interior pointer instruction, we see if the immediate
guaranteed value we projected from has a single borrow introducer value. If not,
we bail. I think this is reasonable since with time, all guaranteed values will
always only have a single borrow introducing value (once struct, tuple,
destructure_struct, destructure_tuple become reborrows).

4. Then we gather up all inner uses of our access path. If for some reason that
fails, we bail.

5. Then we see if all of those uses are within our borrow scope. If so, we can
RAUW without any further worry.

6. Otherwise, we perform a copy+borrow of our interior pointer's operand value
at the interior pointer, create a copy of the interior pointer instruction upon
this new borrow and then RAUW oldValue with that instead. By construction all
uses of oldValue will be within this new interior pointer scope.
2021-01-17 20:08:24 -08:00
Michael Gottesman
fe4c345d0d [ownership] Make OwnershipFixupContext a dump context struct and instead put its RAUW functionality on OwnershipRAUWHelper.
The reason why I am doing this is that I am building up more utilities based on
passing around this struct of context that do not want it for RAUWing
purposes. So it makes sense on a helper (OwnershipRAUWHelper) that composes with
its state.

Just a refactor, should be NFC.
2021-01-17 20:08:24 -08:00
Michael Gottesman
aa38be6d98 [inst-simplify] Hide simplifyInstruction in favor of using simplifyAndReplaceAllSimplifiedUsesAndErase.
Currently all of these places in the code base perform simplifyInstruction and
then a replaceAllSimplifiedUsesAndErase(...). This is a bad pattern since:

1. simplifyInstruction assumes its result will be passed to
   replaceAllSimplifiedUsesAndErase. So by leaving these as separate things, we
   allow for users to pointlessly make this mistake.

2. I am going to implement in a subsequent commit a utility that lifetime
   extends interior pointer bases when replacing an address with an interior
   pointer derived address. To do this efficiently, I want to reuse state I
   compute during simplifyInstruction during the actual RAUW meaning that if the
   two operations are split, that is difficult without extending the API. So by
   removing this, I can make the transform and eliminate mistakes at the same
   time.
2021-01-17 20:08:24 -08:00
Andrew Trick
1ba3066950 CanonicalOSSALifetime: Add support for overlapping access scopes.
Access scopes for enforcing exclusivity are currently the only
exception to our ability to canonicalize OSSA lifetime purely based on
the SSA value's known uses. This is because access scopes have
semantics relative to object deinitializers.

In general, deinitializers are asynchronous with respect to code that
is unrelated to the object's uses. Ignoring exclusivity, the optimizer
may always destroy objects as early as it wants, as long as the object
won't be used again. The optimizer may also extend the lifetime
(although in the future this lifetime extension should be limited by
"synchronization points").

The optimizer's freedom is however limited by exclusivity
enforcement. Optimization may never introduce new exclusivity
violations. Destroying an object within an access scope is an
exclusivity violation if the deinitializer accesses the same variable.

To handle this, OSSA canonicalization must detect access scopes that
overlap with the end of the pruned extended lifetime. Essentially:

    %def
    begin_access // access scope unrelated to def
    use %def     // pruned liveness ends here
    end_access
    destroy %def

Support for access scopes composes cleanly with the existing algorithm
without adding significant cost in the usual case. Overlapping access
scopes are unusual. A single CFG walk within the original extended
lifetime is normally sufficient. Only the blocks that are not already
LiveOut in the pruned liveness need to be visited. During this walk,
local overlapping access are detected by scanning for end_access
instructions after the last use point. Global overlapping accesses are
detected by checking NonLocalAccessBlockAnalysis. This avoids scanning
instructions in the common case. NonLocalAccessBlockAnalysis is a
trivial analysis that caches the rare occurence of nonlocal access
scopes. The analysis itself is a single linear scan over the
instruction stream. This analysis can be preserved across most
transformations and I expect it to be used to speed up other
optimizations related to access marker.

When an overlapping access is detected, pruned liveness is simply
extended to include the end_access as a new use point. Extending the
lifetime is iterative, but with each iteration, blocks that are now
marked LiveOut no longer need to be visited. Furthermore, interleaved
accessed scopes are not expected to happen in practice.
2021-01-15 19:48:33 -08:00
eeckstein
d121d7d55f Merge pull request #35428 from eeckstein/fix-blocklist-api
SIL: move all the block-list modifying APIs to SILFunction.
2021-01-15 10:56:10 +01:00
Michael Gottesman
2b73378ddb [sil] Improve comment on swift::replaceSingleUse. 2021-01-14 11:49:35 -08:00
Erik Eckstein
b7351780f7 SIL: move all the block-list modifying APIs to SILFunction.
... and remove SILFunction::getBlocks().

It's just a cleanup, NFC.
2021-01-14 17:35:31 +01:00
Michael Gottesman
279d058bfe [sil-combine] Canonicalize owned forwarding insts without non-debug non-consuming uses by sinking to their uses.
There are a bunch of optimizations in SILCombine where we try to fold an
ownership forwarding instruction A into another ownership forwarding instruction
B without deleting A. Consider the upcasts in the example below:

```
  %0 = upcast %x : $X->Y
  %1 = upcast %0 : $Y->Z
```

These sorts of optimizations fold the first instruction into the second like so:

```
  %0 = upcast %x : $X->Y
  %1 = upcast %x : $X->Z
```

This creates a problem when we are dealing with owned values since we have just
introduced two consumes for %x. To work around this, we have two options:

1. Introduce extra copies.

2. We recognize the situations where we can guarantee that we can delete the
   first upcast.

The first choice I believe is not a choice since breaking a forwarding chain of
ownership in favor of extra copies is a less canonical form. That leaves us with
the second form. What are the necessary/sufficient conditions for deleting the
first upcast. Simply it is that the upcast cannot have any non-debug,
non-consuming uses! In such a case, we know that along all paths through the
program the value has exactly one non-debug use, one of its consuming uses. If
when optimizing upcasts we could recognize that pattern, duplicate the inst
along paths not through our 2nd upcast and thus delete the original upcast
fixing the ownership error!

While this is all nice and good there is a problem with this: it doesn't
scale. As I was writing a few optimizations like this I began to note that I had
to write different versions of this same helper for many of the visitors (they
generally varied by how many forwarding instructions they looked through).

As I pondered the above, I chatted a bit with @atrick and during our
conversation, we both realized that it is much easier to solve this problem in
one block and that the condition above would allow us to sink these instructiosn
into the same block and thus if we could check for this condition and
canonicialize the IR to sink these instructions before we visiting, we could use
a single helper to handle all of these cases.
2021-01-13 10:43:41 -08:00
Michael Gottesman
29806a3849 [ownership] Change canFixUpOwnershipForRAUW so that 'oldValue' is a SILValue not a SingleValueInstruction
The only operational change here is that I needed to be able to grab the module
from the SILValue so I could see if we were in Raw SIL or not. I realized the
only case where we could not get the module is from SILUndef and at this point
in the code we know we are going to bail already for SILUndef. This is because
we already know our new value doesn't have OwnershipKind::None and we don't
replace OwnershipKind::None things with non-OwnershipKind::None things since I
haven't implemented support for that corner case yet (but will with time).

Once I realized the previous paragraph, I was able to add support without issue.
2021-01-13 10:43:41 -08:00
Michael Gottesman
1e4d3d2e97 [sil-inst-opt] Add a new utility swift::replaceSingleUse.
Given an Operand *op, this API executes op->set(newSILValue) except that:

1. If the user of op is an end scope, this API no-opts. This API is only used in
   contexts where we are rewriting uses and are not interesting in end scope
   instructions since we are moving uses from one scope to another scope.

2. If the user of op is not an end scope, but is a lifetime ending use of
   op->get(), we insert a destroy_value|end_borrow as appropriate on op->get()
   to ensure op->get()'s lifetime is still ended. We assume that if
   op->getUser() is lifetime ending, that our caller has ensured that we can end
   newValue's lifetime.
2021-01-13 10:43:41 -08:00
Michael Gottesman
1b14b5bcee [sil] Move swift::replaceAllUsesAndErase from Analysis/SimplifyInstruction -> Utils/InstOptUtils
This is a low level API already being used in multiple places besides
InstSimplify (e.x.: Utils/OwnershipOptUtils), so it makes sense to move it into
InstOptUtil.
2021-01-13 10:43:41 -08:00
Michael Gottesman
ec50d03d76 [ownership] Rename OwnershipFixupContext::replaceAllUsesAndErase{FixingOwnership,}.
The class name is already OwnershipFixupContext... why do we need to include
FixingOwnership in its helpers... its redundant.
2021-01-13 10:43:41 -08:00
Michael Gottesman
4db2aa6b79 [sil-combine] Add an OwnershipFixupContext to SILCombine. 2021-01-13 10:43:41 -08:00