1/*
2 * Copyright (C) 2015-2019 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 "JITDivGenerator.h"
28
29#if ENABLE(JIT)
30
31#include "ArithProfile.h"
32#include "JSCJSValueInlines.h"
33#include "MathCommon.h"
34
35namespace JSC {
36
37void JITDivGenerator::loadOperand(CCallHelpers& jit, SnippetOperand& opr, JSValueRegs oprRegs, FPRReg destFPR)
38{
39 if (opr.isConstInt32()) {
40 // FIXME: this does not looks right.
41 // -On x86_64, CVTSI2SD has partial register stall on its FPR.
42 // A move or load might be a tiny bit larger but safer.
43 // -On ARM64 we also have FMOV that can load small immediates.
44 jit.move(CCallHelpers::Imm32(opr.asConstInt32()), m_scratchGPR);
45 jit.convertInt32ToDouble(m_scratchGPR, destFPR);
46#if USE(JSVALUE64)
47 } else if (opr.isConstDouble()) {
48 jit.move(CCallHelpers::Imm64(opr.asRawBits()), m_scratchGPR);
49 jit.move64ToDouble(m_scratchGPR, destFPR);
50#endif
51 } else {
52 if (!opr.definitelyIsNumber())
53 m_slowPathJumpList.append(jit.branchIfNotNumber(oprRegs, m_scratchGPR));
54 CCallHelpers::Jump notInt32 = jit.branchIfNotInt32(oprRegs);
55 jit.convertInt32ToDouble(oprRegs.payloadGPR(), destFPR);
56 CCallHelpers::Jump oprIsLoaded = jit.jump();
57 notInt32.link(&jit);
58 jit.unboxDoubleNonDestructive(oprRegs, destFPR, m_scratchGPR, m_scratchFPR);
59 oprIsLoaded.link(&jit);
60 }
61}
62
63void JITDivGenerator::generateFastPath(CCallHelpers& jit)
64{
65 ASSERT(m_scratchGPR != InvalidGPRReg);
66 ASSERT(m_scratchGPR != m_left.payloadGPR());
67 ASSERT(m_scratchGPR != m_right.payloadGPR());
68#if USE(JSVALUE32_64)
69 ASSERT(m_scratchGPR != m_left.tagGPR());
70 ASSERT(m_scratchGPR != m_right.tagGPR());
71 ASSERT(m_scratchFPR != InvalidFPRReg);
72#endif
73
74 ASSERT(!m_didEmitFastPath);
75 if (!jit.supportsFloatingPoint())
76 return;
77 if (!m_leftOperand.mightBeNumber() || !m_rightOperand.mightBeNumber())
78 return;
79
80 ASSERT(!m_leftOperand.isConstInt32() || !m_rightOperand.isConstInt32());
81 m_didEmitFastPath = true;
82 loadOperand(jit, m_leftOperand, m_left, m_leftFPR);
83
84#if USE(JSVALUE64)
85 Optional<double> safeReciprocal;
86 if (m_rightOperand.isConst()) {
87 double constant = m_rightOperand.asConstNumber();
88 safeReciprocal = safeReciprocalForDivByConst(constant);
89 }
90
91 if (safeReciprocal) {
92 jit.move(CCallHelpers::Imm64(bitwise_cast<int64_t>(*safeReciprocal)), m_scratchGPR);
93 jit.move64ToDouble(m_scratchGPR, m_rightFPR);
94
95 jit.mulDouble(m_rightFPR, m_leftFPR);
96 } else
97#endif
98 {
99 loadOperand(jit, m_rightOperand, m_right, m_rightFPR);
100
101 jit.divDouble(m_rightFPR, m_leftFPR);
102 }
103
104 // Is the result actually an integer? The DFG JIT would really like to know. If it's
105 // not an integer, we set a bit. If this together with the slow case counter are below
106 // threshold then the DFG JIT will compile this division with a speculation that the
107 // remainder is zero.
108
109 // As well, there are cases where a double result here would cause an important field
110 // in the heap to sometimes have doubles in it, resulting in double predictions getting
111 // propagated to a use site where it might cause damage (such as the index to an array
112 // access). So if we are DFG compiling anything in the program, we want this code to
113 // ensure that it produces integers whenever possible.
114
115 CCallHelpers::JumpList notInt32;
116 jit.branchConvertDoubleToInt32(m_leftFPR, m_scratchGPR, notInt32, m_scratchFPR);
117
118 // If we've got an integer, we might as well make that the result of the division.
119 jit.boxInt32(m_scratchGPR, m_result);
120 m_endJumpList.append(jit.jump());
121
122 notInt32.link(&jit);
123#if USE(JSVALUE64)
124 jit.moveDoubleTo64(m_leftFPR, m_scratchGPR);
125 CCallHelpers::Jump notDoubleZero = jit.branchTest64(CCallHelpers::NonZero, m_scratchGPR);
126
127 jit.move(GPRInfo::numberTagRegister, m_result.payloadGPR());
128 m_endJumpList.append(jit.jump());
129
130 notDoubleZero.link(&jit);
131#endif
132 if (m_arithProfile)
133 m_arithProfile->emitUnconditionalSet(jit, BinaryArithProfile::specialFastPathBit);
134 jit.boxDouble(m_leftFPR, m_result);
135}
136
137} // namespace JSC
138
139#endif // ENABLE(JIT)
140