|
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 #ifndef jit_AsmJSModule_h |
|
8 #define jit_AsmJSModule_h |
|
9 |
|
10 #ifdef JS_ION |
|
11 |
|
12 #include "mozilla/Move.h" |
|
13 #include "mozilla/PodOperations.h" |
|
14 |
|
15 #include "jsscript.h" |
|
16 |
|
17 #include "gc/Marking.h" |
|
18 #include "jit/AsmJS.h" |
|
19 #include "jit/IonMacroAssembler.h" |
|
20 #ifdef JS_ION_PERF |
|
21 # include "jit/PerfSpewer.h" |
|
22 #endif |
|
23 #include "jit/RegisterSets.h" |
|
24 #include "vm/TypedArrayObject.h" |
|
25 |
|
26 namespace js { |
|
27 |
|
28 // These EcmaScript-defined coercions form the basis of the asm.js type system. |
|
29 enum AsmJSCoercion |
|
30 { |
|
31 AsmJS_ToInt32, |
|
32 AsmJS_ToNumber, |
|
33 AsmJS_FRound |
|
34 }; |
|
35 |
|
36 // The asm.js spec recognizes this set of builtin Math functions. |
|
37 enum AsmJSMathBuiltinFunction |
|
38 { |
|
39 AsmJSMathBuiltin_sin, AsmJSMathBuiltin_cos, AsmJSMathBuiltin_tan, |
|
40 AsmJSMathBuiltin_asin, AsmJSMathBuiltin_acos, AsmJSMathBuiltin_atan, |
|
41 AsmJSMathBuiltin_ceil, AsmJSMathBuiltin_floor, AsmJSMathBuiltin_exp, |
|
42 AsmJSMathBuiltin_log, AsmJSMathBuiltin_pow, AsmJSMathBuiltin_sqrt, |
|
43 AsmJSMathBuiltin_abs, AsmJSMathBuiltin_atan2, AsmJSMathBuiltin_imul, |
|
44 AsmJSMathBuiltin_fround, AsmJSMathBuiltin_min, AsmJSMathBuiltin_max |
|
45 }; |
|
46 |
|
47 // An asm.js module represents the collection of functions nested inside a |
|
48 // single outer "use asm" function. For example, this asm.js module: |
|
49 // function() { "use asm"; function f() {} function g() {} return f } |
|
50 // contains the functions 'f' and 'g'. |
|
51 // |
|
52 // An asm.js module contains both the jit-code produced by compiling all the |
|
53 // functions in the module as well all the data required to perform the |
|
54 // link-time validation step in the asm.js spec. |
|
55 // |
|
56 // NB: this means that AsmJSModule must be GC-safe. |
|
57 class AsmJSModule |
|
58 { |
|
59 public: |
|
60 class Global |
|
61 { |
|
62 public: |
|
63 enum Which { Variable, FFI, ArrayView, MathBuiltinFunction, Constant }; |
|
64 enum VarInitKind { InitConstant, InitImport }; |
|
65 enum ConstantKind { GlobalConstant, MathConstant }; |
|
66 |
|
67 private: |
|
68 struct Pod { |
|
69 Which which_; |
|
70 union { |
|
71 struct { |
|
72 uint32_t index_; |
|
73 VarInitKind initKind_; |
|
74 AsmJSCoercion coercion_; |
|
75 union { |
|
76 Value constant_; // will only contain int32/double |
|
77 } init; |
|
78 } var; |
|
79 uint32_t ffiIndex_; |
|
80 ArrayBufferView::ViewType viewType_; |
|
81 AsmJSMathBuiltinFunction mathBuiltinFunc_; |
|
82 struct { |
|
83 ConstantKind kind_; |
|
84 double value_; |
|
85 } constant; |
|
86 } u; |
|
87 } pod; |
|
88 PropertyName *name_; |
|
89 |
|
90 friend class AsmJSModule; |
|
91 |
|
92 Global(Which which, PropertyName *name) { |
|
93 pod.which_ = which; |
|
94 name_ = name; |
|
95 JS_ASSERT_IF(name_, name_->isTenured()); |
|
96 } |
|
97 |
|
98 void trace(JSTracer *trc) { |
|
99 if (name_) |
|
100 MarkStringUnbarriered(trc, &name_, "asm.js global name"); |
|
101 JS_ASSERT_IF(pod.which_ == Variable && pod.u.var.initKind_ == InitConstant, |
|
102 !pod.u.var.init.constant_.isMarkable()); |
|
103 } |
|
104 |
|
105 public: |
|
106 Global() {} |
|
107 Which which() const { |
|
108 return pod.which_; |
|
109 } |
|
110 uint32_t varIndex() const { |
|
111 JS_ASSERT(pod.which_ == Variable); |
|
112 return pod.u.var.index_; |
|
113 } |
|
114 VarInitKind varInitKind() const { |
|
115 JS_ASSERT(pod.which_ == Variable); |
|
116 return pod.u.var.initKind_; |
|
117 } |
|
118 const Value &varInitConstant() const { |
|
119 JS_ASSERT(pod.which_ == Variable); |
|
120 JS_ASSERT(pod.u.var.initKind_ == InitConstant); |
|
121 return pod.u.var.init.constant_; |
|
122 } |
|
123 AsmJSCoercion varInitCoercion() const { |
|
124 JS_ASSERT(pod.which_ == Variable); |
|
125 return pod.u.var.coercion_; |
|
126 } |
|
127 PropertyName *varImportField() const { |
|
128 JS_ASSERT(pod.which_ == Variable); |
|
129 JS_ASSERT(pod.u.var.initKind_ == InitImport); |
|
130 return name_; |
|
131 } |
|
132 PropertyName *ffiField() const { |
|
133 JS_ASSERT(pod.which_ == FFI); |
|
134 return name_; |
|
135 } |
|
136 uint32_t ffiIndex() const { |
|
137 JS_ASSERT(pod.which_ == FFI); |
|
138 return pod.u.ffiIndex_; |
|
139 } |
|
140 PropertyName *viewName() const { |
|
141 JS_ASSERT(pod.which_ == ArrayView); |
|
142 return name_; |
|
143 } |
|
144 ArrayBufferView::ViewType viewType() const { |
|
145 JS_ASSERT(pod.which_ == ArrayView); |
|
146 return pod.u.viewType_; |
|
147 } |
|
148 PropertyName *mathName() const { |
|
149 JS_ASSERT(pod.which_ == MathBuiltinFunction); |
|
150 return name_; |
|
151 } |
|
152 AsmJSMathBuiltinFunction mathBuiltinFunction() const { |
|
153 JS_ASSERT(pod.which_ == MathBuiltinFunction); |
|
154 return pod.u.mathBuiltinFunc_; |
|
155 } |
|
156 PropertyName *constantName() const { |
|
157 JS_ASSERT(pod.which_ == Constant); |
|
158 return name_; |
|
159 } |
|
160 ConstantKind constantKind() const { |
|
161 JS_ASSERT(pod.which_ == Constant); |
|
162 return pod.u.constant.kind_; |
|
163 } |
|
164 double constantValue() const { |
|
165 JS_ASSERT(pod.which_ == Constant); |
|
166 return pod.u.constant.value_; |
|
167 } |
|
168 |
|
169 size_t serializedSize() const; |
|
170 uint8_t *serialize(uint8_t *cursor) const; |
|
171 const uint8_t *deserialize(ExclusiveContext *cx, const uint8_t *cursor); |
|
172 bool clone(ExclusiveContext *cx, Global *out) const; |
|
173 }; |
|
174 |
|
175 class Exit |
|
176 { |
|
177 unsigned ffiIndex_; |
|
178 unsigned globalDataOffset_; |
|
179 unsigned interpCodeOffset_; |
|
180 unsigned ionCodeOffset_; |
|
181 |
|
182 friend class AsmJSModule; |
|
183 |
|
184 public: |
|
185 Exit() {} |
|
186 Exit(unsigned ffiIndex, unsigned globalDataOffset) |
|
187 : ffiIndex_(ffiIndex), globalDataOffset_(globalDataOffset), |
|
188 interpCodeOffset_(0), ionCodeOffset_(0) |
|
189 {} |
|
190 unsigned ffiIndex() const { |
|
191 return ffiIndex_; |
|
192 } |
|
193 unsigned globalDataOffset() const { |
|
194 return globalDataOffset_; |
|
195 } |
|
196 void initInterpOffset(unsigned off) { |
|
197 JS_ASSERT(!interpCodeOffset_); |
|
198 interpCodeOffset_ = off; |
|
199 } |
|
200 void initIonOffset(unsigned off) { |
|
201 JS_ASSERT(!ionCodeOffset_); |
|
202 ionCodeOffset_ = off; |
|
203 } |
|
204 void updateOffsets(jit::MacroAssembler &masm) { |
|
205 interpCodeOffset_ = masm.actualOffset(interpCodeOffset_); |
|
206 ionCodeOffset_ = masm.actualOffset(ionCodeOffset_); |
|
207 } |
|
208 |
|
209 size_t serializedSize() const; |
|
210 uint8_t *serialize(uint8_t *cursor) const; |
|
211 const uint8_t *deserialize(ExclusiveContext *cx, const uint8_t *cursor); |
|
212 bool clone(ExclusiveContext *cx, Exit *out) const; |
|
213 }; |
|
214 typedef int32_t (*CodePtr)(uint64_t *args, uint8_t *global); |
|
215 |
|
216 typedef Vector<AsmJSCoercion, 0, SystemAllocPolicy> ArgCoercionVector; |
|
217 |
|
218 enum ReturnType { Return_Int32, Return_Double, Return_Void }; |
|
219 |
|
220 class ExportedFunction |
|
221 { |
|
222 PropertyName *name_; |
|
223 PropertyName *maybeFieldName_; |
|
224 ArgCoercionVector argCoercions_; |
|
225 struct Pod { |
|
226 ReturnType returnType_; |
|
227 uint32_t codeOffset_; |
|
228 // These two fields are offsets to the beginning of the ScriptSource |
|
229 // of the module, and thus invariant under serialization (unlike |
|
230 // absolute offsets into ScriptSource). |
|
231 uint32_t startOffsetInModule_; |
|
232 uint32_t endOffsetInModule_; |
|
233 } pod; |
|
234 |
|
235 friend class AsmJSModule; |
|
236 |
|
237 ExportedFunction(PropertyName *name, |
|
238 uint32_t startOffsetInModule, uint32_t endOffsetInModule, |
|
239 PropertyName *maybeFieldName, |
|
240 ArgCoercionVector &&argCoercions, |
|
241 ReturnType returnType) |
|
242 { |
|
243 name_ = name; |
|
244 maybeFieldName_ = maybeFieldName; |
|
245 argCoercions_ = mozilla::Move(argCoercions); |
|
246 pod.returnType_ = returnType; |
|
247 pod.codeOffset_ = UINT32_MAX; |
|
248 pod.startOffsetInModule_ = startOffsetInModule; |
|
249 pod.endOffsetInModule_ = endOffsetInModule; |
|
250 JS_ASSERT_IF(maybeFieldName_, name_->isTenured()); |
|
251 } |
|
252 |
|
253 void trace(JSTracer *trc) { |
|
254 MarkStringUnbarriered(trc, &name_, "asm.js export name"); |
|
255 if (maybeFieldName_) |
|
256 MarkStringUnbarriered(trc, &maybeFieldName_, "asm.js export field"); |
|
257 } |
|
258 |
|
259 public: |
|
260 ExportedFunction() {} |
|
261 ExportedFunction(ExportedFunction &&rhs) { |
|
262 name_ = rhs.name_; |
|
263 maybeFieldName_ = rhs.maybeFieldName_; |
|
264 argCoercions_ = mozilla::Move(rhs.argCoercions_); |
|
265 pod = rhs.pod; |
|
266 } |
|
267 void updateCodeOffset(jit::MacroAssembler &masm) { |
|
268 pod.codeOffset_ = masm.actualOffset(pod.codeOffset_); |
|
269 } |
|
270 |
|
271 void initCodeOffset(unsigned off) { |
|
272 JS_ASSERT(pod.codeOffset_ == UINT32_MAX); |
|
273 pod.codeOffset_ = off; |
|
274 } |
|
275 |
|
276 PropertyName *name() const { |
|
277 return name_; |
|
278 } |
|
279 uint32_t startOffsetInModule() const { |
|
280 return pod.startOffsetInModule_; |
|
281 } |
|
282 uint32_t endOffsetInModule() const { |
|
283 return pod.endOffsetInModule_; |
|
284 } |
|
285 PropertyName *maybeFieldName() const { |
|
286 return maybeFieldName_; |
|
287 } |
|
288 unsigned numArgs() const { |
|
289 return argCoercions_.length(); |
|
290 } |
|
291 AsmJSCoercion argCoercion(unsigned i) const { |
|
292 return argCoercions_[i]; |
|
293 } |
|
294 ReturnType returnType() const { |
|
295 return pod.returnType_; |
|
296 } |
|
297 |
|
298 size_t serializedSize() const; |
|
299 uint8_t *serialize(uint8_t *cursor) const; |
|
300 const uint8_t *deserialize(ExclusiveContext *cx, const uint8_t *cursor); |
|
301 bool clone(ExclusiveContext *cx, ExportedFunction *out) const; |
|
302 }; |
|
303 |
|
304 class Name |
|
305 { |
|
306 PropertyName *name_; |
|
307 public: |
|
308 Name() : name_(nullptr) {} |
|
309 Name(PropertyName *name) : name_(name) {} |
|
310 PropertyName *name() const { return name_; } |
|
311 PropertyName *&name() { return name_; } |
|
312 size_t serializedSize() const; |
|
313 uint8_t *serialize(uint8_t *cursor) const; |
|
314 const uint8_t *deserialize(ExclusiveContext *cx, const uint8_t *cursor); |
|
315 bool clone(ExclusiveContext *cx, Name *out) const; |
|
316 }; |
|
317 |
|
318 #if defined(MOZ_VTUNE) || defined(JS_ION_PERF) |
|
319 // Function information to add to the VTune JIT profiler following linking. |
|
320 struct ProfiledFunction |
|
321 { |
|
322 PropertyName *name; |
|
323 struct Pod { |
|
324 unsigned startCodeOffset; |
|
325 unsigned endCodeOffset; |
|
326 unsigned lineno; |
|
327 unsigned columnIndex; |
|
328 } pod; |
|
329 |
|
330 explicit ProfiledFunction() |
|
331 : name(nullptr) |
|
332 { } |
|
333 |
|
334 ProfiledFunction(PropertyName *name, unsigned start, unsigned end, |
|
335 unsigned line = 0, unsigned column = 0) |
|
336 : name(name) |
|
337 { |
|
338 JS_ASSERT(name->isTenured()); |
|
339 |
|
340 pod.startCodeOffset = start; |
|
341 pod.endCodeOffset = end; |
|
342 pod.lineno = line; |
|
343 pod.columnIndex = column; |
|
344 } |
|
345 |
|
346 void trace(JSTracer *trc) { |
|
347 if (name) |
|
348 MarkStringUnbarriered(trc, &name, "asm.js profiled function name"); |
|
349 } |
|
350 |
|
351 size_t serializedSize() const; |
|
352 uint8_t *serialize(uint8_t *cursor) const; |
|
353 const uint8_t *deserialize(ExclusiveContext *cx, const uint8_t *cursor); |
|
354 }; |
|
355 #endif |
|
356 |
|
357 #if defined(JS_ION_PERF) |
|
358 struct ProfiledBlocksFunction : public ProfiledFunction |
|
359 { |
|
360 unsigned endInlineCodeOffset; |
|
361 jit::BasicBlocksVector blocks; |
|
362 |
|
363 ProfiledBlocksFunction(PropertyName *name, unsigned start, unsigned endInline, unsigned end, |
|
364 jit::BasicBlocksVector &blocksVector) |
|
365 : ProfiledFunction(name, start, end), endInlineCodeOffset(endInline), |
|
366 blocks(mozilla::Move(blocksVector)) |
|
367 { |
|
368 JS_ASSERT(name->isTenured()); |
|
369 } |
|
370 |
|
371 ProfiledBlocksFunction(ProfiledBlocksFunction &©) |
|
372 : ProfiledFunction(copy.name, copy.pod.startCodeOffset, copy.pod.endCodeOffset), |
|
373 endInlineCodeOffset(copy.endInlineCodeOffset), blocks(mozilla::Move(copy.blocks)) |
|
374 { } |
|
375 }; |
|
376 #endif |
|
377 |
|
378 struct RelativeLink |
|
379 { |
|
380 uint32_t patchAtOffset; |
|
381 uint32_t targetOffset; |
|
382 }; |
|
383 |
|
384 typedef Vector<RelativeLink, 0, SystemAllocPolicy> RelativeLinkVector; |
|
385 |
|
386 struct AbsoluteLink |
|
387 { |
|
388 jit::CodeOffsetLabel patchAt; |
|
389 jit::AsmJSImmKind target; |
|
390 }; |
|
391 |
|
392 typedef Vector<AbsoluteLink, 0, SystemAllocPolicy> AbsoluteLinkVector; |
|
393 |
|
394 // Static-link data is used to patch a module either after it has been |
|
395 // compiled or deserialized with various absolute addresses (of code or |
|
396 // data in the process) or relative addresses (of code or data in the same |
|
397 // AsmJSModule). |
|
398 struct StaticLinkData |
|
399 { |
|
400 uint32_t interruptExitOffset; |
|
401 RelativeLinkVector relativeLinks; |
|
402 AbsoluteLinkVector absoluteLinks; |
|
403 |
|
404 size_t serializedSize() const; |
|
405 uint8_t *serialize(uint8_t *cursor) const; |
|
406 const uint8_t *deserialize(ExclusiveContext *cx, const uint8_t *cursor); |
|
407 bool clone(ExclusiveContext *cx, StaticLinkData *out) const; |
|
408 |
|
409 size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const; |
|
410 }; |
|
411 |
|
412 private: |
|
413 typedef Vector<Global, 0, SystemAllocPolicy> GlobalVector; |
|
414 typedef Vector<Exit, 0, SystemAllocPolicy> ExitVector; |
|
415 typedef Vector<ExportedFunction, 0, SystemAllocPolicy> ExportedFunctionVector; |
|
416 typedef Vector<jit::CallSite, 0, SystemAllocPolicy> CallSiteVector; |
|
417 typedef Vector<Name, 0, SystemAllocPolicy> FunctionNameVector; |
|
418 typedef Vector<jit::AsmJSHeapAccess, 0, SystemAllocPolicy> HeapAccessVector; |
|
419 typedef Vector<jit::IonScriptCounts *, 0, SystemAllocPolicy> FunctionCountsVector; |
|
420 #if defined(MOZ_VTUNE) || defined(JS_ION_PERF) |
|
421 typedef Vector<ProfiledFunction, 0, SystemAllocPolicy> ProfiledFunctionVector; |
|
422 #endif |
|
423 #if defined(JS_ION_PERF) |
|
424 typedef Vector<ProfiledBlocksFunction, 0, SystemAllocPolicy> ProfiledBlocksFunctionVector; |
|
425 #endif |
|
426 |
|
427 private: |
|
428 PropertyName * globalArgumentName_; |
|
429 PropertyName * importArgumentName_; |
|
430 PropertyName * bufferArgumentName_; |
|
431 |
|
432 GlobalVector globals_; |
|
433 ExitVector exits_; |
|
434 ExportedFunctionVector exports_; |
|
435 CallSiteVector callSites_; |
|
436 FunctionNameVector functionNames_; |
|
437 HeapAccessVector heapAccesses_; |
|
438 #if defined(MOZ_VTUNE) || defined(JS_ION_PERF) |
|
439 ProfiledFunctionVector profiledFunctions_; |
|
440 #endif |
|
441 #if defined(JS_ION_PERF) |
|
442 ProfiledBlocksFunctionVector perfProfiledBlocksFunctions_; |
|
443 #endif |
|
444 |
|
445 struct Pod { |
|
446 uint32_t funcLength_; |
|
447 uint32_t funcLengthWithRightBrace_; |
|
448 bool strict_; |
|
449 uint32_t numGlobalVars_; |
|
450 uint32_t numFFIs_; |
|
451 size_t funcPtrTableAndExitBytes_; |
|
452 bool hasArrayView_; |
|
453 size_t functionBytes_; // just the function bodies, no stubs |
|
454 size_t codeBytes_; // function bodies and stubs |
|
455 size_t totalBytes_; // function bodies, stubs, and global data |
|
456 uint32_t minHeapLength_; |
|
457 } pod; |
|
458 |
|
459 uint8_t * code_; |
|
460 uint8_t * interruptExit_; |
|
461 |
|
462 StaticLinkData staticLinkData_; |
|
463 bool dynamicallyLinked_; |
|
464 bool loadedFromCache_; |
|
465 HeapPtr<ArrayBufferObject> maybeHeap_; |
|
466 |
|
467 // The next two fields need to be kept out of the Pod as they depend on the |
|
468 // position of the module within the ScriptSource and thus aren't invariant |
|
469 // with caching. |
|
470 uint32_t funcStart_; |
|
471 uint32_t offsetToEndOfUseAsm_; |
|
472 |
|
473 ScriptSource * scriptSource_; |
|
474 |
|
475 // This field is accessed concurrently when requesting an interrupt. |
|
476 // Access must be synchronized via the runtime's interrupt lock. |
|
477 mutable bool codeIsProtected_; |
|
478 |
|
479 public: |
|
480 explicit AsmJSModule(ScriptSource *scriptSource, uint32_t functStart, |
|
481 uint32_t offsetToEndOfUseAsm, bool strict); |
|
482 ~AsmJSModule(); |
|
483 |
|
484 void trace(JSTracer *trc) { |
|
485 for (unsigned i = 0; i < globals_.length(); i++) |
|
486 globals_[i].trace(trc); |
|
487 for (unsigned i = 0; i < exports_.length(); i++) |
|
488 exports_[i].trace(trc); |
|
489 for (unsigned i = 0; i < exits_.length(); i++) { |
|
490 if (exitIndexToGlobalDatum(i).fun) |
|
491 MarkObject(trc, &exitIndexToGlobalDatum(i).fun, "asm.js imported function"); |
|
492 } |
|
493 for (unsigned i = 0; i < functionNames_.length(); i++) |
|
494 MarkStringUnbarriered(trc, &functionNames_[i].name(), "asm.js module function name"); |
|
495 #if defined(MOZ_VTUNE) || defined(JS_ION_PERF) |
|
496 for (unsigned i = 0; i < profiledFunctions_.length(); i++) |
|
497 profiledFunctions_[i].trace(trc); |
|
498 #endif |
|
499 #if defined(JS_ION_PERF) |
|
500 for (unsigned i = 0; i < perfProfiledBlocksFunctions_.length(); i++) |
|
501 perfProfiledBlocksFunctions_[i].trace(trc); |
|
502 #endif |
|
503 if (maybeHeap_) |
|
504 gc::MarkObject(trc, &maybeHeap_, "asm.js heap"); |
|
505 |
|
506 if (globalArgumentName_) |
|
507 MarkStringUnbarriered(trc, &globalArgumentName_, "asm.js global argument name"); |
|
508 if (importArgumentName_) |
|
509 MarkStringUnbarriered(trc, &importArgumentName_, "asm.js import argument name"); |
|
510 if (bufferArgumentName_) |
|
511 MarkStringUnbarriered(trc, &bufferArgumentName_, "asm.js buffer argument name"); |
|
512 } |
|
513 |
|
514 ScriptSource *scriptSource() const { |
|
515 JS_ASSERT(scriptSource_ != nullptr); |
|
516 return scriptSource_; |
|
517 } |
|
518 |
|
519 /* |
|
520 * funcStart() refers to the offset in the ScriptSource to the beginning |
|
521 * of the function. If the function has been created with the Function |
|
522 * constructor, this will be the first character in the function source. |
|
523 * Otherwise, it will be the opening parenthesis of the arguments list. |
|
524 */ |
|
525 uint32_t funcStart() const { |
|
526 return funcStart_; |
|
527 } |
|
528 uint32_t offsetToEndOfUseAsm() const { |
|
529 return offsetToEndOfUseAsm_; |
|
530 } |
|
531 void initFuncEnd(uint32_t endBeforeCurly, uint32_t endAfterCurly) { |
|
532 JS_ASSERT(endBeforeCurly >= offsetToEndOfUseAsm_); |
|
533 JS_ASSERT(endAfterCurly >= offsetToEndOfUseAsm_); |
|
534 pod.funcLength_ = endBeforeCurly - funcStart_; |
|
535 pod.funcLengthWithRightBrace_ = endAfterCurly - funcStart_; |
|
536 } |
|
537 uint32_t funcEndBeforeCurly() const { |
|
538 return funcStart_ + pod.funcLength_; |
|
539 } |
|
540 uint32_t funcEndAfterCurly() const { |
|
541 return funcStart_ + pod.funcLengthWithRightBrace_; |
|
542 } |
|
543 bool strict() const { |
|
544 return pod.strict_; |
|
545 } |
|
546 |
|
547 bool addGlobalVarInit(const Value &v, AsmJSCoercion coercion, uint32_t *globalIndex) { |
|
548 JS_ASSERT(pod.funcPtrTableAndExitBytes_ == 0); |
|
549 if (pod.numGlobalVars_ == UINT32_MAX) |
|
550 return false; |
|
551 Global g(Global::Variable, nullptr); |
|
552 g.pod.u.var.initKind_ = Global::InitConstant; |
|
553 g.pod.u.var.init.constant_ = v; |
|
554 g.pod.u.var.coercion_ = coercion; |
|
555 g.pod.u.var.index_ = *globalIndex = pod.numGlobalVars_++; |
|
556 return globals_.append(g); |
|
557 } |
|
558 bool addGlobalVarImport(PropertyName *name, AsmJSCoercion coercion, uint32_t *globalIndex) { |
|
559 JS_ASSERT(pod.funcPtrTableAndExitBytes_ == 0); |
|
560 Global g(Global::Variable, name); |
|
561 g.pod.u.var.initKind_ = Global::InitImport; |
|
562 g.pod.u.var.coercion_ = coercion; |
|
563 g.pod.u.var.index_ = *globalIndex = pod.numGlobalVars_++; |
|
564 return globals_.append(g); |
|
565 } |
|
566 bool addFFI(PropertyName *field, uint32_t *ffiIndex) { |
|
567 if (pod.numFFIs_ == UINT32_MAX) |
|
568 return false; |
|
569 Global g(Global::FFI, field); |
|
570 g.pod.u.ffiIndex_ = *ffiIndex = pod.numFFIs_++; |
|
571 return globals_.append(g); |
|
572 } |
|
573 bool addArrayView(ArrayBufferView::ViewType vt, PropertyName *field) { |
|
574 pod.hasArrayView_ = true; |
|
575 Global g(Global::ArrayView, field); |
|
576 g.pod.u.viewType_ = vt; |
|
577 return globals_.append(g); |
|
578 } |
|
579 bool addMathBuiltinFunction(AsmJSMathBuiltinFunction func, PropertyName *field) { |
|
580 Global g(Global::MathBuiltinFunction, field); |
|
581 g.pod.u.mathBuiltinFunc_ = func; |
|
582 return globals_.append(g); |
|
583 } |
|
584 bool addMathBuiltinConstant(double value, PropertyName *field) { |
|
585 Global g(Global::Constant, field); |
|
586 g.pod.u.constant.value_ = value; |
|
587 g.pod.u.constant.kind_ = Global::MathConstant; |
|
588 return globals_.append(g); |
|
589 } |
|
590 bool addGlobalConstant(double value, PropertyName *name) { |
|
591 Global g(Global::Constant, name); |
|
592 g.pod.u.constant.value_ = value; |
|
593 g.pod.u.constant.kind_ = Global::GlobalConstant; |
|
594 return globals_.append(g); |
|
595 } |
|
596 bool addFuncPtrTable(unsigned numElems, uint32_t *globalDataOffset) { |
|
597 JS_ASSERT(IsPowerOfTwo(numElems)); |
|
598 if (SIZE_MAX - pod.funcPtrTableAndExitBytes_ < numElems * sizeof(void*)) |
|
599 return false; |
|
600 *globalDataOffset = globalDataBytes(); |
|
601 pod.funcPtrTableAndExitBytes_ += numElems * sizeof(void*); |
|
602 return true; |
|
603 } |
|
604 bool addExit(unsigned ffiIndex, unsigned *exitIndex) { |
|
605 if (SIZE_MAX - pod.funcPtrTableAndExitBytes_ < sizeof(ExitDatum)) |
|
606 return false; |
|
607 uint32_t globalDataOffset = globalDataBytes(); |
|
608 JS_STATIC_ASSERT(sizeof(ExitDatum) % sizeof(void*) == 0); |
|
609 pod.funcPtrTableAndExitBytes_ += sizeof(ExitDatum); |
|
610 *exitIndex = unsigned(exits_.length()); |
|
611 return exits_.append(Exit(ffiIndex, globalDataOffset)); |
|
612 } |
|
613 |
|
614 bool addExportedFunction(PropertyName *name, uint32_t srcStart, uint32_t srcEnd, |
|
615 PropertyName *maybeFieldName, |
|
616 ArgCoercionVector &&argCoercions, |
|
617 ReturnType returnType) |
|
618 { |
|
619 ExportedFunction func(name, srcStart, srcEnd, maybeFieldName, |
|
620 mozilla::Move(argCoercions), returnType); |
|
621 if (exports_.length() >= UINT32_MAX) |
|
622 return false; |
|
623 return exports_.append(mozilla::Move(func)); |
|
624 } |
|
625 unsigned numExportedFunctions() const { |
|
626 return exports_.length(); |
|
627 } |
|
628 const ExportedFunction &exportedFunction(unsigned i) const { |
|
629 return exports_[i]; |
|
630 } |
|
631 ExportedFunction &exportedFunction(unsigned i) { |
|
632 return exports_[i]; |
|
633 } |
|
634 CodePtr entryTrampoline(const ExportedFunction &func) const { |
|
635 JS_ASSERT(func.pod.codeOffset_ != UINT32_MAX); |
|
636 return JS_DATA_TO_FUNC_PTR(CodePtr, code_ + func.pod.codeOffset_); |
|
637 } |
|
638 |
|
639 bool addFunctionName(PropertyName *name, uint32_t *nameIndex) { |
|
640 JS_ASSERT(name->isTenured()); |
|
641 if (functionNames_.length() > jit::CallSiteDesc::FUNCTION_NAME_INDEX_MAX) |
|
642 return false; |
|
643 *nameIndex = functionNames_.length(); |
|
644 return functionNames_.append(name); |
|
645 } |
|
646 PropertyName *functionName(uint32_t i) const { |
|
647 return functionNames_[i].name(); |
|
648 } |
|
649 |
|
650 #if defined(MOZ_VTUNE) || defined(JS_ION_PERF) |
|
651 bool trackProfiledFunction(PropertyName *name, unsigned startCodeOffset, unsigned endCodeOffset, |
|
652 unsigned line, unsigned column) |
|
653 { |
|
654 ProfiledFunction func(name, startCodeOffset, endCodeOffset, line, column); |
|
655 return profiledFunctions_.append(func); |
|
656 } |
|
657 unsigned numProfiledFunctions() const { |
|
658 return profiledFunctions_.length(); |
|
659 } |
|
660 ProfiledFunction &profiledFunction(unsigned i) { |
|
661 return profiledFunctions_[i]; |
|
662 } |
|
663 #endif |
|
664 |
|
665 #ifdef JS_ION_PERF |
|
666 bool trackPerfProfiledBlocks(PropertyName *name, unsigned startCodeOffset, unsigned endInlineCodeOffset, |
|
667 unsigned endCodeOffset, jit::BasicBlocksVector &basicBlocks) { |
|
668 ProfiledBlocksFunction func(name, startCodeOffset, endInlineCodeOffset, endCodeOffset, basicBlocks); |
|
669 return perfProfiledBlocksFunctions_.append(mozilla::Move(func)); |
|
670 } |
|
671 unsigned numPerfBlocksFunctions() const { |
|
672 return perfProfiledBlocksFunctions_.length(); |
|
673 } |
|
674 ProfiledBlocksFunction &perfProfiledBlocksFunction(unsigned i) { |
|
675 return perfProfiledBlocksFunctions_[i]; |
|
676 } |
|
677 #endif |
|
678 |
|
679 bool hasArrayView() const { |
|
680 return pod.hasArrayView_; |
|
681 } |
|
682 unsigned numFFIs() const { |
|
683 return pod.numFFIs_; |
|
684 } |
|
685 unsigned numGlobalVars() const { |
|
686 return pod.numGlobalVars_; |
|
687 } |
|
688 unsigned numGlobals() const { |
|
689 return globals_.length(); |
|
690 } |
|
691 Global &global(unsigned i) { |
|
692 return globals_[i]; |
|
693 } |
|
694 unsigned numExits() const { |
|
695 return exits_.length(); |
|
696 } |
|
697 Exit &exit(unsigned i) { |
|
698 return exits_[i]; |
|
699 } |
|
700 const Exit &exit(unsigned i) const { |
|
701 return exits_[i]; |
|
702 } |
|
703 uint8_t *interpExitTrampoline(const Exit &exit) const { |
|
704 JS_ASSERT(exit.interpCodeOffset_); |
|
705 return code_ + exit.interpCodeOffset_; |
|
706 } |
|
707 uint8_t *ionExitTrampoline(const Exit &exit) const { |
|
708 JS_ASSERT(exit.ionCodeOffset_); |
|
709 return code_ + exit.ionCodeOffset_; |
|
710 } |
|
711 |
|
712 // An Exit holds bookkeeping information about an exit; the ExitDatum |
|
713 // struct overlays the actual runtime data stored in the global data |
|
714 // section. |
|
715 struct ExitDatum |
|
716 { |
|
717 uint8_t *exit; |
|
718 HeapPtrFunction fun; |
|
719 }; |
|
720 |
|
721 // Global data section |
|
722 // |
|
723 // The global data section is placed after the executable code (i.e., at |
|
724 // offset codeBytes_) in the module's linear allocation. The global data |
|
725 // are laid out in this order: |
|
726 // 0. a pointer/descriptor for the heap that was linked to the module |
|
727 // 1. global variable state (elements are sizeof(uint64_t)) |
|
728 // 2. interleaved function-pointer tables and exits. These are allocated |
|
729 // while type checking function bodies (as exits and uses of |
|
730 // function-pointer tables are encountered). |
|
731 size_t offsetOfGlobalData() const { |
|
732 JS_ASSERT(code_); |
|
733 return pod.codeBytes_; |
|
734 } |
|
735 uint8_t *globalData() const { |
|
736 return code_ + offsetOfGlobalData(); |
|
737 } |
|
738 size_t globalDataBytes() const { |
|
739 return sizeof(void*) + |
|
740 pod.numGlobalVars_ * sizeof(uint64_t) + |
|
741 pod.funcPtrTableAndExitBytes_; |
|
742 } |
|
743 unsigned heapOffset() const { |
|
744 return 0; |
|
745 } |
|
746 uint8_t *&heapDatum() const { |
|
747 return *(uint8_t**)(globalData() + heapOffset()); |
|
748 } |
|
749 unsigned globalVarIndexToGlobalDataOffset(unsigned i) const { |
|
750 JS_ASSERT(i < pod.numGlobalVars_); |
|
751 return sizeof(void*) + |
|
752 i * sizeof(uint64_t); |
|
753 } |
|
754 void *globalVarIndexToGlobalDatum(unsigned i) const { |
|
755 return (void *)(globalData() + globalVarIndexToGlobalDataOffset(i)); |
|
756 } |
|
757 uint8_t **globalDataOffsetToFuncPtrTable(unsigned globalDataOffset) const { |
|
758 JS_ASSERT(globalDataOffset < globalDataBytes()); |
|
759 return (uint8_t **)(globalData() + globalDataOffset); |
|
760 } |
|
761 unsigned exitIndexToGlobalDataOffset(unsigned exitIndex) const { |
|
762 return exits_[exitIndex].globalDataOffset(); |
|
763 } |
|
764 ExitDatum &exitIndexToGlobalDatum(unsigned exitIndex) const { |
|
765 return *(ExitDatum *)(globalData() + exitIndexToGlobalDataOffset(exitIndex)); |
|
766 } |
|
767 |
|
768 void initFunctionBytes(size_t functionBytes) { |
|
769 JS_ASSERT(pod.functionBytes_ == 0); |
|
770 pod.functionBytes_ = functionBytes; |
|
771 } |
|
772 void updateFunctionBytes(jit::MacroAssembler &masm) { |
|
773 pod.functionBytes_ = masm.actualOffset(pod.functionBytes_); |
|
774 JS_ASSERT(pod.functionBytes_ % AsmJSPageSize == 0); |
|
775 } |
|
776 size_t functionBytes() const { |
|
777 JS_ASSERT(pod.functionBytes_); |
|
778 JS_ASSERT(pod.functionBytes_ % AsmJSPageSize == 0); |
|
779 return pod.functionBytes_; |
|
780 } |
|
781 bool containsPC(void *pc) const { |
|
782 return pc >= code_ && pc < (code_ + functionBytes()); |
|
783 } |
|
784 |
|
785 void assignHeapAccesses(jit::AsmJSHeapAccessVector &&accesses) { |
|
786 heapAccesses_ = Move(accesses); |
|
787 } |
|
788 unsigned numHeapAccesses() const { |
|
789 return heapAccesses_.length(); |
|
790 } |
|
791 const jit::AsmJSHeapAccess &heapAccess(unsigned i) const { |
|
792 return heapAccesses_[i]; |
|
793 } |
|
794 jit::AsmJSHeapAccess &heapAccess(unsigned i) { |
|
795 return heapAccesses_[i]; |
|
796 } |
|
797 |
|
798 void assignCallSites(jit::CallSiteVector &&callsites) { |
|
799 callSites_ = Move(callsites); |
|
800 } |
|
801 unsigned numCallSites() const { |
|
802 return callSites_.length(); |
|
803 } |
|
804 const jit::CallSite &callSite(unsigned i) const { |
|
805 return callSites_[i]; |
|
806 } |
|
807 jit::CallSite &callSite(unsigned i) { |
|
808 return callSites_[i]; |
|
809 } |
|
810 |
|
811 void initHeap(Handle<ArrayBufferObject*> heap, JSContext *cx); |
|
812 |
|
813 void requireHeapLengthToBeAtLeast(uint32_t len) { |
|
814 if (len > pod.minHeapLength_) |
|
815 pod.minHeapLength_ = len; |
|
816 } |
|
817 uint32_t minHeapLength() const { |
|
818 return pod.minHeapLength_; |
|
819 } |
|
820 |
|
821 bool allocateAndCopyCode(ExclusiveContext *cx, jit::MacroAssembler &masm); |
|
822 |
|
823 // StaticLinkData setters (called after finishing compilation, before |
|
824 // staticLink). |
|
825 bool addRelativeLink(RelativeLink link) { |
|
826 return staticLinkData_.relativeLinks.append(link); |
|
827 } |
|
828 bool addAbsoluteLink(AbsoluteLink link) { |
|
829 return staticLinkData_.absoluteLinks.append(link); |
|
830 } |
|
831 void setInterruptOffset(uint32_t offset) { |
|
832 staticLinkData_.interruptExitOffset = offset; |
|
833 } |
|
834 |
|
835 void restoreToInitialState(ArrayBufferObject *maybePrevBuffer, ExclusiveContext *cx); |
|
836 void setAutoFlushICacheRange(); |
|
837 void staticallyLink(ExclusiveContext *cx); |
|
838 |
|
839 uint8_t *codeBase() const { |
|
840 JS_ASSERT(code_); |
|
841 JS_ASSERT(uintptr_t(code_) % AsmJSPageSize == 0); |
|
842 return code_; |
|
843 } |
|
844 |
|
845 uint8_t *interruptExit() const { |
|
846 return interruptExit_; |
|
847 } |
|
848 |
|
849 void setIsDynamicallyLinked() { |
|
850 JS_ASSERT(!dynamicallyLinked_); |
|
851 dynamicallyLinked_ = true; |
|
852 } |
|
853 bool isDynamicallyLinked() const { |
|
854 return dynamicallyLinked_; |
|
855 } |
|
856 uint8_t *maybeHeap() const { |
|
857 JS_ASSERT(dynamicallyLinked_); |
|
858 return heapDatum(); |
|
859 } |
|
860 ArrayBufferObject *maybeHeapBufferObject() const { |
|
861 JS_ASSERT(dynamicallyLinked_); |
|
862 return maybeHeap_; |
|
863 } |
|
864 size_t heapLength() const { |
|
865 JS_ASSERT(dynamicallyLinked_); |
|
866 return maybeHeap_ ? maybeHeap_->byteLength() : 0; |
|
867 } |
|
868 |
|
869 void initGlobalArgumentName(PropertyName *n) { |
|
870 JS_ASSERT_IF(n, n->isTenured()); |
|
871 globalArgumentName_ = n; |
|
872 } |
|
873 void initImportArgumentName(PropertyName *n) { |
|
874 JS_ASSERT_IF(n, n->isTenured()); |
|
875 importArgumentName_ = n; |
|
876 } |
|
877 void initBufferArgumentName(PropertyName *n) { |
|
878 JS_ASSERT_IF(n, n->isTenured()); |
|
879 bufferArgumentName_ = n; |
|
880 } |
|
881 |
|
882 PropertyName *globalArgumentName() const { |
|
883 return globalArgumentName_; |
|
884 } |
|
885 PropertyName *importArgumentName() const { |
|
886 return importArgumentName_; |
|
887 } |
|
888 PropertyName *bufferArgumentName() const { |
|
889 return bufferArgumentName_; |
|
890 } |
|
891 |
|
892 void detachIonCompilation(size_t exitIndex) const { |
|
893 exitIndexToGlobalDatum(exitIndex).exit = interpExitTrampoline(exit(exitIndex)); |
|
894 } |
|
895 |
|
896 void addSizeOfMisc(mozilla::MallocSizeOf mallocSizeOf, size_t *asmJSModuleCode, |
|
897 size_t *asmJSModuleData); |
|
898 |
|
899 size_t serializedSize() const; |
|
900 uint8_t *serialize(uint8_t *cursor) const; |
|
901 const uint8_t *deserialize(ExclusiveContext *cx, const uint8_t *cursor); |
|
902 bool loadedFromCache() const { return loadedFromCache_; } |
|
903 |
|
904 bool clone(JSContext *cx, ScopedJSDeletePtr<AsmJSModule> *moduleOut) const; |
|
905 |
|
906 // These methods may only be called while holding the Runtime's interrupt |
|
907 // lock. |
|
908 void protectCode(JSRuntime *rt) const; |
|
909 void unprotectCode(JSRuntime *rt) const; |
|
910 bool codeIsProtected(JSRuntime *rt) const; |
|
911 }; |
|
912 |
|
913 // Store the just-parsed module in the cache using AsmJSCacheOps. |
|
914 extern bool |
|
915 StoreAsmJSModuleInCache(AsmJSParser &parser, |
|
916 const AsmJSModule &module, |
|
917 ExclusiveContext *cx); |
|
918 |
|
919 // Attempt to load the asm.js module that is about to be parsed from the cache |
|
920 // using AsmJSCacheOps. On cache hit, *module will be non-null. Note: the |
|
921 // return value indicates whether or not an error was encountered, not whether |
|
922 // there was a cache hit. |
|
923 extern bool |
|
924 LookupAsmJSModuleInCache(ExclusiveContext *cx, |
|
925 AsmJSParser &parser, |
|
926 ScopedJSDeletePtr<AsmJSModule> *module, |
|
927 ScopedJSFreePtr<char> *compilationTimeReport); |
|
928 |
|
929 // An AsmJSModuleObject is an internal implementation object (i.e., not exposed |
|
930 // directly to user script) which manages the lifetime of an AsmJSModule. A |
|
931 // JSObject is necessary since we want LinkAsmJS/CallAsmJS JSFunctions to be |
|
932 // able to point to their module via their extended slots. |
|
933 class AsmJSModuleObject : public JSObject |
|
934 { |
|
935 static const unsigned MODULE_SLOT = 0; |
|
936 |
|
937 public: |
|
938 static const unsigned RESERVED_SLOTS = 1; |
|
939 |
|
940 // On success, return an AsmJSModuleClass JSObject that has taken ownership |
|
941 // (and release()ed) the given module. |
|
942 static AsmJSModuleObject *create(ExclusiveContext *cx, ScopedJSDeletePtr<AsmJSModule> *module); |
|
943 |
|
944 AsmJSModule &module() const; |
|
945 |
|
946 void addSizeOfMisc(mozilla::MallocSizeOf mallocSizeOf, size_t *asmJSModuleCode, |
|
947 size_t *asmJSModuleData) { |
|
948 module().addSizeOfMisc(mallocSizeOf, asmJSModuleCode, asmJSModuleData); |
|
949 } |
|
950 |
|
951 static const Class class_; |
|
952 }; |
|
953 |
|
954 } // namespace js |
|
955 |
|
956 #endif // JS_ION |
|
957 |
|
958 #endif /* jit_AsmJSModule_h */ |