-
Notifications
You must be signed in to change notification settings - Fork 424
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
I/O and Destroying temporaries at the end of the block vs end of statement #11492
Comments
see also #11534 |
mppf
added a commit
that referenced
this issue
Jan 23, 2020
Improve expiring value analysis This PR makes two improvements to expiring value analysis. * It updates the analysis to consider variables mentioned in a defer statement to expire at the end-of-block. This resolves issue #14797. * It changes the logic for inserting into the map detecting captures. Now, a capture will not be detected before the variable it is describing is initialized. This resolves a problem I had not noticed with the I/O example from issue #11492. I also updated the test to output the array that was read, so that it will be more obvious if this pattern fails again. Resolves #14797. Reviewed by @benharsh - thanks! - [x] full local testing
mppf
added a commit
that referenced
this issue
Mar 4, 2020
Use simpler rule for deinit point This PR updates the compiler to use simpler rules for when a variable is dead. The expiring value analysis (proposed in #13704) did not end up being as useful as initially expected for copy elision. As a result we are shifting to a simpler design for when copy elision can occur - #14874. That leaves the expiring value analysis impacting the deinitialization point (#11534 #11492) and the ownership transfer from non-nilable checking (#13600). However the ownership transfer checking is not significantly improved with expiring value analysis (it is approximately as effective when handled by the must-alias analysis in the nil checker). That leaves the deinit points. Since a copy elision can cause a variable to be effectively dead in the current scope after it occurs, there is some relationship between deinit points and copy elision. That is why #13704 tried to address these two together. For example: ``` chapel proc acceptsInIntent( in arg ) { ... } proc test() { var x: MyRecord; ... acceptsInIntent(x); // copy elision occurs here // x is effectively dead in this scope // accesses through refs/borrows hopefully are compilation errors // but could be use-after-frees. } ``` However since the copy elision rule is relatively simple, this PR changes the rule for when a variable is dead to simply include the copy elision rule. Besides that, it is based upon the simpler rule in #13704 / #11534 (comment) . Here is the rule as of this PR: > A variable is dead: > * after copy elision if it occurred (after the last mention is > used to copy-initialize a variable or in intent argument) > * for the results of nested call expressions not involved in initializing > a user variable or reference after the end of the statement > * otherwise, at the end of the scope in which it is declared There are two important things to note about the above rule: 1. "involved in initializing a user variable or reference" includes through split-init 2. the above rule applies to module-scope variables (aka global variables) as well as local variables. For global variables Also note that `--no-early-deinit` is available to make all temps deinited end-of-block. This PR takes the following steps to implement the change to deinitialization points: * add a shared helper isInitOrReturn to CallExpr.cpp; use it in possiblyInitsDestroyedVariable * removes --report-expiring flag and the entire expiring value analysis * adjusts the normalizer to leave temps in module init functions (instead of making them global variables) so that this can be determined later (after split-inits are known) * fixes a few bugs with nil checking * adds MarkTempsVisitor / gatherTempsDeadLastMention / markTempsDeadLastMention which are called from fixPrimInitsAndAddCasts in resolveFunction.cpp. This analyzes temp variables by how they are used to determine if they are used to initialize a user variable. Besides adding `FLAG_DEAD_LAST_MENTION` or `FLAG_DEAD_END_OF_BLOCK`, if the variable is in a module initialization function, it will pull it out into global scope. * insertReturnTemps marks the new temps since this runs after MarkTempsVisitor * adjust several tests that have use-after-frees according to the new rules: * nbody_orig_1.chpl * assignRecord.chpl * owned-class-instantiation-types-user-init.chpl * owned-class-instantiation-types.chpl * (and these also which were added after checking in PR #15072 detected an issue with them) * iter-return-first-borrowed-array-element.chpl * owned-record-instantiation-types-user-init.chpl * classes/initializers/generics/inheritance/inherited.chpl Reviewed by @benharsh - thanks! - [x] full local testing
PR #15041 implements a simpler rule for deinit points that enables this program to behave as originally expected. |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
I was writing a program testing I/O and was surprised by its behavior.
I was surpsied that B did not equal A at the end of this program. What's going on?
This is the first compelling case I've seen that might argue for destroying temporaries at the end of a statement rather than the end of a block. However there are other ways to handle this should we decide to support this pattern.
See also #9532 which includes some discussion of where to destroy temporaries.
The text was updated successfully, but these errors were encountered: