Backed out changeset 88fbb17e3c20 (bug 1865637) for causing animation related mochite...
[gecko.git] / js / src / wasm / WasmGcObject.h
blobf4c34516aa45cd0b3e43501cc50b2b7d6137704e
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 wasm_WasmGcObject_h
8 #define wasm_WasmGcObject_h
10 #include "mozilla/Attributes.h"
11 #include "mozilla/CheckedInt.h"
12 #include "mozilla/Maybe.h"
14 #include "gc/GCProbes.h"
15 #include "gc/Pretenuring.h"
16 #include "gc/ZoneAllocator.h" // AddCellMemory
17 #include "vm/JSContext.h"
18 #include "vm/JSObject.h"
19 #include "vm/Probes.h"
20 #include "wasm/WasmInstanceData.h"
21 #include "wasm/WasmMemory.h"
22 #include "wasm/WasmTypeDef.h"
23 #include "wasm/WasmValType.h"
25 using js::wasm::StorageType;
27 namespace js::wasm {
29 // For trailer blocks whose owning Wasm{Struct,Array}Objects make it into the
30 // tenured heap, we have to tell the tenured heap how big those trailers are
31 // in order to get major GCs to happen sufficiently frequently. In an attempt
32 // to make the numbers more accurate, for each block we overstate the size by
33 // the following amount, on the assumption that:
35 // * mozjemalloc has an overhead of at least one word per block
37 // * the malloc-cache mechanism rounds up small block sizes to the nearest 16;
38 // hence the average increase is 16 / 2.
39 static const size_t TrailerBlockOverhead = (16 / 2) + (1 * sizeof(void*));
41 } // namespace js::wasm
43 namespace js {
45 //=========================================================================
46 // WasmGcObject
48 class WasmGcObject : public JSObject {
49 protected:
50 const wasm::SuperTypeVector* superTypeVector_;
52 static const ObjectOps objectOps_;
54 [[nodiscard]] static bool obj_lookupProperty(JSContext* cx, HandleObject obj,
55 HandleId id,
56 MutableHandleObject objp,
57 PropertyResult* propp);
59 [[nodiscard]] static bool obj_defineProperty(JSContext* cx, HandleObject obj,
60 HandleId id,
61 Handle<PropertyDescriptor> desc,
62 ObjectOpResult& result);
64 [[nodiscard]] static bool obj_hasProperty(JSContext* cx, HandleObject obj,
65 HandleId id, bool* foundp);
67 [[nodiscard]] static bool obj_getProperty(JSContext* cx, HandleObject obj,
68 HandleValue receiver, HandleId id,
69 MutableHandleValue vp);
71 [[nodiscard]] static bool obj_setProperty(JSContext* cx, HandleObject obj,
72 HandleId id, HandleValue v,
73 HandleValue receiver,
74 ObjectOpResult& result);
76 [[nodiscard]] static bool obj_getOwnPropertyDescriptor(
77 JSContext* cx, HandleObject obj, HandleId id,
78 MutableHandle<mozilla::Maybe<PropertyDescriptor>> desc);
80 [[nodiscard]] static bool obj_deleteProperty(JSContext* cx, HandleObject obj,
81 HandleId id,
82 ObjectOpResult& result);
84 // PropOffset is a uint32_t that is used to carry information about the
85 // location of an value from WasmGcObject::lookupProperty to
86 // WasmGcObject::loadValue. It is distinct from a normal uint32_t to
87 // emphasise the fact that it cannot be interpreted as an offset in any
88 // single contiguous area of memory:
90 // * If the object in question is a WasmStructObject, it is the value of
91 // `wasm::StructField::offset` for the relevant field, without regard to
92 // the inline/outline split.
94 // * If the object in question is a WasmArrayObject, then
95 // - u32 == UINT32_MAX (0xFFFF'FFFF) means the "length" property
96 // is requested
97 // - u32 < UINT32_MAX means the array element starting at that byte
98 // offset in WasmArrayObject::data_. It is not an array index value.
99 // See WasmGcObject::lookupProperty for details.
100 class PropOffset {
101 uint32_t u32_;
103 public:
104 PropOffset() : u32_(0) {}
105 uint32_t get() const { return u32_; }
106 void set(uint32_t u32) { u32_ = u32; }
109 [[nodiscard]] static bool lookUpProperty(JSContext* cx,
110 Handle<WasmGcObject*> obj, jsid id,
111 PropOffset* offset,
112 StorageType* type);
114 public:
115 [[nodiscard]] static bool loadValue(JSContext* cx, Handle<WasmGcObject*> obj,
116 jsid id, MutableHandleValue vp);
118 const wasm::SuperTypeVector& superTypeVector() const {
119 return *superTypeVector_;
122 static constexpr size_t offsetOfSuperTypeVector() {
123 return offsetof(WasmGcObject, superTypeVector_);
126 // These are both expensive in that they involve a double indirection.
127 // Avoid them if possible.
128 const wasm::TypeDef& typeDef() const { return *superTypeVector().typeDef(); }
129 wasm::TypeDefKind kind() const { return superTypeVector().typeDef()->kind(); }
131 [[nodiscard]] bool isRuntimeSubtypeOf(
132 const wasm::TypeDef* parentTypeDef) const;
134 [[nodiscard]] static bool obj_newEnumerate(JSContext* cx, HandleObject obj,
135 MutableHandleIdVector properties,
136 bool enumerableOnly);
139 //=========================================================================
140 // WasmArrayObject
142 // Class for a wasm array. It contains a pointer to the array contents, that
143 // lives in the C++ heap.
145 class WasmArrayObject : public WasmGcObject {
146 public:
147 static const JSClass class_;
149 // The number of elements in the array.
150 uint32_t numElements_;
152 // Owned data pointer, holding `numElements_` entries. This is null if
153 // `numElements_` is zero; otherwise it must be non-null. See bug 1812283.
154 uint8_t* data_;
156 // AllocKind for object creation
157 static gc::AllocKind allocKind();
159 // Creates a new non-empty array typed object, optionally initialized to
160 // zero, for the specified number of elements. Reports an error if the
161 // number of elements is too large, or if there is an out of memory error.
162 // The element type, shape, class pointer, alloc site and alloc kind are
163 // taken from `typeDefData`; the initial heap must be specified separately.
164 // The number of elements is assumed and debug-asserted to be non-zero.
165 template <bool ZeroFields>
166 static WasmArrayObject* createArrayNonEmpty(
167 JSContext* cx, wasm::TypeDefInstanceData* typeDefData,
168 js::gc::Heap initialHeap, uint32_t numElements);
170 // Creates a new empty array typed object, for zero elements. Reports an
171 // error if there is an out of memory error. The element type, shape, class
172 // pointer, alloc site and alloc kind are taken from `typeDefData`; the
173 // initial heap must be specified separately. The number of elements is
174 // assumed and debug-asserted to be zero.
175 static WasmArrayObject* createArrayEmpty(
176 JSContext* cx, wasm::TypeDefInstanceData* typeDefData,
177 js::gc::Heap initialHeap);
179 // This just selects one of the above two routines, depending on
180 // `numElements`.
181 template <bool ZeroFields>
182 static MOZ_ALWAYS_INLINE WasmArrayObject* createArray(
183 JSContext* cx, wasm::TypeDefInstanceData* typeDefData,
184 js::gc::Heap initialHeap, uint32_t numElements) {
185 return numElements == 0 ? createArrayEmpty(cx, typeDefData, initialHeap)
186 : createArrayNonEmpty<ZeroFields>(
187 cx, typeDefData, initialHeap, numElements);
190 // JIT accessors
191 static constexpr size_t offsetOfNumElements() {
192 return offsetof(WasmArrayObject, numElements_);
194 static constexpr size_t offsetOfData() {
195 return offsetof(WasmArrayObject, data_);
198 // Tracing and finalization
199 static void obj_trace(JSTracer* trc, JSObject* object);
200 static void obj_finalize(JS::GCContext* gcx, JSObject* object);
201 static size_t obj_moved(JSObject* obj, JSObject* old);
203 void storeVal(const wasm::Val& val, uint32_t itemIndex);
204 void fillVal(const wasm::Val& val, uint32_t itemIndex, uint32_t len);
207 // Helper to mark all locations that assume that the type of
208 // WasmArrayObject::numElements is uint32_t.
209 #define STATIC_ASSERT_WASMARRAYELEMENTS_NUMELEMENTS_IS_U32 \
210 static_assert(sizeof(js::WasmArrayObject::numElements_) == sizeof(uint32_t))
212 //=========================================================================
213 // WasmStructObject
215 // Class for a wasm struct. It has inline data and, if the inline area is
216 // insufficient, a pointer to outline data that lives in the C++ heap.
217 // Computing the field offsets is somewhat tricky; see block comment on `class
218 // StructLayout` for background.
220 class WasmStructObject : public WasmGcObject {
221 public:
222 static const JSClass classInline_;
223 static const JSClass classOutline_;
225 // Owned pointer to a malloc'd block containing out-of-line fields, or
226 // nullptr if none. Note that MIR alias analysis assumes this is readonly
227 // for the life of the object; do not change it once the object is created.
228 // See MWasmLoadObjectField::congruentTo.
229 uint8_t* outlineData_;
231 // The inline (wasm-struct-level) data fields. This must be a multiple of
232 // 16 bytes long in order to ensure that no field gets split across the
233 // inline-outline boundary. As a refinement, we request this field to begin
234 // at an 8-aligned offset relative to the start of the object, so as to
235 // guarantee that `double` typed fields are not subject to misaligned-access
236 // penalties on any target, whilst wasting at maximum 4 bytes of space.
238 // `inlineData_` is in reality a variable length block with maximum size
239 // WasmStructObject_MaxInlineBytes bytes. Do not add any (C++-level) fields
240 // after this point!
241 alignas(8) uint8_t inlineData_[0];
243 // This tells us how big the object is if we know the number of inline bytes
244 // it was created with.
245 static inline size_t sizeOfIncludingInlineData(size_t sizeOfInlineData) {
246 size_t n = sizeof(WasmStructObject) + sizeOfInlineData;
247 MOZ_ASSERT(n <= JSObject::MAX_BYTE_SIZE);
248 return n;
251 static const JSClass* classForTypeDef(const wasm::TypeDef* typeDef);
252 static js::gc::AllocKind allocKindForTypeDef(const wasm::TypeDef* typeDef);
254 // Creates a new struct typed object, optionally initialized to zero.
255 // Reports if there is an out of memory error. The structure's type, shape,
256 // class pointer, alloc site and alloc kind are taken from `typeDefData`;
257 // the initial heap must be specified separately. It is assumed and debug-
258 // asserted that `typeDefData` refers to a type that does not need OOL
259 // storage.
260 template <bool ZeroFields>
261 static MOZ_ALWAYS_INLINE WasmStructObject* createStructIL(
262 JSContext* cx, wasm::TypeDefInstanceData* typeDefData,
263 js::gc::Heap initialHeap);
265 // Same as ::createStructIL, except it is assumed and debug-asserted that
266 // `typeDefData` refers to a type that does need OOL storage.
267 template <bool ZeroFields>
268 static MOZ_ALWAYS_INLINE WasmStructObject* createStructOOL(
269 JSContext* cx, wasm::TypeDefInstanceData* typeDefData,
270 js::gc::Heap initialHeap);
272 // Given the total number of data bytes required (including alignment
273 // holes), return the number of inline and outline bytes required.
274 static inline void getDataByteSizes(uint32_t totalBytes,
275 uint32_t* inlineBytes,
276 uint32_t* outlineBytes);
278 // Convenience function; returns true iff ::getDataByteSizes would set
279 // *outlineBytes to a non-zero value.
280 static inline bool requiresOutlineBytes(uint32_t totalBytes);
282 // Given the offset of a field, produce the offset in `inlineData_` or
283 // `*outlineData_` to use, plus a bool indicating which area it is.
284 // `fieldType` is for assertional purposes only.
285 static inline void fieldOffsetToAreaAndOffset(StorageType fieldType,
286 uint32_t fieldOffset,
287 bool* areaIsOutline,
288 uint32_t* areaOffset);
290 // Given the offset of a field, return its actual address. `fieldType` is
291 // for assertional purposes only.
292 inline uint8_t* fieldOffsetToAddress(StorageType fieldType,
293 uint32_t fieldOffset) const;
295 // JIT accessors
296 static constexpr size_t offsetOfOutlineData() {
297 return offsetof(WasmStructObject, outlineData_);
299 static constexpr size_t offsetOfInlineData() {
300 return offsetof(WasmStructObject, inlineData_);
303 // Tracing and finalization
304 static void obj_trace(JSTracer* trc, JSObject* object);
305 static void obj_finalize(JS::GCContext* gcx, JSObject* object);
306 static size_t obj_moved(JSObject* obj, JSObject* old);
308 void storeVal(const wasm::Val& val, uint32_t fieldIndex);
311 // This is ensured by the use of `alignas` on `WasmStructObject::inlineData_`.
312 static_assert((offsetof(WasmStructObject, inlineData_) % 8) == 0);
314 // MaxInlineBytes must be a multiple of 16 for reasons described in the
315 // comment on `class StructLayout`. This unfortunately can't be defined
316 // inside the class definition itself because the sizeof(..) expression isn't
317 // valid until after the end of the class definition.
318 const size_t WasmStructObject_MaxInlineBytes =
319 ((JSObject::MAX_BYTE_SIZE - sizeof(WasmStructObject)) / 16) * 16;
321 static_assert((WasmStructObject_MaxInlineBytes % 16) == 0);
323 /*static*/
324 inline void WasmStructObject::getDataByteSizes(uint32_t totalBytes,
325 uint32_t* inlineBytes,
326 uint32_t* outlineBytes) {
327 if (MOZ_UNLIKELY(totalBytes > WasmStructObject_MaxInlineBytes)) {
328 *inlineBytes = WasmStructObject_MaxInlineBytes;
329 *outlineBytes = totalBytes - WasmStructObject_MaxInlineBytes;
330 } else {
331 *inlineBytes = totalBytes;
332 *outlineBytes = 0;
336 /* static */
337 inline bool WasmStructObject::requiresOutlineBytes(uint32_t totalBytes) {
338 uint32_t inlineBytes, outlineBytes;
339 WasmStructObject::getDataByteSizes(totalBytes, &inlineBytes, &outlineBytes);
340 return outlineBytes > 0;
343 /*static*/
344 inline void WasmStructObject::fieldOffsetToAreaAndOffset(StorageType fieldType,
345 uint32_t fieldOffset,
346 bool* areaIsOutline,
347 uint32_t* areaOffset) {
348 if (fieldOffset < WasmStructObject_MaxInlineBytes) {
349 *areaIsOutline = false;
350 *areaOffset = fieldOffset;
351 } else {
352 *areaIsOutline = true;
353 *areaOffset = fieldOffset - WasmStructObject_MaxInlineBytes;
355 // Assert that the first and last bytes for the field agree on which side of
356 // the inline/outline boundary they live.
357 MOZ_RELEASE_ASSERT(
358 (fieldOffset < WasmStructObject_MaxInlineBytes) ==
359 ((fieldOffset + fieldType.size() - 1) < WasmStructObject_MaxInlineBytes));
362 inline uint8_t* WasmStructObject::fieldOffsetToAddress(
363 StorageType fieldType, uint32_t fieldOffset) const {
364 bool areaIsOutline;
365 uint32_t areaOffset;
366 fieldOffsetToAreaAndOffset(fieldType, fieldOffset, &areaIsOutline,
367 &areaOffset);
368 return ((uint8_t*)(areaIsOutline ? outlineData_ : &inlineData_[0])) +
369 areaOffset;
372 // Ensure that faulting loads/stores for WasmStructObject and WasmArrayObject
373 // are in the NULL pointer guard page.
374 static_assert(WasmStructObject_MaxInlineBytes <= wasm::NullPtrGuardSize);
375 static_assert(sizeof(WasmArrayObject) <= wasm::NullPtrGuardSize);
377 } // namespace js
379 //=========================================================================
380 // misc
382 namespace js {
384 inline bool IsWasmGcObjectClass(const JSClass* class_) {
385 return class_ == &WasmArrayObject::class_ ||
386 class_ == &WasmStructObject::classInline_ ||
387 class_ == &WasmStructObject::classOutline_;
390 } // namespace js
392 template <>
393 inline bool JSObject::is<js::WasmGcObject>() const {
394 return js::IsWasmGcObjectClass(getClass());
397 template <>
398 inline bool JSObject::is<js::WasmStructObject>() const {
399 const JSClass* class_ = getClass();
400 return class_ == &js::WasmStructObject::classInline_ ||
401 class_ == &js::WasmStructObject::classOutline_;
404 #endif /* wasm_WasmGcObject_h */