Commit Graph

143 Commits

Author SHA1 Message Date
Meghana Gupta
b1db77adff Fix ConnectionGraph verification for calls to no return functions. (#33655)
Functions that do not have a return, and instead end with 'unreachable'
due to NoReturnFolding will not have a ReturnNode in the connection
graph.

A caller calling a no return function then may not have a CGNode
corresponding to the call's result.

Fix the ConnectionGraph's verifier so we don't assert in such cases.
2020-08-27 20:42:23 -07:00
Andrew Trick
3ff11aa683 Teach EscapeAnalysis to ignore end_access markers.
The presence of end_access markers was causing object properties to
appear to escape globally. That's too conservative.
2020-07-11 01:24:35 -07:00
Anthony Latsis
9fd1aa5d59 [NFC] Pre- increment and decrement where possible 2020-06-01 15:39:29 +03:00
Erik Eckstein
33c8e16ce0 SIL optimizer: Support begin_cow_mutation and end_cow_mutation in some optimizations.
Mostly this is about "looking through" a begin_cow_mutation or end_cow_mutation.
2020-05-26 18:01:17 +02:00
Arnold Schwaighofer
147144baa6 SIL: Thread type expansion context through to function convention apis
This became necessary after recent function type changes that keep
substituted generic function types abstract even after substitution to
correctly handle automatic opaque result type substitution.

Instead of performing the opaque result type substitution as part of
substituting the generic args the underlying type will now be reified as
part of looking at the parameter/return types which happens as part of
the function convention apis.

rdar://62560867
2020-05-04 13:53:30 -07:00
Erik Eckstein
fd6c26e948 EscapeAnalysis: don't compute the connection graph for very large functions
For functions which results in > 10000 nodes, just bail and don't compute the connection graph.
The node merging algorithm is quadratic and can result in significant compile times for very large functions.

rdar://problem/56268570
2020-04-10 20:10:24 +02:00
Erik Eckstein
f33c2ade1d EscapeAnalysis: remove an unused parameter from canOptimizeArrayUninitializedCall
NFC
2020-04-10 20:10:24 +02:00
Erik Eckstein
5a5cbf78a0 EscapeAnalysis: fix a problem with unchecked_addr_cast
In case an unchecked_addr_cast is used to convert between pointer and non-pointer, we missed some escaping values.
This can be the case when using C unions.

https://bugs.swift.org/browse/SR-12427
rdar://problem/60983997
2020-04-03 09:07:34 +02:00
Andrew Trick
bebbe370e8 Fix EscapeAnalysis verification assert at unreachable blocks
If EscapeAnalysis verification runs on unreachable code, it asserts
with "Missing escape connection graph mapping" because the connection
graph builder only runs on reachable blocks.

Add a ReachableBlocks utility and use it during verification.

Fixes <rdar://problem/60373501> EscapeAnalysis crashes with CFG with
unreachable blocks
2020-03-14 14:31:41 -07:00
Andrew Trick
601a51a605 Fix EscapeAnalysis connection graph for existential values.
Change the connection graph builder to map an existential address and
its opened address onto the same node.

Fixes <rdar://59559805> miscompile; use-after-free

Immediate bug: EscapeAnalysis::mayReleaseContent incorrectly returns
'false' when comparing a store into an existential's value with a
release of the existential.

How this was exposed: mayReleaseContent was made more aggressive so
that only the connection graph node representing the released object
is considered to be "released". This fails for existentials because a
separate connection graph node is created for the value within the
existential.

This is just one manifestation of a broader bug. Representing an
existential as two logically distinct objects could also result in
incorrect escaping information and incorrect alias analysis. Note that
copying directly into an existential address and storing into the
opened value in fact modify the same memory. Furthermore, when opening
an existential, no local reference count is used to keep the opened
value "alive" independent from the existential (this is another
assumption made by mayReleaseContent). This is always how existentials
were represented in the connection graph, but issues had never been
exposed.
2020-02-22 02:07:32 -08:00
Andrew Trick
4028ac245a EscapeAnalysis: add support for access markers.
This will be critical when OSSA relies on access markers everywhere
and more passes are converted to OSSA.
2020-01-29 11:15:53 -08:00
Andrew Trick
c881a4f008 Fix EscapeAnalysis verification for an array.uninitialized case.
The most recently added verification exposed an issue where the code
pattern-matches an array.uninitialized call. The pattern matching does
not handle multiple tuple_extracts but the API returned "success" in
some of those cases.

Fixes the verifier assert when building ImagePicker.
2020-01-26 22:32:26 -08:00
Andrew Trick
06645d3c0f Add EscapeAnalysis verification to catch unmapped SILValues.
This verification would have prevented the following recent miscompilation:

  commit fbe38ce78d
  Fix EscapeAnalysis losing precision during merge.
2020-01-23 17:43:19 -08:00
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
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
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
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
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
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
eeckstein
bb067f4d68 Revert "EscapeAnalysis: add node flags and change the meaning of "escaping"" 2019-12-18 16:17:12 +01:00
Andrew Trick
0ea3bb1ff5 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-17 11:05:28 -08:00
Andrew Trick
cec925c549 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-16 16:43:06 -08:00
Andrew Trick
5a27e5d802 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-16 16:43:06 -08:00
Andrew Trick
f7472e2c45 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-16 16:43:06 -08:00
Andrew Trick
b8d901be63 EscapeAnalysis: minor cleanup to prepare for alias analysis rewrite.
Create a Predecessor abstraction for readability. Eliminates frequent
occurence of inscrutable getInt() and getPointer() calls.

Create an escapesInsideFunction() API that only uses mapped
values. This prevents bugs where a content node's value would be
mistakenly used to check for escapes.
2019-12-03 21:50:48 -08:00
Andrew Trick
191f9db59f EscapeAnalysis: eliminate dangling pointers.
Avoid dangling pointers in the connection graph when SILValues are
deleted. This didn't show up in normal compilation because the values
were only used for printing the graph. But now we have more assertions
that check for well-formed graph. These could hit the dangling
pointers.

To guarantee this can't happen, establish a strict invariant that the
mappedValue is only used for nodes that exist in the value-to-node
map. Then clear out mappedValue whenever a value is deleted. Add
verification to ensure that each node's mappedValue is valid and catch
dangling pointers quickly.

This completely changes the way that a node values are set and used
which in turn makes it *much* easier to debug the graph and associate
the nodes with the SIL function. For example, it's normal to view or
dump the graph at a key point. When the nodes are printed, the
associated SILValues are now more precise and consistent. However,
it's also normal to call CGNode::dump() from tracing code or within a
debugger. Previously, there was no way to assocate the output of
CGNode::dump() with the printed or displayed connection graph. Now
both forms of output have identical node IDs!

While we're at it, make sure the hasRC flag is always merged
conservatively in a couple more places.

Fixed <rdar://57290845> While running SILFunctionTransform "TempRValueOpt"
2019-11-20 23:06:10 -08:00
Andrew Trick
e66e033b00 Cleanup EscapeAnalysis::ConnectionGraph::initializePointsTo.
Remove cruft that I added in the previous commit. This eliminates
unnecessary cleverness so initializePointsTo is now very simple.

Add hand-coded SIL test cases to somewhat verify that the new
algorithm works.
2019-11-15 09:11:35 -08:00
Andrew Trick
999b32e0fd EscapeAnalysis: add a refcount flag to content nodes.
This property will allow alias analysis to be safely optmistic when
querying the connection graph. A node that is known to have a ref
count is know to keep alive everything it points to. Therefore,
calling a deinitializer on a different reference cannot release the RC
node's contents.
2019-11-13 11:41:34 -08:00
Andrew Trick
2c7e348814 EscapeAnalysis: rework graph update and merge algorithms
The two major take aways from this patch are:

(1) Impose graph structure and reduce superfluous nodes and edges.

Incrementally make the connection graph and the APIs used to construct
it more structured.

_This allows node properties based on the SILValue to be reliably added to nodes_

Although that was the initial motiviation, there are other
benefits. Non-content nodes now have verifiable SILValues. Content
nodes now have meaningful SILValues even though they can't be
guaranteed due to merging. As a result it is *much* easier to debug
the graph and correlate it with the SIL. Rather than a web of
connection graph nodes with no identity and edges that don't
correspond to anything in SIL, the graph nodes now have value number
that correspond to the instruction used to dereference the node. The
edges also exhibit structure now. A pointsTo edge now (in practice)
always corresponds to a real pointer deference in the SIL. Doing this
required more than just adding some helpers, it was also necessary to
rewrite the graph merge and update algorithms.

(2) Split up underlying functionality into more explicit steps

Breaks apart the most complex parts of the graph algorithms into small
self-contained, self-checked steps. The purpose of each step is clear
and it's possible to reason about correctness from basic
invariants. Each merge step can now run full graph verification.

This was also done to move toward an invariant that the graph is never
mutated during a query. But to finish that goal, we need to add a
use-point query. With that, there will be no node creation, use point
propagation, new defer edges, etc. after graph building. At the very
least, this will make it sane to debug the output of the analysis.

---
Here is a change-by-change description in diff order:

Replace `updatePointsTo` with `initializePointsTo` and
`mergePointsTo`. Merging is very simple on its own. Initialization
requires some extra consideration for graph invariants. This
separation makes it possible to write stong asserts and to
independently reason about the correctness of each step based on
static invariants.

Replace `getContentNode` with `createContentNode`, and add two higher
level APIs `createMergedContent`, and `getFieldContent`. This makes
explicit the important cases of merging nodes and creating a special
nodes for class fields. This slightly simplifies adding properties to
content nodes and helps understand the structure of the graph.

Factor out an `escapeContentsOf` helper for use elsewhere...

Add a `getValueContent` helper. This is where we can tie the
properties of content nodes to the address values that are used to
address that content. This now also ensures that a Value node's
value field is consistent with all SILValues that map to it.

Add -escapes-internal-verify to check that the graph is in a valid
state after every merge or update step. This verification drove the
partial rewrite of mergeAllScheduledNodes.

ConnectionGraph::defer implementation: explictly handle the three
possible cases of pointsTo initialization or pointsTo merging at the
top level, so that those underlying implementations do not need to
dynamically handle weirdly different scenarios.

ConnectionGraph::initializePointsTo implementation: this simplified
implementation is possible by relying on invariants that can be
checked at each merge/update step. The major functional difference is
that it avoids creating unnecessary pointsTo edges. The previous
implementation often created pointsTo edges when adding defer edges
just to be conservative. Fixing this saved my sanity during debugging
because the pointsTo edges now always correspond to a SIL operations
that dereference the pointer. I'm also arguing without evidence that
this should be much more efficient.

ConnectionGraph::mergeAllScheduledNodes implementation: Add
verification to each step so that we can prove the other utilities
that are used while merging aren't making incorrect assumptions about
the graph state. Remove checks for merged nodes now that the graph is
consistently valid. Also remove a loop at the end that didn't seem to
do anything. The diff is impossible to review, but the idea is
basically the same. As long as it's still possible to scan through the
steps in the new code without getting totally lost, then the goal was
achieved.

ConnectionGraph::mergePointsTo: This is extremely simple now. In all
the places where we used to call updatePointsTo, and now call
mergePointsTo, it's a lot easier for someone debugging the code to
reason about what could possibly happen at that point.

`createMergedContent` is a placeholder for transferring node properties.

The `getFieldContent` helper may seem silly, but I find it helpful to
see all the important ways that content can be created in one place
next to the createContentNode, and I like the way that the creation of
the special "field content" node is more explicit in the source.

ConnectionGraph::mergeFrom implementation: this is only a minor
cleanup to remove some control flow nesting and use the CGNodeWorklist
abstraction.

In AnalyzeInstruction, add EscapeAnalysis::getValueContent helper. It
eliminates an extra step of going through the value node to get at its
content node. This is where we can derive content node properties from
the SILValue that dereferences the content. We can update the content
node's associated value 'V' if it's useful. It's also a place to put
assertions specific to the first level of content.

In AnalyzeInstruction, Array semantic calls: add support for
getValueContent so we can derive node properties. This is also nice
because it's explicit about which nodes are value content vs. field
content.

In AnalyzeInstruction, cleanup Release handling: use the explicit
APIs: getValueContent, getFieldContent, and escapeContentsOf.

In AnalyzeInstruction, assert that load-like things can't produce addresses.

In AnalyzeInstruction, add comments to clarify object projection handling.

In AnalyzeInstruction, add comments to explain store handling.

In AnalyzeInstruction, drop the assumption that all partial applies hold pointers.

In AnalyzeInstruction, handle aggregates differently so that Value
nodes are always consistent with their SILValue and can be
verified. Aggregates nodes are still coalesced if they only have a
single pointer-type subelement. If we arbitrarily coalesced an
aggregate with just one of its subelements then there would be no
consistent way to identify the value that corresponds to a connection
graph node.
2019-11-12 14:08:23 -08:00
Arnold Schwaighofer
8aaa7b4dc1 SILOptimizer: Pipe through TypeExpansionContext 2019-11-11 14:21:52 -08:00
Andrew Trick
13b7f454f9 EscapeAnalysis: remove unused pointsToEnd utility, for now. 2019-11-06 15:15:39 -08:00
Andrew Trick
cb1e787f50 EscapeAnalysis: remove unused cast handling from the main loop.
AnalyzeInstruction no longer needs to handle all these casts.
getPointerBase does it now.
2019-11-06 15:15:39 -08:00
Andrew Trick
f009cf3de8 EscapeAnalysis cleanup and add utilities [nearly NFC]
This is the first in a series of patches that reworks
EscapeAnalysis. For this patch, I extracted every change that does not
introduce new features, rewrite logic, or appear to change
functionality.

These cleanups were done in preparation for:

- adding a graph representation for reference counted objects

- rewriting parts to the query logic

- ...which then allows the analysis to safely assume that all
  exclusive arguments are unique

- ...which then allows more aggressive optimization of local variables
  that don't escape

There are two possible functional changes:

1. getUnderlyingAddressRoot in InstructionUtils now sees through OSSA
instructions: begin_borrow and copy_value

2. The getPointerBase helper in EscapeAnalysis now sees through all of
these reference and pointer casts:

+  case ValueKind::UncheckedRefCastInst:
+  case ValueKind::ConvertFunctionInst:
+  case ValueKind::UpcastInst:
+  case ValueKind::InitExistentialRefInst:
+  case ValueKind::OpenExistentialRefInst:
+  case ValueKind::RawPointerToRefInst:
+  case ValueKind::RefToRawPointerInst:
+  case ValueKind::RefToBridgeObjectInst:
+  case ValueKind::BridgeObjectToRefInst:
+  case ValueKind::UncheckedAddrCastInst:
+  case ValueKind::UnconditionalCheckedCastInst:
+  case ValueKind::RefTo##Name##Inst:
+  case ValueKind::Name##ToRefInst:

This coalesces a whole bunch of nodes together that were just there
because of casts. The existing code was already doing this for one
level of casts, but there was a bug that prevented it from happening
transitively. So, in theory, anything that breaks with this fix could
also break without this fix, but may not have been exposed. The fact
that this analysis coalesces address-to-reference casts at all is what
caused me to spent vast amounts of time debugging any time I tried to
force some structure on the graph via assertions. If it is done at
all, it should be done everywhere consistently to expose issues as
early as possible.

Here is a description of the changes in diff order. If something in
the diff is not listed here, then the code probably just moved around
in the file:

Rename isNotAliasedIndirectParameter to
isExclusiveIndirectParameter. The argument may be aliased in the
caller's scope and it's contents may have already escaped.

Add comments to SILType APIs (isTrivial, isReferenceCounted) that give
answers about the AST type which don't really make sense for address
SILTypes.

Add comments about CGNode's 'Value' field. I spent lots of time
attempting to tighten this down with asserts, but it's only possible
for non-content nodes. For content nodes, the node's value is highly
unpredictable and basically nonsense but needed for debugging.

Add comments about not assuming that the content nodes pointsTo edge
represents physical indirection. This matters when reasoning about
aliasing and it's a tempting assumption to make.

Add a CGNode::mergeProperties placeholder for adding node properties.

Factor out a CGNode::canAddDeferred helper for use later.

Rename `setPointsTo` to `setPointsToEdge` because it actually creates
an edge rather than just setting `pointsTo`.

Add CGNode::getValue() and related helpers to help maintain invariants.

Factor out a `markEscaping` helper.

Clean up the `escapesInsideFunction` helper.

Add node visitor helpers: visitSuccessors, visitDefers. This made is
much easier to prototype utilities.

Add comments to clarify the `pointsTo` invariant. In particular, an
entire defer web may have a null pointsTo for a while.

Add an `activeWorklist` to avoid nasty bugs when composing multiple
helpers that each use the worklist.

Remove the `EA` argument from `getNode`. I ended up needing access to
the `EA` context from the ConnectionGraph many times during
prototyping and passing `this` was all the `getNode` calls was very
silly.

Add graph visitor helpers: backwardTraverse, forwardTraverseDefer,
forwardTraversePointsToEdges, and mayReach for ease in developing new
logic and utilities.

Add isExclusiveArgument helper and distinguish exclusive arguments
from local objects. Confusing these properties could lead to scary
bugs. For example, unlike a local object, an exclusive argument's
contents may still escape even when the content's connection graph
node is non-escaping!

Add isUniquelyIdentified helper when we want to treat exclusive
arguments and local objects uniformly.

getUnderlyingAddressRoot now looks through OSSA instructions.

Rename `getAccessedMemory` to `getDirectlyAccessedMemory` with
comments. This is another dangerous API because it assumes the memory
access to a given address won't touch memory represented by different
graph nodes, but graph edges don't necessarily represent physical
indirection. Further clarify this issue in comments in
AliasAnalysis.cpp.

Factor out a 'findRecursiveRefType' helper from the old
'mayContainReference' for checking whether types may or must contain
references. Support both kinds of queries so the analysis can be
certain when a pointer's content is a physical heap object.

Factor out 'getPointerBase' and 'getPointerRoot' helpers that follow
address projections within what EscapeAnalysis will consider a single
node.

Create a CGNodeWorklist abstraction to safely formalize the worklist
mechanism that's used all over the place. In one place, there were
even two separate independent lists used independently (nodes added to
one list could appear to be in the other list).

The CGNodeMap abstraction did not significantly change, it was just moved.

Added 'dumpCG' for dumping .dot files making it possible to remote debug.

Added '-escapes-enable-graphwriter' option to dump .dot files, since
they are so much more useful than the textual dump of the connection
graph, which lacks node identity!
2019-11-06 11:07:52 -08:00
Michael Gottesman
7ee5ad7318 [sil] Rename {,Strong}Copy{Unowned,Unmanaged}. 2019-10-26 17:03:47 -07:00
eeckstein
8fc1501081 Revert "EscapeAnalysis: make the use-point analysis more precise" 2019-10-15 19:21:26 +02:00
Erik Eckstein
d1072621ba EscapeAnalysis: make the use-point analysis more precise
In canEscapeToUsePoint only check the content node if it's a reference (see comment why this is needed).
For all other node types, especially addresses, handle defer edges by propagating use-point infomation backward in the graph.
This makes escape analysis more precise with address types, e.g. don't consider an inout address to escape to an apply if just the loaded value is passed to an apply argument.
2019-10-14 20:43:46 +02:00
Andrew Trick
bddc69c8a6 Organize SILOptimizer/Utils headers. Remove Local.h.
The XXOptUtils.h convention is already established and parallels
the SIL/XXUtils convention.

New:
- InstOptUtils.h
- CFGOptUtils.h
- BasicBlockOptUtils.h
- ValueLifetime.h

Removed:
- Local.h
- Two conflicting CFG.h files

This reorganization is helpful before I introduce more
utilities for block cloning similar to SinkAddressProjections.

Move the control flow utilies out of Local.h, which was an
unreadable, unprincipled mess. Rename it to InstOptUtils.h, and
confine it to small APIs for working with individual instructions.
These are the optimizer's additions to /SIL/InstUtils.h.

Rename CFG.h to CFGOptUtils.h and remove the one in /Analysis. Now
there is only SIL/CFG.h, resolving the naming conflict within the
swift project (this has always been a problem for source tools). Limit
this header to low-level APIs for working with branches and CFG edges.

Add BasicBlockOptUtils.h for block level transforms (it makes me sad
that I can't use BBOptUtils.h, but SIL already has
BasicBlockUtils.h). These are larger APIs for cloning or removing
whole blocks.
2019-10-02 11:34:54 -07:00
Michael Gottesman
5fc1d1d349 [ownership] Define a new instruction copy_unmanaged_value.
This provides a singular instruction for convert an unmanaged value to a ref,
then strong_retain it. I expanded the definition of UNCHECKED_REF_STORAGE to
include these copy like instructions. This instruction is valid in all SIL.

The reason why I am adding this instruction is that currently when we emit an
access to an unowned (unsafe) ivar, we use an unmanaged_to_ref and a strong
retain. This can look to the optimizer like a strong retain that can potentially
be optimized. By combining the two together into a new instruction, we can avoid
this potential problem since the pattern matching will break.
2019-08-25 21:26:40 -07:00
Slava Pestov
0063f158be AST: Request-ify synthesis of the implicit destructor 2019-08-09 19:08:47 -04:00
Andrew Trick
9627a05e00 Document EscapeAnalysis.
Describe the algorithm in the file-level doc comment. The basic
algorithm in the referenced paper is similar, but the most
interesting/important information is how it is adapted to SIL.
2019-07-30 22:47:11 -07:00
Arnold Schwaighofer
c187c8ac13 SIL: Replace uses of getReferencedFunction() by getReferencedFunctionOrNull() and getInitialReferencedFunction()
With the advent of dynamic_function_ref the actual callee of such a ref
my vary. Optimizations should not assume to know the content of a
function referenced by dynamic_function_ref. Introduce
getReferencedFunctionOrNull which will return null for such function
refs. And getInitialReferencedFunction to return the referenced
function.
Use as appropriate.

rdar://50959798
2019-05-26 08:58:14 -07:00
Erik Eckstein
34931428e1 EscapeAnalysis: handle SILUndef values
fixes a crash

rdar://problem/50279857
2019-04-29 13:37:03 -07:00