Bug 1834537 - Part 1: Simplify JIT nursery allocation r=jandem
[gecko.git] / js / src / jit / Snapshots.h
blobc0c332d926071bd74a8c494cbe8d8f322ecfe4ab
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/. */
7 #ifndef jit_Snapshot_h
8 #define jit_Snapshot_h
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"
19 namespace js {
20 class JS_PUBLIC_API GenericPrinter;
22 namespace jit {
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
29 // an Ion frame.
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 {
36 public:
37 // See RValueAllocation encoding in Snapshots.cpp
38 enum Mode {
39 CONSTANT = 0x00,
40 CST_UNDEFINED = 0x01,
41 CST_NULL = 0x02,
42 DOUBLE_REG = 0x03,
43 ANY_FLOAT_REG = 0x04,
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)
51 UNTYPED_REG = 0x06,
52 UNTYPED_STACK = 0x07,
53 #endif
55 // Recover instructions.
56 RECOVER_INSTRUCTION = 0x0a,
57 RI_WITH_DEFAULT_CST = 0x0b,
59 // The JSValueType is packed in the Mode.
60 TYPED_REG_MIN = 0x10,
61 TYPED_REG_MAX = 0x1f,
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,
80 INVALID = 0x100,
83 enum { PACKED_TAG_MASK = 0x0f };
85 // See Payload encoding in Snapshots.cpp
86 enum PayloadType {
87 PAYLOAD_NONE,
88 PAYLOAD_INDEX,
89 PAYLOAD_STACK_OFFSET,
90 PAYLOAD_GPR,
91 PAYLOAD_FPU,
92 PAYLOAD_PACKED_TAG
95 struct Layout {
96 PayloadType type1;
97 PayloadType type2;
98 const char* name;
101 private:
102 Mode mode_;
104 // Additional information to recover the content of the allocation.
105 struct FloatRegisterBits {
106 uint32_t data;
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);
113 return tmp.name();
117 union Payload {
118 uint32_t index;
119 int32_t stackOffset;
120 Register gpr;
121 FloatRegisterBits fpu;
122 JSValueType type;
124 Payload() : index(0) {
125 static_assert(sizeof(index) == sizeof(Payload),
126 "All Payload bits are initialized.");
130 Payload arg1_;
131 Payload arg2_;
133 static Payload payloadOfIndex(uint32_t index) {
134 Payload p;
135 p.index = index;
136 return p;
138 static Payload payloadOfStackOffset(int32_t offset) {
139 Payload p;
140 p.stackOffset = offset;
141 return p;
143 static Payload payloadOfRegister(Register reg) {
144 Payload p;
145 p.gpr = reg;
146 return p;
148 static Payload payloadOfFloatRegister(FloatRegister reg) {
149 Payload p;
150 FloatRegisterBits b;
151 b.data = reg.code();
152 p.fpu = b;
153 return p;
155 static Payload payloadOfValueType(JSValueType type) {
156 Payload p;
157 p.type = type;
158 return p;
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,
166 Payload p);
167 static void writePadding(CompactBufferWriter& writer);
168 #ifdef JS_JITSPEW
169 static void dumpPayload(GenericPrinter& out, PayloadType t, Payload p);
170 #endif
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) {
177 arg2_.index = 0;
180 explicit RValueAllocation(Mode mode) : mode_(mode) {
181 arg1_.index = 0;
182 arg2_.index = 0;
185 public:
186 RValueAllocation() : mode_(INVALID) {
187 arg1_.index = 0;
188 arg2_.index = 0;
191 // DOUBLE_REG
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));
218 // UNTYPED
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));
251 #endif
253 // common constants.
254 static RValueAllocation Undefined() {
255 return RValueAllocation(CST_UNDEFINED);
257 static RValueAllocation Null() { return RValueAllocation(CST_NULL); }
259 // CONSTANT's index
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,
269 uint32_t cstIndex) {
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;
282 public:
283 static RValueAllocation read(CompactBufferReader& reader);
284 void write(CompactBufferWriter& writer) const;
286 public:
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);
293 return arg1_.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);
301 return arg1_.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);
310 return arg1_.type;
313 uint32_t index2() const {
314 MOZ_ASSERT(layoutFromMode(mode()).type2 == PAYLOAD_INDEX);
315 return arg2_.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);
323 return arg2_.gpr;
326 public:
327 #ifdef JS_JITSPEW
328 void dump(GenericPrinter& out) const;
329 #endif
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;
343 struct Hasher {
344 using Key = RValueAllocation;
345 using Lookup = Key;
346 static HashNumber hash(const Lookup& v) { return v.hash(); }
347 static bool match(const Key& k, const Lookup& l) { return k == l; }
351 class RecoverWriter;
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_;
371 public:
372 SnapshotWriter();
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);
378 #endif
379 [[nodiscard]] bool add(const RValueAllocation& slot);
381 uint32_t allocWritten() const { return allocWritten_; }
382 void endSnapshot();
384 bool oom() const {
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(); }
396 class MNode;
398 class RecoverWriter {
399 CompactBufferWriter writer_;
401 uint32_t instructionCount_;
402 uint32_t instructionsWritten_;
404 public:
405 SnapshotOffset startRecover(uint32_t instructionCount);
407 void writeInstruction(const MNode* rp);
409 void endRecover();
411 size_t size() const { return writer_.length(); }
412 const uint8_t* buffer() const { return writer_.buffer(); }
414 bool oom() const {
415 return writer_.oom() || writer_.length() >= MAX_BUFFER_SIZE;
419 class RecoverReader;
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
435 private:
436 uint32_t pcOpcode_;
437 uint32_t mirOpcode_;
438 uint32_t mirId_;
439 uint32_t lirOpcode_;
440 uint32_t lirId_;
442 public:
443 void readTrackSnapshot();
444 void spewBailingFrom() const;
445 #endif
447 private:
448 void readSnapshotHeader();
449 uint32_t readAllocationIndex();
451 public:
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];
475 public:
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;
488 class RInstruction;
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_;
503 private:
504 void readRecoverHeader();
505 void readInstruction();
507 public:
508 RecoverReader(SnapshotReader& snapshot, const uint8_t* recovers,
509 uint32_t size);
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());
526 } // namespace jit
527 } // namespace js
529 #endif /* jit_Snapshot_h */