js/src/jit/AsmJSModule.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

     1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
     2  * vim: set ts=8 sts=4 et sw=4 tw=99:
     3  * This Source Code Form is subject to the terms of the Mozilla Public
     4  * License, v. 2.0. If a copy of the MPL was not distributed with this
     5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     7 #include "jit/AsmJSModule.h"
     9 #ifndef XP_WIN
    10 # include <sys/mman.h>
    11 #endif
    13 #include "mozilla/Compression.h"
    14 #include "mozilla/PodOperations.h"
    16 #include "jslibmath.h"
    17 #include "jsmath.h"
    18 #include "jsprf.h"
    19 #ifdef XP_WIN
    20 # include "jswin.h"
    21 #endif
    22 #include "prmjtime.h"
    24 #include "frontend/Parser.h"
    25 #include "jit/IonCode.h"
    26 #include "js/MemoryMetrics.h"
    28 #include "jsobjinlines.h"
    30 #include "frontend/ParseNode-inl.h"
    32 using namespace js;
    33 using namespace jit;
    34 using namespace frontend;
    35 using mozilla::PodCopy;
    36 using mozilla::PodEqual;
    37 using mozilla::Compression::LZ4;
    39 void
    40 AsmJSModule::initHeap(Handle<ArrayBufferObject*> heap, JSContext *cx)
    41 {
    42     JS_ASSERT(IsValidAsmJSHeapLength(heap->byteLength()));
    43     JS_ASSERT(dynamicallyLinked_);
    44     JS_ASSERT(!maybeHeap_);
    46     maybeHeap_ = heap;
    47     heapDatum() = heap->dataPointer();
    49 #if defined(JS_CODEGEN_X86)
    50     uint8_t *heapOffset = heap->dataPointer();
    51     void *heapLength = (void*)heap->byteLength();
    52     for (unsigned i = 0; i < heapAccesses_.length(); i++) {
    53         const jit::AsmJSHeapAccess &access = heapAccesses_[i];
    54         if (access.hasLengthCheck())
    55             JSC::X86Assembler::setPointer(access.patchLengthAt(code_), heapLength);
    56         void *addr = access.patchOffsetAt(code_);
    57         uint32_t disp = reinterpret_cast<uint32_t>(JSC::X86Assembler::getPointer(addr));
    58         JS_ASSERT(disp <= INT32_MAX);
    59         JSC::X86Assembler::setPointer(addr, (void *)(heapOffset + disp));
    60     }
    61 #elif defined(JS_CODEGEN_ARM)
    62     uint32_t heapLength = heap->byteLength();
    63     for (unsigned i = 0; i < heapAccesses_.length(); i++) {
    64         jit::Assembler::updateBoundsCheck(heapLength,
    65                                           (jit::Instruction*)(heapAccesses_[i].offset() + code_));
    66     }
    67 #endif
    68 }
    70 static uint8_t *
    71 AllocateExecutableMemory(ExclusiveContext *cx, size_t totalBytes)
    72 {
    73     JS_ASSERT(totalBytes % AsmJSPageSize == 0);
    75 #ifdef XP_WIN
    76     void *p = VirtualAlloc(nullptr, totalBytes, MEM_COMMIT, PAGE_EXECUTE_READWRITE);
    77     if (!p) {
    78         js_ReportOutOfMemory(cx);
    79         return nullptr;
    80     }
    81 #else  // assume Unix
    82     void *p = mmap(nullptr, totalBytes,
    83                    PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANON,
    84                    -1, 0);
    85     if (p == MAP_FAILED) {
    86         js_ReportOutOfMemory(cx);
    87         return nullptr;
    88     }
    89 #endif
    91     return (uint8_t *)p;
    92 }
    94 static void
    95 DeallocateExecutableMemory(uint8_t *code, size_t totalBytes)
    96 {
    97 #ifdef XP_WIN
    98     JS_ALWAYS_TRUE(VirtualFree(code, 0, MEM_RELEASE));
    99 #else
   100     JS_ALWAYS_TRUE(munmap(code, totalBytes) == 0);
   101 #endif
   102 }
   104 bool
   105 AsmJSModule::allocateAndCopyCode(ExclusiveContext *cx, MacroAssembler &masm)
   106 {
   107     JS_ASSERT(!code_);
   109     // The global data section sits immediately after the executable (and
   110     // other) data allocated by the MacroAssembler, so ensure it is
   111     // double-aligned.
   112     pod.codeBytes_ = AlignBytes(masm.bytesNeeded(), sizeof(double));
   114     // The entire region is allocated via mmap/VirtualAlloc which requires
   115     // units of pages.
   116     pod.totalBytes_ = AlignBytes(pod.codeBytes_ + globalDataBytes(), AsmJSPageSize);
   118     code_ = AllocateExecutableMemory(cx, pod.totalBytes_);
   119     if (!code_)
   120         return false;
   122     JS_ASSERT(uintptr_t(code_) % AsmJSPageSize == 0);
   123     masm.executableCopy(code_);
   124     return true;
   125 }
   127 static int32_t
   128 CoerceInPlace_ToInt32(JSContext *cx, MutableHandleValue val)
   129 {
   130     int32_t i32;
   131     if (!ToInt32(cx, val, &i32))
   132         return false;
   133     val.set(Int32Value(i32));
   135     return true;
   136 }
   138 static int32_t
   139 CoerceInPlace_ToNumber(JSContext *cx, MutableHandleValue val)
   140 {
   141     double dbl;
   142     if (!ToNumber(cx, val, &dbl))
   143         return false;
   144     val.set(DoubleValue(dbl));
   146     return true;
   147 }
   149 namespace js {
   151 // Defined in AsmJS.cpp:
   153 int32_t
   154 InvokeFromAsmJS_Ignore(JSContext *cx, int32_t exitIndex, int32_t argc, Value *argv);
   156 int32_t
   157 InvokeFromAsmJS_ToInt32(JSContext *cx, int32_t exitIndex, int32_t argc, Value *argv);
   159 int32_t
   160 InvokeFromAsmJS_ToNumber(JSContext *cx, int32_t exitIndex, int32_t argc, Value *argv);
   162 }
   164 #if defined(JS_CODEGEN_ARM)
   165 extern "C" {
   167 extern int64_t
   168 __aeabi_idivmod(int, int);
   170 extern int64_t
   171 __aeabi_uidivmod(int, int);
   173 }
   174 #endif
   176 template <class F>
   177 static inline void *
   178 FuncCast(F *pf)
   179 {
   180     return JS_FUNC_TO_DATA_PTR(void *, pf);
   181 }
   183 static void *
   184 RedirectCall(void *fun, ABIFunctionType type)
   185 {
   186 #ifdef JS_ARM_SIMULATOR
   187     fun = Simulator::RedirectNativeFunction(fun, type);
   188 #endif
   189     return fun;
   190 }
   192 #ifdef DEBUG
   193 static void
   194 AssumeUnreachable()
   195 {
   196     MOZ_CRASH("Reached unreachable code in asm.js");
   197 }
   198 #endif
   200 static void *
   201 AddressOf(AsmJSImmKind kind, ExclusiveContext *cx)
   202 {
   203     switch (kind) {
   204       case AsmJSImm_Runtime:
   205         return cx->runtimeAddressForJit();
   206       case AsmJSImm_StackLimit:
   207         return cx->stackLimitAddressForJitCode(StackForUntrustedScript);
   208       case AsmJSImm_ReportOverRecursed:
   209         return RedirectCall(FuncCast<void (JSContext*)>(js_ReportOverRecursed), Args_General1);
   210       case AsmJSImm_HandleExecutionInterrupt:
   211         return RedirectCall(FuncCast(js::HandleExecutionInterrupt), Args_General1);
   212       case AsmJSImm_InvokeFromAsmJS_Ignore:
   213         return RedirectCall(FuncCast(InvokeFromAsmJS_Ignore), Args_General4);
   214       case AsmJSImm_InvokeFromAsmJS_ToInt32:
   215         return RedirectCall(FuncCast(InvokeFromAsmJS_ToInt32), Args_General4);
   216       case AsmJSImm_InvokeFromAsmJS_ToNumber:
   217         return RedirectCall(FuncCast(InvokeFromAsmJS_ToNumber), Args_General4);
   218       case AsmJSImm_CoerceInPlace_ToInt32:
   219         return RedirectCall(FuncCast(CoerceInPlace_ToInt32), Args_General2);
   220       case AsmJSImm_CoerceInPlace_ToNumber:
   221         return RedirectCall(FuncCast(CoerceInPlace_ToNumber), Args_General2);
   222       case AsmJSImm_ToInt32:
   223         return RedirectCall(FuncCast<int32_t (double)>(js::ToInt32), Args_Int_Double);
   224 #if defined(JS_CODEGEN_ARM)
   225       case AsmJSImm_aeabi_idivmod:
   226         return RedirectCall(FuncCast(__aeabi_idivmod), Args_General2);
   227       case AsmJSImm_aeabi_uidivmod:
   228         return RedirectCall(FuncCast(__aeabi_uidivmod), Args_General2);
   229 #endif
   230       case AsmJSImm_ModD:
   231         return RedirectCall(FuncCast(NumberMod), Args_Double_DoubleDouble);
   232       case AsmJSImm_SinD:
   233         return RedirectCall(FuncCast<double (double)>(sin), Args_Double_Double);
   234       case AsmJSImm_CosD:
   235         return RedirectCall(FuncCast<double (double)>(cos), Args_Double_Double);
   236       case AsmJSImm_TanD:
   237         return RedirectCall(FuncCast<double (double)>(tan), Args_Double_Double);
   238       case AsmJSImm_ASinD:
   239         return RedirectCall(FuncCast<double (double)>(asin), Args_Double_Double);
   240       case AsmJSImm_ACosD:
   241         return RedirectCall(FuncCast<double (double)>(acos), Args_Double_Double);
   242       case AsmJSImm_ATanD:
   243         return RedirectCall(FuncCast<double (double)>(atan), Args_Double_Double);
   244       case AsmJSImm_CeilD:
   245         return RedirectCall(FuncCast<double (double)>(ceil), Args_Double_Double);
   246       case AsmJSImm_CeilF:
   247         return RedirectCall(FuncCast<float (float)>(ceilf), Args_Float32_Float32);
   248       case AsmJSImm_FloorD:
   249         return RedirectCall(FuncCast<double (double)>(floor), Args_Double_Double);
   250       case AsmJSImm_FloorF:
   251         return RedirectCall(FuncCast<float (float)>(floorf), Args_Float32_Float32);
   252       case AsmJSImm_ExpD:
   253         return RedirectCall(FuncCast<double (double)>(exp), Args_Double_Double);
   254       case AsmJSImm_LogD:
   255         return RedirectCall(FuncCast<double (double)>(log), Args_Double_Double);
   256       case AsmJSImm_PowD:
   257         return RedirectCall(FuncCast(ecmaPow), Args_Double_DoubleDouble);
   258       case AsmJSImm_ATan2D:
   259         return RedirectCall(FuncCast(ecmaAtan2), Args_Double_DoubleDouble);
   260 #ifdef DEBUG
   261       case AsmJSImm_AssumeUnreachable:
   262         return RedirectCall(FuncCast(AssumeUnreachable), Args_General0);
   263 #endif
   264       case AsmJSImm_Invalid:
   265         break;
   266     }
   268     MOZ_ASSUME_UNREACHABLE("Bad AsmJSImmKind");
   269     return nullptr;
   270 }
   272 void
   273 AsmJSModule::restoreToInitialState(ArrayBufferObject *maybePrevBuffer, ExclusiveContext *cx)
   274 {
   275 #ifdef DEBUG
   276     // Put the absolute links back to -1 so patchDataWithValueCheck assertions
   277     // in staticallyLink are valid.
   278     for (size_t i = 0; i < staticLinkData_.absoluteLinks.length(); i++) {
   279         AbsoluteLink link = staticLinkData_.absoluteLinks[i];
   280         Assembler::patchDataWithValueCheck(code_ + link.patchAt.offset(),
   281                                            PatchedImmPtr((void*)-1),
   282                                            PatchedImmPtr(AddressOf(link.target, cx)));
   283     }
   284 #endif
   286     if (maybePrevBuffer) {
   287 #if defined(JS_CODEGEN_X86)
   288         // Subtract out the base-pointer added by AsmJSModule::initHeap.
   289         uint8_t *ptrBase = maybePrevBuffer->dataPointer();
   290         for (unsigned i = 0; i < heapAccesses_.length(); i++) {
   291             const jit::AsmJSHeapAccess &access = heapAccesses_[i];
   292             void *addr = access.patchOffsetAt(code_);
   293             uint8_t *ptr = reinterpret_cast<uint8_t*>(JSC::X86Assembler::getPointer(addr));
   294             JS_ASSERT(ptr >= ptrBase);
   295             JSC::X86Assembler::setPointer(addr, (void *)(ptr - ptrBase));
   296         }
   297 #endif
   298     }
   299 }
   301 void
   302 AsmJSModule::setAutoFlushICacheRange()
   303 {
   304     AutoFlushICache::setRange(uintptr_t(code_), pod.codeBytes_);
   305 }
   307 void
   308 AsmJSModule::staticallyLink(ExclusiveContext *cx)
   309 {
   310     // Process staticLinkData_
   312     interruptExit_ = code_ + staticLinkData_.interruptExitOffset;
   314     for (size_t i = 0; i < staticLinkData_.relativeLinks.length(); i++) {
   315         RelativeLink link = staticLinkData_.relativeLinks[i];
   316         *(void **)(code_ + link.patchAtOffset) = code_ + link.targetOffset;
   317     }
   319     for (size_t i = 0; i < staticLinkData_.absoluteLinks.length(); i++) {
   320         AbsoluteLink link = staticLinkData_.absoluteLinks[i];
   321         Assembler::patchDataWithValueCheck(code_ + link.patchAt.offset(),
   322                                            PatchedImmPtr(AddressOf(link.target, cx)),
   323                                            PatchedImmPtr((void*)-1));
   324     }
   326     // Initialize global data segment
   328     for (size_t i = 0; i < exits_.length(); i++) {
   329         exitIndexToGlobalDatum(i).exit = interpExitTrampoline(exits_[i]);
   330         exitIndexToGlobalDatum(i).fun = nullptr;
   331     }
   332 }
   334 AsmJSModule::AsmJSModule(ScriptSource *scriptSource, uint32_t funcStart,
   335                          uint32_t offsetToEndOfUseAsm, bool strict)
   336   : globalArgumentName_(nullptr),
   337     importArgumentName_(nullptr),
   338     bufferArgumentName_(nullptr),
   339     code_(nullptr),
   340     interruptExit_(nullptr),
   341     dynamicallyLinked_(false),
   342     loadedFromCache_(false),
   343     funcStart_(funcStart),
   344     offsetToEndOfUseAsm_(offsetToEndOfUseAsm),
   345     scriptSource_(scriptSource),
   346     codeIsProtected_(false)
   347 {
   348     mozilla::PodZero(&pod);
   349     scriptSource_->incref();
   350     pod.minHeapLength_ = AsmJSAllocationGranularity;
   351     pod.strict_ = strict;
   352 }
   354 AsmJSModule::~AsmJSModule()
   355 {
   356     scriptSource_->decref();
   358     if (code_) {
   359         for (unsigned i = 0; i < numExits(); i++) {
   360             AsmJSModule::ExitDatum &exitDatum = exitIndexToGlobalDatum(i);
   361             if (!exitDatum.fun)
   362                 continue;
   364             if (!exitDatum.fun->hasScript())
   365                 continue;
   367             JSScript *script = exitDatum.fun->nonLazyScript();
   368             if (!script->hasIonScript())
   369                 continue;
   371             jit::DependentAsmJSModuleExit exit(this, i);
   372             script->ionScript()->removeDependentAsmJSModule(exit);
   373         }
   375         DeallocateExecutableMemory(code_, pod.totalBytes_);
   376     }
   377 }
   379 void
   380 AsmJSModule::addSizeOfMisc(mozilla::MallocSizeOf mallocSizeOf, size_t *asmJSModuleCode,
   381                            size_t *asmJSModuleData)
   382 {
   383     *asmJSModuleCode += pod.totalBytes_;
   384     *asmJSModuleData += mallocSizeOf(this) +
   385                         globals_.sizeOfExcludingThis(mallocSizeOf) +
   386                         exits_.sizeOfExcludingThis(mallocSizeOf) +
   387                         exports_.sizeOfExcludingThis(mallocSizeOf) +
   388                         callSites_.sizeOfExcludingThis(mallocSizeOf) +
   389                         functionNames_.sizeOfExcludingThis(mallocSizeOf) +
   390                         heapAccesses_.sizeOfExcludingThis(mallocSizeOf) +
   391 #if defined(MOZ_VTUNE) || defined(JS_ION_PERF)
   392                         profiledFunctions_.sizeOfExcludingThis(mallocSizeOf) +
   393 #endif
   394 #if defined(JS_ION_PERF)
   395                         perfProfiledBlocksFunctions_.sizeOfExcludingThis(mallocSizeOf) +
   396 #endif
   397                         staticLinkData_.sizeOfExcludingThis(mallocSizeOf);
   398 }
   400 static void
   401 AsmJSModuleObject_finalize(FreeOp *fop, JSObject *obj)
   402 {
   403     fop->delete_(&obj->as<AsmJSModuleObject>().module());
   404 }
   406 static void
   407 AsmJSModuleObject_trace(JSTracer *trc, JSObject *obj)
   408 {
   409     obj->as<AsmJSModuleObject>().module().trace(trc);
   410 }
   412 const Class AsmJSModuleObject::class_ = {
   413     "AsmJSModuleObject",
   414     JSCLASS_IS_ANONYMOUS | JSCLASS_IMPLEMENTS_BARRIERS |
   415     JSCLASS_HAS_RESERVED_SLOTS(AsmJSModuleObject::RESERVED_SLOTS),
   416     JS_PropertyStub,         /* addProperty */
   417     JS_DeletePropertyStub,   /* delProperty */
   418     JS_PropertyStub,         /* getProperty */
   419     JS_StrictPropertyStub,   /* setProperty */
   420     JS_EnumerateStub,
   421     JS_ResolveStub,
   422     nullptr,                 /* convert     */
   423     AsmJSModuleObject_finalize,
   424     nullptr,                 /* call        */
   425     nullptr,                 /* hasInstance */
   426     nullptr,                 /* construct   */
   427     AsmJSModuleObject_trace
   428 };
   430 AsmJSModuleObject *
   431 AsmJSModuleObject::create(ExclusiveContext *cx, ScopedJSDeletePtr<AsmJSModule> *module)
   432 {
   433     JSObject *obj = NewObjectWithGivenProto(cx, &AsmJSModuleObject::class_, nullptr, nullptr);
   434     if (!obj)
   435         return nullptr;
   437     obj->setReservedSlot(MODULE_SLOT, PrivateValue(module->forget()));
   438     return &obj->as<AsmJSModuleObject>();
   439 }
   441 AsmJSModule &
   442 AsmJSModuleObject::module() const
   443 {
   444     JS_ASSERT(is<AsmJSModuleObject>());
   445     return *(AsmJSModule *)getReservedSlot(MODULE_SLOT).toPrivate();
   446 }
   448 static inline uint8_t *
   449 WriteBytes(uint8_t *dst, const void *src, size_t nbytes)
   450 {
   451     memcpy(dst, src, nbytes);
   452     return dst + nbytes;
   453 }
   455 static inline const uint8_t *
   456 ReadBytes(const uint8_t *src, void *dst, size_t nbytes)
   457 {
   458     memcpy(dst, src, nbytes);
   459     return src + nbytes;
   460 }
   462 template <class T>
   463 static inline uint8_t *
   464 WriteScalar(uint8_t *dst, T t)
   465 {
   466     memcpy(dst, &t, sizeof(t));
   467     return dst + sizeof(t);
   468 }
   470 template <class T>
   471 static inline const uint8_t *
   472 ReadScalar(const uint8_t *src, T *dst)
   473 {
   474     memcpy(dst, src, sizeof(*dst));
   475     return src + sizeof(*dst);
   476 }
   478 static size_t
   479 SerializedNameSize(PropertyName *name)
   480 {
   481     return sizeof(uint32_t) +
   482            (name ? name->length() * sizeof(jschar) : 0);
   483 }
   485 size_t
   486 AsmJSModule::Name::serializedSize() const
   487 {
   488     return SerializedNameSize(name_);
   489 }
   491 static uint8_t *
   492 SerializeName(uint8_t *cursor, PropertyName *name)
   493 {
   494     JS_ASSERT_IF(name, !name->empty());
   495     if (name) {
   496         cursor = WriteScalar<uint32_t>(cursor, name->length());
   497         cursor = WriteBytes(cursor, name->chars(), name->length() * sizeof(jschar));
   498     } else {
   499         cursor = WriteScalar<uint32_t>(cursor, 0);
   500     }
   501     return cursor;
   502 }
   504 uint8_t *
   505 AsmJSModule::Name::serialize(uint8_t *cursor) const
   506 {
   507     return SerializeName(cursor, name_);
   508 }
   510 static const uint8_t *
   511 DeserializeName(ExclusiveContext *cx, const uint8_t *cursor, PropertyName **name)
   512 {
   513     uint32_t length;
   514     cursor = ReadScalar<uint32_t>(cursor, &length);
   516     if (length == 0) {
   517         *name = nullptr;
   518         return cursor;
   519     }
   521     js::Vector<jschar> tmp(cx);
   522     jschar *src;
   523     if ((size_t(cursor) & (sizeof(jschar) - 1)) != 0) {
   524         // Align 'src' for AtomizeChars.
   525         if (!tmp.resize(length))
   526             return nullptr;
   527         memcpy(tmp.begin(), cursor, length * sizeof(jschar));
   528         src = tmp.begin();
   529     } else {
   530         src = (jschar *)cursor;
   531     }
   533     JSAtom *atom = AtomizeChars(cx, src, length);
   534     if (!atom)
   535         return nullptr;
   537     *name = atom->asPropertyName();
   538     return cursor + length * sizeof(jschar);
   539 }
   541 const uint8_t *
   542 AsmJSModule::Name::deserialize(ExclusiveContext *cx, const uint8_t *cursor)
   543 {
   544     return DeserializeName(cx, cursor, &name_);
   545 }
   547 bool
   548 AsmJSModule::Name::clone(ExclusiveContext *cx, Name *out) const
   549 {
   550     out->name_ = name_;
   551     return true;
   552 }
   554 template <class T>
   555 size_t
   556 SerializedVectorSize(const js::Vector<T, 0, SystemAllocPolicy> &vec)
   557 {
   558     size_t size = sizeof(uint32_t);
   559     for (size_t i = 0; i < vec.length(); i++)
   560         size += vec[i].serializedSize();
   561     return size;
   562 }
   564 template <class T>
   565 uint8_t *
   566 SerializeVector(uint8_t *cursor, const js::Vector<T, 0, SystemAllocPolicy> &vec)
   567 {
   568     cursor = WriteScalar<uint32_t>(cursor, vec.length());
   569     for (size_t i = 0; i < vec.length(); i++)
   570         cursor = vec[i].serialize(cursor);
   571     return cursor;
   572 }
   574 template <class T>
   575 const uint8_t *
   576 DeserializeVector(ExclusiveContext *cx, const uint8_t *cursor, js::Vector<T, 0, SystemAllocPolicy> *vec)
   577 {
   578     uint32_t length;
   579     cursor = ReadScalar<uint32_t>(cursor, &length);
   580     if (!vec->resize(length))
   581         return nullptr;
   582     for (size_t i = 0; i < vec->length(); i++) {
   583         if (!(cursor = (*vec)[i].deserialize(cx, cursor)))
   584             return nullptr;
   585     }
   586     return cursor;
   587 }
   589 template <class T>
   590 bool
   591 CloneVector(ExclusiveContext *cx, const Vector<T, 0, SystemAllocPolicy> &in,
   592             Vector<T, 0, SystemAllocPolicy> *out)
   593 {
   594     if (!out->resize(in.length()))
   595         return false;
   596     for (size_t i = 0; i < in.length(); i++) {
   597         if (!in[i].clone(cx, &(*out)[i]))
   598             return false;
   599     }
   600     return true;
   601 }
   603 template <class T, class AllocPolicy, class ThisVector>
   604 size_t
   605 SerializedPodVectorSize(const mozilla::VectorBase<T, 0, AllocPolicy, ThisVector> &vec)
   606 {
   607     return sizeof(uint32_t) +
   608            vec.length() * sizeof(T);
   609 }
   611 template <class T, class AllocPolicy, class ThisVector>
   612 uint8_t *
   613 SerializePodVector(uint8_t *cursor, const mozilla::VectorBase<T, 0, AllocPolicy, ThisVector> &vec)
   614 {
   615     cursor = WriteScalar<uint32_t>(cursor, vec.length());
   616     cursor = WriteBytes(cursor, vec.begin(), vec.length() * sizeof(T));
   617     return cursor;
   618 }
   620 template <class T, class AllocPolicy, class ThisVector>
   621 const uint8_t *
   622 DeserializePodVector(ExclusiveContext *cx, const uint8_t *cursor,
   623                      mozilla::VectorBase<T, 0, AllocPolicy, ThisVector> *vec)
   624 {
   625     uint32_t length;
   626     cursor = ReadScalar<uint32_t>(cursor, &length);
   627     if (!vec->resize(length))
   628         return nullptr;
   629     cursor = ReadBytes(cursor, vec->begin(), length * sizeof(T));
   630     return cursor;
   631 }
   633 template <class T>
   634 bool
   635 ClonePodVector(ExclusiveContext *cx, const Vector<T, 0, SystemAllocPolicy> &in,
   636                Vector<T, 0, SystemAllocPolicy> *out)
   637 {
   638     if (!out->resize(in.length()))
   639         return false;
   640     PodCopy(out->begin(), in.begin(), in.length());
   641     return true;
   642 }
   644 uint8_t *
   645 AsmJSModule::Global::serialize(uint8_t *cursor) const
   646 {
   647     cursor = WriteBytes(cursor, &pod, sizeof(pod));
   648     cursor = SerializeName(cursor, name_);
   649     return cursor;
   650 }
   652 size_t
   653 AsmJSModule::Global::serializedSize() const
   654 {
   655     return sizeof(pod) +
   656            SerializedNameSize(name_);
   657 }
   659 const uint8_t *
   660 AsmJSModule::Global::deserialize(ExclusiveContext *cx, const uint8_t *cursor)
   661 {
   662     (cursor = ReadBytes(cursor, &pod, sizeof(pod))) &&
   663     (cursor = DeserializeName(cx, cursor, &name_));
   664     return cursor;
   665 }
   667 bool
   668 AsmJSModule::Global::clone(ExclusiveContext *cx, Global *out) const
   669 {
   670     *out = *this;
   671     return true;
   672 }
   674 uint8_t *
   675 AsmJSModule::Exit::serialize(uint8_t *cursor) const
   676 {
   677     cursor = WriteBytes(cursor, this, sizeof(*this));
   678     return cursor;
   679 }
   681 size_t
   682 AsmJSModule::Exit::serializedSize() const
   683 {
   684     return sizeof(*this);
   685 }
   687 const uint8_t *
   688 AsmJSModule::Exit::deserialize(ExclusiveContext *cx, const uint8_t *cursor)
   689 {
   690     cursor = ReadBytes(cursor, this, sizeof(*this));
   691     return cursor;
   692 }
   694 bool
   695 AsmJSModule::Exit::clone(ExclusiveContext *cx, Exit *out) const
   696 {
   697     *out = *this;
   698     return true;
   699 }
   701 uint8_t *
   702 AsmJSModule::ExportedFunction::serialize(uint8_t *cursor) const
   703 {
   704     cursor = SerializeName(cursor, name_);
   705     cursor = SerializeName(cursor, maybeFieldName_);
   706     cursor = SerializePodVector(cursor, argCoercions_);
   707     cursor = WriteBytes(cursor, &pod, sizeof(pod));
   708     return cursor;
   709 }
   711 size_t
   712 AsmJSModule::ExportedFunction::serializedSize() const
   713 {
   714     return SerializedNameSize(name_) +
   715            SerializedNameSize(maybeFieldName_) +
   716            sizeof(uint32_t) +
   717            argCoercions_.length() * sizeof(argCoercions_[0]) +
   718            sizeof(pod);
   719 }
   721 const uint8_t *
   722 AsmJSModule::ExportedFunction::deserialize(ExclusiveContext *cx, const uint8_t *cursor)
   723 {
   724     (cursor = DeserializeName(cx, cursor, &name_)) &&
   725     (cursor = DeserializeName(cx, cursor, &maybeFieldName_)) &&
   726     (cursor = DeserializePodVector(cx, cursor, &argCoercions_)) &&
   727     (cursor = ReadBytes(cursor, &pod, sizeof(pod)));
   728     return cursor;
   729 }
   731 bool
   732 AsmJSModule::ExportedFunction::clone(ExclusiveContext *cx, ExportedFunction *out) const
   733 {
   734     out->name_ = name_;
   735     out->maybeFieldName_ = maybeFieldName_;
   737     if (!ClonePodVector(cx, argCoercions_, &out->argCoercions_))
   738         return false;
   740     out->pod = pod;
   741     return true;
   742 }
   744 size_t
   745 AsmJSModule::StaticLinkData::serializedSize() const
   746 {
   747     return sizeof(uint32_t) +
   748            SerializedPodVectorSize(relativeLinks) +
   749            SerializedPodVectorSize(absoluteLinks);
   750 }
   752 uint8_t *
   753 AsmJSModule::StaticLinkData::serialize(uint8_t *cursor) const
   754 {
   755     cursor = WriteScalar<uint32_t>(cursor, interruptExitOffset);
   756     cursor = SerializePodVector(cursor, relativeLinks);
   757     cursor = SerializePodVector(cursor, absoluteLinks);
   758     return cursor;
   759 }
   761 const uint8_t *
   762 AsmJSModule::StaticLinkData::deserialize(ExclusiveContext *cx, const uint8_t *cursor)
   763 {
   764     (cursor = ReadScalar<uint32_t>(cursor, &interruptExitOffset)) &&
   765     (cursor = DeserializePodVector(cx, cursor, &relativeLinks)) &&
   766     (cursor = DeserializePodVector(cx, cursor, &absoluteLinks));
   767     return cursor;
   768 }
   770 #if defined(MOZ_VTUNE) || defined(JS_ION_PERF)
   771 size_t
   772 AsmJSModule::ProfiledFunction::serializedSize() const
   773 {
   774     return SerializedNameSize(name) +
   775            sizeof(pod);
   776 }
   778 uint8_t *
   779 AsmJSModule::ProfiledFunction::serialize(uint8_t *cursor) const
   780 {
   781     cursor = SerializeName(cursor, name);
   782     cursor = WriteBytes(cursor, &pod, sizeof(pod));
   783     return cursor;
   784 }
   786 const uint8_t *
   787 AsmJSModule::ProfiledFunction::deserialize(ExclusiveContext *cx, const uint8_t *cursor)
   788 {
   789     (cursor = DeserializeName(cx, cursor, &name)) &&
   790     (cursor = ReadBytes(cursor, &pod, sizeof(pod)));
   791     return cursor;
   792 }
   793 #endif
   795 bool
   796 AsmJSModule::StaticLinkData::clone(ExclusiveContext *cx, StaticLinkData *out) const
   797 {
   798     out->interruptExitOffset = interruptExitOffset;
   799     return ClonePodVector(cx, relativeLinks, &out->relativeLinks) &&
   800            ClonePodVector(cx, absoluteLinks, &out->absoluteLinks);
   801 }
   803 size_t
   804 AsmJSModule::StaticLinkData::sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const
   805 {
   806     return relativeLinks.sizeOfExcludingThis(mallocSizeOf) +
   807            absoluteLinks.sizeOfExcludingThis(mallocSizeOf);
   808 }
   810 size_t
   811 AsmJSModule::serializedSize() const
   812 {
   813     return sizeof(pod) +
   814            pod.codeBytes_ +
   815            SerializedNameSize(globalArgumentName_) +
   816            SerializedNameSize(importArgumentName_) +
   817            SerializedNameSize(bufferArgumentName_) +
   818            SerializedVectorSize(globals_) +
   819            SerializedVectorSize(exits_) +
   820            SerializedVectorSize(exports_) +
   821            SerializedPodVectorSize(callSites_) +
   822            SerializedVectorSize(functionNames_) +
   823            SerializedPodVectorSize(heapAccesses_) +
   824 #if defined(MOZ_VTUNE) || defined(JS_ION_PERF)
   825            SerializedVectorSize(profiledFunctions_) +
   826 #endif
   827            staticLinkData_.serializedSize();
   828 }
   830 uint8_t *
   831 AsmJSModule::serialize(uint8_t *cursor) const
   832 {
   833     cursor = WriteBytes(cursor, &pod, sizeof(pod));
   834     cursor = WriteBytes(cursor, code_, pod.codeBytes_);
   835     cursor = SerializeName(cursor, globalArgumentName_);
   836     cursor = SerializeName(cursor, importArgumentName_);
   837     cursor = SerializeName(cursor, bufferArgumentName_);
   838     cursor = SerializeVector(cursor, globals_);
   839     cursor = SerializeVector(cursor, exits_);
   840     cursor = SerializeVector(cursor, exports_);
   841     cursor = SerializePodVector(cursor, callSites_);
   842     cursor = SerializeVector(cursor, functionNames_);
   843     cursor = SerializePodVector(cursor, heapAccesses_);
   844 #if defined(MOZ_VTUNE) || defined(JS_ION_PERF)
   845     cursor = SerializeVector(cursor, profiledFunctions_);
   846 #endif
   847     cursor = staticLinkData_.serialize(cursor);
   848     return cursor;
   849 }
   851 const uint8_t *
   852 AsmJSModule::deserialize(ExclusiveContext *cx, const uint8_t *cursor)
   853 {
   854     // To avoid GC-during-deserialization corner cases, prevent atoms from
   855     // being collected.
   856     AutoKeepAtoms aka(cx->perThreadData);
   858     (cursor = ReadBytes(cursor, &pod, sizeof(pod))) &&
   859     (code_ = AllocateExecutableMemory(cx, pod.totalBytes_)) &&
   860     (cursor = ReadBytes(cursor, code_, pod.codeBytes_)) &&
   861     (cursor = DeserializeName(cx, cursor, &globalArgumentName_)) &&
   862     (cursor = DeserializeName(cx, cursor, &importArgumentName_)) &&
   863     (cursor = DeserializeName(cx, cursor, &bufferArgumentName_)) &&
   864     (cursor = DeserializeVector(cx, cursor, &globals_)) &&
   865     (cursor = DeserializeVector(cx, cursor, &exits_)) &&
   866     (cursor = DeserializeVector(cx, cursor, &exports_)) &&
   867     (cursor = DeserializePodVector(cx, cursor, &callSites_)) &&
   868     (cursor = DeserializeVector(cx, cursor, &functionNames_)) &&
   869     (cursor = DeserializePodVector(cx, cursor, &heapAccesses_)) &&
   870 #if defined(MOZ_VTUNE) || defined(JS_ION_PERF)
   871     (cursor = DeserializeVector(cx, cursor, &profiledFunctions_)) &&
   872 #endif
   873     (cursor = staticLinkData_.deserialize(cx, cursor));
   875     loadedFromCache_ = true;
   877     return cursor;
   878 }
   880 // When a module is cloned, we memcpy its executable code. If, right before or
   881 // during the clone, another thread calls AsmJSModule::protectCode() then the
   882 // executable code will become inaccessible. In theory, we could take away only
   883 // PROT_EXEC, but this seems to break emulators.
   884 class AutoUnprotectCodeForClone
   885 {
   886     JSRuntime *rt_;
   887     JSRuntime::AutoLockForInterrupt lock_;
   888     const AsmJSModule &module_;
   889     const bool protectedBefore_;
   891   public:
   892     AutoUnprotectCodeForClone(JSContext *cx, const AsmJSModule &module)
   893       : rt_(cx->runtime()),
   894         lock_(rt_),
   895         module_(module),
   896         protectedBefore_(module_.codeIsProtected(rt_))
   897     {
   898         if (protectedBefore_)
   899             module_.unprotectCode(rt_);
   900     }
   902     ~AutoUnprotectCodeForClone()
   903     {
   904         if (protectedBefore_)
   905             module_.protectCode(rt_);
   906     }
   907 };
   909 bool
   910 AsmJSModule::clone(JSContext *cx, ScopedJSDeletePtr<AsmJSModule> *moduleOut) const
   911 {
   912     AutoUnprotectCodeForClone cloneGuard(cx, *this);
   914     *moduleOut = cx->new_<AsmJSModule>(scriptSource_, funcStart_, offsetToEndOfUseAsm_, pod.strict_);
   915     if (!*moduleOut)
   916         return false;
   918     AsmJSModule &out = **moduleOut;
   920     // Mirror the order of serialize/deserialize in cloning:
   922     out.pod = pod;
   924     out.code_ = AllocateExecutableMemory(cx, pod.totalBytes_);
   925     if (!out.code_)
   926         return false;
   928     memcpy(out.code_, code_, pod.codeBytes_);
   930     out.globalArgumentName_ = globalArgumentName_;
   931     out.importArgumentName_ = importArgumentName_;
   932     out.bufferArgumentName_ = bufferArgumentName_;
   934     if (!CloneVector(cx, globals_, &out.globals_) ||
   935         !CloneVector(cx, exits_, &out.exits_) ||
   936         !CloneVector(cx, exports_, &out.exports_) ||
   937         !ClonePodVector(cx, callSites_, &out.callSites_) ||
   938         !CloneVector(cx, functionNames_, &out.functionNames_) ||
   939         !ClonePodVector(cx, heapAccesses_, &out.heapAccesses_) ||
   940         !staticLinkData_.clone(cx, &out.staticLinkData_))
   941     {
   942         return false;
   943     }
   945     out.loadedFromCache_ = loadedFromCache_;
   947     // We already know the exact extent of areas that need to be patched, just make sure we
   948     // flush all of them at once.
   949     out.setAutoFlushICacheRange();
   951     out.restoreToInitialState(maybeHeap_, cx);
   952     return true;
   953 }
   955 void
   956 AsmJSModule::protectCode(JSRuntime *rt) const
   957 {
   958     JS_ASSERT(rt->currentThreadOwnsInterruptLock());
   960     codeIsProtected_ = true;
   962     if (!pod.functionBytes_)
   963         return;
   965     // Technically, we should be able to only take away the execute permissions,
   966     // however this seems to break our emulators which don't always check
   967     // execute permissions while executing code.
   968 #if defined(XP_WIN)
   969     DWORD oldProtect;
   970     if (!VirtualProtect(codeBase(), functionBytes(), PAGE_NOACCESS, &oldProtect))
   971         MOZ_CRASH();
   972 #else  // assume Unix
   973     if (mprotect(codeBase(), functionBytes(), PROT_NONE))
   974         MOZ_CRASH();
   975 #endif
   976 }
   978 void
   979 AsmJSModule::unprotectCode(JSRuntime *rt) const
   980 {
   981     JS_ASSERT(rt->currentThreadOwnsInterruptLock());
   983     codeIsProtected_ = false;
   985     if (!pod.functionBytes_)
   986         return;
   988 #if defined(XP_WIN)
   989     DWORD oldProtect;
   990     if (!VirtualProtect(codeBase(), functionBytes(), PAGE_EXECUTE_READWRITE, &oldProtect))
   991         MOZ_CRASH();
   992 #else  // assume Unix
   993     if (mprotect(codeBase(), functionBytes(), PROT_READ | PROT_WRITE | PROT_EXEC))
   994         MOZ_CRASH();
   995 #endif
   996 }
   998 bool
   999 AsmJSModule::codeIsProtected(JSRuntime *rt) const
  1001     JS_ASSERT(rt->currentThreadOwnsInterruptLock());
  1002     return codeIsProtected_;
  1005 static bool
  1006 GetCPUID(uint32_t *cpuId)
  1008     enum Arch {
  1009         X86 = 0x1,
  1010         X64 = 0x2,
  1011         ARM = 0x3,
  1012         ARCH_BITS = 2
  1013     };
  1015 #if defined(JS_CODEGEN_X86)
  1016     JS_ASSERT(uint32_t(JSC::MacroAssembler::getSSEState()) <= (UINT32_MAX >> ARCH_BITS));
  1017     *cpuId = X86 | (JSC::MacroAssembler::getSSEState() << ARCH_BITS);
  1018     return true;
  1019 #elif defined(JS_CODEGEN_X64)
  1020     JS_ASSERT(uint32_t(JSC::MacroAssembler::getSSEState()) <= (UINT32_MAX >> ARCH_BITS));
  1021     *cpuId = X64 | (JSC::MacroAssembler::getSSEState() << ARCH_BITS);
  1022     return true;
  1023 #elif defined(JS_CODEGEN_ARM)
  1024     JS_ASSERT(GetARMFlags() <= (UINT32_MAX >> ARCH_BITS));
  1025     *cpuId = ARM | (GetARMFlags() << ARCH_BITS);
  1026     return true;
  1027 #else
  1028     return false;
  1029 #endif
  1032 class MachineId
  1034     uint32_t cpuId_;
  1035     JS::BuildIdCharVector buildId_;
  1037   public:
  1038     bool extractCurrentState(ExclusiveContext *cx) {
  1039         if (!cx->asmJSCacheOps().buildId)
  1040             return false;
  1041         if (!cx->asmJSCacheOps().buildId(&buildId_))
  1042             return false;
  1043         if (!GetCPUID(&cpuId_))
  1044             return false;
  1045         return true;
  1048     size_t serializedSize() const {
  1049         return sizeof(uint32_t) +
  1050                SerializedPodVectorSize(buildId_);
  1053     uint8_t *serialize(uint8_t *cursor) const {
  1054         cursor = WriteScalar<uint32_t>(cursor, cpuId_);
  1055         cursor = SerializePodVector(cursor, buildId_);
  1056         return cursor;
  1059     const uint8_t *deserialize(ExclusiveContext *cx, const uint8_t *cursor) {
  1060         (cursor = ReadScalar<uint32_t>(cursor, &cpuId_)) &&
  1061         (cursor = DeserializePodVector(cx, cursor, &buildId_));
  1062         return cursor;
  1065     bool operator==(const MachineId &rhs) const {
  1066         return cpuId_ == rhs.cpuId_ &&
  1067                buildId_.length() == rhs.buildId_.length() &&
  1068                PodEqual(buildId_.begin(), rhs.buildId_.begin(), buildId_.length());
  1070     bool operator!=(const MachineId &rhs) const {
  1071         return !(*this == rhs);
  1073 };
  1075 struct PropertyNameWrapper
  1077     PropertyName *name;
  1079     PropertyNameWrapper()
  1080       : name(nullptr)
  1081     {}
  1082     PropertyNameWrapper(PropertyName *name)
  1083       : name(name)
  1084     {}
  1085     size_t serializedSize() const {
  1086         return SerializedNameSize(name);
  1088     uint8_t *serialize(uint8_t *cursor) const {
  1089         return SerializeName(cursor, name);
  1091     const uint8_t *deserialize(ExclusiveContext *cx, const uint8_t *cursor) {
  1092         return DeserializeName(cx, cursor, &name);
  1094 };
  1096 class ModuleChars
  1098   protected:
  1099     uint32_t isFunCtor_;
  1100     js::Vector<PropertyNameWrapper, 0, SystemAllocPolicy> funCtorArgs_;
  1102   public:
  1103     static uint32_t beginOffset(AsmJSParser &parser) {
  1104       return parser.pc->maybeFunction->pn_pos.begin;
  1107     static uint32_t endOffset(AsmJSParser &parser) {
  1108       return parser.tokenStream.peekTokenPos().end;
  1110 };
  1112 class ModuleCharsForStore : ModuleChars
  1114     uint32_t uncompressedSize_;
  1115     uint32_t compressedSize_;
  1116     js::Vector<char, 0, SystemAllocPolicy> compressedBuffer_;
  1118   public:
  1119     bool init(AsmJSParser &parser) {
  1120         JS_ASSERT(beginOffset(parser) < endOffset(parser));
  1122         uncompressedSize_ = (endOffset(parser) - beginOffset(parser)) * sizeof(jschar);
  1123         size_t maxCompressedSize = LZ4::maxCompressedSize(uncompressedSize_);
  1124         if (maxCompressedSize < uncompressedSize_)
  1125             return false;
  1127         if (!compressedBuffer_.resize(maxCompressedSize))
  1128             return false;
  1130         const jschar *chars = parser.tokenStream.rawBase() + beginOffset(parser);
  1131         const char *source = reinterpret_cast<const char*>(chars);
  1132         size_t compressedSize = LZ4::compress(source, uncompressedSize_, compressedBuffer_.begin());
  1133         if (!compressedSize || compressedSize > UINT32_MAX)
  1134             return false;
  1136         compressedSize_ = compressedSize;
  1138         // For a function statement or named function expression:
  1139         //   function f(x,y,z) { abc }
  1140         // the range [beginOffset, endOffset) captures the source:
  1141         //   f(x,y,z) { abc }
  1142         // An unnamed function expression captures the same thing, sans 'f'.
  1143         // Since asm.js modules do not contain any free variables, equality of
  1144         // [beginOffset, endOffset) is sufficient to guarantee identical code
  1145         // generation, modulo MachineId.
  1146         //
  1147         // For functions created with 'new Function', function arguments are
  1148         // not present in the source so we must manually explicitly serialize
  1149         // and match the formals as a Vector of PropertyName.
  1150         isFunCtor_ = parser.pc->isFunctionConstructorBody();
  1151         if (isFunCtor_) {
  1152             unsigned numArgs;
  1153             ParseNode *arg = FunctionArgsList(parser.pc->maybeFunction, &numArgs);
  1154             for (unsigned i = 0; i < numArgs; i++, arg = arg->pn_next) {
  1155                 if (!funCtorArgs_.append(arg->name()))
  1156                     return false;
  1160         return true;
  1163     size_t serializedSize() const {
  1164         return sizeof(uint32_t) +
  1165                sizeof(uint32_t) +
  1166                compressedSize_ +
  1167                sizeof(uint32_t) +
  1168                (isFunCtor_ ? SerializedVectorSize(funCtorArgs_) : 0);
  1171     uint8_t *serialize(uint8_t *cursor) const {
  1172         cursor = WriteScalar<uint32_t>(cursor, uncompressedSize_);
  1173         cursor = WriteScalar<uint32_t>(cursor, compressedSize_);
  1174         cursor = WriteBytes(cursor, compressedBuffer_.begin(), compressedSize_);
  1175         cursor = WriteScalar<uint32_t>(cursor, isFunCtor_);
  1176         if (isFunCtor_)
  1177             cursor = SerializeVector(cursor, funCtorArgs_);
  1178         return cursor;
  1180 };
  1182 class ModuleCharsForLookup : ModuleChars
  1184     js::Vector<jschar, 0, SystemAllocPolicy> chars_;
  1186   public:
  1187     const uint8_t *deserialize(ExclusiveContext *cx, const uint8_t *cursor) {
  1188         uint32_t uncompressedSize;
  1189         cursor = ReadScalar<uint32_t>(cursor, &uncompressedSize);
  1191         uint32_t compressedSize;
  1192         cursor = ReadScalar<uint32_t>(cursor, &compressedSize);
  1194         if (!chars_.resize(uncompressedSize / sizeof(jschar)))
  1195             return nullptr;
  1197         const char *source = reinterpret_cast<const char*>(cursor);
  1198         char *dest = reinterpret_cast<char*>(chars_.begin());
  1199         if (!LZ4::decompress(source, dest, uncompressedSize))
  1200             return nullptr;
  1202         cursor += compressedSize;
  1204         cursor = ReadScalar<uint32_t>(cursor, &isFunCtor_);
  1205         if (isFunCtor_)
  1206             cursor = DeserializeVector(cx, cursor, &funCtorArgs_);
  1208         return cursor;
  1211     bool match(AsmJSParser &parser) const {
  1212         const jschar *parseBegin = parser.tokenStream.rawBase() + beginOffset(parser);
  1213         const jschar *parseLimit = parser.tokenStream.rawLimit();
  1214         JS_ASSERT(parseLimit >= parseBegin);
  1215         if (uint32_t(parseLimit - parseBegin) < chars_.length())
  1216             return false;
  1217         if (!PodEqual(chars_.begin(), parseBegin, chars_.length()))
  1218             return false;
  1219         if (isFunCtor_ != parser.pc->isFunctionConstructorBody())
  1220             return false;
  1221         if (isFunCtor_) {
  1222             // For function statements, the closing } is included as the last
  1223             // character of the matched source. For Function constructor,
  1224             // parsing terminates with EOF which we must explicitly check. This
  1225             // prevents
  1226             //   new Function('"use asm"; function f() {} return f')
  1227             // from incorrectly matching
  1228             //   new Function('"use asm"; function f() {} return ff')
  1229             if (parseBegin + chars_.length() != parseLimit)
  1230                 return false;
  1231             unsigned numArgs;
  1232             ParseNode *arg = FunctionArgsList(parser.pc->maybeFunction, &numArgs);
  1233             if (funCtorArgs_.length() != numArgs)
  1234                 return false;
  1235             for (unsigned i = 0; i < funCtorArgs_.length(); i++, arg = arg->pn_next) {
  1236                 if (funCtorArgs_[i].name != arg->name())
  1237                     return false;
  1240         return true;
  1242 };
  1244 struct ScopedCacheEntryOpenedForWrite
  1246     ExclusiveContext *cx;
  1247     const size_t serializedSize;
  1248     uint8_t *memory;
  1249     intptr_t handle;
  1251     ScopedCacheEntryOpenedForWrite(ExclusiveContext *cx, size_t serializedSize)
  1252       : cx(cx), serializedSize(serializedSize), memory(nullptr), handle(-1)
  1253     {}
  1255     ~ScopedCacheEntryOpenedForWrite() {
  1256         if (memory)
  1257             cx->asmJSCacheOps().closeEntryForWrite(cx->global(), serializedSize, memory, handle);
  1259 };
  1261 bool
  1262 js::StoreAsmJSModuleInCache(AsmJSParser &parser,
  1263                             const AsmJSModule &module,
  1264                             ExclusiveContext *cx)
  1266     MachineId machineId;
  1267     if (!machineId.extractCurrentState(cx))
  1268         return false;
  1270     ModuleCharsForStore moduleChars;
  1271     if (!moduleChars.init(parser))
  1272         return false;
  1274     size_t serializedSize = machineId.serializedSize() +
  1275                             moduleChars.serializedSize() +
  1276                             module.serializedSize();
  1278     JS::OpenAsmJSCacheEntryForWriteOp open = cx->asmJSCacheOps().openEntryForWrite;
  1279     if (!open)
  1280         return false;
  1282     const jschar *begin = parser.tokenStream.rawBase() + ModuleChars::beginOffset(parser);
  1283     const jschar *end = parser.tokenStream.rawBase() + ModuleChars::endOffset(parser);
  1284     bool installed = parser.options().installedFile;
  1286     ScopedCacheEntryOpenedForWrite entry(cx, serializedSize);
  1287     if (!open(cx->global(), installed, begin, end, entry.serializedSize,
  1288               &entry.memory, &entry.handle)) {
  1289         return false;
  1292     uint8_t *cursor = entry.memory;
  1293     cursor = machineId.serialize(cursor);
  1294     cursor = moduleChars.serialize(cursor);
  1295     cursor = module.serialize(cursor);
  1297     JS_ASSERT(cursor == entry.memory + serializedSize);
  1298     return true;
  1301 struct ScopedCacheEntryOpenedForRead
  1303     ExclusiveContext *cx;
  1304     size_t serializedSize;
  1305     const uint8_t *memory;
  1306     intptr_t handle;
  1308     ScopedCacheEntryOpenedForRead(ExclusiveContext *cx)
  1309       : cx(cx), serializedSize(0), memory(nullptr), handle(0)
  1310     {}
  1312     ~ScopedCacheEntryOpenedForRead() {
  1313         if (memory)
  1314             cx->asmJSCacheOps().closeEntryForRead(cx->global(), serializedSize, memory, handle);
  1316 };
  1318 bool
  1319 js::LookupAsmJSModuleInCache(ExclusiveContext *cx,
  1320                              AsmJSParser &parser,
  1321                              ScopedJSDeletePtr<AsmJSModule> *moduleOut,
  1322                              ScopedJSFreePtr<char> *compilationTimeReport)
  1324     int64_t usecBefore = PRMJ_Now();
  1326     MachineId machineId;
  1327     if (!machineId.extractCurrentState(cx))
  1328         return true;
  1330     JS::OpenAsmJSCacheEntryForReadOp open = cx->asmJSCacheOps().openEntryForRead;
  1331     if (!open)
  1332         return true;
  1334     const jschar *begin = parser.tokenStream.rawBase() + ModuleChars::beginOffset(parser);
  1335     const jschar *limit = parser.tokenStream.rawLimit();
  1337     ScopedCacheEntryOpenedForRead entry(cx);
  1338     if (!open(cx->global(), begin, limit, &entry.serializedSize, &entry.memory, &entry.handle))
  1339         return true;
  1341     const uint8_t *cursor = entry.memory;
  1343     MachineId cachedMachineId;
  1344     cursor = cachedMachineId.deserialize(cx, cursor);
  1345     if (!cursor)
  1346         return false;
  1347     if (machineId != cachedMachineId)
  1348         return true;
  1350     ModuleCharsForLookup moduleChars;
  1351     cursor = moduleChars.deserialize(cx, cursor);
  1352     if (!moduleChars.match(parser))
  1353         return true;
  1355     uint32_t funcStart = parser.pc->maybeFunction->pn_body->pn_pos.begin;
  1356     uint32_t offsetToEndOfUseAsm = parser.tokenStream.currentToken().pos.end;
  1357     bool strict = parser.pc->sc->strict && !parser.pc->sc->hasExplicitUseStrict();
  1358     ScopedJSDeletePtr<AsmJSModule> module(
  1359         cx->new_<AsmJSModule>(parser.ss, funcStart, offsetToEndOfUseAsm, strict));
  1360     if (!module)
  1361         return false;
  1362     cursor = module->deserialize(cx, cursor);
  1364     // No need to flush the instruction cache now, it will be flushed when dynamically linking.
  1365     AutoFlushICache afc("LookupAsmJSModuleInCache", /* inhibit= */ true);
  1366     // We already know the exact extent of areas that need to be patched, just make sure we
  1367     // flush all of them at once.
  1368     module->setAutoFlushICacheRange();
  1370     if (!cursor)
  1371         return false;
  1373     bool atEnd = cursor == entry.memory + entry.serializedSize;
  1374     MOZ_ASSERT(atEnd, "Corrupt cache file");
  1375     if (!atEnd)
  1376         return true;
  1378     module->staticallyLink(cx);
  1380     parser.tokenStream.advance(module->funcEndBeforeCurly());
  1382     int64_t usecAfter = PRMJ_Now();
  1383     int ms = (usecAfter - usecBefore) / PRMJ_USEC_PER_MSEC;
  1384     *compilationTimeReport = JS_smprintf("loaded from cache in %dms", ms);
  1385     *moduleOut = module.forget();
  1386     return true;

mercurial