|
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 #include "jit/Snapshots.h" |
|
8 |
|
9 #include "jsscript.h" |
|
10 |
|
11 #include "jit/CompileInfo.h" |
|
12 #include "jit/IonSpewer.h" |
|
13 #ifdef TRACK_SNAPSHOTS |
|
14 # include "jit/LIR.h" |
|
15 #endif |
|
16 #include "jit/MIR.h" |
|
17 #include "jit/Recover.h" |
|
18 |
|
19 using namespace js; |
|
20 using namespace js::jit; |
|
21 |
|
22 // Snapshot header: |
|
23 // |
|
24 // [vwu] bits ((n+1)-31]: frame count |
|
25 // bit n+1: resume after |
|
26 // bits [0,n): bailout kind (n = SNAPSHOT_BAILOUTKIND_BITS) |
|
27 // |
|
28 // Snapshot body, repeated "frame count" times, from oldest frame to newest frame. |
|
29 // Note that the first frame doesn't have the "parent PC" field. |
|
30 // |
|
31 // [ptr] Debug only: JSScript * |
|
32 // [vwu] pc offset |
|
33 // [vwu] # of RVA's indexes, including nargs |
|
34 // [vwu*] List of indexes to R(ecover)ValueAllocation table. Contains |
|
35 // nargs + nfixed + stackDepth items. |
|
36 // |
|
37 // Recover value allocations are encoded at the end of the Snapshot buffer, and |
|
38 // they are padded on ALLOCATION_TABLE_ALIGNMENT. The encoding of each |
|
39 // allocation is determined by the RValueAllocation::Layout, which can be |
|
40 // obtained from the RValueAllocation::Mode with layoutFromMode function. The |
|
41 // layout structure list the type of payload which are used to serialized / |
|
42 // deserialized / dumped the content of the allocations. |
|
43 // |
|
44 // R(ecover)ValueAllocation items: |
|
45 // [u8'] Mode, which defines the type of the payload as well as the |
|
46 // interpretation. |
|
47 // [pld] first payload (packed tag, index, stack offset, register, ...) |
|
48 // [pld] second payload (register, stack offset, none) |
|
49 // |
|
50 // Modes: |
|
51 // CONSTANT [INDEX] |
|
52 // Index into the constant pool. |
|
53 // |
|
54 // CST_UNDEFINED [] |
|
55 // Constant value which correspond to the "undefined" JS value. |
|
56 // |
|
57 // CST_NULL [] |
|
58 // Constant value which correspond to the "null" JS value. |
|
59 // |
|
60 // DOUBLE_REG [FPU_REG] |
|
61 // Double value stored in a FPU register. |
|
62 // |
|
63 // FLOAT32_REG [FPU_REG] |
|
64 // Float 32bit value stored in a FPU register. |
|
65 // |
|
66 // FLOAT32_STACK [STACK_OFFSET] |
|
67 // Float 32bit value stored on the stack. |
|
68 // |
|
69 // UNTYPED_REG [GPR_REG] |
|
70 // UNTYPED_STACK [STACK_OFFSET] |
|
71 // UNTYPED_REG_REG [GPR_REG, GPR_REG] |
|
72 // UNTYPED_REG_STACK [GPR_REG, STACK_OFFSET] |
|
73 // UNTYPED_STACK_REG [STACK_OFFSET, GPR_REG] |
|
74 // UNTYPED_STACK_STACK [STACK_OFFSET, STACK_OFFSET] |
|
75 // Value with dynamically known type. On 32 bits architecture, the |
|
76 // first register/stack-offset correspond to the holder of the type, |
|
77 // and the second correspond to the payload of the JS Value. |
|
78 // |
|
79 // TYPED_REG [PACKED_TAG, GPR_REG]: |
|
80 // Value with statically known type, which payload is stored in a |
|
81 // register. |
|
82 // |
|
83 // TYPED_STACK [PACKED_TAG, STACK_OFFSET]: |
|
84 // Value with statically known type, which payload is stored at an |
|
85 // offset on the stack. |
|
86 // |
|
87 // Encodings: |
|
88 // [ptr] A fixed-size pointer. |
|
89 // [vwu] A variable-width unsigned integer. |
|
90 // [vws] A variable-width signed integer. |
|
91 // [u8] An 8-bit unsigned integer. |
|
92 // [u8'] An 8-bit unsigned integer which is potentially extended with packed |
|
93 // data. |
|
94 // [u8"] Packed data which is stored and packed in the previous [u8']. |
|
95 // [vwu*] A list of variable-width unsigned integers. |
|
96 // [pld] Payload of Recover Value Allocation: |
|
97 // PAYLOAD_NONE: |
|
98 // There is no payload. |
|
99 // |
|
100 // PAYLOAD_INDEX: |
|
101 // [vwu] Index, such as the constant pool index. |
|
102 // |
|
103 // PAYLOAD_STACK_OFFSET: |
|
104 // [vws] Stack offset based on the base of the Ion frame. |
|
105 // |
|
106 // PAYLOAD_GPR: |
|
107 // [u8] Code of the general register. |
|
108 // |
|
109 // PAYLOAD_FPU: |
|
110 // [u8] Code of the FPU register. |
|
111 // |
|
112 // PAYLOAD_PACKED_TAG: |
|
113 // [u8"] Bits 5-7: JSValueType is encoded on the low bits of the Mode |
|
114 // of the RValueAllocation. |
|
115 // |
|
116 |
|
117 const RValueAllocation::Layout & |
|
118 RValueAllocation::layoutFromMode(Mode mode) |
|
119 { |
|
120 switch (mode) { |
|
121 case CONSTANT: { |
|
122 static const RValueAllocation::Layout layout = { |
|
123 PAYLOAD_INDEX, |
|
124 PAYLOAD_NONE, |
|
125 "constant" |
|
126 }; |
|
127 return layout; |
|
128 } |
|
129 |
|
130 case CST_UNDEFINED: { |
|
131 static const RValueAllocation::Layout layout = { |
|
132 PAYLOAD_NONE, |
|
133 PAYLOAD_NONE, |
|
134 "undefined" |
|
135 }; |
|
136 return layout; |
|
137 } |
|
138 |
|
139 case CST_NULL: { |
|
140 static const RValueAllocation::Layout layout = { |
|
141 PAYLOAD_NONE, |
|
142 PAYLOAD_NONE, |
|
143 "null" |
|
144 }; |
|
145 return layout; |
|
146 } |
|
147 |
|
148 case DOUBLE_REG: { |
|
149 static const RValueAllocation::Layout layout = { |
|
150 PAYLOAD_FPU, |
|
151 PAYLOAD_NONE, |
|
152 "double" |
|
153 }; |
|
154 return layout; |
|
155 } |
|
156 case FLOAT32_REG: { |
|
157 static const RValueAllocation::Layout layout = { |
|
158 PAYLOAD_FPU, |
|
159 PAYLOAD_NONE, |
|
160 "float32" |
|
161 }; |
|
162 return layout; |
|
163 } |
|
164 case FLOAT32_STACK: { |
|
165 static const RValueAllocation::Layout layout = { |
|
166 PAYLOAD_STACK_OFFSET, |
|
167 PAYLOAD_NONE, |
|
168 "float32" |
|
169 }; |
|
170 return layout; |
|
171 } |
|
172 #if defined(JS_NUNBOX32) |
|
173 case UNTYPED_REG_REG: { |
|
174 static const RValueAllocation::Layout layout = { |
|
175 PAYLOAD_GPR, |
|
176 PAYLOAD_GPR, |
|
177 "value" |
|
178 }; |
|
179 return layout; |
|
180 } |
|
181 case UNTYPED_REG_STACK: { |
|
182 static const RValueAllocation::Layout layout = { |
|
183 PAYLOAD_GPR, |
|
184 PAYLOAD_STACK_OFFSET, |
|
185 "value" |
|
186 }; |
|
187 return layout; |
|
188 } |
|
189 case UNTYPED_STACK_REG: { |
|
190 static const RValueAllocation::Layout layout = { |
|
191 PAYLOAD_STACK_OFFSET, |
|
192 PAYLOAD_GPR |
|
193 }; |
|
194 return layout; |
|
195 } |
|
196 case UNTYPED_STACK_STACK: { |
|
197 static const RValueAllocation::Layout layout = { |
|
198 PAYLOAD_STACK_OFFSET, |
|
199 PAYLOAD_STACK_OFFSET, |
|
200 "value" |
|
201 }; |
|
202 return layout; |
|
203 } |
|
204 #elif defined(JS_PUNBOX64) |
|
205 case UNTYPED_REG: { |
|
206 static const RValueAllocation::Layout layout = { |
|
207 PAYLOAD_GPR, |
|
208 PAYLOAD_NONE, |
|
209 "value" |
|
210 }; |
|
211 return layout; |
|
212 } |
|
213 case UNTYPED_STACK: { |
|
214 static const RValueAllocation::Layout layout = { |
|
215 PAYLOAD_STACK_OFFSET, |
|
216 PAYLOAD_NONE, |
|
217 "value" |
|
218 }; |
|
219 return layout; |
|
220 } |
|
221 #endif |
|
222 default: { |
|
223 static const RValueAllocation::Layout regLayout = { |
|
224 PAYLOAD_PACKED_TAG, |
|
225 PAYLOAD_GPR, |
|
226 "typed value" |
|
227 }; |
|
228 |
|
229 static const RValueAllocation::Layout stackLayout = { |
|
230 PAYLOAD_PACKED_TAG, |
|
231 PAYLOAD_STACK_OFFSET, |
|
232 "typed value" |
|
233 }; |
|
234 |
|
235 if (mode >= TYPED_REG_MIN && mode <= TYPED_REG_MAX) |
|
236 return regLayout; |
|
237 if (mode >= TYPED_STACK_MIN && mode <= TYPED_STACK_MAX) |
|
238 return stackLayout; |
|
239 } |
|
240 } |
|
241 |
|
242 MOZ_ASSUME_UNREACHABLE("Wrong mode type?"); |
|
243 } |
|
244 |
|
245 // Pad serialized RValueAllocations by a multiple of X bytes in the allocation |
|
246 // buffer. By padding serialized value allocations, we are building an |
|
247 // indexable table of elements of X bytes, and thus we can safely divide any |
|
248 // offset within the buffer by X to obtain an index. |
|
249 // |
|
250 // By padding, we are loosing space within the allocation buffer, but we |
|
251 // multiple by X the number of indexes that we can store on one byte in each |
|
252 // snapshots. |
|
253 // |
|
254 // Some value allocations are taking more than X bytes to be encoded, in which |
|
255 // case we will pad to a multiple of X, and we are wasting indexes. The choice |
|
256 // of X should be balanced between the wasted padding of serialized value |
|
257 // allocation, and the saving made in snapshot indexes. |
|
258 static const size_t ALLOCATION_TABLE_ALIGNMENT = 2; /* bytes */ |
|
259 |
|
260 void |
|
261 RValueAllocation::readPayload(CompactBufferReader &reader, PayloadType type, |
|
262 uint8_t *mode, Payload *p) |
|
263 { |
|
264 switch (type) { |
|
265 case PAYLOAD_NONE: |
|
266 break; |
|
267 case PAYLOAD_INDEX: |
|
268 p->index = reader.readUnsigned(); |
|
269 break; |
|
270 case PAYLOAD_STACK_OFFSET: |
|
271 p->stackOffset = reader.readSigned(); |
|
272 break; |
|
273 case PAYLOAD_GPR: |
|
274 p->gpr = Register::FromCode(reader.readByte()); |
|
275 break; |
|
276 case PAYLOAD_FPU: |
|
277 p->fpu = FloatRegister::FromCode(reader.readByte()); |
|
278 break; |
|
279 case PAYLOAD_PACKED_TAG: |
|
280 p->type = JSValueType(*mode & 0x07); |
|
281 *mode = *mode & ~0x07; |
|
282 break; |
|
283 } |
|
284 } |
|
285 |
|
286 RValueAllocation |
|
287 RValueAllocation::read(CompactBufferReader &reader) |
|
288 { |
|
289 uint8_t mode = reader.readByte(); |
|
290 const Layout &layout = layoutFromMode(Mode(mode)); |
|
291 Payload arg1, arg2; |
|
292 |
|
293 readPayload(reader, layout.type1, &mode, &arg1); |
|
294 readPayload(reader, layout.type2, &mode, &arg2); |
|
295 return RValueAllocation(Mode(mode), arg1, arg2); |
|
296 } |
|
297 |
|
298 void |
|
299 RValueAllocation::writePayload(CompactBufferWriter &writer, PayloadType type, |
|
300 Payload p) |
|
301 { |
|
302 switch (type) { |
|
303 case PAYLOAD_NONE: |
|
304 break; |
|
305 case PAYLOAD_INDEX: |
|
306 writer.writeUnsigned(p.index); |
|
307 break; |
|
308 case PAYLOAD_STACK_OFFSET: |
|
309 writer.writeSigned(p.stackOffset); |
|
310 break; |
|
311 case PAYLOAD_GPR: |
|
312 static_assert(Registers::Total <= 0x100, |
|
313 "Not enough bytes to encode all registers."); |
|
314 writer.writeByte(p.gpr.code()); |
|
315 break; |
|
316 case PAYLOAD_FPU: |
|
317 static_assert(FloatRegisters::Total <= 0x100, |
|
318 "Not enough bytes to encode all float registers."); |
|
319 writer.writeByte(p.fpu.code()); |
|
320 break; |
|
321 case PAYLOAD_PACKED_TAG: { |
|
322 // This code assumes that the PACKED_TAG payload is following the |
|
323 // writeByte of the mode. |
|
324 MOZ_ASSERT(writer.length()); |
|
325 uint8_t *mode = writer.buffer() + (writer.length() - 1); |
|
326 MOZ_ASSERT((*mode & 0x07) == 0 && (p.type & ~0x07) == 0); |
|
327 *mode = *mode | p.type; |
|
328 break; |
|
329 } |
|
330 } |
|
331 } |
|
332 |
|
333 void |
|
334 RValueAllocation::writePadding(CompactBufferWriter &writer) |
|
335 { |
|
336 // Write 0x7f in all padding bytes. |
|
337 while (writer.length() % ALLOCATION_TABLE_ALIGNMENT) |
|
338 writer.writeByte(0x7f); |
|
339 } |
|
340 |
|
341 void |
|
342 RValueAllocation::write(CompactBufferWriter &writer) const |
|
343 { |
|
344 const Layout &layout = layoutFromMode(mode()); |
|
345 MOZ_ASSERT(layout.type2 != PAYLOAD_PACKED_TAG); |
|
346 MOZ_ASSERT(writer.length() % ALLOCATION_TABLE_ALIGNMENT == 0); |
|
347 |
|
348 writer.writeByte(mode_); |
|
349 writePayload(writer, layout.type1, arg1_); |
|
350 writePayload(writer, layout.type2, arg2_); |
|
351 writePadding(writer); |
|
352 } |
|
353 |
|
354 HashNumber |
|
355 RValueAllocation::hash() const { |
|
356 CompactBufferWriter writer; |
|
357 write(writer); |
|
358 |
|
359 // We should never oom because the compact buffer writer has 32 inlined |
|
360 // bytes, and in the worse case scenario, only encode 12 bytes |
|
361 // (12 == mode + signed + signed + pad). |
|
362 MOZ_ASSERT(!writer.oom()); |
|
363 MOZ_ASSERT(writer.length() <= 12); |
|
364 |
|
365 HashNumber res = 0; |
|
366 for (size_t i = 0; i < writer.length(); i++) { |
|
367 res = ((res << 8) | (res >> (sizeof(res) - 1))); |
|
368 res ^= writer.buffer()[i]; |
|
369 } |
|
370 return res; |
|
371 } |
|
372 |
|
373 static const char * |
|
374 ValTypeToString(JSValueType type) |
|
375 { |
|
376 switch (type) { |
|
377 case JSVAL_TYPE_INT32: |
|
378 return "int32_t"; |
|
379 case JSVAL_TYPE_DOUBLE: |
|
380 return "double"; |
|
381 case JSVAL_TYPE_STRING: |
|
382 return "string"; |
|
383 case JSVAL_TYPE_BOOLEAN: |
|
384 return "boolean"; |
|
385 case JSVAL_TYPE_OBJECT: |
|
386 return "object"; |
|
387 case JSVAL_TYPE_MAGIC: |
|
388 return "magic"; |
|
389 default: |
|
390 MOZ_ASSUME_UNREACHABLE("no payload"); |
|
391 } |
|
392 } |
|
393 |
|
394 void |
|
395 RValueAllocation::dumpPayload(FILE *fp, PayloadType type, Payload p) |
|
396 { |
|
397 switch (type) { |
|
398 case PAYLOAD_NONE: |
|
399 break; |
|
400 case PAYLOAD_INDEX: |
|
401 fprintf(fp, "index %u", p.index); |
|
402 break; |
|
403 case PAYLOAD_STACK_OFFSET: |
|
404 fprintf(fp, "stack %d", p.stackOffset); |
|
405 break; |
|
406 case PAYLOAD_GPR: |
|
407 fprintf(fp, "reg %s", p.gpr.name()); |
|
408 break; |
|
409 case PAYLOAD_FPU: |
|
410 fprintf(fp, "reg %s", p.fpu.name()); |
|
411 break; |
|
412 case PAYLOAD_PACKED_TAG: |
|
413 fprintf(fp, "%s", ValTypeToString(p.type)); |
|
414 break; |
|
415 } |
|
416 } |
|
417 |
|
418 void |
|
419 RValueAllocation::dump(FILE *fp) const |
|
420 { |
|
421 const Layout &layout = layoutFromMode(mode()); |
|
422 fprintf(fp, "%s", layout.name); |
|
423 |
|
424 if (layout.type1 != PAYLOAD_NONE) |
|
425 fprintf(fp, " ("); |
|
426 dumpPayload(fp, layout.type1, arg1_); |
|
427 if (layout.type2 != PAYLOAD_NONE) |
|
428 fprintf(fp, ", "); |
|
429 dumpPayload(fp, layout.type2, arg2_); |
|
430 if (layout.type1 != PAYLOAD_NONE) |
|
431 fprintf(fp, ")"); |
|
432 } |
|
433 |
|
434 bool |
|
435 RValueAllocation::equalPayloads(PayloadType type, Payload lhs, Payload rhs) |
|
436 { |
|
437 switch (type) { |
|
438 case PAYLOAD_NONE: |
|
439 return true; |
|
440 case PAYLOAD_INDEX: |
|
441 return lhs.index == rhs.index; |
|
442 case PAYLOAD_STACK_OFFSET: |
|
443 return lhs.stackOffset == rhs.stackOffset; |
|
444 case PAYLOAD_GPR: |
|
445 return lhs.gpr == rhs.gpr; |
|
446 case PAYLOAD_FPU: |
|
447 return lhs.fpu == rhs.fpu; |
|
448 case PAYLOAD_PACKED_TAG: |
|
449 return lhs.type == rhs.type; |
|
450 } |
|
451 |
|
452 return false; |
|
453 } |
|
454 |
|
455 SnapshotReader::SnapshotReader(const uint8_t *snapshots, uint32_t offset, |
|
456 uint32_t RVATableSize, uint32_t listSize) |
|
457 : reader_(snapshots + offset, snapshots + listSize), |
|
458 allocReader_(snapshots + listSize, snapshots + listSize + RVATableSize), |
|
459 allocTable_(snapshots + listSize), |
|
460 allocRead_(0) |
|
461 { |
|
462 if (!snapshots) |
|
463 return; |
|
464 IonSpew(IonSpew_Snapshots, "Creating snapshot reader"); |
|
465 readSnapshotHeader(); |
|
466 } |
|
467 |
|
468 #define COMPUTE_SHIFT_AFTER_(name) (name ## _BITS + name ##_SHIFT) |
|
469 #define COMPUTE_MASK_(name) ((uint32_t(1 << name ## _BITS) - 1) << name ##_SHIFT) |
|
470 |
|
471 // Details of snapshot header packing. |
|
472 static const uint32_t SNAPSHOT_BAILOUTKIND_SHIFT = 0; |
|
473 static const uint32_t SNAPSHOT_BAILOUTKIND_BITS = 3; |
|
474 static const uint32_t SNAPSHOT_BAILOUTKIND_MASK = COMPUTE_MASK_(SNAPSHOT_BAILOUTKIND); |
|
475 |
|
476 static const uint32_t SNAPSHOT_ROFFSET_SHIFT = COMPUTE_SHIFT_AFTER_(SNAPSHOT_BAILOUTKIND); |
|
477 static const uint32_t SNAPSHOT_ROFFSET_BITS = 32 - SNAPSHOT_ROFFSET_SHIFT; |
|
478 static const uint32_t SNAPSHOT_ROFFSET_MASK = COMPUTE_MASK_(SNAPSHOT_ROFFSET); |
|
479 |
|
480 // Details of recover header packing. |
|
481 static const uint32_t RECOVER_RESUMEAFTER_SHIFT = 0; |
|
482 static const uint32_t RECOVER_RESUMEAFTER_BITS = 1; |
|
483 static const uint32_t RECOVER_RESUMEAFTER_MASK = COMPUTE_MASK_(RECOVER_RESUMEAFTER); |
|
484 |
|
485 static const uint32_t RECOVER_RINSCOUNT_SHIFT = COMPUTE_SHIFT_AFTER_(RECOVER_RESUMEAFTER); |
|
486 static const uint32_t RECOVER_RINSCOUNT_BITS = 32 - RECOVER_RINSCOUNT_SHIFT; |
|
487 static const uint32_t RECOVER_RINSCOUNT_MASK = COMPUTE_MASK_(RECOVER_RINSCOUNT); |
|
488 |
|
489 #undef COMPUTE_MASK_ |
|
490 #undef COMPUTE_SHIFT_AFTER_ |
|
491 |
|
492 void |
|
493 SnapshotReader::readSnapshotHeader() |
|
494 { |
|
495 uint32_t bits = reader_.readUnsigned(); |
|
496 |
|
497 bailoutKind_ = BailoutKind((bits & SNAPSHOT_BAILOUTKIND_MASK) >> SNAPSHOT_BAILOUTKIND_SHIFT); |
|
498 recoverOffset_ = (bits & SNAPSHOT_ROFFSET_MASK) >> SNAPSHOT_ROFFSET_SHIFT; |
|
499 |
|
500 IonSpew(IonSpew_Snapshots, "Read snapshot header with bailout kind %u", |
|
501 bailoutKind_); |
|
502 |
|
503 #ifdef TRACK_SNAPSHOTS |
|
504 readTrackSnapshot(); |
|
505 #endif |
|
506 } |
|
507 |
|
508 #ifdef TRACK_SNAPSHOTS |
|
509 void |
|
510 SnapshotReader::readTrackSnapshot() |
|
511 { |
|
512 pcOpcode_ = reader_.readUnsigned(); |
|
513 mirOpcode_ = reader_.readUnsigned(); |
|
514 mirId_ = reader_.readUnsigned(); |
|
515 lirOpcode_ = reader_.readUnsigned(); |
|
516 lirId_ = reader_.readUnsigned(); |
|
517 } |
|
518 |
|
519 void |
|
520 SnapshotReader::spewBailingFrom() const |
|
521 { |
|
522 if (IonSpewEnabled(IonSpew_Bailouts)) { |
|
523 IonSpewHeader(IonSpew_Bailouts); |
|
524 fprintf(IonSpewFile, " bailing from bytecode: %s, MIR: ", js_CodeName[pcOpcode_]); |
|
525 MDefinition::PrintOpcodeName(IonSpewFile, MDefinition::Opcode(mirOpcode_)); |
|
526 fprintf(IonSpewFile, " [%u], LIR: ", mirId_); |
|
527 LInstruction::printName(IonSpewFile, LInstruction::Opcode(lirOpcode_)); |
|
528 fprintf(IonSpewFile, " [%u]", lirId_); |
|
529 fprintf(IonSpewFile, "\n"); |
|
530 } |
|
531 } |
|
532 #endif |
|
533 |
|
534 uint32_t |
|
535 SnapshotReader::readAllocationIndex() |
|
536 { |
|
537 allocRead_++; |
|
538 return reader_.readUnsigned(); |
|
539 } |
|
540 |
|
541 RValueAllocation |
|
542 SnapshotReader::readAllocation() |
|
543 { |
|
544 IonSpew(IonSpew_Snapshots, "Reading slot %u", allocRead_); |
|
545 uint32_t offset = readAllocationIndex() * ALLOCATION_TABLE_ALIGNMENT; |
|
546 allocReader_.seek(allocTable_, offset); |
|
547 return RValueAllocation::read(allocReader_); |
|
548 } |
|
549 |
|
550 bool |
|
551 SnapshotWriter::init() |
|
552 { |
|
553 // Based on the measurements made in Bug 962555 comment 20, this should be |
|
554 // enough to prevent the reallocation of the hash table for at least half of |
|
555 // the compilations. |
|
556 return allocMap_.init(32); |
|
557 } |
|
558 |
|
559 RecoverReader::RecoverReader(SnapshotReader &snapshot, const uint8_t *recovers, uint32_t size) |
|
560 : reader_(nullptr, nullptr), |
|
561 numInstructions_(0), |
|
562 numInstructionsRead_(0) |
|
563 { |
|
564 if (!recovers) |
|
565 return; |
|
566 reader_ = CompactBufferReader(recovers + snapshot.recoverOffset(), recovers + size); |
|
567 readRecoverHeader(); |
|
568 readInstruction(); |
|
569 } |
|
570 |
|
571 void |
|
572 RecoverReader::readRecoverHeader() |
|
573 { |
|
574 uint32_t bits = reader_.readUnsigned(); |
|
575 |
|
576 numInstructions_ = (bits & RECOVER_RINSCOUNT_MASK) >> RECOVER_RINSCOUNT_SHIFT; |
|
577 resumeAfter_ = (bits & RECOVER_RESUMEAFTER_MASK) >> RECOVER_RESUMEAFTER_SHIFT; |
|
578 MOZ_ASSERT(numInstructions_); |
|
579 |
|
580 IonSpew(IonSpew_Snapshots, "Read recover header with instructionCount %u (ra: %d)", |
|
581 numInstructions_, resumeAfter_); |
|
582 } |
|
583 |
|
584 void |
|
585 RecoverReader::readInstruction() |
|
586 { |
|
587 MOZ_ASSERT(moreInstructions()); |
|
588 RInstruction::readRecoverData(reader_, &rawData_); |
|
589 numInstructionsRead_++; |
|
590 } |
|
591 |
|
592 SnapshotOffset |
|
593 SnapshotWriter::startSnapshot(RecoverOffset recoverOffset, BailoutKind kind) |
|
594 { |
|
595 lastStart_ = writer_.length(); |
|
596 allocWritten_ = 0; |
|
597 |
|
598 IonSpew(IonSpew_Snapshots, "starting snapshot with recover offset %u, bailout kind %u", |
|
599 recoverOffset, kind); |
|
600 |
|
601 JS_ASSERT(uint32_t(kind) < (1 << SNAPSHOT_BAILOUTKIND_BITS)); |
|
602 JS_ASSERT(recoverOffset < (1 << SNAPSHOT_ROFFSET_BITS)); |
|
603 uint32_t bits = |
|
604 (uint32_t(kind) << SNAPSHOT_BAILOUTKIND_SHIFT) | |
|
605 (recoverOffset << SNAPSHOT_ROFFSET_SHIFT); |
|
606 |
|
607 writer_.writeUnsigned(bits); |
|
608 return lastStart_; |
|
609 } |
|
610 |
|
611 #ifdef TRACK_SNAPSHOTS |
|
612 void |
|
613 SnapshotWriter::trackSnapshot(uint32_t pcOpcode, uint32_t mirOpcode, uint32_t mirId, |
|
614 uint32_t lirOpcode, uint32_t lirId) |
|
615 { |
|
616 writer_.writeUnsigned(pcOpcode); |
|
617 writer_.writeUnsigned(mirOpcode); |
|
618 writer_.writeUnsigned(mirId); |
|
619 writer_.writeUnsigned(lirOpcode); |
|
620 writer_.writeUnsigned(lirId); |
|
621 } |
|
622 #endif |
|
623 |
|
624 bool |
|
625 SnapshotWriter::add(const RValueAllocation &alloc) |
|
626 { |
|
627 MOZ_ASSERT(allocMap_.initialized()); |
|
628 |
|
629 uint32_t offset; |
|
630 RValueAllocMap::AddPtr p = allocMap_.lookupForAdd(alloc); |
|
631 if (!p) { |
|
632 offset = allocWriter_.length(); |
|
633 alloc.write(allocWriter_); |
|
634 if (!allocMap_.add(p, alloc, offset)) |
|
635 return false; |
|
636 } else { |
|
637 offset = p->value(); |
|
638 } |
|
639 |
|
640 if (IonSpewEnabled(IonSpew_Snapshots)) { |
|
641 IonSpewHeader(IonSpew_Snapshots); |
|
642 fprintf(IonSpewFile, " slot %u (%d): ", allocWritten_, offset); |
|
643 alloc.dump(IonSpewFile); |
|
644 fprintf(IonSpewFile, "\n"); |
|
645 } |
|
646 |
|
647 allocWritten_++; |
|
648 writer_.writeUnsigned(offset / ALLOCATION_TABLE_ALIGNMENT); |
|
649 return true; |
|
650 } |
|
651 |
|
652 void |
|
653 SnapshotWriter::endSnapshot() |
|
654 { |
|
655 // Place a sentinel for asserting on the other end. |
|
656 #ifdef DEBUG |
|
657 writer_.writeSigned(-1); |
|
658 #endif |
|
659 |
|
660 IonSpew(IonSpew_Snapshots, "ending snapshot total size: %u bytes (start %u)", |
|
661 uint32_t(writer_.length() - lastStart_), lastStart_); |
|
662 } |
|
663 |
|
664 RecoverOffset |
|
665 RecoverWriter::startRecover(uint32_t frameCount, bool resumeAfter) |
|
666 { |
|
667 MOZ_ASSERT(frameCount); |
|
668 nframes_ = frameCount; |
|
669 framesWritten_ = 0; |
|
670 |
|
671 IonSpew(IonSpew_Snapshots, "starting recover with frameCount %u", |
|
672 frameCount); |
|
673 |
|
674 MOZ_ASSERT(!(uint32_t(resumeAfter) &~ RECOVER_RESUMEAFTER_MASK)); |
|
675 MOZ_ASSERT(frameCount < uint32_t(1 << RECOVER_RINSCOUNT_BITS)); |
|
676 uint32_t bits = |
|
677 (uint32_t(resumeAfter) << RECOVER_RESUMEAFTER_SHIFT) | |
|
678 (frameCount << RECOVER_RINSCOUNT_SHIFT); |
|
679 |
|
680 RecoverOffset recoverOffset = writer_.length(); |
|
681 writer_.writeUnsigned(bits); |
|
682 return recoverOffset; |
|
683 } |
|
684 |
|
685 bool |
|
686 RecoverWriter::writeFrame(const MResumePoint *rp) |
|
687 { |
|
688 if (!rp->writeRecoverData(writer_)) |
|
689 return false; |
|
690 framesWritten_++; |
|
691 return true; |
|
692 } |
|
693 |
|
694 void |
|
695 RecoverWriter::endRecover() |
|
696 { |
|
697 JS_ASSERT(nframes_ == framesWritten_); |
|
698 } |