|
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_Snapshot_h |
|
8 #define jit_Snapshot_h |
|
9 |
|
10 #include "mozilla/Alignment.h" |
|
11 |
|
12 #include "jsalloc.h" |
|
13 #include "jsbytecode.h" |
|
14 |
|
15 #include "jit/CompactBuffer.h" |
|
16 #include "jit/IonTypes.h" |
|
17 #include "jit/Registers.h" |
|
18 |
|
19 #include "js/HashTable.h" |
|
20 |
|
21 namespace js { |
|
22 namespace jit { |
|
23 |
|
24 class RValueAllocation; |
|
25 |
|
26 // A Recover Value Allocation mirror what is known at compiled time as being the |
|
27 // MIRType and the LAllocation. This is read out of the snapshot to recover the |
|
28 // value which would be there if this frame was an interpreter frame instead of |
|
29 // an Ion frame. |
|
30 // |
|
31 // It is used with the SnapshotIterator to recover a Value from the stack, |
|
32 // spilled registers or the list of constant of the compiled script. |
|
33 // |
|
34 // Unit tests are located in jsapi-tests/testJitRValueAlloc.cpp. |
|
35 class RValueAllocation |
|
36 { |
|
37 public: |
|
38 |
|
39 // See RValueAllocation encoding in Snapshots.cpp |
|
40 enum Mode |
|
41 { |
|
42 CONSTANT = 0x00, |
|
43 CST_UNDEFINED = 0x01, |
|
44 CST_NULL = 0x02, |
|
45 DOUBLE_REG = 0x03, |
|
46 FLOAT32_REG = 0x04, |
|
47 FLOAT32_STACK = 0x05, |
|
48 #if defined(JS_NUNBOX32) |
|
49 UNTYPED_REG_REG = 0x06, |
|
50 UNTYPED_REG_STACK = 0x07, |
|
51 UNTYPED_STACK_REG = 0x08, |
|
52 UNTYPED_STACK_STACK = 0x09, |
|
53 #elif defined(JS_PUNBOX64) |
|
54 UNTYPED_REG = 0x06, |
|
55 UNTYPED_STACK = 0x07, |
|
56 #endif |
|
57 // The JSValueType is packed in the Mode. |
|
58 TYPED_REG_MIN = 0x10, |
|
59 TYPED_REG_MAX = 0x17, |
|
60 TYPED_REG = TYPED_REG_MIN, |
|
61 |
|
62 // The JSValueType is packed in the Mode. |
|
63 TYPED_STACK_MIN = 0x18, |
|
64 TYPED_STACK_MAX = 0x1f, |
|
65 TYPED_STACK = TYPED_STACK_MIN, |
|
66 |
|
67 INVALID = 0x100, |
|
68 }; |
|
69 |
|
70 // See Payload encoding in Snapshots.cpp |
|
71 enum PayloadType { |
|
72 PAYLOAD_NONE, |
|
73 PAYLOAD_INDEX, |
|
74 PAYLOAD_STACK_OFFSET, |
|
75 PAYLOAD_GPR, |
|
76 PAYLOAD_FPU, |
|
77 PAYLOAD_PACKED_TAG |
|
78 }; |
|
79 |
|
80 struct Layout { |
|
81 PayloadType type1; |
|
82 PayloadType type2; |
|
83 const char *name; |
|
84 }; |
|
85 |
|
86 private: |
|
87 Mode mode_; |
|
88 |
|
89 // Additional information to recover the content of the allocation. |
|
90 union Payload { |
|
91 uint32_t index; |
|
92 int32_t stackOffset; |
|
93 Register gpr; |
|
94 FloatRegister fpu; |
|
95 JSValueType type; |
|
96 }; |
|
97 |
|
98 Payload arg1_; |
|
99 Payload arg2_; |
|
100 |
|
101 static Payload payloadOfIndex(uint32_t index) { |
|
102 Payload p; |
|
103 p.index = index; |
|
104 return p; |
|
105 } |
|
106 static Payload payloadOfStackOffset(int32_t offset) { |
|
107 Payload p; |
|
108 p.stackOffset = offset; |
|
109 return p; |
|
110 } |
|
111 static Payload payloadOfRegister(Register reg) { |
|
112 Payload p; |
|
113 p.gpr = reg; |
|
114 return p; |
|
115 } |
|
116 static Payload payloadOfFloatRegister(FloatRegister reg) { |
|
117 Payload p; |
|
118 p.fpu = reg; |
|
119 return p; |
|
120 } |
|
121 static Payload payloadOfValueType(JSValueType type) { |
|
122 Payload p; |
|
123 p.type = type; |
|
124 return p; |
|
125 } |
|
126 |
|
127 static const Layout &layoutFromMode(Mode mode); |
|
128 |
|
129 static void readPayload(CompactBufferReader &reader, PayloadType t, |
|
130 uint8_t *mode, Payload *p); |
|
131 static void writePayload(CompactBufferWriter &writer, PayloadType t, |
|
132 Payload p); |
|
133 static void writePadding(CompactBufferWriter &writer); |
|
134 static void dumpPayload(FILE *fp, PayloadType t, Payload p); |
|
135 static bool equalPayloads(PayloadType t, Payload lhs, Payload rhs); |
|
136 |
|
137 RValueAllocation(Mode mode, Payload a1, Payload a2) |
|
138 : mode_(mode), |
|
139 arg1_(a1), |
|
140 arg2_(a2) |
|
141 { |
|
142 } |
|
143 |
|
144 RValueAllocation(Mode mode, Payload a1) |
|
145 : mode_(mode), |
|
146 arg1_(a1) |
|
147 { |
|
148 } |
|
149 |
|
150 RValueAllocation(Mode mode) |
|
151 : mode_(mode) |
|
152 { |
|
153 } |
|
154 |
|
155 public: |
|
156 RValueAllocation() |
|
157 : mode_(INVALID) |
|
158 { } |
|
159 |
|
160 // DOUBLE_REG |
|
161 static RValueAllocation Double(const FloatRegister ®) { |
|
162 return RValueAllocation(DOUBLE_REG, payloadOfFloatRegister(reg)); |
|
163 } |
|
164 |
|
165 // FLOAT32_REG or FLOAT32_STACK |
|
166 static RValueAllocation Float32(const FloatRegister ®) { |
|
167 return RValueAllocation(FLOAT32_REG, payloadOfFloatRegister(reg)); |
|
168 } |
|
169 static RValueAllocation Float32(int32_t offset) { |
|
170 return RValueAllocation(FLOAT32_STACK, payloadOfStackOffset(offset)); |
|
171 } |
|
172 |
|
173 // TYPED_REG or TYPED_STACK |
|
174 static RValueAllocation Typed(JSValueType type, const Register ®) { |
|
175 JS_ASSERT(type != JSVAL_TYPE_DOUBLE && |
|
176 type != JSVAL_TYPE_MAGIC && |
|
177 type != JSVAL_TYPE_NULL && |
|
178 type != JSVAL_TYPE_UNDEFINED); |
|
179 return RValueAllocation(TYPED_REG, payloadOfValueType(type), |
|
180 payloadOfRegister(reg)); |
|
181 } |
|
182 static RValueAllocation Typed(JSValueType type, int32_t offset) { |
|
183 JS_ASSERT(type != JSVAL_TYPE_MAGIC && |
|
184 type != JSVAL_TYPE_NULL && |
|
185 type != JSVAL_TYPE_UNDEFINED); |
|
186 return RValueAllocation(TYPED_STACK, payloadOfValueType(type), |
|
187 payloadOfStackOffset(offset)); |
|
188 } |
|
189 |
|
190 // UNTYPED |
|
191 #if defined(JS_NUNBOX32) |
|
192 static RValueAllocation Untyped(const Register &type, const Register &payload) { |
|
193 return RValueAllocation(UNTYPED_REG_REG, |
|
194 payloadOfRegister(type), |
|
195 payloadOfRegister(payload)); |
|
196 } |
|
197 |
|
198 static RValueAllocation Untyped(const Register &type, int32_t payloadStackOffset) { |
|
199 return RValueAllocation(UNTYPED_REG_STACK, |
|
200 payloadOfRegister(type), |
|
201 payloadOfStackOffset(payloadStackOffset)); |
|
202 } |
|
203 |
|
204 static RValueAllocation Untyped(int32_t typeStackOffset, const Register &payload) { |
|
205 return RValueAllocation(UNTYPED_STACK_REG, |
|
206 payloadOfStackOffset(typeStackOffset), |
|
207 payloadOfRegister(payload)); |
|
208 } |
|
209 |
|
210 static RValueAllocation Untyped(int32_t typeStackOffset, int32_t payloadStackOffset) { |
|
211 return RValueAllocation(UNTYPED_STACK_STACK, |
|
212 payloadOfStackOffset(typeStackOffset), |
|
213 payloadOfStackOffset(payloadStackOffset)); |
|
214 } |
|
215 |
|
216 #elif defined(JS_PUNBOX64) |
|
217 static RValueAllocation Untyped(const Register ®) { |
|
218 return RValueAllocation(UNTYPED_REG, payloadOfRegister(reg)); |
|
219 } |
|
220 |
|
221 static RValueAllocation Untyped(int32_t stackOffset) { |
|
222 return RValueAllocation(UNTYPED_STACK, payloadOfStackOffset(stackOffset)); |
|
223 } |
|
224 #endif |
|
225 |
|
226 // common constants. |
|
227 static RValueAllocation Undefined() { |
|
228 return RValueAllocation(CST_UNDEFINED); |
|
229 } |
|
230 static RValueAllocation Null() { |
|
231 return RValueAllocation(CST_NULL); |
|
232 } |
|
233 |
|
234 // CONSTANT's index |
|
235 static RValueAllocation ConstantPool(uint32_t index) { |
|
236 return RValueAllocation(CONSTANT, payloadOfIndex(index)); |
|
237 } |
|
238 |
|
239 void writeHeader(CompactBufferWriter &writer, JSValueType type, uint32_t regCode) const; |
|
240 public: |
|
241 static RValueAllocation read(CompactBufferReader &reader); |
|
242 void write(CompactBufferWriter &writer) const; |
|
243 |
|
244 public: |
|
245 Mode mode() const { |
|
246 return mode_; |
|
247 } |
|
248 |
|
249 uint32_t index() const { |
|
250 JS_ASSERT(layoutFromMode(mode()).type1 == PAYLOAD_INDEX); |
|
251 return arg1_.index; |
|
252 } |
|
253 int32_t stackOffset() const { |
|
254 JS_ASSERT(layoutFromMode(mode()).type1 == PAYLOAD_STACK_OFFSET); |
|
255 return arg1_.stackOffset; |
|
256 } |
|
257 Register reg() const { |
|
258 JS_ASSERT(layoutFromMode(mode()).type1 == PAYLOAD_GPR); |
|
259 return arg1_.gpr; |
|
260 } |
|
261 FloatRegister fpuReg() const { |
|
262 JS_ASSERT(layoutFromMode(mode()).type1 == PAYLOAD_FPU); |
|
263 return arg1_.fpu; |
|
264 } |
|
265 JSValueType knownType() const { |
|
266 JS_ASSERT(layoutFromMode(mode()).type1 == PAYLOAD_PACKED_TAG); |
|
267 return arg1_.type; |
|
268 } |
|
269 |
|
270 int32_t stackOffset2() const { |
|
271 JS_ASSERT(layoutFromMode(mode()).type2 == PAYLOAD_STACK_OFFSET); |
|
272 return arg2_.stackOffset; |
|
273 } |
|
274 Register reg2() const { |
|
275 JS_ASSERT(layoutFromMode(mode()).type2 == PAYLOAD_GPR); |
|
276 return arg2_.gpr; |
|
277 } |
|
278 |
|
279 public: |
|
280 void dump(FILE *fp) const; |
|
281 |
|
282 public: |
|
283 bool operator==(const RValueAllocation &rhs) const { |
|
284 if (mode_ != rhs.mode_) |
|
285 return false; |
|
286 |
|
287 const Layout &layout = layoutFromMode(mode()); |
|
288 return equalPayloads(layout.type1, arg1_, rhs.arg1_) && |
|
289 equalPayloads(layout.type2, arg2_, rhs.arg2_); |
|
290 } |
|
291 |
|
292 HashNumber hash() const; |
|
293 |
|
294 struct Hasher |
|
295 { |
|
296 typedef RValueAllocation Key; |
|
297 typedef Key Lookup; |
|
298 static HashNumber hash(const Lookup &v) { |
|
299 return v.hash(); |
|
300 } |
|
301 static bool match(const Key &k, const Lookup &l) { |
|
302 return k == l; |
|
303 } |
|
304 }; |
|
305 }; |
|
306 |
|
307 class RecoverWriter; |
|
308 |
|
309 // Collects snapshots in a contiguous buffer, which is copied into IonScript |
|
310 // memory after code generation. |
|
311 class SnapshotWriter |
|
312 { |
|
313 CompactBufferWriter writer_; |
|
314 CompactBufferWriter allocWriter_; |
|
315 |
|
316 // Map RValueAllocations to an offset in the allocWriter_ buffer. This is |
|
317 // useful as value allocations are repeated frequently. |
|
318 typedef RValueAllocation RVA; |
|
319 typedef HashMap<RVA, uint32_t, RVA::Hasher, SystemAllocPolicy> RValueAllocMap; |
|
320 RValueAllocMap allocMap_; |
|
321 |
|
322 // This is only used to assert sanity. |
|
323 uint32_t allocWritten_; |
|
324 |
|
325 // Used to report size of the snapshot in the spew messages. |
|
326 SnapshotOffset lastStart_; |
|
327 |
|
328 public: |
|
329 bool init(); |
|
330 |
|
331 SnapshotOffset startSnapshot(RecoverOffset recoverOffset, BailoutKind kind); |
|
332 #ifdef TRACK_SNAPSHOTS |
|
333 void trackSnapshot(uint32_t pcOpcode, uint32_t mirOpcode, uint32_t mirId, |
|
334 uint32_t lirOpcode, uint32_t lirId); |
|
335 #endif |
|
336 bool add(const RValueAllocation &slot); |
|
337 |
|
338 uint32_t allocWritten() const { |
|
339 return allocWritten_; |
|
340 } |
|
341 void endSnapshot(); |
|
342 |
|
343 bool oom() const { |
|
344 return writer_.oom() || writer_.length() >= MAX_BUFFER_SIZE || |
|
345 allocWriter_.oom() || allocWriter_.length() >= MAX_BUFFER_SIZE; |
|
346 } |
|
347 |
|
348 size_t listSize() const { |
|
349 return writer_.length(); |
|
350 } |
|
351 const uint8_t *listBuffer() const { |
|
352 return writer_.buffer(); |
|
353 } |
|
354 |
|
355 size_t RVATableSize() const { |
|
356 return allocWriter_.length(); |
|
357 } |
|
358 const uint8_t *RVATableBuffer() const { |
|
359 return allocWriter_.buffer(); |
|
360 } |
|
361 }; |
|
362 |
|
363 class MResumePoint; |
|
364 |
|
365 class RecoverWriter |
|
366 { |
|
367 CompactBufferWriter writer_; |
|
368 |
|
369 uint32_t nframes_; |
|
370 uint32_t framesWritten_; |
|
371 |
|
372 public: |
|
373 SnapshotOffset startRecover(uint32_t frameCount, bool resumeAfter); |
|
374 |
|
375 bool writeFrame(const MResumePoint *rp); |
|
376 |
|
377 void endRecover(); |
|
378 |
|
379 size_t size() const { |
|
380 return writer_.length(); |
|
381 } |
|
382 const uint8_t *buffer() const { |
|
383 return writer_.buffer(); |
|
384 } |
|
385 |
|
386 bool oom() const { |
|
387 return writer_.oom() || writer_.length() >= MAX_BUFFER_SIZE; |
|
388 } |
|
389 }; |
|
390 |
|
391 class RecoverReader; |
|
392 |
|
393 // A snapshot reader reads the entries out of the compressed snapshot buffer in |
|
394 // a script. These entries describe the equivalent interpreter frames at a given |
|
395 // position in JIT code. Each entry is an Ion's value allocations, used to |
|
396 // recover the corresponding Value from an Ion frame. |
|
397 class SnapshotReader |
|
398 { |
|
399 CompactBufferReader reader_; |
|
400 CompactBufferReader allocReader_; |
|
401 const uint8_t* allocTable_; |
|
402 |
|
403 BailoutKind bailoutKind_; |
|
404 uint32_t allocRead_; // Number of slots that have been read. |
|
405 RecoverOffset recoverOffset_; // Offset of the recover instructions. |
|
406 |
|
407 #ifdef TRACK_SNAPSHOTS |
|
408 private: |
|
409 uint32_t pcOpcode_; |
|
410 uint32_t mirOpcode_; |
|
411 uint32_t mirId_; |
|
412 uint32_t lirOpcode_; |
|
413 uint32_t lirId_; |
|
414 |
|
415 public: |
|
416 void readTrackSnapshot(); |
|
417 void spewBailingFrom() const; |
|
418 #endif |
|
419 |
|
420 private: |
|
421 void readSnapshotHeader(); |
|
422 uint32_t readAllocationIndex(); |
|
423 |
|
424 public: |
|
425 SnapshotReader(const uint8_t *snapshots, uint32_t offset, |
|
426 uint32_t RVATableSize, uint32_t listSize); |
|
427 |
|
428 RValueAllocation readAllocation(); |
|
429 void skipAllocation() { |
|
430 readAllocationIndex(); |
|
431 } |
|
432 |
|
433 BailoutKind bailoutKind() const { |
|
434 return bailoutKind_; |
|
435 } |
|
436 RecoverOffset recoverOffset() const { |
|
437 return recoverOffset_; |
|
438 } |
|
439 |
|
440 uint32_t numAllocationsRead() const { |
|
441 return allocRead_; |
|
442 } |
|
443 void resetNumAllocationsRead() { |
|
444 allocRead_ = 0; |
|
445 } |
|
446 }; |
|
447 |
|
448 typedef mozilla::AlignedStorage<4 * sizeof(uint32_t)> RInstructionStorage; |
|
449 class RInstruction; |
|
450 |
|
451 class RecoverReader |
|
452 { |
|
453 CompactBufferReader reader_; |
|
454 |
|
455 // Number of encoded instructions. |
|
456 uint32_t numInstructions_; |
|
457 |
|
458 // Number of instruction read. |
|
459 uint32_t numInstructionsRead_; |
|
460 |
|
461 // True if we need to resume after the Resume Point instruction of the |
|
462 // innermost frame. |
|
463 bool resumeAfter_; |
|
464 |
|
465 // Space is reserved as part of the RecoverReader to avoid allocations of |
|
466 // data which is needed to decode the current instruction. |
|
467 RInstructionStorage rawData_; |
|
468 |
|
469 private: |
|
470 void readRecoverHeader(); |
|
471 void readInstruction(); |
|
472 |
|
473 public: |
|
474 RecoverReader(SnapshotReader &snapshot, const uint8_t *recovers, uint32_t size); |
|
475 |
|
476 bool moreInstructions() const { |
|
477 return numInstructionsRead_ < numInstructions_; |
|
478 } |
|
479 void nextInstruction() { |
|
480 readInstruction(); |
|
481 } |
|
482 |
|
483 const RInstruction *instruction() const { |
|
484 return reinterpret_cast<const RInstruction *>(rawData_.addr()); |
|
485 } |
|
486 |
|
487 bool resumeAfter() const { |
|
488 return resumeAfter_; |
|
489 } |
|
490 }; |
|
491 |
|
492 } |
|
493 } |
|
494 |
|
495 #endif /* jit_Snapshot_h */ |