/src/mozilla-central/js/src/jit/JitcodeMap.h
Line | Count | Source (jump to first uncovered line) |
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_JitcodeMap_h |
8 | | #define jit_JitcodeMap_h |
9 | | |
10 | | #include "jit/CompactBuffer.h" |
11 | | #include "jit/CompileInfo.h" |
12 | | #include "jit/ExecutableAllocator.h" |
13 | | #include "jit/OptimizationTracking.h" |
14 | | |
15 | | namespace js { |
16 | | namespace jit { |
17 | | |
18 | | /* |
19 | | * The Ion jitcode map implements tables to allow mapping from addresses in ion jitcode |
20 | | * to the list of (JSScript*, jsbytecode*) pairs that are implicitly active in the frame at |
21 | | * that point in the native code. |
22 | | * |
23 | | * To represent this information efficiently, a multi-level table is used. |
24 | | * |
25 | | * At the top level, a global splay-tree of JitcodeGlobalEntry describings the mapping for |
26 | | * each individual IonCode script generated by compiles. The entries are ordered by their |
27 | | * nativeStartAddr. |
28 | | * |
29 | | * Every entry in the table is of fixed size, but there are different entry types, |
30 | | * distinguished by the kind field. |
31 | | */ |
32 | | |
33 | | class JitcodeGlobalTable; |
34 | | class JitcodeIonTable; |
35 | | class JitcodeRegionEntry; |
36 | | |
37 | | class JitcodeGlobalEntry; |
38 | | |
39 | | struct NativeToBytecode { |
40 | | CodeOffset nativeOffset; |
41 | | InlineScriptTree* tree; |
42 | | jsbytecode* pc; |
43 | | }; |
44 | | |
45 | | class JitcodeSkiplistTower |
46 | | { |
47 | | public: |
48 | | static const unsigned MAX_HEIGHT = 32; |
49 | | |
50 | | private: |
51 | | uint8_t height_; |
52 | | bool isFree_; |
53 | | JitcodeGlobalEntry* ptrs_[1]; |
54 | | |
55 | | public: |
56 | | explicit JitcodeSkiplistTower(unsigned height) |
57 | | : height_(height), |
58 | | isFree_(false) |
59 | 16 | { |
60 | 16 | MOZ_ASSERT(height >= 1 && height <= MAX_HEIGHT); |
61 | 16 | clearPtrs(); |
62 | 16 | } |
63 | | |
64 | 114 | unsigned height() const { |
65 | 114 | return height_; |
66 | 114 | } |
67 | | |
68 | 0 | JitcodeGlobalEntry** ptrs(unsigned level) { |
69 | 0 | return ptrs_; |
70 | 0 | } |
71 | | |
72 | 475 | JitcodeGlobalEntry* next(unsigned level) const { |
73 | 475 | MOZ_ASSERT(!isFree_); |
74 | 475 | MOZ_ASSERT(level < height()); |
75 | 475 | return ptrs_[level]; |
76 | 475 | } |
77 | 104 | void setNext(unsigned level, JitcodeGlobalEntry* entry) { |
78 | 104 | MOZ_ASSERT(!isFree_); |
79 | 104 | MOZ_ASSERT(level < height()); |
80 | 104 | ptrs_[level] = entry; |
81 | 104 | } |
82 | | |
83 | | // |
84 | | // When stored in a free-list, towers use 'ptrs_[0]' to store a |
85 | | // pointer to the next tower. In this context only, 'ptrs_[0]' |
86 | | // may refer to a |JitcodeSkiplistTower*| instead of a |
87 | | // |JitcodeGlobalEntry*|. |
88 | | // |
89 | | |
90 | 25 | void addToFreeList(JitcodeSkiplistTower** freeList) { |
91 | 25 | JitcodeSkiplistTower* nextFreeTower = *freeList; |
92 | 25 | MOZ_ASSERT_IF(nextFreeTower, nextFreeTower->isFree_ && |
93 | 25 | nextFreeTower->height() == height_); |
94 | 25 | ptrs_[0] = (JitcodeGlobalEntry*) nextFreeTower; |
95 | 25 | isFree_ = true; |
96 | 25 | *freeList = this; |
97 | 25 | } |
98 | | |
99 | 27 | static JitcodeSkiplistTower* PopFromFreeList(JitcodeSkiplistTower** freeList) { |
100 | 27 | if (!*freeList) { |
101 | 16 | return nullptr; |
102 | 16 | } |
103 | 11 | |
104 | 11 | JitcodeSkiplistTower* tower = *freeList; |
105 | 11 | MOZ_ASSERT(tower->isFree_); |
106 | 11 | JitcodeSkiplistTower* nextFreeTower = (JitcodeSkiplistTower*) tower->ptrs_[0]; |
107 | 11 | tower->clearPtrs(); |
108 | 11 | tower->isFree_ = false; |
109 | 11 | *freeList = nextFreeTower; |
110 | 11 | return tower; |
111 | 11 | } |
112 | | |
113 | 16 | static size_t CalculateSize(unsigned height) { |
114 | 16 | MOZ_ASSERT(height >= 1); |
115 | 16 | return sizeof(JitcodeSkiplistTower) + |
116 | 16 | (sizeof(JitcodeGlobalEntry*) * (height - 1)); |
117 | 16 | } |
118 | | |
119 | | private: |
120 | 27 | void clearPtrs() { |
121 | 82 | for (unsigned i = 0; i < height_; i++) { |
122 | 55 | ptrs_[0] = nullptr; |
123 | 55 | } |
124 | 27 | } |
125 | | }; |
126 | | |
127 | | class JitcodeGlobalEntry |
128 | | { |
129 | | friend class JitcodeGlobalTable; |
130 | | |
131 | | public: |
132 | | enum Kind { |
133 | | INVALID = 0, |
134 | | Ion, |
135 | | Baseline, |
136 | | IonCache, |
137 | | Dummy, |
138 | | Query, |
139 | | LIMIT |
140 | | }; |
141 | | JS_STATIC_ASSERT(LIMIT <= 8); |
142 | | |
143 | | struct BytecodeLocation { |
144 | | JSScript* script; |
145 | | jsbytecode* pc; |
146 | 0 | BytecodeLocation(JSScript* script, jsbytecode* pc) : script(script), pc(pc) {} |
147 | | }; |
148 | | typedef Vector<BytecodeLocation, 0, SystemAllocPolicy> BytecodeLocationVector; |
149 | | |
150 | | struct BaseEntry |
151 | | { |
152 | | static const uint64_t kNoSampleInBuffer = UINT64_MAX; |
153 | | |
154 | | JitCode* jitcode_; |
155 | | void* nativeStartAddr_; |
156 | | void* nativeEndAddr_; |
157 | | // If this entry is referenced from the profiler buffer, this is the |
158 | | // position where the most recent sample that references it starts. |
159 | | // Otherwise set to kNoSampleInBuffer. |
160 | | uint64_t samplePositionInBuffer_; |
161 | | Kind kind_ : 7; |
162 | | |
163 | 66 | void init() { |
164 | 66 | jitcode_ = nullptr; |
165 | 66 | nativeStartAddr_ = nullptr; |
166 | 66 | nativeEndAddr_ = nullptr; |
167 | 66 | samplePositionInBuffer_ = kNoSampleInBuffer; |
168 | 66 | kind_ = INVALID; |
169 | 66 | } |
170 | | |
171 | | void init(Kind kind, JitCode* code, |
172 | | void* nativeStartAddr, void* nativeEndAddr) |
173 | 27 | { |
174 | 27 | MOZ_ASSERT_IF(kind != Query, code); |
175 | 27 | MOZ_ASSERT(nativeStartAddr); |
176 | 27 | MOZ_ASSERT(nativeEndAddr); |
177 | 27 | MOZ_ASSERT(kind > INVALID && kind < LIMIT); |
178 | 27 | jitcode_ = code; |
179 | 27 | nativeStartAddr_ = nativeStartAddr; |
180 | 27 | nativeEndAddr_ = nativeEndAddr; |
181 | 27 | samplePositionInBuffer_ = kNoSampleInBuffer; |
182 | 27 | kind_ = kind; |
183 | 27 | } |
184 | | |
185 | 0 | void setSamplePositionInBuffer(uint64_t bufferWritePos) { |
186 | 0 | samplePositionInBuffer_ = bufferWritePos; |
187 | 0 | } |
188 | 124 | void setAsExpired() { |
189 | 124 | samplePositionInBuffer_ = kNoSampleInBuffer; |
190 | 124 | } |
191 | 0 | bool isSampled(uint64_t bufferRangeStart) { |
192 | 0 | if (samplePositionInBuffer_ == kNoSampleInBuffer) { |
193 | 0 | return false; |
194 | 0 | } |
195 | 0 | return bufferRangeStart <= samplePositionInBuffer_; |
196 | 0 | } |
197 | | |
198 | 434 | Kind kind() const { |
199 | 434 | return kind_; |
200 | 434 | } |
201 | 272 | JitCode* jitcode() const { |
202 | 272 | return jitcode_; |
203 | 272 | } |
204 | 122 | void* nativeStartAddr() const { |
205 | 122 | return nativeStartAddr_; |
206 | 122 | } |
207 | 0 | void* nativeEndAddr() const { |
208 | 0 | return nativeEndAddr_; |
209 | 0 | } |
210 | | |
211 | 0 | bool startsBelowPointer(void* ptr) const { |
212 | 0 | return ((uint8_t*)nativeStartAddr()) <= ((uint8_t*) ptr); |
213 | 0 | } |
214 | 0 | bool endsAbovePointer(void* ptr) const { |
215 | 0 | return ((uint8_t*)nativeEndAddr()) > ((uint8_t*) ptr); |
216 | 0 | } |
217 | 0 | bool containsPointer(void* ptr) const { |
218 | 0 | return startsBelowPointer(ptr) && endsAbovePointer(ptr); |
219 | 0 | } |
220 | | |
221 | | template <class ShouldTraceProvider> bool traceJitcode(JSTracer* trc); |
222 | | bool isJitcodeMarkedFromAnyThread(JSRuntime* rt); |
223 | | bool isJitcodeAboutToBeFinalized(); |
224 | | }; |
225 | | |
226 | | struct IonEntry : public BaseEntry |
227 | | { |
228 | | // regionTable_ points to the start of the region table within the |
229 | | // packed map for compile represented by this entry. Since the |
230 | | // region table occurs at the tail of the memory region, this pointer |
231 | | // points somewhere inside the region memory space, and not to the start |
232 | | // of the memory space. |
233 | | JitcodeIonTable* regionTable_; |
234 | | |
235 | | // optsRegionTable_ points to the table within the compact |
236 | | // optimizations map indexing all regions that have tracked |
237 | | // optimization attempts. optsTypesTable_ is the tracked typed info |
238 | | // associated with the attempts vectors; it is the same length as the |
239 | | // attempts table. optsAttemptsTable_ is the table indexing those |
240 | | // attempts vectors. |
241 | | // |
242 | | // All pointers point into the same block of memory; the beginning of |
243 | | // the block is optRegionTable_->payloadStart(). |
244 | | const IonTrackedOptimizationsRegionTable* optsRegionTable_; |
245 | | const IonTrackedOptimizationsTypesTable* optsTypesTable_; |
246 | | const IonTrackedOptimizationsAttemptsTable* optsAttemptsTable_; |
247 | | |
248 | | // The types table above records type sets, which have been gathered |
249 | | // into one vector here. |
250 | | IonTrackedTypeVector* optsAllTypes_; |
251 | | |
252 | | // Linked list pointers to allow traversing through all entries that |
253 | | // could possibly contain nursery pointers. Note that the contained |
254 | | // pointers can be mutated into nursery pointers at any time. |
255 | | IonEntry* prevNursery_; |
256 | | IonEntry* nextNursery_; |
257 | | |
258 | | struct ScriptNamePair { |
259 | | JSScript* script; |
260 | | char* str; |
261 | | }; |
262 | | |
263 | | struct SizedScriptList { |
264 | | uint32_t size; |
265 | | ScriptNamePair pairs[1]; |
266 | 0 | SizedScriptList(uint32_t sz, JSScript** scrs, char** strs) : size(sz) { |
267 | 0 | for (uint32_t i = 0; i < size; i++) { |
268 | 0 | pairs[i].script = scrs[i]; |
269 | 0 | pairs[i].str = strs[i]; |
270 | 0 | } |
271 | 0 | } |
272 | | |
273 | 0 | static uint32_t AllocSizeFor(uint32_t nscripts) { |
274 | 0 | return sizeof(SizedScriptList) + ((nscripts - 1) * sizeof(ScriptNamePair)); |
275 | 0 | } |
276 | | }; |
277 | | |
278 | | SizedScriptList* scriptList_; |
279 | | |
280 | | void init(JitCode* code, void* nativeStartAddr, void* nativeEndAddr, |
281 | | SizedScriptList* scriptList, JitcodeIonTable* regionTable) |
282 | 0 | { |
283 | 0 | MOZ_ASSERT(scriptList); |
284 | 0 | MOZ_ASSERT(regionTable); |
285 | 0 | BaseEntry::init(Ion, code, nativeStartAddr, nativeEndAddr); |
286 | 0 | regionTable_ = regionTable; |
287 | 0 | scriptList_ = scriptList; |
288 | 0 | optsRegionTable_ = nullptr; |
289 | 0 | optsTypesTable_ = nullptr; |
290 | 0 | optsAllTypes_ = nullptr; |
291 | 0 | optsAttemptsTable_ = nullptr; |
292 | 0 | prevNursery_ = nextNursery_ = nullptr; |
293 | 0 | } |
294 | | |
295 | | void initTrackedOptimizations(const IonTrackedOptimizationsRegionTable* regionTable, |
296 | | const IonTrackedOptimizationsTypesTable* typesTable, |
297 | | const IonTrackedOptimizationsAttemptsTable* attemptsTable, |
298 | | IonTrackedTypeVector* allTypes) |
299 | 0 | { |
300 | 0 | optsRegionTable_ = regionTable; |
301 | 0 | optsTypesTable_ = typesTable; |
302 | 0 | optsAttemptsTable_ = attemptsTable; |
303 | 0 | optsAllTypes_ = allTypes; |
304 | 0 | } |
305 | | |
306 | 0 | SizedScriptList* sizedScriptList() const { |
307 | 0 | return scriptList_; |
308 | 0 | } |
309 | | |
310 | 0 | unsigned numScripts() const { |
311 | 0 | return scriptList_->size; |
312 | 0 | } |
313 | | |
314 | 0 | JSScript* getScript(unsigned idx) const { |
315 | 0 | MOZ_ASSERT(idx < numScripts()); |
316 | 0 | return sizedScriptList()->pairs[idx].script; |
317 | 0 | } |
318 | | |
319 | 0 | const char* getStr(unsigned idx) const { |
320 | 0 | MOZ_ASSERT(idx < numScripts()); |
321 | 0 | return sizedScriptList()->pairs[idx].str; |
322 | 0 | } |
323 | | |
324 | | void destroy(); |
325 | | |
326 | 0 | JitcodeIonTable* regionTable() const { |
327 | 0 | return regionTable_; |
328 | 0 | } |
329 | | |
330 | 0 | int scriptIndex(JSScript* script) const { |
331 | 0 | unsigned count = numScripts(); |
332 | 0 | for (unsigned i = 0; i < count; i++) { |
333 | 0 | if (getScript(i) == script) { |
334 | 0 | return i; |
335 | 0 | } |
336 | 0 | } |
337 | 0 | return -1; |
338 | 0 | } |
339 | | |
340 | | void* canonicalNativeAddrFor(void* ptr) const; |
341 | | |
342 | | MOZ_MUST_USE bool callStackAtAddr(void* ptr, BytecodeLocationVector& results, |
343 | | uint32_t* depth) const; |
344 | | |
345 | | uint32_t callStackAtAddr(void* ptr, const char** results, |
346 | | uint32_t maxResults) const; |
347 | | |
348 | | void youngestFrameLocationAtAddr(void* ptr, |
349 | | JSScript** script, jsbytecode** pc) const; |
350 | | |
351 | 0 | bool hasTrackedOptimizations() const { |
352 | 0 | return !!optsRegionTable_; |
353 | 0 | } |
354 | | |
355 | 0 | const IonTrackedOptimizationsRegionTable* trackedOptimizationsRegionTable() const { |
356 | 0 | MOZ_ASSERT(hasTrackedOptimizations()); |
357 | 0 | return optsRegionTable_; |
358 | 0 | } |
359 | | |
360 | 0 | uint8_t numOptimizationAttempts() const { |
361 | 0 | MOZ_ASSERT(hasTrackedOptimizations()); |
362 | 0 | return optsAttemptsTable_->numEntries(); |
363 | 0 | } |
364 | | |
365 | 0 | IonTrackedOptimizationsAttempts trackedOptimizationAttempts(uint8_t index) { |
366 | 0 | MOZ_ASSERT(hasTrackedOptimizations()); |
367 | 0 | return optsAttemptsTable_->entry(index); |
368 | 0 | } |
369 | | |
370 | 0 | IonTrackedOptimizationsTypeInfo trackedOptimizationTypeInfo(uint8_t index) { |
371 | 0 | MOZ_ASSERT(hasTrackedOptimizations()); |
372 | 0 | return optsTypesTable_->entry(index); |
373 | 0 | } |
374 | | |
375 | 0 | const IonTrackedTypeVector* allTrackedTypes() { |
376 | 0 | MOZ_ASSERT(hasTrackedOptimizations()); |
377 | 0 | return optsAllTypes_; |
378 | 0 | } |
379 | | |
380 | | mozilla::Maybe<uint8_t> trackedOptimizationIndexAtAddr( |
381 | | void* ptr, |
382 | | uint32_t* entryOffsetOut); |
383 | | |
384 | | void forEachOptimizationAttempt(uint8_t index, |
385 | | JS::ForEachTrackedOptimizationAttemptOp& op); |
386 | | void forEachOptimizationTypeInfo(uint8_t index, |
387 | | IonTrackedOptimizationsTypeInfo::ForEachOpAdapter& op); |
388 | | |
389 | | template <class ShouldTraceProvider> bool trace(JSTracer* trc); |
390 | | void sweepChildren(); |
391 | | bool isMarkedFromAnyThread(JSRuntime* rt); |
392 | | }; |
393 | | |
394 | | struct BaselineEntry : public BaseEntry |
395 | | { |
396 | | JSScript* script_; |
397 | | const char* str_; |
398 | | |
399 | | // Last location that caused Ion to abort compilation and the reason |
400 | | // therein, if any. Only actionable aborts are tracked. Internal |
401 | | // errors like OOMs are not. |
402 | | jsbytecode* ionAbortPc_; |
403 | | const char* ionAbortMessage_; |
404 | | |
405 | | void init(JitCode* code, void* nativeStartAddr, void* nativeEndAddr, |
406 | | JSScript* script, const char* str) |
407 | 14 | { |
408 | 14 | MOZ_ASSERT(script != nullptr); |
409 | 14 | BaseEntry::init(Baseline, code, nativeStartAddr, nativeEndAddr); |
410 | 14 | script_ = script; |
411 | 14 | str_ = str; |
412 | 14 | } |
413 | | |
414 | 0 | JSScript* script() const { |
415 | 0 | return script_; |
416 | 0 | } |
417 | | |
418 | 0 | const char* str() const { |
419 | 0 | return str_; |
420 | 0 | } |
421 | | |
422 | 0 | void trackIonAbort(jsbytecode* pc, const char* message) { |
423 | 0 | MOZ_ASSERT(script_->containsPC(pc)); |
424 | 0 | MOZ_ASSERT(message); |
425 | 0 | ionAbortPc_ = pc; |
426 | 0 | ionAbortMessage_ = message; |
427 | 0 | } |
428 | | |
429 | 0 | bool hadIonAbort() const { |
430 | 0 | MOZ_ASSERT(!ionAbortPc_ || ionAbortMessage_); |
431 | 0 | return ionAbortPc_ != nullptr; |
432 | 0 | } |
433 | | |
434 | | void destroy(); |
435 | | |
436 | | void* canonicalNativeAddrFor(void* ptr) const; |
437 | | |
438 | | MOZ_MUST_USE bool callStackAtAddr(void* ptr, BytecodeLocationVector& results, |
439 | | uint32_t* depth) const; |
440 | | |
441 | | uint32_t callStackAtAddr(void* ptr, const char** results, |
442 | | uint32_t maxResults) const; |
443 | | |
444 | | void youngestFrameLocationAtAddr(void* ptr, |
445 | | JSScript** script, jsbytecode** pc) const; |
446 | | |
447 | | template <class ShouldTraceProvider> bool trace(JSTracer* trc); |
448 | | void sweepChildren(); |
449 | | bool isMarkedFromAnyThread(JSRuntime* rt); |
450 | | }; |
451 | | |
452 | | struct IonCacheEntry : public BaseEntry |
453 | | { |
454 | | void* rejoinAddr_; |
455 | | JS::TrackedOutcome trackedOutcome_; |
456 | | |
457 | | void init(JitCode* code, void* nativeStartAddr, void* nativeEndAddr, |
458 | | void* rejoinAddr, JS::TrackedOutcome trackedOutcome) |
459 | 0 | { |
460 | 0 | MOZ_ASSERT(rejoinAddr != nullptr); |
461 | 0 | BaseEntry::init(IonCache, code, nativeStartAddr, nativeEndAddr); |
462 | 0 | rejoinAddr_ = rejoinAddr; |
463 | 0 | trackedOutcome_ = trackedOutcome; |
464 | 0 | } |
465 | | |
466 | 0 | void* rejoinAddr() const { |
467 | 0 | return rejoinAddr_; |
468 | 0 | } |
469 | 0 | JS::TrackedOutcome trackedOutcome() const { |
470 | 0 | return trackedOutcome_; |
471 | 0 | } |
472 | | |
473 | 0 | void destroy() {} |
474 | | |
475 | | void* canonicalNativeAddrFor() const; |
476 | | |
477 | | MOZ_MUST_USE bool callStackAtAddr(JSRuntime* rt, void* ptr, BytecodeLocationVector& results, |
478 | | uint32_t* depth) const; |
479 | | |
480 | | uint32_t callStackAtAddr(JSRuntime* rt, void* ptr, const char** results, |
481 | | uint32_t maxResults) const; |
482 | | |
483 | | void youngestFrameLocationAtAddr(JSRuntime* rt, void* ptr, |
484 | | JSScript** script, jsbytecode** pc) const; |
485 | | |
486 | 0 | bool hasTrackedOptimizations() const { return true; } |
487 | | mozilla::Maybe<uint8_t> trackedOptimizationIndexAtAddr( |
488 | | JSRuntime *rt, |
489 | | void* ptr, |
490 | | uint32_t* entryOffsetOut); |
491 | | void forEachOptimizationAttempt(JSRuntime* rt, uint8_t index, |
492 | | JS::ForEachTrackedOptimizationAttemptOp& op); |
493 | | void forEachOptimizationTypeInfo(JSRuntime* rt, uint8_t index, |
494 | | IonTrackedOptimizationsTypeInfo::ForEachOpAdapter& op); |
495 | | |
496 | | template <class ShouldTraceProvider> bool trace(JSTracer* trc); |
497 | | void sweepChildren(JSRuntime* rt); |
498 | | bool isMarkedFromAnyThread(JSRuntime* rt); |
499 | | }; |
500 | | |
501 | | // Dummy entries are created for jitcode generated when profiling is not turned on, |
502 | | // so that they have representation in the global table if they are on the |
503 | | // stack when profiling is enabled. |
504 | | struct DummyEntry : public BaseEntry |
505 | | { |
506 | 13 | void init(JitCode* code, void* nativeStartAddr, void* nativeEndAddr) { |
507 | 13 | BaseEntry::init(Dummy, code, nativeStartAddr, nativeEndAddr); |
508 | 13 | } |
509 | | |
510 | 12 | void destroy() {} |
511 | | |
512 | 0 | void* canonicalNativeAddrFor(JSRuntime* rt, void* ptr) const { |
513 | 0 | return nullptr; |
514 | 0 | } |
515 | | |
516 | | MOZ_MUST_USE bool callStackAtAddr(JSRuntime* rt, void* ptr, BytecodeLocationVector& results, |
517 | | uint32_t* depth) const |
518 | 0 | { |
519 | 0 | return true; |
520 | 0 | } |
521 | | |
522 | | uint32_t callStackAtAddr(JSRuntime* rt, void* ptr, const char** results, |
523 | | uint32_t maxResults) const |
524 | 0 | { |
525 | 0 | return 0; |
526 | 0 | } |
527 | | |
528 | | void youngestFrameLocationAtAddr(JSRuntime* rt, void* ptr, |
529 | | JSScript** script, jsbytecode** pc) const |
530 | 0 | { |
531 | 0 | *script = nullptr; |
532 | 0 | *pc = nullptr; |
533 | 0 | } |
534 | | }; |
535 | | |
536 | | // QueryEntry is never stored in the table, just used for queries |
537 | | // where an instance of JitcodeGlobalEntry is required to do tree |
538 | | // lookups. |
539 | | struct QueryEntry : public BaseEntry |
540 | | { |
541 | 0 | void init(void* addr) { |
542 | 0 | BaseEntry::init(Query, nullptr, addr, addr); |
543 | 0 | } |
544 | 0 | uint8_t* addr() const { |
545 | 0 | return reinterpret_cast<uint8_t*>(nativeStartAddr()); |
546 | 0 | } |
547 | 0 | void destroy() {} |
548 | | }; |
549 | | |
550 | | private: |
551 | | JitcodeSkiplistTower* tower_; |
552 | | |
553 | | union { |
554 | | // Shadowing BaseEntry instance to allow access to base fields |
555 | | // and type extraction. |
556 | | BaseEntry base_; |
557 | | |
558 | | // The most common entry type: describing jitcode generated by |
559 | | // Ion main-line code. |
560 | | IonEntry ion_; |
561 | | |
562 | | // Baseline jitcode. |
563 | | BaselineEntry baseline_; |
564 | | |
565 | | // IonCache stubs. |
566 | | IonCacheEntry ionCache_; |
567 | | |
568 | | // Dummy entries. |
569 | | DummyEntry dummy_; |
570 | | |
571 | | // When doing queries on the SplayTree for particular addresses, |
572 | | // the query addresses are representd using a QueryEntry. |
573 | | QueryEntry query_; |
574 | | }; |
575 | | |
576 | | public: |
577 | | JitcodeGlobalEntry() |
578 | | : tower_(nullptr) |
579 | 66 | { |
580 | 66 | base_.init(); |
581 | 66 | } |
582 | | |
583 | | explicit JitcodeGlobalEntry(const IonEntry& ion) |
584 | | : JitcodeGlobalEntry() |
585 | 0 | { |
586 | 0 | ion_ = ion; |
587 | 0 | } |
588 | | |
589 | | explicit JitcodeGlobalEntry(const BaselineEntry& baseline) |
590 | | : JitcodeGlobalEntry() |
591 | 14 | { |
592 | 14 | baseline_ = baseline; |
593 | 14 | } |
594 | | |
595 | | explicit JitcodeGlobalEntry(const IonCacheEntry& ionCache) |
596 | | : JitcodeGlobalEntry() |
597 | 0 | { |
598 | 0 | ionCache_ = ionCache; |
599 | 0 | } |
600 | | |
601 | | explicit JitcodeGlobalEntry(const DummyEntry& dummy) |
602 | | : JitcodeGlobalEntry() |
603 | 13 | { |
604 | 13 | dummy_ = dummy; |
605 | 13 | } |
606 | | |
607 | | explicit JitcodeGlobalEntry(const QueryEntry& query) |
608 | | : JitcodeGlobalEntry() |
609 | 0 | { |
610 | 0 | query_ = query; |
611 | 0 | } |
612 | | |
613 | 0 | static JitcodeGlobalEntry MakeQuery(void* ptr) { |
614 | 0 | QueryEntry query; |
615 | 0 | query.init(ptr); |
616 | 0 | return JitcodeGlobalEntry(query); |
617 | 0 | } |
618 | | |
619 | 25 | void destroy() { |
620 | 25 | switch (kind()) { |
621 | 25 | case Ion: |
622 | 0 | ionEntry().destroy(); |
623 | 0 | break; |
624 | 25 | case Baseline: |
625 | 13 | baselineEntry().destroy(); |
626 | 13 | break; |
627 | 25 | case IonCache: |
628 | 0 | ionCacheEntry().destroy(); |
629 | 0 | break; |
630 | 25 | case Dummy: |
631 | 12 | dummyEntry().destroy(); |
632 | 12 | break; |
633 | 25 | case Query: |
634 | 0 | queryEntry().destroy(); |
635 | 0 | break; |
636 | 25 | default: |
637 | 0 | MOZ_CRASH("Invalid JitcodeGlobalEntry kind."); |
638 | 25 | } |
639 | 25 | } |
640 | | |
641 | 0 | JitCode* jitcode() const { |
642 | 0 | return baseEntry().jitcode(); |
643 | 0 | } |
644 | 122 | void* nativeStartAddr() const { |
645 | 122 | return base_.nativeStartAddr(); |
646 | 122 | } |
647 | 0 | void* nativeEndAddr() const { |
648 | 0 | return base_.nativeEndAddr(); |
649 | 0 | } |
650 | | |
651 | 0 | void setSamplePositionInBuffer(uint64_t samplePositionInBuffer) { |
652 | 0 | baseEntry().setSamplePositionInBuffer(samplePositionInBuffer); |
653 | 0 | } |
654 | 124 | void setAsExpired() { |
655 | 124 | baseEntry().setAsExpired(); |
656 | 124 | } |
657 | 0 | bool isSampled(uint64_t bufferRangeStart) { |
658 | 0 | return baseEntry().isSampled(bufferRangeStart); |
659 | 0 | } |
660 | | |
661 | 0 | bool startsBelowPointer(void* ptr) const { |
662 | 0 | return base_.startsBelowPointer(ptr); |
663 | 0 | } |
664 | 0 | bool endsAbovePointer(void* ptr) const { |
665 | 0 | return base_.endsAbovePointer(ptr); |
666 | 0 | } |
667 | 0 | bool containsPointer(void* ptr) const { |
668 | 0 | return base_.containsPointer(ptr); |
669 | 0 | } |
670 | | |
671 | 0 | bool overlapsWith(const JitcodeGlobalEntry& entry) const { |
672 | 0 | // Catch full containment of |entry| within |this|, and partial overlaps. |
673 | 0 | if (containsPointer(entry.nativeStartAddr()) || containsPointer(entry.nativeEndAddr())) { |
674 | 0 | return true; |
675 | 0 | } |
676 | 0 |
|
677 | 0 | // Catch full containment of |this| within |entry|. |
678 | 0 | if (startsBelowPointer(entry.nativeEndAddr()) && endsAbovePointer(entry.nativeStartAddr())) { |
679 | 0 | return true; |
680 | 0 | } |
681 | 0 |
|
682 | 0 | return false; |
683 | 0 | } |
684 | | |
685 | 434 | Kind kind() const { |
686 | 434 | return base_.kind(); |
687 | 434 | } |
688 | | |
689 | 0 | bool isValid() const { |
690 | 0 | return (kind() > INVALID) && (kind() < LIMIT); |
691 | 0 | } |
692 | 176 | bool isIon() const { |
693 | 176 | return kind() == Ion; |
694 | 176 | } |
695 | 0 | bool isBaseline() const { |
696 | 0 | return kind() == Baseline; |
697 | 0 | } |
698 | 0 | bool isIonCache() const { |
699 | 0 | return kind() == IonCache; |
700 | 0 | } |
701 | 0 | bool isDummy() const { |
702 | 0 | return kind() == Dummy; |
703 | 0 | } |
704 | 122 | bool isQuery() const { |
705 | 122 | return kind() == Query; |
706 | 122 | } |
707 | | |
708 | 656 | BaseEntry& baseEntry() { |
709 | 656 | MOZ_ASSERT(isValid()); |
710 | 656 | return base_; |
711 | 656 | } |
712 | 0 | IonEntry& ionEntry() { |
713 | 0 | MOZ_ASSERT(isIon()); |
714 | 0 | return ion_; |
715 | 0 | } |
716 | 67 | BaselineEntry& baselineEntry() { |
717 | 67 | MOZ_ASSERT(isBaseline()); |
718 | 67 | return baseline_; |
719 | 67 | } |
720 | 0 | IonCacheEntry& ionCacheEntry() { |
721 | 0 | MOZ_ASSERT(isIonCache()); |
722 | 0 | return ionCache_; |
723 | 0 | } |
724 | 12 | DummyEntry& dummyEntry() { |
725 | 12 | MOZ_ASSERT(isDummy()); |
726 | 12 | return dummy_; |
727 | 12 | } |
728 | 0 | QueryEntry& queryEntry() { |
729 | 0 | MOZ_ASSERT(isQuery()); |
730 | 0 | return query_; |
731 | 0 | } |
732 | | |
733 | 0 | const BaseEntry& baseEntry() const { |
734 | 0 | MOZ_ASSERT(isValid()); |
735 | 0 | return base_; |
736 | 0 | } |
737 | 0 | const IonEntry& ionEntry() const { |
738 | 0 | MOZ_ASSERT(isIon()); |
739 | 0 | return ion_; |
740 | 0 | } |
741 | 0 | const BaselineEntry& baselineEntry() const { |
742 | 0 | MOZ_ASSERT(isBaseline()); |
743 | 0 | return baseline_; |
744 | 0 | } |
745 | 0 | const IonCacheEntry& ionCacheEntry() const { |
746 | 0 | MOZ_ASSERT(isIonCache()); |
747 | 0 | return ionCache_; |
748 | 0 | } |
749 | 0 | const DummyEntry& dummyEntry() const { |
750 | 0 | MOZ_ASSERT(isDummy()); |
751 | 0 | return dummy_; |
752 | 0 | } |
753 | 0 | const QueryEntry& queryEntry() const { |
754 | 0 | MOZ_ASSERT(isQuery()); |
755 | 0 | return query_; |
756 | 0 | } |
757 | | |
758 | 0 | void* canonicalNativeAddrFor(JSRuntime* rt, void* ptr) const { |
759 | 0 | switch (kind()) { |
760 | 0 | case Ion: |
761 | 0 | return ionEntry().canonicalNativeAddrFor(ptr); |
762 | 0 | case Baseline: |
763 | 0 | return baselineEntry().canonicalNativeAddrFor(ptr); |
764 | 0 | case IonCache: |
765 | 0 | return ionCacheEntry().canonicalNativeAddrFor(); |
766 | 0 | case Dummy: |
767 | 0 | return dummyEntry().canonicalNativeAddrFor(rt, ptr); |
768 | 0 | default: |
769 | 0 | MOZ_CRASH("Invalid JitcodeGlobalEntry kind."); |
770 | 0 | } |
771 | 0 | return nullptr; |
772 | 0 | } |
773 | | |
774 | | // Read the inline call stack at a given point in the native code and append into |
775 | | // the given vector. Innermost (script,pc) pair will be appended first, and |
776 | | // outermost appended last. |
777 | | // |
778 | | // Returns false on memory failure. |
779 | | MOZ_MUST_USE bool callStackAtAddr(JSRuntime* rt, void* ptr, BytecodeLocationVector& results, |
780 | | uint32_t* depth) const |
781 | 0 | { |
782 | 0 | switch (kind()) { |
783 | 0 | case Ion: |
784 | 0 | return ionEntry().callStackAtAddr(ptr, results, depth); |
785 | 0 | case Baseline: |
786 | 0 | return baselineEntry().callStackAtAddr(ptr, results, depth); |
787 | 0 | case IonCache: |
788 | 0 | return ionCacheEntry().callStackAtAddr(rt, ptr, results, depth); |
789 | 0 | case Dummy: |
790 | 0 | return dummyEntry().callStackAtAddr(rt, ptr, results, depth); |
791 | 0 | default: |
792 | 0 | MOZ_CRASH("Invalid JitcodeGlobalEntry kind."); |
793 | 0 | } |
794 | 0 | return false; |
795 | 0 | } |
796 | | |
797 | | uint32_t callStackAtAddr(JSRuntime* rt, void* ptr, const char** results, |
798 | | uint32_t maxResults) const |
799 | 0 | { |
800 | 0 | switch (kind()) { |
801 | 0 | case Ion: |
802 | 0 | return ionEntry().callStackAtAddr(ptr, results, maxResults); |
803 | 0 | case Baseline: |
804 | 0 | return baselineEntry().callStackAtAddr(ptr, results, maxResults); |
805 | 0 | case IonCache: |
806 | 0 | return ionCacheEntry().callStackAtAddr(rt, ptr, results, maxResults); |
807 | 0 | case Dummy: |
808 | 0 | return dummyEntry().callStackAtAddr(rt, ptr, results, maxResults); |
809 | 0 | default: |
810 | 0 | MOZ_CRASH("Invalid JitcodeGlobalEntry kind."); |
811 | 0 | } |
812 | 0 | return false; |
813 | 0 | } |
814 | | |
815 | | void youngestFrameLocationAtAddr(JSRuntime* rt, void* ptr, |
816 | | JSScript** script, jsbytecode** pc) const |
817 | 0 | { |
818 | 0 | switch (kind()) { |
819 | 0 | case Ion: |
820 | 0 | return ionEntry().youngestFrameLocationAtAddr(ptr, script, pc); |
821 | 0 | case Baseline: |
822 | 0 | return baselineEntry().youngestFrameLocationAtAddr(ptr, script, pc); |
823 | 0 | case IonCache: |
824 | 0 | return ionCacheEntry().youngestFrameLocationAtAddr(rt, ptr, script, pc); |
825 | 0 | case Dummy: |
826 | 0 | return dummyEntry().youngestFrameLocationAtAddr(rt, ptr, script, pc); |
827 | 0 | default: |
828 | 0 | MOZ_CRASH("Invalid JitcodeGlobalEntry kind."); |
829 | 0 | } |
830 | 0 | } |
831 | | |
832 | | // Figure out the number of the (JSScript*, jsbytecode*) pairs that are active |
833 | | // at this location. |
834 | | uint32_t lookupInlineCallDepth(void* ptr); |
835 | | |
836 | | // Compare two global entries. |
837 | | static int compare(const JitcodeGlobalEntry& ent1, const JitcodeGlobalEntry& ent2); |
838 | 61 | int compareTo(const JitcodeGlobalEntry& other) { |
839 | 61 | return compare(*this, other); |
840 | 61 | } |
841 | | |
842 | | // Compute a profiling string for a given script. |
843 | | static char* createScriptString(JSContext* cx, JSScript* script, size_t* length=nullptr); |
844 | | |
845 | 0 | bool hasTrackedOptimizations() const { |
846 | 0 | switch (kind()) { |
847 | 0 | case Ion: |
848 | 0 | return ionEntry().hasTrackedOptimizations(); |
849 | 0 | case IonCache: |
850 | 0 | return ionCacheEntry().hasTrackedOptimizations(); |
851 | 0 | case Baseline: |
852 | 0 | case Dummy: |
853 | 0 | break; |
854 | 0 | default: |
855 | 0 | MOZ_CRASH("Invalid JitcodeGlobalEntry kind."); |
856 | 0 | } |
857 | 0 | return false; |
858 | 0 | } |
859 | | |
860 | 176 | bool canHoldNurseryPointers() const { |
861 | 176 | return isIon() && ionEntry().hasTrackedOptimizations(); |
862 | 176 | } |
863 | | |
864 | | mozilla::Maybe<uint8_t> trackedOptimizationIndexAtAddr( |
865 | | JSRuntime *rt, |
866 | | void* addr, |
867 | | uint32_t* entryOffsetOut) |
868 | 0 | { |
869 | 0 | switch (kind()) { |
870 | 0 | case Ion: |
871 | 0 | return ionEntry().trackedOptimizationIndexAtAddr(addr, entryOffsetOut); |
872 | 0 | case IonCache: |
873 | 0 | return ionCacheEntry().trackedOptimizationIndexAtAddr(rt, addr, entryOffsetOut); |
874 | 0 | case Baseline: |
875 | 0 | case Dummy: |
876 | 0 | break; |
877 | 0 | default: |
878 | 0 | MOZ_CRASH("Invalid JitcodeGlobalEntry kind."); |
879 | 0 | } |
880 | 0 | return mozilla::Nothing(); |
881 | 0 | } |
882 | | |
883 | | void forEachOptimizationAttempt(JSRuntime* rt, uint8_t index, |
884 | | JS::ForEachTrackedOptimizationAttemptOp& op) |
885 | 0 | { |
886 | 0 | switch (kind()) { |
887 | 0 | case Ion: |
888 | 0 | ionEntry().forEachOptimizationAttempt(index, op); |
889 | 0 | return; |
890 | 0 | case IonCache: |
891 | 0 | ionCacheEntry().forEachOptimizationAttempt(rt, index, op); |
892 | 0 | return; |
893 | 0 | case Baseline: |
894 | 0 | case Dummy: |
895 | 0 | break; |
896 | 0 | default: |
897 | 0 | MOZ_CRASH("Invalid JitcodeGlobalEntry kind."); |
898 | 0 | } |
899 | 0 | } |
900 | | |
901 | | void forEachOptimizationTypeInfo(JSRuntime* rt, uint8_t index, |
902 | | IonTrackedOptimizationsTypeInfo::ForEachOpAdapter& op) |
903 | 0 | { |
904 | 0 | switch (kind()) { |
905 | 0 | case Ion: |
906 | 0 | ionEntry().forEachOptimizationTypeInfo(index, op); |
907 | 0 | return; |
908 | 0 | case IonCache: |
909 | 0 | ionCacheEntry().forEachOptimizationTypeInfo(rt, index, op); |
910 | 0 | return; |
911 | 0 | case Baseline: |
912 | 0 | case Dummy: |
913 | 0 | break; |
914 | 0 | default: |
915 | 0 | MOZ_CRASH("Invalid JitcodeGlobalEntry kind."); |
916 | 0 | } |
917 | 0 | } |
918 | | |
919 | 0 | IonTrackedOptimizationsAttempts trackedOptimizationAttempts(uint8_t index) { |
920 | 0 | return ionEntry().trackedOptimizationAttempts(index); |
921 | 0 | } |
922 | | |
923 | 0 | IonTrackedOptimizationsTypeInfo trackedOptimizationTypeInfo(uint8_t index) { |
924 | 0 | return ionEntry().trackedOptimizationTypeInfo(index); |
925 | 0 | } |
926 | | |
927 | 0 | const IonTrackedTypeVector* allTrackedTypes() { |
928 | 0 | return ionEntry().allTrackedTypes(); |
929 | 0 | } |
930 | | |
931 | 272 | Zone* zone() { |
932 | 272 | return baseEntry().jitcode()->zone(); |
933 | 272 | } |
934 | | |
935 | | template <class ShouldTraceProvider> |
936 | 74 | bool trace(JSTracer* trc) { |
937 | 74 | bool tracedAny = baseEntry().traceJitcode<ShouldTraceProvider>(trc); |
938 | 74 | switch (kind()) { |
939 | 74 | case Ion: |
940 | 0 | tracedAny |= ionEntry().trace<ShouldTraceProvider>(trc); |
941 | 0 | break; |
942 | 74 | case Baseline: |
943 | 36 | tracedAny |= baselineEntry().trace<ShouldTraceProvider>(trc); |
944 | 36 | break; |
945 | 74 | case IonCache: |
946 | 0 | tracedAny |= ionCacheEntry().trace<ShouldTraceProvider>(trc); |
947 | 0 | break; |
948 | 74 | case Dummy: |
949 | 38 | break; |
950 | 74 | default: |
951 | 0 | MOZ_CRASH("Invalid JitcodeGlobalEntry kind."); |
952 | 74 | } |
953 | 74 | return tracedAny; |
954 | 74 | } |
955 | | |
956 | 37 | void sweepChildren(JSRuntime* rt) { |
957 | 37 | switch (kind()) { |
958 | 37 | case Ion: |
959 | 0 | ionEntry().sweepChildren(); |
960 | 0 | break; |
961 | 37 | case Baseline: |
962 | 18 | baselineEntry().sweepChildren(); |
963 | 18 | break; |
964 | 37 | case IonCache: |
965 | 0 | ionCacheEntry().sweepChildren(rt); |
966 | 0 | break; |
967 | 37 | case Dummy: |
968 | 19 | break; |
969 | 37 | default: |
970 | 0 | MOZ_CRASH("Invalid JitcodeGlobalEntry kind."); |
971 | 37 | } |
972 | 37 | } |
973 | | |
974 | 0 | bool isMarkedFromAnyThread(JSRuntime* rt) { |
975 | 0 | if (!baseEntry().isJitcodeMarkedFromAnyThread(rt)) { |
976 | 0 | return false; |
977 | 0 | } |
978 | 0 | switch (kind()) { |
979 | 0 | case Ion: |
980 | 0 | return ionEntry().isMarkedFromAnyThread(rt); |
981 | 0 | case Baseline: |
982 | 0 | return baselineEntry().isMarkedFromAnyThread(rt); |
983 | 0 | case IonCache: |
984 | 0 | return ionCacheEntry().isMarkedFromAnyThread(rt); |
985 | 0 | case Dummy: |
986 | 0 | break; |
987 | 0 | default: |
988 | 0 | MOZ_CRASH("Invalid JitcodeGlobalEntry kind."); |
989 | 0 | } |
990 | 0 | return true; |
991 | 0 | } |
992 | | |
993 | | // |
994 | | // When stored in a free-list, entries use 'tower_' to store a |
995 | | // pointer to the next entry. In this context only, 'tower_' |
996 | | // may refer to a |JitcodeGlobalEntry*| instead of a |
997 | | // |JitcodeSkiplistTower*|. |
998 | | // |
999 | | |
1000 | 25 | void addToFreeList(JitcodeGlobalEntry** freeList) { |
1001 | 25 | MOZ_ASSERT(!isValid()); |
1002 | 25 | |
1003 | 25 | JitcodeGlobalEntry* nextFreeEntry = *freeList; |
1004 | 25 | MOZ_ASSERT_IF(nextFreeEntry, !nextFreeEntry->isValid()); |
1005 | 25 | |
1006 | 25 | tower_ = (JitcodeSkiplistTower*) nextFreeEntry; |
1007 | 25 | *freeList = this; |
1008 | 25 | } |
1009 | | |
1010 | 27 | static JitcodeGlobalEntry* PopFromFreeList(JitcodeGlobalEntry** freeList) { |
1011 | 27 | if (!*freeList) { |
1012 | 14 | return nullptr; |
1013 | 14 | } |
1014 | 13 | |
1015 | 13 | JitcodeGlobalEntry* entry = *freeList; |
1016 | 13 | MOZ_ASSERT(!entry->isValid()); |
1017 | 13 | JitcodeGlobalEntry* nextFreeEntry = (JitcodeGlobalEntry*) entry->tower_; |
1018 | 13 | entry->tower_ = nullptr; |
1019 | 13 | *freeList = nextFreeEntry; |
1020 | 13 | return entry; |
1021 | 13 | } |
1022 | | }; |
1023 | | |
1024 | | /* |
1025 | | * Global table of JitcodeGlobalEntry values sorted by native address range. |
1026 | | */ |
1027 | | class JitcodeGlobalTable |
1028 | | { |
1029 | | private: |
1030 | | static const size_t LIFO_CHUNK_SIZE = 16 * 1024; |
1031 | | |
1032 | | LifoAlloc alloc_; |
1033 | | JitcodeGlobalEntry* freeEntries_; |
1034 | | uint32_t rand_; |
1035 | | uint32_t skiplistSize_; |
1036 | | JitcodeGlobalEntry::IonEntry* nurseryEntries_; |
1037 | | |
1038 | | JitcodeGlobalEntry* startTower_[JitcodeSkiplistTower::MAX_HEIGHT]; |
1039 | | JitcodeSkiplistTower* freeTowers_[JitcodeSkiplistTower::MAX_HEIGHT]; |
1040 | | |
1041 | | public: |
1042 | | JitcodeGlobalTable() |
1043 | | : alloc_(LIFO_CHUNK_SIZE), freeEntries_(nullptr), rand_(0), skiplistSize_(0), |
1044 | | nurseryEntries_(nullptr) |
1045 | 3 | { |
1046 | 99 | for (unsigned i = 0; i < JitcodeSkiplistTower::MAX_HEIGHT; i++) { |
1047 | 96 | startTower_[i] = nullptr; |
1048 | 96 | } |
1049 | 99 | for (unsigned i = 0; i < JitcodeSkiplistTower::MAX_HEIGHT; i++) { |
1050 | 96 | freeTowers_[i] = nullptr; |
1051 | 96 | } |
1052 | 3 | } |
1053 | 0 | ~JitcodeGlobalTable() {} |
1054 | | |
1055 | 0 | bool empty() const { |
1056 | 0 | return skiplistSize_ == 0; |
1057 | 0 | } |
1058 | | |
1059 | 0 | JitcodeGlobalEntry* lookup(void* ptr) { |
1060 | 0 | return lookupInternal(ptr); |
1061 | 0 | } |
1062 | | |
1063 | 0 | JitcodeGlobalEntry& lookupInfallible(void* ptr) { |
1064 | 0 | JitcodeGlobalEntry* entry = lookupInternal(ptr); |
1065 | 0 | MOZ_ASSERT(entry); |
1066 | 0 | return *entry; |
1067 | 0 | } |
1068 | | |
1069 | | const JitcodeGlobalEntry& lookupForSamplerInfallible(void* ptr, JSRuntime* rt, |
1070 | | uint64_t samplePosInBuffer); |
1071 | | |
1072 | 0 | MOZ_MUST_USE bool addEntry(const JitcodeGlobalEntry::IonEntry& entry) { |
1073 | 0 | return addEntry(JitcodeGlobalEntry(entry)); |
1074 | 0 | } |
1075 | 14 | MOZ_MUST_USE bool addEntry(const JitcodeGlobalEntry::BaselineEntry& entry) { |
1076 | 14 | return addEntry(JitcodeGlobalEntry(entry)); |
1077 | 14 | } |
1078 | 0 | MOZ_MUST_USE bool addEntry(const JitcodeGlobalEntry::IonCacheEntry& entry) { |
1079 | 0 | return addEntry(JitcodeGlobalEntry(entry)); |
1080 | 0 | } |
1081 | 13 | MOZ_MUST_USE bool addEntry(const JitcodeGlobalEntry::DummyEntry& entry) { |
1082 | 13 | return addEntry(JitcodeGlobalEntry(entry)); |
1083 | 13 | } |
1084 | | |
1085 | | void removeEntry(JitcodeGlobalEntry& entry, JitcodeGlobalEntry** prevTower); |
1086 | | void releaseEntry(JitcodeGlobalEntry& entry, JitcodeGlobalEntry** prevTower, JSRuntime* rt); |
1087 | | |
1088 | | void setAllEntriesAsExpired(); |
1089 | | void traceForMinorGC(JSTracer* trc); |
1090 | | MOZ_MUST_USE bool markIteratively(GCMarker* marker); |
1091 | | void sweep(JSRuntime* rt); |
1092 | | |
1093 | | private: |
1094 | | MOZ_MUST_USE bool addEntry(const JitcodeGlobalEntry& entry); |
1095 | | |
1096 | | JitcodeGlobalEntry* lookupInternal(void* ptr); |
1097 | | |
1098 | | // Initialize towerOut such that towerOut[i] (for i in [0, MAX_HEIGHT-1]) |
1099 | | // is a JitcodeGlobalEntry that is sorted to be <query, whose successor at |
1100 | | // level i is either null, or sorted to be >= query. |
1101 | | // |
1102 | | // If entry with the given properties does not exist for level i, then |
1103 | | // towerOut[i] is initialized to nullptr. |
1104 | | void searchInternal(const JitcodeGlobalEntry& query, JitcodeGlobalEntry** towerOut); |
1105 | | |
1106 | | JitcodeGlobalEntry* searchAtHeight(unsigned level, JitcodeGlobalEntry* start, |
1107 | | const JitcodeGlobalEntry& query); |
1108 | | |
1109 | | // Calculate next random tower height. |
1110 | | unsigned generateTowerHeight(); |
1111 | | |
1112 | | JitcodeSkiplistTower* allocateTower(unsigned height); |
1113 | | JitcodeGlobalEntry* allocateEntry(); |
1114 | | |
1115 | | #ifdef DEBUG |
1116 | | void verifySkiplist(); |
1117 | | #else |
1118 | 0 | void verifySkiplist() {} |
1119 | | #endif |
1120 | | |
1121 | 0 | void addToNurseryList(JitcodeGlobalEntry::IonEntry* entry) { |
1122 | 0 | MOZ_ASSERT(entry->prevNursery_ == nullptr); |
1123 | 0 | MOZ_ASSERT(entry->nextNursery_ == nullptr); |
1124 | 0 |
|
1125 | 0 | entry->nextNursery_ = nurseryEntries_; |
1126 | 0 | if (nurseryEntries_) { |
1127 | 0 | nurseryEntries_->prevNursery_ = entry; |
1128 | 0 | } |
1129 | 0 | nurseryEntries_ = entry; |
1130 | 0 | } |
1131 | | |
1132 | 0 | void removeFromNurseryList(JitcodeGlobalEntry::IonEntry* entry) { |
1133 | 0 | // Splice out of list to be scanned on a minor GC. |
1134 | 0 | if (entry->prevNursery_) { |
1135 | 0 | entry->prevNursery_->nextNursery_ = entry->nextNursery_; |
1136 | 0 | } |
1137 | 0 | if (entry->nextNursery_) { |
1138 | 0 | entry->nextNursery_->prevNursery_ = entry->prevNursery_; |
1139 | 0 | } |
1140 | 0 |
|
1141 | 0 | if (nurseryEntries_ == entry) { |
1142 | 0 | nurseryEntries_ = entry->nextNursery_; |
1143 | 0 | } |
1144 | 0 |
|
1145 | 0 | entry->prevNursery_ = entry->nextNursery_ = nullptr; |
1146 | 0 | } |
1147 | | |
1148 | | public: |
1149 | | class Range |
1150 | | { |
1151 | | protected: |
1152 | | JitcodeGlobalTable& table_; |
1153 | | JitcodeGlobalEntry* cur_; |
1154 | | |
1155 | | public: |
1156 | | explicit Range(JitcodeGlobalTable& table) |
1157 | | : table_(table), |
1158 | | cur_(table.startTower_[0]) |
1159 | 54 | { } |
1160 | | |
1161 | 186 | JitcodeGlobalEntry* front() const { |
1162 | 186 | MOZ_ASSERT(!empty()); |
1163 | 186 | return cur_; |
1164 | 186 | } |
1165 | | |
1166 | 302 | bool empty() const { |
1167 | 302 | return !cur_; |
1168 | 302 | } |
1169 | | |
1170 | 124 | void popFront() { |
1171 | 124 | MOZ_ASSERT(!empty()); |
1172 | 124 | cur_ = cur_->tower_->next(0); |
1173 | 124 | } |
1174 | | }; |
1175 | | |
1176 | | // An enumerator class that can remove entries as it enumerates. If this |
1177 | | // functionality is not needed, use Range instead. |
1178 | | class Enum : public Range |
1179 | | { |
1180 | | JSRuntime* rt_; |
1181 | | JitcodeGlobalEntry* next_; |
1182 | | JitcodeGlobalEntry* prevTower_[JitcodeSkiplistTower::MAX_HEIGHT]; |
1183 | | |
1184 | | public: |
1185 | | Enum(JitcodeGlobalTable& table, JSRuntime* rt); |
1186 | | |
1187 | | void popFront(); |
1188 | | void removeFront(); |
1189 | | }; |
1190 | | }; |
1191 | | |
1192 | | |
1193 | | /* |
1194 | | * Container class for main jitcode table. |
1195 | | * The Region table's memory is structured as follows: |
1196 | | * |
1197 | | * +------------------------------------------------+ | |
1198 | | * | Region 1 Run | | |
1199 | | * |------------------------------------------------| | |
1200 | | * | Region 2 Run | | |
1201 | | * | | | |
1202 | | * | | | |
1203 | | * |------------------------------------------------| | |
1204 | | * | Region 3 Run | | |
1205 | | * | | | |
1206 | | * |------------------------------------------------| |-- Payload |
1207 | | * | | | |
1208 | | * | ... | | |
1209 | | * | | | |
1210 | | * |------------------------------------------------| | |
1211 | | * | Region M Run | | |
1212 | | * | | | |
1213 | | * +================================================+ <- RegionTable pointer points here |
1214 | | * | uint23_t numRegions = M | | |
1215 | | * +------------------------------------------------+ | |
1216 | | * | Region 1 | | |
1217 | | * | uint32_t entryOffset = size(Payload) | | |
1218 | | * +------------------------------------------------+ | |
1219 | | * | | |-- Table |
1220 | | * | ... | | |
1221 | | * | | | |
1222 | | * +------------------------------------------------+ | |
1223 | | * | Region M | | |
1224 | | * | uint32_t entryOffset | | |
1225 | | * +------------------------------------------------+ | |
1226 | | * |
1227 | | * The region table is composed of two sections: a tail section that contains a table of |
1228 | | * fixed-size entries containing offsets into the the head section, and a head section that |
1229 | | * holds a sequence of variable-sized runs. The table in the tail section serves to |
1230 | | * locate the variable-length encoded structures in the head section. |
1231 | | * |
1232 | | * The entryOffsets in the table indicate the bytes offset to subtract from the regionTable |
1233 | | * pointer to arrive at the encoded region in the payload. |
1234 | | * |
1235 | | * |
1236 | | * Variable-length entries in payload |
1237 | | * ---------------------------------- |
1238 | | * The entryOffsets in the region table's fixed-sized entries refer to a location within the |
1239 | | * variable-length payload section. This location contains a compactly encoded "run" of |
1240 | | * mappings. |
1241 | | * |
1242 | | * Each run starts by describing the offset within the native code it starts at, and the |
1243 | | * sequence of (JSScript*, jsbytecode*) pairs active at that site. Following that, there |
1244 | | * are a number of variable-length entries encoding (nativeOffsetDelta, bytecodeOffsetDelta) |
1245 | | * pairs for the run. |
1246 | | * |
1247 | | * VarUint32 nativeOffset; |
1248 | | * - The offset from nativeStartAddr in the global table entry at which |
1249 | | * the jitcode for this region starts. |
1250 | | * |
1251 | | * Uint8_t scriptDepth; |
1252 | | * - The depth of inlined scripts for this region. |
1253 | | * |
1254 | | * List<VarUint32> inlineScriptPcStack; |
1255 | | * - We encode (2 * scriptDepth) VarUint32s here. Each pair of uint32s are taken |
1256 | | * as an index into the scriptList in the global table entry, and a pcOffset |
1257 | | * respectively. |
1258 | | * |
1259 | | * List<NativeAndBytecodeDelta> deltaRun; |
1260 | | * - The rest of the entry is a deltaRun that stores a series of variable-length |
1261 | | * encoded NativeAndBytecodeDelta datums. |
1262 | | */ |
1263 | | class JitcodeRegionEntry |
1264 | | { |
1265 | | private: |
1266 | | static const unsigned MAX_RUN_LENGTH = 100; |
1267 | | |
1268 | | public: |
1269 | | static void WriteHead(CompactBufferWriter& writer, |
1270 | | uint32_t nativeOffset, uint8_t scriptDepth); |
1271 | | static void ReadHead(CompactBufferReader& reader, |
1272 | | uint32_t* nativeOffset, uint8_t* scriptDepth); |
1273 | | |
1274 | | static void WriteScriptPc(CompactBufferWriter& writer, uint32_t scriptIdx, uint32_t pcOffset); |
1275 | | static void ReadScriptPc(CompactBufferReader& reader, uint32_t* scriptIdx, uint32_t* pcOffset); |
1276 | | |
1277 | | static void WriteDelta(CompactBufferWriter& writer, uint32_t nativeDelta, int32_t pcDelta); |
1278 | | static void ReadDelta(CompactBufferReader& reader, uint32_t* nativeDelta, int32_t* pcDelta); |
1279 | | |
1280 | | // Given a pointer into an array of NativeToBytecode (and a pointer to the end of the array), |
1281 | | // compute the number of entries that would be consume by outputting a run starting |
1282 | | // at this one. |
1283 | | static uint32_t ExpectedRunLength(const NativeToBytecode* entry, |
1284 | | const NativeToBytecode* end); |
1285 | | |
1286 | | // Write a run, starting at the given NativeToBytecode entry, into the given buffer writer. |
1287 | | static MOZ_MUST_USE bool WriteRun(CompactBufferWriter& writer, JSScript** scriptList, |
1288 | | uint32_t scriptListSize, uint32_t runLength, |
1289 | | const NativeToBytecode* entry); |
1290 | | |
1291 | | // Delta Run entry formats are encoded little-endian: |
1292 | | // |
1293 | | // byte 0 |
1294 | | // NNNN-BBB0 |
1295 | | // Single byte format. nativeDelta in [0, 15], pcDelta in [0, 7] |
1296 | | // |
1297 | | static const uint32_t ENC1_MASK = 0x1; |
1298 | | static const uint32_t ENC1_MASK_VAL = 0x0; |
1299 | | |
1300 | | static const uint32_t ENC1_NATIVE_DELTA_MAX = 0xf; |
1301 | | static const unsigned ENC1_NATIVE_DELTA_SHIFT = 4; |
1302 | | |
1303 | | static const uint32_t ENC1_PC_DELTA_MASK = 0x0e; |
1304 | | static const int32_t ENC1_PC_DELTA_MAX = 0x7; |
1305 | | static const unsigned ENC1_PC_DELTA_SHIFT = 1; |
1306 | | |
1307 | | // byte 1 byte 0 |
1308 | | // NNNN-NNNN BBBB-BB01 |
1309 | | // Two-byte format. nativeDelta in [0, 255], pcDelta in [0, 63] |
1310 | | // |
1311 | | static const uint32_t ENC2_MASK = 0x3; |
1312 | | static const uint32_t ENC2_MASK_VAL = 0x1; |
1313 | | |
1314 | | static const uint32_t ENC2_NATIVE_DELTA_MAX = 0xff; |
1315 | | static const unsigned ENC2_NATIVE_DELTA_SHIFT = 8; |
1316 | | |
1317 | | static const uint32_t ENC2_PC_DELTA_MASK = 0x00fc; |
1318 | | static const int32_t ENC2_PC_DELTA_MAX = 0x3f; |
1319 | | static const unsigned ENC2_PC_DELTA_SHIFT = 2; |
1320 | | |
1321 | | // byte 2 byte 1 byte 0 |
1322 | | // NNNN-NNNN NNNB-BBBB BBBB-B011 |
1323 | | // Three-byte format. nativeDelta in [0, 2047], pcDelta in [-512, 511] |
1324 | | // |
1325 | | static const uint32_t ENC3_MASK = 0x7; |
1326 | | static const uint32_t ENC3_MASK_VAL = 0x3; |
1327 | | |
1328 | | static const uint32_t ENC3_NATIVE_DELTA_MAX = 0x7ff; |
1329 | | static const unsigned ENC3_NATIVE_DELTA_SHIFT = 13; |
1330 | | |
1331 | | static const uint32_t ENC3_PC_DELTA_MASK = 0x001ff8; |
1332 | | static const int32_t ENC3_PC_DELTA_MAX = 0x1ff; |
1333 | | static const int32_t ENC3_PC_DELTA_MIN = -ENC3_PC_DELTA_MAX - 1; |
1334 | | static const unsigned ENC3_PC_DELTA_SHIFT = 3; |
1335 | | |
1336 | | // byte 3 byte 2 byte 1 byte 0 |
1337 | | // NNNN-NNNN NNNN-NNNN BBBB-BBBB BBBB-B111 |
1338 | | // Three-byte format. nativeDelta in [0, 65535], pcDelta in [-4096, 4095] |
1339 | | static const uint32_t ENC4_MASK = 0x7; |
1340 | | static const uint32_t ENC4_MASK_VAL = 0x7; |
1341 | | |
1342 | | static const uint32_t ENC4_NATIVE_DELTA_MAX = 0xffff; |
1343 | | static const unsigned ENC4_NATIVE_DELTA_SHIFT = 16; |
1344 | | |
1345 | | static const uint32_t ENC4_PC_DELTA_MASK = 0x0000fff8; |
1346 | | static const int32_t ENC4_PC_DELTA_MAX = 0xfff; |
1347 | | static const int32_t ENC4_PC_DELTA_MIN = -ENC4_PC_DELTA_MAX - 1; |
1348 | | static const unsigned ENC4_PC_DELTA_SHIFT = 3; |
1349 | | |
1350 | 0 | static bool IsDeltaEncodeable(uint32_t nativeDelta, int32_t pcDelta) { |
1351 | 0 | return (nativeDelta <= ENC4_NATIVE_DELTA_MAX) && |
1352 | 0 | (pcDelta >= ENC4_PC_DELTA_MIN) && (pcDelta <= ENC4_PC_DELTA_MAX); |
1353 | 0 | } |
1354 | | |
1355 | | private: |
1356 | | const uint8_t* data_; |
1357 | | const uint8_t* end_; |
1358 | | |
1359 | | // Unpacked state from jitcode entry. |
1360 | | uint32_t nativeOffset_; |
1361 | | uint8_t scriptDepth_; |
1362 | | const uint8_t* scriptPcStack_; |
1363 | | const uint8_t* deltaRun_; |
1364 | | |
1365 | | void unpack(); |
1366 | | |
1367 | | public: |
1368 | | JitcodeRegionEntry(const uint8_t* data, const uint8_t* end) |
1369 | | : data_(data), end_(end), |
1370 | | nativeOffset_(0), scriptDepth_(0), |
1371 | | scriptPcStack_(nullptr), deltaRun_(nullptr) |
1372 | 0 | { |
1373 | 0 | MOZ_ASSERT(data_ < end_); |
1374 | 0 | unpack(); |
1375 | 0 | MOZ_ASSERT(scriptPcStack_ < end_); |
1376 | 0 | MOZ_ASSERT(deltaRun_ <= end_); |
1377 | 0 | } |
1378 | | |
1379 | 0 | uint32_t nativeOffset() const { |
1380 | 0 | return nativeOffset_; |
1381 | 0 | } |
1382 | 0 | uint32_t scriptDepth() const { |
1383 | 0 | return scriptDepth_; |
1384 | 0 | } |
1385 | | |
1386 | | class ScriptPcIterator |
1387 | | { |
1388 | | private: |
1389 | | const uint8_t* start_; |
1390 | | const uint8_t* end_; |
1391 | | #ifdef DEBUG |
1392 | | uint32_t count_; |
1393 | | #endif |
1394 | | uint32_t idx_; |
1395 | | const uint8_t* cur_; |
1396 | | |
1397 | | public: |
1398 | | ScriptPcIterator(const uint8_t* start, const uint8_t* end, uint32_t count) |
1399 | | : start_(start), |
1400 | | end_(end), |
1401 | | #ifdef DEBUG |
1402 | | count_(count), |
1403 | | #endif |
1404 | | idx_(0), |
1405 | | cur_(start_) |
1406 | 0 | {} |
1407 | | |
1408 | | bool hasMore() const |
1409 | 0 | { |
1410 | 0 | MOZ_ASSERT((idx_ == count_) == (cur_ == end_)); |
1411 | 0 | MOZ_ASSERT((idx_ < count_) == (cur_ < end_)); |
1412 | 0 | return cur_ < end_; |
1413 | 0 | } |
1414 | | |
1415 | | void readNext(uint32_t* scriptIdxOut, uint32_t* pcOffsetOut) |
1416 | 0 | { |
1417 | 0 | MOZ_ASSERT(scriptIdxOut); |
1418 | 0 | MOZ_ASSERT(pcOffsetOut); |
1419 | 0 | MOZ_ASSERT(hasMore()); |
1420 | 0 |
|
1421 | 0 | CompactBufferReader reader(cur_, end_); |
1422 | 0 | ReadScriptPc(reader, scriptIdxOut, pcOffsetOut); |
1423 | 0 |
|
1424 | 0 | cur_ = reader.currentPosition(); |
1425 | 0 | MOZ_ASSERT(cur_ <= end_); |
1426 | 0 |
|
1427 | 0 | idx_++; |
1428 | 0 | MOZ_ASSERT_IF(idx_ == count_, cur_ == end_); |
1429 | 0 | } |
1430 | | |
1431 | 0 | void reset() { |
1432 | 0 | idx_ = 0; |
1433 | 0 | cur_ = start_; |
1434 | 0 | } |
1435 | | }; |
1436 | | |
1437 | 0 | ScriptPcIterator scriptPcIterator() const { |
1438 | 0 | // End of script+pc sequence is the start of the delta run. |
1439 | 0 | return ScriptPcIterator(scriptPcStack_, deltaRun_, scriptDepth_); |
1440 | 0 | } |
1441 | | |
1442 | | class DeltaIterator { |
1443 | | private: |
1444 | | const uint8_t* start_; |
1445 | | const uint8_t* end_; |
1446 | | const uint8_t* cur_; |
1447 | | |
1448 | | public: |
1449 | | DeltaIterator(const uint8_t* start, const uint8_t* end) |
1450 | | : start_(start), end_(end), cur_(start) |
1451 | 0 | {} |
1452 | | |
1453 | | bool hasMore() const |
1454 | 0 | { |
1455 | 0 | MOZ_ASSERT(cur_ <= end_); |
1456 | 0 | return cur_ < end_; |
1457 | 0 | } |
1458 | | |
1459 | | void readNext(uint32_t* nativeDeltaOut, int32_t* pcDeltaOut) |
1460 | 0 | { |
1461 | 0 | MOZ_ASSERT(nativeDeltaOut != nullptr); |
1462 | 0 | MOZ_ASSERT(pcDeltaOut != nullptr); |
1463 | 0 |
|
1464 | 0 | MOZ_ASSERT(hasMore()); |
1465 | 0 |
|
1466 | 0 | CompactBufferReader reader(cur_, end_); |
1467 | 0 | ReadDelta(reader, nativeDeltaOut, pcDeltaOut); |
1468 | 0 |
|
1469 | 0 | cur_ = reader.currentPosition(); |
1470 | 0 | MOZ_ASSERT(cur_ <= end_); |
1471 | 0 | } |
1472 | | |
1473 | 0 | void reset() { |
1474 | 0 | cur_ = start_; |
1475 | 0 | } |
1476 | | }; |
1477 | 0 | DeltaIterator deltaIterator() const { |
1478 | 0 | return DeltaIterator(deltaRun_, end_); |
1479 | 0 | } |
1480 | | |
1481 | | uint32_t findPcOffset(uint32_t queryNativeOffset, uint32_t startPcOffset) const; |
1482 | | }; |
1483 | | |
1484 | | class JitcodeIonTable |
1485 | | { |
1486 | | private: |
1487 | | /* Variable length payload section "below" here. */ |
1488 | | uint32_t numRegions_; |
1489 | | uint32_t regionOffsets_[1]; |
1490 | | |
1491 | 0 | const uint8_t* payloadEnd() const { |
1492 | 0 | return reinterpret_cast<const uint8_t*>(this); |
1493 | 0 | } |
1494 | | |
1495 | | public: |
1496 | | explicit JitcodeIonTable(uint32_t numRegions) |
1497 | | : numRegions_(numRegions) |
1498 | 0 | { |
1499 | 0 | for (uint32_t i = 0; i < numRegions; i++) { |
1500 | 0 | regionOffsets_[i] = 0; |
1501 | 0 | } |
1502 | 0 | } |
1503 | | |
1504 | | MOZ_MUST_USE bool makeIonEntry(JSContext* cx, JitCode* code, uint32_t numScripts, |
1505 | | JSScript** scripts, JitcodeGlobalEntry::IonEntry& out); |
1506 | | |
1507 | 0 | uint32_t numRegions() const { |
1508 | 0 | return numRegions_; |
1509 | 0 | } |
1510 | | |
1511 | 0 | uint32_t regionOffset(uint32_t regionIndex) const { |
1512 | 0 | MOZ_ASSERT(regionIndex < numRegions()); |
1513 | 0 | return regionOffsets_[regionIndex]; |
1514 | 0 | } |
1515 | | |
1516 | 0 | JitcodeRegionEntry regionEntry(uint32_t regionIndex) const { |
1517 | 0 | const uint8_t* regionStart = payloadEnd() - regionOffset(regionIndex); |
1518 | 0 | const uint8_t* regionEnd = payloadEnd(); |
1519 | 0 | if (regionIndex < numRegions_ - 1) { |
1520 | 0 | regionEnd -= regionOffset(regionIndex + 1); |
1521 | 0 | } |
1522 | 0 | return JitcodeRegionEntry(regionStart, regionEnd); |
1523 | 0 | } |
1524 | | |
1525 | 0 | bool regionContainsOffset(uint32_t regionIndex, uint32_t nativeOffset) { |
1526 | 0 | MOZ_ASSERT(regionIndex < numRegions()); |
1527 | 0 |
|
1528 | 0 | JitcodeRegionEntry ent = regionEntry(regionIndex); |
1529 | 0 | if (nativeOffset < ent.nativeOffset()) { |
1530 | 0 | return false; |
1531 | 0 | } |
1532 | 0 |
|
1533 | 0 | if (regionIndex == numRegions_ - 1) { |
1534 | 0 | return true; |
1535 | 0 | } |
1536 | 0 |
|
1537 | 0 | return nativeOffset < regionEntry(regionIndex + 1).nativeOffset(); |
1538 | 0 | } |
1539 | | |
1540 | | uint32_t findRegionEntry(uint32_t offset) const; |
1541 | | |
1542 | 0 | const uint8_t* payloadStart() const { |
1543 | 0 | // The beginning of the payload the beginning of the first region are the same. |
1544 | 0 | return payloadEnd() - regionOffset(0); |
1545 | 0 | } |
1546 | | |
1547 | | static MOZ_MUST_USE bool WriteIonTable(CompactBufferWriter& writer, |
1548 | | JSScript** scriptList, uint32_t scriptListSize, |
1549 | | const NativeToBytecode* start, |
1550 | | const NativeToBytecode* end, |
1551 | | uint32_t* tableOffsetOut, uint32_t* numRegionsOut); |
1552 | | }; |
1553 | | |
1554 | | |
1555 | | } // namespace jit |
1556 | | } // namespace js |
1557 | | |
1558 | | #endif /* jit_JitcodeMap_h */ |