Commit Graph

4277 Commits

Author SHA1 Message Date
Andrew Trick
ae04531b5f Fix EscapeAnalysis value_to_bridge_object and strong_copy_unowned_value.
Noticed via code inspection. This could potentially miscompile, but we
haven't seen that happen to my knowledge.

Both value_to_bridge_object and strong_copy_XXX need to escape their
resulting value.

The implementation seemed to assume that it is conservatively correct
simply to avoid building a connection graph node for an value. This is
*not* true. Any value that has a pointer type requires a connection
graph node. The only way to be conservative is to create the value
node *and* point it to an escaping content node.

We can always declare that certain special types are not considered
pointer types, but then we need to handle all conversions from those
types to pointer types by escaping the resulting
pointer. BridgeObjects are often on the performance-critical path.
2020-01-23 16:48:22 -08:00
Andrew Trick
82a9f132a7 Merge pull request #29316 from atrick/escape-verifysummary
EscapeAnalysis verification: fix false positives.
2020-01-22 17:15:19 -08:00
Andrew Trick
1654dd54a1 Cleanup EscapeAnalysis getNode and fix undef handling.
Consistently handle base and derived pointers.

Consistently avoid creating nodes for undef, which breaks
verification.

Be more precise about creating the node based on the derived pointer
type.

Remove a few extraneous helpers.

Fixes <rdar://58445744> swiftc assert during EscapeAnalysis
verification: (EA->isPointer(Nd->mappedValue)), function verify
2020-01-22 11:48:05 -08:00
Andrew Trick
bd2bb2e8de EscapeAnalysis: Invert defer edges for array semantics.
Defer edges should always point to the more specific SSA value. For
example, the contents of a memory location point to the values stored
in that locations. Tuples point to their elements. BBArgs point to
their source operands. Likewise, array objects should point to their
exposed unsafe pointer (which is effectively a value stored in the
array object). I'm not sure why the defer edges were reversed in the
first place, but it was always confusing me.
2020-01-22 11:48:05 -08:00
Andrew Trick
78a8cac400 Fix EscapeAnalysis iterator invalidation.
Noticed by code inspection during debugging.

Iterator invalidaton could occur because two types of edges, which are
handled independently, actually share the same phyical list.

Fix the visitor utility that iterates over defer edges to be robust
for visitors that change the pointsTo edge. Use a temporary vector to
be safe.
2020-01-22 11:48:05 -08:00
Andrew Trick
1f580bee87 EscapeAnalysis add an early check to avoid a little work.
Noticed by code inspection during debugging. This avoids some work but
is probably NFC.
2020-01-22 11:48:05 -08:00
Andrew Trick
9495af6b75 Fix -escapes-internal-verify for summary graph merging.
Don't merge node properties until after node merging begins, so
internal verification can run right before each merge.

Rework ConnectionGraph::mergeFrom. Remove an extra loop. Defer
mergeAllScheduledNodes until all the source graph's mapped nodes are
added so that the graph is always structurally valid before a
merge. This is also necessary to avoid EscapeAnalysis
assert: (!To->mergeTo), in setPointsToEdge.

Enable -escapes-internal-verify to all tests in escape_analysis.sil.

Add hand-reduced unit tests in escape_analysis_reduced.sil.
2020-01-22 11:48:05 -08:00
Andrew Trick
e50ff0f2b8 EscapeAnalysis debugging: PrettyStackTrace for graph generation.
When ConnectionGraph merging triggers an assert, report which
functions are being merged.
2020-01-19 22:43:24 -08:00
David Zarzycki
32c66c38a9 [QoI] Fix -Wunused-variable warning 2020-01-19 13:32:12 -05:00
David Zarzycki
f185dd66f1 [QoI] Fix -Wrange-loop-analysis warnings 2020-01-19 13:29:23 -05:00
Andrew Trick
c3a08f06ad Merge pull request #29249 from atrick/fix-stackpromote
Fix EscapeAnalysis losing precision during merge.
2020-01-17 09:06:49 -08:00
Andrew Trick
fbe38ce78d Fix EscapeAnalysis losing precision during merge.
Array storage was being stack promoted even though it escaped. This
happened because multiple locally allocated arrays were merged into
the same locally allocated array value box. For this to become a
problem, other bizarre merge events need to take place, such as a
value node being mapped with a content node. The series of events led
to a missing edge in the connection graph. One of the arrays was
mapped directly to a project_box instruction which had forgotten it's
relationship with the alloc_box.

It is a trivial one line fix to simply preserve all value mappings.

<rdar://58371330> app crashes (miscompile)
2020-01-16 23:21:34 -08:00
Ravi Kandhadai
45f4ce5d4a [SIL Optimization] Improving comments for the constant evaluable call case
in the new InstructionDeleter utility.
2020-01-16 18:37:35 -08:00
swift-ci
b890bcb3c2 Merge pull request #28899 from ravikandhadai/constexpr-checked-casts 2020-01-13 14:45:32 -08:00
Slava Pestov
3b9014dc9a Merge pull request #28324 from zoecarver/optimize/dead-global-vars
Optimize dead private global variables
2020-01-13 16:41:21 -05:00
Dan Zheng
1486d6b346 NFC: Add GenericSignature::getCanonicalSignature. (#29105)
Motivation: `GenericSignatureImpl::getCanonicalSignature` crashes for
`GenericSignature` with underlying `nullptr`. This led to verbose workarounds
when computing `CanGenericSignature` from `GenericSignature`.

Solution: `GenericSignature::getCanonicalSignature` is a wrapper around
`GenericSignatureImpl::getCanonicalSignature` that returns the canonical
signature, or `nullptr` if the underlying pointer is `nullptr`.

Rewrite all verbose workarounds using `GenericSignature::getCanonicalSignature`.
2020-01-12 12:17:41 -08:00
Michael Gottesman
b4cf1afd19 [di] When emitting element addresses to destroy fields, use begin_access [deinit]. 2020-01-08 14:09:55 -08:00
David Zarzycki
89c5c0e4cc Merge pull request #29013 from davezarzycki/pr29013
NFC: Fix -Wdeprecated-copy warnings
2020-01-07 21:29:10 -05:00
David Zarzycki
3d1739fa86 NFC: Fix -Wdeprecated-copy warnings 2020-01-07 16:09:00 -05:00
Andrew Trick
659a37c122 Rewrite AliasAnalysis may-release/may-decrement queries.
Use the new EscapeAnalysis infrastructure to make ARC code motion and
ARC sequence opts much more powerful and fix a latent bug in
AliasAnalysis.

Adds a new API `EscapeAnalysis::mayReleaseContent()`. This replaces
all uses if `EscapeAnalysis::canEscapeValueTo()`, which affects
`AliasAnalysis::can[Apply|Builtin]DecrementRefCount()`.

Also rewrite `AliasAnalysis::mayValueReleaseInterferWithInstruction` to
directly use `EscapeAnalysis::mayReleaseContent`.

The new implementation in `EscapeAnalysis::mayReleaseContent()`
generalizes the logic to handle more cases while avoiding an incorrect
assumption in the prior code. In particular, it adds support for
disambiguating local references from accessed addresses. This helps
handle cases in which inlining was defeating ARC optimization. The
incorrect assumption was that a non-escaping address is never
reachable via a reference. However, if a reference does not escape,
then an address into its object also does not escape.

The bug in `AliasAnalysis::mayValueReleaseInterfereWithInstruction()`
appears not to have broken anything yet because it is always called by
`AliasAnalysis::mayHaveSymmetricInteference()`, which later checks
whether the accessed address may alias with the released reference
using a separate query, `EscapeAnalysis::canPointToSameMemory()`. This
happens to work because an address into memory that is directly
released when destroying a reference necesasarilly points to the same
memory object. For this reason, I couldn't figure out a simple way to
hand-code SIL tests to expose this bug.

The changes in diff order:

Replace EscapeAnalysis `canEscapeToValue` with `mayReleaseContent` to
make the semantics clear. It queries: "Can the given reference release
the content pointed to the given address".

Change `AliasAnalysis::canApplyDecrementRefCount` to use
`mayReleaseContent` instead if 'canEscapeToValue'.

Change `AliasAnalysis::mayValueReleaseInterferWithInstruction`: after
getting the memory address accessed by the instruction, simply call
`EscapeAnalysis::mayReleaseContent`, which now implements all the
logic. This avoids the bad assumption made by AliasAnalysis.

Handle two cases in mayReleaseContent: non-escaping instruction
addresses and non-escaping referenecs. Fix the non-escaping address
case by following all content nodes to determine whether the address
is reachable from the released reference. Introduce a new optimization
for the case in which the reference being released is allocated
locally.

The following test case is now optimized in arcsequenceopts.sil:
remove_as_local_object_indirectly_escapes_to_callee. It was trying to
test that ARC optimization was not too aggressive when it removed a
retain/release of a child object whose parent container is still in
use. But the retain/release should be removed. The original example
already over-releases the parent object.

Add new unit tests to late_release_hoisting.sil.
2020-01-06 23:58:59 -08:00
Slava Pestov
f0a1fb51b4 Merge pull request #29003 from slavapestov/fix-speculative-devirt
Fix speculative devirtualization to correctly use casted value
2020-01-06 13:25:50 -05:00
Slava Pestov
64971e7e55 SILOptimizer: Fix speculative devirtualization to correctly use casted value
We used to emit this:

  checked_cast_br [exact] %0 : $Foo to $Bar, bb1, bb2

bb1(%1 : $Bar):
  %2 = unchecked_ref_cast %0 : $Foo to %1 : $Bar
  apply ...(..., %2)
  br ...

bb2:
  ...

This is not ownership SIL-safe, because we're re-using the original
operand, which will have already been consumed at that point.

The more immediate problem here is that this is actually not safe
when combined with other optimizations. Suppose that after the
speculative devirtualization, our function is inlined into another
function where the operand was an upcast. Now we have this code:

  %0 = upcast ...
  checked_cast_br [exact] %0 : $Foo to $Bar, bb1, bb2

bb1(%1 : $Bar):
  %2 = unchecked_ref_cast %0 : $Foo to %1 : $Bar
  apply ...(..., %2)
  br ...

bb2:
  ...

The SILCombiner will simplify the unchecked_ref_cast of the upcast
into an unchecked_ref_cast of the upcast's original operand. At
this point, we have an unchecked_ref_cast between two unrelated
class types. While this means the block bb1 is unreachable, we
might perform further optimizations on it before we run the cast
optimizer and delete it altogether.

In particular, the devirtualizer follows chains of reference cast
instructions, and it will get very confused if it finds this invalid
cast between unrelated types.

Fixes <rdar://problem/57712094>, <https://bugs.swift.org/browse/SR-11916>.
2020-01-03 20:17:37 -05:00
Michael Gottesman
2b09547c07 [semantic-arc-opts] Use the LiveRange abstraction to find destroys instead of iterating over direct uses so we handle forwarding uses.
Previously, we were incorrectly handling these test cases since we weren't
properly finding destroys through forwarding instructions. I fixed that in a
previous commit by changing the code here to only support load [copy] without
forwarding instructions.

In this commit, I change the code to instead use the LiveRange abstraction. The
LiveRange abstraction already knows how to find destroys through forwarding
instructions and has this destroy array already computed for us!

So we get better runtime performance of code (due to the better opt) and better
compile time (since we aren't computing the destroy list twice)!

rdar://58289320
2020-01-03 16:51:20 -08:00
Michael Gottesman
750289e556 Merge pull request #28989 from gottesmm/pr-42b06814f186ae88911b84b21640d36b25c7a1cd
[semantic-arc-opts] When performing load [copy] -> load_borrow on classes, do not ignore forwarding uses.
2020-01-03 14:47:36 -08:00
Slava Pestov
3997aa5754 SIL: Remove SILBuilder::tryCreateUncheckedRefCast() 2020-01-03 15:37:19 -05:00
Michael Gottesman
990af4bdd3 [sil] Change TermInst::getSuccessorBlockArguments() to yield SILArguments instead of SILPhiArgument since I am going to be adding SwitchEnumResult (which the API can vend as well). 2020-01-03 11:12:48 -08:00
Michael Gottesman
23f36a059a [semantic-arc-opts] When performing load [copy] -> load_borrow on classes, do not ignore forwarding uses.
This is the first of two commits. This commit is a very simple, easily
cherry-pickable fix but does not use the LiveRange infrastructure so that we
handle forwarding uses here. Instead, we just bail if all uses of our load
[copy] are not destroy_value.

In a subsequent commit, I am going to change this to use the LiveRange
infrastructure so we will handle these cases. Sadly doing so doesn't cherry-pick
well. = /.

rdar://58289320
2020-01-03 10:51:39 -08:00
Michael Gottesman
5b557afbbe [sil] Now that we aren't using mark-uninitialized-fixup anywhere, remove it. 2020-01-02 12:10:36 -08:00
swift-ci
71ded284fa Merge pull request #28963 from gottesmm/mark_uninit_fixup_removal 2020-01-02 11:48:28 -08:00
Saleem Abdulrasool
87341e3296 Merge pull request #28939 from compnerd/shadow-walker
disambiguate some type shadowing (NFCI)
2020-01-02 10:06:09 -08:00
Michael Gottesman
28ffcf9a7a [ownership] Eliminate the need for mark_uninitialized fixup.
This commit eliminates the need for mark uninitialized fixup by updating the
compiler so that we now emit:

```
%0 = alloc_box
%1 = mark_uninitialized %0
%2 = project_box %1
...
destroy_value %1
```

Instead of:

```
%0 = alloc_box
%1 = project_box %0
%2 = mark_uninitialized %1
...
destroy_value %0
```

Now that the first type of code is generated, I can change project_box to only
take guaranteed arguments. This will ensure that the OSSA ARC optimizer can
eliminate copies of boxes without needing to understand the usage of the
project_box.
2020-01-02 09:54:18 -08:00
Michael Gottesman
c4c78517e2 Merge pull request #28915 from gottesmm/pr-fda4ad657562fae35331ce52c53f6d30ca784174
[di] Debride dead code from splitting PMO/DI and hide state in preparation for other commits.
2020-01-02 09:39:21 -08:00
Michael Gottesman
321185e82f [sil] Rename TermInst::{getSuccessorBlockArguments,getSuccessorBlockArgumentLists}()
This method returns argument lists, not arguments! We should add in the future
an additional API that returns a flap mapped range over all such argument lists
to cleanup some of this code. But at least now the name is more accurate.
2019-12-28 15:33:30 -08:00
Michael Gottesman
ba6763ac10 [di] Instead of accessing TheMemory.MemoryInst directly, use the helper getUninitializedValue().
This is going to let me change getUninitializedValue() to special case alloc_box
so that we return the project_box (the address we want to analyze) rather than
the mark_uninitialized (which is on the box).
2019-12-27 19:59:12 -08:00
Michael Gottesman
722d0bb028 [di] Add an accessor for NumElements and use that instead of accessing the field directly. 2019-12-27 19:49:34 -08:00
Michael Gottesman
2cf6d02a93 [di] Now that DI and PMO are split, debride some dead code.
Specifically isDefiniteInitFinished is always set to false and
TreatAddressToPointerAsInout is set to true when ever DI is run, so this patch
just constant propagates those values and then DCEs as appropriate.
2019-12-27 19:07:54 -08:00
Michael Gottesman
74120f3e9f [di] Remove dead code.
Since collectUses in DI land always asserts that we are processing a pointer,
this code for sure has never been called (since it works with destructures),
meaning that the code must be dead.
2019-12-27 19:07:54 -08:00
zoecarver
be6239aa01 Fix should optimize logic 2019-12-24 15:39:56 -08:00
Saleem Abdulrasool
7d8aac60ca disambiguate some type shadowing (NFCI)
Adjust the type shadowing identified by GCC 7.  The declaration shadows
a type which changes the meaning of the identifier subsequently.
2019-12-23 15:34:55 -08:00
Andrew Trick
786c29a139 Fix a crash in StackPromotion; case not handled in EscapeAnalysis.
StackPromotion queries EscapeAnalysis. The escape information for the
result of an array semantic was missing because it was an ill-formed
call. This fix introduces a new check to determine whether a call is
well formed so it can be handled consistently everywhere.

Fixes <rdar://problem/58113508>
Interpreter/SDK/objc_fast_enumeration.swift failed on
iphonesimulator-i386

The bug was introduced here:
commit 8b926af49a
Author: Andrew Trick <atrick@apple.com>
Date:   Mon Dec 16 16:04:09 2019 -0800

    EscapeAnalysis: Add PointerKind and interior/reference flags

The bug can occur when a semantic "array.ininitialized"
call (Array.adoptStorage) at the SIL level has direct "release_value"
instructions that don't use the tuple_extract values corresponding to
the call's returned value. This is part of the tragedy of not
supporting multiple call results. When users of the call's result can
use either the entire tuple (which is a meaningless value), or the
individual tuple extracts, then there's no way to handle calls
uniformly.

The change that broke this was to remove the special handling of
TupleExtract instructions. That special handling was identical to the
way that any normal pointer projection is handled. So, as a
simplification, the new code just expects that case to be handled
naturally as a pointer projection. This case was already guarded by a
check to determine whether the TupleExtract was actually the result of
an array.uninitialized call, in which case the normal handling does
not apply. However, because of the weirdness mentioned above, the
handling of "array.ininitialized" may silently bail out. When that
happens, the TupleExtract gets neither the special handling, nor the
normal handling.

Note that the old special handling of TupleExtract was bizarre and
relied on corresponding weirdness in the code that handles
"array.uninitialized". The new code simply creates a separate graph
node for each result of the call, and creates the edges that exactly
correspond to load and stores on those results. So, rather than
reinstating the previous code, which I can't quite reason about, this
fix creates a single check to determine whether a TupleExtract is
treated as an "array.uninitialized" result or not, and use that check
in both places.
2019-12-21 01:11:55 -08:00
Ravi Kandhadai
ef254c0e9c [Constant Evaluator] Add support for evaluating checked_cast_br
instruction and store_borrow.
2019-12-20 09:29:20 -08:00
Slava Pestov
f994334912 SILOptimizer: AllocBoxToStack preserves [transparent] bit in cloned function
We need this to uphold the invariant that in the performance pipeline
before SIL serialization occurs, a function has ownership iff it is
transparent.
2019-12-19 23:51:53 -05:00
Michael Gottesman
980b1f0561 [di] Hide some internal state and give some methods better names given current usage.
NFC.
2019-12-19 15:09:15 -08:00
Andrew Trick
2630be1876 Merge pull request #28871 from atrick/escape-cycle
Unrevert EscapeAnalysis commits
2019-12-19 11:17:05 -08:00
Andrew Trick
3782d67cda EscapeAnalysis: rewrite canEscapeToUsePoint.
Correctness: do not make any unenforced assumptions about how the
connection graph is built (I don't think the previous assumption about
the structure of the graph node mapped to a reference-type value would
always hold if content nodes can be arbitrarily merged). Only make one
assumption about the client code: the access being checked must be to
some address within the provided value, not another object indirectly
reachable from that value.

Optimization: Allow escape analysis to prove that an addressable
object does not escape even when one of its reference-type fields
escapes.
2019-12-19 00:12:37 -08:00
Andrew Trick
17ab0ad5b5 EscapeAnalysis: Do not create defer edges for block arguemnts.
That appears to have been a partial workaround for the real
problem that usepoints need to be propagated across the entire
defer web. This is now solved by considering use points on the
reference node's content, not the reference node itself.
2019-12-19 00:12:37 -08:00
Andrew Trick
7fb4e21bc0 EscapeAnalysis: Make EscapeState and UsePoints a property of the content node only.
For alias analysis query to be generally correct, we need to
effectively merge the escape state and use points for everything in a
defer web.

It was unclear from the current design whether the "escaping" property
applied to the pointer value or its content. The implementation is
inconsistent in how it was treated. It appears that some bugs have
been worked around by propagating forward through defer edges, some
have been worked around by querying the content instead of the
pointer, and others have been worked around be creating fake use
points at block arguments.

If we always simply query the content for escape state and use points,
then we never need to propagate along defer edges. The current code
that propagates escape state along defer edges in one direction is
simply incorrect from the perspective of alias analysis.

One very attractive solution is to merge nodes eagerly without
creating any defer edges, but that would be a much more radical change
even than what I've done here. It would also pose some new issues: how
to resolve the current "node types" when merging and how to deal with
missing content nodes.

This solution of applying escape state to content nodes solves all
these problems without too radical of a change at the expense of
eagerly creating content nodes. (The potential graph memory usage is
not really an issue because it's possible to drastically shrink the
size of the graph anyway in a future commit--I've been able to fit a
node within one cache line). This solution nicely preserves graph
structure which makes it easy to debug and relate to the IR.

Eagerly creating content nodes also solves the missing content node
problem. For example, when querying canEscapeTo, we need to know
whether to look at the escape state for just the pointer value itself,
or also for its content. It may be possible the its content node is
actually part of the same object at the IR level. If the content node
is missing, then we don't know if the object's interior address is not
recognizable/representable or whether we simply never saw an access to
the interior address. We can't simply look at whether the current IR
value happens to be a reference, because that doesn't tell us whether
the graph node may have been merged with a non-reference node or even
with it's own content node. To be correct in general, this query would
need to be extremely conservative. However, if content nodes are
always created for references, then we only need to query the escape
state of a pointer's content node. The content node's flag tells us if
it's an interior node, in which case it will always point to another
content node which also needs to be queried.
2019-12-19 00:12:37 -08:00
Andrew Trick
8b926af49a EscapeAnalysis: Add PointerKind and interior/reference flags
Categorize three kinds of pointers:

NoPointer (don't create a node)

ReferenceOnly (safe to make normal assumptions)

AnyPointer (may have addresses, rawpointers, or any mix of thoses with references)

Flag ConnectionGraph nodes as
- hasReferenceOnly
- isInterior

An interior node always has an additional content node.

All sorts of arbitrary node merging is supported. Nodes with totally
different properties can be safely merged. Interior nodes can safely
be merged with their field content (which does happen surprisingly
often).

Alias analysis will use these flags to safely make assumptions about
properties of the connection graph.
2019-12-19 00:12:37 -08:00
swift-ci
a50b94011a Merge pull request #28865 from gottesmm/pr-0437ed0a25aba3ef32d53473cb1ab2c68287ca77 2019-12-18 18:09:45 -08:00
Ravi Kandhadai
774af19712 Merge pull request #28635 from ravikandhadai/oslog-dead-code-elim-patch1
[SIL Optimization] Create new utilities for dead code elimination.
2019-12-18 17:33:15 -08:00