|
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_BaselineJIT_h |
|
8 #define jit_BaselineJIT_h |
|
9 |
|
10 #ifdef JS_ION |
|
11 |
|
12 #include "mozilla/MemoryReporting.h" |
|
13 |
|
14 #include "jscntxt.h" |
|
15 #include "jscompartment.h" |
|
16 |
|
17 #include "ds/LifoAlloc.h" |
|
18 #include "jit/Bailouts.h" |
|
19 #include "jit/IonCode.h" |
|
20 #include "jit/IonMacroAssembler.h" |
|
21 |
|
22 namespace js { |
|
23 namespace jit { |
|
24 |
|
25 class StackValue; |
|
26 class ICEntry; |
|
27 class ICStub; |
|
28 |
|
29 class PCMappingSlotInfo |
|
30 { |
|
31 uint8_t slotInfo_; |
|
32 |
|
33 public: |
|
34 // SlotInfo encoding: |
|
35 // Bits 0 & 1: number of slots at top of stack which are unsynced. |
|
36 // Bits 2 & 3: SlotLocation of top slot value (only relevant if numUnsynced > 0). |
|
37 // Bits 3 & 4: SlotLocation of next slot value (only relevant if numUnsynced > 1). |
|
38 enum SlotLocation { SlotInR0 = 0, SlotInR1 = 1, SlotIgnore = 3 }; |
|
39 |
|
40 PCMappingSlotInfo() |
|
41 : slotInfo_(0) |
|
42 { } |
|
43 |
|
44 explicit PCMappingSlotInfo(uint8_t slotInfo) |
|
45 : slotInfo_(slotInfo) |
|
46 { } |
|
47 |
|
48 static inline bool ValidSlotLocation(SlotLocation loc) { |
|
49 return (loc == SlotInR0) || (loc == SlotInR1) || (loc == SlotIgnore); |
|
50 } |
|
51 |
|
52 static SlotLocation ToSlotLocation(const StackValue *stackVal); |
|
53 |
|
54 inline static PCMappingSlotInfo MakeSlotInfo() { return PCMappingSlotInfo(0); } |
|
55 |
|
56 inline static PCMappingSlotInfo MakeSlotInfo(SlotLocation topSlotLoc) { |
|
57 JS_ASSERT(ValidSlotLocation(topSlotLoc)); |
|
58 return PCMappingSlotInfo(1 | (topSlotLoc << 2)); |
|
59 } |
|
60 |
|
61 inline static PCMappingSlotInfo MakeSlotInfo(SlotLocation topSlotLoc, SlotLocation nextSlotLoc) { |
|
62 JS_ASSERT(ValidSlotLocation(topSlotLoc)); |
|
63 JS_ASSERT(ValidSlotLocation(nextSlotLoc)); |
|
64 return PCMappingSlotInfo(2 | (topSlotLoc << 2) | (nextSlotLoc) << 4); |
|
65 } |
|
66 |
|
67 inline unsigned numUnsynced() const { |
|
68 return slotInfo_ & 0x3; |
|
69 } |
|
70 inline SlotLocation topSlotLocation() const { |
|
71 return static_cast<SlotLocation>((slotInfo_ >> 2) & 0x3); |
|
72 } |
|
73 inline SlotLocation nextSlotLocation() const { |
|
74 return static_cast<SlotLocation>((slotInfo_ >> 4) & 0x3); |
|
75 } |
|
76 inline uint8_t toByte() const { |
|
77 return slotInfo_; |
|
78 } |
|
79 }; |
|
80 |
|
81 // A CompactBuffer is used to store native code offsets (relative to the |
|
82 // previous pc) and PCMappingSlotInfo bytes. To allow binary search into this |
|
83 // table, we maintain a second table of "index" entries. Every X ops, the |
|
84 // compiler will add an index entry, so that from the index entry to the |
|
85 // actual native code offset, we have to iterate at most X times. |
|
86 struct PCMappingIndexEntry |
|
87 { |
|
88 // jsbytecode offset. |
|
89 uint32_t pcOffset; |
|
90 |
|
91 // Native code offset. |
|
92 uint32_t nativeOffset; |
|
93 |
|
94 // Offset in the CompactBuffer where data for pcOffset starts. |
|
95 uint32_t bufferOffset; |
|
96 }; |
|
97 |
|
98 struct BaselineScript |
|
99 { |
|
100 public: |
|
101 static const uint32_t MAX_JSSCRIPT_LENGTH = 0x0fffffffu; |
|
102 |
|
103 // Limit the locals on a given script so that stack check on baseline frames |
|
104 // doesn't overflow a uint32_t value. |
|
105 // (MAX_JSSCRIPT_SLOTS * sizeof(Value)) must fit within a uint32_t. |
|
106 static const uint32_t MAX_JSSCRIPT_SLOTS = 0xffffu; |
|
107 |
|
108 private: |
|
109 // Code pointer containing the actual method. |
|
110 HeapPtr<JitCode> method_; |
|
111 |
|
112 // For heavyweight scripts, template objects to use for the call object and |
|
113 // decl env object (linked via the call object's enclosing scope). |
|
114 HeapPtrObject templateScope_; |
|
115 |
|
116 // Allocated space for fallback stubs. |
|
117 FallbackICStubSpace fallbackStubSpace_; |
|
118 |
|
119 // Native code offset right before the scope chain is initialized. |
|
120 uint32_t prologueOffset_; |
|
121 |
|
122 // Native code offset right before the frame is popped and the method |
|
123 // returned from. |
|
124 uint32_t epilogueOffset_; |
|
125 |
|
126 // The offsets for the toggledJump instructions for SPS update ICs. |
|
127 #ifdef DEBUG |
|
128 mozilla::DebugOnly<bool> spsOn_; |
|
129 #endif |
|
130 uint32_t spsPushToggleOffset_; |
|
131 |
|
132 // Native code offsets right after the debug prologue VM call returns, or |
|
133 // would have returned. This offset is recorded even when debug mode is |
|
134 // off to aid on-stack debug mode recompilation. |
|
135 // |
|
136 // We don't need one for the debug epilogue because that always happens |
|
137 // right before the epilogue, so we just use the epilogue offset. |
|
138 uint32_t postDebugPrologueOffset_; |
|
139 |
|
140 public: |
|
141 enum Flag { |
|
142 // Flag set by JSScript::argumentsOptimizationFailed. Similar to |
|
143 // JSScript::needsArgsObj_, but can be read from JIT code. |
|
144 NEEDS_ARGS_OBJ = 1 << 0, |
|
145 |
|
146 // Flag set when discarding JIT code, to indicate this script is |
|
147 // on the stack and should not be discarded. |
|
148 ACTIVE = 1 << 1, |
|
149 |
|
150 // Flag set when the script contains any writes to its on-stack |
|
151 // (rather than call object stored) arguments. |
|
152 MODIFIES_ARGUMENTS = 1 << 2, |
|
153 |
|
154 // Flag set when compiled for use for debug mode. Handles various |
|
155 // Debugger hooks and compiles toggled calls for traps. |
|
156 DEBUG_MODE = 1 << 3 |
|
157 }; |
|
158 |
|
159 private: |
|
160 uint32_t flags_; |
|
161 |
|
162 private: |
|
163 void trace(JSTracer *trc); |
|
164 |
|
165 uint32_t icEntriesOffset_; |
|
166 uint32_t icEntries_; |
|
167 |
|
168 uint32_t pcMappingIndexOffset_; |
|
169 uint32_t pcMappingIndexEntries_; |
|
170 |
|
171 uint32_t pcMappingOffset_; |
|
172 uint32_t pcMappingSize_; |
|
173 |
|
174 // List mapping indexes of bytecode type sets to the offset of the opcode |
|
175 // they correspond to, for use by TypeScript::BytecodeTypes. |
|
176 uint32_t bytecodeTypeMapOffset_; |
|
177 |
|
178 public: |
|
179 // Do not call directly, use BaselineScript::New. This is public for cx->new_. |
|
180 BaselineScript(uint32_t prologueOffset, uint32_t epilogueOffset, |
|
181 uint32_t spsPushToggleOffset, uint32_t postDebugPrologueOffset); |
|
182 |
|
183 static BaselineScript *New(JSContext *cx, uint32_t prologueOffset, |
|
184 uint32_t epilogueOffset, uint32_t postDebugPrologueOffset, |
|
185 uint32_t spsPushToggleOffset, size_t icEntries, |
|
186 size_t pcMappingIndexEntries, size_t pcMappingSize, |
|
187 size_t bytecodeTypeMapEntries); |
|
188 static void Trace(JSTracer *trc, BaselineScript *script); |
|
189 static void Destroy(FreeOp *fop, BaselineScript *script); |
|
190 |
|
191 void purgeOptimizedStubs(Zone *zone); |
|
192 |
|
193 static inline size_t offsetOfMethod() { |
|
194 return offsetof(BaselineScript, method_); |
|
195 } |
|
196 |
|
197 void addSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf, size_t *data, |
|
198 size_t *fallbackStubs) const { |
|
199 *data += mallocSizeOf(this); |
|
200 |
|
201 // |data| already includes the ICStubSpace itself, so use |
|
202 // sizeOfExcludingThis. |
|
203 *fallbackStubs += fallbackStubSpace_.sizeOfExcludingThis(mallocSizeOf); |
|
204 } |
|
205 |
|
206 bool active() const { |
|
207 return flags_ & ACTIVE; |
|
208 } |
|
209 void setActive() { |
|
210 flags_ |= ACTIVE; |
|
211 } |
|
212 void resetActive() { |
|
213 flags_ &= ~ACTIVE; |
|
214 } |
|
215 |
|
216 void setNeedsArgsObj() { |
|
217 flags_ |= NEEDS_ARGS_OBJ; |
|
218 } |
|
219 |
|
220 void setModifiesArguments() { |
|
221 flags_ |= MODIFIES_ARGUMENTS; |
|
222 } |
|
223 bool modifiesArguments() { |
|
224 return flags_ & MODIFIES_ARGUMENTS; |
|
225 } |
|
226 |
|
227 void setDebugMode() { |
|
228 flags_ |= DEBUG_MODE; |
|
229 } |
|
230 bool debugMode() const { |
|
231 return flags_ & DEBUG_MODE; |
|
232 } |
|
233 |
|
234 uint32_t prologueOffset() const { |
|
235 return prologueOffset_; |
|
236 } |
|
237 uint8_t *prologueEntryAddr() const { |
|
238 return method_->raw() + prologueOffset_; |
|
239 } |
|
240 |
|
241 uint32_t epilogueOffset() const { |
|
242 return epilogueOffset_; |
|
243 } |
|
244 uint8_t *epilogueEntryAddr() const { |
|
245 return method_->raw() + epilogueOffset_; |
|
246 } |
|
247 |
|
248 uint32_t postDebugPrologueOffset() const { |
|
249 return postDebugPrologueOffset_; |
|
250 } |
|
251 uint8_t *postDebugPrologueAddr() const { |
|
252 return method_->raw() + postDebugPrologueOffset_; |
|
253 } |
|
254 |
|
255 ICEntry *icEntryList() { |
|
256 return (ICEntry *)(reinterpret_cast<uint8_t *>(this) + icEntriesOffset_); |
|
257 } |
|
258 PCMappingIndexEntry *pcMappingIndexEntryList() { |
|
259 return (PCMappingIndexEntry *)(reinterpret_cast<uint8_t *>(this) + pcMappingIndexOffset_); |
|
260 } |
|
261 uint8_t *pcMappingData() { |
|
262 return reinterpret_cast<uint8_t *>(this) + pcMappingOffset_; |
|
263 } |
|
264 FallbackICStubSpace *fallbackStubSpace() { |
|
265 return &fallbackStubSpace_; |
|
266 } |
|
267 |
|
268 JitCode *method() const { |
|
269 return method_; |
|
270 } |
|
271 void setMethod(JitCode *code) { |
|
272 JS_ASSERT(!method_); |
|
273 method_ = code; |
|
274 } |
|
275 |
|
276 JSObject *templateScope() const { |
|
277 return templateScope_; |
|
278 } |
|
279 void setTemplateScope(JSObject *templateScope) { |
|
280 JS_ASSERT(!templateScope_); |
|
281 templateScope_ = templateScope; |
|
282 } |
|
283 |
|
284 void toggleBarriers(bool enabled) { |
|
285 method()->togglePreBarriers(enabled); |
|
286 } |
|
287 |
|
288 bool containsCodeAddress(uint8_t *addr) const { |
|
289 return method()->raw() <= addr && addr <= method()->raw() + method()->instructionsSize(); |
|
290 } |
|
291 |
|
292 ICEntry &icEntry(size_t index); |
|
293 ICEntry *maybeICEntryFromReturnOffset(CodeOffsetLabel returnOffset); |
|
294 ICEntry &icEntryFromReturnOffset(CodeOffsetLabel returnOffset); |
|
295 ICEntry &icEntryFromPCOffset(uint32_t pcOffset); |
|
296 ICEntry &icEntryForDebugModeRecompileFromPCOffset(uint32_t pcOffset); |
|
297 ICEntry &icEntryFromPCOffset(uint32_t pcOffset, ICEntry *prevLookedUpEntry); |
|
298 ICEntry *maybeICEntryFromReturnAddress(uint8_t *returnAddr); |
|
299 ICEntry &icEntryFromReturnAddress(uint8_t *returnAddr); |
|
300 uint8_t *returnAddressForIC(const ICEntry &ent); |
|
301 |
|
302 size_t numICEntries() const { |
|
303 return icEntries_; |
|
304 } |
|
305 |
|
306 void copyICEntries(JSScript *script, const ICEntry *entries, MacroAssembler &masm); |
|
307 void adoptFallbackStubs(FallbackICStubSpace *stubSpace); |
|
308 |
|
309 PCMappingIndexEntry &pcMappingIndexEntry(size_t index); |
|
310 CompactBufferReader pcMappingReader(size_t indexEntry); |
|
311 |
|
312 size_t numPCMappingIndexEntries() const { |
|
313 return pcMappingIndexEntries_; |
|
314 } |
|
315 |
|
316 void copyPCMappingIndexEntries(const PCMappingIndexEntry *entries); |
|
317 |
|
318 void copyPCMappingEntries(const CompactBufferWriter &entries); |
|
319 uint8_t *nativeCodeForPC(JSScript *script, jsbytecode *pc, PCMappingSlotInfo *slotInfo = nullptr); |
|
320 jsbytecode *pcForReturnOffset(JSScript *script, uint32_t nativeOffset); |
|
321 jsbytecode *pcForReturnAddress(JSScript *script, uint8_t *nativeAddress); |
|
322 |
|
323 // Toggle debug traps (used for breakpoints and step mode) in the script. |
|
324 // If |pc| is nullptr, toggle traps for all ops in the script. Else, only |
|
325 // toggle traps at |pc|. |
|
326 void toggleDebugTraps(JSScript *script, jsbytecode *pc); |
|
327 |
|
328 void toggleSPS(bool enable); |
|
329 |
|
330 void noteAccessedGetter(uint32_t pcOffset); |
|
331 void noteArrayWriteHole(uint32_t pcOffset); |
|
332 |
|
333 static size_t offsetOfFlags() { |
|
334 return offsetof(BaselineScript, flags_); |
|
335 } |
|
336 |
|
337 static void writeBarrierPre(Zone *zone, BaselineScript *script); |
|
338 |
|
339 uint32_t *bytecodeTypeMap() { |
|
340 JS_ASSERT(bytecodeTypeMapOffset_); |
|
341 return reinterpret_cast<uint32_t *>(reinterpret_cast<uint8_t *>(this) + bytecodeTypeMapOffset_); |
|
342 } |
|
343 }; |
|
344 |
|
345 inline bool |
|
346 IsBaselineEnabled(JSContext *cx) |
|
347 { |
|
348 return cx->runtime()->options().baseline(); |
|
349 } |
|
350 |
|
351 MethodStatus |
|
352 CanEnterBaselineMethod(JSContext *cx, RunState &state); |
|
353 |
|
354 MethodStatus |
|
355 CanEnterBaselineAtBranch(JSContext *cx, InterpreterFrame *fp, bool newType); |
|
356 |
|
357 IonExecStatus |
|
358 EnterBaselineMethod(JSContext *cx, RunState &state); |
|
359 |
|
360 IonExecStatus |
|
361 EnterBaselineAtBranch(JSContext *cx, InterpreterFrame *fp, jsbytecode *pc); |
|
362 |
|
363 void |
|
364 FinishDiscardBaselineScript(FreeOp *fop, JSScript *script); |
|
365 |
|
366 void |
|
367 AddSizeOfBaselineData(JSScript *script, mozilla::MallocSizeOf mallocSizeOf, size_t *data, |
|
368 size_t *fallbackStubs); |
|
369 |
|
370 void |
|
371 ToggleBaselineSPS(JSRuntime *runtime, bool enable); |
|
372 |
|
373 struct BaselineBailoutInfo |
|
374 { |
|
375 // Pointer into the current C stack, where overwriting will start. |
|
376 uint8_t *incomingStack; |
|
377 |
|
378 // The top and bottom heapspace addresses of the reconstructed stack |
|
379 // which will be copied to the bottom. |
|
380 uint8_t *copyStackTop; |
|
381 uint8_t *copyStackBottom; |
|
382 |
|
383 // Fields to store the top-of-stack baseline values that are held |
|
384 // in registers. The setR0 and setR1 fields are flags indicating |
|
385 // whether each one is initialized. |
|
386 uint32_t setR0; |
|
387 Value valueR0; |
|
388 uint32_t setR1; |
|
389 Value valueR1; |
|
390 |
|
391 // The value of the frame pointer register on resume. |
|
392 void *resumeFramePtr; |
|
393 |
|
394 // The native code address to resume into. |
|
395 void *resumeAddr; |
|
396 |
|
397 // If resuming into a TypeMonitor IC chain, this field holds the |
|
398 // address of the first stub in that chain. If this field is |
|
399 // set, then the actual jitcode resumed into is the jitcode for |
|
400 // the first stub, not the resumeAddr above. The resumeAddr |
|
401 // above, in this case, is pushed onto the stack so that the |
|
402 // TypeMonitor chain can tail-return into the main jitcode when done. |
|
403 ICStub *monitorStub; |
|
404 |
|
405 // Number of baseline frames to push on the stack. |
|
406 uint32_t numFrames; |
|
407 |
|
408 // The bailout kind. |
|
409 BailoutKind bailoutKind; |
|
410 }; |
|
411 |
|
412 uint32_t |
|
413 BailoutIonToBaseline(JSContext *cx, JitActivation *activation, IonBailoutIterator &iter, |
|
414 bool invalidate, BaselineBailoutInfo **bailoutInfo, |
|
415 const ExceptionBailoutInfo *exceptionInfo = nullptr); |
|
416 |
|
417 // Mark baseline scripts on the stack as active, so that they are not discarded |
|
418 // during GC. |
|
419 void |
|
420 MarkActiveBaselineScripts(Zone *zone); |
|
421 |
|
422 MethodStatus |
|
423 BaselineCompile(JSContext *cx, JSScript *script); |
|
424 |
|
425 } // namespace jit |
|
426 } // namespace js |
|
427 |
|
428 #endif // JS_ION |
|
429 |
|
430 #endif /* jit_BaselineJIT_h */ |