1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 * vim: set ts=8 sts=4 et sw=4 tw=99:
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7 #include "jit/BaselineIC.h"
8 #include "jit/SharedICHelpers.h"
9
10 #include "jit/MacroAssembler-inl.h"
11
12 using namespace js;
13 using namespace js::jit;
14
15 namespace js {
16 namespace jit {
17
18 // ICBinaryArith_Int32
19
generateStubCode(MacroAssembler & masm)20 bool ICBinaryArith_Int32::Compiler::generateStubCode(MacroAssembler& masm) {
21 // Guard that R0 is an integer and R1 is an integer.
22 Label failure;
23 masm.branchTestInt32(Assembler::NotEqual, R0, &failure);
24 masm.branchTestInt32(Assembler::NotEqual, R1, &failure);
25
26 // The scratch register is only used in the case of JSOP_URSH.
27 mozilla::Maybe<ScratchRegisterScope> scratch;
28
29 Label revertRegister, maybeNegZero;
30 switch (op_) {
31 case JSOP_ADD:
32 masm.unboxInt32(R0, ExtractTemp0);
33 // Just jump to failure on overflow. R0 and R1 are preserved, so we can
34 // just jump to the next stub.
35 masm.addl(R1.valueReg(), ExtractTemp0);
36 masm.j(Assembler::Overflow, &failure);
37
38 // Box the result
39 masm.boxValue(JSVAL_TYPE_INT32, ExtractTemp0, R0.valueReg());
40 break;
41 case JSOP_SUB:
42 masm.unboxInt32(R0, ExtractTemp0);
43 masm.subl(R1.valueReg(), ExtractTemp0);
44 masm.j(Assembler::Overflow, &failure);
45 masm.boxValue(JSVAL_TYPE_INT32, ExtractTemp0, R0.valueReg());
46 break;
47 case JSOP_MUL:
48 masm.unboxInt32(R0, ExtractTemp0);
49 masm.imull(R1.valueReg(), ExtractTemp0);
50 masm.j(Assembler::Overflow, &failure);
51
52 masm.branchTest32(Assembler::Zero, ExtractTemp0, ExtractTemp0,
53 &maybeNegZero);
54
55 masm.boxValue(JSVAL_TYPE_INT32, ExtractTemp0, R0.valueReg());
56 break;
57 case JSOP_DIV: {
58 MOZ_ASSERT(R2.scratchReg() == rax);
59 MOZ_ASSERT(R0.valueReg() != rdx);
60 MOZ_ASSERT(R1.valueReg() != rdx);
61 masm.unboxInt32(R0, eax);
62 masm.unboxInt32(R1, ExtractTemp0);
63
64 // Prevent division by 0.
65 masm.branchTest32(Assembler::Zero, ExtractTemp0, ExtractTemp0, &failure);
66
67 // Prevent negative 0 and -2147483648 / -1.
68 masm.branch32(Assembler::Equal, eax, Imm32(INT32_MIN), &failure);
69
70 Label notZero;
71 masm.branch32(Assembler::NotEqual, eax, Imm32(0), ¬Zero);
72 masm.branchTest32(Assembler::Signed, ExtractTemp0, ExtractTemp0,
73 &failure);
74 masm.bind(¬Zero);
75
76 // Sign extend eax into edx to make (edx:eax), since idiv is 64-bit.
77 masm.cdq();
78 masm.idiv(ExtractTemp0);
79
80 // A remainder implies a double result.
81 masm.branchTest32(Assembler::NonZero, edx, edx, &failure);
82
83 masm.boxValue(JSVAL_TYPE_INT32, eax, R0.valueReg());
84 break;
85 }
86 case JSOP_MOD: {
87 MOZ_ASSERT(R2.scratchReg() == rax);
88 MOZ_ASSERT(R0.valueReg() != rdx);
89 MOZ_ASSERT(R1.valueReg() != rdx);
90 masm.unboxInt32(R0, eax);
91 masm.unboxInt32(R1, ExtractTemp0);
92
93 // x % 0 always results in NaN.
94 masm.branchTest32(Assembler::Zero, ExtractTemp0, ExtractTemp0, &failure);
95
96 // Prevent negative 0 and -2147483648 % -1.
97 masm.branchTest32(Assembler::Zero, eax, Imm32(0x7fffffff), &failure);
98
99 // Sign extend eax into edx to make (edx:eax), since idiv is 64-bit.
100 masm.cdq();
101 masm.idiv(ExtractTemp0);
102
103 // Fail when we would need a negative remainder.
104 Label done;
105 masm.branchTest32(Assembler::NonZero, edx, edx, &done);
106 masm.orl(ExtractTemp0, eax);
107 masm.branchTest32(Assembler::Signed, eax, eax, &failure);
108
109 masm.bind(&done);
110 masm.boxValue(JSVAL_TYPE_INT32, edx, R0.valueReg());
111 break;
112 }
113 case JSOP_BITOR:
114 // We can overide R0, because the instruction is unfailable.
115 // Because the tag bits are the same, we don't need to retag.
116 masm.orq(R1.valueReg(), R0.valueReg());
117 break;
118 case JSOP_BITXOR:
119 masm.xorl(R1.valueReg(), R0.valueReg());
120 masm.tagValue(JSVAL_TYPE_INT32, R0.valueReg(), R0);
121 break;
122 case JSOP_BITAND:
123 masm.andq(R1.valueReg(), R0.valueReg());
124 break;
125 case JSOP_LSH:
126 masm.unboxInt32(R0, ExtractTemp0);
127 masm.unboxInt32(R1, ecx); // Unboxing R1 to ecx, clobbers R0.
128 masm.shll_cl(ExtractTemp0);
129 masm.boxValue(JSVAL_TYPE_INT32, ExtractTemp0, R0.valueReg());
130 break;
131 case JSOP_RSH:
132 masm.unboxInt32(R0, ExtractTemp0);
133 masm.unboxInt32(R1, ecx);
134 masm.sarl_cl(ExtractTemp0);
135 masm.boxValue(JSVAL_TYPE_INT32, ExtractTemp0, R0.valueReg());
136 break;
137 case JSOP_URSH:
138 if (!allowDouble_) {
139 scratch.emplace(masm);
140 masm.movq(R0.valueReg(), *scratch);
141 }
142
143 masm.unboxInt32(R0, ExtractTemp0);
144 masm.unboxInt32(R1, ecx); // This clobbers R0
145
146 masm.shrl_cl(ExtractTemp0);
147 masm.test32(ExtractTemp0, ExtractTemp0);
148 if (allowDouble_) {
149 Label toUint;
150 masm.j(Assembler::Signed, &toUint);
151
152 // Box and return.
153 masm.boxValue(JSVAL_TYPE_INT32, ExtractTemp0, R0.valueReg());
154 EmitReturnFromIC(masm);
155
156 masm.bind(&toUint);
157 ScratchDoubleScope scratchDouble(masm);
158 masm.convertUInt32ToDouble(ExtractTemp0, scratchDouble);
159 masm.boxDouble(scratchDouble, R0, scratchDouble);
160 } else {
161 masm.j(Assembler::Signed, &revertRegister);
162 masm.boxValue(JSVAL_TYPE_INT32, ExtractTemp0, R0.valueReg());
163 }
164 break;
165 default:
166 MOZ_CRASH("Unhandled op in BinaryArith_Int32");
167 }
168
169 // Return from stub.
170 EmitReturnFromIC(masm);
171
172 if (op_ == JSOP_MUL) {
173 masm.bind(&maybeNegZero);
174
175 // Result is -0 if exactly one of lhs or rhs is negative.
176 {
177 ScratchRegisterScope scratch(masm);
178 masm.movl(R0.valueReg(), scratch);
179 masm.orl(R1.valueReg(), scratch);
180 masm.j(Assembler::Signed, &failure);
181 }
182
183 // Result is +0.
184 masm.moveValue(Int32Value(0), R0);
185 EmitReturnFromIC(masm);
186 }
187
188 // Revert the content of R0 in the fallible >>> case.
189 if (op_ == JSOP_URSH && !allowDouble_) {
190 // Scope continuation from JSOP_URSH case above.
191 masm.bind(&revertRegister);
192 // Restore tag and payload.
193 masm.movq(*scratch, R0.valueReg());
194 // Fall through to failure.
195 }
196 // Failure case - jump to next stub
197 masm.bind(&failure);
198 EmitStubGuardFailure(masm);
199
200 return true;
201 }
202
generateStubCode(MacroAssembler & masm)203 bool ICUnaryArith_Int32::Compiler::generateStubCode(MacroAssembler& masm) {
204 Label failure;
205 masm.branchTestInt32(Assembler::NotEqual, R0, &failure);
206
207 switch (op) {
208 case JSOP_BITNOT:
209 masm.notl(R0.valueReg());
210 break;
211 case JSOP_NEG:
212 // Guard against 0 and MIN_INT, both result in a double.
213 masm.branchTest32(Assembler::Zero, R0.valueReg(), Imm32(0x7fffffff),
214 &failure);
215 masm.negl(R0.valueReg());
216 break;
217 default:
218 MOZ_CRASH("Unexpected op");
219 }
220
221 masm.tagValue(JSVAL_TYPE_INT32, R0.valueReg(), R0);
222
223 EmitReturnFromIC(masm);
224
225 masm.bind(&failure);
226 EmitStubGuardFailure(masm);
227 return true;
228 }
229
230 } // namespace jit
231 } // namespace js
232