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), &notZero);
72       masm.branchTest32(Assembler::Signed, ExtractTemp0, ExtractTemp0,
73                         &failure);
74       masm.bind(&notZero);
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