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