Skip to content

DI: self-consumed analysis rework, volume 2 #12454

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged

Conversation

slavapestov
Copy link
Contributor

@slavapestov slavapestov commented Oct 16, 2017

Copy link
Contributor

@gottesmm gottesmm left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would like to review this when you are done.

@slavapestov slavapestov force-pushed the di-self-consumed-analysis-volume-2 branch from a0a125b to 0b1f941 Compare October 17, 2017 05:05
@slavapestov
Copy link
Contributor Author

@swift-ci Please smoke test

@slavapestov slavapestov force-pushed the di-self-consumed-analysis-volume-2 branch 3 times, most recently from 87549a0 to bdc687d Compare October 18, 2017 10:03
@slavapestov
Copy link
Contributor Author

@gottesmm Executable tests now pass, and the crashes in the JIRAs I referenced in the PR description no longer crash. I need to add more tests though, and update some existing SIL test cases, since code generation patterns changed a bit.

@slavapestov
Copy link
Contributor Author

Found another dupe in JIRA :)

@slavapestov slavapestov force-pushed the di-self-consumed-analysis-volume-2 branch from 5874f71 to 44a8e02 Compare October 18, 2017 10:36
@slavapestov slavapestov changed the title DI WIP DI: self-consumed analysis rework, volume 2 Oct 18, 2017
@slavapestov slavapestov force-pushed the di-self-consumed-analysis-volume-2 branch 2 times, most recently from 18fad41 to e69c864 Compare October 19, 2017 00:23
@slavapestov
Copy link
Contributor Author

@swift-ci Please test

@slavapestov
Copy link
Contributor Author

@swift-ci Please test source compatibility

@slavapestov
Copy link
Contributor Author

@gottesmm Can you review this PR now?

@slavapestov
Copy link
Contributor Author

@swift-ci Please test

@slavapestov
Copy link
Contributor Author

@swift-ci Please test source compatibility

@swift-ci
Copy link
Contributor

Build failed
Swift Test OS X Platform
Git Sha - e69c8645d13a1ec1a848cc99c7668740dc33ed7f

@gottesmm
Copy link
Contributor

@slavapestov Doing it now.

@slavapestov
Copy link
Contributor Author

/Users/buildnode/jenkins/workspace-private/swift-PR-source-compat-suite/swift/include/swift/AST/Module.h:31:10: fatal error: 'swift/Syntax/SyntaxNodes.h' file not found
#include "swift/Syntax/SyntaxNodes.h"
         ^~~~~~~~~~~~~~~~~~~~~~~~~~~~
1 error generated.

@harlanhaskins any ideas?

@harlanhaskins
Copy link
Contributor

Got a link to the specific failure? Likely a file importing Module.h that didn’t declare the syntax headers as a dependency.

Copy link
Contributor

@gottesmm gottesmm left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Slava, I am just going to do post commit. I want to ensure you can make progress. I have a few comments so far, but most are about adding documentation and why you are able to make assumptions. I was able to understand why, but I had to think about it. I shouldn't have to think about why something is correct when certain assumptions are made in the source. It should be explicit either via asserts or comments.

if (Op->getOperandNumber() == 1) {
// The initial store of 'self' into the box at the start of the
// function. Ignore it.
if (auto *Arg = dyn_cast<SILArgument>(SI->getSrc()))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this is safe. What if you have multiple stores of the argument to the box in the function?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oops, I didn't update this singular comment. This is a case where you should comment that you know that the argument is owned and can only be consumed once since otherwise the ownership verifier would blow up. That being said, you should probably add an assert that you only see one of these while iterating over the loop.

In a throwing or failable initializer for a class, the typical pattern
is that an apply or try_apply consumes the self value, and returns
success or failure. On success, a new self value is produced.
On failure, there is no new self value. In both cases, the original
self value no longer exists.

We used to model this by attempting to look at the apply or try_apply
instruction, and figure out from subsequent control flow which
successor block was the success case and which was the error case.

The error blocks were marked as such, and a dataflow analysis was used
to compute whether 'self' had been consumed in each block reachable
from the entry block.

This analysis was used to prevent invalid use of 'self' in catch
blocks when the initializer delegation was wrapped in do/catch;
more importantly, it was also used to know when to release 'self'
on exit from the initializer.

For example, when we 'throw e' here, 'self' was already consumed
and does not need to be released -- doing so would cause a crash:

do {
  try self.init(...)
} catch let e {
  // do some other cleanup
  throw e
}

On the other hand, here we do have to release 'self', otherwise we
will exit leaking memory:

do {
  try someOtherThing()
  self.init(...)
} catch let e {
  // do some other cleanup
  throw e
}

The problem with the old analysis is that it was too brittle and did
not recognize certain patterns generated by SILGen. For example, it
did not correctly detect the failure block of a delegation to a
foreign throwing initializer, because those are not modeled as a
try_apply; instead, they return an Optional value.

For similar reasons, we did not correctly failure blocks emitted
after calls to initializers which are both throwing and failable.

The new analysis is simpler and more robust. The idea is that in the
success block, SILGen emits a store of the new 'self' value into
the self box. So all we need to do is seed the dataflow analysis with
the set of blocks where the 'self' box is stored to, excluding the
initial entry block.

The new analysis is called 'self initialized' rather than 'self
consumed'. In blocks dominated by the self.init() delegation,
the result is the logical not of the old analysis:

- If the old analysis said self was consumed, the new one says self
 is not initialized.

- If the old analysis said self was not consumed, the new analysis
  says that self *is* initialized.

- If the old analysis returned a partial result, the new analysis
  will also; it means the block in question can be reached from
  blocks where the 'self' box is both initialized and not.

Note that any blocks that precede the self.init() delegation now
report self as uninitialized, because they are not dominated by
a store into the box. So any clients of the old analysis must first
check if self is "live", meaning we're past the point of the
self.init() call. Only if self is live do we then go on to check
the 'self initialized' analysis.
This changes code generation a bit, because now the conditional
state bitmap uses a bit to track if the 'self' box was stored,
not if the 'self' value was consumed. In some cases, this
eliminates an extra bit, in other places it introduces an
extra bit, but it really doesn't matter because LLVM will
optimize this bit manipulation easily.
@slavapestov slavapestov force-pushed the di-self-consumed-analysis-volume-2 branch from e69c864 to 196559e Compare October 20, 2017 23:43
@slavapestov
Copy link
Contributor Author

@swift-ci Please test

1 similar comment
@slavapestov
Copy link
Contributor Author

@swift-ci Please test

@slavapestov
Copy link
Contributor Author

@swift-ci Please test source compatibility

1 similar comment
@slavapestov
Copy link
Contributor Author

@swift-ci Please test source compatibility

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants