Line data Source code
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 : #include "mozilla/Attributes.h"
12 :
13 : #include "jsalloc.h"
14 : #include "jsbytecode.h"
15 :
16 : #include "jit/CompactBuffer.h"
17 : #include "jit/IonTypes.h"
18 : #include "jit/Registers.h"
19 :
20 : #include "js/HashTable.h"
21 :
22 : namespace js {
23 : class GenericPrinter;
24 :
25 : namespace jit {
26 :
27 : class RValueAllocation;
28 :
29 : // A Recover Value Allocation mirror what is known at compiled time as being the
30 : // MIRType and the LAllocation. This is read out of the snapshot to recover the
31 : // value which would be there if this frame was an interpreter frame instead of
32 : // an Ion frame.
33 : //
34 : // It is used with the SnapshotIterator to recover a Value from the stack,
35 : // spilled registers or the list of constant of the compiled script.
36 : //
37 : // Unit tests are located in jsapi-tests/testJitRValueAlloc.cpp.
38 : class RValueAllocation
39 : {
40 : public:
41 :
42 : // See RValueAllocation encoding in Snapshots.cpp
43 : enum Mode
44 : {
45 : CONSTANT = 0x00,
46 : CST_UNDEFINED = 0x01,
47 : CST_NULL = 0x02,
48 : DOUBLE_REG = 0x03,
49 : ANY_FLOAT_REG = 0x04,
50 : ANY_FLOAT_STACK = 0x05,
51 : #if defined(JS_NUNBOX32)
52 : UNTYPED_REG_REG = 0x06,
53 : UNTYPED_REG_STACK = 0x07,
54 : UNTYPED_STACK_REG = 0x08,
55 : UNTYPED_STACK_STACK = 0x09,
56 : #elif defined(JS_PUNBOX64)
57 : UNTYPED_REG = 0x06,
58 : UNTYPED_STACK = 0x07,
59 : #endif
60 :
61 : // Recover instructions.
62 : RECOVER_INSTRUCTION = 0x0a,
63 : RI_WITH_DEFAULT_CST = 0x0b,
64 :
65 : // The JSValueType is packed in the Mode.
66 : TYPED_REG_MIN = 0x10,
67 : TYPED_REG_MAX = 0x1f,
68 : TYPED_REG = TYPED_REG_MIN,
69 :
70 : // The JSValueType is packed in the Mode.
71 : TYPED_STACK_MIN = 0x20,
72 : TYPED_STACK_MAX = 0x2f,
73 : TYPED_STACK = TYPED_STACK_MIN,
74 :
75 : // This mask can be used with any other valid mode. When this flag is
76 : // set on the mode, this inform the snapshot iterator that even if the
77 : // allocation is readable, the content of if might be incomplete unless
78 : // all side-effects are executed.
79 : RECOVER_SIDE_EFFECT_MASK = 0x80,
80 :
81 : // This mask represents the set of bits which can be used to encode a
82 : // value in a snapshot. The mode is used to determine how to interpret
83 : // the union of values and how to pack the value in memory.
84 : MODE_BITS_MASK = 0x17f,
85 :
86 : INVALID = 0x100,
87 : };
88 :
89 : enum { PACKED_TAG_MASK = 0x0f };
90 :
91 : // See Payload encoding in Snapshots.cpp
92 : enum PayloadType {
93 : PAYLOAD_NONE,
94 : PAYLOAD_INDEX,
95 : PAYLOAD_STACK_OFFSET,
96 : PAYLOAD_GPR,
97 : PAYLOAD_FPU,
98 : PAYLOAD_PACKED_TAG
99 : };
100 :
101 : struct Layout {
102 : PayloadType type1;
103 : PayloadType type2;
104 : const char* name;
105 : };
106 :
107 : private:
108 : Mode mode_;
109 :
110 : // Additional information to recover the content of the allocation.
111 : struct FloatRegisterBits {
112 : uint32_t data;
113 0 : bool operator == (const FloatRegisterBits& other) const {
114 0 : return data == other.data;
115 : }
116 2 : uint32_t code() const {
117 2 : return data;
118 : }
119 0 : const char* name() const {
120 0 : FloatRegister tmp = FloatRegister::FromCode(data);
121 0 : return tmp.name();
122 : }
123 : };
124 :
125 37092 : union Payload {
126 : uint32_t index;
127 : int32_t stackOffset;
128 : Register gpr;
129 : FloatRegisterBits fpu;
130 : JSValueType type;
131 : };
132 :
133 : Payload arg1_;
134 : Payload arg2_;
135 :
136 1074 : static Payload payloadOfIndex(uint32_t index) {
137 1074 : Payload p;
138 1074 : p.index = index;
139 1074 : return p;
140 : }
141 4368 : static Payload payloadOfStackOffset(int32_t offset) {
142 4368 : Payload p;
143 4368 : p.stackOffset = offset;
144 4368 : return p;
145 : }
146 774 : static Payload payloadOfRegister(Register reg) {
147 774 : Payload p;
148 774 : p.gpr = reg;
149 774 : return p;
150 : }
151 1 : static Payload payloadOfFloatRegister(FloatRegister reg) {
152 1 : Payload p;
153 : FloatRegisterBits b;
154 1 : b.data = reg.code();
155 1 : p.fpu = b;
156 1 : return p;
157 : }
158 2767 : static Payload payloadOfValueType(JSValueType type) {
159 2767 : Payload p;
160 2767 : p.type = type;
161 2767 : return p;
162 : }
163 :
164 : static const Layout& layoutFromMode(Mode mode);
165 :
166 : static void readPayload(CompactBufferReader& reader, PayloadType t,
167 : uint8_t* mode, Payload* p);
168 : static void writePayload(CompactBufferWriter& writer, PayloadType t,
169 : Payload p);
170 : static void writePadding(CompactBufferWriter& writer);
171 : static void dumpPayload(GenericPrinter& out, PayloadType t, Payload p);
172 : static bool equalPayloads(PayloadType t, Payload lhs, Payload rhs);
173 :
174 2767 : RValueAllocation(Mode mode, Payload a1, Payload a2)
175 2767 : : mode_(mode),
176 : arg1_(a1),
177 2767 : arg2_(a2)
178 : {
179 2767 : }
180 :
181 3450 : RValueAllocation(Mode mode, Payload a1)
182 3450 : : mode_(mode),
183 3450 : arg1_(a1)
184 : {
185 3450 : }
186 :
187 1043 : explicit RValueAllocation(Mode mode)
188 1043 : : mode_(mode)
189 : {
190 1043 : }
191 :
192 : public:
193 11286 : RValueAllocation()
194 11286 : : mode_(INVALID)
195 11286 : { }
196 :
197 : // DOUBLE_REG
198 1 : static RValueAllocation Double(FloatRegister reg) {
199 1 : return RValueAllocation(DOUBLE_REG, payloadOfFloatRegister(reg));
200 : }
201 :
202 : // ANY_FLOAT_REG or ANY_FLOAT_STACK
203 0 : static RValueAllocation AnyFloat(FloatRegister reg) {
204 0 : return RValueAllocation(ANY_FLOAT_REG, payloadOfFloatRegister(reg));
205 : }
206 0 : static RValueAllocation AnyFloat(int32_t offset) {
207 0 : return RValueAllocation(ANY_FLOAT_STACK, payloadOfStackOffset(offset));
208 : }
209 :
210 : // TYPED_REG or TYPED_STACK
211 131 : static RValueAllocation Typed(JSValueType type, Register reg) {
212 131 : MOZ_ASSERT(type != JSVAL_TYPE_DOUBLE &&
213 : type != JSVAL_TYPE_MAGIC &&
214 : type != JSVAL_TYPE_NULL &&
215 : type != JSVAL_TYPE_UNDEFINED);
216 262 : return RValueAllocation(TYPED_REG, payloadOfValueType(type),
217 262 : payloadOfRegister(reg));
218 : }
219 2636 : static RValueAllocation Typed(JSValueType type, int32_t offset) {
220 2636 : MOZ_ASSERT(type != JSVAL_TYPE_MAGIC &&
221 : type != JSVAL_TYPE_NULL &&
222 : type != JSVAL_TYPE_UNDEFINED);
223 5272 : return RValueAllocation(TYPED_STACK, payloadOfValueType(type),
224 5272 : payloadOfStackOffset(offset));
225 : }
226 :
227 : // UNTYPED
228 : #if defined(JS_NUNBOX32)
229 : static RValueAllocation Untyped(Register type, Register payload) {
230 : return RValueAllocation(UNTYPED_REG_REG,
231 : payloadOfRegister(type),
232 : payloadOfRegister(payload));
233 : }
234 :
235 : static RValueAllocation Untyped(Register type, int32_t payloadStackOffset) {
236 : return RValueAllocation(UNTYPED_REG_STACK,
237 : payloadOfRegister(type),
238 : payloadOfStackOffset(payloadStackOffset));
239 : }
240 :
241 : static RValueAllocation Untyped(int32_t typeStackOffset, Register payload) {
242 : return RValueAllocation(UNTYPED_STACK_REG,
243 : payloadOfStackOffset(typeStackOffset),
244 : payloadOfRegister(payload));
245 : }
246 :
247 : static RValueAllocation Untyped(int32_t typeStackOffset, int32_t payloadStackOffset) {
248 : return RValueAllocation(UNTYPED_STACK_STACK,
249 : payloadOfStackOffset(typeStackOffset),
250 : payloadOfStackOffset(payloadStackOffset));
251 : }
252 :
253 : #elif defined(JS_PUNBOX64)
254 643 : static RValueAllocation Untyped(Register reg) {
255 643 : return RValueAllocation(UNTYPED_REG, payloadOfRegister(reg));
256 : }
257 :
258 1732 : static RValueAllocation Untyped(int32_t stackOffset) {
259 1732 : return RValueAllocation(UNTYPED_STACK, payloadOfStackOffset(stackOffset));
260 : }
261 : #endif
262 :
263 : // common constants.
264 1043 : static RValueAllocation Undefined() {
265 1043 : return RValueAllocation(CST_UNDEFINED);
266 : }
267 0 : static RValueAllocation Null() {
268 0 : return RValueAllocation(CST_NULL);
269 : }
270 :
271 : // CONSTANT's index
272 766 : static RValueAllocation ConstantPool(uint32_t index) {
273 766 : return RValueAllocation(CONSTANT, payloadOfIndex(index));
274 : }
275 :
276 : // Recover instruction's index
277 308 : static RValueAllocation RecoverInstruction(uint32_t index) {
278 308 : return RValueAllocation(RECOVER_INSTRUCTION, payloadOfIndex(index));
279 : }
280 0 : static RValueAllocation RecoverInstruction(uint32_t riIndex, uint32_t cstIndex) {
281 0 : return RValueAllocation(RI_WITH_DEFAULT_CST,
282 : payloadOfIndex(riIndex),
283 0 : payloadOfIndex(cstIndex));
284 : }
285 :
286 308 : void setNeedSideEffect() {
287 308 : MOZ_ASSERT(!needSideEffect() && mode_ != INVALID);
288 308 : mode_ = Mode(mode_ | RECOVER_SIDE_EFFECT_MASK);
289 308 : }
290 :
291 : void writeHeader(CompactBufferWriter& writer, JSValueType type, uint32_t regCode) const;
292 : public:
293 : static RValueAllocation read(CompactBufferReader& reader);
294 : void write(CompactBufferWriter& writer) const;
295 :
296 : public:
297 14520 : Mode mode() const {
298 14520 : return Mode(mode_ & MODE_BITS_MASK);
299 : }
300 308 : bool needSideEffect() const {
301 308 : return mode_ & RECOVER_SIDE_EFFECT_MASK;
302 : }
303 :
304 0 : uint32_t index() const {
305 0 : MOZ_ASSERT(layoutFromMode(mode()).type1 == PAYLOAD_INDEX);
306 0 : return arg1_.index;
307 : }
308 0 : int32_t stackOffset() const {
309 0 : MOZ_ASSERT(layoutFromMode(mode()).type1 == PAYLOAD_STACK_OFFSET);
310 0 : return arg1_.stackOffset;
311 : }
312 0 : Register reg() const {
313 0 : MOZ_ASSERT(layoutFromMode(mode()).type1 == PAYLOAD_GPR);
314 0 : return arg1_.gpr;
315 : }
316 0 : FloatRegister fpuReg() const {
317 0 : MOZ_ASSERT(layoutFromMode(mode()).type1 == PAYLOAD_FPU);
318 0 : FloatRegisterBits b = arg1_.fpu;
319 0 : return FloatRegister::FromCode(b.data);
320 : }
321 0 : JSValueType knownType() const {
322 0 : MOZ_ASSERT(layoutFromMode(mode()).type1 == PAYLOAD_PACKED_TAG);
323 0 : return arg1_.type;
324 : }
325 :
326 0 : uint32_t index2() const {
327 0 : MOZ_ASSERT(layoutFromMode(mode()).type2 == PAYLOAD_INDEX);
328 0 : return arg2_.index;
329 : }
330 0 : int32_t stackOffset2() const {
331 0 : MOZ_ASSERT(layoutFromMode(mode()).type2 == PAYLOAD_STACK_OFFSET);
332 0 : return arg2_.stackOffset;
333 : }
334 0 : Register reg2() const {
335 0 : MOZ_ASSERT(layoutFromMode(mode()).type2 == PAYLOAD_GPR);
336 0 : return arg2_.gpr;
337 : }
338 :
339 : public:
340 : void dump(GenericPrinter& out) const;
341 :
342 : public:
343 7023 : bool operator==(const RValueAllocation& rhs) const {
344 7023 : if (mode_ != rhs.mode_)
345 0 : return false;
346 :
347 7023 : const Layout& layout = layoutFromMode(mode());
348 14046 : return equalPayloads(layout.type1, arg1_, rhs.arg1_) &&
349 14046 : equalPayloads(layout.type2, arg2_, rhs.arg2_);
350 : }
351 :
352 : HashNumber hash() const;
353 :
354 : struct Hasher
355 : {
356 : typedef RValueAllocation Key;
357 : typedef Key Lookup;
358 7260 : static HashNumber hash(const Lookup& v) {
359 7260 : return v.hash();
360 : }
361 7023 : static bool match(const Key& k, const Lookup& l) {
362 7023 : return k == l;
363 : }
364 : };
365 : };
366 :
367 : class RecoverWriter;
368 :
369 : // Collects snapshots in a contiguous buffer, which is copied into IonScript
370 : // memory after code generation.
371 16 : class SnapshotWriter
372 : {
373 : CompactBufferWriter writer_;
374 : CompactBufferWriter allocWriter_;
375 :
376 : // Map RValueAllocations to an offset in the allocWriter_ buffer. This is
377 : // useful as value allocations are repeated frequently.
378 : typedef RValueAllocation RVA;
379 : typedef HashMap<RVA, uint32_t, RVA::Hasher, SystemAllocPolicy> RValueAllocMap;
380 : RValueAllocMap allocMap_;
381 :
382 : // This is only used to assert sanity.
383 : uint32_t allocWritten_;
384 :
385 : // Used to report size of the snapshot in the spew messages.
386 : SnapshotOffset lastStart_;
387 :
388 : public:
389 : MOZ_MUST_USE bool init();
390 :
391 : SnapshotOffset startSnapshot(RecoverOffset recoverOffset, BailoutKind kind);
392 : #ifdef TRACK_SNAPSHOTS
393 : void trackSnapshot(uint32_t pcOpcode, uint32_t mirOpcode, uint32_t mirId,
394 : uint32_t lirOpcode, uint32_t lirId);
395 : #endif
396 : MOZ_MUST_USE bool add(const RValueAllocation& slot);
397 :
398 14520 : uint32_t allocWritten() const {
399 14520 : return allocWritten_;
400 : }
401 : void endSnapshot();
402 :
403 7583 : bool oom() const {
404 22749 : return writer_.oom() || writer_.length() >= MAX_BUFFER_SIZE ||
405 22749 : allocWriter_.oom() || allocWriter_.length() >= MAX_BUFFER_SIZE;
406 : }
407 :
408 20 : size_t listSize() const {
409 20 : return writer_.length();
410 : }
411 5 : const uint8_t* listBuffer() const {
412 5 : return writer_.buffer();
413 : }
414 :
415 10 : size_t RVATableSize() const {
416 10 : return allocWriter_.length();
417 : }
418 5 : const uint8_t* RVATableBuffer() const {
419 5 : return allocWriter_.buffer();
420 : }
421 : };
422 :
423 : class MNode;
424 :
425 16 : class RecoverWriter
426 : {
427 : CompactBufferWriter writer_;
428 :
429 : uint32_t instructionCount_;
430 : uint32_t instructionsWritten_;
431 :
432 : public:
433 : SnapshotOffset startRecover(uint32_t instructionCount, bool resumeAfter);
434 :
435 : void writeInstruction(const MNode* rp);
436 :
437 : void endRecover();
438 :
439 20 : size_t size() const {
440 20 : return writer_.length();
441 : }
442 5 : const uint8_t* buffer() const {
443 5 : return writer_.buffer();
444 : }
445 :
446 174 : bool oom() const {
447 174 : return writer_.oom() || writer_.length() >= MAX_BUFFER_SIZE;
448 : }
449 : };
450 :
451 : class RecoverReader;
452 :
453 : // A snapshot reader reads the entries out of the compressed snapshot buffer in
454 : // a script. These entries describe the equivalent interpreter frames at a given
455 : // position in JIT code. Each entry is an Ion's value allocations, used to
456 : // recover the corresponding Value from an Ion frame.
457 : class SnapshotReader
458 : {
459 : CompactBufferReader reader_;
460 : CompactBufferReader allocReader_;
461 : const uint8_t* allocTable_;
462 :
463 : BailoutKind bailoutKind_;
464 : uint32_t allocRead_; // Number of slots that have been read.
465 : RecoverOffset recoverOffset_; // Offset of the recover instructions.
466 :
467 : #ifdef TRACK_SNAPSHOTS
468 : private:
469 : uint32_t pcOpcode_;
470 : uint32_t mirOpcode_;
471 : uint32_t mirId_;
472 : uint32_t lirOpcode_;
473 : uint32_t lirId_;
474 :
475 : public:
476 : void readTrackSnapshot();
477 : void spewBailingFrom() const;
478 : #endif
479 :
480 : private:
481 : void readSnapshotHeader();
482 : uint32_t readAllocationIndex();
483 :
484 : public:
485 : SnapshotReader(const uint8_t* snapshots, uint32_t offset,
486 : uint32_t RVATableSize, uint32_t listSize);
487 :
488 : RValueAllocation readAllocation();
489 0 : void skipAllocation() {
490 0 : readAllocationIndex();
491 0 : }
492 :
493 0 : BailoutKind bailoutKind() const {
494 0 : return bailoutKind_;
495 : }
496 840 : RecoverOffset recoverOffset() const {
497 840 : return recoverOffset_;
498 : }
499 :
500 840 : uint32_t numAllocationsRead() const {
501 840 : return allocRead_;
502 : }
503 0 : void resetNumAllocationsRead() {
504 0 : allocRead_ = 0;
505 0 : }
506 : };
507 :
508 : class MOZ_NON_PARAM RInstructionStorage
509 : {
510 : static constexpr size_t Size = 4 * sizeof(uint32_t);
511 :
512 : // This presumes all RInstructionStorage are safely void*-alignable.
513 : // RInstruction::readRecoverData asserts that no RInstruction subclass
514 : // has stricter alignment requirements than RInstructionStorage.
515 : static constexpr size_t Alignment = alignof(void*);
516 :
517 : alignas(Alignment) unsigned char mem[Size];
518 :
519 : public:
520 3360 : const void* addr() const { return mem; }
521 2520 : void* addr() { return mem; }
522 :
523 7212 : RInstructionStorage() = default;
524 :
525 : // Making a copy of raw bytes holding a RInstruction instance would be a
526 : // strict aliasing violation: see bug 1269319 for an instance of bytewise
527 : // copying having caused crashes.
528 : RInstructionStorage(const RInstructionStorage&) = delete;
529 : RInstructionStorage& operator=(const RInstructionStorage& other) = delete;
530 : };
531 :
532 : class RInstruction;
533 :
534 : class RecoverReader
535 : {
536 : CompactBufferReader reader_;
537 :
538 : // Number of encoded instructions.
539 : uint32_t numInstructions_;
540 :
541 : // Number of instruction read.
542 : uint32_t numInstructionsRead_;
543 :
544 : // True if we need to resume after the Resume Point instruction of the
545 : // innermost frame.
546 : bool resumeAfter_;
547 :
548 : // Space is reserved as part of the RecoverReader to avoid allocations of
549 : // data which is needed to decode the current instruction.
550 : RInstructionStorage rawData_;
551 :
552 : private:
553 : void readRecoverHeader();
554 : void readInstruction();
555 :
556 : public:
557 : RecoverReader(SnapshotReader& snapshot, const uint8_t* recovers, uint32_t size);
558 : explicit RecoverReader(const RecoverReader& rr);
559 : RecoverReader& operator=(const RecoverReader& rr);
560 :
561 0 : uint32_t numInstructions() const {
562 0 : return numInstructions_;
563 : }
564 0 : uint32_t numInstructionsRead() const {
565 0 : return numInstructionsRead_;
566 : }
567 :
568 2520 : bool moreInstructions() const {
569 2520 : return numInstructionsRead_ < numInstructions_;
570 : }
571 0 : void nextInstruction() {
572 0 : readInstruction();
573 0 : }
574 :
575 3360 : const RInstruction* instruction() const {
576 3360 : return reinterpret_cast<const RInstruction*>(rawData_.addr());
577 : }
578 :
579 0 : bool resumeAfter() const {
580 0 : return resumeAfter_;
581 : }
582 : };
583 :
584 : } // namespace jit
585 : } // namespace js
586 :
587 : #endif /* jit_Snapshot_h */
|