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/. */
10 #include "mozilla/Attributes.h"
12 #include "jit/CompactBuffer.h"
13 #include "jit/IonTypes.h"
14 #include "jit/Registers.h"
15 #include "js/AllocPolicy.h"
16 #include "js/HashTable.h"
17 #include "js/TypeDecls.h"
20 class JS_PUBLIC_API GenericPrinter
;
24 class RValueAllocation
;
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
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.
34 // Unit tests are located in jsapi-tests/testJitRValueAlloc.cpp.
35 class RValueAllocation
{
37 // See RValueAllocation encoding in Snapshots.cpp
44 ANY_FLOAT_STACK
= 0x05,
45 #if defined(JS_NUNBOX32)
46 UNTYPED_REG_REG
= 0x06,
47 UNTYPED_REG_STACK
= 0x07,
48 UNTYPED_STACK_REG
= 0x08,
49 UNTYPED_STACK_STACK
= 0x09,
50 #elif defined(JS_PUNBOX64)
55 // Recover instructions.
56 RECOVER_INSTRUCTION
= 0x0a,
57 RI_WITH_DEFAULT_CST
= 0x0b,
59 // The JSValueType is packed in the Mode.
62 TYPED_REG
= TYPED_REG_MIN
,
64 // The JSValueType is packed in the Mode.
65 TYPED_STACK_MIN
= 0x20,
66 TYPED_STACK_MAX
= 0x2f,
67 TYPED_STACK
= TYPED_STACK_MIN
,
69 // This mask can be used with any other valid mode. When this flag is
70 // set on the mode, this inform the snapshot iterator that even if the
71 // allocation is readable, the content of if might be incomplete unless
72 // all side-effects are executed.
73 RECOVER_SIDE_EFFECT_MASK
= 0x80,
75 // This mask represents the set of bits which can be used to encode a
76 // value in a snapshot. The mode is used to determine how to interpret
77 // the union of values and how to pack the value in memory.
78 MODE_BITS_MASK
= 0x17f,
83 enum { PACKED_TAG_MASK
= 0x0f };
85 // See Payload encoding in Snapshots.cpp
104 // Additional information to recover the content of the allocation.
105 struct FloatRegisterBits
{
107 bool operator==(const FloatRegisterBits
& other
) const {
108 return data
== other
.data
;
110 uint32_t code() const { return data
; }
111 const char* name() const {
112 FloatRegister tmp
= FloatRegister::FromCode(data
);
121 FloatRegisterBits fpu
;
124 Payload() : index(0) {
125 static_assert(sizeof(index
) == sizeof(Payload
),
126 "All Payload bits are initialized.");
133 static Payload
payloadOfIndex(uint32_t index
) {
138 static Payload
payloadOfStackOffset(int32_t offset
) {
140 p
.stackOffset
= offset
;
143 static Payload
payloadOfRegister(Register reg
) {
148 static Payload
payloadOfFloatRegister(FloatRegister reg
) {
155 static Payload
payloadOfValueType(JSValueType type
) {
161 static const Layout
& layoutFromMode(Mode mode
);
163 static void readPayload(CompactBufferReader
& reader
, PayloadType t
,
164 uint8_t* mode
, Payload
* p
);
165 static void writePayload(CompactBufferWriter
& writer
, PayloadType t
,
167 static void writePadding(CompactBufferWriter
& writer
);
169 static void dumpPayload(GenericPrinter
& out
, PayloadType t
, Payload p
);
171 static bool equalPayloads(PayloadType t
, Payload lhs
, Payload rhs
);
173 RValueAllocation(Mode mode
, Payload a1
, Payload a2
)
174 : mode_(mode
), arg1_(a1
), arg2_(a2
) {}
176 RValueAllocation(Mode mode
, Payload a1
) : mode_(mode
), arg1_(a1
) {
180 explicit RValueAllocation(Mode mode
) : mode_(mode
) {
186 RValueAllocation() : mode_(INVALID
) {
192 static RValueAllocation
Double(FloatRegister reg
) {
193 return RValueAllocation(DOUBLE_REG
, payloadOfFloatRegister(reg
));
196 // ANY_FLOAT_REG or ANY_FLOAT_STACK
197 static RValueAllocation
AnyFloat(FloatRegister reg
) {
198 return RValueAllocation(ANY_FLOAT_REG
, payloadOfFloatRegister(reg
));
200 static RValueAllocation
AnyFloat(int32_t offset
) {
201 return RValueAllocation(ANY_FLOAT_STACK
, payloadOfStackOffset(offset
));
204 // TYPED_REG or TYPED_STACK
205 static RValueAllocation
Typed(JSValueType type
, Register reg
) {
206 MOZ_ASSERT(type
!= JSVAL_TYPE_DOUBLE
&& type
!= JSVAL_TYPE_MAGIC
&&
207 type
!= JSVAL_TYPE_NULL
&& type
!= JSVAL_TYPE_UNDEFINED
);
208 return RValueAllocation(TYPED_REG
, payloadOfValueType(type
),
209 payloadOfRegister(reg
));
211 static RValueAllocation
Typed(JSValueType type
, int32_t offset
) {
212 MOZ_ASSERT(type
!= JSVAL_TYPE_MAGIC
&& type
!= JSVAL_TYPE_NULL
&&
213 type
!= JSVAL_TYPE_UNDEFINED
);
214 return RValueAllocation(TYPED_STACK
, payloadOfValueType(type
),
215 payloadOfStackOffset(offset
));
219 #if defined(JS_NUNBOX32)
220 static RValueAllocation
Untyped(Register type
, Register payload
) {
221 return RValueAllocation(UNTYPED_REG_REG
, payloadOfRegister(type
),
222 payloadOfRegister(payload
));
225 static RValueAllocation
Untyped(Register type
, int32_t payloadStackOffset
) {
226 return RValueAllocation(UNTYPED_REG_STACK
, payloadOfRegister(type
),
227 payloadOfStackOffset(payloadStackOffset
));
230 static RValueAllocation
Untyped(int32_t typeStackOffset
, Register payload
) {
231 return RValueAllocation(UNTYPED_STACK_REG
,
232 payloadOfStackOffset(typeStackOffset
),
233 payloadOfRegister(payload
));
236 static RValueAllocation
Untyped(int32_t typeStackOffset
,
237 int32_t payloadStackOffset
) {
238 return RValueAllocation(UNTYPED_STACK_STACK
,
239 payloadOfStackOffset(typeStackOffset
),
240 payloadOfStackOffset(payloadStackOffset
));
243 #elif defined(JS_PUNBOX64)
244 static RValueAllocation
Untyped(Register reg
) {
245 return RValueAllocation(UNTYPED_REG
, payloadOfRegister(reg
));
248 static RValueAllocation
Untyped(int32_t stackOffset
) {
249 return RValueAllocation(UNTYPED_STACK
, payloadOfStackOffset(stackOffset
));
254 static RValueAllocation
Undefined() {
255 return RValueAllocation(CST_UNDEFINED
);
257 static RValueAllocation
Null() { return RValueAllocation(CST_NULL
); }
260 static RValueAllocation
ConstantPool(uint32_t index
) {
261 return RValueAllocation(CONSTANT
, payloadOfIndex(index
));
264 // Recover instruction's index
265 static RValueAllocation
RecoverInstruction(uint32_t index
) {
266 return RValueAllocation(RECOVER_INSTRUCTION
, payloadOfIndex(index
));
268 static RValueAllocation
RecoverInstruction(uint32_t riIndex
,
270 return RValueAllocation(RI_WITH_DEFAULT_CST
, payloadOfIndex(riIndex
),
271 payloadOfIndex(cstIndex
));
274 void setNeedSideEffect() {
275 MOZ_ASSERT(!needSideEffect() && mode_
!= INVALID
);
276 mode_
= Mode(mode_
| RECOVER_SIDE_EFFECT_MASK
);
279 void writeHeader(CompactBufferWriter
& writer
, JSValueType type
,
280 uint32_t regCode
) const;
283 static RValueAllocation
read(CompactBufferReader
& reader
);
284 void write(CompactBufferWriter
& writer
) const;
287 bool valid() const { return mode_
!= INVALID
; }
288 Mode
mode() const { return Mode(mode_
& MODE_BITS_MASK
); }
289 bool needSideEffect() const { return mode_
& RECOVER_SIDE_EFFECT_MASK
; }
291 uint32_t index() const {
292 MOZ_ASSERT(layoutFromMode(mode()).type1
== PAYLOAD_INDEX
);
295 int32_t stackOffset() const {
296 MOZ_ASSERT(layoutFromMode(mode()).type1
== PAYLOAD_STACK_OFFSET
);
297 return arg1_
.stackOffset
;
299 Register
reg() const {
300 MOZ_ASSERT(layoutFromMode(mode()).type1
== PAYLOAD_GPR
);
303 FloatRegister
fpuReg() const {
304 MOZ_ASSERT(layoutFromMode(mode()).type1
== PAYLOAD_FPU
);
305 FloatRegisterBits b
= arg1_
.fpu
;
306 return FloatRegister::FromCode(b
.data
);
308 JSValueType
knownType() const {
309 MOZ_ASSERT(layoutFromMode(mode()).type1
== PAYLOAD_PACKED_TAG
);
313 uint32_t index2() const {
314 MOZ_ASSERT(layoutFromMode(mode()).type2
== PAYLOAD_INDEX
);
317 int32_t stackOffset2() const {
318 MOZ_ASSERT(layoutFromMode(mode()).type2
== PAYLOAD_STACK_OFFSET
);
319 return arg2_
.stackOffset
;
321 Register
reg2() const {
322 MOZ_ASSERT(layoutFromMode(mode()).type2
== PAYLOAD_GPR
);
328 void dump(GenericPrinter
& out
) const;
331 bool operator==(const RValueAllocation
& rhs
) const {
332 // Note, this equality compares the verbatim content of the payload,
333 // which is made possible because we ensure that the payload content is
334 // fully initialized during the creation.
335 static_assert(sizeof(int32_t) == sizeof(Payload
),
336 "All Payload bits are compared.");
337 return mode_
== rhs
.mode_
&& arg1_
.index
== rhs
.arg1_
.index
&&
338 arg2_
.index
== rhs
.arg2_
.index
;
341 HashNumber
hash() const;
344 using Key
= RValueAllocation
;
346 static HashNumber
hash(const Lookup
& v
) { return v
.hash(); }
347 static bool match(const Key
& k
, const Lookup
& l
) { return k
== l
; }
353 // Collects snapshots in a contiguous buffer, which is copied into IonScript
354 // memory after code generation.
355 class SnapshotWriter
{
356 CompactBufferWriter writer_
;
357 CompactBufferWriter allocWriter_
;
359 // Map RValueAllocations to an offset in the allocWriter_ buffer. This is
360 // useful as value allocations are repeated frequently.
361 using RVA
= RValueAllocation
;
362 typedef HashMap
<RVA
, uint32_t, RVA::Hasher
, SystemAllocPolicy
> RValueAllocMap
;
363 RValueAllocMap allocMap_
;
365 // This is only used to assert sanity.
366 uint32_t allocWritten_
;
368 // Used to report size of the snapshot in the spew messages.
369 SnapshotOffset lastStart_
;
374 SnapshotOffset
startSnapshot(RecoverOffset recoverOffset
, BailoutKind kind
);
375 #ifdef TRACK_SNAPSHOTS
376 void trackSnapshot(uint32_t pcOpcode
, uint32_t mirOpcode
, uint32_t mirId
,
377 uint32_t lirOpcode
, uint32_t lirId
);
379 [[nodiscard
]] bool add(const RValueAllocation
& slot
);
381 uint32_t allocWritten() const { return allocWritten_
; }
385 return writer_
.oom() || writer_
.length() >= MAX_BUFFER_SIZE
||
386 allocWriter_
.oom() || allocWriter_
.length() >= MAX_BUFFER_SIZE
;
389 size_t listSize() const { return writer_
.length(); }
390 const uint8_t* listBuffer() const { return writer_
.buffer(); }
392 size_t RVATableSize() const { return allocWriter_
.length(); }
393 const uint8_t* RVATableBuffer() const { return allocWriter_
.buffer(); }
398 class RecoverWriter
{
399 CompactBufferWriter writer_
;
401 uint32_t instructionCount_
;
402 uint32_t instructionsWritten_
;
405 SnapshotOffset
startRecover(uint32_t instructionCount
);
407 void writeInstruction(const MNode
* rp
);
411 size_t size() const { return writer_
.length(); }
412 const uint8_t* buffer() const { return writer_
.buffer(); }
415 return writer_
.oom() || writer_
.length() >= MAX_BUFFER_SIZE
;
421 // A snapshot reader reads the entries out of the compressed snapshot buffer in
422 // a script. These entries describe the equivalent interpreter frames at a given
423 // position in JIT code. Each entry is an Ion's value allocations, used to
424 // recover the corresponding Value from an Ion frame.
425 class SnapshotReader
{
426 CompactBufferReader reader_
;
427 CompactBufferReader allocReader_
;
428 const uint8_t* allocTable_
;
430 BailoutKind bailoutKind_
;
431 uint32_t allocRead_
; // Number of slots that have been read.
432 RecoverOffset recoverOffset_
; // Offset of the recover instructions.
434 #ifdef TRACK_SNAPSHOTS
443 void readTrackSnapshot();
444 void spewBailingFrom() const;
448 void readSnapshotHeader();
449 uint32_t readAllocationIndex();
452 SnapshotReader(const uint8_t* snapshots
, uint32_t offset
,
453 uint32_t RVATableSize
, uint32_t listSize
);
455 RValueAllocation
readAllocation();
456 void skipAllocation() { readAllocationIndex(); }
458 BailoutKind
bailoutKind() const { return bailoutKind_
; }
459 RecoverOffset
recoverOffset() const { return recoverOffset_
; }
461 uint32_t numAllocationsRead() const { return allocRead_
; }
462 void resetNumAllocationsRead() { allocRead_
= 0; }
465 class MOZ_NON_PARAM RInstructionStorage
{
466 static constexpr size_t Size
= 4 * sizeof(uint32_t);
468 // This presumes all RInstructionStorage are safely void*-alignable.
469 // RInstruction::readRecoverData asserts that no RInstruction subclass
470 // has stricter alignment requirements than RInstructionStorage.
471 static constexpr size_t Alignment
= alignof(void*);
473 alignas(Alignment
) unsigned char mem
[Size
];
476 const void* addr() const { return mem
; }
477 void* addr() { return mem
; }
479 RInstructionStorage() = default;
481 // Making a copy of raw bytes holding a RInstruction instance would be a
482 // strict aliasing violation: see bug 1269319 for an instance of bytewise
483 // copying having caused crashes.
484 RInstructionStorage(const RInstructionStorage
&) = delete;
485 RInstructionStorage
& operator=(const RInstructionStorage
& other
) = delete;
490 class RecoverReader
{
491 CompactBufferReader reader_
;
493 // Number of encoded instructions.
494 uint32_t numInstructions_
;
496 // Number of instruction read.
497 uint32_t numInstructionsRead_
;
499 // Space is reserved as part of the RecoverReader to avoid allocations of
500 // data which is needed to decode the current instruction.
501 RInstructionStorage rawData_
;
504 void readRecoverHeader();
505 void readInstruction();
508 RecoverReader(SnapshotReader
& snapshot
, const uint8_t* recovers
,
510 explicit RecoverReader(const RecoverReader
& rr
);
511 RecoverReader
& operator=(const RecoverReader
& rr
);
513 uint32_t numInstructions() const { return numInstructions_
; }
514 uint32_t numInstructionsRead() const { return numInstructionsRead_
; }
516 bool moreInstructions() const {
517 return numInstructionsRead_
< numInstructions_
;
519 void nextInstruction() { readInstruction(); }
521 const RInstruction
* instruction() const {
522 return reinterpret_cast<const RInstruction
*>(rawData_
.addr());
529 #endif /* jit_Snapshot_h */