Closed Bug 939581 Opened 7 years ago Closed 7 years ago

Don't unconditionally insert a resume point for an MCall and make it possible to apply CSE, DCE, and LICM optimizations to DOM calls

Categories

(Core :: JavaScript Engine: JIT, defect)

x86
macOS
defect
Not set
normal

Tracking

()

RESOLVED FIXED
mozilla29

People

(Reporter: bzbarsky, Assigned: bzbarsky)

References

Details

Attachments

(3 files, 10 obsolete files)

13.69 KB, patch
efaust
: review+
Details | Diff | Splinter Review
1.24 KB, patch
jandem
: review+
efaust
: review+
Details | Diff | Splinter Review
9.05 KB, patch
efaust
: review+
Details | Diff | Splinter Review
We seem to unconditionally insert a resume point in makeCall.  I don't think we should for cases when the call is infallible and not effectful.
Attached patch diff -w for ease of review (obsolete) — Splinter Review
Depends on: 938294
Comment on attachment 8333552 [details] [diff] [review]
Only insert a resume point for an MCall in cases when the MCall can fail or might have side effects.

Hrm.  So I'm not sure this is right.

In particular, having no arg to pass where a string arg is expected is not effectful, but it _is_ fallible.  So the processing of the jitinfo's argTypes needs to be a bit different in the getAliasSet() and isInfallible() cases.

So canceling review requests, but would still like to know whether the general idea makes sense, with isInfallible() fixed to be correct.
Attachment #8333552 - Flags: review?(jdemooij)
Attachment #8333552 - Flags: review?(efaustbmo)
Attachment #8333552 - Flags: feedback?(jdemooij)
Comment on attachment 8333552 [details] [diff] [review]
Only insert a resume point for an MCall in cases when the MCall can fail or might have side effects.

Per discussion, non-effectful stuff that's fallible in a reliable way doesn't need a resume point.
Attachment #8333552 - Flags: feedback?(jdemooij)
Please pay special attention to the XXX comments!
Attachment #8335369 - Flags: review?(efaustbmo)
Attachment #8335371 - Flags: review?(jdemooij)
Attachment #8335371 - Flags: review?(efaustbmo)
Talked to Eric a bit; will post patches with his IRC comments addressed.
Attachment #8335608 - Flags: review?(jdemooij)
Attachment #8335608 - Flags: review?(efaustbmo)
Attachment #8335369 - Attachment is obsolete: true
Attachment #8335369 - Flags: review?(efaustbmo)
Attachment #8335612 - Flags: review?(jdemooij)
Attachment #8335612 - Flags: review?(efaustbmo)
Attachment #8335371 - Attachment is obsolete: true
Attachment #8335371 - Flags: review?(jdemooij)
Attachment #8335371 - Flags: review?(efaustbmo)
Comment on attachment 8335608 [details] [diff] [review]
part 1.  Factor out MCallDOMNative from MCall.

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

Looks good. Thanks for doing this. r=me
Attachment #8335608 - Flags: review?(efaustbmo) → review+
Comment on attachment 8335608 [details] [diff] [review]
part 1.  Factor out MCallDOMNative from MCall.

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

Nice.

::: js/src/jit/MIR.cpp
@@ +663,5 @@
> +    const JSJitInfo* jitInfo = getSingleTarget()->jitInfo();
> +    JS_ASSERT(jitInfo);
> +
> +    JS_ASSERT(jitInfo->aliasSet != JSJitInfo::AliasNone);
> +    if (jitInfo->aliasSet != JSJitInfo::AliasDOMSets || !jitInfo->argTypes)

Nit: Add a short comment here to explain why we need the !argTypes check.

::: js/src/jit/MIR.h
@@ +1906,5 @@
> +    // isCall() to check for calls and all we really want is to overload a few
> +    // virtual things from MCall.
> +protected:
> +    MCallDOMNative(JSFunction *target, uint32_t numActualArgs) :
> +        MCall(target, numActualArgs, false)

Nit: indent "protected:" with two spaces, also move the : to the beginning of the next line while you're here:

    MCallDOMNative(...)
      : MCall(...)
    {

@@ +1913,5 @@
> +
> +    friend MCall *MCall::New(TempAllocator &alloc, JSFunction *target, size_t maxArgc,
> +                             size_t numActualArgs, bool construct, bool isDOMCall);
> +
> +public:

Same here.
Attachment #8335608 - Flags: review?(jdemooij) → review+
Comment on attachment 8335612 [details] [diff] [review]
part 2.  Make EliminateDeadCode play nice with MCall and MCallDOMNative.

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

::: js/src/jit/IonAnalysis.cpp
@@ +167,5 @@
>          // Remove unused instructions.
>          for (MInstructionReverseIterator inst = block->rbegin(); inst != block->rend(); ) {
>              if (!inst->isEffectful() && !inst->resumePoint() &&
>                  !inst->hasUses() && !inst->isGuard() &&
>                  !inst->isControlInstruction()) {

Pre-existing nit, but can you move the { to the next line?
Attachment #8335612 - Flags: review?(jdemooij) → review+
Comment on attachment 8335608 [details] [diff] [review]
part 1.  Factor out MCallDOMNative from MCall.

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

::: js/src/jit/MIR.cpp
@@ +659,5 @@
> +MCallDOMNative::getAliasSet() const
> +{
> +    JS_ASSERT(getSingleTarget() && getSingleTarget()->isNative());
> +
> +    const JSJitInfo* jitInfo = getSingleTarget()->jitInfo();

*jitInfo

@@ +667,5 @@
> +    if (jitInfo->aliasSet != JSJitInfo::AliasDOMSets || !jitInfo->argTypes)
> +        return AliasSet::Store(AliasSet::Any);
> +
> +    uint32_t argIndex = 0;
> +    for (const JSJitInfo::ArgType* argType = jitInfo->argTypes;

*argType
> Nit: Add a short comment here to explain why we need the !argTypes check.

    // If we don't know anything about the types of our arguments, we have to
    // assume that type-coercions can have side-effects, so we need to alias
    // everything.

Addressed all the other comments.
Comment on attachment 8335612 [details] [diff] [review]
part 2.  Make EliminateDeadCode play nice with MCall and MCallDOMNative.

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

See comment below. Grumble grumble grumble. If Jan is OK with this, I will not stand in the way of landing, but I think a little more thought could be useful in this area.

::: js/src/jit/Lowering.cpp
@@ +419,5 @@
>      // Check MPrepareCall/MCall nesting.
>      JS_ASSERT(prepareCallStack_.popCopy() == call->getPrepareCall());
>  
> +    if (call->isDead()) {
> +        // Nothing else we need to do here, since we've already done freeArguments.

I will begrudgingly agree to this design, I guess.

I don't understand why we can't just DCE the passagrs and the preparecall with the call? It should be their only uses anyway, right? And the resume point captures the stack /before/ popping arguments, so they are not captured there, right? This is just for our internal state, all of which is changed during lowering these things, right? So if we just rm them, then this problem goes away?

Am I missing something? That seems cleaner and more like what we want than decoupling the MIR and LIR graphs...
Attachment #8335612 - Flags: review?(efaustbmo) → review+
From what little I understand, if we can in fact nix the passargs and preparecall we should be able to nix the call as well.

We do _something_ like that in UnreachableCodeElimination::removeUnmarkedBlocksAndClearDominators near the end, though it's got some comments about resume points that make it sound like a resume point might in fact capture passargs?

I agree that the setup in this patch is totally nuts in general; it's just all I've been able to come up with given the lack of anyone willing to own up to understanding the actual constraints on the whole preparecall/passargs setup so far.  :(

I definitely think we should file a followup bug to come up with a saner setup here.
Setting needinfo on myself so that I remember to do this tomorrow.

I agree that nobody on the jit inside has really stepped up to figure out how feasible it is to undertake other designs, and that my complaint above was not exceeding well-researched, and I am sorry about that. I also fully admit that the solution in the patch is easily the easiest solution for someone on the outside.

I want to play with this for a few hours tomorrow, and see what I can come up with. Can we hold off landing for just a moment in case that yields anything?
Flags: needinfo?(efaustbmo)
Sure thing.  For one, part 3 still needs reviews.  ;)
Attached patch Part 1 merged to tip (obsolete) — Splinter Review
Depends on: 952992
Attachment #8351302 - Attachment description: Experiment in making DOM calls movable, based on → Experiment in making DOM calls movable, based on bug 952992
Attachment #8351302 - Attachment is obsolete: true
The only substantive difference here from the earlier patch version is the change in makeCallHelper to use callInfo.thisArg()->resultTypeSet() instead of hoisting the decl of thisArg higher.  The other option is to hoist thisArg higher but also reintroduce the code that munges thisArg if we change callInfo.thisArg().  Note that the change to callInfo.thisArg() only happens in the constructing case, while we only examine the typeset for DOM purposes in the non-constructing case, so this code really is OK.
Attachment #8351607 - Flags: review?(efaustbmo)
Attachment #8348345 - Attachment is obsolete: true
Attachment #8351607 - Attachment description: Part 1 merged on top of → Part 1 merged on top of bug 95299
Comment on attachment 8335612 [details] [diff] [review]
part 2.  Make EliminateDeadCode play nice with MCall and MCallDOMNative.

This is no longer needed.
Attachment #8335612 - Attachment is obsolete: true
Attachment #8333552 - Attachment is obsolete: true
Attachment #8333553 - Attachment is obsolete: true
Attachment #8335372 - Attachment is obsolete: true
Attachment #8335372 - Flags: review?(jdemooij)
Attachment #8335372 - Flags: review?(efaustbmo)
Attachment #8335608 - Attachment is obsolete: true
This incidentally fixes a preexisting bug in MCallDOMNative::getAliasSet that could affect methods that are marked [Pure] and take sequence arguments, say.  I have audited our existing [Pure] methods that take non-primitive arguments; they are Node.isEqualNode, Node.compareDocumentPosition, and Node.contains.  All three take Node arguments, so while they can't be movable in the new setup they also can't have artument-conversion side-effects (other than throwing), so it was actually safe to mark them as not write-aliasing things....
Attachment #8351609 - Flags: review?(efaustbmo)
Attachment #8351398 - Attachment is obsolete: true
Comment on attachment 8351607 [details] [diff] [review]
Part 1 merged on top of bug 95299

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

Good. This seems to carry the plus fine
Attachment #8351607 - Flags: review?(efaustbmo) → review+
Comment on attachment 8351608 [details] [diff] [review]
part 2.  Don't create resume points for non-effectful methods (which currently just means DOM methods), on the assumption that such methods have no side-effects and throw deterministically based on thisval and arguments.

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

Yes. I now respect this in light of the alias set information from jitinfo.
Attachment #8351608 - Flags: review?(efaustbmo) → review+
Comment on attachment 8351609 [details] [diff] [review]
part 3.  Mark DOM calls as movable as needed and allow them to be CSE'd.

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

Look good. r=me

::: js/src/jit/MIR.cpp
@@ +756,5 @@
> +    if (!congruentIfOperandsEqual(call))
> +        return false;
> +
> +    // The other call had better be movable at this point!
> +    JS_ASSERT(call->isMovable());

I am glad that you didn't hold here with the tradition of return congruentIfOperandsEqual().

This assert puts my mind even more at rest :)
Attachment #8351609 - Flags: review?(efaustbmo) → review+
Do we have any more recent perf numbers on these changes, just out of curiosity?

Clearing needinfo on self, as Jan's work on passargs renders it no longer relevant.
Flags: needinfo?(efaustbmo)
Attachment #8351608 - Flags: review?(jdemooij) → review+
> Do we have any more recent perf numbers on these changes, just out of curiosity?

It does _really_ well in microbenchmarks.  e.g. Dromaeo's getAttribute test gets loop-hoisted, as does the test at http://jsperf.com/demo-of-microbenchmark-silliness (See the "Firefox 29" numbers there from my test builds).  And once we can do ion optimizations on document, we'll hoist getElementById too.

For real-world code, unclear.
   https://hg.mozilla.org/integration/mozilla-inbound/rev/4abc092e62c5
   https://hg.mozilla.org/integration/mozilla-inbound/rev/6badd39e9d6f
   https://hg.mozilla.org/integration/mozilla-inbound/rev/6034450c8684

We'll see what talos apart from dromaeo-dom thinks!
Summary: Don't unconditionally insert a resume point for an MCall → Don't unconditionally insert a resume point for an MCall and make it possible to apply CSE, DCE, and LICM optimizations to DOM calls
Target Milestone: --- → mozilla29
And it does.  What the heck is going on here?  Those are jit-test failures, and they're totally consistent.  Each time it's:

 TEST-UNEXPECTED-FAIL | js/src/jit-test/tests/basic/testArrayBufferSlice.js | --no-baseline --no-ion
 INFO stderr 2> /builds/slave/m-in_l64-d_sm-ra-0000000000000/src/js/src/jit-test/tests/basic/testArrayBufferSlice.js:8:12 Error: Assertion failed: got 16, expected 43

and neither shu nor I can reproduce locally just running jit-test on either mac64 or linux64.

How could these patches possibly affect a --no-ion run in the first place?  How do I go about running exactly what "r" runs on tinderbox locally?
Flags: needinfo?(sphink)
Flags: needinfo?(jdemooij)
Not sure how many of these are relevant outside exact rooting and gcgenerational.

--enable-optimize
--enable-debug
--enable-stdcxx-compat
--enable-ctypes
--enable-trace-malloc
--disable-shared-js
--enable-exact-rooting
--enable-gcgenerational
--enable-threadsafe
Boris, do you also see this failure on Try? This has happened to me before, with the ggc build. Green on Try, perma-orange on my (unrelated!) inbound push, but fortunately it was green on the next one but it kept failing intermittently (bug 946939) until it was fixed (bug 948162).

Let me try to reproduce it too, but as you said if it's failing with --no-ion --no-baseline it's unlikely to be caused by your changes.
Flags: needinfo?(jdemooij)
Oh and the r build runs with the JS_GC_ZEAL=7 environment var.
> Boris, do you also see this failure on Try

Working on that.  Hoping I found the right try incantation to get this test to run....
(In reply to Boris Zbarsky [:bz] from comment #38)
> Working on that.  Hoping I found the right try incantation to get this test
> to run....

Just make some minor change to js/src/* to get r/e/ggc builds.
All the try runs I did (on top of current inbound) are green.  See https://tbpl.mozilla.org/?tree=Try&rev=344c6b8cef45

Now what?  :(
Flags: needinfo?(ryanvm)
Flags: needinfo?(kwierso)
Might as well re-land and see what happens.
Flags: needinfo?(sphink)
Flags: needinfo?(ryanvm)
Flags: needinfo?(kwierso)
And now it's nice and green.  I don't like this test.  :(
Depends on: 966665
You need to log in before you can comment on or make changes to this bug.