michael@0: /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- michael@0: * vim: set ts=8 sts=4 et sw=4 tw=99: michael@0: * This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "jit/AsmJSModule.h" michael@0: michael@0: #ifndef XP_WIN michael@0: # include michael@0: #endif michael@0: michael@0: #include "mozilla/Compression.h" michael@0: #include "mozilla/PodOperations.h" michael@0: michael@0: #include "jslibmath.h" michael@0: #include "jsmath.h" michael@0: #include "jsprf.h" michael@0: #ifdef XP_WIN michael@0: # include "jswin.h" michael@0: #endif michael@0: #include "prmjtime.h" michael@0: michael@0: #include "frontend/Parser.h" michael@0: #include "jit/IonCode.h" michael@0: #include "js/MemoryMetrics.h" michael@0: michael@0: #include "jsobjinlines.h" michael@0: michael@0: #include "frontend/ParseNode-inl.h" michael@0: michael@0: using namespace js; michael@0: using namespace jit; michael@0: using namespace frontend; michael@0: using mozilla::PodCopy; michael@0: using mozilla::PodEqual; michael@0: using mozilla::Compression::LZ4; michael@0: michael@0: void michael@0: AsmJSModule::initHeap(Handle heap, JSContext *cx) michael@0: { michael@0: JS_ASSERT(IsValidAsmJSHeapLength(heap->byteLength())); michael@0: JS_ASSERT(dynamicallyLinked_); michael@0: JS_ASSERT(!maybeHeap_); michael@0: michael@0: maybeHeap_ = heap; michael@0: heapDatum() = heap->dataPointer(); michael@0: michael@0: #if defined(JS_CODEGEN_X86) michael@0: uint8_t *heapOffset = heap->dataPointer(); michael@0: void *heapLength = (void*)heap->byteLength(); michael@0: for (unsigned i = 0; i < heapAccesses_.length(); i++) { michael@0: const jit::AsmJSHeapAccess &access = heapAccesses_[i]; michael@0: if (access.hasLengthCheck()) michael@0: JSC::X86Assembler::setPointer(access.patchLengthAt(code_), heapLength); michael@0: void *addr = access.patchOffsetAt(code_); michael@0: uint32_t disp = reinterpret_cast(JSC::X86Assembler::getPointer(addr)); michael@0: JS_ASSERT(disp <= INT32_MAX); michael@0: JSC::X86Assembler::setPointer(addr, (void *)(heapOffset + disp)); michael@0: } michael@0: #elif defined(JS_CODEGEN_ARM) michael@0: uint32_t heapLength = heap->byteLength(); michael@0: for (unsigned i = 0; i < heapAccesses_.length(); i++) { michael@0: jit::Assembler::updateBoundsCheck(heapLength, michael@0: (jit::Instruction*)(heapAccesses_[i].offset() + code_)); michael@0: } michael@0: #endif michael@0: } michael@0: michael@0: static uint8_t * michael@0: AllocateExecutableMemory(ExclusiveContext *cx, size_t totalBytes) michael@0: { michael@0: JS_ASSERT(totalBytes % AsmJSPageSize == 0); michael@0: michael@0: #ifdef XP_WIN michael@0: void *p = VirtualAlloc(nullptr, totalBytes, MEM_COMMIT, PAGE_EXECUTE_READWRITE); michael@0: if (!p) { michael@0: js_ReportOutOfMemory(cx); michael@0: return nullptr; michael@0: } michael@0: #else // assume Unix michael@0: void *p = mmap(nullptr, totalBytes, michael@0: PROT_READ | PROT_WRITE | PROT_EXEC, MAP_PRIVATE | MAP_ANON, michael@0: -1, 0); michael@0: if (p == MAP_FAILED) { michael@0: js_ReportOutOfMemory(cx); michael@0: return nullptr; michael@0: } michael@0: #endif michael@0: michael@0: return (uint8_t *)p; michael@0: } michael@0: michael@0: static void michael@0: DeallocateExecutableMemory(uint8_t *code, size_t totalBytes) michael@0: { michael@0: #ifdef XP_WIN michael@0: JS_ALWAYS_TRUE(VirtualFree(code, 0, MEM_RELEASE)); michael@0: #else michael@0: JS_ALWAYS_TRUE(munmap(code, totalBytes) == 0); michael@0: #endif michael@0: } michael@0: michael@0: bool michael@0: AsmJSModule::allocateAndCopyCode(ExclusiveContext *cx, MacroAssembler &masm) michael@0: { michael@0: JS_ASSERT(!code_); michael@0: michael@0: // The global data section sits immediately after the executable (and michael@0: // other) data allocated by the MacroAssembler, so ensure it is michael@0: // double-aligned. michael@0: pod.codeBytes_ = AlignBytes(masm.bytesNeeded(), sizeof(double)); michael@0: michael@0: // The entire region is allocated via mmap/VirtualAlloc which requires michael@0: // units of pages. michael@0: pod.totalBytes_ = AlignBytes(pod.codeBytes_ + globalDataBytes(), AsmJSPageSize); michael@0: michael@0: code_ = AllocateExecutableMemory(cx, pod.totalBytes_); michael@0: if (!code_) michael@0: return false; michael@0: michael@0: JS_ASSERT(uintptr_t(code_) % AsmJSPageSize == 0); michael@0: masm.executableCopy(code_); michael@0: return true; michael@0: } michael@0: michael@0: static int32_t michael@0: CoerceInPlace_ToInt32(JSContext *cx, MutableHandleValue val) michael@0: { michael@0: int32_t i32; michael@0: if (!ToInt32(cx, val, &i32)) michael@0: return false; michael@0: val.set(Int32Value(i32)); michael@0: michael@0: return true; michael@0: } michael@0: michael@0: static int32_t michael@0: CoerceInPlace_ToNumber(JSContext *cx, MutableHandleValue val) michael@0: { michael@0: double dbl; michael@0: if (!ToNumber(cx, val, &dbl)) michael@0: return false; michael@0: val.set(DoubleValue(dbl)); michael@0: michael@0: return true; michael@0: } michael@0: michael@0: namespace js { michael@0: michael@0: // Defined in AsmJS.cpp: michael@0: michael@0: int32_t michael@0: InvokeFromAsmJS_Ignore(JSContext *cx, int32_t exitIndex, int32_t argc, Value *argv); michael@0: michael@0: int32_t michael@0: InvokeFromAsmJS_ToInt32(JSContext *cx, int32_t exitIndex, int32_t argc, Value *argv); michael@0: michael@0: int32_t michael@0: InvokeFromAsmJS_ToNumber(JSContext *cx, int32_t exitIndex, int32_t argc, Value *argv); michael@0: michael@0: } michael@0: michael@0: #if defined(JS_CODEGEN_ARM) michael@0: extern "C" { michael@0: michael@0: extern int64_t michael@0: __aeabi_idivmod(int, int); michael@0: michael@0: extern int64_t michael@0: __aeabi_uidivmod(int, int); michael@0: michael@0: } michael@0: #endif michael@0: michael@0: template michael@0: static inline void * michael@0: FuncCast(F *pf) michael@0: { michael@0: return JS_FUNC_TO_DATA_PTR(void *, pf); michael@0: } michael@0: michael@0: static void * michael@0: RedirectCall(void *fun, ABIFunctionType type) michael@0: { michael@0: #ifdef JS_ARM_SIMULATOR michael@0: fun = Simulator::RedirectNativeFunction(fun, type); michael@0: #endif michael@0: return fun; michael@0: } michael@0: michael@0: #ifdef DEBUG michael@0: static void michael@0: AssumeUnreachable() michael@0: { michael@0: MOZ_CRASH("Reached unreachable code in asm.js"); michael@0: } michael@0: #endif michael@0: michael@0: static void * michael@0: AddressOf(AsmJSImmKind kind, ExclusiveContext *cx) michael@0: { michael@0: switch (kind) { michael@0: case AsmJSImm_Runtime: michael@0: return cx->runtimeAddressForJit(); michael@0: case AsmJSImm_StackLimit: michael@0: return cx->stackLimitAddressForJitCode(StackForUntrustedScript); michael@0: case AsmJSImm_ReportOverRecursed: michael@0: return RedirectCall(FuncCast(js_ReportOverRecursed), Args_General1); michael@0: case AsmJSImm_HandleExecutionInterrupt: michael@0: return RedirectCall(FuncCast(js::HandleExecutionInterrupt), Args_General1); michael@0: case AsmJSImm_InvokeFromAsmJS_Ignore: michael@0: return RedirectCall(FuncCast(InvokeFromAsmJS_Ignore), Args_General4); michael@0: case AsmJSImm_InvokeFromAsmJS_ToInt32: michael@0: return RedirectCall(FuncCast(InvokeFromAsmJS_ToInt32), Args_General4); michael@0: case AsmJSImm_InvokeFromAsmJS_ToNumber: michael@0: return RedirectCall(FuncCast(InvokeFromAsmJS_ToNumber), Args_General4); michael@0: case AsmJSImm_CoerceInPlace_ToInt32: michael@0: return RedirectCall(FuncCast(CoerceInPlace_ToInt32), Args_General2); michael@0: case AsmJSImm_CoerceInPlace_ToNumber: michael@0: return RedirectCall(FuncCast(CoerceInPlace_ToNumber), Args_General2); michael@0: case AsmJSImm_ToInt32: michael@0: return RedirectCall(FuncCast(js::ToInt32), Args_Int_Double); michael@0: #if defined(JS_CODEGEN_ARM) michael@0: case AsmJSImm_aeabi_idivmod: michael@0: return RedirectCall(FuncCast(__aeabi_idivmod), Args_General2); michael@0: case AsmJSImm_aeabi_uidivmod: michael@0: return RedirectCall(FuncCast(__aeabi_uidivmod), Args_General2); michael@0: #endif michael@0: case AsmJSImm_ModD: michael@0: return RedirectCall(FuncCast(NumberMod), Args_Double_DoubleDouble); michael@0: case AsmJSImm_SinD: michael@0: return RedirectCall(FuncCast(sin), Args_Double_Double); michael@0: case AsmJSImm_CosD: michael@0: return RedirectCall(FuncCast(cos), Args_Double_Double); michael@0: case AsmJSImm_TanD: michael@0: return RedirectCall(FuncCast(tan), Args_Double_Double); michael@0: case AsmJSImm_ASinD: michael@0: return RedirectCall(FuncCast(asin), Args_Double_Double); michael@0: case AsmJSImm_ACosD: michael@0: return RedirectCall(FuncCast(acos), Args_Double_Double); michael@0: case AsmJSImm_ATanD: michael@0: return RedirectCall(FuncCast(atan), Args_Double_Double); michael@0: case AsmJSImm_CeilD: michael@0: return RedirectCall(FuncCast(ceil), Args_Double_Double); michael@0: case AsmJSImm_CeilF: michael@0: return RedirectCall(FuncCast(ceilf), Args_Float32_Float32); michael@0: case AsmJSImm_FloorD: michael@0: return RedirectCall(FuncCast(floor), Args_Double_Double); michael@0: case AsmJSImm_FloorF: michael@0: return RedirectCall(FuncCast(floorf), Args_Float32_Float32); michael@0: case AsmJSImm_ExpD: michael@0: return RedirectCall(FuncCast(exp), Args_Double_Double); michael@0: case AsmJSImm_LogD: michael@0: return RedirectCall(FuncCast(log), Args_Double_Double); michael@0: case AsmJSImm_PowD: michael@0: return RedirectCall(FuncCast(ecmaPow), Args_Double_DoubleDouble); michael@0: case AsmJSImm_ATan2D: michael@0: return RedirectCall(FuncCast(ecmaAtan2), Args_Double_DoubleDouble); michael@0: #ifdef DEBUG michael@0: case AsmJSImm_AssumeUnreachable: michael@0: return RedirectCall(FuncCast(AssumeUnreachable), Args_General0); michael@0: #endif michael@0: case AsmJSImm_Invalid: michael@0: break; michael@0: } michael@0: michael@0: MOZ_ASSUME_UNREACHABLE("Bad AsmJSImmKind"); michael@0: return nullptr; michael@0: } michael@0: michael@0: void michael@0: AsmJSModule::restoreToInitialState(ArrayBufferObject *maybePrevBuffer, ExclusiveContext *cx) michael@0: { michael@0: #ifdef DEBUG michael@0: // Put the absolute links back to -1 so patchDataWithValueCheck assertions michael@0: // in staticallyLink are valid. michael@0: for (size_t i = 0; i < staticLinkData_.absoluteLinks.length(); i++) { michael@0: AbsoluteLink link = staticLinkData_.absoluteLinks[i]; michael@0: Assembler::patchDataWithValueCheck(code_ + link.patchAt.offset(), michael@0: PatchedImmPtr((void*)-1), michael@0: PatchedImmPtr(AddressOf(link.target, cx))); michael@0: } michael@0: #endif michael@0: michael@0: if (maybePrevBuffer) { michael@0: #if defined(JS_CODEGEN_X86) michael@0: // Subtract out the base-pointer added by AsmJSModule::initHeap. michael@0: uint8_t *ptrBase = maybePrevBuffer->dataPointer(); michael@0: for (unsigned i = 0; i < heapAccesses_.length(); i++) { michael@0: const jit::AsmJSHeapAccess &access = heapAccesses_[i]; michael@0: void *addr = access.patchOffsetAt(code_); michael@0: uint8_t *ptr = reinterpret_cast(JSC::X86Assembler::getPointer(addr)); michael@0: JS_ASSERT(ptr >= ptrBase); michael@0: JSC::X86Assembler::setPointer(addr, (void *)(ptr - ptrBase)); michael@0: } michael@0: #endif michael@0: } michael@0: } michael@0: michael@0: void michael@0: AsmJSModule::setAutoFlushICacheRange() michael@0: { michael@0: AutoFlushICache::setRange(uintptr_t(code_), pod.codeBytes_); michael@0: } michael@0: michael@0: void michael@0: AsmJSModule::staticallyLink(ExclusiveContext *cx) michael@0: { michael@0: // Process staticLinkData_ michael@0: michael@0: interruptExit_ = code_ + staticLinkData_.interruptExitOffset; michael@0: michael@0: for (size_t i = 0; i < staticLinkData_.relativeLinks.length(); i++) { michael@0: RelativeLink link = staticLinkData_.relativeLinks[i]; michael@0: *(void **)(code_ + link.patchAtOffset) = code_ + link.targetOffset; michael@0: } michael@0: michael@0: for (size_t i = 0; i < staticLinkData_.absoluteLinks.length(); i++) { michael@0: AbsoluteLink link = staticLinkData_.absoluteLinks[i]; michael@0: Assembler::patchDataWithValueCheck(code_ + link.patchAt.offset(), michael@0: PatchedImmPtr(AddressOf(link.target, cx)), michael@0: PatchedImmPtr((void*)-1)); michael@0: } michael@0: michael@0: // Initialize global data segment michael@0: michael@0: for (size_t i = 0; i < exits_.length(); i++) { michael@0: exitIndexToGlobalDatum(i).exit = interpExitTrampoline(exits_[i]); michael@0: exitIndexToGlobalDatum(i).fun = nullptr; michael@0: } michael@0: } michael@0: michael@0: AsmJSModule::AsmJSModule(ScriptSource *scriptSource, uint32_t funcStart, michael@0: uint32_t offsetToEndOfUseAsm, bool strict) michael@0: : globalArgumentName_(nullptr), michael@0: importArgumentName_(nullptr), michael@0: bufferArgumentName_(nullptr), michael@0: code_(nullptr), michael@0: interruptExit_(nullptr), michael@0: dynamicallyLinked_(false), michael@0: loadedFromCache_(false), michael@0: funcStart_(funcStart), michael@0: offsetToEndOfUseAsm_(offsetToEndOfUseAsm), michael@0: scriptSource_(scriptSource), michael@0: codeIsProtected_(false) michael@0: { michael@0: mozilla::PodZero(&pod); michael@0: scriptSource_->incref(); michael@0: pod.minHeapLength_ = AsmJSAllocationGranularity; michael@0: pod.strict_ = strict; michael@0: } michael@0: michael@0: AsmJSModule::~AsmJSModule() michael@0: { michael@0: scriptSource_->decref(); michael@0: michael@0: if (code_) { michael@0: for (unsigned i = 0; i < numExits(); i++) { michael@0: AsmJSModule::ExitDatum &exitDatum = exitIndexToGlobalDatum(i); michael@0: if (!exitDatum.fun) michael@0: continue; michael@0: michael@0: if (!exitDatum.fun->hasScript()) michael@0: continue; michael@0: michael@0: JSScript *script = exitDatum.fun->nonLazyScript(); michael@0: if (!script->hasIonScript()) michael@0: continue; michael@0: michael@0: jit::DependentAsmJSModuleExit exit(this, i); michael@0: script->ionScript()->removeDependentAsmJSModule(exit); michael@0: } michael@0: michael@0: DeallocateExecutableMemory(code_, pod.totalBytes_); michael@0: } michael@0: } michael@0: michael@0: void michael@0: AsmJSModule::addSizeOfMisc(mozilla::MallocSizeOf mallocSizeOf, size_t *asmJSModuleCode, michael@0: size_t *asmJSModuleData) michael@0: { michael@0: *asmJSModuleCode += pod.totalBytes_; michael@0: *asmJSModuleData += mallocSizeOf(this) + michael@0: globals_.sizeOfExcludingThis(mallocSizeOf) + michael@0: exits_.sizeOfExcludingThis(mallocSizeOf) + michael@0: exports_.sizeOfExcludingThis(mallocSizeOf) + michael@0: callSites_.sizeOfExcludingThis(mallocSizeOf) + michael@0: functionNames_.sizeOfExcludingThis(mallocSizeOf) + michael@0: heapAccesses_.sizeOfExcludingThis(mallocSizeOf) + michael@0: #if defined(MOZ_VTUNE) || defined(JS_ION_PERF) michael@0: profiledFunctions_.sizeOfExcludingThis(mallocSizeOf) + michael@0: #endif michael@0: #if defined(JS_ION_PERF) michael@0: perfProfiledBlocksFunctions_.sizeOfExcludingThis(mallocSizeOf) + michael@0: #endif michael@0: staticLinkData_.sizeOfExcludingThis(mallocSizeOf); michael@0: } michael@0: michael@0: static void michael@0: AsmJSModuleObject_finalize(FreeOp *fop, JSObject *obj) michael@0: { michael@0: fop->delete_(&obj->as().module()); michael@0: } michael@0: michael@0: static void michael@0: AsmJSModuleObject_trace(JSTracer *trc, JSObject *obj) michael@0: { michael@0: obj->as().module().trace(trc); michael@0: } michael@0: michael@0: const Class AsmJSModuleObject::class_ = { michael@0: "AsmJSModuleObject", michael@0: JSCLASS_IS_ANONYMOUS | JSCLASS_IMPLEMENTS_BARRIERS | michael@0: JSCLASS_HAS_RESERVED_SLOTS(AsmJSModuleObject::RESERVED_SLOTS), michael@0: JS_PropertyStub, /* addProperty */ michael@0: JS_DeletePropertyStub, /* delProperty */ michael@0: JS_PropertyStub, /* getProperty */ michael@0: JS_StrictPropertyStub, /* setProperty */ michael@0: JS_EnumerateStub, michael@0: JS_ResolveStub, michael@0: nullptr, /* convert */ michael@0: AsmJSModuleObject_finalize, michael@0: nullptr, /* call */ michael@0: nullptr, /* hasInstance */ michael@0: nullptr, /* construct */ michael@0: AsmJSModuleObject_trace michael@0: }; michael@0: michael@0: AsmJSModuleObject * michael@0: AsmJSModuleObject::create(ExclusiveContext *cx, ScopedJSDeletePtr *module) michael@0: { michael@0: JSObject *obj = NewObjectWithGivenProto(cx, &AsmJSModuleObject::class_, nullptr, nullptr); michael@0: if (!obj) michael@0: return nullptr; michael@0: michael@0: obj->setReservedSlot(MODULE_SLOT, PrivateValue(module->forget())); michael@0: return &obj->as(); michael@0: } michael@0: michael@0: AsmJSModule & michael@0: AsmJSModuleObject::module() const michael@0: { michael@0: JS_ASSERT(is()); michael@0: return *(AsmJSModule *)getReservedSlot(MODULE_SLOT).toPrivate(); michael@0: } michael@0: michael@0: static inline uint8_t * michael@0: WriteBytes(uint8_t *dst, const void *src, size_t nbytes) michael@0: { michael@0: memcpy(dst, src, nbytes); michael@0: return dst + nbytes; michael@0: } michael@0: michael@0: static inline const uint8_t * michael@0: ReadBytes(const uint8_t *src, void *dst, size_t nbytes) michael@0: { michael@0: memcpy(dst, src, nbytes); michael@0: return src + nbytes; michael@0: } michael@0: michael@0: template michael@0: static inline uint8_t * michael@0: WriteScalar(uint8_t *dst, T t) michael@0: { michael@0: memcpy(dst, &t, sizeof(t)); michael@0: return dst + sizeof(t); michael@0: } michael@0: michael@0: template michael@0: static inline const uint8_t * michael@0: ReadScalar(const uint8_t *src, T *dst) michael@0: { michael@0: memcpy(dst, src, sizeof(*dst)); michael@0: return src + sizeof(*dst); michael@0: } michael@0: michael@0: static size_t michael@0: SerializedNameSize(PropertyName *name) michael@0: { michael@0: return sizeof(uint32_t) + michael@0: (name ? name->length() * sizeof(jschar) : 0); michael@0: } michael@0: michael@0: size_t michael@0: AsmJSModule::Name::serializedSize() const michael@0: { michael@0: return SerializedNameSize(name_); michael@0: } michael@0: michael@0: static uint8_t * michael@0: SerializeName(uint8_t *cursor, PropertyName *name) michael@0: { michael@0: JS_ASSERT_IF(name, !name->empty()); michael@0: if (name) { michael@0: cursor = WriteScalar(cursor, name->length()); michael@0: cursor = WriteBytes(cursor, name->chars(), name->length() * sizeof(jschar)); michael@0: } else { michael@0: cursor = WriteScalar(cursor, 0); michael@0: } michael@0: return cursor; michael@0: } michael@0: michael@0: uint8_t * michael@0: AsmJSModule::Name::serialize(uint8_t *cursor) const michael@0: { michael@0: return SerializeName(cursor, name_); michael@0: } michael@0: michael@0: static const uint8_t * michael@0: DeserializeName(ExclusiveContext *cx, const uint8_t *cursor, PropertyName **name) michael@0: { michael@0: uint32_t length; michael@0: cursor = ReadScalar(cursor, &length); michael@0: michael@0: if (length == 0) { michael@0: *name = nullptr; michael@0: return cursor; michael@0: } michael@0: michael@0: js::Vector tmp(cx); michael@0: jschar *src; michael@0: if ((size_t(cursor) & (sizeof(jschar) - 1)) != 0) { michael@0: // Align 'src' for AtomizeChars. michael@0: if (!tmp.resize(length)) michael@0: return nullptr; michael@0: memcpy(tmp.begin(), cursor, length * sizeof(jschar)); michael@0: src = tmp.begin(); michael@0: } else { michael@0: src = (jschar *)cursor; michael@0: } michael@0: michael@0: JSAtom *atom = AtomizeChars(cx, src, length); michael@0: if (!atom) michael@0: return nullptr; michael@0: michael@0: *name = atom->asPropertyName(); michael@0: return cursor + length * sizeof(jschar); michael@0: } michael@0: michael@0: const uint8_t * michael@0: AsmJSModule::Name::deserialize(ExclusiveContext *cx, const uint8_t *cursor) michael@0: { michael@0: return DeserializeName(cx, cursor, &name_); michael@0: } michael@0: michael@0: bool michael@0: AsmJSModule::Name::clone(ExclusiveContext *cx, Name *out) const michael@0: { michael@0: out->name_ = name_; michael@0: return true; michael@0: } michael@0: michael@0: template michael@0: size_t michael@0: SerializedVectorSize(const js::Vector &vec) michael@0: { michael@0: size_t size = sizeof(uint32_t); michael@0: for (size_t i = 0; i < vec.length(); i++) michael@0: size += vec[i].serializedSize(); michael@0: return size; michael@0: } michael@0: michael@0: template michael@0: uint8_t * michael@0: SerializeVector(uint8_t *cursor, const js::Vector &vec) michael@0: { michael@0: cursor = WriteScalar(cursor, vec.length()); michael@0: for (size_t i = 0; i < vec.length(); i++) michael@0: cursor = vec[i].serialize(cursor); michael@0: return cursor; michael@0: } michael@0: michael@0: template michael@0: const uint8_t * michael@0: DeserializeVector(ExclusiveContext *cx, const uint8_t *cursor, js::Vector *vec) michael@0: { michael@0: uint32_t length; michael@0: cursor = ReadScalar(cursor, &length); michael@0: if (!vec->resize(length)) michael@0: return nullptr; michael@0: for (size_t i = 0; i < vec->length(); i++) { michael@0: if (!(cursor = (*vec)[i].deserialize(cx, cursor))) michael@0: return nullptr; michael@0: } michael@0: return cursor; michael@0: } michael@0: michael@0: template michael@0: bool michael@0: CloneVector(ExclusiveContext *cx, const Vector &in, michael@0: Vector *out) michael@0: { michael@0: if (!out->resize(in.length())) michael@0: return false; michael@0: for (size_t i = 0; i < in.length(); i++) { michael@0: if (!in[i].clone(cx, &(*out)[i])) michael@0: return false; michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: template michael@0: size_t michael@0: SerializedPodVectorSize(const mozilla::VectorBase &vec) michael@0: { michael@0: return sizeof(uint32_t) + michael@0: vec.length() * sizeof(T); michael@0: } michael@0: michael@0: template michael@0: uint8_t * michael@0: SerializePodVector(uint8_t *cursor, const mozilla::VectorBase &vec) michael@0: { michael@0: cursor = WriteScalar(cursor, vec.length()); michael@0: cursor = WriteBytes(cursor, vec.begin(), vec.length() * sizeof(T)); michael@0: return cursor; michael@0: } michael@0: michael@0: template michael@0: const uint8_t * michael@0: DeserializePodVector(ExclusiveContext *cx, const uint8_t *cursor, michael@0: mozilla::VectorBase *vec) michael@0: { michael@0: uint32_t length; michael@0: cursor = ReadScalar(cursor, &length); michael@0: if (!vec->resize(length)) michael@0: return nullptr; michael@0: cursor = ReadBytes(cursor, vec->begin(), length * sizeof(T)); michael@0: return cursor; michael@0: } michael@0: michael@0: template michael@0: bool michael@0: ClonePodVector(ExclusiveContext *cx, const Vector &in, michael@0: Vector *out) michael@0: { michael@0: if (!out->resize(in.length())) michael@0: return false; michael@0: PodCopy(out->begin(), in.begin(), in.length()); michael@0: return true; michael@0: } michael@0: michael@0: uint8_t * michael@0: AsmJSModule::Global::serialize(uint8_t *cursor) const michael@0: { michael@0: cursor = WriteBytes(cursor, &pod, sizeof(pod)); michael@0: cursor = SerializeName(cursor, name_); michael@0: return cursor; michael@0: } michael@0: michael@0: size_t michael@0: AsmJSModule::Global::serializedSize() const michael@0: { michael@0: return sizeof(pod) + michael@0: SerializedNameSize(name_); michael@0: } michael@0: michael@0: const uint8_t * michael@0: AsmJSModule::Global::deserialize(ExclusiveContext *cx, const uint8_t *cursor) michael@0: { michael@0: (cursor = ReadBytes(cursor, &pod, sizeof(pod))) && michael@0: (cursor = DeserializeName(cx, cursor, &name_)); michael@0: return cursor; michael@0: } michael@0: michael@0: bool michael@0: AsmJSModule::Global::clone(ExclusiveContext *cx, Global *out) const michael@0: { michael@0: *out = *this; michael@0: return true; michael@0: } michael@0: michael@0: uint8_t * michael@0: AsmJSModule::Exit::serialize(uint8_t *cursor) const michael@0: { michael@0: cursor = WriteBytes(cursor, this, sizeof(*this)); michael@0: return cursor; michael@0: } michael@0: michael@0: size_t michael@0: AsmJSModule::Exit::serializedSize() const michael@0: { michael@0: return sizeof(*this); michael@0: } michael@0: michael@0: const uint8_t * michael@0: AsmJSModule::Exit::deserialize(ExclusiveContext *cx, const uint8_t *cursor) michael@0: { michael@0: cursor = ReadBytes(cursor, this, sizeof(*this)); michael@0: return cursor; michael@0: } michael@0: michael@0: bool michael@0: AsmJSModule::Exit::clone(ExclusiveContext *cx, Exit *out) const michael@0: { michael@0: *out = *this; michael@0: return true; michael@0: } michael@0: michael@0: uint8_t * michael@0: AsmJSModule::ExportedFunction::serialize(uint8_t *cursor) const michael@0: { michael@0: cursor = SerializeName(cursor, name_); michael@0: cursor = SerializeName(cursor, maybeFieldName_); michael@0: cursor = SerializePodVector(cursor, argCoercions_); michael@0: cursor = WriteBytes(cursor, &pod, sizeof(pod)); michael@0: return cursor; michael@0: } michael@0: michael@0: size_t michael@0: AsmJSModule::ExportedFunction::serializedSize() const michael@0: { michael@0: return SerializedNameSize(name_) + michael@0: SerializedNameSize(maybeFieldName_) + michael@0: sizeof(uint32_t) + michael@0: argCoercions_.length() * sizeof(argCoercions_[0]) + michael@0: sizeof(pod); michael@0: } michael@0: michael@0: const uint8_t * michael@0: AsmJSModule::ExportedFunction::deserialize(ExclusiveContext *cx, const uint8_t *cursor) michael@0: { michael@0: (cursor = DeserializeName(cx, cursor, &name_)) && michael@0: (cursor = DeserializeName(cx, cursor, &maybeFieldName_)) && michael@0: (cursor = DeserializePodVector(cx, cursor, &argCoercions_)) && michael@0: (cursor = ReadBytes(cursor, &pod, sizeof(pod))); michael@0: return cursor; michael@0: } michael@0: michael@0: bool michael@0: AsmJSModule::ExportedFunction::clone(ExclusiveContext *cx, ExportedFunction *out) const michael@0: { michael@0: out->name_ = name_; michael@0: out->maybeFieldName_ = maybeFieldName_; michael@0: michael@0: if (!ClonePodVector(cx, argCoercions_, &out->argCoercions_)) michael@0: return false; michael@0: michael@0: out->pod = pod; michael@0: return true; michael@0: } michael@0: michael@0: size_t michael@0: AsmJSModule::StaticLinkData::serializedSize() const michael@0: { michael@0: return sizeof(uint32_t) + michael@0: SerializedPodVectorSize(relativeLinks) + michael@0: SerializedPodVectorSize(absoluteLinks); michael@0: } michael@0: michael@0: uint8_t * michael@0: AsmJSModule::StaticLinkData::serialize(uint8_t *cursor) const michael@0: { michael@0: cursor = WriteScalar(cursor, interruptExitOffset); michael@0: cursor = SerializePodVector(cursor, relativeLinks); michael@0: cursor = SerializePodVector(cursor, absoluteLinks); michael@0: return cursor; michael@0: } michael@0: michael@0: const uint8_t * michael@0: AsmJSModule::StaticLinkData::deserialize(ExclusiveContext *cx, const uint8_t *cursor) michael@0: { michael@0: (cursor = ReadScalar(cursor, &interruptExitOffset)) && michael@0: (cursor = DeserializePodVector(cx, cursor, &relativeLinks)) && michael@0: (cursor = DeserializePodVector(cx, cursor, &absoluteLinks)); michael@0: return cursor; michael@0: } michael@0: michael@0: #if defined(MOZ_VTUNE) || defined(JS_ION_PERF) michael@0: size_t michael@0: AsmJSModule::ProfiledFunction::serializedSize() const michael@0: { michael@0: return SerializedNameSize(name) + michael@0: sizeof(pod); michael@0: } michael@0: michael@0: uint8_t * michael@0: AsmJSModule::ProfiledFunction::serialize(uint8_t *cursor) const michael@0: { michael@0: cursor = SerializeName(cursor, name); michael@0: cursor = WriteBytes(cursor, &pod, sizeof(pod)); michael@0: return cursor; michael@0: } michael@0: michael@0: const uint8_t * michael@0: AsmJSModule::ProfiledFunction::deserialize(ExclusiveContext *cx, const uint8_t *cursor) michael@0: { michael@0: (cursor = DeserializeName(cx, cursor, &name)) && michael@0: (cursor = ReadBytes(cursor, &pod, sizeof(pod))); michael@0: return cursor; michael@0: } michael@0: #endif michael@0: michael@0: bool michael@0: AsmJSModule::StaticLinkData::clone(ExclusiveContext *cx, StaticLinkData *out) const michael@0: { michael@0: out->interruptExitOffset = interruptExitOffset; michael@0: return ClonePodVector(cx, relativeLinks, &out->relativeLinks) && michael@0: ClonePodVector(cx, absoluteLinks, &out->absoluteLinks); michael@0: } michael@0: michael@0: size_t michael@0: AsmJSModule::StaticLinkData::sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const michael@0: { michael@0: return relativeLinks.sizeOfExcludingThis(mallocSizeOf) + michael@0: absoluteLinks.sizeOfExcludingThis(mallocSizeOf); michael@0: } michael@0: michael@0: size_t michael@0: AsmJSModule::serializedSize() const michael@0: { michael@0: return sizeof(pod) + michael@0: pod.codeBytes_ + michael@0: SerializedNameSize(globalArgumentName_) + michael@0: SerializedNameSize(importArgumentName_) + michael@0: SerializedNameSize(bufferArgumentName_) + michael@0: SerializedVectorSize(globals_) + michael@0: SerializedVectorSize(exits_) + michael@0: SerializedVectorSize(exports_) + michael@0: SerializedPodVectorSize(callSites_) + michael@0: SerializedVectorSize(functionNames_) + michael@0: SerializedPodVectorSize(heapAccesses_) + michael@0: #if defined(MOZ_VTUNE) || defined(JS_ION_PERF) michael@0: SerializedVectorSize(profiledFunctions_) + michael@0: #endif michael@0: staticLinkData_.serializedSize(); michael@0: } michael@0: michael@0: uint8_t * michael@0: AsmJSModule::serialize(uint8_t *cursor) const michael@0: { michael@0: cursor = WriteBytes(cursor, &pod, sizeof(pod)); michael@0: cursor = WriteBytes(cursor, code_, pod.codeBytes_); michael@0: cursor = SerializeName(cursor, globalArgumentName_); michael@0: cursor = SerializeName(cursor, importArgumentName_); michael@0: cursor = SerializeName(cursor, bufferArgumentName_); michael@0: cursor = SerializeVector(cursor, globals_); michael@0: cursor = SerializeVector(cursor, exits_); michael@0: cursor = SerializeVector(cursor, exports_); michael@0: cursor = SerializePodVector(cursor, callSites_); michael@0: cursor = SerializeVector(cursor, functionNames_); michael@0: cursor = SerializePodVector(cursor, heapAccesses_); michael@0: #if defined(MOZ_VTUNE) || defined(JS_ION_PERF) michael@0: cursor = SerializeVector(cursor, profiledFunctions_); michael@0: #endif michael@0: cursor = staticLinkData_.serialize(cursor); michael@0: return cursor; michael@0: } michael@0: michael@0: const uint8_t * michael@0: AsmJSModule::deserialize(ExclusiveContext *cx, const uint8_t *cursor) michael@0: { michael@0: // To avoid GC-during-deserialization corner cases, prevent atoms from michael@0: // being collected. michael@0: AutoKeepAtoms aka(cx->perThreadData); michael@0: michael@0: (cursor = ReadBytes(cursor, &pod, sizeof(pod))) && michael@0: (code_ = AllocateExecutableMemory(cx, pod.totalBytes_)) && michael@0: (cursor = ReadBytes(cursor, code_, pod.codeBytes_)) && michael@0: (cursor = DeserializeName(cx, cursor, &globalArgumentName_)) && michael@0: (cursor = DeserializeName(cx, cursor, &importArgumentName_)) && michael@0: (cursor = DeserializeName(cx, cursor, &bufferArgumentName_)) && michael@0: (cursor = DeserializeVector(cx, cursor, &globals_)) && michael@0: (cursor = DeserializeVector(cx, cursor, &exits_)) && michael@0: (cursor = DeserializeVector(cx, cursor, &exports_)) && michael@0: (cursor = DeserializePodVector(cx, cursor, &callSites_)) && michael@0: (cursor = DeserializeVector(cx, cursor, &functionNames_)) && michael@0: (cursor = DeserializePodVector(cx, cursor, &heapAccesses_)) && michael@0: #if defined(MOZ_VTUNE) || defined(JS_ION_PERF) michael@0: (cursor = DeserializeVector(cx, cursor, &profiledFunctions_)) && michael@0: #endif michael@0: (cursor = staticLinkData_.deserialize(cx, cursor)); michael@0: michael@0: loadedFromCache_ = true; michael@0: michael@0: return cursor; michael@0: } michael@0: michael@0: // When a module is cloned, we memcpy its executable code. If, right before or michael@0: // during the clone, another thread calls AsmJSModule::protectCode() then the michael@0: // executable code will become inaccessible. In theory, we could take away only michael@0: // PROT_EXEC, but this seems to break emulators. michael@0: class AutoUnprotectCodeForClone michael@0: { michael@0: JSRuntime *rt_; michael@0: JSRuntime::AutoLockForInterrupt lock_; michael@0: const AsmJSModule &module_; michael@0: const bool protectedBefore_; michael@0: michael@0: public: michael@0: AutoUnprotectCodeForClone(JSContext *cx, const AsmJSModule &module) michael@0: : rt_(cx->runtime()), michael@0: lock_(rt_), michael@0: module_(module), michael@0: protectedBefore_(module_.codeIsProtected(rt_)) michael@0: { michael@0: if (protectedBefore_) michael@0: module_.unprotectCode(rt_); michael@0: } michael@0: michael@0: ~AutoUnprotectCodeForClone() michael@0: { michael@0: if (protectedBefore_) michael@0: module_.protectCode(rt_); michael@0: } michael@0: }; michael@0: michael@0: bool michael@0: AsmJSModule::clone(JSContext *cx, ScopedJSDeletePtr *moduleOut) const michael@0: { michael@0: AutoUnprotectCodeForClone cloneGuard(cx, *this); michael@0: michael@0: *moduleOut = cx->new_(scriptSource_, funcStart_, offsetToEndOfUseAsm_, pod.strict_); michael@0: if (!*moduleOut) michael@0: return false; michael@0: michael@0: AsmJSModule &out = **moduleOut; michael@0: michael@0: // Mirror the order of serialize/deserialize in cloning: michael@0: michael@0: out.pod = pod; michael@0: michael@0: out.code_ = AllocateExecutableMemory(cx, pod.totalBytes_); michael@0: if (!out.code_) michael@0: return false; michael@0: michael@0: memcpy(out.code_, code_, pod.codeBytes_); michael@0: michael@0: out.globalArgumentName_ = globalArgumentName_; michael@0: out.importArgumentName_ = importArgumentName_; michael@0: out.bufferArgumentName_ = bufferArgumentName_; michael@0: michael@0: if (!CloneVector(cx, globals_, &out.globals_) || michael@0: !CloneVector(cx, exits_, &out.exits_) || michael@0: !CloneVector(cx, exports_, &out.exports_) || michael@0: !ClonePodVector(cx, callSites_, &out.callSites_) || michael@0: !CloneVector(cx, functionNames_, &out.functionNames_) || michael@0: !ClonePodVector(cx, heapAccesses_, &out.heapAccesses_) || michael@0: !staticLinkData_.clone(cx, &out.staticLinkData_)) michael@0: { michael@0: return false; michael@0: } michael@0: michael@0: out.loadedFromCache_ = loadedFromCache_; michael@0: michael@0: // We already know the exact extent of areas that need to be patched, just make sure we michael@0: // flush all of them at once. michael@0: out.setAutoFlushICacheRange(); michael@0: michael@0: out.restoreToInitialState(maybeHeap_, cx); michael@0: return true; michael@0: } michael@0: michael@0: void michael@0: AsmJSModule::protectCode(JSRuntime *rt) const michael@0: { michael@0: JS_ASSERT(rt->currentThreadOwnsInterruptLock()); michael@0: michael@0: codeIsProtected_ = true; michael@0: michael@0: if (!pod.functionBytes_) michael@0: return; michael@0: michael@0: // Technically, we should be able to only take away the execute permissions, michael@0: // however this seems to break our emulators which don't always check michael@0: // execute permissions while executing code. michael@0: #if defined(XP_WIN) michael@0: DWORD oldProtect; michael@0: if (!VirtualProtect(codeBase(), functionBytes(), PAGE_NOACCESS, &oldProtect)) michael@0: MOZ_CRASH(); michael@0: #else // assume Unix michael@0: if (mprotect(codeBase(), functionBytes(), PROT_NONE)) michael@0: MOZ_CRASH(); michael@0: #endif michael@0: } michael@0: michael@0: void michael@0: AsmJSModule::unprotectCode(JSRuntime *rt) const michael@0: { michael@0: JS_ASSERT(rt->currentThreadOwnsInterruptLock()); michael@0: michael@0: codeIsProtected_ = false; michael@0: michael@0: if (!pod.functionBytes_) michael@0: return; michael@0: michael@0: #if defined(XP_WIN) michael@0: DWORD oldProtect; michael@0: if (!VirtualProtect(codeBase(), functionBytes(), PAGE_EXECUTE_READWRITE, &oldProtect)) michael@0: MOZ_CRASH(); michael@0: #else // assume Unix michael@0: if (mprotect(codeBase(), functionBytes(), PROT_READ | PROT_WRITE | PROT_EXEC)) michael@0: MOZ_CRASH(); michael@0: #endif michael@0: } michael@0: michael@0: bool michael@0: AsmJSModule::codeIsProtected(JSRuntime *rt) const michael@0: { michael@0: JS_ASSERT(rt->currentThreadOwnsInterruptLock()); michael@0: return codeIsProtected_; michael@0: } michael@0: michael@0: static bool michael@0: GetCPUID(uint32_t *cpuId) michael@0: { michael@0: enum Arch { michael@0: X86 = 0x1, michael@0: X64 = 0x2, michael@0: ARM = 0x3, michael@0: ARCH_BITS = 2 michael@0: }; michael@0: michael@0: #if defined(JS_CODEGEN_X86) michael@0: JS_ASSERT(uint32_t(JSC::MacroAssembler::getSSEState()) <= (UINT32_MAX >> ARCH_BITS)); michael@0: *cpuId = X86 | (JSC::MacroAssembler::getSSEState() << ARCH_BITS); michael@0: return true; michael@0: #elif defined(JS_CODEGEN_X64) michael@0: JS_ASSERT(uint32_t(JSC::MacroAssembler::getSSEState()) <= (UINT32_MAX >> ARCH_BITS)); michael@0: *cpuId = X64 | (JSC::MacroAssembler::getSSEState() << ARCH_BITS); michael@0: return true; michael@0: #elif defined(JS_CODEGEN_ARM) michael@0: JS_ASSERT(GetARMFlags() <= (UINT32_MAX >> ARCH_BITS)); michael@0: *cpuId = ARM | (GetARMFlags() << ARCH_BITS); michael@0: return true; michael@0: #else michael@0: return false; michael@0: #endif michael@0: } michael@0: michael@0: class MachineId michael@0: { michael@0: uint32_t cpuId_; michael@0: JS::BuildIdCharVector buildId_; michael@0: michael@0: public: michael@0: bool extractCurrentState(ExclusiveContext *cx) { michael@0: if (!cx->asmJSCacheOps().buildId) michael@0: return false; michael@0: if (!cx->asmJSCacheOps().buildId(&buildId_)) michael@0: return false; michael@0: if (!GetCPUID(&cpuId_)) michael@0: return false; michael@0: return true; michael@0: } michael@0: michael@0: size_t serializedSize() const { michael@0: return sizeof(uint32_t) + michael@0: SerializedPodVectorSize(buildId_); michael@0: } michael@0: michael@0: uint8_t *serialize(uint8_t *cursor) const { michael@0: cursor = WriteScalar(cursor, cpuId_); michael@0: cursor = SerializePodVector(cursor, buildId_); michael@0: return cursor; michael@0: } michael@0: michael@0: const uint8_t *deserialize(ExclusiveContext *cx, const uint8_t *cursor) { michael@0: (cursor = ReadScalar(cursor, &cpuId_)) && michael@0: (cursor = DeserializePodVector(cx, cursor, &buildId_)); michael@0: return cursor; michael@0: } michael@0: michael@0: bool operator==(const MachineId &rhs) const { michael@0: return cpuId_ == rhs.cpuId_ && michael@0: buildId_.length() == rhs.buildId_.length() && michael@0: PodEqual(buildId_.begin(), rhs.buildId_.begin(), buildId_.length()); michael@0: } michael@0: bool operator!=(const MachineId &rhs) const { michael@0: return !(*this == rhs); michael@0: } michael@0: }; michael@0: michael@0: struct PropertyNameWrapper michael@0: { michael@0: PropertyName *name; michael@0: michael@0: PropertyNameWrapper() michael@0: : name(nullptr) michael@0: {} michael@0: PropertyNameWrapper(PropertyName *name) michael@0: : name(name) michael@0: {} michael@0: size_t serializedSize() const { michael@0: return SerializedNameSize(name); michael@0: } michael@0: uint8_t *serialize(uint8_t *cursor) const { michael@0: return SerializeName(cursor, name); michael@0: } michael@0: const uint8_t *deserialize(ExclusiveContext *cx, const uint8_t *cursor) { michael@0: return DeserializeName(cx, cursor, &name); michael@0: } michael@0: }; michael@0: michael@0: class ModuleChars michael@0: { michael@0: protected: michael@0: uint32_t isFunCtor_; michael@0: js::Vector funCtorArgs_; michael@0: michael@0: public: michael@0: static uint32_t beginOffset(AsmJSParser &parser) { michael@0: return parser.pc->maybeFunction->pn_pos.begin; michael@0: } michael@0: michael@0: static uint32_t endOffset(AsmJSParser &parser) { michael@0: return parser.tokenStream.peekTokenPos().end; michael@0: } michael@0: }; michael@0: michael@0: class ModuleCharsForStore : ModuleChars michael@0: { michael@0: uint32_t uncompressedSize_; michael@0: uint32_t compressedSize_; michael@0: js::Vector compressedBuffer_; michael@0: michael@0: public: michael@0: bool init(AsmJSParser &parser) { michael@0: JS_ASSERT(beginOffset(parser) < endOffset(parser)); michael@0: michael@0: uncompressedSize_ = (endOffset(parser) - beginOffset(parser)) * sizeof(jschar); michael@0: size_t maxCompressedSize = LZ4::maxCompressedSize(uncompressedSize_); michael@0: if (maxCompressedSize < uncompressedSize_) michael@0: return false; michael@0: michael@0: if (!compressedBuffer_.resize(maxCompressedSize)) michael@0: return false; michael@0: michael@0: const jschar *chars = parser.tokenStream.rawBase() + beginOffset(parser); michael@0: const char *source = reinterpret_cast(chars); michael@0: size_t compressedSize = LZ4::compress(source, uncompressedSize_, compressedBuffer_.begin()); michael@0: if (!compressedSize || compressedSize > UINT32_MAX) michael@0: return false; michael@0: michael@0: compressedSize_ = compressedSize; michael@0: michael@0: // For a function statement or named function expression: michael@0: // function f(x,y,z) { abc } michael@0: // the range [beginOffset, endOffset) captures the source: michael@0: // f(x,y,z) { abc } michael@0: // An unnamed function expression captures the same thing, sans 'f'. michael@0: // Since asm.js modules do not contain any free variables, equality of michael@0: // [beginOffset, endOffset) is sufficient to guarantee identical code michael@0: // generation, modulo MachineId. michael@0: // michael@0: // For functions created with 'new Function', function arguments are michael@0: // not present in the source so we must manually explicitly serialize michael@0: // and match the formals as a Vector of PropertyName. michael@0: isFunCtor_ = parser.pc->isFunctionConstructorBody(); michael@0: if (isFunCtor_) { michael@0: unsigned numArgs; michael@0: ParseNode *arg = FunctionArgsList(parser.pc->maybeFunction, &numArgs); michael@0: for (unsigned i = 0; i < numArgs; i++, arg = arg->pn_next) { michael@0: if (!funCtorArgs_.append(arg->name())) michael@0: return false; michael@0: } michael@0: } michael@0: michael@0: return true; michael@0: } michael@0: michael@0: size_t serializedSize() const { michael@0: return sizeof(uint32_t) + michael@0: sizeof(uint32_t) + michael@0: compressedSize_ + michael@0: sizeof(uint32_t) + michael@0: (isFunCtor_ ? SerializedVectorSize(funCtorArgs_) : 0); michael@0: } michael@0: michael@0: uint8_t *serialize(uint8_t *cursor) const { michael@0: cursor = WriteScalar(cursor, uncompressedSize_); michael@0: cursor = WriteScalar(cursor, compressedSize_); michael@0: cursor = WriteBytes(cursor, compressedBuffer_.begin(), compressedSize_); michael@0: cursor = WriteScalar(cursor, isFunCtor_); michael@0: if (isFunCtor_) michael@0: cursor = SerializeVector(cursor, funCtorArgs_); michael@0: return cursor; michael@0: } michael@0: }; michael@0: michael@0: class ModuleCharsForLookup : ModuleChars michael@0: { michael@0: js::Vector chars_; michael@0: michael@0: public: michael@0: const uint8_t *deserialize(ExclusiveContext *cx, const uint8_t *cursor) { michael@0: uint32_t uncompressedSize; michael@0: cursor = ReadScalar(cursor, &uncompressedSize); michael@0: michael@0: uint32_t compressedSize; michael@0: cursor = ReadScalar(cursor, &compressedSize); michael@0: michael@0: if (!chars_.resize(uncompressedSize / sizeof(jschar))) michael@0: return nullptr; michael@0: michael@0: const char *source = reinterpret_cast(cursor); michael@0: char *dest = reinterpret_cast(chars_.begin()); michael@0: if (!LZ4::decompress(source, dest, uncompressedSize)) michael@0: return nullptr; michael@0: michael@0: cursor += compressedSize; michael@0: michael@0: cursor = ReadScalar(cursor, &isFunCtor_); michael@0: if (isFunCtor_) michael@0: cursor = DeserializeVector(cx, cursor, &funCtorArgs_); michael@0: michael@0: return cursor; michael@0: } michael@0: michael@0: bool match(AsmJSParser &parser) const { michael@0: const jschar *parseBegin = parser.tokenStream.rawBase() + beginOffset(parser); michael@0: const jschar *parseLimit = parser.tokenStream.rawLimit(); michael@0: JS_ASSERT(parseLimit >= parseBegin); michael@0: if (uint32_t(parseLimit - parseBegin) < chars_.length()) michael@0: return false; michael@0: if (!PodEqual(chars_.begin(), parseBegin, chars_.length())) michael@0: return false; michael@0: if (isFunCtor_ != parser.pc->isFunctionConstructorBody()) michael@0: return false; michael@0: if (isFunCtor_) { michael@0: // For function statements, the closing } is included as the last michael@0: // character of the matched source. For Function constructor, michael@0: // parsing terminates with EOF which we must explicitly check. This michael@0: // prevents michael@0: // new Function('"use asm"; function f() {} return f') michael@0: // from incorrectly matching michael@0: // new Function('"use asm"; function f() {} return ff') michael@0: if (parseBegin + chars_.length() != parseLimit) michael@0: return false; michael@0: unsigned numArgs; michael@0: ParseNode *arg = FunctionArgsList(parser.pc->maybeFunction, &numArgs); michael@0: if (funCtorArgs_.length() != numArgs) michael@0: return false; michael@0: for (unsigned i = 0; i < funCtorArgs_.length(); i++, arg = arg->pn_next) { michael@0: if (funCtorArgs_[i].name != arg->name()) michael@0: return false; michael@0: } michael@0: } michael@0: return true; michael@0: } michael@0: }; michael@0: michael@0: struct ScopedCacheEntryOpenedForWrite michael@0: { michael@0: ExclusiveContext *cx; michael@0: const size_t serializedSize; michael@0: uint8_t *memory; michael@0: intptr_t handle; michael@0: michael@0: ScopedCacheEntryOpenedForWrite(ExclusiveContext *cx, size_t serializedSize) michael@0: : cx(cx), serializedSize(serializedSize), memory(nullptr), handle(-1) michael@0: {} michael@0: michael@0: ~ScopedCacheEntryOpenedForWrite() { michael@0: if (memory) michael@0: cx->asmJSCacheOps().closeEntryForWrite(cx->global(), serializedSize, memory, handle); michael@0: } michael@0: }; michael@0: michael@0: bool michael@0: js::StoreAsmJSModuleInCache(AsmJSParser &parser, michael@0: const AsmJSModule &module, michael@0: ExclusiveContext *cx) michael@0: { michael@0: MachineId machineId; michael@0: if (!machineId.extractCurrentState(cx)) michael@0: return false; michael@0: michael@0: ModuleCharsForStore moduleChars; michael@0: if (!moduleChars.init(parser)) michael@0: return false; michael@0: michael@0: size_t serializedSize = machineId.serializedSize() + michael@0: moduleChars.serializedSize() + michael@0: module.serializedSize(); michael@0: michael@0: JS::OpenAsmJSCacheEntryForWriteOp open = cx->asmJSCacheOps().openEntryForWrite; michael@0: if (!open) michael@0: return false; michael@0: michael@0: const jschar *begin = parser.tokenStream.rawBase() + ModuleChars::beginOffset(parser); michael@0: const jschar *end = parser.tokenStream.rawBase() + ModuleChars::endOffset(parser); michael@0: bool installed = parser.options().installedFile; michael@0: michael@0: ScopedCacheEntryOpenedForWrite entry(cx, serializedSize); michael@0: if (!open(cx->global(), installed, begin, end, entry.serializedSize, michael@0: &entry.memory, &entry.handle)) { michael@0: return false; michael@0: } michael@0: michael@0: uint8_t *cursor = entry.memory; michael@0: cursor = machineId.serialize(cursor); michael@0: cursor = moduleChars.serialize(cursor); michael@0: cursor = module.serialize(cursor); michael@0: michael@0: JS_ASSERT(cursor == entry.memory + serializedSize); michael@0: return true; michael@0: } michael@0: michael@0: struct ScopedCacheEntryOpenedForRead michael@0: { michael@0: ExclusiveContext *cx; michael@0: size_t serializedSize; michael@0: const uint8_t *memory; michael@0: intptr_t handle; michael@0: michael@0: ScopedCacheEntryOpenedForRead(ExclusiveContext *cx) michael@0: : cx(cx), serializedSize(0), memory(nullptr), handle(0) michael@0: {} michael@0: michael@0: ~ScopedCacheEntryOpenedForRead() { michael@0: if (memory) michael@0: cx->asmJSCacheOps().closeEntryForRead(cx->global(), serializedSize, memory, handle); michael@0: } michael@0: }; michael@0: michael@0: bool michael@0: js::LookupAsmJSModuleInCache(ExclusiveContext *cx, michael@0: AsmJSParser &parser, michael@0: ScopedJSDeletePtr *moduleOut, michael@0: ScopedJSFreePtr *compilationTimeReport) michael@0: { michael@0: int64_t usecBefore = PRMJ_Now(); michael@0: michael@0: MachineId machineId; michael@0: if (!machineId.extractCurrentState(cx)) michael@0: return true; michael@0: michael@0: JS::OpenAsmJSCacheEntryForReadOp open = cx->asmJSCacheOps().openEntryForRead; michael@0: if (!open) michael@0: return true; michael@0: michael@0: const jschar *begin = parser.tokenStream.rawBase() + ModuleChars::beginOffset(parser); michael@0: const jschar *limit = parser.tokenStream.rawLimit(); michael@0: michael@0: ScopedCacheEntryOpenedForRead entry(cx); michael@0: if (!open(cx->global(), begin, limit, &entry.serializedSize, &entry.memory, &entry.handle)) michael@0: return true; michael@0: michael@0: const uint8_t *cursor = entry.memory; michael@0: michael@0: MachineId cachedMachineId; michael@0: cursor = cachedMachineId.deserialize(cx, cursor); michael@0: if (!cursor) michael@0: return false; michael@0: if (machineId != cachedMachineId) michael@0: return true; michael@0: michael@0: ModuleCharsForLookup moduleChars; michael@0: cursor = moduleChars.deserialize(cx, cursor); michael@0: if (!moduleChars.match(parser)) michael@0: return true; michael@0: michael@0: uint32_t funcStart = parser.pc->maybeFunction->pn_body->pn_pos.begin; michael@0: uint32_t offsetToEndOfUseAsm = parser.tokenStream.currentToken().pos.end; michael@0: bool strict = parser.pc->sc->strict && !parser.pc->sc->hasExplicitUseStrict(); michael@0: ScopedJSDeletePtr module( michael@0: cx->new_(parser.ss, funcStart, offsetToEndOfUseAsm, strict)); michael@0: if (!module) michael@0: return false; michael@0: cursor = module->deserialize(cx, cursor); michael@0: michael@0: // No need to flush the instruction cache now, it will be flushed when dynamically linking. michael@0: AutoFlushICache afc("LookupAsmJSModuleInCache", /* inhibit= */ true); michael@0: // We already know the exact extent of areas that need to be patched, just make sure we michael@0: // flush all of them at once. michael@0: module->setAutoFlushICacheRange(); michael@0: michael@0: if (!cursor) michael@0: return false; michael@0: michael@0: bool atEnd = cursor == entry.memory + entry.serializedSize; michael@0: MOZ_ASSERT(atEnd, "Corrupt cache file"); michael@0: if (!atEnd) michael@0: return true; michael@0: michael@0: module->staticallyLink(cx); michael@0: michael@0: parser.tokenStream.advance(module->funcEndBeforeCurly()); michael@0: michael@0: int64_t usecAfter = PRMJ_Now(); michael@0: int ms = (usecAfter - usecBefore) / PRMJ_USEC_PER_MSEC; michael@0: *compilationTimeReport = JS_smprintf("loaded from cache in %dms", ms); michael@0: *moduleOut = module.forget(); michael@0: return true; michael@0: }