-
Notifications
You must be signed in to change notification settings - Fork 7.9k
Infer value is not undef within foreach #4982
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
Conversation
/* Handle cases such as `$i = &$obj->typedProperty`, which can change the type of assignments */ | ||
tmp = t2; | ||
} | ||
// fprintf(stderr, "Previously %x for FE_FETCH_ARR at line %d\n", (int)tmp, (int)opline->lineno); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please split this into a separate PR and remove the commented code.
I also don't really understand the particular logic you used here. Possibly what you want is just tmp = (t2 & MAY_BE_REF)
? That's the only thing that should be inherited from the original variable...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.
I also don't really understand the particular logic you used here. Possibly what you want is just
tmp = (t2 & MAY_BE_REF)
? That's the only thing that should be inherited from the original variable...
I wasn't familiar with why t2
was being used or if there were edge cases that I wasn't aware of that would require t2
(e.g. if this was deliberate for some op types). Apparently not.
I discovered that opcache infers that the variable can be undefined within the loop body (or any types prior to the loop) This seems to be unnecessary - the sources of blocks outside of the loop body include both FE_FETCH_R and FE_FETCH_RW.
537c71a
to
2fa69a8
Compare
Add a specialized opcode handler to use for `===`/`!==` when: 1. At least one side is a $cv, and the other is a $cv or CONST (avoids the need to free operands) 2. Neither operand can be undefined or a reference (avoids the need for error handling and dereferencing) ``` // Elapsed time decreased from 0.275 seconds to 0.243 seconds in combination // with PR php#4982 function count_same(array $values) { $same = 0; foreach ($values as $x) { foreach ($values as $y) { if ($y === $x) { $same++; } } } return $same; } $values = range(0, 5000); $values[] = new stdClass(); $values[] = null; $values[] = 3; $start = microtime(true); $total = count_same($values); ```
Unfortunately this won't work: The old value is going to be preserved when FE_FETCH aborts the iteration, as we don't distinguish the two cases here. It also looks like we have a related miscompile:
This prints |
That miscompile predates this PR, and can also be found in php 7.4-dev. I also see the same issue (both with and without this PR) for this example:
Opcache enabled (both 8.0 NTS and 7.4 debug zts):
No opcache (correct behavior):
Could you give a different example - both of these examples were broken prior to this PR. I would have hoped that the ssa graph could take the definitions from the locations of the |
I can also reproduce this issue for the example in #4982 (comment) in php 7.2.6, and 7.3.9/7.3.14. 7.1.30 seems to be unaffected |
Sorry for being unclear, the miscompile is just something I noticed while looking at this PR, it's not caused by it (though I suspect the root issue is the same). I think we'll want to adjust SSA construction to use the pre-assignment value on the loop exit edge. This is slightly fishy in that we don't do it anywhere else, but I also don't think it should cause any issues in this case. |
To clarify what I mean, currently we generate the following type-inferred SSA form for my code example:
Note that the phi node in BB3 uses This will allow us to treat op2 of FE_FETCH as a proper no-val use. Which we currently already mistaken do, resulting in the DCE issue and possibly other issues, see php-src/ext/opcache/Optimizer/zend_ssa.h Lines 218 to 220 in 2ede8db
Once this is done, we can also change type-inference as proposed here. |
@nikic The problem, that FE_FETCH in BB1 includes ASSIGN to |
@dstogov I agree that having the separate ASSIGN (like we did in PHP 5) would be cleanest, but I'm not sure it's worth it just for the SSA representation. Using |
For now, don't treat FE_FETCH op2 as no-val use. See GH-4982.
I've committed 87691e7 as a temporary fix for the DCE issue. |
Add a specialized opcode handler to use for `===`/`!==` when: 1. At least one side is a $cv, and the other is a $cv or CONST (avoids the need to free operands) 2. Neither operand can be undefined or a reference (avoids the need for error handling and dereferencing) ``` // Elapsed time decreased from 0.275 seconds to 0.243 seconds in combination // with PR php#4982 function count_same(array $values) { $same = 0; foreach ($values as $x) { foreach ($values as $y) { if ($y === $x) { $same++; } } } return $same; } $values = range(0, 5000); $values[] = new stdClass(); $values[] = null; $values[] = 3; $start = microtime(true); $total = count_same($values); ```
I've opened #5040 to address the issue in SSA construction. I'll close this PR in favor of that one. |
I discovered that opcache infers that the variable can be undefined
within the loop body (or any types prior to the loop)
This seems to be unnecessary - the sources of blocks outside of the loop body
include both FE_FETCH_R and FE_FETCH_RW.