Commit Graph

183 Commits

Author SHA1 Message Date
Nate Chandler
1eafced6a2 [AllocBoxToStack] Don't destroy in dead-ends.
It is valid to leak a value on paths into dead-end regions.
Specifically, it is valid to leak an `alloc_box`.  Thus, "final
releases" in dead-end regions may not destroy the box and consequently
may not release its contents.  Therefore it's invalid to lower such final
releases to `dealloc_stack`s, let alone `destroy_addr`s.  The in-general
invalidity of that transformation results in miscompiling whenever a box
is leaked and its projected address is used after such final releases.

Fix this by not treating final releases as boundary markers of the
`alloc_box` and not lowering them to `destroy_addr`s and
`dealloc_stack`s.

rdar://158149082
2025-08-27 17:03:48 -07:00
Erik Eckstein
6714a72256 Optimizer: re-implement and improve the AllocBoxToStack pass
This pass replaces `alloc_box` with `alloc_stack` if the box is not escaping.
The original implementation had some limitations. It could not handle cases of local functions which are called multiple times or even recursively, e.g.

```
public func foo() -> Int {
  var i = 1

  func localFunction() { i += 1 }

  localFunction()
  localFunction()
  return i
}

```

The new implementation (done in Swift) fixes this problem with a new algorithm.
It's not only more powerful, but also simpler: the new pass has less than half lines of code than the old pass.

The pass is invoked in the mandatory pipeline and later in the optimizer pipeline.
The new implementation provides a module-pass for the mandatory pipeline (whereas the "regular" pass is a function pass).
This is required because the mandatory pass needs to remove originals of specialized closures, which cannot be done from a function-pass.
In the old implementation this was done with a hack by adding a semantic attribute and deleting the function later in the pipeline.

I still kept the sources of the old pass for being able to bootstrap the compiler without a host compiler.

rdar://142756547
2025-06-20 08:15:04 +02:00
Kuba Mracek
6f4ae28520 [ASTMangler] Pass ASTContext to all instantiations of ASTMangler 2024-12-02 15:01:04 -08:00
Nate Chandler
02c28f5de5 [AllocBoxToStack] Drop dead_end dealloc_boxes.
When creating `dealloc_stack`s corresponding to `dealloc_box`es, don't
bother to create a dealloc_stack if the `dealloc_box` is flagged
`dead_end`.
2024-07-03 17:33:51 -07:00
Tim Kientzle
1d961ba22d Add #include "swift/Basic/Assertions.h" to a lot of source files
Although I don't plan to bring over new assertions wholesale
into the current qualification branch, it's entirely possible
that various minor changes in main will use the new assertions;
having this basic support in the release branch will simplify that.
(This is why I'm adding the includes as a separate pass from
rewriting the individual assertions)
2024-06-05 19:37:30 -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
Nate Chandler
08a832b803 [AllocBoxToStack] Transfer var_decl flag.
As with the lexical flag, when creating an alloc_stack corresponding to
an alloc_box, transfer the var_decl flag from any begin_borrow users of
the box.
2024-03-08 22:28:22 -08:00
Nate Chandler
3dbeebaa9b [SIL] Add var_decl flag to alloc_stack. 2024-03-08 22:28:22 -08:00
Nate Chandler
c6eb3112b5 [NFC] SIL: Typed alloc_stack's isLexical. 2024-03-08 21:24:39 -08:00
Nate Chandler
ec6b447b68 [NFC] SIL: Typed alloc_box's hasDynamicLifetime. 2024-03-08 21:24:39 -08:00
Nate Chandler
dfa5c9814b [NFC] SIL: Typed alloc_stack's hasDynamicLifetime. 2024-03-08 21:24:39 -08:00
Nate Chandler
611511a71f [NFC] SIL: Typed alloc_stack's wasMoved.
Help avoid errors with boolean flags by introducing
UsesMoveableValueDebugInfo_t.
2024-03-08 21:24:39 -08:00
Michael Gottesman
3236bc26fa [region-isolation] Refactor out the stubify dead function if no longer used functionality from move only checker into its own pass and put it before region based isolation.
I am doing this since region based isolation hit the same issue that the move
checker did. So it makes sense to refactor the functionality into its own pass
and move it into a helper pass that runs before both.

It is very conservative and only stubifies functions that the specialization
passes explicitly mark as this being ok to be done to.
2024-03-01 13:11:07 -08:00
Michael Gottesman
11f0ff6e32 [sil] Ensure that all SILValues have a parent function by making it so that SILUndef is uniqued at the function instead of module level.
For years, optimizer engineers have been hitting a common bug caused by passes
assuming all SILValues have a parent function only to be surprised by SILUndef.
Generally we see SILUndef not that often so we see this come up later in
testing. This patch eliminates that problem by making SILUndef uniqued at the
function level instead of the module level. This ensures that it makes sense for
SILUndef to have a parent function, eliminating this possibility since we can
define an API to get its parent function.

rdar://123484595
2024-02-27 13:14:47 -08:00
Ben Barham
ef8825bfe6 Migrate llvm::Optional to std::optional
LLVM has removed llvm::Optional, move over to std::optional. Also
clang-format to fix up all the renamed #includes.
2024-02-21 11:20:06 -08:00
John McCall
b23bc82cdb Merge pull request #71574 from rjmccall/isolation-any-sil-irgen
SIL and IRGen support for `@isolated(any)` function types
2024-02-13 11:57:16 -05:00
John McCall
d5142668f4 SIL and IRGen support for @isolated(any). SILGen to come. 2024-02-13 03:04:13 -05:00
Joe Groff
0365c6abba MoveOnlyAddressUtils: Fixes for borrowing switch over address-only types.
Relax some existing pattern matches and add some unhandled instructions to the
walkers so that borrowing switches over address-only enums are properly analyzed
for incorrect consumption. Add a `[strict]` flag to `mark_unresolved_move_only_value`
to indicate a borrow access that should remain a borrow access even if the subject
is later stack-promoted from a box.
2024-02-12 20:20:50 -08:00
Kavon Farvardin
70fe62cce9 [nfc] remove unnecessary overload 2023-10-28 23:57:09 -07:00
Michael Gottesman
37d60a08bb [move-only] Rename mark_must_check -> mark_unresolved_non_copyable_value.
I was originally hoping to reuse mark_must_check for multiple types of checkers.
In practice, this is not what happened... so giving it a name specifically to do
with non copyable types makes more sense and makes the code clearer.

Just a pure rename.
2023-08-30 22:29:30 -07:00
nate-chandler
f423115b71 Merge pull request #67218 from nate-chandler/rdar112078094
[AllocBoxToStack] Computing partial_apply frontier looks thru copies+moves.
2023-07-12 07:11:20 -07:00
Nate Chandler
6cf2bb4151 [AllocBoxToStack] PAI frontier looks thru moves.
When determining where to destroy a captured alloc_box, the frontier of
the capturing partial_apply is computed.  Previously, that computation
just used the uses of the partial_apply.  If the partial_apply were
moved and the original destroyed before the apply, however, the result
would be a miscompile where the destroy_addr/dealloc_stack for the
alloc_stack to which the alloc_box was promoted was destroyed before the
apply of the closure which used the address.
2023-07-11 16:27:47 -07:00
Nate Chandler
15ce42ae09 [AllocBoxToStack] PAI frontier looks thru copies.
When determining where to destroy a captured alloc_box, the frontier of
the capturing partial_apply is computed.  Previously, that computation
just used the uses of the partial_apply.  If the partial_apply were
copied and the original destroyed before the apply, however, the result
would be a miscompile where the destroy_addr/dealloc_stack for the
alloc_stack to which the alloc_box was promoted was destroyed before the
apply of the closure which used the address.
2023-07-11 16:27:27 -07:00
Joe Groff
3d5285be6f Arrange for closure bodies promoted by AllocBoxToStack to have their originals removed by MoveOnlyChecker.
This is an improvement of #67031 which avoids deleting the closure function
body during AllocBoxToStack, which still breaks pass invariants by modifying
functions other than the currently-analyzed function. As a function pass,
AllocBoxToStack also doesn't really know with certainty whether the original
closure function is unused after stack promotion or not. We still want to
eliminate the original when it may contain invalid SIL for move-only values
that rely on the escape analysis for correct semantics, so rather than mark the
original function to be *ignored* during move-only checking, mark it to be
*deleted* by move-only checking if the function is in fact unused at that
point.

If the marked function is still used, we let it pass through move-only
checking normally, which may cause redundant diagnostics but is the right
thing to do since code is still potentially using the closure with escaping
semantics. We should rearrange things to make this situation impossible in
the future.

rdar://110675352
2023-07-10 15:18:16 -07:00
Joe Groff
8d974212f1 Revert "AllocBoxToStack: Remove bodies of closure functions left unused after specialization."
This reverts commit bd5f0a7af0.
2023-07-10 12:43:21 -07:00
Joe Groff
421c762862 Merge pull request #67031 from jckarter/allocbox-to-stack-remove-unused-original-bodies
AllocBoxToStack: Remove bodies of closure functions left unused after specialization.
2023-06-30 08:30:42 -07:00
Joe Groff
bd5f0a7af0 AllocBoxToStack: Remove bodies of closure functions left unused after specialization.
We can't remove the functions at this point in case they might have other function
passes enqueued to run on them, but we can at least remove the function contents
that are now unnecessary. We need to do this in cases when move-only types are
involved, since the semantics of the move checker rely on unescaped captures being
promoted before the pass runs, and we leave behind invalid SIL in the unpromoted code.
rdar://110675352
2023-06-29 13:45:07 -07:00
Evan Wilde
f3ff561c6f [NFC] add llvm namespace to Optional and None
This is phase-1 of switching from llvm::Optional to std::optional in the
next rebranch. llvm::Optional was removed from upstream LLVM, so we need
to migrate off rather soon. On Darwin, std::optional, and llvm::Optional
have the same layout, so we don't need to be as concerned about ABI
beyond the name mangling. `llvm::Optional` is only returned from one
function in
```
getStandardTypeSubst(StringRef TypeName,
                     bool allowConcurrencyManglings);
```
It's the return value, so it should not impact the mangling of the
function, and the layout is the same as `std::optional`, so it should be
mostly okay. This function doesn't appear to have users, and the ABI was
already broken 2 years ago for concurrency and no one seemed to notice
so this should be "okay".

I'm doing the migration incrementally so that folks working on main can
cherry-pick back to the release/5.9 branch. Once 5.9 is done and locked
away, then we can go through and finish the replacement. Since `None`
and `Optional` show up in contexts where they are not `llvm::None` and
`llvm::Optional`, I'm preparing the work now by going through and
removing the namespace unwrapping and making the `llvm` namespace
explicit. This should make it fairly mechanical to go through and
replace llvm::Optional with std::optional, and llvm::None with
std::nullopt. It's also a change that can be brought onto the
release/5.9 with minimal impact. This should be an NFC change.
2023-06-27 09:03:52 -07:00
Joe Groff
359e045192 Require switch on a noncopyable-type binding to be explicitly consume-d.
Pattern matching as currently implemented is consuming, but that's not
necessarily what we want to be the default behavior when borrowing pattern
matching is implemented. When a binding of noncopyable type is pattern-matched,
require it to be annotated with the `consume` operator explicitly. That way,
when we introduce borrowing pattern matching later, we have the option to make
`switch x` do the right thing without subtly changing the behavior of existing
code. rdar://110073984
2023-06-02 18:14:37 -07:00
Michael Gottesman
9c9fe09e25 [allocbox-to-stack] When we promote an argument that is moveonly, use consumable_and_assignable, not assignable_but_not_consumable.
Otherwise, since the compiler views assignable_but_not_consumable in this
position as a sign of an escaping closure, we emit escaping closure errors
instead of nonescaping closure errors.
2023-05-14 13:34:36 -07:00
Michael Gottesman
3ccda34275 Revert "[move-only] Ensure that we treat captured escaping closure arguments as such even if the closure doesn't actually escape"
This reverts commit 224674cad1.

Originally, I made this change since we were going to follow the AST in a strict
way in terms of what closures are considered escaping or not from a diagnostics
perspective. Upon further investigation I found that we actually do something
different for inout escaping semantics and by treating the AST as the one point
of truth, we are being inconsistent with the rest of the compiler. As an
example, the following code is considered by the compiler to not be an invalid
escaping use of an inout implying that we do not consider the closure to be
escaping:

```
func f(_ x: inout Int) {
  let g = {
    _ = x
  }
}
```

in contrast, a var is always considered to be an escape:

```
func f(_ x: inout Int) {
  var g = {
    _ = x
  }
}

test2.swift:3:13: error: escaping closure captures 'inout' parameter 'x'
    var g = {
            ^
test2.swift:2:10: note: parameter 'x' is declared 'inout'
func f(_ x: inout Int) {
         ^
test2.swift:4:11: note: captured here
        _ = x
          ^
```

Of course, if we store the let into memory, we get the error one would expect:

```
var global: () -> () = {}
func f(_ x: inout Int) {
  let g = {
    _ = x
  }
  global = g
}

test2.swift:4:11: error: escaping closure captures 'inout' parameter 'x'
  let g = {
          ^
test2.swift:3:10: note: parameter 'x' is declared 'inout'
func f(_ x: inout Int) {
         ^
test2.swift:5:7: note: captured here
    _ = x
      ^
```

By reverting to the old behavior where allocbox to stack ran early, noncopyable
types now have the same sort of semantics: let closures that capture a
noncopyable type that do not on the face of it escape are considered
non-escaping, while if the closure is ever stored into memory (e.x.: store into
a global, into a local var) or escapes, we get the appropriate escaping
diagnostics. E.x.:

```
public struct E : ~Copyable {}
public func borrowVal(_ e: borrowing E) {}
public func consumeVal(_ e: consuming E) {}

func f1() {
  var e = E()

  // Mutable borrowing use of e. We can consume e as long as we reinit at end
  // of function. We don't here, so we get an error.
  let c1: () -> () = {
    borrowVal(e)
    consumeVal(e)
  }

  // Mutable borrowing use of e. We can consume e as long as we reinit at end
  // of function. We do do that here, so no error.
  let c2: () -> () = {
    borrowVal(e)
    consumeVal(e)
    e = E()
  }
}

```
2023-05-14 13:34:36 -07:00
Michael Gottesman
224674cad1 [move-only] Ensure that we treat captured escaping closure arguments as such even if the closure doesn't actually escape
Specifically, we already have the appropriate semantics for arguments captured
by escaping closures but in certain cases allocbox to stack is able to prove
that the closure doesn’t actually escape. This results in the capture being
converted into a non-escaping SIL form. This then causes the move checker to
emit the wrong kind of error.

The solution is to create an early allocbox to stack that doesn’t promote move
only types in boxes from heap -> stack if it is captured by an escaping closure
but does everything else normally. Then once the move checking is completed, we
run alloc box to stack an additional time to ensure that we keep the guarantee
that heap -> stack is performed in those cases.

rdar://108905586
2023-05-04 12:25:19 -07:00
Nate Chandler
20147e61f1 [AllocBoxToStack] Specialize moved PAIs.
Added move_value to the list of instructions looked through when
determining whether a partial_apply instruction escapes.

rdar://108376960
2023-04-21 11:07:14 -07:00
Adrian Prantl
158772c2ab Rebase SILScope generation on top of ASTScope.
This patch replaces the stateful generation of SILScope information in
SILGenFunction with data derived from the ASTScope hierarchy, which should be
100% in sync with the scopes needed for local variables. The goal is to
eliminate the surprising effects that the stack of cleanup operations can have
on the current state of SILBuilder leading to a fully deterministic (in the
sense of: predictible by a human) association of SILDebugScopes with
SILInstructions. The patch also eliminates the need to many workarounds. There
are still some accomodations for several Sema transformation passes such as
ResultBuilders, which don't correctly update the source locations when moving
around nodes. If these were implemented as macros, this problem would disappear.

This necessary rewrite of the macro scope handling included in this patch also
adds proper support nested macro expansions.

This fixes

rdar://88274783

and either fixes or at least partially addresses the following:

rdar://89252827
rdar://105186946
rdar://105757810
rdar://105997826
rdar://105102288
2023-04-04 15:20:11 -07:00
Michael Gottesman
1dd896ded9 [move-only] Implement escaping closure semantics.
NOTE: A few of the test patterns need to be made better, but this patch series
is large enough, I want to get it into tree and iterate.
2023-02-20 11:04:21 -08:00
Michael Gottesman
f4e1b2a8f2 [move-only] Update SILGen/MoveCheckers so that vars are emitted in eagerly projected box form.
This is the first slice of bringing up escaping closure support. The support is
based around introducing a new type of SILGen VarLoc: a VarLoc with a box and
without a value. Because the VarLoc only has a box, we have to in SILGen always
eagerly reproject out the address from the box. The reason why I am doing this
is that it makes it easy for the move checker to distinguish in between
different accesses to the box that we want to check separately. As such every
time that we open the box, we insert a mark_must_check
[assignable_but_not_consumable] on that project. If allocbox_to_stack manages to
determine that the box can be stack allocated, we eliminate all of the
mark_must_check and place a new mark_must_check [consumable_and_assignable] on
the alloc_stack.  The end result is that we get the old model that we had before
and also can support escaping closures.
2023-02-20 11:04:21 -08:00
Michael Gottesman
969d776c15 [allocbox-to-stack] When we promote a heap -> stack of noncopyable type, move mark_must_check onto alloc_stack and always
ensure that we use consumable_to_assign.

What this patch is does is add an extra phase after alloc_box runs where we look
at uses of the alloc_stack and if we see any mark_must_check of any kind, we
delete them and rewrite a single mark_must_check [consumable_and_assignable] on
the alloc_stack and make all uses of the alloc_stack go through the
mark_must_check.

This has two effects:

1. In a subsequent PR when I add noncopyable semantics for escaping closures,
this will cause allocbox to stack to convert such boxes from having escaping
semantics to having non-escaping semantics. Escaping semantics means that we
always reproject out from the box and use mark_must_check
[assignable_but_not_consumable] (since we can't consume from the box, but can
assign to it). In contrast, non-escaping semantics means that the box becomes an
alloc_stack and we use the traditional var checker semantics. NOTE: We can do
this for lets represented as addresses and vars since the typechecker will
validate that the let is never actually written to even if at the SIL level we
would allow that.

2. In cases where we are implementing simple mark_must_check
[consumable_and_assignable] on one of the project_box and capture the box, we
used to have a problem where the direct box uses would be on the alloc_stack and
not go through the mark_must_check. Now, all uses after allocbox_to_stack occur
go through the mark_must_check. This is why I was able to remove instances of
the "compiler does not understand this pattern" errors... since the compiler
with this change can now understand them.
2023-02-18 12:38:38 -08:00
Joe Groff
02385c895a AllocBoxToStack: Don't try to destroy promoted stack closure captures.
If the partial_apply is already [on_stack], then ClosureLifetimeFixup would
have already turned the captures into borrows and established their final
lifetimes.
2023-02-17 09:28:53 -08:00
Pavel Yaskevich
3fb69b3c3c [SIL] SILFunction: Add runtime accessible function attribute
This attribute indicates that the given SILFunction has to be
added to "accessible functions" section and could be looked up
at runtime using a special API.
2022-12-20 09:33:44 -08:00
Michael Gottesman
9e44011e4d [sil] Add a new attribute called @closureCaptured to SILFunctionArguments that are closure capture arguments.
I am adding this to make it easy to determine if a SILFunction that is not inout
aliasable is captured. This is useful when emitting certain types of
diagnostics like I need to emit with move only.
2022-12-14 15:16:43 -08:00
Erik Eckstein
6d0903ad7b AllocBoxToStack: handle begin_borrow when checking if a partial_apply escapes. 2022-12-01 07:05:02 +01:00
Erik Eckstein
ab1b343dad use new llvm::Optional API
`getValue` -> `value`
`getValueOr` -> `value_or`
`hasValue` -> `has_value`
`map` -> `transform`

The old API will be deprecated in the rebranch.
To avoid merge conflicts, use the new API already in the main branch.

rdar://102362022
2022-11-21 19:44:24 +01:00
Nate Chandler
5d14610043 [SILOptimizer] Preserve arg attrs at cloning.
Arguments are copied into new cloned functions in a number of places.
Wherever that happens, be sure to transfer the attributes as well.
2022-09-26 16:55:50 -07:00
Michael Gottesman
467b742a5c [move-only] Update the non-address move checker part of the optimizer to handle mark_must_check on addresses. 2022-09-09 13:38:18 -07:00
Nate Chandler
896a464ced [AllocBoxToStack] Lexical borrowees get lexical alloc_stacks.
Previously, whenever an alloc_box that was promoted to an alloc_stack,
the new alloc_stack would be lexical.  The result was that alloc_boxes
which didn't need (or explicitly didn't want, in the case of eager move
vars) received lexical alloc_stacks.

Here, only add the lexical flag to the new alloc_stack instruction if
any of the box's uses are `begin_borrow [lexical]`.  That way,
alloc_boxes end up having lexical alloc_stacks only if they were
"lexical alloc_boxes".
2022-08-12 11:37:19 -07:00
Erik Eckstein
dc42c4c17d AllocStackToBox: fix a bug which results in a too early released captured variable
In case of a borrowed `alloc_box`, the optimization didn't look through the `begin_borrow` when calculating the final release of the box.
This resulted in inserting the destroy of the inserted `alloc_stack` too early.

rdar://97087762
2022-07-18 17:13:12 +02:00
Erik Eckstein
6a020f8f15 Stabilize and simplify SIL linkage and serialization
The main point of this change is to make sure that a shared function always has a body: both, in the optimizer pipeline and in the swiftmodule file.
This is important because the compiler always needs to emit code for a shared function. Shared functions cannot be referenced from outside the module.
In several corner cases we missed to maintain this invariant which resulted in unresolved-symbol linker errors.

As side-effect of this change we can drop the shared_external SIL linkage and the IsSerializable flag, which simplifies the serialization and linkage concept.
2022-03-09 15:28:05 +01:00
nate-chandler
5fcac0811b Merge pull request #40793 from nate-chandler/silgen/emit-borrow-scopes-for-alloc_boxes
[SIL] Added borrow scopes to alloc_boxes.
2022-01-14 07:32:55 -08:00
Nate Chandler
ceea4cf57a [AllocBoxToStack] See through borrows.
Now that alloc_boxes whose lifetimes are lexical are emitted with
begin_borrow [lexical]/end_borrow, AllocBoxToStack needs to be able to
see through those new borrow scopes in order to continue stack promoting
the same boxes that it was able to before those lexical scopes were
emitted.
2022-01-13 13:33:21 -08:00
Andrew Trick
08b445fb7e Remove APPLYSITE obfuscation.
These macros make ApplySite.h and SILNodes.def unreadable. Getting rid
of them will save me (and I'm sure others) a lot of time whenever I
work with ApplySite.

Best practice dictates that the ApplySite abstraction be modeled in
one place. The macros serve no purpose other than obfuscation.
2022-01-12 10:03:16 -08:00