|
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 } |