Bug 151266

Summary: Use the JITAddGenerator snippet in the DFG.
Product: WebKit Reporter: Mark Lam <mark.lam>
Component: JavaScriptCoreAssignee: Mark Lam <mark.lam>
Status: RESOLVED FIXED    
Severity: Normal CC: benjamin, commit-queue, fpizlo, ggaren, keith_miller, msaboff, saam
Priority: P2    
Version: WebKit Local Build   
Hardware: Unspecified   
OS: Unspecified   
Attachments:
Description Flags
the patch.
ggaren: review-
x86_64 benchmark result.
none
x86 benchmark result.
none
patch 2: applied all of Geoff's feedback.
mark.lam: review-
x86 benchmark result.
none
x86_64 benchmark result.
none
patch 3: the real patch with Geoff's feedback applied. ggaren: review+

Description Mark Lam 2015-11-13 10:52:57 PST
Patch coming.
Comment 1 Mark Lam 2015-11-13 16:32:23 PST
Created attachment 265511 [details]
the patch.
Comment 2 Mark Lam 2015-11-13 16:33:19 PST
Created attachment 265512 [details]
x86_64 benchmark result.
Comment 3 Mark Lam 2015-11-13 16:33:47 PST
Created attachment 265513 [details]
x86 benchmark result.
Comment 4 Mark Lam 2015-11-13 16:34:38 PST
The benchmark runs show that perf is neutral.  Any changes are due to noise.  when I rerun the noise component individually, the diff disappears.
Comment 5 Geoffrey Garen 2015-11-13 17:17:09 PST
Comment on attachment 265511 [details]
the patch.

View in context: https://bugs.webkit.org/attachment.cgi?id=265511&action=review

> Source/JavaScriptCore/dfg/DFGAbstractValue.cpp:-502
> -    ASSERT(isType(SpecHeapTop));

Can't you still assert byte code top?

> Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp:2845
> +        // Let's swap the operands.
> +        ResultType tempType = rightType;
> +        rightType = leftType;
> +        leftType = tempType;

Instead of saying "let's swap" in a comment, you can call std::swap.

> Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp:2877
> +
> +    if (leftIsConstInt32) {
> +        // Since we may be dealing with non-numeric operands (which may not be commutative)
> +        // in the slow path, we need to restore the left right order.
> +        rightRegs = right.jsValueRegs();
> +        leftRegs = resultRegs;
> +        int64_t leftConst = node->child1()->asInt32();
> +        m_jit.moveValue(JSValue(leftConst), leftRegs);

Can we avoid the swapping concern by simply building into JITAddGenerator the ability to swap left and right internally if left is constant?

In addition to reducing code at the call sites, that seems like a better separation of concerns. JITAddGenerator's client does not know that swapping is appropriate since it does not know whether all of JITAddGenerator's fast path operations are commutative. Only JITAddGenerator can know that.

> Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h:2587
> +#if USE(JSVALUE64)
> +        if (m_gprOrInvalid != InvalidGPRReg)
> +            m_jit->unlock(m_gprOrInvalid);
> +#elif USE(JSVALUE32_64)
> +        if (m_isDouble && m_register.fpr != InvalidFPRReg)
> +            m_jit->unlock(m_register.fpr);
> +        else if (m_register.pair.tagGPR != InvalidGPRReg && m_register.pair.payloadGPR != InvalidGPRReg) {
> +            m_jit->unlock(m_register.pair.tagGPR);
> +            m_jit->unlock(m_register.pair.payloadGPR);
> +        }
> +#endif

Is there a condition where we fill a register and then set it to not needed? It looks like we only set not needed when we choose not to fill -- and that is good, since filling a register for a known constant value is a waste.

It's not good to have dead code that covers unreachable conditions. I think it would be better to assert that our register is invalid and simply set edge to null.
Comment 6 Mark Lam 2015-11-16 14:13:37 PST
(In reply to comment #5)
> Comment on attachment 265511 [details]
> the patch.
> 
> View in context:
> https://bugs.webkit.org/attachment.cgi?id=265511&action=review
> 
> > Source/JavaScriptCore/dfg/DFGAbstractValue.cpp:-502
> > -    ASSERT(isType(SpecHeapTop));
> 
> Can't you still assert byte code top?

Fixed.

> > Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp:2845
> > +        // Let's swap the operands.
> > +        ResultType tempType = rightType;
> > +        rightType = leftType;
> > +        leftType = tempType;
> 
> Instead of saying "let's swap" in a comment, you can call std::swap.

Thanks.  Learned something new today.

> > Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp:2877
> > +
> > +    if (leftIsConstInt32) {
> > +        // Since we may be dealing with non-numeric operands (which may not be commutative)
> > +        // in the slow path, we need to restore the left right order.
> > +        rightRegs = right.jsValueRegs();
> > +        leftRegs = resultRegs;
> > +        int64_t leftConst = node->child1()->asInt32();
> > +        m_jit.moveValue(JSValue(leftConst), leftRegs);
> 
> Can we avoid the swapping concern by simply building into JITAddGenerator
> the ability to swap left and right internally if left is constant?
> 
> In addition to reducing code at the call sites, that seems like a better
> separation of concerns. JITAddGenerator's client does not know that swapping
> is appropriate since it does not know whether all of JITAddGenerator's fast
> path operations are commutative. Only JITAddGenerator can know that.

Good point.  I will fix.

> > Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h:2587
> > +#if USE(JSVALUE64)
> > +        if (m_gprOrInvalid != InvalidGPRReg)
> > +            m_jit->unlock(m_gprOrInvalid);
> > +#elif USE(JSVALUE32_64)
> > +        if (m_isDouble && m_register.fpr != InvalidFPRReg)
> > +            m_jit->unlock(m_register.fpr);
> > +        else if (m_register.pair.tagGPR != InvalidGPRReg && m_register.pair.payloadGPR != InvalidGPRReg) {
> > +            m_jit->unlock(m_register.pair.tagGPR);
> > +            m_jit->unlock(m_register.pair.payloadGPR);
> > +        }
> > +#endif
> 
> Is there a condition where we fill a register and then set it to not needed?

Yes.  I have observed that on 32-bit x86, we do encounter a situation where a node is already filled an the time of JSValueOperand construction.  As a result, it auto fills the register and locks it even though we didn't tell it to.

> It looks like we only set not needed when we choose not to fill -- and that
> is good, since filling a register for a known constant value is a waste.

Yes, it is a waste.  One option is to add a mode to JSValueOperand's constructor to tell it not to autofill?  In practice, there's no additional cost with the fill at runtime execution.  It was already loaded by a previous node.  We are merely choosing not to use it.  But we do need to undo the lock.  Hence, the addition of setNotNeeded().  I can look into adding a mode to the constructor to prevent auto-fill.

> It's not good to have dead code that covers unreachable conditions. I think
> it would be better to assert that our register is invalid and simply set
> edge to null.

It is not unreachable (as described above) ... at least not as the code is today.
Comment 7 Geoffrey Garen 2015-11-16 15:02:26 PST
> > Is there a condition where we fill a register and then set it to not needed?
> 
> Yes.  I have observed that on 32-bit x86, we do encounter a situation where
> a node is already filled an the time of JSValueOperand construction.  As a
> result, it auto fills the register and locks it even though we didn't tell
> it to.

Got it.

I think the best way to represent this, rather than JSValueOperand::setNotNeeded(), is to use WTF::Optional<JSValueOperand> inside this function. That expresses that we may or may not need one, instead of saying "I need one" followed by "I didn't mean it".

Another way to model the same thing is to add to JSValueOperand a default constructor and a move constructor. But that seems more involved.
Comment 8 Geoffrey Garen 2015-11-16 15:03:12 PST
Comment on attachment 265511 [details]
the patch.

Clearing the review flag since it looks like we should make these changes.
Comment 9 Geoffrey Garen 2015-11-16 15:07:56 PST
Comment on attachment 265511 [details]
the patch.

View in context: https://bugs.webkit.org/attachment.cgi?id=265511&action=review

> Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp:2887
> +
> +    if (isKnownNotNumber(node->child1().node()) || isKnownNotNumber(node->child2().node()))
> +        callOperation(operationValueAddNotNumber, resultRegs, leftRegs, rightRegs);
> +    else
> +        callOperation(operationValueAdd, resultRegs, leftRegs, rightRegs);

One more comment: This code is a bit out-of-sync with our generator. If we know that both children are not numbers, we'd really rather not call the generator at all. So, I guess we want that case up at the top, beside the constant case. Then the fact that we do ValueAdd on the slow path is always true.
Comment 10 Mark Lam 2015-11-17 11:55:55 PST
Created attachment 265690 [details]
patch 2: applied all of Geoff's feedback.

This patch has passed the JSC and layout tests.  Benchmarks are still being run.
Comment 11 Mark Lam 2015-11-17 12:06:16 PST
Created attachment 265691 [details]
x86 benchmark result.
Comment 12 Mark Lam 2015-11-17 12:06:53 PST
Created attachment 265693 [details]
x86_64 benchmark result.
Comment 13 Mark Lam 2015-11-17 12:08:47 PST
Comment on attachment 265690 [details]
patch 2: applied all of Geoff's feedback.

View in context: https://bugs.webkit.org/attachment.cgi?id=265690&action=review

> Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h:2589
> +
> +    void setNotNeeded()
> +    {
> +#if USE(JSVALUE64)
> +        if (m_gprOrInvalid != InvalidGPRReg)
> +            m_jit->unlock(m_gprOrInvalid);
> +#elif USE(JSVALUE32_64)
> +        if (m_isDouble && m_register.fpr != InvalidFPRReg)
> +            m_jit->unlock(m_register.fpr);
> +        else if (m_register.pair.tagGPR != InvalidGPRReg && m_register.pair.payloadGPR != InvalidGPRReg) {
> +            m_jit->unlock(m_register.pair.tagGPR);
> +            m_jit->unlock(m_register.pair.payloadGPR);
> +        }
> +#endif
> +        m_edge = Edge();
> +    }

Eeek.  Stale changes.  This should be removed.
Comment 14 Mark Lam 2015-11-17 12:10:43 PST
Comment on attachment 265690 [details]
patch 2: applied all of Geoff's feedback.

Uploaded wrong patch.
Comment 15 Mark Lam 2015-11-17 12:13:52 PST
Created attachment 265694 [details]
patch 3: the real patch with Geoff's feedback applied.
Comment 16 WebKit Commit Bot 2015-11-17 12:15:45 PST
Attachment 265694 [details] did not pass style-queue:


ERROR: Source/JavaScriptCore/jit/JITAddGenerator.h:47:  Wrong number of spaces before statement. (expected: 12)  [whitespace/indent] [4]
ERROR: Source/JavaScriptCore/jit/JITAddGenerator.h:48:  Wrong number of spaces before statement. (expected: 12)  [whitespace/indent] [4]
ERROR: Source/JavaScriptCore/jit/JITAddGenerator.h:49:  Wrong number of spaces before statement. (expected: 12)  [whitespace/indent] [4]
ERROR: Source/JavaScriptCore/jit/JITAddGenerator.h:50:  Wrong number of spaces before statement. (expected: 12)  [whitespace/indent] [4]
Total errors found: 4 in 12 files


If any of these errors are false positives, please file a bug against check-webkit-style.
Comment 17 Geoffrey Garen 2015-11-17 13:38:04 PST
Comment on attachment 265694 [details]
patch 3: the real patch with Geoff's feedback applied.

View in context: https://bugs.webkit.org/attachment.cgi?id=265694&action=review

r=me

> Source/JavaScriptCore/dfg/DFGSpeculativeJIT.cpp:2867
> +

Stray whitespace.

> Source/JavaScriptCore/dfg/DFGSpeculativeJIT.h:2574
> +        other.m_edge = Edge();

Let's also reset other's 

        , m_gprOrInvalid(InvalidGPRReg)
        , m_isDouble(false)
Comment 18 Mark Lam 2015-11-17 13:56:18 PST
Thanks for the review.  Landed in r192531: <http://trac.webkit.org/r192531>.