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