LCOV - code coverage report
Current view: directory - js/src/methodjit - TypedArrayIC.h (source / functions) Found Hit Coverage
Test: app.info Lines: 181 164 90.6 %
Date: 2012-06-02 Functions: 6 6 100.0 %

       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                 : #include "jstypedarrayinlines.h"
      46                 : 
      47                 : #include "jsnuminlines.h"
      48                 : #include "jstypedarrayinlines.h"
      49                 : 
      50                 : namespace js {
      51                 : namespace mjit {
      52                 : 
      53                 : #ifdef JS_METHODJIT_TYPED_ARRAY
      54                 : 
      55                 : typedef JSC::MacroAssembler::RegisterID RegisterID;
      56                 : typedef JSC::MacroAssembler::FPRegisterID FPRegisterID;
      57                 : typedef JSC::MacroAssembler::Jump Jump;
      58                 : typedef JSC::MacroAssembler::Imm32 Imm32;
      59                 : typedef JSC::MacroAssembler::ImmDouble ImmDouble;
      60                 : 
      61                 : static inline bool
      62             122 : ConstantFoldForFloatArray(JSContext *cx, ValueRemat *vr)
      63                 : {
      64             122 :     if (!vr->isTypeKnown())
      65              43 :         return true;
      66                 : 
      67                 :     // Objects and undefined coerce to NaN, which coerces to 0.
      68                 :     // Null converts to 0.
      69             147 :     if (vr->knownType() == JSVAL_TYPE_OBJECT ||
      70              68 :         vr->knownType() == JSVAL_TYPE_UNDEFINED) {
      71              11 :         *vr = ValueRemat::FromConstant(DoubleValue(js_NaN));
      72              11 :         return true;
      73                 :     }
      74              68 :     if (vr->knownType() == JSVAL_TYPE_NULL) {
      75               0 :         *vr = ValueRemat::FromConstant(DoubleValue(0));
      76               0 :         return true;
      77                 :     }
      78                 : 
      79              68 :     if (!vr->isConstant())
      80              16 :         return true;
      81                 : 
      82              52 :     if (vr->knownType() == JSVAL_TYPE_DOUBLE)
      83              14 :         return true;
      84                 : 
      85              38 :     double d = 0;
      86              38 :     Value v = vr->value();
      87              38 :     if (v.isString()) {
      88               3 :         if (!StringToNumberType<double>(cx, v.toString(), &d))
      89               0 :             return false;
      90              35 :     } else if (v.isBoolean()) {
      91               8 :         d = v.toBoolean() ? 1 : 0;
      92              27 :     } else if (v.isInt32()) {
      93              27 :         d = v.toInt32();
      94                 :     } else {
      95               0 :         JS_NOT_REACHED("unknown constant type");
      96                 :     }
      97              38 :     *vr = ValueRemat::FromConstant(DoubleValue(d));
      98              38 :     return true;
      99                 : }
     100                 : 
     101                 : static inline bool
     102             518 : ConstantFoldForIntArray(JSContext *cx, JSObject *tarray, ValueRemat *vr)
     103                 : {
     104             518 :     if (!vr->isTypeKnown())
     105             165 :         return true;
     106                 : 
     107                 :     // Objects and undefined coerce to NaN, which coerces to 0.
     108                 :     // Null converts to 0.
     109             997 :     if (vr->knownType() == JSVAL_TYPE_OBJECT ||
     110             322 :         vr->knownType() == JSVAL_TYPE_UNDEFINED ||
     111             322 :         vr->knownType() == JSVAL_TYPE_NULL) {
     112              31 :         *vr = ValueRemat::FromConstant(Int32Value(0));
     113              31 :         return true;
     114                 :     }
     115                 : 
     116             322 :     if (!vr->isConstant())
     117               8 :         return true;
     118                 : 
     119                 :     // Convert from string to double first (see bug 624483).
     120             314 :     Value v = vr->value();
     121             314 :     if (v.isString()) {
     122                 :         double d;
     123              35 :         if (!StringToNumberType<double>(cx, v.toString(), &d))
     124               0 :             return false;
     125              35 :         v.setNumber(d);
     126                 :     }
     127                 : 
     128             314 :     int32_t i32 = 0;
     129             314 :     if (v.isDouble()) {
     130              38 :         i32 = (TypedArray::getType(tarray) == js::TypedArray::TYPE_UINT8_CLAMPED)
     131               8 :               ? js_TypedArray_uint8_clamp_double(v.toDouble())
     132              46 :               : js_DoubleToECMAInt32(v.toDouble());
     133             276 :     } else if (v.isInt32()) {
     134             224 :         i32 = v.toInt32();
     135             224 :         if (TypedArray::getType(tarray) == js::TypedArray::TYPE_UINT8_CLAMPED)
     136              36 :             i32 = ClampIntForUint8Array(i32);
     137              52 :     } else if (v.isBoolean()) {
     138              52 :         i32 = v.toBoolean() ? 1 : 0;
     139                 :     } else {
     140               0 :         JS_NOT_REACHED("unknown constant type");
     141                 :     }
     142                 : 
     143             314 :     *vr = ValueRemat::FromConstant(Int32Value(i32));
     144                 : 
     145             314 :     return true;
     146                 : }
     147                 : 
     148                 : // Generate code that will ensure a dynamically typed value, pinned in |vr|,
     149                 : // can be stored in an integer typed array. If any sort of conversion is
     150                 : // required, |dataReg| will be clobbered by a new value. |saveMask| is
     151                 : // used to ensure that |dataReg| (and volatile registers) are preserved
     152                 : // across any conversion process.
     153                 : static void
     154             518 : GenConversionForIntArray(Assembler &masm, JSObject *tarray, const ValueRemat &vr,
     155                 :                          uint32_t saveMask)
     156                 : {
     157             518 :     if (vr.isConstant()) {
     158                 :         // Constants are always folded to ints up-front.
     159             345 :         JS_ASSERT(vr.knownType() == JSVAL_TYPE_INT32);
     160             345 :         return;
     161                 :     }
     162                 : 
     163             173 :     if (!vr.isTypeKnown() || vr.knownType() != JSVAL_TYPE_INT32) {
     164                 :         // If a conversion is necessary, save registers now.
     165             165 :         MaybeJump checkInt32;
     166             165 :         if (!vr.isTypeKnown())
     167             165 :             checkInt32 = masm.testInt32(Assembler::Equal, vr.typeReg());
     168                 : 
     169                 :         // Store the value to convert.
     170             165 :         StackMarker vp = masm.allocStack(sizeof(Value), sizeof(double));
     171             165 :         masm.storeValue(vr, masm.addressOfExtra(vp));
     172                 : 
     173                 :         // Preserve volatile registers.
     174             330 :         PreserveRegisters saveForCall(masm);
     175             165 :         saveForCall.preserve(saveMask & Registers::TempRegs);
     176                 : 
     177             165 :         masm.setupABICall(Registers::FastCall, 2);
     178             165 :         masm.storeArg(0, masm.vmFrameOffset(offsetof(VMFrame, cx)));
     179             165 :         masm.storeArgAddr(1, masm.addressOfExtra(vp));
     180                 : 
     181                 :         typedef int32_t (JS_FASTCALL *Int32CxVp)(JSContext *, Value *);
     182                 :         Int32CxVp stub;
     183             165 :         if (TypedArray::getType(tarray) == js::TypedArray::TYPE_UINT8_CLAMPED)
     184              23 :             stub = stubs::ConvertToTypedInt<true>;
     185                 :         else 
     186             142 :             stub = stubs::ConvertToTypedInt<false>;
     187             165 :         masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, stub), false);
     188             165 :         if (vr.dataReg() != Registers::ReturnReg)
     189             119 :             masm.move(Registers::ReturnReg, vr.dataReg());
     190                 : 
     191             165 :         saveForCall.restore();
     192             165 :         masm.freeStack(vp);
     193                 : 
     194             165 :         if (checkInt32.isSet())
     195             165 :             checkInt32.get().linkTo(masm.label(), &masm);
     196                 :     }
     197                 : 
     198                 :     // Performing clamping, if needed.
     199             173 :     if (TypedArray::getType(tarray) == js::TypedArray::TYPE_UINT8_CLAMPED)
     200              27 :         masm.clampInt32ToUint8(vr.dataReg());
     201                 : }
     202                 : 
     203                 : // Generate code that will ensure a dynamically typed value, pinned in |vr|,
     204                 : // can be stored in an integer typed array.  saveMask| is used to ensure that
     205                 : // |dataReg| (and volatile registers) are preserved across any conversion
     206                 : // process.
     207                 : //
     208                 : // Constants are left untouched. Any other value is placed into destReg.
     209                 : static void
     210             122 : GenConversionForFloatArray(Assembler &masm, JSObject *tarray, const ValueRemat &vr,
     211                 :                            FPRegisterID destReg, uint32_t saveMask)
     212                 : {
     213             122 :     if (vr.isConstant()) {
     214                 :         // Constants are always folded to doubles up-front.
     215              63 :         JS_ASSERT(vr.knownType() == JSVAL_TYPE_DOUBLE);
     216              63 :         return;
     217                 :     }
     218                 : 
     219                 :     // Fast-path, if the value is a double, skip converting.
     220              59 :     MaybeJump isDouble;
     221              59 :     if (!vr.isTypeKnown())
     222              43 :         isDouble = masm.testDouble(Assembler::Equal, vr.typeReg());
     223                 : 
     224                 :     // If the value is an integer, inline the conversion.
     225              59 :     MaybeJump skip1, skip2;
     226              59 :     if (!vr.isTypeKnown() || vr.knownType() == JSVAL_TYPE_INT32) {
     227              51 :         MaybeJump isNotInt32;
     228              51 :         if (!vr.isTypeKnown())
     229              43 :             isNotInt32 = masm.testInt32(Assembler::NotEqual, vr.typeReg());
     230              51 :         masm.convertInt32ToDouble(vr.dataReg(), destReg);
     231              51 :         if (isNotInt32.isSet()) {
     232              43 :             skip1 = masm.jump();
     233              43 :             isNotInt32.get().linkTo(masm.label(), &masm);
     234                 :         }
     235                 :     }
     236                 : 
     237                 :     // Generate a generic conversion call, if not known to be int32_t or double.
     238              83 :     if (!vr.isTypeKnown() ||
     239              16 :         (vr.knownType() != JSVAL_TYPE_INT32 &&
     240               8 :          vr.knownType() != JSVAL_TYPE_DOUBLE)) {
     241                 :         // Store this value, which is also an outparam.
     242              51 :         StackMarker vp = masm.allocStack(sizeof(Value), sizeof(double));
     243              51 :         masm.storeValue(vr, masm.addressOfExtra(vp));
     244                 : 
     245                 :         // Preserve volatile registers, and make the call.
     246             102 :         PreserveRegisters saveForCall(masm);
     247              51 :         saveForCall.preserve(saveMask & Registers::TempRegs);
     248              51 :         masm.setupABICall(Registers::FastCall, 2);
     249              51 :         masm.storeArg(0, masm.vmFrameOffset(offsetof(VMFrame, cx)));
     250              51 :         masm.storeArgAddr(1, masm.addressOfExtra(vp));
     251              51 :         masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, stubs::ConvertToTypedFloat), false);
     252              51 :         saveForCall.restore();
     253                 : 
     254                 :         // Load the value from the outparam, then pop the stack.
     255              51 :         masm.loadDouble(masm.addressOfExtra(vp), destReg);
     256              51 :         masm.freeStack(vp);
     257              51 :         skip2 = masm.jump();
     258                 :     }
     259                 : 
     260              59 :     if (isDouble.isSet())
     261              43 :         isDouble.get().linkTo(masm.label(), &masm);
     262                 : 
     263                 :     // If it's possible the value was already a double, load it directly
     264                 :     // from registers (the known type is distinct from typeReg, which has
     265                 :     // 32-bits of the 64-bit double).
     266              59 :     if (!vr.isTypeKnown() || vr.knownType() == JSVAL_TYPE_DOUBLE)
     267              43 :         masm.fastLoadDouble(vr.dataReg(), vr.typeReg(), destReg);
     268                 : 
     269                 :     // At this point, all loads into xmm1 are complete.
     270              59 :     if (skip1.isSet())
     271              43 :         skip1.get().linkTo(masm.label(), &masm);
     272              59 :     if (skip2.isSet())
     273              51 :         skip2.get().linkTo(masm.label(), &masm);
     274                 : 
     275              59 :     if (TypedArray::getType(tarray) == js::TypedArray::TYPE_FLOAT32)
     276              40 :         masm.convertDoubleToFloat(destReg, destReg);
     277                 : }
     278                 : 
     279                 : template <typename T>
     280                 : static bool
     281             640 : StoreToTypedArray(JSContext *cx, Assembler &masm, JSObject *tarray, T address,
     282                 :                   const ValueRemat &vrIn, uint32_t saveMask)
     283                 : {
     284             640 :     ValueRemat vr = vrIn;
     285                 : 
     286             640 :     uint32_t type = TypedArray::getType(tarray);
     287             640 :     switch (type) {
     288                 :       case js::TypedArray::TYPE_INT8:
     289                 :       case js::TypedArray::TYPE_UINT8:
     290                 :       case js::TypedArray::TYPE_UINT8_CLAMPED:
     291                 :       case js::TypedArray::TYPE_INT16:
     292                 :       case js::TypedArray::TYPE_UINT16:
     293                 :       case js::TypedArray::TYPE_INT32:
     294                 :       case js::TypedArray::TYPE_UINT32:
     295                 :       {
     296             518 :         if (!ConstantFoldForIntArray(cx, tarray, &vr))
     297               0 :             return false;
     298                 : 
     299            1036 :         PreserveRegisters saveRHS(masm);
     300            1036 :         PreserveRegisters saveLHS(masm);
     301                 : 
     302                 :         // There are three tricky situations to handle:
     303                 :         //   (1) The RHS needs conversion. saveMask will be stomped, and 
     304                 :         //       the RHS may need to be stomped.
     305                 :         //   (2) The RHS may need to be clamped, which clobbers it.
     306                 :         //   (3) The RHS may need to be in a single-byte register.
     307                 :         //
     308                 :         // In all of these cases, we try to find a free register that can be
     309                 :         // used to mutate the RHS. Failing that, we evict an existing volatile
     310                 :         // register.
     311                 :         //
     312                 :         // Note that we are careful to preserve the RHS before saving registers
     313                 :         // for the conversion call. This is because the object and key may be
     314                 :         // in temporary registers, and we want to restore those without killing
     315                 :         // the mutated RHS.
     316                 :         bool singleByte = (type == js::TypedArray::TYPE_INT8 ||
     317                 :                            type == js::TypedArray::TYPE_UINT8 ||
     318             518 :                            type == js::TypedArray::TYPE_UINT8_CLAMPED);
     319             518 :         bool mayNeedConversion = (!vr.isTypeKnown() || vr.knownType() != JSVAL_TYPE_INT32);
     320             518 :         bool mayNeedClamping = !vr.isConstant() && (type == js::TypedArray::TYPE_UINT8_CLAMPED);
     321                 :         bool needsSingleByteReg = singleByte &&
     322                 :                                   !vr.isConstant() &&
     323             518 :                                   !(Registers::SingleByteRegs & Registers::maskReg(vr.dataReg()));
     324             518 :         bool rhsIsMutable = !vr.isConstant() && !(saveMask & Registers::maskReg(vr.dataReg()));
     325                 : 
     326             518 :         if (((mayNeedConversion || mayNeedClamping) && !rhsIsMutable) || needsSingleByteReg) {
     327                 :             // First attempt to find a free temporary register that:
     328                 :             //   - is compatible with the RHS constraints
     329                 :             //   - won't clobber the key, object, or RHS type regs
     330                 :             //   - is temporary, but
     331                 :             //   - is not in saveMask, which contains live volatile registers.
     332              63 :             uint32_t allowMask = Registers::AvailRegs;
     333              63 :             if (singleByte)
     334              29 :                 allowMask &= Registers::SingleByteRegs;
     335                 : 
     336                 :             // Create a mask of registers we absolutely cannot clobber.
     337              63 :             uint32_t pinned = Assembler::maskAddress(address);
     338              63 :             if (!vr.isTypeKnown())
     339              59 :                 pinned |= Registers::maskReg(vr.typeReg());
     340                 : 
     341              63 :             Registers avail = allowMask & ~(pinned | saveMask);
     342                 : 
     343                 :             RegisterID newReg;
     344              63 :             if (!avail.empty()) {
     345               7 :                 newReg = avail.takeAnyReg().reg();
     346                 :             } else {
     347                 :                 // If no registers meet the ideal set, relax a constraint and spill.
     348              56 :                 avail = allowMask & ~pinned;
     349                 : 
     350              56 :                 if (!avail.empty()) {
     351              56 :                     newReg = avail.takeAnyReg().reg();
     352              56 :                     saveRHS.preserve(Registers::maskReg(newReg));
     353                 :                 } else {
     354                 :                     // Oh no! *All* single byte registers are pinned. This
     355                 :                     // sucks. We'll swap the type and data registers in |vr|
     356                 :                     // and unswap them later.
     357                 : 
     358                 :                     // If |vr|'s registers are part of the address, swapping is
     359                 :                     // going to cause problems during the store.
     360               0 :                     uint32_t vrRegs = Registers::mask2Regs(vr.dataReg(), vr.typeReg());
     361               0 :                     uint32_t lhsMask = vrRegs & Assembler::maskAddress(address);
     362                 : 
     363                 :                     // We'll also need to save any of the registers which won't
     364                 :                     // be restored via |lhsMask| above.
     365               0 :                     uint32_t rhsMask = vrRegs & ~lhsMask;
     366                 : 
     367                 :                     // Push them, but get the order right. We'll pop LHS first.
     368               0 :                     saveRHS.preserve(rhsMask);
     369               0 :                     saveLHS.preserve(lhsMask);
     370                 : 
     371                 :                     // Don't store/restore registers if we dont have to.
     372               0 :                     saveMask &= ~lhsMask;
     373                 : 
     374                 :                     // Actually perform the swap.
     375               0 :                     masm.swap(vr.typeReg(), vr.dataReg());
     376               0 :                     vr = ValueRemat::FromRegisters(vr.dataReg(), vr.typeReg());
     377               0 :                     newReg = vr.dataReg();
     378                 :                 }
     379                 : 
     380                 :                 // Now, make sure the new register is not in the saveMask,
     381                 :                 // so it won't get restored right after the call.
     382              56 :                 saveMask &= ~Registers::maskReg(newReg);
     383                 :             }
     384                 : 
     385              63 :             if (vr.dataReg() != newReg)
     386              39 :                 masm.move(vr.dataReg(), newReg);
     387                 : 
     388                 :             // Update |vr|.
     389              63 :             if (vr.isTypeKnown())
     390               4 :                 vr = ValueRemat::FromKnownType(vr.knownType(), newReg);
     391                 :             else
     392              59 :                 vr = ValueRemat::FromRegisters(vr.typeReg(), newReg);
     393                 :         }
     394                 : 
     395             518 :         GenConversionForIntArray(masm, tarray, vr, saveMask);
     396                 : 
     397                 :         // Restore the registers in |address|. |GenConversionForIntArray| won't
     398                 :         // restore them because we told it not to by fiddling with |saveMask|.
     399             518 :         saveLHS.restore();
     400                 : 
     401             518 :         if (vr.isConstant())
     402             345 :             masm.storeToTypedIntArray(type, Imm32(vr.value().toInt32()), address);
     403                 :         else
     404             173 :             masm.storeToTypedIntArray(type, vr.dataReg(), address);
     405                 : 
     406                 :         // Note that this will finish restoring the damage from the
     407                 :         // earlier register swap.
     408             518 :         saveRHS.restore();
     409                 :         break;
     410                 :       }
     411                 : 
     412                 :       case js::TypedArray::TYPE_FLOAT32:
     413                 :       case js::TypedArray::TYPE_FLOAT64: {
     414                 :         /*
     415                 :          * Use a temporary for conversion. Inference is disabled, so no FP
     416                 :          * registers are live.
     417                 :          */
     418             122 :         Registers regs(Registers::TempFPRegs);
     419             122 :         FPRegisterID temp = regs.takeAnyReg().fpreg();
     420                 : 
     421             122 :         if (!ConstantFoldForFloatArray(cx, &vr))
     422               0 :             return false;
     423             122 :         GenConversionForFloatArray(masm, tarray, vr, temp, saveMask);
     424             122 :         if (vr.isConstant())
     425              63 :             masm.storeToTypedFloatArray(type, ImmDouble(vr.value().toDouble()), address);
     426                 :         else
     427              59 :             masm.storeToTypedFloatArray(type, temp, address);
     428             122 :         break;
     429                 :       }
     430                 :     }
     431                 : 
     432             640 :     return true;
     433                 : }
     434                 : 
     435                 : #endif /* JS_METHODJIT_TYPED_ARRAY */
     436                 : 
     437                 : } /* namespace mjit */
     438                 : } /* namespace js */
     439                 : 
     440                 : #endif /* js_typedarray_ic_h___ */
     441                 : 

Generated by: LCOV version 1.7