Commit Graph

19 Commits

Author SHA1 Message Date
Anthony Latsis
fec049e5e4 Address llvm::PointerUnion::{is,get} deprecations
These were deprecated in
https://github.com/llvm/llvm-project/pull/122623.
2025-07-29 18:37:48 +01: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
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
Andrew Trick
0bc30e6f49 Add ValueLifetimeBoundary::visitInsertionPoints
Given a computed ValueLifetimeBoundary, visit all the points at which
the lifetime needs to be terminated, e.g. via and end_borrow or
destroy_value.

Especially useful for creating a borrow scope over guaranteed uses.

This completely decouples the DeadBlocks analysis from the liveness
analysis.

It will allow phasing out the complex and bug-prone
ValueLifetimeAnalysis::Frontier API.
2021-10-06 09:23:17 -07:00
Erik Eckstein
b3a7792d1d Reinstate "SIL: add a StackList data structure with zero cost operations."
... with a fix for a non-assert build crash: I used the wrong ilist type for SlabList. This does not explain the crash, though. What I think happened here is that llvm miscompiled and put the llvm_unreachable from the Slab's deleteNode function unconditionally into the SILModule destructor.
Now by using simple_ilist, there is no need for a deleteNode at all.
2021-04-13 13:49:45 +02:00
Arnold Schwaighofer
ddfdf4779d Revert "SIL: add a StackList data structure with zero cost operations." 2021-04-12 12:48:16 -07:00
Erik Eckstein
0456d95cb0 SIL: Use StackList in BasicBlockWorklist and BasicBlockSetVector
plus: I moved both data structures into a separate header file.
2021-04-11 14:07:26 +02:00
Andrew Trick
2f34014ffc Rescue the ValueLifetimeAnalysis utility.
This is a basic SSA-based liveness algorithm. It should just do what
it says. This change rescues the core logic from the nonsense that's
been hacked in. There are now two APIs:

(1) computeLifetimeBoundary (new) only does lifetime analysis.  It
provides a new API that always succeeds and provides the last use
points so clients can do the right thing based on the user. I need
this for OSSA utilities.

(2) computeFrontier (old) emulates the original API with a simplified
version of the original logic.

Next steps:

- replace the old API with a separate utility that computes destroy
  insertion points. Completely remove DeadEndBlocks from lifetime
  analysis, because it simply has nothing to do with liveness.

- Gradually migrate clients to either the new lifetime API provided
  here or the new destroy insertion API to be provided later.
2021-03-17 23:08:11 -07:00
Erik Eckstein
462e58d3cb SILLocation: a big refactoring and reducing its size from 3 to 2 words
My goal was to reduce the size of SILLocation. It now contains only of a storage union, which is basically a pointer and a bitfield containing the Kind, StorageKind and flags. By far, most locations are only single pointers to an AST node. For the few cases where more data needs to be stored, this data is allocated separately: with the SILModule's bump pointer allocator.

While working on this, I couldn't resist to do a major refactoring to simplify the code:

* removed unused stuff
* The term "DebugLoc" was used for 3 completely different things:
    - for `struct SILLocation::DebugLoc` -> renamed it to `FilePosition`
    - for `hasDebugLoc()`/`getDebugSourceLoc()` -> renamed it to `hasASTNodeForDebugging()`/`getSourceLocForDebugging()`
    - for `class SILDebugLocation` -> kept it as it is (though, `SILScopedLocation` would be a better name, IMO)
* made SILLocation more "functional", i.e. replaced some setters with corresponding constructors
* replaced the hand-written bitfield `KindData` with C bitfields
* updated and improved comments
2021-01-29 20:28:21 +01:00
Erik Eckstein
f48191966c SILOptimizer: use BasicBlockSet instead of SmallPtrSet in various transformations.
It reduces compile time.
2021-01-27 10:31:17 +01:00
Erik Eckstein
3e8612b0d3 SILOptimizer: use the BasicBlockFlag utility in ValueLifetimeAnalysis 2021-01-21 21:31:41 +01:00
Michael Gottesman
d2de176264 [sil][value-lifetime] Add ValueLifetimeAnalysis::FrontierImpl = SmallVectorImpl<SILInstruction *>
Otherwise, one is always forced to use ValueLifetimeAnalysis::Frontier, a
SmallVector<SILInstruction *, 4>. This may not be a size appropriate for every
problem, so it makes sense to provide Frontier as a good rule of thumb, but use
FrontierImpl on the actual API boundary to loosen the constraint if the user
wishes to do so.
2020-11-15 16:41:47 -08:00
Meghana Gupta
0a21c4d96f Fix another use-after-free in SILCombine (#34168)
* Fix another use-after-free in SILCombine

swift::endLifetimeAtFrontier also needs to use
swift::emitDestroyOperation and delete instructions via callbacks that
can correctly remove it from the worklist that SILCombine maintains

* Add test for use-after-free in SILCombine
2020-10-06 13:37:05 -07:00
Michael Gottesman
962106fed3 [semantic-arc] When computing Lifetimes include all consuming uses, not just the final destroying use.
TLDR: This fixes an ownership verifier assert caused by not placing end_borrows
along paths where an enum is provable to have a trivial case. It only happens if
all non-trivial cases in a switch_enum are "dead end blocks" where the program
will end and we leak objects.

The Problem
-----------

The actual bug here only occurs in cases where we have a switch_enum on an enum
with mixed trivial, non-trivial cases and all of the non-trivial payloaded cases
are "dead end blocks". As an example, lets look at a simple switch_enum over an
optional where the .some case is a dead end block and we leak the Klass object
into program termination:

```
%0 = load [copy] %mem : $Klass
switch_enum %0 : $Optional<Klass>, case #Optional.some: bbDeadEnd, case #Optional.none: bbContinue

bbDeadEnd(%0a : @owned $Klass): // %0 is leaked into program end!
  unreachable

bbContinue:
  ... // program continue.
```

In this case, if we were only looking at final destroying uses, we would pass a
def without any uses to the ValueLifetimeChecker causing us to not have a
frontier at all causing us to not insert any end_borrows, yielding:

```
%0 = load_borrow %mem : $Klass
switch_enum %0 : $Optional<Klass>, case #Optional.some: bbDeadEnd, case #Optional.none: bbContinue

bbDeadEnd(%0a : @guaranteed $Klass): // %0 is leaked into program end and
                                     // doesnt need an end_borrow!
  unreachable

bbContinue:
  ... // program continue... we need an end_borrow here though!
```

This then trips the ownership verifier since switch_enum is a transforming
terminator that acts like a forwarding instruction implying we need an
end_borrow on the base value along all non-dead end paths through the program.

Importantly this is not actually a leak of a value or unsafe behavior since the
only time that we enter into unsafe territory is along paths where the enum was
actually trivial. So the load_borrow is actually just loaded the trivial enum
value.

The Fix
-------

In order to work around this, I realized that the right solution is to also
include the forwarding consuming uses (in this case the switch_enum use) when
determining the lifetime and that this solves the problem.

That being said, after I made that change, I noticed that I needed to remove my
previous manner of computing the insertion point to use for arguments when
finding the lifetime using ValueLifetimeAnalysis. Previously since I was using
only the destroying uses I knew that the destroy_value could not be the first
instruction in the block of my argument since I handled that case individually
before using the ValueLifetimeAnalysis. That invariant is no longer true as can
be seen in the case above if %0 was from a SILArgument itself instead of a load
[copy] and we were converting that argument to be a guaranteed argument.

To fix this, I taught ValueLifetimeAnalysis how to handle defs from
Arguments. The key thing is I noticed while reading the code that the analysis
only generally cared about the instruction's parent block. Beyond that, the def
being from an instruction was only needed to determine if a user is earlier in
the same block as the def instruction. Those concerns not apply to SILArgument
which dominate all instructions in the same block, so in this patch, we just
skip those conditional checks when we have a SILArgument. The rest of the code
that uses the parent block is the same for both SILArgument/SILInstructions.

rdar://65244617
2020-08-07 21:45:22 -07:00
Anthony Latsis
9fd1aa5d59 [NFC] Pre- increment and decrement where possible 2020-06-01 15:39:29 +03:00
Erik Eckstein
85789367a3 SILOptimizer: restructure the apply(partial_apply) peephole and the dead partial_apply elimination optimizations
Changes:

* Allow optimizing partial_apply capturing opened existential: we didn't do this originally because it was complicated to insert the required alloc/dealloc_stack instructions at the right places. Now we have the StackNesting utility, which makes this easier.

* Support indirect-in parameters. Not super important, but why not? It's also easy to do with the StackNesting utility.

* Share code between dead closure elimination and the apply(partial_apply) optimization. It's a bit of refactoring and allowed to eliminate some code which is not used anymore.

* Fix an ownership problem: We inserted copies of partial_apply arguments _after_ the partial_apply (which consumes the arguments).

* When replacing an apply(partial_apply) -> apply and the partial_apply becomes dead, avoid inserting copies of the arguments twice.

These changes don't have any immediate effect on our current benchmarks, but will allow eliminating curry thunks for existentials.
2020-02-11 12:48:39 +01:00
Ravi Kandhadai
d195ecc64a [SIL Optimization] Fix a bug in isAliveAtBeginOfBlock function
of ValueLifetimeAnalysis and generalize the utility so that it
can provide the critical edges as well, when it is invoked in
DoNotModifyCFG mode.
2019-10-25 18:19:40 -07:00
Andrew Trick
22500e7e6b Update the coding style for all code moved in the previous commit.
Requested by gottesmm during review.

Update the variable naming conventions to lower-camel.

Run clang-format.

I'm sure I missed some local variables somewhere--this was a best
effort.
2019-10-02 11:34:54 -07:00
Andrew Trick
bddc69c8a6 Organize SILOptimizer/Utils headers. Remove Local.h.
The XXOptUtils.h convention is already established and parallels
the SIL/XXUtils convention.

New:
- InstOptUtils.h
- CFGOptUtils.h
- BasicBlockOptUtils.h
- ValueLifetime.h

Removed:
- Local.h
- Two conflicting CFG.h files

This reorganization is helpful before I introduce more
utilities for block cloning similar to SinkAddressProjections.

Move the control flow utilies out of Local.h, which was an
unreadable, unprincipled mess. Rename it to InstOptUtils.h, and
confine it to small APIs for working with individual instructions.
These are the optimizer's additions to /SIL/InstUtils.h.

Rename CFG.h to CFGOptUtils.h and remove the one in /Analysis. Now
there is only SIL/CFG.h, resolving the naming conflict within the
swift project (this has always been a problem for source tools). Limit
this header to low-level APIs for working with branches and CFG edges.

Add BasicBlockOptUtils.h for block level transforms (it makes me sad
that I can't use BBOptUtils.h, but SIL already has
BasicBlockUtils.h). These are larger APIs for cloning or removing
whole blocks.
2019-10-02 11:34:54 -07:00