Commit Graph

74 Commits

Author SHA1 Message Date
Andrew Trick
a2fac95f9f OSSA ownership optimization RAUW utility fixes.
Verify that the OwnershipRAUWUtility always preserves the original
borrow scope by exhaustively switching over OperandOwnership.

And related cleanup.
2021-01-23 18:15:58 -08:00
Michael Gottesman
2ae43f97af [ownership] Eliminate Optional return value APIs from OwnershipUtils in favor of an Invalid enum case.
Should be NFC.
2021-01-14 11:02:10 -08:00
swift-ci
07dbf4ef57 Merge pull request #35157 from atrick/remove-deadblocks 2020-12-20 17:07:35 -08:00
Andrew Trick
fe6e87913a Remove the default DEBlocks = nullptr argument from verifyOwnership.
Passing nullptr was undefined behavior because ReborrowVerifier takes
a reference to DeadEndBlocks.
2020-12-20 14:46:43 -08:00
Michael Gottesman
368f8acc4b [sil] Eliminate a confusing optional method result by changing type dependent operands to have OwnershipKind::None instead of returning Optional::None from Operand::getOperandOwnership().
This eliminates when talking about this API an ambiguity in between
Optional::None (the C++ ADT) and OwnershipKind::None (the ownership kind).
2020-12-18 19:33:27 -08:00
Andrew Trick
f777e0a70e Convert OperandOwnership from an enum class to a struct enum. 2020-12-17 21:24:41 -08:00
Andrew Trick
b1dba2554e Introduce OperandOwnership to classify OSSA uses.
Migrating to this classification was made easy by the recent rewrite
of the OSSA constraint model. It's also consistent with
instruction-level abstractions for working with different kinds of
OperandOwnership that are being designed.

This classification vastly simplifies OSSA passes that rewrite OSSA
live ranges, making it straightforward to reason about completeness
and correctness. It will allow a simple utility to canonicalize OSSA
live ranges on-the-fly.

This avoids the need for OSSA-based utilities and passes to hard-code
SIL opcodes. This will allow several of those unmaintainable pieces of
code to be replaced with a trivial OperandOwnership check.

It's extremely important for SIL maintainers to see a list of all SIL
opcodes associated with a simple OSSA classification and set of
well-specified rules for each opcode class, without needing to guess
or reverse-engineer the meaning from the implementation. This
classification does that while eliminating a pile of unreadable
macros.

This classification system is the model that CopyPropagation was
initially designed to use. Now, rather than relying on a separate
pass, a simple, lightweight utility will canonicalize OSSA
live ranges.

The major problem with writing optimizations based on OperandOwnership
is that some operations don't follow structural OSSA requirements,
such as project_box and unchecked_ownership_conversion. Those are
classified as PointerEscape which prevents the compiler from reasoning
about, or rewriting the OSSA live range.

Functional Changes:

As a side effect, this corrects many operand constraints that should
in fact require trivial operand values.
2020-12-16 01:58:53 -08:00
Michael Gottesman
c35ff33a1f [ownership] Add a subclass for all OwnershipForwarding instructions and fix the classof to the various ownership abstract classes so isa/dyn_cast work.
I think what was happening here was that we were using one of the superclass
classofs and were getting lucky since in the place I was using this I was
guaranteed to have single value instructions and that is what I wrote as my
first case X ).

I also added more robust checks tieing the older isGuaranteed...* APIs to the
ForwardingOperand API. I also eliminated the notion of Branch being an owned
forwarding instruction. We only used this in one place in the compiler (when
finding owned value introducers), yet we treat a phi as an introducer, so we
would never hit a branch in our search since we would stop at the phi argument.
The bigger picture here is that this means that all "forwarding instructions"
either forward ownership for everything or for everything but owned/unowned.

And for those listening in, I did find one instruction that was from an
ownership forwarding subclass but was not marked as forwarding:
DifferentiableFunctionInst. With this change, we can no longer by mistake have
such errors enter the code base.
2020-11-18 12:34:18 -08:00
Michael Gottesman
a718e7d156 [ownership] Centralize the concept of isReborrow on BorrowingOperand.
A reborrow occurs when a Borrowing Operand ends the lifetime of a borrowed value
and propagates forward a new guaranteed value that continues the guaranteed
lifetime of the value.
2020-11-15 16:41:54 -08:00
Michael Gottesman
a294ab61ad [ownership] Eliminate OperandOwnershipKindMap in favor of OwnershipConstraint.
I also used this as a moment to clarify the lattice that related
ValueOwnershipKind and OwnershipConstraint.
2020-11-10 19:07:30 -08: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
Michael Gottesman
f16c4ba203 [ownership] Convert ValueOwnershipKind to have an Invalid state instead of using optional.
At Andy's request. If it creates too much noise in covered switches, we may go
back to the original.
2020-11-10 10:34:44 -08:00
Michael Gottesman
642a993702 [ownership] Rename Operand::isConsumingUse() -> Operand::isLifetimeEnding().
This makes it clearer that isConsumingUse() is not an owned oriented API and
returns also for instructions that end the lifetime of guaranteed values like
end_borrow.
2020-11-08 13:23:17 -08:00
Michael Gottesman
e5c98ad2c7 [ownership] Now that we allow reborrows eliminate isForwardingSubValue it is dead. 2020-11-05 19:42:10 -08:00
Meghana Gupta
601ea65b5d [ownership] Add a new ReborrowVerifier
This updates how we model reborrow's lifetimes for ownership verification.
Today we follow and combine a borrow's lifetime through phi args as well.
Owned values lifetimes end at a phi arg. This discrepency in modeling
lifetimes leads to the OwnershipVerifier raising errors incorrectly for
cases such as this, where the borrow and the base value do not dominate
the end_borrow:

bb0:
  cond_br undef, bb1, bb2
bb1:
  %copy0 = copy_value %0
  %borrow0 = begin_borrow %copy0
  br bb3(%borrow0, %copy0)
bb2:
  %copy1 = copy_value %1
  %borrow1 = begin_borrow %copy1
  br bb3(%borrow1, %copy1)
bb3(%borrow, %baseVal):
  end_borrow %borrow
  destroy_value %baseVal

This PR adds a new ReborrowVerifier. The ownership verifier collects borrow's
lifetime ending users and populates the worklist of the ReborrowVerifier
with reborrows and the corresponding base value.
ReborrowVerifier then verifies that the lifetime of the reborrow is
within the lifetime of the base value.
2020-10-29 20:46:37 -07:00
Slava Pestov
7bc5c5ecb1 SIL: Fix ownership verifier to handle some missing interior pointer projections
My upcoming SILGen change introduces more stores directly into
the result of a ref_element_addr or struct_element_addr in some
cases. Make sure we handle the failing combinations flagged by
the ownership verifier.
2020-10-10 20:04:50 -04:00
Michael Gottesman
a1f4a43483 [ownership] Teach the ownership verifier how to verify that guaranteed yielded values are only used within the coroutine's lifetime.
I think validating this was an oversight from the bringup of coroutines. I
discovered this while writing test cases for coroutine lifetime extension. I
realized it was possible to write a test case that should have triggered this
but was not.

I added some tests to make sure that we continue to flag this in the future.

rdar://69597888
2020-09-25 17:38:53 -05:00
Michael Gottesman
d82470a07d [ownership] Merge regularUsers and implicitRegularUsers into just "regularUsers".
When bringing up ownership I thought that it might be useful to distinguish
"normal users" that just require liveness and are real transitive users vs
implicit users that require liveness (I provide a constrasting example
below). Turns out this distinction did not provide any benefit, so I am ripping
it out so I can refactor this code to be used in other parts of the compiler
where we only want a single array of "normal users".

This is just a pure refactor. NFCI.

--------------------------------------------------------------------------------

An example of the former would be an apply that uses an owned value as a
guaranteed parameter:

```
bb0(%0 : @owned $Klass):
  apply %func(%0) : $@convention(thin) (@guaranteed $Klass) -> ()
```

while an example of the latter is an end_apply of a coroutine that takes an
owned value as a guaranteed parameter:

```
bb0(%0 : @owned $Klass):
  (%result, %token) = begin_apply %func(%0)
  ...
  end_apply %token
  destroy_value %0
```

In the latter, we can not move the destroy_value past the end_apply.
2020-08-31 14:33:06 -07:00
Michael Gottesman
d29bca5a53 [ownership] Extract out the computation of implicit uses for BorrowingOperands and InteriorPointerOperands into utilities on those classes.
I am currently working on updating SimplifyCFG for ownership. One thing that I
am finding that I need is the ability to compute the lifetime of a guaranteed
argument from a checked_cast_br/switch_enum in order to convert said
instructions to a br. The issue comes from br acting as a consuming (lifetime
ending) use of a borrowed value, unlike checked_cast_br/switch_enum (which are
treated like forwarding instructions). This means that during the conversion we
need to insert a begin_borrow on the value before the new br instruction and
then create an end_borrow at the end of the successor block's argument's
lifetime.

By refactoring out this code from the ownership verifier, I can guarantee that
said lifetime computation will always match what the ownership verifier does
internally preventing them from getting out of sync.
2020-08-31 11:54:58 -07:00
Erik Eckstein
3c3425d098 SILOwnershipVerifier: fix a compiler error in the no-assert build. 2020-05-07 09:25:09 +02:00
Michael Gottesman
820d204285 [ownership] Change the ownership verifier textual error dumper to emit error counts on a per function instead of global basis.
This will just make them easier to update over time since adding a new function
doesn't require one to renumber the /entire/ file... *face-palm*.

Originally the reason why I added this is b/c I need to ensure that we handle
all errors exactly once so making sure we control the exact amount of emitted
errors is important. I can still do this with the new approach by just doing
per-function max error counts. Thus I also in this commit added FileCheck
patterns that implemented this scheme so now we have everything.
2020-05-06 09:39:19 -07:00
Michael Gottesman
82b08c5722 [ownership] Add new pass OwnershipVerifierTextualErrorDumper and use it in ownership verifier FileCheck tests instead of SILVerifier.
This will make it easier for me with a few further refactors to make the
ownership verifier testing mode emit per function error numbers instead of the
global error number that it is emitting now.

The reason why this is necessary is that today, the verification by
-sil-verify-all causes the errors to be emitted. That verification is done on a
per value level, rather than a per function level, so it is hard to get per
function error numbers without doing unprincipled things like propagating around
state saying what the current function being verified is.

This pass instead will let me make the error counter be per ErrorBuilder which
are created per function.

One thing to be aware of is that this /will/ cause SILValue::verifyOwnership to
not emit any output when the testing flag is enabled. This is to ensure I only
do not get duplicate textual error messages from the SILVerifier.
2020-05-04 13:58:56 -07:00
Michael Gottesman
973a82e85d [ownership] Cleanup how we emit filecheck-compatible verification errors for the ownership verifier.
This is doing a few things with a simple change to use a builder:

1. It cleans up how we emit errors so we have a builder object that constructs
errors. The errors then just become dumb POD data that the builder vends to
callers that via the boolean values describe what errors were found.

2. Now that we have one place where we are actually emitting these errors, I
cleaned up how we emit the errors by normalizing the output so function names
are quoted the same.

3. I changed our error emission so that we emit a unique count of the errors as
we emit them. This makes it so that our pattern matching is much more robust
against weird pattern match errors that can be difficult to debug due to the
errors having unrelated test cases/file check patterns bleed
together. Before/end checks eliminate this problem. I updated all of the
relevant test cases.

The reason /why/ I am doing this though is that I am going to be adding support
to the LinearLifetimeChecker for flagging objects that are outside of the
lifetime that we are verifying (meaning either before or after). This is going
to cause me to need to track /all/ non consuming uses when performing linear
lifetime checks and thus most likely emit more errors. I was finding it to be
difficult to update the current tests given the state of the world before this
patch, so I was inspired to clean this up to satisfy practical as well as debt
concerns.
2020-04-28 12:35:02 -07:00
Michael Gottesman
e1a19e4173 [sil] Split library into subfolders, while still building as a single library still.
Specifically, I split it into 3 initial categories: IR, Utils, Verifier. I just
did this quickly, we can always split it more later if we want.

I followed the model that we use in SILOptimizer: ./lib/SIL/CMakeLists.txt vends
 a macro (sil_register_sources) to the sub-folders that register the sources of
 the subdirectory with a global state variable that ./lib/SIL/CMakeLists.txt
 defines. Then after including those subdirs, the parent cmake declares the SIL
 library. So the output is the same, but we have the flexibility of having
 subdirectories to categorize source files.
2020-03-30 11:01:00 -07:00