Bug 132470 - Profiling should detect when multiplication overflows but does not create negative zero
Summary: Profiling should detect when multiplication overflows but does not create neg...
Status: RESOLVED FIXED
Alias: None
Product: WebKit
Classification: Unclassified
Component: JavaScriptCore (show other bugs)
Version: 528+ (Nightly build)
Hardware: All All
: P2 Normal
Assignee: Mark Lam
URL:
Keywords:
: 152752 (view as bug list)
Depends on: 152784 152805
Blocks:
  Show dependency treegraph
 
Reported: 2014-05-02 09:52 PDT by Filip Pizlo
Modified: 2016-01-07 13:37 PST (History)
12 users (show)

See Also:


Attachments
proposed patch. (26.72 KB, patch)
2016-01-04 09:38 PST, Mark Lam
mark.lam: review-
Details | Formatted Diff | Diff
x86_64 benchmark result. (66.37 KB, text/plain)
2016-01-04 09:39 PST, Mark Lam
no flags Details
x86 benchmark result. (65.79 KB, text/plain)
2016-01-04 09:40 PST, Mark Lam
no flags Details
proposed patch w/ iOS build fixes. (29.19 KB, patch)
2016-01-05 12:06 PST, Mark Lam
ggaren: review+
Details | Formatted Diff | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description Filip Pizlo 2014-05-02 09:52:19 PDT
This would unlock more Int52 coverage.  Currently, multiplication basically doesn't do Int52.
Comment 1 Mark Lam 2015-12-23 15:43:25 PST
I'll take this.  I'm currently working on https://bugs.webkit.org/show_bug.cgi?id=151793 which needed to address this.
Comment 2 Mark Lam 2016-01-04 09:38:54 PST
Created attachment 268194 [details]
proposed patch.

This patch has passed the layout tests and JSC tests.  Benchmark results shows perf to be neutral in general with a 14% speed up on ftl-polymorphic-mul on x86_64 (Int52 is only available on 64-bit ports).  Benchmark results to be uploaded shortly.
Comment 3 Mark Lam 2016-01-04 09:39:52 PST
Created attachment 268195 [details]
x86_64 benchmark result.
Comment 4 Mark Lam 2016-01-04 09:40:23 PST
Created attachment 268197 [details]
x86 benchmark result.
Comment 5 Mark Lam 2016-01-04 09:56:38 PST
Comment on attachment 268194 [details]
proposed patch.

Investigating iOS build failure.
Comment 6 Filip Pizlo 2016-01-04 13:31:18 PST
Comment on attachment 268194 [details]
proposed patch.

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

Looks about right, but I think I found a bug.

> Source/JavaScriptCore/bytecode/CodeBlock.h:1065
> +    HashMap<uint32_t, uint32_t, IntHash<uint32_t>, WTF::UnsignedWithZeroKeyHashTraits<uint32_t>> m_bytecodeOffsetToResultProfileIndexMap;

Slight preference to say "unsigned" instead of "uint32_t".  I think that's what we do elsehwere.

> Source/JavaScriptCore/runtime/CommonSlowPaths.cpp:372
> +                static const int64_t int52OverflowPoint = (1ll << 51);
> +                int64_t int64Val = static_cast<int64_t>(std::abs(doubleVal));
> +                if (int64Val >= int52OverflowPoint)

Pretty sure this is wrong.  The negative overflow point doesn't have the same absolute value as the positive overflow point.  Why not use CheckedMath from WTF to do the check?
Comment 7 Mark Lam 2016-01-04 13:34:27 PST
Comment on attachment 268194 [details]
proposed patch.

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

>> Source/JavaScriptCore/bytecode/CodeBlock.h:1065
>> +    HashMap<uint32_t, uint32_t, IntHash<uint32_t>, WTF::UnsignedWithZeroKeyHashTraits<uint32_t>> m_bytecodeOffsetToResultProfileIndexMap;
> 
> Slight preference to say "unsigned" instead of "uint32_t".  I think that's what we do elsehwere.

Sure, I can do that.

>> Source/JavaScriptCore/runtime/CommonSlowPaths.cpp:372
>> +                if (int64Val >= int52OverflowPoint)
> 
> Pretty sure this is wrong.  The negative overflow point doesn't have the same absolute value as the positive overflow point.  Why not use CheckedMath from WTF to do the check?

Yes, I'm aware that we might get a false positive on the negative overflow (1 out of 51 bits worth of numbers), but I considered that an OK tradeoff for simplicity.  I was not aware of CheckedMath.  Let me look into it.
Comment 8 Filip Pizlo 2016-01-04 13:40:35 PST
(In reply to comment #7)
> Comment on attachment 268194 [details]
> proposed patch.
> 
> View in context:
> https://bugs.webkit.org/attachment.cgi?id=268194&action=review
> 
> >> Source/JavaScriptCore/bytecode/CodeBlock.h:1065
> >> +    HashMap<uint32_t, uint32_t, IntHash<uint32_t>, WTF::UnsignedWithZeroKeyHashTraits<uint32_t>> m_bytecodeOffsetToResultProfileIndexMap;
> > 
> > Slight preference to say "unsigned" instead of "uint32_t".  I think that's what we do elsehwere.
> 
> Sure, I can do that.
> 
> >> Source/JavaScriptCore/runtime/CommonSlowPaths.cpp:372
> >> +                if (int64Val >= int52OverflowPoint)
> > 
> > Pretty sure this is wrong.  The negative overflow point doesn't have the same absolute value as the positive overflow point.  Why not use CheckedMath from WTF to do the check?
> 
> Yes, I'm aware that we might get a false positive on the negative overflow
> (1 out of 51 bits worth of numbers), but I considered that an OK tradeoff
> for simplicity.  I was not aware of CheckedMath.  Let me look into it.

I see.  I'm fine with your approach, if you add a comment that the imprecision is intentional.

It may be that CheckedMath makes it easier to do it right.  You'll have to do the int52 arithmetic using int64, so left-shift before checking.
Comment 9 Mark Lam 2016-01-05 11:32:27 PST
(In reply to comment #8)
> (In reply to comment #7)
> > >> Source/JavaScriptCore/runtime/CommonSlowPaths.cpp:372
> > >> +                if (int64Val >= int52OverflowPoint)
> > > 
> > > Pretty sure this is wrong.  The negative overflow point doesn't have the same absolute value as the positive overflow point.  Why not use CheckedMath from WTF to do the check?
> > 
> > Yes, I'm aware that we might get a false positive on the negative overflow
> > (1 out of 51 bits worth of numbers), but I considered that an OK tradeoff
> > for simplicity.  I was not aware of CheckedMath.  Let me look into it.
> 
> I see.  I'm fine with your approach, if you add a comment that the
> imprecision is intentional.
> 
> It may be that CheckedMath makes it easier to do it right.  You'll have to
> do the int52 arithmetic using int64, so left-shift before checking.

After looking into Checked<> math usage, I see that I would need to add the following to the slow path functions:
1. extract the JSValue numbers into an Int64 value.
2. Shift the Int64 values.
3. Convert them into Checked<> values.
4. Do the desired operation on the Checked<> values.  This is different for each slow path function.
5. Check the Checked<> result for overflow.

All this needs to be done in addition to the existing code that does the desired operation on the unshifted Int64 values, because we still need the real result of the operation.

Based on that, I think it's better to stick with my solution.  It has the following advantages:
1. Less code changes.
2. The overflow check does not depend on the operation being done, only the result value.
3. The algorithm is consistent with the one emitted by the snippet for the profiling in the baseline JIT on double results.
4. The profiling code can be cleanly #ifdef'ed out for non-DFG builds.  The Checked<> alternative can be #ifdef'ed out too, but less cleanly.

I'll add your suggested comment about the intentional imprecision.
Comment 10 Filip Pizlo 2016-01-05 11:33:52 PST
(In reply to comment #9)
> (In reply to comment #8)
> > (In reply to comment #7)
> > > >> Source/JavaScriptCore/runtime/CommonSlowPaths.cpp:372
> > > >> +                if (int64Val >= int52OverflowPoint)
> > > > 
> > > > Pretty sure this is wrong.  The negative overflow point doesn't have the same absolute value as the positive overflow point.  Why not use CheckedMath from WTF to do the check?
> > > 
> > > Yes, I'm aware that we might get a false positive on the negative overflow
> > > (1 out of 51 bits worth of numbers), but I considered that an OK tradeoff
> > > for simplicity.  I was not aware of CheckedMath.  Let me look into it.
> > 
> > I see.  I'm fine with your approach, if you add a comment that the
> > imprecision is intentional.
> > 
> > It may be that CheckedMath makes it easier to do it right.  You'll have to
> > do the int52 arithmetic using int64, so left-shift before checking.
> 
> After looking into Checked<> math usage, I see that I would need to add the
> following to the slow path functions:
> 1. extract the JSValue numbers into an Int64 value.
> 2. Shift the Int64 values.
> 3. Convert them into Checked<> values.
> 4. Do the desired operation on the Checked<> values.  This is different for
> each slow path function.
> 5. Check the Checked<> result for overflow.
> 
> All this needs to be done in addition to the existing code that does the
> desired operation on the unshifted Int64 values, because we still need the
> real result of the operation.
> 
> Based on that, I think it's better to stick with my solution.  It has the
> following advantages:
> 1. Less code changes.
> 2. The overflow check does not depend on the operation being done, only the
> result value.
> 3. The algorithm is consistent with the one emitted by the snippet for the
> profiling in the baseline JIT on double results.
> 4. The profiling code can be cleanly #ifdef'ed out for non-DFG builds.  The
> Checked<> alternative can be #ifdef'ed out too, but less cleanly.
> 
> I'll add your suggested comment about the intentional imprecision.

SGTM.
Comment 11 Mark Lam 2016-01-05 12:06:25 PST
Created attachment 268302 [details]
proposed patch w/ iOS build fixes.

The patch now builds on iOS.  I'm still running tests to make sure that the added emitters are doing the right thing.  This patch will break the other ports that don't have the following emitter though:

    void or32(TrustedImm32 imm, AbsoluteAddress address);
Comment 12 Mark Lam 2016-01-05 12:46:34 PST
*** Bug 152752 has been marked as a duplicate of this bug. ***
Comment 13 Mark Lam 2016-01-05 14:01:09 PST
The JSC tests runs fine on ARMv7 and ARM64.  Ready for a review.
Comment 14 Geoffrey Garen 2016-01-05 15:04:17 PST
Comment on attachment 268302 [details]
proposed patch w/ iOS build fixes.

r=me
Comment 15 Mark Lam 2016-01-05 15:10:05 PST
Thanks for the review.  Landed in r194613: <http://trac.webkit.org/r194613>.
Comment 16 WebKit Commit Bot 2016-01-06 13:41:27 PST
Re-opened since this is blocked by bug 152805
Comment 17 Csaba Osztrogonác 2016-01-07 03:31:24 PST
Comment on attachment 268302 [details]
proposed patch w/ iOS build fixes.

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

> Source/JavaScriptCore/assembler/MacroAssemblerARMv7.h:359
> +        or32(imm, dataTempRegister, dataTempRegister);

I think it is incorrect, because or32(TrustedImm32 imm, RegisterID src, RegisterID dest)
moves the immediate to dataTempRegister and overwrite the value you loaded to it previously.
(I had a similar issue in bug152784 during implementing the or32 for ARM instruction set.)
Comment 18 Mark Lam 2016-01-07 09:10:05 PST
Comment on attachment 268302 [details]
proposed patch w/ iOS build fixes.

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

>> Source/JavaScriptCore/assembler/MacroAssemblerARMv7.h:359
>> +        or32(imm, dataTempRegister, dataTempRegister);
> 
> I think it is incorrect, because or32(TrustedImm32 imm, RegisterID src, RegisterID dest)
> moves the immediate to dataTempRegister and overwrite the value you loaded to it previously.
> (I had a similar issue in bug152784 during implementing the or32 for ARM instruction set.)

Funny.  I knew about this issue (which is why I didn't implement it for ARM), and thought I had solved it for ARMv7.  Obviously, the bug still exists.  In practice, this will not be a problem for the current use case were the imm being used is always a single bit.  However, there is a correctness issue here.  I will fix.
Comment 19 Mark Lam 2016-01-07 09:36:12 PST
The fix for the or32 emitter bug is tracked at https://bugs.webkit.org/show_bug.cgi?id=152833.
Comment 20 Mark Lam 2016-01-07 13:37:39 PST
*** Bug 152752 has been marked as a duplicate of this bug. ***