Commit Graph

47 Commits

Author SHA1 Message Date
Doug Gregor
956243cd7e Teach #isolation to respect the flow-sensitive nature of actor initializers
Actor initializers have a flow-sensitive property where they are isolated
to the actor being initialized only after the actor instance itself is
fully-initialized. However, this behavior was not being reflected in
the expansion of `#isolation`, which was always expanding to `self`,
even before `self` is fully formed.

This led to a source compatibility issue with code that used the async
for..in loop within an actor initializer *prior* to the point where the
actor was fully initialized, because the type checker is introducing
the `#isolation` (SE-0421) but Definite Initialization properly rejects
the use of `self` before it is initialized.

Address this issue by delaying the expansion of `#isolation` until
after the actor is fully initialized. In SILGen, we introduce a new
builtin for this case (and *just* this case) called
`flowSensitiveSelfIsolation`, which takes in `self` as its argument
and produces an `(any Actor)?`. Definite initialization does not treat
this as a use of `self`. Rather, it tracks these builtins and
replaces them either with `self` (if it is fully-initialized at this
point) or `nil` (if it is not fully-initialized at this point),
mirroring the flow-sensitive isolation semantics described in SE-0327.

Fixes rdar://127080037.
2024-06-07 14:54:20 -07:00
Pavel Yaskevich
c82559e33c [DI/Lowering] InitAccessors: Implement lowering of property assignments
DI marks all of of the previously initialized properties and Raw SIL
lowering emits `destroy_addr` before calling init accessor for such
properties to destroy previously set value.
2023-06-06 18:59:46 -07:00
Pavel Yaskevich
112d0d4f2d [DI] InitAccessors: Start marking initializes(...) properties as "out"
Adjust DI to recognize that "out" location without uses is uninitialized.
2023-06-06 18:59:45 -07:00
Pavel Yaskevich
63e7561f8c [SIL] InitAccessors: Introduce a new Out kind to MarkUninitializedInst
`Out` marks indirect results that have to be fully initialized
before their lifetime ends.
2023-06-06 18:59:43 -07:00
Holly Borla
0028bdf91d [DefiniteInitialization] Rename DIUseKind::AssignWrappedValue to DIUseKind::Set. 2023-06-06 18:59:13 -07:00
Pavel Yaskevich
e0bf2ff854 [SIL/DI] NFC: Remove TypeWrappers feature functionality 2023-02-08 10:14:29 -08:00
Pavel Yaskevich
d5844e5300 [DI] Process type wrapper _storage before any other initialization
`_storage` is special because it emits assignments to compiler
synthesized stored property `$storage`, without that `self`
cannot be fully initialized.
2022-09-29 20:50:37 -07:00
Pavel Yaskevich
b5126a97d3 [SILOptimizer] DI: Add a way to check whether TheMemory is _storage local var 2022-09-29 20:50:36 -07:00
Pavel Yaskevich
f66f9dc8f7 [DI] Add a way to find value of self in initializer context
This is staging for type wrappers that need to inject
`self.$storage = ...` call to user-defined initializers,
to be able to do that logic needs to be able to find
`self` that is being constructed.
2022-09-20 14:36:22 -07:00
Kavon Farvardin
13cbe0dd15 adding the @_compilerInitialized attribute
This attribute is designed for let-bound variables whose initializing
assignment is synthesized by the compiler. This assignment is
expected to happen at some point before DefiniteInitialization has
run, which is the pass that verifies whether the compiler truly
initialized the variable.

I generally expect that this will never be a user-facing feature, and
that the synthesized assignment happens in SILGen.
2022-03-24 16:18:49 -07:00
Kavon Farvardin
4f28b87de9 basic implementation of flow-isolation for SE-327
Flow-isolation is a diagnostic SIL pass that finds
unsafe accesses to properties in initializers and
deinitializers that cannot gain isolation to otherwise
protect those accesses from concurrent modifications.
See SE-327 for more details about how and why it exists.

This commit includes changes and features like:

- The removal of the escaping-use restriction
- Flow-isolation that works properly with `defer` statements
- Flow-isolation with an emphasis on helpful diagnostics.

It also includes known issues like:

- Local / nonescaping functions are not analyzed by
  flow-isolation, despite it being technically possible.
  The main challenge in supporting it efficiently is that
  such functions do not have a single exit-point, like
  a `defer`. In particular, arbitrary functions can throw
  so there are points where nonisolation should _not_ flow
  out of the function at a call-site in the initializer, etc.

- The implementation of the flow-isolation pass is not
  particularly memory efficient; it relies on BitDataflow
  even though the particular flow problem is simple.
  So, a more efficient implementation would be specialized for
  this particular problem, etc.

There are also some changes to the Swift language itself: defer
will respect its context when deciding its property access kind.

Previously, a defer in an initializer would always access a stored
property through its accessor methods, instead of doing so directly
like its enclosing function might. This inconsistency is unfortunate,
so for Swift 6+ we make this consistent. For Swift 5, only a defer
in a function that is a member of the following kinds of types
will gain this consistency:

- an actor type
- any nominal type that is actor-isolated, excluding UnsafeGlobalActor.

These types are still rather new, so there is much less of a chance of
breaking expected behaviors around defer. In particular, the danger is
that users are relying on the behavior of defer triggering a property
observer within an init or deinit, when it would not be triggering it
without the defer.
2022-02-02 13:31:14 -07:00
Nate Chandler
7b6fb3fbf5 [DI] See through alloc_box borrows. 2022-01-13 13:33:34 -08:00
Kavon Farvardin
d23858f61a inject hop_to_executor in async actor inits
Because `self` can be used unrestricted after it is
fully-initialized in an async actor init, we perform
a hop_to_executor(self) immediately after `self`
is fully-initialized on all paths within the
initializer.

If we did not, then code could race with the
initializer if it, e.g., spawns a task from
the initializer and mutates actor state that
the initializer then reads. By hopping to the
executor, we prevent that task from running
until _after_ the initializer completes.
2021-07-16 15:01:12 -07:00
Kavon Farvardin
f722ccdcdd restrict uses of 'self' in certain actor initializers
A uniqueness rule for 'self' is enforced throughout
the following types of actor initializers:

1. synchronous
2. global-actor isolated

This means that 'self' cannot be used for anything,
even after 'self' is fully initialized, other than
for access to stored properties. Method calls are
considered escaping uses of 'self', since it is passed
implicitly to the method of computed property when invoked.

Also, an actor's convenience init now treats self as
nonisolated.

This provides a way to perform follow-up work after
self is fully initialized, when creating the
actor from a synchronous context.

Resolves rdar://78790369
2021-06-23 14:17:00 -07:00
John McCall
12a4d471c0 In DI, cache whether a memory object is a box.
Boxes tend to have a small number of uses, so frequently finding the
unique projection isn't too bad.  Non-boxes, however, can have a large
number of uses: for example, a class instance has expected uses
proportionate to the number of stored properties.  So if we do a linear
scan of the uses on a non-box instruction, we'll scale quadratically.

Fixes SR-14532.
2021-05-07 18:50:00 -04:00
Erik Eckstein
27f670b974 DefiniteInitialization: remove an always-true dyn_cast
NFC
2021-01-07 23:36:08 +01:00
Erik Eckstein
8075010892 DefiniteInitialization: remove the limit of 64k aggregate elements.
I didn't add a test, because there is a performance problem in PredictableMemoryAccessOptimizations.
Until this is fixed, the test would take too long.

rdar://71040556
2021-01-07 12:52:35 +01:00
Michael Gottesman
611284fcc2 [sil] Element shadowing of SILInstruction::getKind() by renaming MarkUninitializedInst::get{,MarkUninitialized}Kind().
Interestingly this problem can only occur if one invokes
MarkUninitializedInst::getKind() directly. Once our instruction is just a
SILInstruction, we call the appropriate method so we didn't notice it.

I used Xcode's refactoring functionality to find all of the invocation
locations.
2020-11-17 21:25:57 -08:00
Slava Pestov
37f2bc1e59 DI: Add non-working code for handling value_metatype when used on an address-only type 2020-10-23 22:25:58 -04:00
Holly Borla
1f426773ee [Property Wrappers] Lower assign_by_wrapper to re-assignment of the backing
property wrapper before all of self is initialized if the wrapper has already
been initialized.
2020-09-11 15:43:21 -07:00
Slava Pestov
df0a8b3746 DI: Track of initialization state of trivial fields in root class initializers
While we don't need to destroy these fields, we still need to know
when the class is _semantically_ fully initialized, since this will
determine if we're going to release the class, running the deinitializer,
or if we're going to deallocate the memory for the instance directly.
2020-08-31 22:08:47 -04:00
Michael Gottesman
b4cf1afd19 [di] When emitting element addresses to destroy fields, use begin_access [deinit]. 2020-01-08 14:09:55 -08:00
Michael Gottesman
28ffcf9a7a [ownership] Eliminate the need for mark_uninitialized fixup.
This commit eliminates the need for mark uninitialized fixup by updating the
compiler so that we now emit:

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

Instead of:

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

Now that the first type of code is generated, I can change project_box to only
take guaranteed arguments. This will ensure that the OSSA ARC optimizer can
eliminate copies of boxes without needing to understand the usage of the
project_box.
2020-01-02 09:54:18 -08:00
Michael Gottesman
ba6763ac10 [di] Instead of accessing TheMemory.MemoryInst directly, use the helper getUninitializedValue().
This is going to let me change getUninitializedValue() to special case alloc_box
so that we return the project_box (the address we want to analyze) rather than
the mark_uninitialized (which is on the box).
2019-12-27 19:59:12 -08:00
Michael Gottesman
722d0bb028 [di] Add an accessor for NumElements and use that instead of accessing the field directly. 2019-12-27 19:49:34 -08:00
Michael Gottesman
2cf6d02a93 [di] Now that DI and PMO are split, debride some dead code.
Specifically isDefiniteInitFinished is always set to false and
TreatAddressToPointerAsInout is set to true when ever DI is run, so this patch
just constant propagates those values and then DCEs as appropriate.
2019-12-27 19:07:54 -08:00
Michael Gottesman
980b1f0561 [di] Hide some internal state and give some methods better names given current usage.
NFC.
2019-12-19 15:09:15 -08:00
Erik Eckstein
86fb74a34e DI: support assign_by_delegate instruction 2019-04-23 11:32:28 -07:00
Jordan Rose
425c190086 Restore initializing entry points for @objc convenience initializers (#21815)
This undoes some of Joe's work in 8665342 to add a guarantee: if an
@objc convenience initializer only calls other @objc initializers that
eventually call a designated initializer, it won't result in an extra
allocation. While Objective-C /allows/ returning a different object
from an initializer than the allocation you were given, doing so
doesn't play well with some very hairy implementation details of
compiled nib files (or NSCoding archives with cyclic references in
general).

This guarantee only applies to
(1) calling `self.init`
(2) where the delegated-to initializer is @objc
because convenience initializers must do dynamic dispatch when they
delegate, and Swift only stores allocating entry points for
initializers in a class's vtable. To dynamically find an initializing
entry point, ObjC dispatch must be used instead.

(It's worth noting that this patch does NOT check that the calling
initializer is a convenience initializer when deciding whether to use
ObjC dispatch for `self.init`. If we ever add peer delegation to
designated initializers, which is totally a valid feature, that should
use static dispatch and therefore should not go through objc_msgSend.)

This change doesn't /always/ result in fewer allocations; if the
delegated-to initializer ends up returning a different object after
all, the original allocation was wasted. Objective-C has the same
problem (one of the reasons why factory methods exist for things like
NSNumber and NSArray).

We do still get most of the benefits of Joe's original change. In
particular, vtables only ever contain allocating initializer entry
points, never the initializing ones, and never /both/ (which was a
thing that could happen with 'required' before).

rdar://problem/46823518
2019-01-14 13:06:50 -08:00
Michael Gottesman
9620bedf7a [di] Rename: DIMemoryUseCollector{Ownership,}.{cpp,h}
This was done early on during the split of predictable mem opts from DI. This
has been done for a long time, so eliminate the "Ownership" basename suffix.
2018-12-30 16:11:56 -08:00
Michael Gottesman
db30959a9d [pred-memopt] Replace remaining occurances of prefix DI with PMO prefix.
I am going to DI and predictable mem opts have been split for a long time and
their subroutines aren't going to be joined in the future... so replace the DI
prefixes in pred-mem-opts impl with PMO and rename DIMemoryUseCollector =>
PMOUseCollector.

Been sitting on this for a long time... just happy to get it in.
2018-05-22 12:52:43 -07:00
Michael Gottesman
269f8e8d56 [pred-memopts] Rather than asserting on recursive initialization, just return false and bail.
Until the beginning of the ownership transition, DI and predictable mem opts
used the same memory use collector. I split them partially since I need to turn
on ownership for predictable mem opts at one time, but also b/c there was a huge
amount of special code that would only trigger if it was used by DI or used by
predictable mem opts. After I did the copy some of the asserts that were needed
for DI remained in the predictable mem opts code. When pred-memopts was only run
in the mandatory pipeline keeping these assertions were ok, but pred-memopts was
recently added to the perf pipeline meaning that it may see code that breaks
these DI invariants (and thus hit this assertion).

We should remove this limitation on predictable-memopts but that would require
some scheduled time to read the code (more than I have to fix this bug = p). So
instead I changed the code to just bail in these cases.

rdar://40032102
2018-05-11 09:50:32 -07:00
David Zarzycki
8c0c55539f [SIL] NFC: Rename misleading getSwiftRValueType() to getASTType()
Reference storage types are not RValues. Also, use more SILType helper
methods to avoid line wrap.
2018-05-04 08:14:38 -04:00
John McCall
ab3f77baf2 Make SILInstruction no longer a subclass of ValueBase and
introduce a common superclass, SILNode.

This is in preparation for allowing instructions to have multiple
results.  It is also a somewhat more elegant representation for
instructions that have zero results.  Instructions that are known
to have exactly one result inherit from a class, SingleValueInstruction,
that subclasses both ValueBase and SILInstruction.  Some care must be
taken when working with SILNode pointers and testing for equality;
please see the comment on SILNode for more information.

A number of SIL passes needed to be updated in order to handle this
new distinction between SIL values and SIL instructions.

Note that the SIL parser is now stricter about not trying to assign
a result value from an instruction (like 'return' or 'strong_retain')
that does not produce any.
2017-09-25 02:06:26 -04:00
Michael Gottesman
676d13846a [pred-memopt] More debriding of DI only stuff from DIMemoryUseCollector.
rdar://31521023
2017-09-13 21:07:19 -07:00
Michael Gottesman
dd17536922 [pred-memopts] Debride all dead MarkUninitialized related code from DIMemoryUseCollector.
rdar://31521023
2017-09-13 17:29:37 -07:00
Michael Gottesman
6adcc2969e [semantic-sil] Change DefiniteInitialization to use the DIMemoryUseCollectorOwnership header instead of the normal DIMemoryUseCollector.
This is necessary since other passes rely on DIMemoryUseCollector.h and I want
to update each one of them individually to minimize disruption.

rdar://31521023
2017-04-13 16:41:53 -07:00
Erik Eckstein
f8034ac7bf PredictableMemOpt: be more conservative about address_to_pointer
Handling address_to_pointer as a plain inout missed some mutations and lead to miscompiles.
We now treat address_to_pointer as escaping address.

Fixes SR-3554
2017-01-10 09:25:51 -08:00
practicalswift
6d1ae2a39c [gardening] 2016 → 2017 2017-01-06 16:41:22 +01:00
Slava Pestov
786d87b9bb DI: Operate on substituted field types
When adding a designated initializer to a nominal type in another
module, we would call getType() on deserialized VarDecls, which
is not allowed.

Instead, it is more correct to use SILTypes throughout and call
SILType::getFieldType() to get a substituted field type.

Fixes <https://bugs.swift.org/browse/SR-3545>.
2017-01-04 21:30:21 -08:00
Hugh Bellamy
63db0041d4 Fix warnings and error building swift/SILOptimizer on Windows with MSVC (#5956) 2016-12-02 13:06:08 -08:00
practicalswift
797b80765f [gardening] Use the correct base URL (https://swift.org) in references to the Swift website
Remove all references to the old non-TLS enabled base URL (http://swift.org)
2016-11-20 17:36:03 +01:00
Erik Eckstein
8110b1ebc8 [SIL] Let alloc_box return a single value.
And use project_box to get to the address value.
SILGen now generates a project_box for each alloc_box.
And IRGen re-uses the address value from the alloc_box if the operand of project_box is an alloc_box.
This lets the generated code be the same as before.

Other than that most changes of this (quite large) commit are straightforward.
2016-01-19 08:59:24 -08:00
Erik Eckstein
6ff2f09796 [SIL] Let alloc_stack return a single value.
Having a separate address and container value returned from alloc_stack is not really needed in SIL.
Even if they differ we have both addresses available during IRGen, because a dealloc_stack is always dominated by the corresponding alloc_stack in the same function.

Although this commit quite large, most changes are trivial. The largest non-trivial change is in IRGenSIL.

This commit is a NFC regarding the generated code. Even the generated SIL is the same (except removed #0, #1 and @local_storage).
2016-01-06 17:35:27 -08:00
Zach Panzarino
e3a4147ac9 Update copyright date 2015-12-31 23:28:40 +00:00
practicalswift
85e2e6eb9a Fix a vs. an 2015-12-26 14:40:16 +01:00
Andrew Trick
739b0e9c56 Reorganize SILOptimizer directories for better discoverability.
(libraries now)

It has been generally agreed that we need to do this reorg, and now
seems like the perfect time. Some major pass reorganization is in the
works.

This does not have to be the final word on the matter. The consensus
among those working on the code is that it's much better than what we
had and a better starting point for future bike shedding.

Note that the previous organization was designed to allow separate
analysis and optimization libraries. It turns out this is an
artificial distinction and not an important goal.
2015-12-11 15:14:23 -08:00