Weird inconsistency between the getprop and getname case in baseline ICs

RESOLVED FIXED in Firefox 40

Status

()

defect
RESOLVED FIXED
4 years ago
4 years ago

People

(Reporter: bzbarsky, Assigned: jandem)

Tracking

Trunk
mozilla40
x86
macOS
Points:
---

Firefox Tracking Flags

(firefox40 fixed)

Details

Attachments

(4 attachments, 3 obsolete attachments)

Consider these two testcases, run in a worker:

  for (var i = 0; i < 100000; ++i) performance;

and:

  var s = self; for (var i = 0; i < 100000; ++i) s.performance;

The latter is a lot slower than the former: over 2 orders of magnitude.

What happens in Ion is that it tries to do the getPropTryCommonGetter thing, and finds a baseline stub in the former case but not the latter.  So the latter ends up trying to use an Ion IC, which it can never generate because the prop lives on some proto of the global and the IC code does LookupPropertyPure (in CanAttachNativeGetProp) and LookupPropertyPure fails out if it hits a resolve hook and hasn't found the property (possibly on that object itself) yet.  And globals _always_ have a resolve hook, pretty much....

OK, so we should consider making the IC stuff better in Ion, but why the difference in baseline stubs?

That comes from the fact that TryAttachNativeGetAccessorPropStub does an EffectlesslyLookupProperty, which ends up using LookupPropertyPure, which as discussed above fails out.

On the other hand, TryAttachGlobalNameAccessorStub just does a manual walk up the proto chain while it's got native objects, doing a current->lookup() at each step.

So I guess the question is: is TryAttachGlobalNameAccessorStub unsound or is TryAttachNativeGetAccessorPropStub being too pessimistic in its assumptions?

And an even more important question is: how do we make this consistently fast?
Flags: needinfo?(jdemooij)
So I thought about this a bit, and I think the problem is in the getprop stub, kinda.  At least for resolve hooks on globals.

Specifically, I believe we can make the following assumption about resolve hooks on our globals: they are time-invariant.  That is, if you do a lookup and the resolve hook does NOT resolve it, then it won't in the future either.

If we can make this assumption, then we should allow ICs even in the face of globals' resolve hooks, because ICs do shape checks.  So when we're generating the IC we do a lookup, and there are three possibilities:

1)  We've already done a real lookup of this property on this object.  Then the resolve hook has already done its thing and there is no problem.

2)  We have not done a real lookup, but when we do end up doing it the resolve hook does not resolve this property name.  Clearly no problem.

3)  We have not done a real lookup, and when we do it the resolve hook will resolve this property name.  This will reshape the relevant object and if it was a delegate its proto chain, so the IC stub we generated (which found the property on some prototype of the thing with the resolve hook) will no longer match.  This should also not be a problem.
Posted file Testcase
Posted patch Possible fix (obsolete) — Splinter Review
Attachment #8594338 - Flags: review?(jdemooij)
Assignee: nobody → bzbarsky
Status: NEW → ASSIGNED
We really do need to start growing some sort of set of DOM microbenchmarks that we run in automation.  We've been talking about this a decade... :(
What's the global resolve hook in the browser, ResolveGlobal in BindingUtils.cpp or nsGlobalWindow::DoResolve?

Instead of special-casing more resolve hooks in LookupPropertyPure, it might be nice to have a new class hook - mayResolve(jsid id) - and have LookupPropertyPure call that instead of the current whitelisting. We could add some strong asserts: if the resolve hook sets *resolved to true, MOZ_ASSERT(clasp->mayResolve(id)).

Does this make sense? Are there other DOM classes with resolve hooks that could benefit from this?
Flags: needinfo?(bzbarsky)
> What's the global resolve hook in the browser

Both.  The BindingUtils.cpp ResolveGlobal is called by both Window and Worker global resolve hooks (and in fact _is_ the resolve hook for Worker).  The Window resolve hook also calls nsGlobalWindow::DoResolve.

> Does this make sense?

What would mayResolve do, exactly?  Just return true or false depending on whether the resolve hook would resolve the property?  That _does_ require duplicating a bunch of code in the Window case, which won't be terribly fun...  And it would involve calling the various "is foo enabled?" functions, which have no guarantees about not having side-effects, unfortunately.  Though I suppose we could have mayResolve be a bit pessimistic and just return true in those cases without calling those functions...

Of course this also means that we'd need a way to ask the JS engine whether JS_ResolveStandardClass would resolve a given id.

> Are there other DOM classes with resolve hooks that could benefit from this?

The only DOM classes with resolve hooks are globals, HTMLEmbedElement, HTMLAppletElement, HTMLObjectElement, and Navigator.  The three elements have resolve hooks that do little things like mutate the proto chain, so there's no sane way to optimize those (in the mayResolve world they'd just return true for every id, I expect).  The Navigator resolve hook is pretty similar to the window one in terms of how it works; I guess the mayResolve setup could help there.  It's just that we haven't found anything that really depends on the performance of navigator.something.

I'm happy to try doing this if you're willing to hook up the JSAPI bits.
Flags: needinfo?(bzbarsky)
Posted patch WIP (obsolete) — Splinter Review
This patch adds the mayResolve hook to JSFunction, StringObject and the shell's global object class.

I can build the browser with this applied. Can you see if this works with your changes?
Attachment #8594987 - Flags: feedback?(bzbarsky)
Comment on attachment 8594987 [details] [diff] [review]
WIP

This works great.
Attachment #8594987 - Flags: feedback?(bzbarsky) → feedback+
Attachment #8594338 - Attachment is obsolete: true
Attachment #8594338 - Flags: review?(jdemooij)
Peter, I just used an infallible string allocation for the id, but if you prefer I can do a fallible one, returning true if the allocation fails.  Please let me know.
Attachment #8595081 - Flags: review?(peterv)
There's an annoying case: JS_ResolveStandardClass does global->getOrCreateObjectPrototype to make sure the global has a proto. In other words, the global's proto can be lazily initialized by the resolve hook :(

I tried to move this getOrCreateObjectPrototype call to GlobalObject::new_ and that works in the shell but it breaks XPConnect's sandbox: we call sandbox_addProperty under getOrCreateObjectPrototype *before* we set the compartment's private and sandbox_addProperty crashes.

The simpler fix is to add a |JSObject* maybeObj| argument to the mayResolve hook and JS_MayResolveStandardClass, and have the hook check that. IonBuilder does not always know the JSObject*, so there we can call mayResolve with maybeObj == nullptr.

bz, it should be trivial to rebase your patch on top of this. I'll do some Try-servering.
This patch adds a |mayResolve| hook, to replace the resolve hook whitelisting we currently do in LookupPropertyPure and IonBuilder's ObjectHasExtraOwnProperty.

The idea is that classes with a resolve hook can (optionally) have this extra, non-effectful hook to help JIT optimizations and other places that do pure lookups.

CallResolveOp asserts mayResolve returns true if we resolved a property, that's a pretty strong correctness check.

I converted JSFunction, StringObject and the shell's global to this new model.
Assignee: bzbarsky → jdemooij
Attachment #8594987 - Attachment is obsolete: true
Flags: needinfo?(jdemooij)
Attachment #8595353 - Flags: review?(bhackett1024)
Attachment #8595081 - Attachment is obsolete: true
Attachment #8595081 - Flags: review?(peterv)
Assignee: jdemooij → bzbarsky
Assignee: bzbarsky → jdemooij
Also, I did verify that with the hazard annotations in part 2 the hazard analysis stuff on try passes.
Attachment #8595353 - Flags: review?(bhackett1024) → review+
Pushed part 1, it's pretty big so I want to avoid bitrot.
Keywords: leave-open
Comment on attachment 8595409 [details] [diff] [review]
Part 2 merged on top of jandem's changes to pass a maybeObject

Review of attachment 8595409 [details] [diff] [review]:
-----------------------------------------------------------------

::: dom/base/Navigator.cpp
@@ +2226,5 @@
> +/* static */
> +bool
> +Navigator::MayResolve(jsid aId)
> +{
> +  // Note: Keep this in sync with DoResolve.

I think we should add a similar comment to DoResolve.

::: dom/base/Navigator.h
@@ +282,5 @@
>                   JS::Handle<jsid> aId,
>                   JS::MutableHandle<JSPropertyDescriptor> aDesc);
> +  // NOTE: This function does not fail and may not have any
> +  // side-effects.  The return value is whether DoResolve might end up
> +  // resolving the given id.  If in doubt, return true.

I'd put most of this comment in or next to the implementations (describing the return value here still makes sense).

::: dom/base/nsGlobalWindow.cpp
@@ +4273,5 @@
> +/* static */
> +bool
> +nsGlobalWindow::MayResolve(jsid aId)
> +{
> +  // Note: Keep this in sync with DoResolve.

Same here.

::: dom/base/nsGlobalWindow.h
@@ +502,5 @@
>                   JS::Handle<jsid> aId,
>                   JS::MutableHandle<JSPropertyDescriptor> aDesc);
> +  // NOTE: This function does not fail and may not have any side-effects.  The
> +  // return value is whether DoResolve might end up resolving the given id.  If
> +  // in doubt, return true.

Same here.

::: dom/base/nsObjectLoadingContent.h
@@ +172,5 @@
>                     JS::Handle<jsid> aId,
>                     JS::MutableHandle<JSPropertyDescriptor> aDesc);
> +    // NOTE: This function does not fail and may not have any side-effects.  The
> +    // return value is whether DoResolve might end up resolving the given id.
> +    // If in doubt, return true.

Same here.
Attachment #8595409 - Flags: review?(peterv) → review+
Keywords: leave-open
https://hg.mozilla.org/mozilla-central/rev/bc969d371858
Status: ASSIGNED → RESOLVED
Closed: 4 years ago
Resolution: --- → FIXED
Target Milestone: --- → mozilla40
You need to log in before you can comment on or make changes to this bug.