Commit Graph

162 Commits

Author SHA1 Message Date
Meghana Gupta
a0a0ebb558 Enable Mem2Reg for allocs with load_borrow (#59350) 2022-06-15 14:37:10 -07:00
Anton Korobeynikov
df6e2ebbd5 [NFC] Fix use-after-free issues in debug printing inside SIL Mem2Reg (#42183)
Turned printing of addresses of instructions into instructions while there.
2022-05-02 11:35:09 -07:00
nate-chandler
4343d940ba Merge pull request #41751 from nate-chandler/mem2reg/bail-on-load-take-complex-projections
[Mem2Reg] Skip load [take] of cast projections.
2022-03-09 17:14:34 -08:00
Nate Chandler
3508d41459 [Mem2Reg] Skip load [take] of cast projections.
Already, load [take]s of struct_element_addr|tuple_element_addr
projections resulted in Mem2Reg bailing.  Expand that to include load
[take]s involving unchecked_addr_cast.

To handle load [take]s of (struct|tuple)_element_addr projections, it
would be necessary to replace the running value with a value obtained
from the original product by recursive destructuring, replacing the
value at the load [take]n address with undef, and then restructuring.

To handle load [take]s of cast projections, it would be necessary to use
unchecked_value_cast instead of unchecked_bitwise_cast.  But we would
need to still use unchecked_bitwise_cast in the case of load [copy]
because otherwise we would lose the original value--unchecked_value_cast
forwards ownership, and not all casts can be reversed (because they may
narrow).

For now, just bail out in the face of these complex load [take]s.
2022-03-09 08:39:33 -08:00
Erik Eckstein
a62a5caaf4 SILMem2Reg: fix a problem with leaking enum values
When optimizing an enum `store` to an `alloc_stack`, require that all uses are in the same block.
Otherwise it could be a `switch_enum` of an optional where the none-case does not have a destroy of the enum value.
After transforming such an `alloc_stack`, the value would leak in the none-case block.

It fixes the same OSSA verification error as done for TempRValueOpt in a previous commit.
2022-03-09 09:47:48 +01:00
Michael Gottesman
0e3bca99f5 [debug-var] When comparing address debug_value against value debug_value remove the diexpr from the address SILDebugVariable.
Otherwise they will never compare the same. Also restore the test that was
updated for the previous commit to its old state. I did this to ensure that each
commit would compile successfully and so that I could show the test associated
with this commit.
2022-02-09 14:06:23 -08:00
Nate Chandler
1464c1b1ff [NFC] Renamed LexicalLifetimesOption cases.
The cases' new names more accurately reflect what behavior occurs when
SILOptions::LexicalLifetimes is assigned that case.
2021-12-10 18:36:28 -08:00
Michael Gottesman
72eb5e2eec [move-operator] Specify if LexicalLifetimes is enabled using an enum instead of a bool.
The reason why I am doing this is that we are going to be enabling lexical
lifetimes early in the pipeline so that I can use it for the move operator's
diagnostics.

To make it easy for passes to know whether or not they should support lexical
lifetimes, I included a query on SILOptions called
supportsLexicalLifetimes. This will return true if the pass (given the passed in
option) should insert the lexical lifetime flag. This ensures that passes that
run in both pipelines (e.x.: AllocBoxToStack) know whether or not to set the
lexical lifetime flag without having to locally reason about it.

This is just chopping off layers of a larger patch I am upstreaming.

NOTE: This is technically NFC since it leaves the default alone of not inserting
lexical lifetimes at all.
2021-11-15 13:47:22 -08:00
Nate Chandler
ee9d309116 [NFC] Added TODO about phi pruning of lifetimes. 2021-11-02 11:51:04 -07:00
Nate Chandler
09dde09a8d [Mem2Reg] Replaced loop with getSingleSuccessor.
Thanks to the lack of critical edges in SIL, if a block B dominated by P
has a successor S which is not dominated by P, then B must have only a
single successor.  Used this fact to replace a loop over successors to a
call to getSinglePredecessor.

Also added an assertion that, in the notation above, B is dominated by
P.
2021-11-02 11:50:28 -07:00
Nate Chandler
797eab10c6 [Mem2Reg] Handled unreachables for single-block allocations.
Previously, it was asserted that any single-block allocation which had
valid memory after all instructions in the block were visited terminated
in unreachable.  That assertion was false--while all paths through that
block must end in unreachable, the block itself need not be terminated
with an unreachable inst.  Here, that is corrected by walking forward
from the block until blocks terminated by unreachables or blocks not
dominated by the block containing the alloc_stack are reached.  In the
first case, the lifetime is ended just before the unreachable inst.  In
the second, the lifetime is ended just before the branch to such a
successor block (i.e. just before the branch to a block which is not
dominated by the block containing the alloc_stack).
2021-10-27 13:42:51 -07:00
Nate Chandler
0fc25cc7f2 [Mem2Reg] Skipped unreachable blocks.
Uses of an alloc_stack instruction in an unreachable block must also be
unreachable.  Don't waste time optimizing unreachable code.
2021-10-27 13:38:57 -07:00
Nate Chandler
173c010faa [Mem2Reg] RAUW undef lexical lifetime phis.
Previously, if it was determined that a proactive phi was unnecessary,
it was removed, along with the phis for the lifetime and the original
value of which the proactive phi was a copy.  The uses of only one of
the three phis (namely, the proactive phi) was RAUW undef.  In the case
where the only usage of the phi was to branch back to the block that
took the phi as an argument, that was a problem.  Here, that is fixed by
giving all three phis the same treatment.  To avoid duplicating code,
that treatment is pulled out into a new lambda.

This was exposed by adding lifetime versions of some OSSA versions of
mem2reg tests that had been missed previously.
2021-10-20 12:06:46 -07:00
Nate Chandler
42f318d9ef [Basic] Renamed GraphNodeWorklist.
Addressed the TODO saying that DAGNodeWorklist should be renamed
GraphNodeWorklist.
2021-10-18 08:55:13 -07:00
Ben Barham
624337148b [NFC] Formatting cleanup to help with next conflicts 2021-10-15 17:15:51 +10:00
Nate Chandler
23a9a1d4c9 Moved lexical lifetime flag to SILOptions.
Previously, the flag was a LangOptioins.  That didn't make much sense because
this isn't really a user-facing behavior.  More importantly, as a member
of that type type it couldn't be accessed when setting up pass
pipelines.  Here, the flag is moved to SILOptions.
2021-10-13 13:47:44 -07:00
Nate Chandler
58695b5557 [Mem2Reg] Maintain write-only lexical lifetimes.
Previously, Mem2Reg would delete write-only alloc_stacks.  That is
incorrect for lexical alloc_stacks--if a var was assigned but never
read, we still want to keep it alive for the duration of that var's
lexical scope.
2021-10-12 10:46:32 -07:00
Nate Chandler
871088209e [Mem2Reg] Tied lexical borrow to initialization.
Previously, the lexical borrow scopes that were introduced to replace
lexical stack allocations were tied to the uses of the stored value.
That meant that the borrow scope could be both too long and also too
short.  It could be too long if there were uses of the value after the
dealloc stack and it would be too short if there were not uses of the
value while the value was still stored into the stack allocation.

Here, instead, the lexical borrow scopes are tied to the storage of a
value into the stack allocation.  A value's lexical borrow scope begins
when the storage is initialied with the value; its lexical borrow scope
ends when the storage is deinitialized.  That corresponds to the range
during which a var in the Swift source has a particular value assigned
to it.

Mem2Reg's implementation is split into a few steps:
(1) visiting the instructions in a basic block which contains all uses
    of an alloc_stack
(2.a) visiting the instructions in each basic block which contains a use
      of the alloc_stack
(2.b) adding phis
(2.c) using the last stored values as arguments to the new outgoing phi
      arguments
(2.c) replacing initial uses of the storage with the new incoming phi
      arguments
And here, (1) amounts to a special case of (2.a).

During (1) and (2.a):
(a) lexical borrow scopes are begun after store instructions for the
    values that were stored
(b) when possible, lexical borrow scopes are ended before instructions
    that deinitialize memory
    - destroy_addr
    - store [assign]
    - load [take]
For (1), that is enough to create valid borrow scopes.

For (2), there are two complications:
(a) Borrow scopes that are begun may not be ended (when visiting a
    single block's instructions).

    For example, when visiting

    bb1:
      store %instance to [init] %addr
      br bb2

    a borrow scope is started after the store but cannot be ended.

(b) There may not be enough information available to end borrow scopes
    when visiting instructions that would typically introduce borrow
    scopes.

    For example, when visiting

    bb2:
      %copy = load [copy] %addr
      %instance = load [take] %addr
      br bb3

    there is not enough information available to end the borrow scope
    that should be ended before the load [take]

To resolve these issues, both sorts of instructions are tracked.  For
(a), in StackAllocationPromoter::initializationPoints.  For (b), in
StackAllocationPromoter::deinitializationPoints.  Finally, a new
step is added:

(2.d) StackAllocationPromoter::endLexicalLifetimes does a forward CFG
      walk starting from the out-edge of each of the blocks which began
      but did not end lexical lifetimes.  At an out-edge, we do a check
      regarding unreachables is done, and we may end the borrow scope.
      Otherwise, the walk continues to the in-edge of each successor.
      At an in-edge, we look for an instruction from (b) (in
      unprecedentedDeinitializations) above.  If one is found, then we
      end the borrow scope before that instruction.  Otherwise, the walk
      continues to the out-edge of the block.
2021-10-12 10:46:22 -07:00
Nate Chandler
8b99d34221 [Gardening] Tweaked comment. 2021-10-11 09:12:37 -07:00
Nate Chandler
0827390188 [NFC] Marked endLexicalLifetimeInBlock static.
The function is currently only (and only ever intended to be) used only
within SILMem2Reg.
2021-10-11 09:12:37 -07:00
swift-ci
c51550f30e Merge remote-tracking branch 'origin/main' into rebranch 2021-09-30 15:11:41 -07:00
Evan Wilde
514fc83639 Fix missing template member in SmallVector
The ubuntu 18.04 Linux builder fails to build when the SmallVector does
not include the number of elements. This is a known issue and is
incorrect, but that is the version of clang on that OS.

The definition of SmallVector is already available transitively since
there are values of that type, I've just made it explicit.

llvm::SmallVector has a default parameter for the number of elements
while swift::SmallVector doesn't. I've gone ahead and made it explicitly
use the llvm::SmallVector to receive that.
2021-09-28 23:25:21 -07:00
Nate Chandler
79bc4cba31 [Mem2Reg] Lexical allocs create lexical borrows.
SILGen turns vars into alloc_boxes.  When possible, AllocBoxToStack
turns those into alloc_stacks.  In order to preserve the lexical
lifetime of those vars, the alloc_stacks are annotated with the
[lexical] attribute.  When Mem2Reg runs, it promotes the loads from
and stores into those alloc_stacks to uses of registers.  In order to
preserve the lexical lifetime during that transformation, lexical borrow
scopes must be introduces that encode the same lifetime as the
alloc_stacks did.  Here, that is done.
2021-09-27 20:29:47 -07:00
Nate Chandler
16cbae367b [SILMem2Reg] Added explanation to assert. 2021-09-27 20:29:46 -07:00
swift-ci
ebad328a4f Merge remote-tracking branch 'origin/main' into rebranch 2021-09-01 09:14:57 -07:00
Min-Yih Hsu
343d842394 [SIL][DebugInfo] PATCH 3/3: Deprecate debug_value_addr SIL instruciton
This patch removes all references to DebugValueAddrInst class and
debug_value_addr instruction in textual SIL files.
2021-08-31 12:01:04 -07:00
Min-Yih Hsu
e1023bc323 [DebugInfo] PATCH 2/3: Duplicate logics regarding debug_value_addr
This patch replace all in-memory objects of DebugValueAddrInst with
DebugValueInst + op_deref, and duplicates logics that handles
DebugValueAddrInst with the latter. All related check in the tests
have been updated as well.

Note that this patch neither remove the DebugValueAddrInst class nor
remove `debug_value_addr` syntax in the test inputs.
2021-08-31 11:57:56 -07:00
Arnold Schwaighofer
c288cbb6ab Fix some more SmallVector usage without an on stack size 2021-08-05 12:15:23 -07:00
Meghana Gupta
dc57d6b3e9 Disable Mem2Reg in ossa for alloc_stack [dynamic_lifetime]
With such alloc_stack we can pass over-consumed values to a
proactively added phi. Even though such a SIL does not have
issues at runtime, they raise an ownership verification error.
2021-07-08 00:33:23 -07:00
Meghana Gupta
42863d4090 Cleanup dead phis in SILMem2Reg for OSSA
Mem2Reg creates phis proactively that may be unnecessary.
Unnecessary phis are those without uses or operands to other
proactive phis that are unnecessary.

Even though this does not translate to a real issue at runtime, we can
see ownership verification errors. This PR identifies such phis and deletes them.
2021-07-08 00:33:18 -07:00
Meghana Gupta
2c3f1ac7e7 Rename misleading using BlockSet to BlockSetVector 2021-07-05 17:05:43 -07:00
Andrew Trick
0407a4e34a Add UpdatingInstructionIterator.
Track in-use iterators and update them both when instructions are
deleted and when they are added.

Safe iteration in the presence of arbitrary changes now looks like
this:

    for (SILInstruction *inst : deleter.updatingRange(&bb)) {
      modify(inst);
    }
2021-06-02 07:38:27 -07:00
Andrew Trick
0f88e0f3cc Rewrite instruction deletion logic in many passes
Fix innumerable latent bugs with iterator invalidation and callback invocation.

Removes dead code earlier and chips away at all the redundant copies the compiler generates.
2021-06-02 07:38:27 -07:00
Michael Gottesman
8821621647 [silmem2reg] Compute DomTreeLevels lazily.
We previously were computing this eagerly meaning that if we did not actually
need the DomTreeLevel map, we would calculate it and additionally incur a
relatively large malloc for the DenseMap (which stores by default IIRC 64 key
value pairs).

Now, we do it lazily ensuring that we only compute this if we actually need it
(when promoting non-single block stack allocations).
2021-04-18 11:56:47 -07:00
Michael Gottesman
c0e31d8dfc [sil-mem2reg] Add a simple scope data structure and use it to simplify some code.
We have for a long time talked about creating a scope like data structure for
use in the SILOptimizer. The discussion was whether or not to reuse the
infrastructure in SILGen that does this already. There were concerns about doing
so since the code in the SILOptimizer and SILGen can work differently.

With that in mind, I added a small AssertingScope class and built on top of that
a composition SIL level class called SILOptScope that one can use to add various
cleanups. One is able to both destructively pop at end of scope and pop along
early exits.

At an implementation level, I kept it simple and:

1. Represented a scope as a stack of Optional<Cleanup> which are just a wrapper
   around a std::function. The Optional is so that we can invalidate a cleanup.
2. Based all of these scopes around the idea that the user of the scope must
   invalidate the scope by hand. If not, the scope object will assert at the end
   of its RAII scope.
3. Rather than creating a whole class hierarchy, I just used std::function
   closures to keep things simple.
2021-04-14 11:42:31 -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
Michael Gottesman
7d5178b1d7 [sil-mem2reg] Rather than pass around a shared builder, pass around a shared SILBuilderContext.
This is a pretty old style of code that we are trying to move away from.
Specifically, we use to always store a global SILBuilder and manually manipulate
its insertion point/other state when we moved places. This was very bugprone so
we instead now create new SILBuilders when we want to create instructions at a
new insertion point and pass down a SILBuilderContext for global state (like the
list of inserted instructions/etc) to each SILBuilder.

I went through and updated all of the code to now follow this pattern.
2021-04-12 18:10:42 -07: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
Michael Gottesman
9f08ae8d9b [mem2reg] Reorganize code into a utilities section, a single stack alloc promotion, and a generalized MemoryToRegisters.
This code organization already existed in the file in content, but all of the
routines were mixed together. This splits the code by topic providing to the eye
easy code locality when reading.
2021-04-09 13:40:47 -07:00
Michael Gottesman
aaffd9aef7 [mem2reg] Make style consistently new swift style before adding support for borrows.
This is code that was written at the very beginning of Swift and has some early
style remaining interspirsed with newer swift style code. Since I am going to be
touching this and I don't expect other people to touch it for a while, I am
fixing up the formatting/reorganizing rather than leaving the style inconsistent.
2021-04-09 13:40:47 -07:00
Erik Eckstein
28a5eee217 SILMem2Reg: don't create an "undef" as a replacement of a load of an empty tuple.
Instead create the empty tuple value.
In general, it's not a good idea to create undef values in SIL.
2021-03-16 11:26:54 +01:00
Meghana Gupta
1f89d9ff89 Verify critical edges when -sil-verify-all is enabled 2021-03-03 23:45:56 -08:00
Erik Eckstein
fe10f98cf0 SIL: rename the SILBitfield.h header file to BasicBlockBits.h
NFC
2021-02-12 11:15:55 +01:00
Meghana Gupta
aed2dcb07b Fix Mem2Reg for store [assign]
This PR simplifies the handling of store [assign] in Mem2Reg by always
converting it to a store [init].
Also fixes an edge case where store [assign] in a non entry block was
not the first store of an alloc_stack with uses in multiple blocks.
2021-02-06 19:54:34 -08:00
Erik Eckstein
32224d2576 SILMem2Reg: a small cleanup
NFC
2021-01-27 16:40:14 +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
Eric Miotto
8e7f9c9cbd Revert "SIL: let SingleValueInstruction only inherit from a single SILNode." 2021-01-26 10:02:24 -08:00
Erik Eckstein
ff1991740a SIL: let SingleValueInstruction only inherit from a single SILNode.
This removes the ambiguity when casting from a SingleValueInstruction to SILNode, which makes the code simpler. E.g. the "isRepresentativeSILNode" logic is not needed anymore.
Also, it reduces the size of the most used instruction class - SingleValueInstruction - by one pointer.

Conceptually, SILInstruction is still a SILNode. But implementation-wise SILNode is not a base class of SILInstruction anymore.
Only the two sub-classes of SILInstruction - SingleValueInstruction and NonSingleValueInstruction - inherit from SILNode. SingleValueInstruction's SILNode is embedded into a ValueBase and its relative offset in the class is the same as in NonSingleValueInstruction (see SILNodeOffsetChecker).
This makes it possible to cast from a SILInstruction to a SILNode without knowing which SILInstruction sub-class it is.
Casting to SILNode cannot be done implicitly, but only with an LLVM `cast` or with SILInstruction::asSILNode(). But this is a rare case anyway.
2021-01-25 09:30:04 +01:00
Michael Gottesman
c026e95cce [ownership] Extract out SILOwnershipKind from ValueOwnershipKind into its own type and rename Invalid -> Any.
This makes it easier to understand conceptually why a ValueOwnershipKind with
Any ownership is invalid and also allowed me to explicitly document the lattice
that relates ownership constraints/value ownership kinds.
2020-11-10 14:29:11 -08:00