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