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 "jstypes.h"
15 
16 #include "gc/Heap.h"
17 #include "jit/ExecutableAllocator.h"
18 #include "jit/ICStubSpace.h"
19 #include "jit/IonOptimizationLevels.h"
20 #include "jit/IonTypes.h"
21 #include "js/UbiNode.h"
22 #include "vm/TraceLogging.h"
23 #include "vm/TypeInference.h"
24 
25 namespace js {
26 namespace jit {
27 
28 class MacroAssembler;
29 class PatchableBackedge;
30 class IonBuilder;
31 class IonICEntry;
32 class JitCode;
33 
34 typedef Vector<JSObject*, 4, JitAllocPolicy> ObjectVector;
35 typedef Vector<TraceLoggerEvent, 0, SystemAllocPolicy> TraceLoggerEventVector;
36 
37 // Header at start of raw code buffer
38 struct JitCodeHeader {
39   // Link back to corresponding gcthing
40   JitCode* jitCode_;
41 
42   // !!! NOTE !!!
43   // If we are running on AMD Bobcat, insert a NOP-slide at end of the JitCode
44   // header so we can try to recover when the CPU screws up the branch landing
45   // site. See Bug 1281759.
46   void* nops_;
47 
48   void init(JitCode* jitCode);
49 
FromExecutableJitCodeHeader50   static JitCodeHeader* FromExecutable(uint8_t* buffer) {
51     return (JitCodeHeader*)(buffer - sizeof(JitCodeHeader));
52   }
53 };
54 
55 class JitCode : public gc::TenuredCell {
56  protected:
57   uint8_t* code_;
58   ExecutablePool* pool_;
59   uint32_t bufferSize_;  // Total buffer size. Does not include headerSize_.
60   uint32_t insnSize_;    // Instruction stream size.
61   uint32_t dataSize_;    // Size of the read-only data area.
62   uint32_t jumpRelocTableBytes_;  // Size of the jump relocation table.
63   uint32_t dataRelocTableBytes_;  // Size of the data relocation table.
64   uint8_t headerSize_ : 5;        // Number of bytes allocated before codeStart.
65   uint8_t kind_ : 3;              // jit::CodeKind, for the memory reporters.
66   bool invalidated_ : 1;     // Whether the code object has been invalidated.
67                              // This is necessary to prevent GC tracing.
68   bool hasBytecodeMap_ : 1;  // Whether the code object has been registered with
69                              // native=>bytecode mapping tables.
70 
JitCode()71   JitCode() : code_(nullptr), pool_(nullptr) {}
JitCode(uint8_t * code,uint32_t bufferSize,uint32_t headerSize,ExecutablePool * pool,CodeKind kind)72   JitCode(uint8_t* code, uint32_t bufferSize, uint32_t headerSize,
73           ExecutablePool* pool, CodeKind kind)
74       : code_(code),
75         pool_(pool),
76         bufferSize_(bufferSize),
77         insnSize_(0),
78         dataSize_(0),
79         jumpRelocTableBytes_(0),
80         dataRelocTableBytes_(0),
81         headerSize_(headerSize),
82         kind_(uint8_t(kind)),
83         invalidated_(false),
84         hasBytecodeMap_(false) {
85     MOZ_ASSERT(CodeKind(kind_) == kind);
86     MOZ_ASSERT(headerSize_ == headerSize);
87   }
88 
dataOffset()89   uint32_t dataOffset() const { return insnSize_; }
jumpRelocTableOffset()90   uint32_t jumpRelocTableOffset() const { return dataOffset() + dataSize_; }
dataRelocTableOffset()91   uint32_t dataRelocTableOffset() const {
92     return jumpRelocTableOffset() + jumpRelocTableBytes_;
93   }
94 
95  public:
raw()96   uint8_t* raw() const { return code_; }
rawEnd()97   uint8_t* rawEnd() const { return code_ + insnSize_; }
containsNativePC(const void * addr)98   bool containsNativePC(const void* addr) const {
99     const uint8_t* addr_u8 = (const uint8_t*)addr;
100     return raw() <= addr_u8 && addr_u8 < rawEnd();
101   }
instructionsSize()102   size_t instructionsSize() const { return insnSize_; }
bufferSize()103   size_t bufferSize() const { return bufferSize_; }
headerSize()104   size_t headerSize() const { return headerSize_; }
105 
106   void traceChildren(JSTracer* trc);
107   void finalize(FreeOp* fop);
setInvalidated()108   void setInvalidated() { invalidated_ = true; }
109 
setHasBytecodeMap()110   void setHasBytecodeMap() { hasBytecodeMap_ = true; }
111 
112   void togglePreBarriers(bool enabled, ReprotectCode reprotect);
113 
114   // If this JitCode object has been, effectively, corrupted due to
115   // invalidation patching, then we have to remember this so we don't try and
116   // trace relocation entries that may now be corrupt.
invalidated()117   bool invalidated() const { return !!invalidated_; }
118 
119   template <typename T>
as()120   T as() const {
121     return JS_DATA_TO_FUNC_PTR(T, raw());
122   }
123 
124   void copyFrom(MacroAssembler& masm);
125 
FromExecutable(uint8_t * buffer)126   static JitCode* FromExecutable(uint8_t* buffer) {
127     JitCode* code = JitCodeHeader::FromExecutable(buffer)->jitCode_;
128     MOZ_ASSERT(code->raw() == buffer);
129     return code;
130   }
131 
offsetOfCode()132   static size_t offsetOfCode() { return offsetof(JitCode, code_); }
133 
jumpRelocTable()134   uint8_t* jumpRelocTable() { return code_ + jumpRelocTableOffset(); }
135 
136   // Allocates a new JitCode object which will be managed by the GC. If no
137   // object can be allocated, nullptr is returned. On failure, |pool| is
138   // automatically released, so the code may be freed.
139   template <AllowGC allowGC>
140   static JitCode* New(JSContext* cx, uint8_t* code, uint32_t bufferSize,
141                       uint32_t headerSize, ExecutablePool* pool, CodeKind kind);
142 
143  public:
144   static const JS::TraceKind TraceKind = JS::TraceKind::JitCode;
145 };
146 
147 class SnapshotWriter;
148 class RecoverWriter;
149 class SafepointWriter;
150 class SafepointIndex;
151 class OsiIndex;
152 class IonIC;
153 struct PatchableBackedgeInfo;
154 
155 // An IonScript attaches Ion-generated information to a JSScript.
156 struct IonScript {
157  private:
158   // Code pointer containing the actual method.
159   PreBarrieredJitCode method_;
160 
161   // Entrypoint for OSR, or nullptr.
162   jsbytecode* osrPc_;
163 
164   // Offset to OSR entrypoint from method_->raw(), or 0.
165   uint32_t osrEntryOffset_;
166 
167   // Offset to entrypoint skipping type arg check from method_->raw().
168   uint32_t skipArgCheckEntryOffset_;
169 
170   // Offset of the invalidation epilogue (which pushes this IonScript
171   // and calls the invalidation thunk).
172   uint32_t invalidateEpilogueOffset_;
173 
174   // The offset immediately after the IonScript immediate.
175   // NOTE: technically a constant delta from
176   // |invalidateEpilogueOffset_|, so we could hard-code this
177   // per-platform if we want.
178   uint32_t invalidateEpilogueDataOffset_;
179 
180   // Number of times this script bailed out without invalidation.
181   uint32_t numBailouts_;
182 
183   // Flag set if IonScript was compiled with profiling enabled.
184   bool hasProfilingInstrumentation_;
185 
186   // Flag for if this script is getting recompiled.
187   uint32_t recompiling_;
188 
189   // Any kind of data needed by the runtime, these can be either cache
190   // information or profiling info.
191   uint32_t runtimeData_;
192   uint32_t runtimeSize_;
193 
194   // State for polymorphic caches in the compiled code. All caches are stored
195   // in the runtimeData buffer and indexed by the icIndex which gives a
196   // relative offset in the runtimeData array.
197   uint32_t icIndex_;
198   uint32_t icEntries_;
199 
200   // Map code displacement to safepoint / OSI-patch-delta.
201   uint32_t safepointIndexOffset_;
202   uint32_t safepointIndexEntries_;
203 
204   // Offset to and length of the safepoint table in bytes.
205   uint32_t safepointsStart_;
206   uint32_t safepointsSize_;
207 
208   // Number of bytes this function reserves on the stack.
209   uint32_t frameSlots_;
210 
211   // Number of bytes used passed in as formal arguments or |this|.
212   uint32_t argumentSlots_;
213 
214   // Frame size is the value that can be added to the StackPointer along
215   // with the frame prefix to get a valid JitFrameLayout.
216   uint32_t frameSize_;
217 
218   // Table mapping bailout IDs to snapshot offsets.
219   uint32_t bailoutTable_;
220   uint32_t bailoutEntries_;
221 
222   // Map OSI-point displacement to snapshot.
223   uint32_t osiIndexOffset_;
224   uint32_t osiIndexEntries_;
225 
226   // Offset from the start of the code buffer to its snapshot buffer.
227   uint32_t snapshots_;
228   uint32_t snapshotsListSize_;
229   uint32_t snapshotsRVATableSize_;
230 
231   // List of instructions needed to recover stack frames.
232   uint32_t recovers_;
233   uint32_t recoversSize_;
234 
235   // Constant table for constants stored in snapshots.
236   uint32_t constantTable_;
237   uint32_t constantEntries_;
238 
239   // List of patchable backedges which are threaded into the runtime's list.
240   uint32_t backedgeList_;
241   uint32_t backedgeEntries_;
242 
243   // List of entries to the shared stub.
244   uint32_t sharedStubList_;
245   uint32_t sharedStubEntries_;
246 
247   // Number of references from invalidation records.
248   uint32_t invalidationCount_;
249 
250   // Identifier of the compilation which produced this code.
251   RecompileInfo recompileInfo_;
252 
253   // The optimization level this script was compiled in.
254   OptimizationLevel optimizationLevel_;
255 
256   // Number of times we tried to enter this script via OSR but failed due to
257   // a LOOPENTRY pc other than osrPc_.
258   uint32_t osrPcMismatchCounter_;
259 
260   // Allocated space for fallback stubs.
261   FallbackICStubSpace fallbackStubSpace_;
262 
263   // TraceLogger events that are baked into the IonScript.
264   TraceLoggerEventVector traceLoggerEvents_;
265 
266  private:
bottomBufferIonScript267   inline uint8_t* bottomBuffer() { return reinterpret_cast<uint8_t*>(this); }
bottomBufferIonScript268   inline const uint8_t* bottomBuffer() const {
269     return reinterpret_cast<const uint8_t*>(this);
270   }
271 
272  public:
bailoutTableIonScript273   SnapshotOffset* bailoutTable() {
274     return (SnapshotOffset*)&bottomBuffer()[bailoutTable_];
275   }
constantsIonScript276   PreBarrieredValue* constants() {
277     return (PreBarrieredValue*)&bottomBuffer()[constantTable_];
278   }
safepointIndicesIonScript279   const SafepointIndex* safepointIndices() const {
280     return const_cast<IonScript*>(this)->safepointIndices();
281   }
safepointIndicesIonScript282   SafepointIndex* safepointIndices() {
283     return (SafepointIndex*)&bottomBuffer()[safepointIndexOffset_];
284   }
osiIndicesIonScript285   const OsiIndex* osiIndices() const {
286     return const_cast<IonScript*>(this)->osiIndices();
287   }
osiIndicesIonScript288   OsiIndex* osiIndices() { return (OsiIndex*)&bottomBuffer()[osiIndexOffset_]; }
icIndexIonScript289   uint32_t* icIndex() { return (uint32_t*)&bottomBuffer()[icIndex_]; }
runtimeDataIonScript290   uint8_t* runtimeData() { return &bottomBuffer()[runtimeData_]; }
backedgeListIonScript291   PatchableBackedge* backedgeList() {
292     return (PatchableBackedge*)&bottomBuffer()[backedgeList_];
293   }
294 
295  private:
296   void trace(JSTracer* trc);
297 
298  public:
299   // Do not call directly, use IonScript::New. This is public for cx->new_.
300   IonScript();
301 
~IonScriptIonScript302   ~IonScript() {
303     // The contents of the fallback stub space are removed and freed
304     // separately after the next minor GC. See IonScript::Destroy.
305     MOZ_ASSERT(fallbackStubSpace_.isEmpty());
306   }
307 
308   static IonScript* New(JSContext* cx, RecompileInfo recompileInfo,
309                         uint32_t frameSlots, uint32_t argumentSlots,
310                         uint32_t frameSize, size_t snapshotsListSize,
311                         size_t snapshotsRVATableSize, size_t recoversSize,
312                         size_t bailoutEntries, size_t constants,
313                         size_t safepointIndexEntries, size_t osiIndexEntries,
314                         size_t icEntries, size_t runtimeSize,
315                         size_t safepointsSize, size_t backedgeEntries,
316                         size_t sharedStubEntries,
317                         OptimizationLevel optimizationLevel);
318   static void Trace(JSTracer* trc, IonScript* script);
319   static void Destroy(FreeOp* fop, IonScript* script);
320 
offsetOfMethodIonScript321   static inline size_t offsetOfMethod() { return offsetof(IonScript, method_); }
offsetOfOsrEntryOffsetIonScript322   static inline size_t offsetOfOsrEntryOffset() {
323     return offsetof(IonScript, osrEntryOffset_);
324   }
offsetOfSkipArgCheckEntryOffsetIonScript325   static inline size_t offsetOfSkipArgCheckEntryOffset() {
326     return offsetof(IonScript, skipArgCheckEntryOffset_);
327   }
offsetOfInvalidationCountIonScript328   static inline size_t offsetOfInvalidationCount() {
329     return offsetof(IonScript, invalidationCount_);
330   }
offsetOfRecompilingIonScript331   static inline size_t offsetOfRecompiling() {
332     return offsetof(IonScript, recompiling_);
333   }
334 
335  public:
methodIonScript336   JitCode* method() const { return method_; }
setMethodIonScript337   void setMethod(JitCode* code) {
338     MOZ_ASSERT(!invalidated());
339     method_ = code;
340   }
setOsrPcIonScript341   void setOsrPc(jsbytecode* osrPc) { osrPc_ = osrPc; }
osrPcIonScript342   jsbytecode* osrPc() const { return osrPc_; }
setOsrEntryOffsetIonScript343   void setOsrEntryOffset(uint32_t offset) {
344     MOZ_ASSERT(!osrEntryOffset_);
345     osrEntryOffset_ = offset;
346   }
osrEntryOffsetIonScript347   uint32_t osrEntryOffset() const { return osrEntryOffset_; }
setSkipArgCheckEntryOffsetIonScript348   void setSkipArgCheckEntryOffset(uint32_t offset) {
349     MOZ_ASSERT(!skipArgCheckEntryOffset_);
350     skipArgCheckEntryOffset_ = offset;
351   }
getSkipArgCheckEntryOffsetIonScript352   uint32_t getSkipArgCheckEntryOffset() const {
353     return skipArgCheckEntryOffset_;
354   }
containsCodeAddressIonScript355   bool containsCodeAddress(uint8_t* addr) const {
356     return method()->raw() <= addr &&
357            addr <= method()->raw() + method()->instructionsSize();
358   }
containsReturnAddressIonScript359   bool containsReturnAddress(uint8_t* addr) const {
360     // This accounts for an off by one error caused by the return address of a
361     // bailout sitting outside the range of the containing function.
362     return method()->raw() <= addr &&
363            addr <= method()->raw() + method()->instructionsSize();
364   }
setInvalidationEpilogueOffsetIonScript365   void setInvalidationEpilogueOffset(uint32_t offset) {
366     MOZ_ASSERT(!invalidateEpilogueOffset_);
367     invalidateEpilogueOffset_ = offset;
368   }
invalidateEpilogueOffsetIonScript369   uint32_t invalidateEpilogueOffset() const {
370     MOZ_ASSERT(invalidateEpilogueOffset_);
371     return invalidateEpilogueOffset_;
372   }
setInvalidationEpilogueDataOffsetIonScript373   void setInvalidationEpilogueDataOffset(uint32_t offset) {
374     MOZ_ASSERT(!invalidateEpilogueDataOffset_);
375     invalidateEpilogueDataOffset_ = offset;
376   }
invalidateEpilogueDataOffsetIonScript377   uint32_t invalidateEpilogueDataOffset() const {
378     MOZ_ASSERT(invalidateEpilogueDataOffset_);
379     return invalidateEpilogueDataOffset_;
380   }
incNumBailoutsIonScript381   void incNumBailouts() { numBailouts_++; }
bailoutExpectedIonScript382   bool bailoutExpected() const {
383     return numBailouts_ >= JitOptions.frequentBailoutThreshold;
384   }
setHasProfilingInstrumentationIonScript385   void setHasProfilingInstrumentation() { hasProfilingInstrumentation_ = true; }
clearHasProfilingInstrumentationIonScript386   void clearHasProfilingInstrumentation() {
387     hasProfilingInstrumentation_ = false;
388   }
hasProfilingInstrumentationIonScript389   bool hasProfilingInstrumentation() const {
390     return hasProfilingInstrumentation_;
391   }
addTraceLoggerEventIonScript392   MOZ_MUST_USE bool addTraceLoggerEvent(TraceLoggerEvent& event) {
393     MOZ_ASSERT(event.hasTextId());
394     return traceLoggerEvents_.append(mozilla::Move(event));
395   }
snapshotsIonScript396   const uint8_t* snapshots() const {
397     return reinterpret_cast<const uint8_t*>(this) + snapshots_;
398   }
snapshotsListSizeIonScript399   size_t snapshotsListSize() const { return snapshotsListSize_; }
snapshotsRVATableSizeIonScript400   size_t snapshotsRVATableSize() const { return snapshotsRVATableSize_; }
recoversIonScript401   const uint8_t* recovers() const {
402     return reinterpret_cast<const uint8_t*>(this) + recovers_;
403   }
recoversSizeIonScript404   size_t recoversSize() const { return recoversSize_; }
safepointsIonScript405   const uint8_t* safepoints() const {
406     return reinterpret_cast<const uint8_t*>(this) + safepointsStart_;
407   }
safepointsSizeIonScript408   size_t safepointsSize() const { return safepointsSize_; }
sizeOfIncludingThisIonScript409   size_t sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const {
410     return mallocSizeOf(this);
411   }
getConstantIonScript412   PreBarrieredValue& getConstant(size_t index) {
413     MOZ_ASSERT(index < numConstants());
414     return constants()[index];
415   }
numConstantsIonScript416   size_t numConstants() const { return constantEntries_; }
frameSlotsIonScript417   uint32_t frameSlots() const { return frameSlots_; }
argumentSlotsIonScript418   uint32_t argumentSlots() const { return argumentSlots_; }
frameSizeIonScript419   uint32_t frameSize() const { return frameSize_; }
bailoutToSnapshotIonScript420   SnapshotOffset bailoutToSnapshot(uint32_t bailoutId) {
421     MOZ_ASSERT(bailoutId < bailoutEntries_);
422     return bailoutTable()[bailoutId];
423   }
424   const SafepointIndex* getSafepointIndex(uint32_t disp) const;
getSafepointIndexIonScript425   const SafepointIndex* getSafepointIndex(uint8_t* retAddr) const {
426     MOZ_ASSERT(containsCodeAddress(retAddr));
427     return getSafepointIndex(retAddr - method()->raw());
428   }
429   const OsiIndex* getOsiIndex(uint32_t disp) const;
430   const OsiIndex* getOsiIndex(uint8_t* retAddr) const;
431 
getICFromIndexIonScript432   IonIC& getICFromIndex(uint32_t index) {
433     MOZ_ASSERT(index < icEntries_);
434     uint32_t offset = icIndex()[index];
435     return getIC(offset);
436   }
getICIonScript437   inline IonIC& getIC(uint32_t offset) {
438     MOZ_ASSERT(offset < runtimeSize_);
439     return *(IonIC*)&runtimeData()[offset];
440   }
numICsIonScript441   size_t numICs() const { return icEntries_; }
sharedStubListIonScript442   IonICEntry* sharedStubList() {
443     return (IonICEntry*)&bottomBuffer()[sharedStubList_];
444   }
numSharedStubsIonScript445   size_t numSharedStubs() const { return sharedStubEntries_; }
runtimeSizeIonScript446   size_t runtimeSize() const { return runtimeSize_; }
447   void purgeICs(Zone* zone);
448   void unlinkFromRuntime(FreeOp* fop);
449   void copySnapshots(const SnapshotWriter* writer);
450   void copyRecovers(const RecoverWriter* writer);
451   void copyBailoutTable(const SnapshotOffset* table);
452   void copyConstants(const Value* vp);
453   void copySafepointIndices(const SafepointIndex* firstSafepointIndex);
454   void copyOsiIndices(const OsiIndex* firstOsiIndex);
455   void copyRuntimeData(const uint8_t* data);
456   void copyICEntries(const uint32_t* caches, MacroAssembler& masm);
457   void copySafepoints(const SafepointWriter* writer);
458   void copyPatchableBackedges(JSContext* cx, JitCode* code,
459                               PatchableBackedgeInfo* backedges,
460                               MacroAssembler& masm);
461 
invalidatedIonScript462   bool invalidated() const { return invalidationCount_ != 0; }
463 
464   // Invalidate the current compilation.
465   void invalidate(JSContext* cx, bool resetUses, const char* reason);
466 
invalidationCountIonScript467   size_t invalidationCount() const { return invalidationCount_; }
incrementInvalidationCountIonScript468   void incrementInvalidationCount() { invalidationCount_++; }
decrementInvalidationCountIonScript469   void decrementInvalidationCount(FreeOp* fop) {
470     MOZ_ASSERT(invalidationCount_);
471     invalidationCount_--;
472     if (!invalidationCount_) Destroy(fop, this);
473   }
recompileInfoIonScript474   const RecompileInfo& recompileInfo() const { return recompileInfo_; }
recompileInfoRefIonScript475   RecompileInfo& recompileInfoRef() { return recompileInfo_; }
optimizationLevelIonScript476   OptimizationLevel optimizationLevel() const { return optimizationLevel_; }
incrOsrPcMismatchCounterIonScript477   uint32_t incrOsrPcMismatchCounter() { return ++osrPcMismatchCounter_; }
resetOsrPcMismatchCounterIonScript478   void resetOsrPcMismatchCounter() { osrPcMismatchCounter_ = 0; }
479 
setRecompilingIonScript480   void setRecompiling() { recompiling_ = true; }
481 
isRecompilingIonScript482   bool isRecompiling() const { return recompiling_; }
483 
clearRecompilingIonScript484   void clearRecompiling() { recompiling_ = false; }
485 
fallbackStubSpaceIonScript486   FallbackICStubSpace* fallbackStubSpace() { return &fallbackStubSpace_; }
487   void adoptFallbackStubs(FallbackICStubSpace* stubSpace);
488   void purgeOptimizedStubs(Zone* zone);
489 
490   enum ShouldIncreaseAge { IncreaseAge = true, KeepAge = false };
491 
492   static void writeBarrierPre(Zone* zone, IonScript* ionScript);
493 };
494 
495 // Execution information for a basic block which may persist after the
496 // accompanying IonScript is destroyed, for use during profiling.
497 struct IonBlockCounts {
498  private:
499   uint32_t id_;
500 
501   // Approximate bytecode in the outer (not inlined) script this block
502   // was generated from.
503   uint32_t offset_;
504 
505   // File and line of the inner script this block was generated from.
506   char* description_;
507 
508   // ids for successors of this block.
509   uint32_t numSuccessors_;
510   uint32_t* successors_;
511 
512   // Hit count for this block.
513   uint64_t hitCount_;
514 
515   // Text information about the code generated for this block.
516   char* code_;
517 
518  public:
initIonBlockCounts519   MOZ_MUST_USE bool init(uint32_t id, uint32_t offset, char* description,
520                          uint32_t numSuccessors) {
521     id_ = id;
522     offset_ = offset;
523     description_ = description;
524     numSuccessors_ = numSuccessors;
525     if (numSuccessors) {
526       successors_ = js_pod_calloc<uint32_t>(numSuccessors);
527       if (!successors_) return false;
528     }
529     return true;
530   }
531 
destroyIonBlockCounts532   void destroy() {
533     js_free(description_);
534     js_free(successors_);
535     js_free(code_);
536   }
537 
idIonBlockCounts538   uint32_t id() const { return id_; }
539 
offsetIonBlockCounts540   uint32_t offset() const { return offset_; }
541 
descriptionIonBlockCounts542   const char* description() const { return description_; }
543 
numSuccessorsIonBlockCounts544   size_t numSuccessors() const { return numSuccessors_; }
545 
setSuccessorIonBlockCounts546   void setSuccessor(size_t i, uint32_t id) {
547     MOZ_ASSERT(i < numSuccessors_);
548     successors_[i] = id;
549   }
550 
successorIonBlockCounts551   uint32_t successor(size_t i) const {
552     MOZ_ASSERT(i < numSuccessors_);
553     return successors_[i];
554   }
555 
addressOfHitCountIonBlockCounts556   uint64_t* addressOfHitCount() { return &hitCount_; }
557 
hitCountIonBlockCounts558   uint64_t hitCount() const { return hitCount_; }
559 
setCodeIonBlockCounts560   void setCode(const char* code) {
561     char* ncode = js_pod_malloc<char>(strlen(code) + 1);
562     if (ncode) {
563       strcpy(ncode, code);
564       code_ = ncode;
565     }
566   }
567 
codeIonBlockCounts568   const char* code() const { return code_; }
569 
sizeOfExcludingThisIonBlockCounts570   size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const {
571     return mallocSizeOf(description_) + mallocSizeOf(successors_) +
572            mallocSizeOf(code_);
573   }
574 };
575 
576 // Execution information for a compiled script which may persist after the
577 // IonScript is destroyed, for use during profiling.
578 struct IonScriptCounts {
579  private:
580   // Any previous invalidated compilation(s) for the script.
581   IonScriptCounts* previous_;
582 
583   // Information about basic blocks in this script.
584   size_t numBlocks_;
585   IonBlockCounts* blocks_;
586 
587  public:
IonScriptCountsIonScriptCounts588   IonScriptCounts() { mozilla::PodZero(this); }
589 
~IonScriptCountsIonScriptCounts590   ~IonScriptCounts() {
591     for (size_t i = 0; i < numBlocks_; i++) blocks_[i].destroy();
592     js_free(blocks_);
593     // The list can be long in some corner cases (bug 1140084), so
594     // unroll the recursion.
595     IonScriptCounts* victims = previous_;
596     while (victims) {
597       IonScriptCounts* victim = victims;
598       victims = victim->previous_;
599       victim->previous_ = nullptr;
600       js_delete(victim);
601     }
602   }
603 
initIonScriptCounts604   MOZ_MUST_USE bool init(size_t numBlocks) {
605     blocks_ = js_pod_calloc<IonBlockCounts>(numBlocks);
606     if (!blocks_) return false;
607 
608     numBlocks_ = numBlocks;
609     return true;
610   }
611 
numBlocksIonScriptCounts612   size_t numBlocks() const { return numBlocks_; }
613 
blockIonScriptCounts614   IonBlockCounts& block(size_t i) {
615     MOZ_ASSERT(i < numBlocks_);
616     return blocks_[i];
617   }
618 
setPreviousIonScriptCounts619   void setPrevious(IonScriptCounts* previous) { previous_ = previous; }
620 
previousIonScriptCounts621   IonScriptCounts* previous() const { return previous_; }
622 
sizeOfIncludingThisIonScriptCounts623   size_t sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const {
624     size_t size = 0;
625     auto currCounts = this;
626     while (currCounts) {
627       const IonScriptCounts* currCount = currCounts;
628       currCounts = currCount->previous_;
629       size += currCount->sizeOfOneIncludingThis(mallocSizeOf);
630     }
631     return size;
632   }
633 
sizeOfOneIncludingThisIonScriptCounts634   size_t sizeOfOneIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const {
635     size_t size = mallocSizeOf(this) + mallocSizeOf(blocks_);
636     for (size_t i = 0; i < numBlocks_; i++)
637       blocks_[i].sizeOfExcludingThis(mallocSizeOf);
638     return size;
639   }
640 };
641 
642 struct VMFunction;
643 
644 struct AutoFlushICache {
645  private:
646 #if defined(JS_CODEGEN_ARM) || defined(JS_CODEGEN_ARM64) || \
647     defined(JS_CODEGEN_MIPS32) || defined(JS_CODEGEN_MIPS64)
648   uintptr_t start_;
649   uintptr_t stop_;
650 #ifdef JS_JITSPEW
651   const char* name_;
652 #endif
653   bool inhibit_;
654   AutoFlushICache* prev_;
655 #endif
656 
657  public:
658   static void setRange(uintptr_t p, size_t len);
659   static void flush(uintptr_t p, size_t len);
660   static void setInhibit();
661   ~AutoFlushICache();
662   explicit AutoFlushICache(const char* nonce, bool inhibit = false);
663 };
664 
665 }  // namespace jit
666 
667 namespace gc {
668 
IsMarked(JSRuntime * rt,const jit::VMFunction *)669 inline bool IsMarked(JSRuntime* rt, const jit::VMFunction*) {
670   // VMFunction are only static objects which are used by WeakMaps as keys.
671   // It is considered as a root object which is always marked.
672   return true;
673 }
674 
675 }  // namespace gc
676 
677 }  // namespace js
678 
679 // JS::ubi::Nodes can point to js::jit::JitCode instances; they're js::gc::Cell
680 // instances with no associated compartment.
681 namespace JS {
682 namespace ubi {
683 template <>
684 class Concrete<js::jit::JitCode> : TracerConcrete<js::jit::JitCode> {
685  protected:
Concrete(js::jit::JitCode * ptr)686   explicit Concrete(js::jit::JitCode* ptr)
687       : TracerConcrete<js::jit::JitCode>(ptr) {}
688 
689  public:
construct(void * storage,js::jit::JitCode * ptr)690   static void construct(void* storage, js::jit::JitCode* ptr) {
691     new (storage) Concrete(ptr);
692   }
693 
coarseType()694   CoarseType coarseType() const final { return CoarseType::Script; }
695 
size(mozilla::MallocSizeOf mallocSizeOf)696   Size size(mozilla::MallocSizeOf mallocSizeOf) const override {
697     Size size = js::gc::Arena::thingSize(get().asTenured().getAllocKind());
698     size += get().bufferSize();
699     size += get().headerSize();
700     return size;
701   }
702 
typeName()703   const char16_t* typeName() const override { return concreteTypeName; }
704   static const char16_t concreteTypeName[];
705 };
706 
707 }  // namespace ubi
708 
709 template <>
710 struct DeletePolicy<js::jit::IonScript> {
711   explicit DeletePolicy(JSRuntime* rt) : rt_(rt) {}
712   void operator()(const js::jit::IonScript* script);
713 
714  private:
715   JSRuntime* rt_;
716 };
717 
718 }  // namespace JS
719 
720 #endif /* jit_IonCode_h */
721