Skip to content
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

Invalidate IsInstInlineCache in Object.setPrototypeOf #6858

Open
wants to merge 14 commits into
base: master
Choose a base branch
from

Conversation

ShortDevelopment
Copy link
Contributor

@ShortDevelopment ShortDevelopment commented Oct 22, 2022

Using Object.setPrototypeOf may result in instanceof returning the wrong result, as the IsInstInlineCache is not invalidated (See #5915).

Steps:

  • Iterate over the existing function -> IsInstInlineCache[] dictionary using the new ThreadContext.MapIsInstInlineCaches function
  • Walk the prototype chain to check for the cached type
  • Ignore if type is null
    inlineCache->Cache(VarIs<RecyclableObject>(instance) ?
    UnsafeVarTo<RecyclableObject>(instance)->GetType() : nullptr,
    function, scriptContext->GetLibrary()->GetFalse(), scriptContext);
  • If found, remove from list using ThreadContext.UnregisterIsInstInlineCache (corrects next) and ...
  • Invalidate using memset

I couldn't come up with a scenario where the function could be affected (x instance of function).
Therefore, the following code should not be necessary (not included in latest commit):

JavascriptFunction* jsFunction = VarTo<JavascriptFunction>(function);
// Check if cached function type is same as the old prototype
bool clearCurrentCacheList = jsFunction->GetType() == object->GetType();
if (!clearCurrentCacheList)
{
// Check if function prototype contains old prototype
JavascriptOperators::MapObjectAndPrototypes<true>(jsFunction->GetPrototype(), [&](RecyclableObject* obj)
{
if (object->GetType() == obj->GetType())
clearCurrentCacheList = true;
});
}
if (clearCurrentCacheList)
{
threadContext->InvalidateIsInstInlineCachesForFunction(function);
return;
}


@rhuanjl

Fixes #5915

@rhuanjl
Copy link
Collaborator

rhuanjl commented Oct 22, 2022

Thank you for the submission, I'm going to have think a bit more about this (probably on monday)

This certainly fixes the specific case, but I'd like to throw some more complex cases at it AND also see if we can test whether it's removing caches it shouldn't (which will be slightly harder to test).

@ShortDevelopment
Copy link
Contributor Author

Yes, that's a good idea!
I already tried to figure that out, but I couldn't really figure out, what class is behind a Type in CC (e.g. reading constructor name).

The logic defenently doesn't remove everything all the time.

@rhuanjl
Copy link
Collaborator

rhuanjl commented Oct 29, 2022

Sorry that I'm not done with this yet, I've been struggling to think of how to do the additional tests I'd like to do, there is no obvious good way to verify cache hits, so, awkwardly we may need to add one (for debug builds only) in order to test this sufficiently.

@ShortDevelopment
Copy link
Contributor Author

No problem, I’m struggling with that as well.
What exactly do you mean by “verify cache hits”? How could we do that?

@rhuanjl
Copy link
Collaborator

rhuanjl commented Oct 29, 2022

No problem, I’m struggling with that as well. What exactly do you mean by “verify cache hits”? How could we do that?

If you want to try and do it:

  1. Add a phase for instanceofCache in here: https://github.com/chakra-core/ChakraCore/blob/master/lib/Common/ConfigFlagsList.h
  2. In JavascriptFunction::HasInstance (in JavascriptFunction.cpp) add some code inside a #ifdef ENABLE_TEST_HOOKS that contains an if(PHASE_TRACE1(phase_you_created)) containing code to print/log the result of the cache check for instanceof.
  3. With a debug or test build of ch the command line flag -trace:phase_you_created will then trigger that logging code
  4. We could then create a test case using a baseline file to enable us to check that the right things are logged, specifically we'd want cases where some cache should be cleared but some should not be and check it works.

@ShortDevelopment
Copy link
Contributor Author

ShortDevelopment commented Nov 4, 2022

Sorry, that it took me so long, but I still had some work to do.
I’ve now replaced the test with a baseline test using the trace output from the previous commit.
It would be great if you could check if I missed an interesting case.

The Problem is, that jit abstracts the calls to the test functions away.
Therefore, the baseline is "incorrect" in those tests.

EDIT: I'm now preventing the fast path in jit, kind of like it's done in some other scenarios for testing.

@rhuanjl
Copy link
Collaborator

rhuanjl commented Nov 9, 2022

I'm not ignoring this - thank you for your efforts, I've still got some more cases I'd like to check and out of time for today will look a bit further when I can.

@rhuanjl
Copy link
Collaborator

rhuanjl commented Dec 28, 2022

Apologies for the massive delay - I've paused working on other code changes until I've sorted apple silicon support which is taking me longer than I hoped...

@ShortDevelopment
Copy link
Contributor Author

@rhuanjl
No problem; just take your time!

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