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.

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

mercurial