Commit Graph

35 Commits

Author SHA1 Message Date
Michael Gottesman
b5ce28fc57 [region-isolation] Cache getUnderlyingTrackedValue.
TLDR: Was looking at some performance traces and saw that we need to cache the
result of this value.

----

Specifically, I noticed that we were spending a lot of time computing this
operation. When I looked at the code I saw that we already had a cache along the
relevant code paths... but the cache was from equivalence class representative
-> state. Before we hit that cache, we were performing the work to map the value
to the equivalence class representative... so the work to perform the relevant
lookup from value -> state (which goes through the equivalence class
representative) was not just a hash table lookup. This operation makes it
cheaper by making it two cache lookups.

It may be possible to make this cheaper by redoing the actual mapping of
information so that we can go straight from value to state. I think it would be
slightly different since we would probably need to represent the state in a
separate array and map with indices... which is really just a more efficient
hash table. We could also use malloc/etc but lets not even talk about that.

rdar://139520959
2024-11-11 11:43:07 -08:00
Michael Gottesman
32b4de60a9 Rename transfer -> send.
Accomplished using clangd's rename functionality.
2024-11-04 15:17:51 -08:00
Michael Gottesman
1fbc930cdd [region-isolation] Make logging and debug tooling appear in non-asserts builds.
This will just help me to more quickly triage without needing to compile an
asserts compiler.
2024-08-07 13:35:18 -07:00
Michael Gottesman
ace94b00ba [region-isolation] Move RepresentativeValue from RegionAnalysis.h -> PartitionUtils.h and add APIs for mapping an ElementID -> Representative.
This is just moving up the declaration in the chain of dependencies so that I
can write logic in PartitionUtils.h using it. I also added entrypoints to lookup
the ReprensetativeValue for our various emitters.
2024-07-18 21:28:22 -07:00
Michael Gottesman
e9e5c4eb4c [region-isolation] Ensure that some NDEBUG code is properly guarded. 2024-07-02 13:55:33 -07:00
Michael Gottesman
78d74cf716 [region-isolation] Make sil-region-isolation-assert-on-unknown-pattern also work with TransferNonSendable versions of the error.
This asserts only option is an option to make it quicker/easier to triage
unknown pattern match errors by aborting when we emit it (allowing one to
immediately drop into the debugger at that point).

Previously, it only happened for errors in RegionAnalysis not in
TransferNonSendable itself.
2024-07-01 13:12:36 -07:00
Michael Gottesman
2de13df909 [region-isolation] Use SILIsolationInfo::initializeTrackableValue instead of SILIsolationInfo::mergeIsolationRegionInfo to fix last issue
When merging SILIsolationInfo for regions, we want to drop
nonisolated(unsafe). This is important since nonisolated(unsafe) should only
apply to the specific "value" that it belongs to, not the entire region.

This creates a problem since in a few places in the code base we initialize a
value (producing a disconnected value) and then initialize it by merging in an
actor isolation. This no longer work since we will then always have
nonisolated(unsafe) stripped, so no values would ever be considered to be
nonisolated(unsafe). After analyzing the use case, I realized that these were
just initialization patterns and in this commit, I added a specific
initialization operation called SILIsolationInfo::initializeTrackableValue and
eliminated those calls to SILIsolationInfo::mergeIsolationRegionInfo.

Since SILIsolationInfo no longer has any merge operation on it, I then
eliminated that code in this commit. This completes the behavior split that I
put into the type system in the last commit. Specifically, I defined a
composition type called SILDynamicMergedIsolationInfo. It represents a
SILIsolationInfo that has been merged... that is why I called it the
DynamicMergedIsolationInfo. It could probably use a better name = (.

This fixes one of the last weird test case that I wrote where we were not letting through valid
nonisolated(unsafe) code.

At the same time, I discovered an additional issue (which can be seen in the
TODOs in this commit), where we are being too conservative around a non-Sendable
class var field. I am going to fix that in the next commit.

rdar://128299305
2024-05-27 21:42:15 -07:00
Michael Gottesman
741244e16b [region-isolation] Split in the type system SILIsolationInfo that has been merged and those that haven't.
Specifically, I introduced a new composition type called
SILDynamicMergedIsolationInfo that just contains a
SILIsolationInfo. Importantly, whenever one merges a SILIsolationInfo with
another SILIsolationInfo, one gets back a SILDynamicMergedIsolationInfo.

The reason why I am doing this is that we drop nonisolated(unsafe) when merging
so I want to ensure that parts of the code that use merging (where the dropping
occurs) and normal SILIsolationInfo where we do not want to merge is
distinguished.
2024-05-27 21:41:54 -07:00
Michael Gottesman
3a1f58a72a [region-isolation] Make sure that nonisolated(unsafe) works in all cases.
I made sure we match what we get without region isolation by turning off region
isolation in one of the test runs on the test for this.

There is one problem where for non-final classes with nonisolated(unsafe) var
fields, we currently do not properly squelch since I need to do more
infrastructure work. I am going to do that in the next commit.

rdar://128299305
2024-05-27 21:41:32 -07:00
Michael Gottesman
89a2cfce0b [region-isolation] Initialize TrackableValueState's regionInfo with a .none instead of a disconnected region.
The design change here is that instead of just initializing the regionInfo with
disconnected, we set it as .none and if we see .none, just return a newly
construct disconnected isolation region info when getIsolationRegionInfo() is
called.

This enables us to provide a setIsolationRegionInfo() helper for
RegionAnalysisValueMap::getTrackableValue that does not perform a merge. This is
important since for nonisolated(unsafe), we want to not have nonisolated(unsafe)
propagate through merging. So if we use merging to initialize the internal
regionInfo state of a SILIsolationInfo, we will never have a SILIsolationInfo
with that bit set since it will be lost in the merge. So we need some sort of
other assignment operator. Noting that we should only compute a value's
SILIsolationInfo once in RegionAnalysisValueMap before we cache it in the map,
it made sense to just represent it as an optional that way we can guarantee that
the regionInfo is only ever set exactly once by that routine.
2024-05-27 21:28:34 -07:00
Michael Gottesman
3f26d08ee4 [region-isolation] Add the ability in SILIsolationInfo to represent a disconnected value that is nonisolated(unsafe). 2024-05-27 21:25:44 -07:00
Michael Gottesman
e3e78ad6bb [sending] Change the internals of sending to be based around 'sending' instead of 'transferring'.
We still only parse transferring... but this sets us up for adding the new
'sending' syntax by first validating that this internal change does not mess up
the current transferring impl since we want both to keep working for now.

rdar://128216574
2024-05-16 12:20:45 -07:00
Michael Gottesman
50c2d678f2 [region-isolation] When inferring isolation for an argument, handle non-self isolated parameters as well as self parameters that are actor isolated.
As part of this I went through how we handled inference and rather than using a
grab-bag getActorIsolation that was confusing to use, I created split APIs for
specific use cases (actor instance, global actor, just an apply expr crossing)
that makes it clearer inside the SILIsolationInfo::get* APIs what we are
actually trying to model. I found a few issues as a result and fixed most of
them if they were small. I also fixed one bigger one around computed property
initializers in the next commit. There is a larger change I didn't fix around allowing function
ref/partial_apply with isolated self parameters have a delayed flow sensitive
actor isolation... this will be fixed in a subsequent commit.

This also fixes a bunch of cases where we were printing actor-isolated instead
of 'self' isolated.

rdar://127295657
2024-05-10 15:33:44 -07:00
Michael Gottesman
9bfb3b7ee7 [region-isolation] Some small gardening updates in preparation for the next commit.
Specifically, I added a few helper methods and improved the logging printing.
This all makes the next commit a more focused commit.
2024-05-10 15:33:44 -07:00
Michael Gottesman
2e5b3bc257 [region-isolation] Do not treat functions/class_methods that are isolated to a #isolated as being globally isolated to that.
Instead, we need to consider the isolation at the apply site.

rdar://126285681
rdar://125078448
2024-04-11 16:00:55 -07:00
Michael Gottesman
c9fe8ff935 [region-isolation] Eliminate unnecessary using TrackableValueID = Element.
Having two artificial typedefs for the same wrapped value is just confusing.
Better to just have one and make the code simpler to understand.
2024-04-11 15:41:18 -07:00
Michael Gottesman
beaab91376 [region-isolation] Add a small helper to TrackableValue to print it's isolation info.
I am finding that I am calling that a bunch so this just makes it a little more
convenient.
2024-04-11 15:41:18 -07:00
Michael Gottesman
ca8179aa7c [region-isolation] Track operand info in a separate map rather than inline in a TransferringOperand data structure.
This is backing out an approach that I thought would be superior, but ended up
causing problems.

Originally, we mapped a region number to an immutable pointer set containing
Operand * where the region was tranferred. This worked great for a time... until
I began to need to propagate other information from the transferring code in the
analysis to the actual diagnostic emitter.

To be able to do that, my thought was to make a wrapper type around Operand
called TransferringOperand that contained the operand and the other information
I needed. This seemed to provide me what I wanted but I later found that since
the immutable pointer set was tracking TransferringOperands which were always
newly wrapped with an Operand *, we actually always created new pointer
sets. This is of course wasteful from a memory perspective, but also prevents me
from tracking transferring operand sets during the dataflow since we would never
converge.

In this commit, I fix that issue by again tracking just an Operand * in the
TransferringOperandSet and instead map each operand to a state structure which
we merge dataflow state into whenever we visit it. This provides us with
everything we need to in the next commit to including a region -> transferring
operand set equality check in our dataflow equations and always converge.
2024-04-10 10:30:10 -07:00
Michael Gottesman
aee5a37d9d [region-isolation] Add isolation history support but do not wire it up to the checker.
This means that I added an IsolationHistory field to Partition. Just upstreaming
the beginning part of this work. I added some unittests to exercise the code as
well. NOTE: This means that I did need to begin tracking an
IsolationHistoryFactory and propagating IsolationHistory in the pass
itself... but we do not use it for anything.

A quick overview of the design.

IsolationHistory is the head of an immutable directed acyclic graph. It is
actually represented as an immutable linked list with a special node that ties
in extra children nodes. The users of the information are expected to get a
SmallVectorImpl and process those sibling nodes afterwards. The reason why we
use an immutable approach is that it fits well with the problem and saves space
since different partitions could be pointing at the same linked list
node. Operations occur on an isolation history by pushing/popping nodes. It is
assumed that the user will push nodes in batches with a sequence boundary at the
bottom of the addition which signals to stop processing nodes.

Tieing this together, each Partition within it contains an IsolationHistory. As
the PartitionOpEvaluator applies PartitionOps to Partition in
PartitionOpEvaluator::apply, the evaluator also updates the isolation history in
the partition by first pushing a SequenceBoundary node and then pushing nodes
that will undo the operation that it is performing. This information is used by
the method Partition::popHistory. This pops linked list nodes from its history,
performing the operation in reverse until it hits a SequenceBoundary node.

This allows for one to rewind Partition history. And if one stashes an isolation
history as a target, one can even unwind a partition to what its state was at a
specific transfer point or earlier. Once we are at that point, we can begin
going one node back at a time and see when two values that we are searching for
no longer are apart of the same region. That is a place where we want to emit a
diagnostic. We then process until we find for both of our values history points
where they were the immediate reason why the two regions merge.

rdar://123479934
2024-04-06 00:58:05 -07:00
Michael Gottesman
c2350dfe7e [region-isolation] Change SILIsolationInfo to only traffic in ActorIsolation instead of that or nominal type decls.
This should be NFC since the only case where I used this was with self... and I
found another way of doing that using the API I added in the previous commit.
2024-04-04 10:58:57 -07:00
Michael Gottesman
b19081daad [region-isolation] Change printing of values before dataflow to dump the full value state of the value instead of just the representative.
Just makes it easier to debug.
2024-03-25 20:17:26 -07:00
Michael Gottesman
c09b9f811b [region-isolation] Add a helper for getting an ApplyIsolationCrossing from an instruction.
I am making this specific API since I am going to make it so that
SILIsolationInfo::get(SILInstruction *) can infer isolation info from self even
from functions that are not apply isolation crossing points. For example, in the
following, we need to understand that test is main actor isolated and we
shouldn't emit an error.

```swift
@MainActor func test(_ x: NonSendable) {}

@OtherActor func doSomething() {
  let x = NonSendable()
  Task.init { @MainActor in print(x) }
  test(x)
}
```
2024-03-21 14:16:20 -07:00
Michael Gottesman
5b8171240d [region-isolation] Rename IsolationRegionInfo -> SILIsolationInfo.
That is really what IsolationRegionInfo is.
2024-03-21 14:16:20 -07:00
Michael Gottesman
f796f11c97 [region-isolation] Rename ValueIsolationRegionInfo -> IsolationRegionInfo. 2024-03-18 12:24:34 -07:00
Michael Gottesman
b23b791c1b [region-isolation] Move ValueIsolationRegionInfo from RegionAnalysis.h -> PartitionUtils.h.
I am going to need this so that I can use it when evaluating partition
ops. Specifically, so I can when I find two values that do not merge correctly,
emit an error.
2024-03-18 12:13:30 -07:00
Michael Gottesman
258992cc71 Add an include of variant. 2024-03-11 00:07:52 -07:00
Michael Gottesman
806cd7940e [region-isolation] Fix actor isolated parameters to get an actor isolated error instead of a task isolated error.
Now that we actually know the region that non transferrable things belong to, we
can use this information to give a better diagnostic here.

A really nice effect of this is that we now emit that actor isolated parameters
are actually actor isolated instead of task isolated.
2024-03-10 22:08:40 -07:00
Michael Gottesman
b2c85a8294 [region-isolation] Rather than tracking task isolated values via a separate non-transferrable array... just track it by using ValueIsolationRegionInfo on a value.
In a subsequent commit, this is going to let me begin handling parameters with
actor regions in a nice way (and standardize all of the errors).

This is meant to be a refactoring commit that uses the current tests in tree to
make sure I did it correctly, so no tests need to be updated.
2024-03-10 22:08:40 -07:00
Michael Gottesman
93844934f9 [region-isolation] Begin tracking region information instead of just whether a value is actor derived.
To keep this as an NFC commit, I only modeled initially actor isolated using
this. I am going to make it so that we properly treat global actor isolated
values as actor isolated/etc in a subsequent commit.
2024-03-10 22:08:40 -07:00
Michael Gottesman
f06244f397 [region-isolation] Take into account that Swift's RPO order doesn't include blocks that are dead.
When we run RegionAnalysis, since it uses RPO order, we do not visit dead
blocks. This can create a problem when we emit diagnostics since we may merge in
a value into the region that was never actually defined. In this patch, if we
actually visit the block while performing dataflow, I mark a bit in its state
saying that it was live. Then when we emit diagnostics, I do not visit blocks
that were not marked live.

rdar://124042351
2024-03-04 17:09:51 -08:00
Michael Gottesman
12e4e8d6e8 [region-isolation] Add support for all pack instructions. 2024-02-02 14:50:10 -08:00
Doug Gregor
e96dcac7b4 Make sure we always have a SILFunction for region-based isolation checking
Some values (such as `undef`) might not be associated with a function.
2024-01-25 16:04:48 -08:00
Michael Gottesman
9303c40162 [region-isolation] Teach region isolation that assigning into a transferring parameter is a transfer of the value.
The specific semantics is if we assign into a transferring parameter's field,
then we "merge" src's value into the transferring parameter, so we
conservatively leave the region of the transferring parameter alone. If we
assign over the entire transferring parameter, we perform an assign fresh since
any value that used to be in the transferring parameter cannot reference
anything in its new value since they are all gone.
2024-01-18 13:20:28 -08:00
Michael Gottesman
053e30d46d [region-isolation] Make sure not to run RegionAnalysis on functions that we do not support.
Before this commit, this was done at the beginning of TransferNonSendable. I
thought that those checks would be sufficient to ensure that
RegionAnalysisFunctionInfo was not created for functions that we do not
support. Turns out when we perform certain forms of verification, we force all
function analyses to be created for all functions meaning that we would create a
RegionAnalysisFunctionInfo for such an unsupported function causing us to hit
asserts.

In this commit, I move the check to whether or not we support a function into
RegionAnalysisFunctionInfo itself and use that to determine if we should run
TransferNonSendable. This additionally allows me to change
RegionAnalysisFunctionInfo so that one can construct one for an unsupported
function... as long as one doesn't actually touch any of its methods. If one
does, I put in an assert so we will know that operator error has occured.
2024-01-10 13:41:12 -08:00
Michael Gottesman
a9dc4ed6cc [region-isolation] Refactor out the Region analysis from TransferNonSendable.
NFCI. This is just a pure refactor of the analysis part of TransferNonSendable
into a separate SIL level analysis so it can be reused by other passes.

The reason that I am committing this earlier is that I am working concurrently
on other patches that change TransferNonSendable itself and I want to avoid
issues when rebasing those patches. Getting this patch into tree earlier avoids
that.

This is in preparation for adding a new flow sensitive initialization pass that
combines region based analysis with the current flow sensitive isolation's
diagnostic emitter. The idea is that we want to preserve the diagnostics from
that pass rather than try to make our own as an initial step.
2024-01-10 13:41:12 -08:00