LCOV - app.info - js/src/methodjit/TypedArrayIC.h

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-04-07 | 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