Commit Graph

43 Commits

Author SHA1 Message Date
John McCall 8cda95bca4 Don't delay stack deallocation of non-nested allocations
Credit to @eeckstein for noticing this possibility

I considered some much more complicated solutions to this problem, like
tracking all the deallocations we add and remove and then undoing them
for the allocations we end up marking non_nested, but ultimately I
figured out that this would work and was much less invasive, even if it
potentially yields slightly worse results in some cases.
2026-04-02 19:30:57 -04:00
John McCall a6be0957b8 StackNesting: don't mark pending allocations [non_nested]
Instead, just emit them immediately when we encounter a non-reorderable
deallocation. Credit for this idea goes to Michael Gottesman.

This is a nice optimization; it avoids marking a lot of allocations as
[non_nested] unnecessarily. But by not marking allocations that are
well-nested w.r.t the non-reorderable deallocation, it also means we never
need to mark allocations that were emitted well-ordered and never fell
out of order. That should actually achieve the intuition I had with the
earlier work that it was okay to not support marking certain stack
allocations that are never introduced directly by the SIL optimizer.
2026-04-02 19:30:57 -04:00
John McCall bf851fa4dd Model the stack allocation effects of {add,remove}TaskLocalValue.
The biggest complexity here is dealing with the improper nesting of the
allocation for the argument, and I'm not very proud of the solution I found,
but for a one-off builtin, I think it'll work.
2026-03-20 00:21:40 -04:00
John McCall 9aa0cb4e4f Make the cancellation and priority-ecscalation builtins respect stack nesting.
You can see in the patch that I obviously wrote this to include the
task-local builtins, but unfortunately they don't work for this because
they don't actually have a use/def relationship. That will need to be
follow-up work.

Includes a test that the defer special case applies to the priority
escalation builtin.
2026-03-13 19:40:20 -04:00
John McCall e1e7e8886c Fix a bug involving two async lets on the stack at once. 2026-03-06 03:14:46 -05:00
John McCall baaefaff98 Model the async let builtins as participating in the stack discipline.
This interacts with my previous commits to ensure that we mark other
allocation operations as non-nested when we would otherwise need to move
the finishAsyncLet operation.
2026-03-06 03:14:46 -05:00
John McCall a3c4f35c02 Add some abstractions for working with stack alloc/dealloc instructions.
NFC. Should make it easier to be exhaustive about these things.
2026-03-06 03:14:46 -05:00
John McCall 1ebb2eb885 Fix the stack nesting utility to mark allocations as non-nested
when they would need to be reordered across an allocation that cannot
be reordered across.

Or at least, do this in theory; this commit does not change anything for
any instructions that are currently considered stack allocations.
2026-03-06 03:14:46 -05:00
Michael Gottesman 682ef268d2 [optimizer] Teach SIL optimizer that stack nesting should ignore nested stack allocations. 2025-11-21 11:21:15 -08:00
John McCall 8d231d20c6 Rewrite StackNesting to be a non-iterative single-pass algorithm.
The previous algorithm was doing an iterative forward data flow analysis
followed by a reverse data flow analysis. I suspect the history here is that
it was a reverse analysis, and that didn't really work for infinite loops,
and so complexity accumulated.

The new algorithm is quite straightforward and relies on the allocations
being properly jointly post-dominated, just not nested. We simply walk
forward through the blocks in consistent-with-dominance order, maintaining
the stack of active allocations and deferring deallocations that are
improperly nested until we deallocate the allocations above it. The only
real subtlety is that we have to delay walking into dead-end regions until
we've seen all of the edges into them, so that we can know whether we have
a coherent stack state in them. If the state is incoherent, we need to
remove any deallocations of previous allocations because we cannot talk
correctly about what's on top of the stack.

The reason I'm doing this, besides it just being a simpler and hopefully
faster algorithm, is that modeling some of the uses of the async stack
allocator properly requires builtins that cannot just be semantically
reordered. That should be somewhat easier to handle with the new approach,
although really (1) we should not have runtime functions that need this and
(2) we're going to need a conservatively-correct solution that's different
from this anyway because hoisting allocations is *also* limited in its own
way.

I've attached a rather pedantic proof of the correctness of the algorithm.

The thing that concerns me most about the rewritten pass is that it isn't
actually validating joint post-dominance on input, so if you give it bad
input, it might be a little mystifying to debug the verifier failures.
2025-11-03 11:51:17 -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
Nate Chandler 71239d6357 [CoroutineAccessors] SIL represents callee alloc.
When its operand has coroutine kind `yield_once_2`, a `begin_apply`
instruction produces an additional value representing the storage
allocated by the callee.  This storage must be deallocated by a
`dealloc_stack` on every path out of the function.  Like any other stack
allocation, it must obey stack discipline.
2024-10-11 08:25:03 -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
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
Erik Eckstein e652f2c92e SIL: add the alloc_vector and vector instructions
* `alloc_vector`: allocates an uninitialized vector of elements on the stack or in a statically initialized global
* `vector`: creates an initialized vector in a statically initialized global
2023-12-09 18:49:55 +01:00
Evan Wilde 250082df25 [NFC] Reformat all the LLVMs
Reformatting everything now that we have `llvm` namespaces. I've
separated this from the main commit to help manage merge-conflicts and
for making it a bit easier to read the mega-patch.
2023-06-27 09:03:52 -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
Nate Chandler efca4e57c8 [SIL] Added on-stack pack metadata marker insts.
The new alloc_pack_metadata and dealloc_pack_metadata are inserted as
part of IRGen lowering.  The former indicates that the next instruction
might result in on-stack pack metadata being emitted.  The latter
indicates that this is the position at which metadata emitted on behalf
of its operand should be cleaned up.
2023-06-05 08:11:28 -07:00
Nate Chandler ddd0157abb [StackNesting] Handle stackAlloc builtins.
Both stackAlloc and unprotectedStackAlloc are paired with stackDealloc
builtins.
2023-06-05 08:11:28 -07:00
Nate Chandler 1cd2bdfacf [StackNesting] NFC: Allocs are just insts.
Stop requiring that allocation instructions produce single values.
2023-06-05 08:11:28 -07:00
Nate Chandler 389fb97b44 [StackNesting] NFC: Added helper.
Just pull the notion that the alloc corresponds to operand 0 into a
helper method.
2023-06-05 08:11:28 -07:00
Nate Chandler 8c8f5dfc2a [StackNesting] Gardening: Fixed typos. 2023-06-05 08:11:27 -07:00
Erik Eckstein 6d13bf188f SIL Optimizer: handle dead-end infinite loops in StackNesting
Fixes a verifier crash caused by a not properly nested alloc-dealloc pair

rdar://109204178
2023-05-11 16:16:13 +02:00
Slava Pestov ee386558b9 SILOptimizer: Handle alloc_pack in StackNesting 2023-05-05 22:45:03 -04: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
Arnold Schwaighofer 9f2b6a4ebb Reuse _ContiguousArrayStorage<AnyObject> metadata for any class or objc generic type
Reduces the number of _ContiguousArrayStorage metadata.

In order to support constant time bridging we do need to set the correct
metadata when we bridge to Objective-C. This is so that the type check
succeeds when bridging back from Objective-C to reuse the storage
instance rather than bridging the elements.

To support dynamically setting the `_ContiguousArrayStorage` element
type i needed to add support for optimizing `alloc_ref_dynamic`
throughout the optimizer.

Possible future improvements:
* Use different metadata such that we can disambiguate native Swift
  classes during destruction -- allowing native release rather then unknown
  release usage.
* Optimize the newly added semantic function
  getContiguousArrayStorageType

rdar://86171143
2022-02-16 07:55:34 -08:00
Erik Eckstein 383c52aa35 SIL: rename dealloc_ref [stack] -> dealloc_stack_ref
Introduce a new instruction `dealloc_stack_ref ` and remove the `stack` flag from `dealloc_ref`.

The `dealloc_ref [stack]` was confusing, because all it does is to mark the deallocation of the stack space for a stack promoted object.
2022-01-07 16:20:27 +01:00
Erik Eckstein cf17fd4df4 SILOptimizer: Use BasicBlockData in the StackNesting utility 2021-01-20 16:09:01 +01:00
Jordan Rose 171ff440fc Remove swift::reversed in favor of llvm::reverse (#27610)
The former predates the latter, but we don't need it anymore! The
latter has more features anyway.

No functionality change.
2019-10-10 17:16:09 -07:00
Erik Eckstein 8cdeb45284 StackNesting: fix a corner case crash related to unreachable blocks
When deallocs are inserted at block boundaries, it's necessary to recompute the data flow.

rdar://problem/55249524
2019-09-12 13:08:31 +02:00
Erik Eckstein e433759e73 SILOptimizer: fix a stupid bug in StackNesting which can cause a miscompile in functions with unreachable blocks.
rdar://problem/47973577
2019-02-27 10:17:46 -08:00
Erik Eckstein 767ad5e70a Inliner: fix a stack nesting problem when inlining coroutines
Beside fixing the compiler crash, this change also improves the stack-nesting correction mechanisms in the inliners:

* Instead of trying to correct the nesting after each inlining of a callee, correct the nesting once when inlining is finished for a caller function.
This fixes a potential compile time problem, because StackNesting iterates over the whole function.
In worst case this can lead to quadratic behavior in case many begin_apply instructions with overlapping stack locations are inlined.

* Because we are doing it only once for a caller, we can remove the complex logic for checking if it is necessary.
We can just do it unconditionally in case any coroutine gets inlined.
The inliners iterate over all instruction of a function anyway, so this does not increase the computational complexity (StackNesting is roughly linear with the number of instructions).

rdar://problem/47615442
2019-02-01 08:32:19 -08:00
Erik Eckstein 787c35f165 SILOptimizer: correctly handle unreachable blocks in StackNesting.
Instead of some special treatment of unreachable blocks, model unreachable as implicitly deallocating all alive stack locations at that point.
This requires an additional forward-dataflow pass. But it now correctly models the problem and fixes a compiler crash.

rdar://problem/47402694
2019-01-25 11:29:21 -08:00
Arnold Schwaighofer df1eb708fb Fix StackNesting to work with partial_apply [stack] 2019-01-15 11:20:33 -08:00
John McCall 18a8ed6090 Fix a stack-discipline bug with inlining coroutines. 2018-11-05 17:14:29 -05:00
Andrew Trick 12bb49f57a Expose SIL/BasicBlockUtils for critical edge splitting.
The primary interfaces for CFG manipulation belong in SIL. This is
just what's necessary to fix SILCloner.
2018-10-22 08:34:57 -07:00
Davide Italiano 5e7c3084bd [StackNesting] Preserve debug info correctly when solving.
Some of the `dealloc_stack` instructions inserted where getting
 a wrong scope. This manifests when running AllocBoxToStack because
it uses StackNesting as an utility. Yet another improvement in
debug informations at `-Onone`.

Fixes SR-6738.
2018-01-11 16:50:00 -08: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
practicalswift 7eb7d5b109 [gardening] Fix 100 typos. 2017-04-18 17:01:42 +02:00
Erik Eckstein 1702b831e4 StackNesting: fix use-after-free problem.
rdar://problem/31470661
2017-04-06 08:40:31 -07:00
Erik Eckstein a43a844838 StackNesting: use the right debug locations for inserted deallocation instructions
Fixes a crash because we used a return-location for a deallocation instruction.

rdar://problem/31458587
rdar://problem/31458617
2017-04-05 18:05:39 -07:00
Erik Eckstein c41de405c4 fix a wrong assert in StackNesting 2017-03-30 09:33:00 -07:00
Erik Eckstein 607318e0c7 Utility for correcting the nesting of stack allocation/deallocation instructions in SIL.
This is useful for optimizations (like AllocBoxToStack) which create (de-)alloc_stack instructions.
They can just insert the new instructions anywhere without worrying about nesting and correct the nesting afterwards.
2017-03-29 15:41:04 -07:00