js/src/jit/AsmJSModule.cpp

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

mercurial