1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 * vim: set ts=4 sw=4 et tw=99:
4 * ***** BEGIN LICENSE BLOCK *****
5 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
7 * The contents of this file are subject to the Mozilla Public License Version
8 * 1.1 (the "License"); you may not use this file except in compliance with
9 * the License. You may obtain a copy of the License at
10 * http://www.mozilla.org/MPL/
12 * Software distributed under the License is distributed on an "AS IS" basis,
13 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
14 * for the specific language governing rights and limitations under the
17 * The Original Code is Mozilla SpiderMonkey JavaScript 1.9 code, released
20 * The Initial Developer of the Original Code is
21 * Brendan Eich <brendan@mozilla.org>
24 * David Anderson <danderson@mozilla.com>
26 * Alternatively, the contents of this file may be used under the terms of
27 * either of the GNU General Public License Version 2 or later (the "GPL"),
28 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
29 * in which case the provisions of the GPL or the LGPL are applicable instead
30 * of those above. If you wish to allow use of your version of this file only
31 * under the terms of either the GPL or the LGPL, and not to allow others to
32 * use your version of this file under the terms of the MPL, indicate your
33 * decision by deleting the provisions above and replace them with the notice
34 * and other provisions required by the GPL or the LGPL. If you do not delete
35 * the provisions above, a recipient may use your version of this file under
36 * the terms of any one of the MPL, the GPL or the LGPL.
38 * ***** END LICENSE BLOCK ***** */
40 #ifndef js_typedarray_ic_h___
41 #define js_typedarray_ic_h___
44 #include "jstypedarray.h"
49 #if defined(JS_POLYIC) && (defined JS_CPU_X86 || defined JS_CPU_X64)
51 typedef JSC::MacroAssembler::RegisterID RegisterID;
52 typedef JSC::MacroAssembler::FPRegisterID FPRegisterID;
53 typedef JSC::MacroAssembler::Jump Jump;
54 typedef JSC::MacroAssembler::Imm32 Imm32;
55 typedef JSC::MacroAssembler::ImmDouble ImmDouble;
59 LoadFromTypedArray(Assembler &masm, js::TypedArray *tarray, T address,
60 RegisterID typeReg, RegisterID dataReg)
62 switch (tarray->type) {
63 case js::TypedArray::TYPE_INT8:
64 masm.load8SignExtend(address, dataReg);
65 masm.move(ImmType(JSVAL_TYPE_INT32), typeReg);
67 case js::TypedArray::TYPE_UINT8:
68 case js::TypedArray::TYPE_UINT8_CLAMPED:
69 masm.load8ZeroExtend(address, dataReg);
70 masm.move(ImmType(JSVAL_TYPE_INT32), typeReg);
72 case js::TypedArray::TYPE_INT16:
73 masm.load16SignExtend(address, dataReg);
74 masm.move(ImmType(JSVAL_TYPE_INT32), typeReg);
76 case js::TypedArray::TYPE_UINT16:
77 masm.load16(address, dataReg);
78 masm.move(ImmType(JSVAL_TYPE_INT32), typeReg);
80 case js::TypedArray::TYPE_INT32:
81 masm.load32(address, dataReg);
82 masm.move(ImmType(JSVAL_TYPE_INT32), typeReg);
84 case js::TypedArray::TYPE_UINT32:
86 masm.load32(address, dataReg);
87 masm.move(ImmType(JSVAL_TYPE_INT32), typeReg);
88 Jump safeInt = masm.branch32(Assembler::Below, dataReg, Imm32(0x80000000));
89 masm.convertUInt32ToDouble(dataReg, FPRegisters::First);
90 masm.breakDouble(FPRegisters::First, typeReg, dataReg);
91 safeInt.linkTo(masm.label(), &masm);
94 case js::TypedArray::TYPE_FLOAT32:
95 case js::TypedArray::TYPE_FLOAT64:
97 if (tarray->type == js::TypedArray::TYPE_FLOAT32)
98 masm.loadFloat(address, FPRegisters::First);
100 masm.loadDouble(address, FPRegisters::First);
101 // Make sure NaN gets canonicalized.
102 Jump notNaN = masm.branchDouble(Assembler::DoubleEqual,
105 masm.loadStaticDouble(&js_NaN, FPRegisters::First, dataReg);
106 notNaN.linkTo(masm.label(), &masm);
107 masm.breakDouble(FPRegisters::First, typeReg, dataReg);
114 ConstantFoldForFloatArray(JSContext *cx, ValueRemat *vr)
116 if (!vr->isTypeKnown())
119 // Objects and undefined coerce to NaN, which coerces to 0.
120 // Null converts to 0.
121 if (vr->knownType() == JSVAL_TYPE_OBJECT ||
122 vr->knownType() == JSVAL_TYPE_UNDEFINED) {
123 *vr = ValueRemat::FromConstant(DoubleValue(js_NaN));
126 if (vr->knownType() == JSVAL_TYPE_NULL) {
127 *vr = ValueRemat::FromConstant(DoubleValue(0));
131 if (!vr->isConstant())
134 if (vr->knownType() == JSVAL_TYPE_DOUBLE)
138 Value v = vr->value();
140 if (!StringToNumberType<jsdouble>(cx, v.toString(), &d))
142 } else if (v.isBoolean()) {
143 d = v.toBoolean() ? 1 : 0;
144 } else if (v.isInt32()) {
147 JS_NOT_REACHED("unknown constant type");
149 *vr = ValueRemat::FromConstant(DoubleValue(d));
154 ClampIntForUint8Array(int32 x)
164 ConstantFoldForIntArray(JSContext *cx, js::TypedArray *tarray, ValueRemat *vr)
166 if (!vr->isTypeKnown())
169 // Objects and undefined coerce to NaN, which coerces to 0.
170 // Null converts to 0.
171 if (vr->knownType() == JSVAL_TYPE_OBJECT ||
172 vr->knownType() == JSVAL_TYPE_UNDEFINED ||
173 vr->knownType() == JSVAL_TYPE_NULL) {
174 *vr = ValueRemat::FromConstant(Int32Value(0));
178 if (!vr->isConstant())
181 // Convert from string to double first (see bug 624483).
182 Value v = vr->value();
185 if (!StringToNumberType<double>(cx, v.toString(), &d))
192 i32 = (tarray->type == js::TypedArray::TYPE_UINT8_CLAMPED)
193 ? js_TypedArray_uint8_clamp_double(v.toDouble())
194 : js_DoubleToECMAInt32(v.toDouble());
195 } else if (v.isInt32()) {
197 if (tarray->type == js::TypedArray::TYPE_UINT8_CLAMPED)
198 i32 = ClampIntForUint8Array(i32);
199 } else if (v.isBoolean()) {
200 i32 = v.toBoolean() ? 1 : 0;
202 JS_NOT_REACHED("unknown constant type");
205 *vr = ValueRemat::FromConstant(Int32Value(i32));
210 template <typename S, typename T>
212 StoreToIntArray(Assembler &masm, js::TypedArray *tarray, S src, T address)
214 switch (tarray->type) {
215 case js::TypedArray::TYPE_INT8:
216 case js::TypedArray::TYPE_UINT8:
217 case js::TypedArray::TYPE_UINT8_CLAMPED:
218 masm.store8(src, address);
220 case js::TypedArray::TYPE_INT16:
221 case js::TypedArray::TYPE_UINT16:
222 masm.store16(src, address);
224 case js::TypedArray::TYPE_INT32:
225 case js::TypedArray::TYPE_UINT32:
226 masm.store32(src, address);
229 JS_NOT_REACHED("unknown int array type");
233 template <typename S, typename T>
235 StoreToFloatArray(Assembler &masm, js::TypedArray *tarray, S src, T address)
237 if (tarray->type == js::TypedArray::TYPE_FLOAT32)
238 masm.storeFloat(src, address);
240 masm.storeDouble(src, address);
243 // Generate code that will ensure a dynamically typed value, pinned in |vr|,
244 // can be stored in an integer typed array. If any sort of conversion is
245 // required, |dataReg| will be clobbered by a new value. |saveMask| is
246 // used to ensure that |dataReg| (and volatile registers) are preserved
247 // across any conversion process.
249 GenConversionForIntArray(Assembler &masm, js::TypedArray *tarray, const ValueRemat &vr,
252 if (vr.isConstant()) {
253 // Constants are always folded to ints up-front.
254 JS_ASSERT(vr.knownType() == JSVAL_TYPE_INT32);
258 if (!vr.isTypeKnown() || vr.knownType() != JSVAL_TYPE_INT32) {
259 // If a conversion is necessary, save registers now.
260 MaybeJump checkInt32;
261 if (!vr.isTypeKnown())
262 checkInt32 = masm.testInt32(Assembler::Equal, vr.typeReg());
264 // Store the value to convert.
265 StackMarker vp = masm.allocStack(sizeof(Value), sizeof(double));
266 masm.storeValue(vr, masm.addressOfExtra(vp));
268 // Preserve volatile registers.
269 PreserveRegisters saveForCall(masm);
270 saveForCall.preserve(saveMask & Registers::TempRegs);
272 masm.setupABICall(Registers::FastCall, 2);
273 masm.storeArg(0, masm.vmFrameOffset(offsetof(VMFrame, cx)));
274 masm.storeArgAddr(1, masm.addressOfExtra(vp));
276 typedef int32 (JS_FASTCALL *Int32CxVp)(JSContext *, Value *);
278 if (tarray->type == js::TypedArray::TYPE_UINT8_CLAMPED)
279 stub = stubs::ConvertToTypedInt<true>;
281 stub = stubs::ConvertToTypedInt<false>;
282 masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, stub), false);
283 if (vr.dataReg() != Registers::ReturnReg)
284 masm.move(Registers::ReturnReg, vr.dataReg());
286 saveForCall.restore();
289 if (checkInt32.isSet())
290 checkInt32.get().linkTo(masm.label(), &masm);
293 // Performing clamping, if needed.
294 if (tarray->type == js::TypedArray::TYPE_UINT8_CLAMPED) {
305 Jump j = masm.branch32(Assembler::GreaterThanOrEqual, vr.dataReg(), Imm32(0));
306 masm.move(Imm32(0), vr.dataReg());
307 Jump done = masm.jump();
308 j.linkTo(masm.label(), &masm);
309 j = masm.branch32(Assembler::LessThanOrEqual, vr.dataReg(), Imm32(255));
310 masm.move(Imm32(255), vr.dataReg());
311 j.linkTo(masm.label(), &masm);
312 done.linkTo(masm.label(), &masm);
316 // Generate code that will ensure a dynamically typed value, pinned in |vr|,
317 // can be stored in an integer typed array. saveMask| is used to ensure that
318 // |dataReg| (and volatile registers) are preserved across any conversion
321 // Constants are left untouched. Any other value is placed into
322 // FPRegisters::First.
324 GenConversionForFloatArray(Assembler &masm, js::TypedArray *tarray, const ValueRemat &vr,
325 FPRegisterID destReg, uint32 saveMask)
327 if (vr.isConstant()) {
328 // Constants are always folded to doubles up-front.
329 JS_ASSERT(vr.knownType() == JSVAL_TYPE_DOUBLE);
333 // Fast-path, if the value is a double, skip converting.
335 if (!vr.isTypeKnown())
336 isDouble = masm.testDouble(Assembler::Equal, vr.typeReg());
338 // If the value is an integer, inline the conversion.
339 MaybeJump skip1, skip2;
340 if (!vr.isTypeKnown() || vr.knownType() == JSVAL_TYPE_INT32) {
341 MaybeJump isNotInt32;
342 if (!vr.isTypeKnown())
343 isNotInt32 = masm.testInt32(Assembler::NotEqual, vr.typeReg());
344 masm.convertInt32ToDouble(vr.dataReg(), destReg);
345 if (isNotInt32.isSet()) {
347 isNotInt32.get().linkTo(masm.label(), &masm);
351 // Generate a generic conversion call, if not known to be int32 or double.
352 if (!vr.isTypeKnown() ||
353 (vr.knownType() != JSVAL_TYPE_INT32 &&
354 vr.knownType() != JSVAL_TYPE_DOUBLE)) {
355 // Store this value, which is also an outparam.
356 StackMarker vp = masm.allocStack(sizeof(Value), sizeof(double));
357 masm.storeValue(vr, masm.addressOfExtra(vp));
359 // Preserve volatile registers, and make the call.
360 PreserveRegisters saveForCall(masm);
361 saveForCall.preserve(saveMask & Registers::TempRegs);
362 masm.setupABICall(Registers::FastCall, 2);
363 masm.storeArg(0, masm.vmFrameOffset(offsetof(VMFrame, cx)));
364 masm.storeArgAddr(1, masm.addressOfExtra(vp));
365 masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, stubs::ConvertToTypedFloat), false);
366 saveForCall.restore();
368 // Load the value from the outparam, then pop the stack.
369 masm.loadDouble(masm.addressOfExtra(vp), destReg);
374 if (isDouble.isSet())
375 isDouble.get().linkTo(masm.label(), &masm);
377 // If it's possible the value was already a double, load it directly
378 // from registers (the known type is distinct from typeReg, which has
379 // 32-bits of the 64-bit double).
380 if (!vr.isTypeKnown() || vr.knownType() == JSVAL_TYPE_DOUBLE)
381 masm.fastLoadDouble(vr.dataReg(), vr.typeReg(), destReg);
383 // At this point, all loads into xmm1 are complete.
385 skip1.get().linkTo(masm.label(), &masm);
387 skip2.get().linkTo(masm.label(), &masm);
389 if (tarray->type == js::TypedArray::TYPE_FLOAT32)
390 masm.convertDoubleToFloat(destReg, destReg);
393 template <typename T>
395 StoreToTypedArray(JSContext *cx, Assembler &masm, js::TypedArray *tarray, T address,
396 const ValueRemat &vrIn, uint32 saveMask)
398 ValueRemat vr = vrIn;
400 switch (tarray->type) {
401 case js::TypedArray::TYPE_INT8:
402 case js::TypedArray::TYPE_UINT8:
403 case js::TypedArray::TYPE_UINT8_CLAMPED:
404 case js::TypedArray::TYPE_INT16:
405 case js::TypedArray::TYPE_UINT16:
406 case js::TypedArray::TYPE_INT32:
407 case js::TypedArray::TYPE_UINT32:
409 if (!ConstantFoldForIntArray(cx, tarray, &vr))
412 PreserveRegisters saveRHS(masm);
413 PreserveRegisters saveLHS(masm);
415 // There are three tricky situations to handle:
416 // (1) The RHS needs conversion. saveMask will be stomped, and
417 // the RHS may need to be stomped.
418 // (2) The RHS may need to be clamped, which clobbers it.
419 // (3) The RHS may need to be in a single-byte register.
421 // In all of these cases, we try to find a free register that can be
422 // used to mutate the RHS. Failing that, we evict an existing volatile
425 // Note that we are careful to preserve the RHS before saving registers
426 // for the conversion call. This is because the object and key may be
427 // in temporary registers, and we want to restore those without killing
429 bool singleByte = (tarray->type == js::TypedArray::TYPE_INT8 ||
430 tarray->type == js::TypedArray::TYPE_UINT8 ||
431 tarray->type == js::TypedArray::TYPE_UINT8_CLAMPED);
432 bool mayNeedConversion = (!vr.isTypeKnown() || vr.knownType() != JSVAL_TYPE_INT32);
433 bool mayNeedClamping = !vr.isConstant() && (tarray->type == js::TypedArray::TYPE_UINT8_CLAMPED);
434 bool needsSingleByteReg = singleByte &&
436 !(Registers::SingleByteRegs & Registers::maskReg(vr.dataReg()));
437 bool rhsIsMutable = !vr.isConstant() && !(saveMask & Registers::maskReg(vr.dataReg()));
439 if (((mayNeedConversion || mayNeedClamping) && !rhsIsMutable) || needsSingleByteReg) {
440 // First attempt to find a free temporary register that:
441 // - is compatible with the RHS constraints
442 // - won't clobber the key, object, or RHS type regs
443 // - is temporary, but
444 // - is not in saveMask, which contains live volatile registers.
445 uint32 allowMask = Registers::AvailRegs;
447 allowMask &= Registers::SingleByteRegs;
449 // Create a mask of registers we absolutely cannot clobber.
450 uint32 pinned = Assembler::maskAddress(address);
451 if (!vr.isTypeKnown())
452 pinned |= Registers::maskReg(vr.typeReg());
454 Registers avail = allowMask & ~(pinned | saveMask);
457 if (!avail.empty()) {
458 newReg = avail.takeAnyReg();
460 // If no registers meet the ideal set, relax a constraint and spill.
461 avail = allowMask & ~pinned;
463 if (!avail.empty()) {
464 newReg = avail.takeAnyReg();
465 saveRHS.preserve(Registers::maskReg(newReg));
467 // Oh no! *All* single byte registers are pinned. This
468 // sucks. We'll swap the type and data registers in |vr|
469 // and unswap them later.
471 // If |vr|'s registers are part of the address, swapping is
472 // going to cause problems during the store.
473 uint32 vrRegs = Registers::mask2Regs(vr.dataReg(), vr.typeReg());
474 uint32 lhsMask = vrRegs & Assembler::maskAddress(address);
476 // We'll also need to save any of the registers which won't
477 // be restored via |lhsMask| above.
478 uint32 rhsMask = vrRegs & ~lhsMask;
480 // Push them, but get the order right. We'll pop LHS first.
481 saveRHS.preserve(rhsMask);
482 saveLHS.preserve(lhsMask);
484 // Don't store/restore registers if we dont have to.
485 saveMask &= ~lhsMask;
487 // Actually perform the swap.
488 masm.swap(vr.typeReg(), vr.dataReg());
489 vr = ValueRemat::FromRegisters(vr.dataReg(), vr.typeReg());
490 newReg = vr.dataReg();
493 // Now, make sure the new register is not in the saveMask,
494 // so it won't get restored right after the call.
495 saveMask &= ~Registers::maskReg(newReg);
498 if (vr.dataReg() != newReg)
499 masm.move(vr.dataReg(), newReg);
502 if (vr.isTypeKnown())
503 vr = ValueRemat::FromKnownType(vr.knownType(), newReg);
505 vr = ValueRemat::FromRegisters(vr.typeReg(), newReg);
508 GenConversionForIntArray(masm, tarray, vr, saveMask);
510 // Restore the registers in |address|. |GenConversionForIntArray| won't
511 // restore them because we told it not to by fiddling with |saveMask|.
515 StoreToIntArray(masm, tarray, Imm32(vr.value().toInt32()), address);
517 StoreToIntArray(masm, tarray, vr.dataReg(), address);
519 // Note that this will finish restoring the damage from the
520 // earlier register swap.
525 case js::TypedArray::TYPE_FLOAT32:
526 case js::TypedArray::TYPE_FLOAT64:
527 if (!ConstantFoldForFloatArray(cx, &vr))
529 GenConversionForFloatArray(masm, tarray, vr, FPRegisters::First, saveMask);
531 StoreToFloatArray(masm, tarray, ImmDouble(vr.value().toDouble()), address);
533 StoreToFloatArray(masm, tarray, FPRegisters::First, address);
540 #endif // defined(JS_POLYIC) && (defined JS_CPU_X86 || defined JS_CPU_X64)
542 } /* namespace mjit */
545 #endif /* js_typedarray_ic_h___ */