Commit Graph

965 Commits

Author SHA1 Message Date
Michael Gottesman
ae797d43e9 [region-isolation] Propagate through the whole source operand instead of just the representative of the source value when constructing assign and merge.
This will let me know the exact source operand used instead of the source value
representative. This will ensure that the name associated with the diagnostic is
not of the representative value, but the actual value that was the source of the
assign.

This is an NFCI commit that is an algebraic refactor.
2024-07-18 21:28:22 -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
75152e7834 [region-isolation] Teach region isolation how to handle cases where due to compiler bugs, we have a function isolated to self without an isolated parameter.
Closures generally only inherit actor instance isolation if they directly
capture state from the actor instance. In this case, for some reason that is not
true, so we hit an assert that assumes that we will only see a global actor
isolated isolation.

Region Isolation should be able to handle code even if the closure isolation
invariant is violated by the frontend. So to do this, I am introducing a new
singleton actor instance to represent the isolation of a defer or closure
created in an actor instance isolated method. The reason why I am using a
singleton is that closures and defer are not methods so we do not actually know
which parameter is 'self' since it isn't in the abi. But we still need some
value to represent the captured values as belonging to. To square this circle, I
just did what we have done in a similar situation where we did not have a value:
(ActorAccessorInit). In that case, we just use a sentinel to represent the
instance (NOTE: This is represented just via a kind so ActorInstances that are
operator== equal will not &value equal since we are just using a kind).
2024-07-18 21:28:22 -07:00
Michael Gottesman
0b42f35f6a Add operator<<(llvm::raw_ostream &os, SILIsolationInfo|SILDynamicMergedIsolationInfo.
Just slicing off a nice addition. These already had appropriate print methods so
this commit just exposes the print API in the raw_ostream format.
2024-07-18 21:28:22 -07:00
Michael Gottesman
62c6de7713 [region-isolation] Reuse ActorInstance::lookThroughInsts when computing the actor instance for SILIsolationInfo purposes.
We are already using this routine in other parts of TransferNonSendable to
ensure that we look through common insts that SILGen inserts that do not change
the actual underlying actor instance that we are using. In this case, I added
support for casts, optional formation, optional extraction, existential ref
initialization.

As an example of where this came up is the following test case where we fail to
look through an init_existential_ref.

```swift
public actor MyActor {
  private var intDict: [Int: Int] = [:]

  public func test() async {
    await withTaskGroup(of: Void.self) { taskGroup in
      for (_, _) in intDict {}
      await taskGroup.waitForAll() // Isolation merge failure happens here
    }
  }
}
```

I also added the ability to at the SIL level actual test out this merge
condition using the analysis test runner. I used this to validate that this
functionality works as expected in a precise way.

rdar://130113744
2024-07-06 23:02:11 -07:00
Michael Gottesman
6129f4e833 [region-isolation] Print out SILIsolationInfo's options on all printing routines for SILIsolationInfo.
Before we wouldn't print them in all situations and even more so a few of the
printing routines did not have it at all. This just adds a centralized
SILIsolationInfo::dumpOptions() method and then goes through all of the printing
helpers and changes them to use them as appropriate.
2024-07-06 23:02:11 -07:00
Michael Gottesman
0c254807bf [region-isolation] Allow for unapplied isolated parameter ownership.
Given a function or a partial_apply with an isolated parameter, we do not know
immediately what the actual isolation is of the function or partial_apply since
we do not know which instance will be applied to the function or partial_apply.

In this commit, I introduce a new bit into SILIsolationInfo that tracks this
information upon construction and allows for it to merge with ownership that has
the appropriate type and a specific instance. Since the values that created the
two isolations, will be in the same region this should ensure that the value is
only ever in a flow sensitive manner in a region with only one actor instance
(since regions with isolations with differing actor instances are illegal).
2024-07-06 23:02:11 -07:00
Erik Eckstein
3e750f9f1c SimplifyCFG: Fix a missing borrowed-from when doing jump threading
Fixes a compiler crash

rdar://129805179
2024-07-03 13:52:52 +02:00
Michael Gottesman
6fe749626f [region-isolation] Add 'inout sending' diagnostics.
Specifically:

1. We error now if one transfers an 'inout sending' parameter and does not
reinitialize it before the end of the function.

2. We error now if one merges an 'inout sending' parameter into an actor
isolated region and do not reinitialize it with a non-actor isolated value
before the end of the function.

rdar://126303739
2024-07-02 16:21:44 -07:00
Michael Gottesman
c01f551ebe [transfer-non-sendable] Teach SILIsolationInfo how to handle "look through instructions" when finding actor instances.
From the perspective of the IR, we are changing SILIsolationInfo such that
inferring an actor instance means looking at equivalence classes of values where
we consider operands to look through instructions to be equivalent to their dest
value. The result is that cases where the IR maybe puts in a copy_value or the
like, we consider the copy_value to have the same isolation info as using the
actor directly. This prevents a class of crashes due to merge failings. Example:

```swift
actor MyActor {
  init() async {

  init(ns: NonSendableKlass) async {
    self.k = NonSendableKlass()
    self.helper(ns)
  }

  func helper(_ newK: NonSendableKlass) {}
}
```

Incidently, we already had a failing test case from this behavior rather than
the one that was the original genesis. Specifically:

1. If a function's SILIsolationInfo is the same as the isolation info of a
SILValue, we assume that no transfer actually occurs.

2. Since we were taking too static of a view of actor instances when comparing,
we would think that a SILIsolationInfo of a #isolation parameter to as an
argument would be different than the ambient's function isolation which is also
that same one. So we would emit a transfer non transferrable error if we pass in
any parameters of the ambient function into another isolated function. Example:

```swift
actor Test {

  @TaskLocal static var local: Int?

  func withTaskLocal(isolation: isolated (any Actor)? = #isolation,
                     _ body: (consuming NonSendableValue, isolated (any Actor)?) -> Void) async {
    Self.$local.withValue(12) {

      // We used to get these errors here since we thought that body's isolation
      // was different than the body's isolation.
      //
      //  warning: sending 'body' risks causing data races
      //  note: actor-isolated 'body' is captured by a actor-isolated closure...
      body(NonSendableValue(), isolation)
    }
  }
}
```

rdar://129400019
2024-06-24 18:16:11 -07:00
Mishal Shah
0fc5035ee1 Merge pull request #74574 from eeckstein/fix-devirtualizer 2024-06-22 07:53:17 -07:00
Erik Eckstein
9cb011322d SILOptimizer: add a utility to check if a generic nominal type's layout is dependent on its generic parameters
This is usually the case. Some examples, where they layout is _not_ dependent:
```
   struct S<T> {
     var x: Int // no members which depend on T
   }

   struct S<T> {
     var c: SomeClass<T> // a class reference does not depend on the layout of the class
   }
```
2024-06-21 17:28:33 +02:00
Michael Gottesman
b786c60e00 [region-isolation] Squelch errors from convert_function uses from a Sendable function.
We view the conversion from a Sendable to a non-Sendable function via
convert function to produce a new fresh sendable value. We should
squelch that error.
2024-06-21 02:24:03 -07:00
Michael Gottesman
6d15e41a2f Merge pull request #74123 from gottesmm/pr-9e8378fdeee3204a34f48ea8d2ff8f0be40a4674
[region-isolation] Make store_borrow a store operation that does not require
2024-06-12 19:43:17 -07:00
Michael Gottesman
bd472b12be [region-isolation] Make store_borrow a store operation that does not require.
TLDR:

The reason why I am doing this is it ensures that temporary store_borrow that we
create when materializing a value before were treated as uses. So we would error
on this:

```swift
@MainActor func transferToMain<T>(_ t: T) async {}

func test() async {
  let x = NonSendableKlass()
  await transferToMain(x)
  await transferToMain(x)
}
```

----

store_borrow is an instruction intended to be used to initialize temporary
alloc_stack with borrows. Since it is a temporary, we do not want to error on
the temporaries initialization... instead, we want to error on the use of the
temporary parameter.

This is achieved by making it so that store_borrow still performs an
assign/merge, but does not require that src/dest be alive. So the regions still
merge (yielding diagnostics for later uses).

It also required me to make it so that PartitionOp::{Assign,Merge} do not
require by default. Instead, we want the individual operations to always emit a
PartitionOp::Require explicitly (which they already did).

One thing to be aware of is that when it comes to diagnostics, we already know
how to find a temporaries original value and how to handle that. So this is the
last part of making store_borrow behave nicely.

rdar://129237675
2024-06-12 15:01:38 -07:00
nate-chandler
23915e8075 Merge pull request #74109 from nate-chandler/rdar113142446
[ConsumeObjectChecker] End lifetimes at consumes.
2024-06-04 18:17:36 -07:00
Nate Chandler
04ffbe8a6e [NFC] OwnedValueCan: Add support for end markers.
Enhance the utility with the ability to end lifetimes of lexical values
at indicated instructions, overriding the usual behavior of maintaining
such lifetimes' previous endpoints (modulo non-deinit-barrier
instructions).
2024-06-03 15:45:29 -07:00
Nate Chandler
dc9baba586 [NFC] OwnedValueCan: Extracted extension visitor.
Parameterized `extendUnconsumedLiveness` on the ends of interest and the
action to take when visiting the extended boundary and named the
resulting function `visitExtendedUnconsumedBoundary`.
2024-06-03 15:45:26 -07:00
Nate Chandler
aa3cbe0c67 [NFC] OwnedValueCan: Typed maximizeLifetime. 2024-06-03 15:45:23 -07:00
Nate Chandler
df008924da [NFC] OwnedValueCan: Typed pruneDebugMode. 2024-06-03 15:45:20 -07:00
Michael Gottesman
74ac12c9d2 [region-isolation] Make temporary alloc_stack that we form for returning values from a non-final class field take on the class method's isolation.
The reason why we are doing this is that otherwise, we have that the alloc_stack
formed for the result is disconnected and despite the fact that we merge it into
the actor region of the class method, we do not have that the alloc_stack
specifically is marked when we attempt to squelch Please.

This patch fixes that problem by detecting when an alloc_stack is being used as
a temporary for an out parameter and makes the alloc_stack initially isolated as
appropriate. It only does this in the specific cases where we can pattern match
it which in my limited testing has handled everything.
2024-05-28 17:31:08 -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
1bef011e48 [region-isolation] Add the ability for the analysis to emit "unknown error" partition ops in case we detect a case we cannot pattern match.
DISCUSSION: The analysis itself is unable to emit errors. So we achieve the same
functionality by in such cases emitting a partition op that signals to our user
that when they process that partition op they should emit an "unknown pattern"
error at the partition op's instructions.

I have wanted this for a long time, but I never got around to it.
2024-05-27 21:42:04 -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
4b29089f8b [region-isolation] Add a little bit of documentation to ActorInstance. 2024-05-27 21:21:52 -07:00
Michael Gottesman
3597006200 [region-isolation] Move SILIsolationInfo out of PartitionUtils into its own header/cpp file.
SILIsolationInfo is conceptually a layer that composes with PartitionUtils so it
makes sense for it to be in a different file.
2024-05-27 20:37:08 -07:00
Ellie Shin
5ccc4cd394 SIL function can be serialized with different kinds: [serialized] or
[serialized_for_package] if Package CMO is enabled. The latter kind
allows a function to be serialized even if it contains loadable types,
if Package CMO is enabled. Renamed IsSerialized_t as SerializedKind_t.

The tri-state serialization kind requires validating inlinability
depending on the serialization kinds of callee vs caller; e.g. if the
callee is [serialized_for_package], the caller must be _not_ [serialized].
Renamed `hasValidLinkageForFragileInline` as `canBeInlinedIntoCaller`
that takes in its caller's SerializedKind as an argument. Another argument
`assumeFragileCaller` is also added to ensure that the calle sites of
this function know the caller is serialized unless it's called for SIL
inlining optimization passes.

The [serialized_for_package] attribute is allowed for SIL function, global var,
v-table, and witness-table.

Resolves rdar://128406520
2024-05-23 15:53:02 -07:00
Slava Pestov
a1462ef184 SIL: Promote removeDeadBlock() from SILOptimizer to a method on SILBasicBlock 2024-05-21 13:52:58 -04:00
Michael Gottesman
fe2dc11cb9 [region-isolation] When computing isolation for isolated parameters, use getAnyActor instead of getNominalOrBoundGenericNominal().
This ensures that we can properly compute isolation for generic types that
conform to AnyActor.

I found this by playing with test cases from the previous commit. We would not
find an actor type for the actor instance isolation and would fall back along an
incorrect path.

rdar://128021548
2024-05-13 13:43:51 -07:00
Michael Gottesman
35a5a2546a [region-isolation] Fix isolation inference for init accessors.
rdar://127844737
2024-05-10 15:33:44 -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
Kshitij
a7f8d6c647 [Autodiff] Adds bridging code in preparation for the Swift based Autodiff closure-spec pass 2024-05-02 09:14:05 -07:00
Emil Pedersen
a31d15c438 Merge pull request #72964 from Snowy1803/mem2reg-salvage-store
[DebugInfo] Salvage all stores
2024-04-25 16:52:37 -07:00
Emil Pedersen
523a769e34 [DebugInfo] [Mem2Reg] Move debug info promotion to salvageDebugInfo 2024-04-23 13:12:21 -07:00
Michael Gottesman
1113a61f8d [region-isolation] Stub out getIsolationInfo so the mocking unit tests succeed.
Specifically, the partition unit tests pass in bogus instructions/operands so we
cannot call /any/ methods on them. So I created stubed out helpers on the
evaluator that in the case of mocking just return a default initialized
SILIsolationInfo().
2024-04-19 15:28:48 -07:00
Michael Gottesman
0e21c58619 [region-isolation] Disconnected regions in global actor isolated function are not accessible again after call to nonisolated async function
I also fixed the nonisolated(unsafe) issue.

rdar://126667934
rdar://126174959
2024-04-19 13:52:58 -07:00
Michael Gottesman
3c29997cd1 [region-isolation] Out of an abundance of caution convert isActor -> isAnyActor(). 2024-04-17 13:07:56 -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
51ef67d7df [variable-name-utils] Refactor inferName/inferNameAndRoot helpers onto VariableNameInferrer.
I have been using these in TransferNonSendable and they are useful in terms of
reducing the amount of code that one has to type to use this API. I am going to
need to use it in SILIsolationInfo, so it makes sense to move it into
SILOptimizer/Utils.

NFCI.
2024-04-11 15:41:18 -07:00
Michael Gottesman
d8f39f70d9 [region-isolation] Begin tracking in SILIsolationInfo the actorInstance that a value is isolated to if we are dealing with an actor instance.
This will let us distinguish in between values derived from two actor instances
of the same type and to emit better errors.
2024-04-11 15:41:18 -07:00
Michael Gottesman
b407b21e9a [region-isolation] Finish moving isolation computation into SILIsolationInfo::get. 2024-04-11 15:41:18 -07:00
Michael Gottesman
513ab78602 [region-isolation] Move SILIsolationInfo determining code for ref_element_addr and global_addr onto SILIsolationInfo and call that instead. 2024-04-11 15:41:18 -07:00
Michael Gottesman
b80faee2a7 [region-isolation] Rename Partition::{fresh_label,freshLabel}. 2024-04-11 15:41:18 -07:00
Michael Gottesman
df4fb64ea1 Merge pull request #72955 from gottesmm/rdar126170014
[region-isolation] Include the region -> transferring operand map in the dataflow convergence.
2024-04-10 17:20:55 -07:00
Michael Gottesman
d2bdec2acd [region-isolation] Include the region -> transferring operand map in the dataflow convergence.
The reason why I am doing this is that really we are running two different
dataflow equations at the same time... one for propagating tracking transferring
sets and the other for propagating regions. Since at the source level the two
dataflow problems are very interrelated, I was unable to come up with an example
where we fail to iterate because of this, but I would like to be sure that we do
not hit one, so I am fixing this here.

rdar://126170014
2024-04-10 10:36:42 -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
Erik Eckstein
e14c1d1f62 SIL, Optimizer: update and handle borrowed-from instructions
Compute, update and handle borrowed-from instruction in various utilities and passes.
Also, used borrowed-from to simplify `gatherBorrowIntroducers` and `gatherEnclosingValues`.
Replace those utilities by `Value.getBorrowIntroducers` and `Value.getEnclosingValues`, which return a lazily computed Sequence of borrowed/enclosing values.
2024-04-10 13:38:10 +02:00