Commit Graph

1031 Commits

Author SHA1 Message Date
Michael Gottesman
904ebc6784 [send-non-sendable] Recurse to the full underlying value computation instead of just the object one when computing the underlying object of an address.
Otherwise, depending on the exact value that we perform the underlying look up
at... we will get different underlying values. To see this consider the
following SIL:

```sil
  %1 = alloc_stack $MyEnum<T>
  copy_addr %0 to [init] %1
  %2 = unchecked_take_enum_data_addr %1, #MyEnum.some!enumelt
  %3 = load [take] %2
  %4 = project_box %3, 0
  %5 = load_borrow %4
  %6 = copy_value %5
```

If one were to perform an underlying object query on %4 or %3, one would get
back an underlying object of %1. In contrast, if one performed the same
operation on %5, then one would get back %3. The reason why this happens is that
we first see we have an object but that it is from a load_borrow so we need to
look through the load_borrow and perform the address underlying value
computation. When we do that, we find project_box to be the value. project_box
is special since it is the only address base we ever look through since from an
underlying object perspective, we want to consider the box to be the underlying
object rather than the projection. So thus we see that the result of the
underlying address computation is that the underlying address is from a load
[take]. Since we then pass in load [take] recursively into the underlying value
object computation, we just return load [take]. In contrast, the correct
behavior is to do the more general recurse that recognizes that we have a load
[take] and that we need to look through it and perform the address computation.

rdar://151598281
2025-05-22 10:11:40 -07:00
Michael Gottesman
010443c854 [rbi] Treat a partial_apply as nonisolated(unsafe) if all of its captures are nonisolated(unsafe).
rdar://144111950
2025-05-17 12:21:21 -07:00
Erik Eckstein
bc4310b0eb Optimizer: simplify unchecked_addr_cast for vectors
Reimplement the simplification in swift and add a new transformation:
```
  %1 = unchecked_addr_cast %0 : $*Builtin.FixedArray<N, Element> to $*Element
```
->
```
  %1 = vector_base_addr %0 : $*Builtin.FixedArray<N, Element>
```
2025-05-12 19:25:12 +02:00
Erik Eckstein
a38db6439a SIL: add the vector_base_addr instruction
It derives the address of the first element of a vector, i.e. a `Builtin.FixedArray`, from the address of the vector itself.
Addresses of other vector elements can then be derived with `index_addr`.
2025-05-12 19:24:31 +02:00
Meghana Gupta
35d62a4a36 Introduce end_cow_mutation_addr instruction 2025-04-30 13:39:45 -07:00
Anthony
c9b17383c8 Grammatical corrections for compound modifiers 2025-04-24 09:21:32 +02:00
Michael Gottesman
5c4ff42d77 Merge pull request #80863 from gottesmm/pr-83b58369a3d1ab17d10b9f0f2792801d804d6f81
[rbi] When checking for partial apply reachability of a value at a user, include the user itself in case the user is the actual partial apply
2025-04-17 16:54:55 -07:00
Michael Gottesman
6eee52fb01 [rbi] When checking for partial apply reachability of a value at a user, include the user itself in case the user is the actual partial apply
The specific issue was when we were walking instructions looking to see if there
was a partial apply escaping instruction, we were not including the user
itself. That means that if the user was the partial apply escaping instruction,
we would return that no escape occured.

rdar://149414471
2025-04-17 12:26:34 -07:00
Michael Gottesman
d5e89723b8 [rbi] Remove some unnecessary copying when constructing partition ops.
Just a small efficiency win that @rjmccall and I noticed when I was bringing him
up on rbi.
2025-04-16 11:46:40 -07:00
Michael Gottesman
cda4ebaca0 [rbi] Rename functionArgPartition -> initialEntryBlockPartition.
While bringing up @rjmccall on rbi, our discussions showed that the name
functionArgPartition was misleading to someone who hadn't worked on the pass
before. It became clear that initialEntryBlockPartition would be a better name
that would make it clearer/easy to understand.
2025-04-16 11:46:40 -07:00
Michael Gottesman
23b6937cbc [rbi] Make it so that we correctly do not error on uses of Sendable values that are projected from non-Sendable bases.
Specifically, we only do this if the base is a let or if it is a var but not
captured by reference.

rdar://149019222
2025-04-10 14:53:56 -07:00
Michael Gottesman
a045c9880a [rbi] Add the ability to add flags to PartitionOp.
I am doing this so I can mark requires as being on a mutable non-Sendable base
from a Sendable value.

I also took this as an opportunity to compress the size of PartitionOp to be 24
bytes instead of 40 bytes.
2025-04-10 14:53:56 -07:00
Michael Gottesman
6d8b9b048a [rbi] Implement support for non-Sendable base values.
There are a few major changes here:

1. We now return a TrackableValue from getTrackableValue() if we have either a
non-Sendable value or a non-Sendable base. This means that we /will/ return
TrackableValues that may have a Sendable value or a Sendable base. To make it
easier to work with this, I moved the isSendable check and the do I have a base
check into PartitionOpBuilder. So, most of the actual code around emitting
values does not need to reason about this. They can just call addRequire or
addSend and pass in either TrackableValue::value or TrackableValue::base without
needing to check if the former is non-Sendable or if the latter is non-Sendable
and non-nil.

2. I searched all of the places where we were grabbing trackable values and
inserted require checks for the base value as appropriate.

Both of these together have prevented the code from becoming too heavy.

This fixes https://forums.swift.org/t/lets-debug-missing-rbi-data-race-diagnostics/78910

rdar://149019222
2025-04-10 14:53:40 -07:00
Michael Gottesman
c846c2279e [rbi] Refactor getUnderlyingTrackedValue so that for addresses we return both a value and a base in certain situations.
Previously, when we saw any Sendable type and attempted to look up an underlying
tracked value, we just bailed. This caused an issue in situations like the
following where we need to emit an error:

```swift
func test() {
  var x = 5
  Task.detached { x += 1 }
  print(x)
}
```

The problem with the above example is that despite value in x being Sendable,
'x' is actually in a non-Sendable box. We are passing that non-Sendable box into
the detached task by reference causing a race against the read from the
non-Sendable box later in the function. In SE-0414, this is explicitly banned in
the section called "Accessing Sendable fields of non-Sendable types after weak
transferring". In this example, the box is the non-Sendable type and the value
stored in the box is the Sendable field.

To properly represent this, we need to change how the underlying object part of
our layering returns underlying objects and vends TrackableValues to the actual
analysis for addresses. NOTE: We leave the current behavior alone for SIL
objects.

By doing this, in situations like the above, despite have a Sendable value (the
integer), we are able to ensure that we require that the non-Sendable box
containing the integer is not used after we have sent it into the other Task
despite us not actually using the box directly.

Below I describe the representation change in more detail and describe the
various cases here. In this commit, I only change the representation and do not
actually use the new base information. I do that in the next commit to make this
change easier for others to read and review. I made sure that change was NFC by
leaving RegionAnalysis.cpp:727 returning an optional.none if the value found was
a Sendable value.

----

The way we modify the representation is that we instead of just returning a
single TrackedValue return a pair of tracked values, one for the base and one
for the "value". We return this pair in what is labeled a
"TrackableValueLookupResult":

```c++
struct TrackableValueLookupResult {
  TrackableValue value;

  std::optional<TrackableValue> base;

  TrackableValueLookupResult(TrackableValue value)
    : value(value), base() {}
  TrackableValueLookupResult(TrackableValue value, TrackableValue base)
    : value(value), base(base) {}
};
```

In the case where we are accessing a projection path out of a non-Sendable type
that contains all non-Sendable fields, we do not do anything different than we
did previously. We just walk up from use->def until we find the access path base
which we use as the representative of the leaf of the chain and return
TrackableValueLookupResult(access path base).

In the case where we are accessing a Sendable leaf type projected from a
non-Sendable base, we store the leaf type as our value and return the actual
non-Sendable base in TrackableValueLookupResult. Importantly this ensures that
even though our Sendable value will be ignored by the rest of the analysis, the
rest of the analysis will ensure that our base is required if our base is a var
that had been escaped into a closure by reference.

In the case where we are accessing a non-Sendable leaf type projected from a
Sendable type (which we may have continued to be projected subsequently out of
additional Sendable types or a non-Sendable type), we make the last type on the
projection path before the Sendable type, the value of the leaf type. We return
the eventual access path base as our underlying value base. The logic here is
that since we are dealing with access paths, our access path can only consist of
projections into a recursive value type (e.x.: struct/tuple/enum... never a
class). The minute that we hit a pointer or a class, we will no longer be along
the access path since we will be traversing a non-contiguous piece of
memory (consider a class vs the class's storage) and the traversal from use->def
will stop. Thus, we know that there are only two ways we can get a field in that
value type to be Sendable and have a non-Sendable field:

1. The struct can be @unchecked Sendable. In such a case, we want to treat the
leaf field as part of its own disconnected region.

2. The struct can be global actor isolated. In such a case, we want to treat the
leaf field as part of the global actor's region rather than whatever actor.

The reason why we return the eventual access path base as our tracked value base
is that we want to ensure that if the var value had been escaped by reference,
we can require that the var not be sent since we are going to attempt to access
state from the var in order to get the global actor guarded struct that we are
going to attempt to extract our non-Sendable leaf value out of.
2025-04-10 10:01:18 -07:00
Michael Gottesman
8f458cd029 [rbi] Add the ability to test in SIL RegionAnalysisValueMap::getTrackableValue().
I also added some basic tests of its functionality. I am doing this in
preparation for making some more invasive changes to getTrackableValue and I
want to be able to test it out very specifically in SIL.
2025-04-01 12:33:46 -07:00
Michael Gottesman
5499734ed4 [rbi] Fix an iterator invalidation issue.
Due to compile time issues, I added a cache into
getUnderlyingTrackedValue(). This caused an iterator invalidation issue when we
recursed in the case when we had an underlying object since we would recurse
into getUnderlyingTrackedValue() instead of getUnderlyingTrackedValueHelper()
potentially causing us to cache another value and thus causing the underlying
DenseMap to expand. Now we instead just call getUnderlyingTrackedValueHelper()
so that we avoid the invalidation issue. This may cause us to use slightly more
compile time but we are still only ever going to compute the underlying value
once for any specific value.
2025-04-01 09:01:30 -07:00
Michael Gottesman
98984a2678 [rbi] Remove a dead field, rename a class, and add some comments.
Specifically,

1. UseDefChainVisitor::actorIsolation is dead. I removed it to prevent any
confusion/thoughts that it actually found isolation. I also removed it from
UnderlyingTrackedValue since that was the only place where we were using it. I
left UnderlyingTrackedValue there in case I need to add additional state there
in the future.

2. Now that UseDefChainVisitor is only used to find the base of a value (while
not looking through Sendable addresses), I renamed it to
AddressBaseComputingVisitor.

3. I renamed UseDefChainVisitor::isMerge to isProjectedFromAggregate. That is
actually what we use it for. I also added a comment explaining what it is used
for.
2025-04-01 08:48:08 -07:00
Michael Gottesman
69ee33a25a Fix require operand to also include trackableDest. 2025-03-31 18:04:50 -07:00
Andrew Trick
e7000e4668 SIL: Add mark_dependence_addr 2025-03-25 23:02:42 -07:00
Pavel Yaskevich
41c88f864a [SILOptimizer] Turn "is self-recursive" check into analysis
The body of a function has to be re-analyzed for every call
site of the function, which is very expensive and if the
body is not changed would produce the same result.

This takes about ~10% from swift-syntax overall build time
in release configuration.
2025-03-24 00:25:13 -07:00
Doug Gregor
25e1a00dbe [Region analysis] Remove an assertion that doesn't always hold
It appears that we can end up breaking this assertion when inlining
SIL from modules with strict concurrency enabled into modules that
don't. That's not a assertion-worth condition.
2025-03-10 11:19:16 -07:00
Meghana Gupta
b7952284eb Merge pull request #79644 from meg-gupta/arraypropoptfix
Fix hoisting array semantics call with non-dominant self in ossa
2025-02-27 10:40:23 -08:00
Meghana Gupta
199482a623 Fix hoisting array semantics call with non-dominant self
If we have a self value that does not dominate loop preheader,
and the array semantics call does not consume the self value,
that means there will be instructions that consume the self value
with the loop.

In ossa, we cannot hoist such semantic calls because there is no
support for creating destroys for them in the preheader.
Add a bailout to avoid the ownership error.

rdar://145673368
2025-02-26 12:11:59 -08:00
Michael Gottesman
dab324080b [rbi] Make convert_escape_to_no_escape and convert_function lookthrough if their result/operand is non-Sendable.
In these cases, we want to lookthrough so we propagate through
nonisolated(unsafe) and make it easier to discover that we are processing
keypaths (the reason I am making this change).
2025-02-25 08:39:39 -08:00
Michael Gottesman
b286373631 [rbi] Make a confusing comment clearer.
From talking with @dgregor, it became clear that this comment was easily
interpreted as saying that AssignFresh always introduced a disconnected value...
which is not the case. Instead, AssignFresh just introduces a new value that
could have any form of isolation. The actual isolation of the value is assigned
via tryToTrackValue and eventually SILIsolationInfo::get().
2025-02-24 10:09:32 -08:00
Doug Gregor
a32470f14a Correct the region isolation rule for metatype extraction instructions
Now that metatypes might not be Sendable, we need the Assign rule for
operations that produce the metatype of a value or existential.
2025-02-14 15:33:57 -08:00
Doug Gregor
37d71f362e Add StrictSendableMetatypes to require Sendable requirements on metatypes
Introduce a new experimental feature StrictSendableMetatypes that stops
treating all metatypes as `Sendable`. Instead, metatypes of generic
parameters and existentials are only considered Sendable if their
corresponding instance types are guaranteed to be Sendable.

Start with enforcing this property within region isolation. Track
metatype creation instructions and put them in the task's isolation
domain, so that transferring them into another isolation domain
produces a diagnostic. As an example:

    func f<T: P>(_: T.Type) {
      let x: P.Type = T.self
      Task.detached {
        x.someStaticMethod() // oops, T.Type is not Sendable
      }
    }
2025-02-12 20:21:57 -08:00
Erik Eckstein
e0b4f71af6 SIL: remove the alloc_vector instruction
It's not needed anymore, because the "FixedArray" experimental feature is replaced by inline-arrays.
2025-02-12 10:51:14 +01:00
Erik Eckstein
83aaccc188 remove the "array.copy_into_vector" array-semantic
It's not needed anymore, because the "FixedArray" experimental feature is replaced by inline-arrays.
2025-02-12 10:51:14 +01:00
Arnold Schwaighofer
ed32270d72 Merge pull request #79191 from aschwaighofer/access_enforcement_fixes
AccessEnforcement: Fix analysis to include mayReleases as potentially executing unknown code
2025-02-10 16:57:31 -08:00
Arnold Schwaighofer
7a251af60c AccessEnforcement: Fix analysis to include mayReleases as potentially
executing unknown code

This means we have to claw back some performance by recognizing harmless
releases.

Such as releases on types we known don't call a deinit with unknown
side-effects.

rdar://143497196
rdar://143141695
2025-02-07 15:10:13 -08:00
Erik Eckstein
319258fea2 Optimizer: Remove the TypeExpansionAnalysis
It was used in the old redundant-load- and redundant-store-elimination passes which were replaced by new implementations.
TypeExpansionAnalysis is not used anymore.
2025-02-07 11:55:27 +01:00
Michael Gottesman
7e350bb4ce Revert "[concurrency] Add Concurrent/ConcurrentUnsafe and use it instead of ActorIsolation::Nonisolated."
This reverts commit 0cb64638d0.
2025-02-06 14:05:06 -08:00
Michael Gottesman
0cb64638d0 [concurrency] Add Concurrent/ConcurrentUnsafe and use it instead of ActorIsolation::Nonisolated.
This is just the first part of a larger transition.
2025-02-03 10:56:06 -08:00
Erik Eckstein
631bee446a BasicCalleeAnalysis: don't crash if the bca argument to isDeinitBarrier is null
Some clients don't provide a callee analysis to this API.
Don't crash in this case.
2025-01-28 09:15:47 +01:00
Erik Eckstein
3ec5d7de24 SIL: replace the is_escaping_closure instruction with destroy_not_escaped_closure
The problem with `is_escaping_closure` was that it didn't consume its operand and therefore reference count checks were unreliable.
For example, copy-propagation could break it.
As this instruction was always used together with an immediately following `destroy_value` of the closure, it makes sense to combine both into a `destroy_not_escaped_closure`.
It
1. checks the reference count and returns true if it is 1
2. consumes and destroys the operand
2025-01-24 19:23:27 +01:00
Michael Gottesman
7ae56aab2e [sil] Add a new instruction ignored_use.
This is used for synthetic uses like _ = x that do not act as a true use but
instead only suppress unused variable warnings. This patch just adds the
instruction.

Eventually, we can use it to move the unused variable warning from Sema to SIL
slimmming the type checker down a little bit... but for now I am using it so
that other diagnostic passes can have a SIL instruction (with SIL location) so
that we can emit diagnostics on code like _ = x. Today we just do not emit
anything at all for that case so a diagnostic SIL pass would not see any
instruction that it could emit a diagnostic upon. In the next patch of this
series, I am going to add SILGen support to do that.
2025-01-22 21:12:36 -08:00
Erik Eckstein
9aff288be4 Optimizer: re-implement the pointer_to_address SILCombine peephole optimizations in swift
Which consists of
* removing redundant `address_to_pointer`-`pointer_to_address` pairs
* optimize `index_raw_pointer` of a manually computed stride to `index_addr`
* remove or increase the alignment based on a "assumeAlignment" builtin

This is a big code cleanup but also has some functional differences for the `address_to_pointer`-`pointer_to_address` pair removal:

* It's not done if the resulting SIL would result in a (detectable) use-after-dealloc_stack memory lifetime failure.
* It's not done if `copy_value`s must be inserted or borrow-scopes must be extended to comply with ownership rules (this was the task of the OwnershipRAUWHelper).

Inserting copies is bad anyway.
Extending borrow-scopes would only be required if the original lifetime of the pointer extends a borrow scope - which shouldn't happen in save code. Therefore this is a very rare case which is not worth handling.
2024-12-21 08:28:22 +01:00
Meghana Gupta
2a5ddfbe2d Fix array bounds check optimization for ossa
While hoisting check_subscript call in ossa, isNativeTypeChecked call is also hoisted.
The array value used in the isNativeTypeChecked may not be available if it's lifetime
had ended before. Proactively set the array value of the isNativeTypeChecked call to
the array value in the check_subscript call.
2024-12-17 10:57:11 -08:00
Michael Gottesman
87495c6b83 Merge pull request #77900 from gottesmm/rdar127477211
[region-isolation] Perform checking of non-Sendable results using rbi rather than Sema.
2024-12-03 22:08:49 -08:00
Erik Eckstein
f166f4b4df ArraySemantics: remove some unused code
The code is not used anymore because the ArrayElementPropagation pass was removed: https://github.com/swiftlang/swift/pull/77806
2024-12-03 11:45:54 +01:00
Michael Gottesman
cff835e061 [region-isolation] Perform checking of non-Sendable results using rbi rather than Sema.
In terms of the test suite the only difference is that we allow for non-Sendable
types to be returned from nonisolated functions. This is safe due to the rules
of rbi. We do still error when we return non-Sendable functions across isolation
boundaries though.

The reason that I am doing this now is that I am implementing a prototype that
allows for nonisolated functions to inherit isolation from their caller. This
would have required me to implement support both in Sema for results and
arguments in SIL. Rather than implement results in Sema, I just finished the
work of transitioning the result checking out of Sema and into SIL. The actual
prototype will land in a subsequent change.

rdar://127477211
2024-12-02 16:54:12 -05:00
Erik Eckstein
99ef6f727d Optimizer: replace unchecked_enum_data simplification in SILCombine with the corresponding instruction simplification from SwiftCompilerSources
The optimization in SILCombine had a bug (which is already fixed in the instruction simplification).
2024-11-14 09:18:29 +01:00
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
edd6bf5704 [region-isolation] A little code re-organization.
I am going to be adding more functionality to this that moves a bit of the
utilities code into it. So it really makes sense to move it to the top of the
file closer to that code. I am doing this separately to make the other
refactoring easier to see in the diff.
2024-11-11 11:43:07 -08:00
Nate Chandler
f1f0ccdeff [NFC] Improved predicate names. 2024-11-06 20:52:21 -08:00
eeckstein
58d2259d83 Merge pull request #77390 from eeckstein/fix-rc-identity-analysis
RCIdentityAnalysis: don't let a non-copyable value be the RC root of a copyable value
2024-11-05 18:17:28 +01:00
Erik Eckstein
3b06bef250 RCIdentityAnalysis: don't let a non-copyable value be the RC root of a copyable value
Otherwise optimizations like retain-sinking might create retain_value instructions with a non-copyable operand.

Fixes a compiler crash.
rdar://139103557
2024-11-05 12:05:34 +01:00
Michael Gottesman
32b4de60a9 Rename transfer -> send.
Accomplished using clangd's rename functionality.
2024-11-04 15:17:51 -08:00
Michael Gottesman
3c38c79f7a [region-isolation] Implement MergeIsolationRegionInst.
I am adding this instruction to express artificially that two non-Sendable
values should be part of the same region. It is meant to be used in cases where
due to unsafe code using Sendable, we stop propagating a non-Sendable dependency
that needs to be made in the same region of a use of said Sendable value. I
included an example in ./docs/SIL.rst of where this comes up with @out results
of continuations.
2024-11-01 11:25:53 -07:00