Imported Upstream version 1.0.0
[platform/upstream/js.git] / js / src / methodjit / TypedArrayIC.h
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2  * vim: set ts=4 sw=4 et tw=99:
3  *
4  * ***** BEGIN LICENSE BLOCK *****
5  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
6  *
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/
11  *
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
15  * License.
16  *
17  * The Original Code is Mozilla SpiderMonkey JavaScript 1.9 code, released
18  * May 28, 2008.
19  *
20  * The Initial Developer of the Original Code is
21  *   Brendan Eich <brendan@mozilla.org>
22  *
23  * Contributor(s):
24  *   David Anderson <danderson@mozilla.com>
25  *
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.
37  *
38  * ***** END LICENSE BLOCK ***** */
39
40 #ifndef js_typedarray_ic_h___
41 #define js_typedarray_ic_h___
42
43 #include "jscntxt.h"
44 #include "jstypedarray.h"
45
46 namespace js {
47 namespace mjit {
48
49 #if defined(JS_POLYIC) && (defined JS_CPU_X86 || defined JS_CPU_X64)
50
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;
56
57 template <typename T>
58 static void
59 LoadFromTypedArray(Assembler &masm, js::TypedArray *tarray, T address,
60                    RegisterID typeReg, RegisterID dataReg)
61 {
62     switch (tarray->type) {
63       case js::TypedArray::TYPE_INT8:
64         masm.load8SignExtend(address, dataReg);
65         masm.move(ImmType(JSVAL_TYPE_INT32), typeReg);
66         break;
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);
71         break;
72       case js::TypedArray::TYPE_INT16:
73         masm.load16SignExtend(address, dataReg);
74         masm.move(ImmType(JSVAL_TYPE_INT32), typeReg);
75         break;
76       case js::TypedArray::TYPE_UINT16:
77         masm.load16(address, dataReg);
78         masm.move(ImmType(JSVAL_TYPE_INT32), typeReg);
79         break;
80       case js::TypedArray::TYPE_INT32:
81         masm.load32(address, dataReg);
82         masm.move(ImmType(JSVAL_TYPE_INT32), typeReg);
83         break;
84       case js::TypedArray::TYPE_UINT32:
85       {
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);
92         break;
93       }
94       case js::TypedArray::TYPE_FLOAT32:
95       case js::TypedArray::TYPE_FLOAT64:
96       {
97         if (tarray->type == js::TypedArray::TYPE_FLOAT32)
98             masm.loadFloat(address, FPRegisters::First);
99         else
100             masm.loadDouble(address, FPRegisters::First);
101         // Make sure NaN gets canonicalized.
102         Jump notNaN = masm.branchDouble(Assembler::DoubleEqual,
103                                         FPRegisters::First,
104                                         FPRegisters::First);
105         masm.loadStaticDouble(&js_NaN, FPRegisters::First, dataReg);
106         notNaN.linkTo(masm.label(), &masm);
107         masm.breakDouble(FPRegisters::First, typeReg, dataReg);
108         break;
109       }
110     }
111 }
112
113 static inline bool
114 ConstantFoldForFloatArray(JSContext *cx, ValueRemat *vr)
115 {
116     if (!vr->isTypeKnown())
117         return true;
118
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));
124         return true;
125     }
126     if (vr->knownType() == JSVAL_TYPE_NULL) {
127         *vr = ValueRemat::FromConstant(DoubleValue(0));
128         return true;
129     }
130
131     if (!vr->isConstant())
132         return true;
133
134     if (vr->knownType() == JSVAL_TYPE_DOUBLE)
135         return true;
136
137     jsdouble d = 0;
138     Value v = vr->value();
139     if (v.isString()) {
140         if (!StringToNumberType<jsdouble>(cx, v.toString(), &d))
141             return false;
142     } else if (v.isBoolean()) {
143         d = v.toBoolean() ? 1 : 0;
144     } else if (v.isInt32()) {
145         d = v.toInt32();
146     } else {
147         JS_NOT_REACHED("unknown constant type");
148     }
149     *vr = ValueRemat::FromConstant(DoubleValue(d));
150     return true;
151 }
152
153 static inline int32
154 ClampIntForUint8Array(int32 x)
155 {
156     if (x < 0)
157         return 0;
158     if (x > 255)
159         return 255;
160     return x;
161 }
162
163 static inline bool
164 ConstantFoldForIntArray(JSContext *cx, js::TypedArray *tarray, ValueRemat *vr)
165 {
166     if (!vr->isTypeKnown())
167         return true;
168
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));
175         return true;
176     }
177
178     if (!vr->isConstant())
179         return true;
180
181     // Convert from string to double first (see bug 624483).
182     Value v = vr->value();
183     if (v.isString()) {
184         double d;
185         if (!StringToNumberType<double>(cx, v.toString(), &d))
186             return false;
187         v.setNumber(d);
188     }
189
190     int32 i32 = 0;
191     if (v.isDouble()) {
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()) {
196         i32 = v.toInt32();
197         if (tarray->type == js::TypedArray::TYPE_UINT8_CLAMPED)
198             i32 = ClampIntForUint8Array(i32);
199     } else if (v.isBoolean()) {
200         i32 = v.toBoolean() ? 1 : 0;
201     } else {
202         JS_NOT_REACHED("unknown constant type");
203     }
204
205     *vr = ValueRemat::FromConstant(Int32Value(i32));
206
207     return true;
208 }
209
210 template <typename S, typename T>
211 static void
212 StoreToIntArray(Assembler &masm, js::TypedArray *tarray, S src, T address)
213 {
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);
219         break;
220       case js::TypedArray::TYPE_INT16:
221       case js::TypedArray::TYPE_UINT16:
222         masm.store16(src, address);
223         break;
224       case js::TypedArray::TYPE_INT32:
225       case js::TypedArray::TYPE_UINT32:
226         masm.store32(src, address);
227         break;
228       default:
229         JS_NOT_REACHED("unknown int array type");
230     }
231 }
232
233 template <typename S, typename T>
234 static void
235 StoreToFloatArray(Assembler &masm, js::TypedArray *tarray, S src, T address)
236 {
237     if (tarray->type == js::TypedArray::TYPE_FLOAT32)
238         masm.storeFloat(src, address);
239     else
240         masm.storeDouble(src, address);
241 }
242
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.
248 static void
249 GenConversionForIntArray(Assembler &masm, js::TypedArray *tarray, const ValueRemat &vr,
250                          uint32 saveMask)
251 {
252     if (vr.isConstant()) {
253         // Constants are always folded to ints up-front.
254         JS_ASSERT(vr.knownType() == JSVAL_TYPE_INT32);
255         return;
256     }
257
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());
263
264         // Store the value to convert.
265         StackMarker vp = masm.allocStack(sizeof(Value), sizeof(double));
266         masm.storeValue(vr, masm.addressOfExtra(vp));
267
268         // Preserve volatile registers.
269         PreserveRegisters saveForCall(masm);
270         saveForCall.preserve(saveMask & Registers::TempRegs);
271
272         masm.setupABICall(Registers::FastCall, 2);
273         masm.storeArg(0, masm.vmFrameOffset(offsetof(VMFrame, cx)));
274         masm.storeArgAddr(1, masm.addressOfExtra(vp));
275
276         typedef int32 (JS_FASTCALL *Int32CxVp)(JSContext *, Value *);
277         Int32CxVp stub;
278         if (tarray->type == js::TypedArray::TYPE_UINT8_CLAMPED)
279             stub = stubs::ConvertToTypedInt<true>;
280         else 
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());
285
286         saveForCall.restore();
287         masm.freeStack(vp);
288
289         if (checkInt32.isSet())
290             checkInt32.get().linkTo(masm.label(), &masm);
291     }
292
293     // Performing clamping, if needed.
294     if (tarray->type == js::TypedArray::TYPE_UINT8_CLAMPED) {
295         //     cmp dr, 0
296         //     jge _min
297         //     mov dr, 0
298         //     jump _done
299         // _min:
300         //     cmp dr, 255
301         //     jle _done
302         //     mov dr, 255
303         // _done:
304         //
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);
313     }
314 }
315
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
319 // process.
320 //
321 // Constants are left untouched. Any other value is placed into
322 // FPRegisters::First.
323 static void
324 GenConversionForFloatArray(Assembler &masm, js::TypedArray *tarray, const ValueRemat &vr,
325                            FPRegisterID destReg, uint32 saveMask)
326 {
327     if (vr.isConstant()) {
328         // Constants are always folded to doubles up-front.
329         JS_ASSERT(vr.knownType() == JSVAL_TYPE_DOUBLE);
330         return;
331     }
332
333     // Fast-path, if the value is a double, skip converting.
334     MaybeJump isDouble;
335     if (!vr.isTypeKnown())
336         isDouble = masm.testDouble(Assembler::Equal, vr.typeReg());
337
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()) {
346             skip1 = masm.jump();
347             isNotInt32.get().linkTo(masm.label(), &masm);
348         }
349     }
350
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));
358
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();
367
368         // Load the value from the outparam, then pop the stack.
369         masm.loadDouble(masm.addressOfExtra(vp), destReg);
370         masm.freeStack(vp);
371         skip2 = masm.jump();
372     }
373
374     if (isDouble.isSet())
375         isDouble.get().linkTo(masm.label(), &masm);
376
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);
382
383     // At this point, all loads into xmm1 are complete.
384     if (skip1.isSet())
385         skip1.get().linkTo(masm.label(), &masm);
386     if (skip2.isSet())
387         skip2.get().linkTo(masm.label(), &masm);
388
389     if (tarray->type == js::TypedArray::TYPE_FLOAT32)
390         masm.convertDoubleToFloat(destReg, destReg);
391 }
392
393 template <typename T>
394 static bool
395 StoreToTypedArray(JSContext *cx, Assembler &masm, js::TypedArray *tarray, T address,
396                   const ValueRemat &vrIn, uint32 saveMask)
397 {
398     ValueRemat vr = vrIn;
399
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:
408       {
409         if (!ConstantFoldForIntArray(cx, tarray, &vr))
410             return false;
411
412         PreserveRegisters saveRHS(masm);
413         PreserveRegisters saveLHS(masm);
414
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.
420         //
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
423         // register.
424         //
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
428         // the mutated RHS.
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 &&
435                                   !vr.isConstant() &&
436                                   !(Registers::SingleByteRegs & Registers::maskReg(vr.dataReg()));
437         bool rhsIsMutable = !vr.isConstant() && !(saveMask & Registers::maskReg(vr.dataReg()));
438
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;
446             if (singleByte)
447                 allowMask &= Registers::SingleByteRegs;
448
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());
453
454             Registers avail = allowMask & ~(pinned | saveMask);
455
456             RegisterID newReg;
457             if (!avail.empty()) {
458                 newReg = avail.takeAnyReg();
459             } else {
460                 // If no registers meet the ideal set, relax a constraint and spill.
461                 avail = allowMask & ~pinned;
462
463                 if (!avail.empty()) {
464                     newReg = avail.takeAnyReg();
465                     saveRHS.preserve(Registers::maskReg(newReg));
466                 } else {
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.
470
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);
475
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;
479
480                     // Push them, but get the order right. We'll pop LHS first.
481                     saveRHS.preserve(rhsMask);
482                     saveLHS.preserve(lhsMask);
483
484                     // Don't store/restore registers if we dont have to.
485                     saveMask &= ~lhsMask;
486
487                     // Actually perform the swap.
488                     masm.swap(vr.typeReg(), vr.dataReg());
489                     vr = ValueRemat::FromRegisters(vr.dataReg(), vr.typeReg());
490                     newReg = vr.dataReg();
491                 }
492
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);
496             }
497
498             if (vr.dataReg() != newReg)
499                 masm.move(vr.dataReg(), newReg);
500
501             // Update |vr|.
502             if (vr.isTypeKnown())
503                 vr = ValueRemat::FromKnownType(vr.knownType(), newReg);
504             else
505                 vr = ValueRemat::FromRegisters(vr.typeReg(), newReg);
506         }
507
508         GenConversionForIntArray(masm, tarray, vr, saveMask);
509
510         // Restore the registers in |address|. |GenConversionForIntArray| won't
511         // restore them because we told it not to by fiddling with |saveMask|.
512         saveLHS.restore();
513
514         if (vr.isConstant())
515             StoreToIntArray(masm, tarray, Imm32(vr.value().toInt32()), address);
516         else
517             StoreToIntArray(masm, tarray, vr.dataReg(), address);
518
519         // Note that this will finish restoring the damage from the
520         // earlier register swap.
521         saveRHS.restore();
522         break;
523       }
524
525       case js::TypedArray::TYPE_FLOAT32:
526       case js::TypedArray::TYPE_FLOAT64:
527         if (!ConstantFoldForFloatArray(cx, &vr))
528             return false;
529         GenConversionForFloatArray(masm, tarray, vr, FPRegisters::First, saveMask);
530         if (vr.isConstant())
531             StoreToFloatArray(masm, tarray, ImmDouble(vr.value().toDouble()), address);
532         else
533             StoreToFloatArray(masm, tarray, FPRegisters::First, address);
534         break;
535     }
536
537     return true;
538 }
539
540 #endif // defined(JS_POLYIC) && (defined JS_CPU_X86 || defined JS_CPU_X64)
541
542 } /* namespace mjit */
543 } /* namespace js */
544
545 #endif /* js_typedarray_ic_h___ */
546