|
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_IonCode_h |
|
8 #define jit_IonCode_h |
|
9 |
|
10 #include "mozilla/Atomics.h" |
|
11 #include "mozilla/MemoryReporting.h" |
|
12 #include "mozilla/PodOperations.h" |
|
13 |
|
14 #include "jsinfer.h" |
|
15 #include "jstypes.h" |
|
16 |
|
17 #include "assembler/jit/ExecutableAllocator.h" |
|
18 #include "gc/Heap.h" |
|
19 #include "jit/IonOptimizationLevels.h" |
|
20 #include "jit/IonTypes.h" |
|
21 |
|
22 namespace JSC { |
|
23 class ExecutablePool; |
|
24 } |
|
25 |
|
26 namespace js { |
|
27 |
|
28 class AsmJSModule; |
|
29 |
|
30 namespace jit { |
|
31 |
|
32 class MacroAssembler; |
|
33 class CodeOffsetLabel; |
|
34 class PatchableBackedge; |
|
35 |
|
36 class JitCode : public gc::BarrieredCell<JitCode> |
|
37 { |
|
38 protected: |
|
39 uint8_t *code_; |
|
40 JSC::ExecutablePool *pool_; |
|
41 uint32_t bufferSize_; // Total buffer size. Does not include headerSize_. |
|
42 uint32_t insnSize_; // Instruction stream size. |
|
43 uint32_t dataSize_; // Size of the read-only data area. |
|
44 uint32_t jumpRelocTableBytes_; // Size of the jump relocation table. |
|
45 uint32_t dataRelocTableBytes_; // Size of the data relocation table. |
|
46 uint32_t preBarrierTableBytes_; // Size of the prebarrier table. |
|
47 uint8_t headerSize_ : 5; // Number of bytes allocated before codeStart. |
|
48 uint8_t kind_ : 3; // JSC::CodeKind, for the memory reporters. |
|
49 bool invalidated_ : 1; // Whether the code object has been invalidated. |
|
50 // This is necessary to prevent GC tracing. |
|
51 |
|
52 #if JS_BITS_PER_WORD == 32 |
|
53 // Ensure JitCode is gc::Cell aligned. |
|
54 uint32_t padding_; |
|
55 #endif |
|
56 |
|
57 JitCode() |
|
58 : code_(nullptr), |
|
59 pool_(nullptr) |
|
60 { } |
|
61 JitCode(uint8_t *code, uint32_t bufferSize, uint32_t headerSize, JSC::ExecutablePool *pool, |
|
62 JSC::CodeKind kind) |
|
63 : code_(code), |
|
64 pool_(pool), |
|
65 bufferSize_(bufferSize), |
|
66 insnSize_(0), |
|
67 dataSize_(0), |
|
68 jumpRelocTableBytes_(0), |
|
69 dataRelocTableBytes_(0), |
|
70 preBarrierTableBytes_(0), |
|
71 headerSize_(headerSize), |
|
72 kind_(kind), |
|
73 invalidated_(false) |
|
74 { |
|
75 MOZ_ASSERT(JSC::CodeKind(kind_) == kind); |
|
76 MOZ_ASSERT(headerSize_ == headerSize); |
|
77 } |
|
78 |
|
79 uint32_t dataOffset() const { |
|
80 return insnSize_; |
|
81 } |
|
82 uint32_t jumpRelocTableOffset() const { |
|
83 return dataOffset() + dataSize_; |
|
84 } |
|
85 uint32_t dataRelocTableOffset() const { |
|
86 return jumpRelocTableOffset() + jumpRelocTableBytes_; |
|
87 } |
|
88 uint32_t preBarrierTableOffset() const { |
|
89 return dataRelocTableOffset() + dataRelocTableBytes_; |
|
90 } |
|
91 |
|
92 public: |
|
93 uint8_t *raw() const { |
|
94 return code_; |
|
95 } |
|
96 size_t instructionsSize() const { |
|
97 return insnSize_; |
|
98 } |
|
99 void trace(JSTracer *trc); |
|
100 void finalize(FreeOp *fop); |
|
101 void setInvalidated() { |
|
102 invalidated_ = true; |
|
103 } |
|
104 |
|
105 void togglePreBarriers(bool enabled); |
|
106 |
|
107 // If this JitCode object has been, effectively, corrupted due to |
|
108 // invalidation patching, then we have to remember this so we don't try and |
|
109 // trace relocation entries that may now be corrupt. |
|
110 bool invalidated() const { |
|
111 return !!invalidated_; |
|
112 } |
|
113 |
|
114 template <typename T> T as() const { |
|
115 return JS_DATA_TO_FUNC_PTR(T, raw()); |
|
116 } |
|
117 |
|
118 void copyFrom(MacroAssembler &masm); |
|
119 |
|
120 static JitCode *FromExecutable(uint8_t *buffer) { |
|
121 JitCode *code = *(JitCode **)(buffer - sizeof(JitCode *)); |
|
122 JS_ASSERT(code->raw() == buffer); |
|
123 return code; |
|
124 } |
|
125 |
|
126 static size_t offsetOfCode() { |
|
127 return offsetof(JitCode, code_); |
|
128 } |
|
129 |
|
130 uint8_t *jumpRelocTable() { |
|
131 return code_ + jumpRelocTableOffset(); |
|
132 } |
|
133 |
|
134 // Allocates a new JitCode object which will be managed by the GC. If no |
|
135 // object can be allocated, nullptr is returned. On failure, |pool| is |
|
136 // automatically released, so the code may be freed. |
|
137 template <AllowGC allowGC> |
|
138 static JitCode *New(JSContext *cx, uint8_t *code, uint32_t bufferSize, uint32_t headerSize, |
|
139 JSC::ExecutablePool *pool, JSC::CodeKind kind); |
|
140 |
|
141 public: |
|
142 static inline ThingRootKind rootKind() { return THING_ROOT_JIT_CODE; } |
|
143 }; |
|
144 |
|
145 class SnapshotWriter; |
|
146 class RecoverWriter; |
|
147 class SafepointWriter; |
|
148 class SafepointIndex; |
|
149 class OsiIndex; |
|
150 class IonCache; |
|
151 struct PatchableBackedgeInfo; |
|
152 struct CacheLocation; |
|
153 |
|
154 // Describes a single AsmJSModule which jumps (via an FFI exit with the given |
|
155 // index) directly into an IonScript. |
|
156 struct DependentAsmJSModuleExit |
|
157 { |
|
158 const AsmJSModule *module; |
|
159 size_t exitIndex; |
|
160 |
|
161 DependentAsmJSModuleExit(const AsmJSModule *module, size_t exitIndex) |
|
162 : module(module), |
|
163 exitIndex(exitIndex) |
|
164 { } |
|
165 }; |
|
166 |
|
167 // An IonScript attaches Ion-generated information to a JSScript. |
|
168 struct IonScript |
|
169 { |
|
170 private: |
|
171 // Code pointer containing the actual method. |
|
172 EncapsulatedPtr<JitCode> method_; |
|
173 |
|
174 // Deoptimization table used by this method. |
|
175 EncapsulatedPtr<JitCode> deoptTable_; |
|
176 |
|
177 // Entrypoint for OSR, or nullptr. |
|
178 jsbytecode *osrPc_; |
|
179 |
|
180 // Offset to OSR entrypoint from method_->raw(), or 0. |
|
181 uint32_t osrEntryOffset_; |
|
182 |
|
183 // Offset to entrypoint skipping type arg check from method_->raw(). |
|
184 uint32_t skipArgCheckEntryOffset_; |
|
185 |
|
186 // Offset of the invalidation epilogue (which pushes this IonScript |
|
187 // and calls the invalidation thunk). |
|
188 uint32_t invalidateEpilogueOffset_; |
|
189 |
|
190 // The offset immediately after the IonScript immediate. |
|
191 // NOTE: technically a constant delta from |
|
192 // |invalidateEpilogueOffset_|, so we could hard-code this |
|
193 // per-platform if we want. |
|
194 uint32_t invalidateEpilogueDataOffset_; |
|
195 |
|
196 // Number of times this script bailed out without invalidation. |
|
197 uint32_t numBailouts_; |
|
198 |
|
199 // Flag set when it is likely that one of our (transitive) call |
|
200 // targets is not compiled. Used in ForkJoin.cpp to decide when |
|
201 // we should add call targets to the worklist. |
|
202 mozilla::Atomic<bool, mozilla::Relaxed> hasUncompiledCallTarget_; |
|
203 |
|
204 // Flag set when this script is used as an entry script to parallel |
|
205 // execution. If this is true, then the parent JSScript must be in its |
|
206 // JitCompartment's parallel entry script set. |
|
207 bool isParallelEntryScript_; |
|
208 |
|
209 // Flag set if IonScript was compiled with SPS profiling enabled. |
|
210 bool hasSPSInstrumentation_; |
|
211 |
|
212 // Flag for if this script is getting recompiled. |
|
213 uint32_t recompiling_; |
|
214 |
|
215 // Any kind of data needed by the runtime, these can be either cache |
|
216 // information or profiling info. |
|
217 uint32_t runtimeData_; |
|
218 uint32_t runtimeSize_; |
|
219 |
|
220 // State for polymorphic caches in the compiled code. All caches are stored |
|
221 // in the runtimeData buffer and indexed by the cacheIndex which give a |
|
222 // relative offset in the runtimeData array. |
|
223 uint32_t cacheIndex_; |
|
224 uint32_t cacheEntries_; |
|
225 |
|
226 // Map code displacement to safepoint / OSI-patch-delta. |
|
227 uint32_t safepointIndexOffset_; |
|
228 uint32_t safepointIndexEntries_; |
|
229 |
|
230 // Offset to and length of the safepoint table in bytes. |
|
231 uint32_t safepointsStart_; |
|
232 uint32_t safepointsSize_; |
|
233 |
|
234 // Number of bytes this function reserves on the stack. |
|
235 uint32_t frameSlots_; |
|
236 |
|
237 // Frame size is the value that can be added to the StackPointer along |
|
238 // with the frame prefix to get a valid IonJSFrameLayout. |
|
239 uint32_t frameSize_; |
|
240 |
|
241 // Table mapping bailout IDs to snapshot offsets. |
|
242 uint32_t bailoutTable_; |
|
243 uint32_t bailoutEntries_; |
|
244 |
|
245 // Map OSI-point displacement to snapshot. |
|
246 uint32_t osiIndexOffset_; |
|
247 uint32_t osiIndexEntries_; |
|
248 |
|
249 // Offset from the start of the code buffer to its snapshot buffer. |
|
250 uint32_t snapshots_; |
|
251 uint32_t snapshotsListSize_; |
|
252 uint32_t snapshotsRVATableSize_; |
|
253 |
|
254 // List of instructions needed to recover stack frames. |
|
255 uint32_t recovers_; |
|
256 uint32_t recoversSize_; |
|
257 |
|
258 // Constant table for constants stored in snapshots. |
|
259 uint32_t constantTable_; |
|
260 uint32_t constantEntries_; |
|
261 |
|
262 // List of scripts that we call. |
|
263 // |
|
264 // Currently this is only non-nullptr for parallel IonScripts. |
|
265 uint32_t callTargetList_; |
|
266 uint32_t callTargetEntries_; |
|
267 |
|
268 // List of patchable backedges which are threaded into the runtime's list. |
|
269 uint32_t backedgeList_; |
|
270 uint32_t backedgeEntries_; |
|
271 |
|
272 // Number of references from invalidation records. |
|
273 uint32_t refcount_; |
|
274 |
|
275 // If this is a parallel script, the number of major GC collections it has |
|
276 // been idle, otherwise 0. |
|
277 // |
|
278 // JSScripts with parallel IonScripts are preserved across GC if the |
|
279 // parallel age is < MAX_PARALLEL_AGE. |
|
280 uint32_t parallelAge_; |
|
281 |
|
282 // Identifier of the compilation which produced this code. |
|
283 types::RecompileInfo recompileInfo_; |
|
284 |
|
285 // The optimization level this script was compiled in. |
|
286 OptimizationLevel optimizationLevel_; |
|
287 |
|
288 // Number of times we tried to enter this script via OSR but failed due to |
|
289 // a LOOPENTRY pc other than osrPc_. |
|
290 uint32_t osrPcMismatchCounter_; |
|
291 |
|
292 // If non-null, the list of AsmJSModules |
|
293 // that contain an optimized call directly into this IonScript. |
|
294 Vector<DependentAsmJSModuleExit> *dependentAsmJSModules; |
|
295 |
|
296 private: |
|
297 inline uint8_t *bottomBuffer() { |
|
298 return reinterpret_cast<uint8_t *>(this); |
|
299 } |
|
300 inline const uint8_t *bottomBuffer() const { |
|
301 return reinterpret_cast<const uint8_t *>(this); |
|
302 } |
|
303 |
|
304 public: |
|
305 SnapshotOffset *bailoutTable() { |
|
306 return (SnapshotOffset *) &bottomBuffer()[bailoutTable_]; |
|
307 } |
|
308 EncapsulatedValue *constants() { |
|
309 return (EncapsulatedValue *) &bottomBuffer()[constantTable_]; |
|
310 } |
|
311 const SafepointIndex *safepointIndices() const { |
|
312 return const_cast<IonScript *>(this)->safepointIndices(); |
|
313 } |
|
314 SafepointIndex *safepointIndices() { |
|
315 return (SafepointIndex *) &bottomBuffer()[safepointIndexOffset_]; |
|
316 } |
|
317 const OsiIndex *osiIndices() const { |
|
318 return const_cast<IonScript *>(this)->osiIndices(); |
|
319 } |
|
320 OsiIndex *osiIndices() { |
|
321 return (OsiIndex *) &bottomBuffer()[osiIndexOffset_]; |
|
322 } |
|
323 uint32_t *cacheIndex() { |
|
324 return (uint32_t *) &bottomBuffer()[cacheIndex_]; |
|
325 } |
|
326 uint8_t *runtimeData() { |
|
327 return &bottomBuffer()[runtimeData_]; |
|
328 } |
|
329 JSScript **callTargetList() { |
|
330 return (JSScript **) &bottomBuffer()[callTargetList_]; |
|
331 } |
|
332 PatchableBackedge *backedgeList() { |
|
333 return (PatchableBackedge *) &bottomBuffer()[backedgeList_]; |
|
334 } |
|
335 bool addDependentAsmJSModule(JSContext *cx, DependentAsmJSModuleExit exit); |
|
336 void removeDependentAsmJSModule(DependentAsmJSModuleExit exit) { |
|
337 if (!dependentAsmJSModules) |
|
338 return; |
|
339 for (size_t i = 0; i < dependentAsmJSModules->length(); i++) { |
|
340 if (dependentAsmJSModules->begin()[i].module == exit.module && |
|
341 dependentAsmJSModules->begin()[i].exitIndex == exit.exitIndex) |
|
342 { |
|
343 dependentAsmJSModules->erase(dependentAsmJSModules->begin() + i); |
|
344 break; |
|
345 } |
|
346 } |
|
347 } |
|
348 |
|
349 private: |
|
350 void trace(JSTracer *trc); |
|
351 |
|
352 public: |
|
353 // Do not call directly, use IonScript::New. This is public for cx->new_. |
|
354 IonScript(); |
|
355 |
|
356 static IonScript *New(JSContext *cx, types::RecompileInfo recompileInfo, |
|
357 uint32_t frameLocals, uint32_t frameSize, |
|
358 size_t snapshotsListSize, size_t snapshotsRVATableSize, |
|
359 size_t recoversSize, size_t bailoutEntries, |
|
360 size_t constants, size_t safepointIndexEntries, |
|
361 size_t osiIndexEntries, size_t cacheEntries, |
|
362 size_t runtimeSize, size_t safepointsSize, |
|
363 size_t callTargetEntries, size_t backedgeEntries, |
|
364 OptimizationLevel optimizationLevel); |
|
365 static void Trace(JSTracer *trc, IonScript *script); |
|
366 static void Destroy(FreeOp *fop, IonScript *script); |
|
367 |
|
368 static inline size_t offsetOfMethod() { |
|
369 return offsetof(IonScript, method_); |
|
370 } |
|
371 static inline size_t offsetOfOsrEntryOffset() { |
|
372 return offsetof(IonScript, osrEntryOffset_); |
|
373 } |
|
374 static inline size_t offsetOfSkipArgCheckEntryOffset() { |
|
375 return offsetof(IonScript, skipArgCheckEntryOffset_); |
|
376 } |
|
377 static inline size_t offsetOfRefcount() { |
|
378 return offsetof(IonScript, refcount_); |
|
379 } |
|
380 static inline size_t offsetOfRecompiling() { |
|
381 return offsetof(IonScript, recompiling_); |
|
382 } |
|
383 |
|
384 public: |
|
385 JitCode *method() const { |
|
386 return method_; |
|
387 } |
|
388 void setMethod(JitCode *code) { |
|
389 JS_ASSERT(!invalidated()); |
|
390 method_ = code; |
|
391 } |
|
392 void setDeoptTable(JitCode *code) { |
|
393 deoptTable_ = code; |
|
394 } |
|
395 void setOsrPc(jsbytecode *osrPc) { |
|
396 osrPc_ = osrPc; |
|
397 } |
|
398 jsbytecode *osrPc() const { |
|
399 return osrPc_; |
|
400 } |
|
401 void setOsrEntryOffset(uint32_t offset) { |
|
402 JS_ASSERT(!osrEntryOffset_); |
|
403 osrEntryOffset_ = offset; |
|
404 } |
|
405 uint32_t osrEntryOffset() const { |
|
406 return osrEntryOffset_; |
|
407 } |
|
408 void setSkipArgCheckEntryOffset(uint32_t offset) { |
|
409 JS_ASSERT(!skipArgCheckEntryOffset_); |
|
410 skipArgCheckEntryOffset_ = offset; |
|
411 } |
|
412 uint32_t getSkipArgCheckEntryOffset() const { |
|
413 return skipArgCheckEntryOffset_; |
|
414 } |
|
415 bool containsCodeAddress(uint8_t *addr) const { |
|
416 return method()->raw() <= addr && addr <= method()->raw() + method()->instructionsSize(); |
|
417 } |
|
418 bool containsReturnAddress(uint8_t *addr) const { |
|
419 // This accounts for an off by one error caused by the return address of a |
|
420 // bailout sitting outside the range of the containing function. |
|
421 return method()->raw() <= addr && addr <= method()->raw() + method()->instructionsSize(); |
|
422 } |
|
423 void setInvalidationEpilogueOffset(uint32_t offset) { |
|
424 JS_ASSERT(!invalidateEpilogueOffset_); |
|
425 invalidateEpilogueOffset_ = offset; |
|
426 } |
|
427 uint32_t invalidateEpilogueOffset() const { |
|
428 JS_ASSERT(invalidateEpilogueOffset_); |
|
429 return invalidateEpilogueOffset_; |
|
430 } |
|
431 void setInvalidationEpilogueDataOffset(uint32_t offset) { |
|
432 JS_ASSERT(!invalidateEpilogueDataOffset_); |
|
433 invalidateEpilogueDataOffset_ = offset; |
|
434 } |
|
435 uint32_t invalidateEpilogueDataOffset() const { |
|
436 JS_ASSERT(invalidateEpilogueDataOffset_); |
|
437 return invalidateEpilogueDataOffset_; |
|
438 } |
|
439 void incNumBailouts() { |
|
440 numBailouts_++; |
|
441 } |
|
442 uint32_t numBailouts() const { |
|
443 return numBailouts_; |
|
444 } |
|
445 bool bailoutExpected() const { |
|
446 return numBailouts_ > 0; |
|
447 } |
|
448 void setHasUncompiledCallTarget() { |
|
449 hasUncompiledCallTarget_ = true; |
|
450 } |
|
451 void clearHasUncompiledCallTarget() { |
|
452 hasUncompiledCallTarget_ = false; |
|
453 } |
|
454 bool hasUncompiledCallTarget() const { |
|
455 return hasUncompiledCallTarget_; |
|
456 } |
|
457 void setIsParallelEntryScript() { |
|
458 isParallelEntryScript_ = true; |
|
459 } |
|
460 bool isParallelEntryScript() const { |
|
461 return isParallelEntryScript_; |
|
462 } |
|
463 void setHasSPSInstrumentation() { |
|
464 hasSPSInstrumentation_ = true; |
|
465 } |
|
466 void clearHasSPSInstrumentation() { |
|
467 hasSPSInstrumentation_ = false; |
|
468 } |
|
469 bool hasSPSInstrumentation() const { |
|
470 return hasSPSInstrumentation_; |
|
471 } |
|
472 const uint8_t *snapshots() const { |
|
473 return reinterpret_cast<const uint8_t *>(this) + snapshots_; |
|
474 } |
|
475 size_t snapshotsListSize() const { |
|
476 return snapshotsListSize_; |
|
477 } |
|
478 size_t snapshotsRVATableSize() const { |
|
479 return snapshotsRVATableSize_; |
|
480 } |
|
481 const uint8_t *recovers() const { |
|
482 return reinterpret_cast<const uint8_t *>(this) + recovers_; |
|
483 } |
|
484 size_t recoversSize() const { |
|
485 return recoversSize_; |
|
486 } |
|
487 const uint8_t *safepoints() const { |
|
488 return reinterpret_cast<const uint8_t *>(this) + safepointsStart_; |
|
489 } |
|
490 size_t safepointsSize() const { |
|
491 return safepointsSize_; |
|
492 } |
|
493 size_t callTargetEntries() const { |
|
494 return callTargetEntries_; |
|
495 } |
|
496 size_t sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const { |
|
497 return mallocSizeOf(this); |
|
498 } |
|
499 EncapsulatedValue &getConstant(size_t index) { |
|
500 JS_ASSERT(index < numConstants()); |
|
501 return constants()[index]; |
|
502 } |
|
503 size_t numConstants() const { |
|
504 return constantEntries_; |
|
505 } |
|
506 uint32_t frameSlots() const { |
|
507 return frameSlots_; |
|
508 } |
|
509 uint32_t frameSize() const { |
|
510 return frameSize_; |
|
511 } |
|
512 SnapshotOffset bailoutToSnapshot(uint32_t bailoutId) { |
|
513 JS_ASSERT(bailoutId < bailoutEntries_); |
|
514 return bailoutTable()[bailoutId]; |
|
515 } |
|
516 const SafepointIndex *getSafepointIndex(uint32_t disp) const; |
|
517 const SafepointIndex *getSafepointIndex(uint8_t *retAddr) const { |
|
518 JS_ASSERT(containsCodeAddress(retAddr)); |
|
519 return getSafepointIndex(retAddr - method()->raw()); |
|
520 } |
|
521 const OsiIndex *getOsiIndex(uint32_t disp) const; |
|
522 const OsiIndex *getOsiIndex(uint8_t *retAddr) const; |
|
523 inline IonCache &getCacheFromIndex(uint32_t index) { |
|
524 JS_ASSERT(index < cacheEntries_); |
|
525 uint32_t offset = cacheIndex()[index]; |
|
526 return getCache(offset); |
|
527 } |
|
528 inline IonCache &getCache(uint32_t offset) { |
|
529 JS_ASSERT(offset < runtimeSize_); |
|
530 return *(IonCache *) &runtimeData()[offset]; |
|
531 } |
|
532 size_t numCaches() const { |
|
533 return cacheEntries_; |
|
534 } |
|
535 size_t runtimeSize() const { |
|
536 return runtimeSize_; |
|
537 } |
|
538 CacheLocation *getCacheLocs(uint32_t locIndex) { |
|
539 JS_ASSERT(locIndex < runtimeSize_); |
|
540 return (CacheLocation *) &runtimeData()[locIndex]; |
|
541 } |
|
542 void toggleBarriers(bool enabled); |
|
543 void purgeCaches(); |
|
544 void destroyCaches(); |
|
545 void unlinkFromRuntime(FreeOp *fop); |
|
546 void copySnapshots(const SnapshotWriter *writer); |
|
547 void copyRecovers(const RecoverWriter *writer); |
|
548 void copyBailoutTable(const SnapshotOffset *table); |
|
549 void copyConstants(const Value *vp); |
|
550 void copySafepointIndices(const SafepointIndex *firstSafepointIndex, MacroAssembler &masm); |
|
551 void copyOsiIndices(const OsiIndex *firstOsiIndex, MacroAssembler &masm); |
|
552 void copyRuntimeData(const uint8_t *data); |
|
553 void copyCacheEntries(const uint32_t *caches, MacroAssembler &masm); |
|
554 void copySafepoints(const SafepointWriter *writer); |
|
555 void copyCallTargetEntries(JSScript **callTargets); |
|
556 void copyPatchableBackedges(JSContext *cx, JitCode *code, |
|
557 PatchableBackedgeInfo *backedges); |
|
558 |
|
559 bool invalidated() const { |
|
560 return refcount_ != 0; |
|
561 } |
|
562 size_t refcount() const { |
|
563 return refcount_; |
|
564 } |
|
565 void incref() { |
|
566 refcount_++; |
|
567 } |
|
568 void decref(FreeOp *fop) { |
|
569 JS_ASSERT(refcount_); |
|
570 refcount_--; |
|
571 if (!refcount_) |
|
572 Destroy(fop, this); |
|
573 } |
|
574 const types::RecompileInfo& recompileInfo() const { |
|
575 return recompileInfo_; |
|
576 } |
|
577 types::RecompileInfo& recompileInfoRef() { |
|
578 return recompileInfo_; |
|
579 } |
|
580 OptimizationLevel optimizationLevel() const { |
|
581 return optimizationLevel_; |
|
582 } |
|
583 uint32_t incrOsrPcMismatchCounter() { |
|
584 return ++osrPcMismatchCounter_; |
|
585 } |
|
586 void resetOsrPcMismatchCounter() { |
|
587 osrPcMismatchCounter_ = 0; |
|
588 } |
|
589 |
|
590 void setRecompiling() { |
|
591 recompiling_ = true; |
|
592 } |
|
593 |
|
594 bool isRecompiling() const { |
|
595 return recompiling_; |
|
596 } |
|
597 |
|
598 void clearRecompiling() { |
|
599 recompiling_ = false; |
|
600 } |
|
601 |
|
602 static const uint32_t MAX_PARALLEL_AGE = 5; |
|
603 |
|
604 void resetParallelAge() { |
|
605 MOZ_ASSERT(isParallelEntryScript()); |
|
606 parallelAge_ = 0; |
|
607 } |
|
608 uint32_t parallelAge() const { |
|
609 return parallelAge_; |
|
610 } |
|
611 uint32_t increaseParallelAge() { |
|
612 MOZ_ASSERT(isParallelEntryScript()); |
|
613 return ++parallelAge_; |
|
614 } |
|
615 |
|
616 static void writeBarrierPre(Zone *zone, IonScript *ionScript); |
|
617 }; |
|
618 |
|
619 // Execution information for a basic block which may persist after the |
|
620 // accompanying IonScript is destroyed, for use during profiling. |
|
621 struct IonBlockCounts |
|
622 { |
|
623 private: |
|
624 uint32_t id_; |
|
625 |
|
626 // Approximate bytecode in the outer (not inlined) script this block |
|
627 // was generated from. |
|
628 uint32_t offset_; |
|
629 |
|
630 // ids for successors of this block. |
|
631 uint32_t numSuccessors_; |
|
632 uint32_t *successors_; |
|
633 |
|
634 // Hit count for this block. |
|
635 uint64_t hitCount_; |
|
636 |
|
637 // Text information about the code generated for this block. |
|
638 char *code_; |
|
639 |
|
640 public: |
|
641 |
|
642 bool init(uint32_t id, uint32_t offset, uint32_t numSuccessors) { |
|
643 id_ = id; |
|
644 offset_ = offset; |
|
645 numSuccessors_ = numSuccessors; |
|
646 if (numSuccessors) { |
|
647 successors_ = js_pod_calloc<uint32_t>(numSuccessors); |
|
648 if (!successors_) |
|
649 return false; |
|
650 } |
|
651 return true; |
|
652 } |
|
653 |
|
654 void destroy() { |
|
655 js_free(successors_); |
|
656 js_free(code_); |
|
657 } |
|
658 |
|
659 uint32_t id() const { |
|
660 return id_; |
|
661 } |
|
662 |
|
663 uint32_t offset() const { |
|
664 return offset_; |
|
665 } |
|
666 |
|
667 size_t numSuccessors() const { |
|
668 return numSuccessors_; |
|
669 } |
|
670 |
|
671 void setSuccessor(size_t i, uint32_t id) { |
|
672 JS_ASSERT(i < numSuccessors_); |
|
673 successors_[i] = id; |
|
674 } |
|
675 |
|
676 uint32_t successor(size_t i) const { |
|
677 JS_ASSERT(i < numSuccessors_); |
|
678 return successors_[i]; |
|
679 } |
|
680 |
|
681 uint64_t *addressOfHitCount() { |
|
682 return &hitCount_; |
|
683 } |
|
684 |
|
685 uint64_t hitCount() const { |
|
686 return hitCount_; |
|
687 } |
|
688 |
|
689 void setCode(const char *code) { |
|
690 char *ncode = (char *) js_malloc(strlen(code) + 1); |
|
691 if (ncode) { |
|
692 strcpy(ncode, code); |
|
693 code_ = ncode; |
|
694 } |
|
695 } |
|
696 |
|
697 const char *code() const { |
|
698 return code_; |
|
699 } |
|
700 }; |
|
701 |
|
702 // Execution information for a compiled script which may persist after the |
|
703 // IonScript is destroyed, for use during profiling. |
|
704 struct IonScriptCounts |
|
705 { |
|
706 private: |
|
707 // Any previous invalidated compilation(s) for the script. |
|
708 IonScriptCounts *previous_; |
|
709 |
|
710 // Information about basic blocks in this script. |
|
711 size_t numBlocks_; |
|
712 IonBlockCounts *blocks_; |
|
713 |
|
714 public: |
|
715 |
|
716 IonScriptCounts() { |
|
717 mozilla::PodZero(this); |
|
718 } |
|
719 |
|
720 ~IonScriptCounts() { |
|
721 for (size_t i = 0; i < numBlocks_; i++) |
|
722 blocks_[i].destroy(); |
|
723 js_free(blocks_); |
|
724 js_delete(previous_); |
|
725 } |
|
726 |
|
727 bool init(size_t numBlocks) { |
|
728 numBlocks_ = numBlocks; |
|
729 blocks_ = js_pod_calloc<IonBlockCounts>(numBlocks); |
|
730 return blocks_ != nullptr; |
|
731 } |
|
732 |
|
733 size_t numBlocks() const { |
|
734 return numBlocks_; |
|
735 } |
|
736 |
|
737 IonBlockCounts &block(size_t i) { |
|
738 JS_ASSERT(i < numBlocks_); |
|
739 return blocks_[i]; |
|
740 } |
|
741 |
|
742 void setPrevious(IonScriptCounts *previous) { |
|
743 previous_ = previous; |
|
744 } |
|
745 |
|
746 IonScriptCounts *previous() const { |
|
747 return previous_; |
|
748 } |
|
749 }; |
|
750 |
|
751 struct VMFunction; |
|
752 |
|
753 class JitCompartment; |
|
754 class JitRuntime; |
|
755 |
|
756 struct AutoFlushICache |
|
757 { |
|
758 private: |
|
759 uintptr_t start_; |
|
760 uintptr_t stop_; |
|
761 const char *name_; |
|
762 bool inhibit_; |
|
763 AutoFlushICache *prev_; |
|
764 |
|
765 public: |
|
766 static void setRange(uintptr_t p, size_t len); |
|
767 static void flush(uintptr_t p, size_t len); |
|
768 static void setInhibit(); |
|
769 ~AutoFlushICache(); |
|
770 AutoFlushICache(const char *nonce, bool inhibit=false); |
|
771 }; |
|
772 |
|
773 } // namespace jit |
|
774 |
|
775 namespace gc { |
|
776 |
|
777 inline bool |
|
778 IsMarked(const jit::VMFunction *) |
|
779 { |
|
780 // VMFunction are only static objects which are used by WeakMaps as keys. |
|
781 // It is considered as a root object which is always marked. |
|
782 return true; |
|
783 } |
|
784 |
|
785 } // namespace gc |
|
786 |
|
787 } // namespace js |
|
788 |
|
789 #endif /* jit_IonCode_h */ |