-
Notifications
You must be signed in to change notification settings - Fork 1.2k
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
ByteCode Updward Exposed mismatch after DeadStore #6511
Comments
Confirming on both Windows and Linux. Edit: I am going to investigate, though cannot promise that it would be very quick. |
This likely stems from this PR #6232 The method for tracking register lifetimes in the JIT was changed there and this test case seems to be hitting a code path that was missed in those changes. Following that change I think the JIT is meant to rely slightly more on info from the bytecode BUT Coerce_StrOrRegex is a backend only opcode - added in the optimiser - so perhaps whatever info is needed from the bytecode isn't there/needs to be sorted when the opcode is added. Bizarrely running the snippet with -dump:backend to print out all the optimiser steps suppresses the error, considering this is not meant to do anything different other than add logging that doesn't make much sense. |
In my testing of jitting Async Generators I have hit a very similar looking assert jitting A set of changes that add several lines to the bytecode but do nothing detectably different in the interpretor - however in the JIT this prevented segfaulting/hitting an assert about register lifetimes. The But without more digging I don't know what would need to be changed to ensure the registers associated with that instruction get matching lifetimes. |
Slightly reduced test: function opt(o, zz) {
function foo () {
String.prototype.replace.call(o, zz, {});
}
foo ();
}
for (let xi = 0; xi < 500; xi++) {
opt({}, "kkkk");
} |
Add minimal example from chakra-core#6511.
Add minimal example from chakra-core#6511.
Could this be related to these which hit similar assertions: #6181 #6487 Extra note: done some more digging into these 3 issues - in all 3 it seems that the assertion is that information being tracked for use in BailOut paths has the wrong lifetime - so on a release/test build if you then hit the bailout you end up with nullptr de-ref after dropping into the interpreter. In the original example here steps for segfault:
EDIT: whilst I got somewhat carried away looking at this and can explain the result of the problem I've still got no idea as to why it's going wrong - beyond noting that this error doesn't appear to be in 1.11 and my best guess remains that it was somehow introduced by #6232 |
A bit more thinking about this - #6181 is exactly the same idea - inlining a regex related call, adding the coercion methods and losing the register tracking for bailout. #6487 worries me a little more because it's a register for a bailonnoprofile that it's losing but I can't work out which bailonnoprofile - so don't even know where it's going wrong. As a related point the commit I linked above for Async Generators was again the same concept though in that case I think it was a register for the NewAsyncFromSyncIterator opcode that was getting lost in the bailoutpath - that did not involve string or regex coercion - though possibly relevant the version that failed involved an op with the same register for it's source and destination, the fix (I haven't merged) was adding in extra registers so that source and destination were not the same - I don't however see why this should be a problem. What's alarming about all of these taken together is it feels like we have a category of errors here not a single obvious issue - and I'd be surprised if there aren't many more we haven't located yet. |
#6181 looks suspiciously similar to this one, I still need to take a closer look at #6487. Obvious question - are we releasing By the way, what is the expected optimization "lifecycle" in this case - wouldn't ending up with a bailout defeat the purpose of inlining the call? |
The bailout in this one is a BailOnImplicitCall - the optimisation becomes invalid if converting to a string has an implicit call (that may have a side effect). The optimised code works for: The bailout is hit with the later call: The assertion is when creating the bailout warning that it’s going to have a problem. My speculation is that this is an example of a wider issue whenever there is a bailout path related to an operation which uses the same register as both src and dst - hence the link with the async generator related commit I linked as well as #6487 |
I agree, there might be something more fundamental here. Part of the problem is that we set liveness in a semi-manual way, which is somewhat error prone. |
Add minimal example from chakra-core#6511.
I had guessed wrong on the PR that introduced this bug. I've now done git bisect to locate it. This crash was introduced by this PR: #5854 The PR worked on inlining more functions invoked by .call() and .apply() so it looks like this issue really just relates to .call() and hence is the same issue as #6181 but is not related to #6487 I'll look at that one separately. |
I've narrowed the it down to BytecodeArgOutCapture or the load after it getting eliminated by deadstore. #5854 seems to be closer to home than what we originally thought, thanks for finding it! |
Add minimal example from chakra-core#6511.
Changing the parameter to function opt(o, zz) {
function foo (o) {
String.prototype.replace.call(o, zz, {});
}
foo (o);
}
for (let xi = 0; xi < 500; xi++) {
opt({}, "kkkk");
} AFAIK this is the same reg that is getting deleted by backward pass later. |
I've taken another little look at this, still not solved it BUT I think I understand it slightly more... When ChakraCore/lib/Backend/Inline.cpp Lines 3829 to 3912 in 5f53f55
If the jitted code bails out then the values of the original arguments need to be available for the interpreter to call the original function. They are marked with However I think wrapping them in these coercion methods somehow messes up the bookkeeping for whether In a non-debug build the jitted code will be produced and run AND all is fine unless it BailsOut, upon bailout you hit a segfault (which is what the Assertion is warning about). |
Git Commit: 861a276
Ubuntu 18.04
PoC:
Assertion failed in debug and segfault in release (possible null pointer dereference)
stack:
The text was updated successfully, but these errors were encountered: