[JSC] Shrink the Math inline caches some more
[WebKit-https.git] / Source / JavaScriptCore / jit / JITMulGenerator.cpp
1 /*
2  * Copyright (C) 2015-2016 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24  */
25
26 #include "config.h"
27 #include "JITMulGenerator.h"
28
29 #if ENABLE(JIT)
30
31 #include "ArithProfile.h"
32 #include "JITMathIC.h"
33
34 namespace JSC {
35
36 JITMathICInlineResult JITMulGenerator::generateInline(CCallHelpers& jit, MathICGenerationState& state, const ArithProfile* arithProfile)
37 {
38     // We default to speculating int32.
39     ObservedType lhs = ObservedType().withInt32();
40     ObservedType rhs = ObservedType().withInt32();
41     if (arithProfile) {
42         lhs = arithProfile->lhsObservedType();
43         rhs = arithProfile->rhsObservedType();
44     }
45
46     if (lhs.isOnlyNonNumber() && rhs.isOnlyNonNumber())
47         return JITMathICInlineResult::DontGenerate;
48
49     if (lhs.isOnlyNumber() && rhs.isOnlyNumber()) {
50         if (!jit.supportsFloatingPoint())
51             return JITMathICInlineResult::DontGenerate;
52
53         if (!m_leftOperand.definitelyIsNumber())
54             state.slowPathJumps.append(jit.branchIfNotNumber(m_left, m_scratchGPR));
55         if (!m_rightOperand.definitelyIsNumber())
56             state.slowPathJumps.append(jit.branchIfNotNumber(m_right, m_scratchGPR));
57         state.slowPathJumps.append(jit.branchIfInt32(m_left));
58         state.slowPathJumps.append(jit.branchIfInt32(m_right));
59         jit.unboxDoubleNonDestructive(m_left, m_leftFPR, m_scratchGPR, m_scratchFPR);
60         jit.unboxDoubleNonDestructive(m_right, m_rightFPR, m_scratchGPR, m_scratchFPR);
61         jit.mulDouble(m_rightFPR, m_leftFPR);
62         jit.boxDouble(m_leftFPR, m_result);
63
64         return JITMathICInlineResult::GeneratedFastPath;
65     }
66
67     if ((lhs.isOnlyInt32() || m_leftOperand.isPositiveConstInt32()) && (rhs.isOnlyInt32() || m_rightOperand.isPositiveConstInt32())) {
68         ASSERT(!m_leftOperand.isPositiveConstInt32() || !m_rightOperand.isPositiveConstInt32());
69         if (!m_leftOperand.isPositiveConstInt32())
70             state.slowPathJumps.append(jit.branchIfNotInt32(m_left));
71         if (!m_rightOperand.isPositiveConstInt32())
72             state.slowPathJumps.append(jit.branchIfNotInt32(m_right));
73
74         if (m_leftOperand.isPositiveConstInt32() || m_rightOperand.isPositiveConstInt32()) {
75             JSValueRegs var = m_leftOperand.isPositiveConstInt32() ? m_right : m_left;
76             int32_t constValue = m_leftOperand.isPositiveConstInt32() ? m_leftOperand.asConstInt32() : m_rightOperand.asConstInt32();
77             state.slowPathJumps.append(jit.branchMul32(CCallHelpers::Overflow, var.payloadGPR(), CCallHelpers::Imm32(constValue), m_scratchGPR));
78         } else {
79             state.slowPathJumps.append(jit.branchMul32(CCallHelpers::Overflow, m_right.payloadGPR(), m_left.payloadGPR(), m_scratchGPR));
80             state.slowPathJumps.append(jit.branchTest32(CCallHelpers::Zero, m_scratchGPR)); // Go slow if potential negative zero.
81         }
82         jit.boxInt32(m_scratchGPR, m_result);
83
84         return JITMathICInlineResult::GeneratedFastPath;
85     }
86
87     return JITMathICInlineResult::GenerateFullSnippet;
88 }
89
90 bool JITMulGenerator::generateFastPath(CCallHelpers& jit, CCallHelpers::JumpList& endJumpList, CCallHelpers::JumpList& slowPathJumpList, const ArithProfile* arithProfile, bool shouldEmitProfiling)
91 {
92     ASSERT(m_scratchGPR != InvalidGPRReg);
93     ASSERT(m_scratchGPR != m_left.payloadGPR());
94     ASSERT(m_scratchGPR != m_right.payloadGPR());
95 #if USE(JSVALUE64)
96     ASSERT(m_scratchGPR != m_result.payloadGPR());
97 #else
98     ASSERT(m_scratchGPR != m_left.tagGPR());
99     ASSERT(m_scratchGPR != m_right.tagGPR());
100     ASSERT(m_scratchFPR != InvalidFPRReg);
101 #endif
102
103     ASSERT(!m_leftOperand.isPositiveConstInt32() || !m_rightOperand.isPositiveConstInt32());
104
105     if (!m_leftOperand.mightBeNumber() || !m_rightOperand.mightBeNumber())
106         return false;
107
108     if (m_leftOperand.isPositiveConstInt32() || m_rightOperand.isPositiveConstInt32()) {
109         JSValueRegs var = m_leftOperand.isPositiveConstInt32() ? m_right : m_left;
110         SnippetOperand& varOpr = m_leftOperand.isPositiveConstInt32() ? m_rightOperand : m_leftOperand;
111         SnippetOperand& constOpr = m_leftOperand.isPositiveConstInt32() ? m_leftOperand : m_rightOperand;
112
113         // Try to do intVar * intConstant.
114         CCallHelpers::Jump notInt32 = jit.branchIfNotInt32(var);
115
116         GPRReg multiplyResultGPR = m_result.payloadGPR();
117         if (multiplyResultGPR == var.payloadGPR())
118             multiplyResultGPR = m_scratchGPR;
119
120         slowPathJumpList.append(jit.branchMul32(CCallHelpers::Overflow, var.payloadGPR(), CCallHelpers::Imm32(constOpr.asConstInt32()), multiplyResultGPR));
121
122         jit.boxInt32(multiplyResultGPR, m_result);
123         endJumpList.append(jit.jump());
124
125         if (!jit.supportsFloatingPoint()) {
126             slowPathJumpList.append(notInt32);
127             return true;
128         }
129
130         // Try to do doubleVar * double(intConstant).
131         notInt32.link(&jit);
132         if (!varOpr.definitelyIsNumber())
133             slowPathJumpList.append(jit.branchIfNotNumber(var, m_scratchGPR));
134
135         jit.unboxDoubleNonDestructive(var, m_leftFPR, m_scratchGPR, m_scratchFPR);
136
137         jit.move(CCallHelpers::Imm32(constOpr.asConstInt32()), m_scratchGPR);
138         jit.convertInt32ToDouble(m_scratchGPR, m_rightFPR);
139
140         // Fall thru to doubleVar * doubleVar.
141
142     } else {
143         ASSERT(!m_leftOperand.isPositiveConstInt32() && !m_rightOperand.isPositiveConstInt32());
144
145         CCallHelpers::Jump leftNotInt;
146         CCallHelpers::Jump rightNotInt;
147
148         // Try to do intVar * intVar.
149         leftNotInt = jit.branchIfNotInt32(m_left);
150         rightNotInt = jit.branchIfNotInt32(m_right);
151
152         slowPathJumpList.append(jit.branchMul32(CCallHelpers::Overflow, m_right.payloadGPR(), m_left.payloadGPR(), m_scratchGPR));
153         slowPathJumpList.append(jit.branchTest32(CCallHelpers::Zero, m_scratchGPR)); // Go slow if potential negative zero.
154
155         jit.boxInt32(m_scratchGPR, m_result);
156         endJumpList.append(jit.jump());
157
158         if (!jit.supportsFloatingPoint()) {
159             slowPathJumpList.append(leftNotInt);
160             slowPathJumpList.append(rightNotInt);
161             return true;
162         }
163
164         leftNotInt.link(&jit);
165         if (!m_leftOperand.definitelyIsNumber())
166             slowPathJumpList.append(jit.branchIfNotNumber(m_left, m_scratchGPR));
167         if (!m_rightOperand.definitelyIsNumber())
168             slowPathJumpList.append(jit.branchIfNotNumber(m_right, m_scratchGPR));
169
170         jit.unboxDoubleNonDestructive(m_left, m_leftFPR, m_scratchGPR, m_scratchFPR);
171         CCallHelpers::Jump rightIsDouble = jit.branchIfNotInt32(m_right);
172
173         jit.convertInt32ToDouble(m_right.payloadGPR(), m_rightFPR);
174         CCallHelpers::Jump rightWasInteger = jit.jump();
175
176         rightNotInt.link(&jit);
177         if (!m_rightOperand.definitelyIsNumber())
178             slowPathJumpList.append(jit.branchIfNotNumber(m_right, m_scratchGPR));
179
180         jit.convertInt32ToDouble(m_left.payloadGPR(), m_leftFPR);
181
182         rightIsDouble.link(&jit);
183         jit.unboxDoubleNonDestructive(m_right, m_rightFPR, m_scratchGPR, m_scratchFPR);
184
185         rightWasInteger.link(&jit);
186
187         // Fall thru to doubleVar * doubleVar.
188     }
189
190     // Do doubleVar * doubleVar.
191     jit.mulDouble(m_rightFPR, m_leftFPR);
192
193     if (!arithProfile || !shouldEmitProfiling)
194         jit.boxDouble(m_leftFPR, m_result);
195     else {
196         // The Int52 overflow check below intentionally omits 1ll << 51 as a valid negative Int52 value.
197         // Therefore, we will get a false positive if the result is that value. This is intentionally
198         // done to simplify the checking algorithm.
199
200         const int64_t negativeZeroBits = 1ll << 63;
201 #if USE(JSVALUE64)
202         jit.moveDoubleTo64(m_leftFPR, m_result.payloadGPR());
203
204         CCallHelpers::Jump notNegativeZero = jit.branch64(CCallHelpers::NotEqual, m_result.payloadGPR(), CCallHelpers::TrustedImm64(negativeZeroBits));
205
206         jit.or32(CCallHelpers::TrustedImm32(ArithProfile::NegZeroDouble), CCallHelpers::AbsoluteAddress(arithProfile->addressOfBits()));
207         CCallHelpers::Jump done = jit.jump();
208
209         notNegativeZero.link(&jit);
210         jit.or32(CCallHelpers::TrustedImm32(ArithProfile::NonNegZeroDouble), CCallHelpers::AbsoluteAddress(arithProfile->addressOfBits()));
211
212         jit.move(m_result.payloadGPR(), m_scratchGPR);
213         jit.urshiftPtr(CCallHelpers::Imm32(52), m_scratchGPR);
214         jit.and32(CCallHelpers::Imm32(0x7ff), m_scratchGPR);
215         CCallHelpers::Jump noInt52Overflow = jit.branch32(CCallHelpers::LessThanOrEqual, m_scratchGPR, CCallHelpers::TrustedImm32(0x431));
216
217         jit.or32(CCallHelpers::TrustedImm32(ArithProfile::Int52Overflow), CCallHelpers::AbsoluteAddress(arithProfile->addressOfBits()));
218         noInt52Overflow.link(&jit);
219
220         done.link(&jit);
221         jit.sub64(GPRInfo::tagTypeNumberRegister, m_result.payloadGPR()); // Box the double.
222 #else
223         jit.boxDouble(m_leftFPR, m_result);
224         CCallHelpers::JumpList notNegativeZero;
225         notNegativeZero.append(jit.branch32(CCallHelpers::NotEqual, m_result.payloadGPR(), CCallHelpers::TrustedImm32(0)));
226         notNegativeZero.append(jit.branch32(CCallHelpers::NotEqual, m_result.tagGPR(), CCallHelpers::TrustedImm32(negativeZeroBits >> 32)));
227
228         jit.or32(CCallHelpers::TrustedImm32(ArithProfile::NegZeroDouble), CCallHelpers::AbsoluteAddress(arithProfile->addressOfBits()));
229         CCallHelpers::Jump done = jit.jump();
230
231         notNegativeZero.link(&jit);
232         jit.or32(CCallHelpers::TrustedImm32(ArithProfile::NonNegZeroDouble), CCallHelpers::AbsoluteAddress(arithProfile->addressOfBits()));
233
234         jit.move(m_result.tagGPR(), m_scratchGPR);
235         jit.urshiftPtr(CCallHelpers::Imm32(52 - 32), m_scratchGPR);
236         jit.and32(CCallHelpers::Imm32(0x7ff), m_scratchGPR);
237         CCallHelpers::Jump noInt52Overflow = jit.branch32(CCallHelpers::LessThanOrEqual, m_scratchGPR, CCallHelpers::TrustedImm32(0x431));
238         
239         jit.or32(CCallHelpers::TrustedImm32(ArithProfile::Int52Overflow), CCallHelpers::AbsoluteAddress(arithProfile->addressOfBits()));
240
241         endJumpList.append(noInt52Overflow);
242         if (m_scratchGPR == m_result.tagGPR() || m_scratchGPR == m_result.payloadGPR())
243             jit.boxDouble(m_leftFPR, m_result);
244
245         endJumpList.append(done);
246 #endif
247     }
248
249     return true;
250 }
251
252 } // namespace JSC
253
254 #endif // ENABLE(JIT)