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), ¬Zero); 74 masm.branchTest32(Assembler::Signed, ExtractTemp0, ExtractTemp0, &failure); 75 masm.bind(¬Zero); 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