diff --git a/js/src/jit/CodeGenerator.cpp b/js/src/jit/CodeGenerator.cpp index c067dcef4091..936b64887ab1 100644 --- a/js/src/jit/CodeGenerator.cpp +++ b/js/src/jit/CodeGenerator.cpp @@ -13501,7 +13501,7 @@ void CodeGenerator::visitRecompileCheck(LRecompileCheck* ins) { // Check if warm-up counter is high enough. AbsoluteAddress warmUpCount = - AbsoluteAddress(jitScript->addressOfWarmUpCount()); + AbsoluteAddress(jitScript).offset(JitScript::offsetOfWarmUpCount()); if (ins->mir()->increaseWarmUpCounter()) { masm.load32(warmUpCount, tmp); masm.add32(Imm32(1), tmp); diff --git a/js/src/jit/JitScript.cpp b/js/src/jit/JitScript.cpp index 28a63195a4d3..6088fda0f97e 100644 --- a/js/src/jit/JitScript.cpp +++ b/js/src/jit/JitScript.cpp @@ -54,19 +54,17 @@ size_t JitScript::NumTypeSets(JSScript* script) { return num; } -JitScript::JitScript(JSScript* script, uint32_t typeSetOffset, - uint32_t bytecodeTypeMapOffset, uint32_t allocBytes, +JitScript::JitScript(JSScript* script, Offset typeSetOffset, + Offset bytecodeTypeMapOffset, Offset endOffset, const char* profileString) : profileString_(profileString), typeSetOffset_(typeSetOffset), bytecodeTypeMapOffset_(bytecodeTypeMapOffset), - allocBytes_(allocBytes) { + endOffset_(endOffset) { setTypesGeneration(script->zone()->types.generation); if (IsTypeInferenceEnabled()) { - uint8_t* base = reinterpret_cast(this); - DefaultInitializeElements(base + typeSetOffset, - numTypeSets()); + initElements(typeSetOffset, numTypeSets()); } // Initialize the warm-up count from the count stored in the script. diff --git a/js/src/jit/JitScript.h b/js/src/jit/JitScript.h index 88df8a7b46bc..50fc14f2bea9 100644 --- a/js/src/jit/JitScript.h +++ b/js/src/jit/JitScript.h @@ -12,6 +12,7 @@ #include "jstypes.h" #include "jit/BaselineIC.h" #include "js/UniquePtr.h" +#include "util/TrailingArray.h" #include "vm/TypeInference.h" class JS_PUBLIC_API JSScript; @@ -108,13 +109,13 @@ static IonScript* const IonCompilingScriptPtr = // Item | Offset // ------------------------+------------------------ // JitScript | 0 -// ICEntry[] | sizeof(JitScript) -// StackTypeSet[] | typeSetOffset_ -// uint32_t[] | bytecodeTypeMapOffset_ +// ICEntry[] | icEntriesOffset() +// StackTypeSet[] | typeSetOffset() +// uint32_t[] | bytecodeTypeMapOffset() // (= bytecode type map) | // // These offsets are also used to compute numICEntries and numTypeSets. -class alignas(uintptr_t) JitScript final { +class alignas(uintptr_t) JitScript final : public TrailingArray { friend class ::JSScript; // Allocated space for fallback IC stubs. @@ -184,18 +185,18 @@ class alignas(uintptr_t) JitScript final { mozilla::Atomic warmUpCount_ = {}; // Offset of the StackTypeSet array. - uint32_t typeSetOffset_ = 0; + Offset typeSetOffset_ = 0; // Offset of the bytecode type map. - uint32_t bytecodeTypeMapOffset_ = 0; + Offset bytecodeTypeMapOffset_ = 0; + + // The size of this allocation. + Offset endOffset_ = 0; // This field is used to avoid binary searches for the sought entry when // bytecode map queries are in linear order. uint32_t bytecodeTypeMapHint_ = 0; - // The size of this allocation. - uint32_t allocBytes_ = 0; - struct Flags { // Flag set when discarding JIT code to indicate this script is on the stack // and type information and JIT code should not be discarded. @@ -215,15 +216,18 @@ class alignas(uintptr_t) JitScript final { }; Flags flags_ = {}; // Zero-initialize flags. - ICEntry* icEntries() { - uint8_t* base = reinterpret_cast(this); - return reinterpret_cast(base + offsetOfICEntries()); - } + // End of fields. + + Offset icEntriesOffset() const { return offsetOfICEntries(); } + Offset typeSetOffset() const { return typeSetOffset_; } + Offset bytecodeTypeMapOffset() const { return bytecodeTypeMapOffset_; } + Offset endOffset() const { return endOffset_; } + + ICEntry* icEntries() { return offsetToPointer(icEntriesOffset()); } StackTypeSet* typeArrayDontCheckGeneration() { MOZ_ASSERT(IsTypeInferenceEnabled()); - uint8_t* base = reinterpret_cast(this); - return reinterpret_cast(base + typeSetOffset_); + return offsetToPointer(typeSetOffset()); } uint32_t typesGeneration() const { return uint32_t(flags_.typesGeneration); } @@ -244,8 +248,8 @@ class alignas(uintptr_t) JitScript final { } public: - JitScript(JSScript* script, uint32_t typeSetOffset, - uint32_t bytecodeTypeMapOffset, uint32_t allocBytes, + JitScript(JSScript* script, Offset typeSetOffset, + Offset bytecodeTypeMapOffset, Offset endOffset, const char* profileString); #ifdef DEBUG @@ -300,11 +304,11 @@ class alignas(uintptr_t) JitScript final { } uint32_t numICEntries() const { - return (typeSetOffset_ - offsetOfICEntries()) / sizeof(ICEntry); + return (typeSetOffset() - icEntriesOffset()) / sizeof(ICEntry); } uint32_t numTypeSets() const { MOZ_ASSERT(IsTypeInferenceEnabled()); - return (bytecodeTypeMapOffset_ - typeSetOffset_) / sizeof(StackTypeSet); + return (bytecodeTypeMapOffset() - typeSetOffset()) / sizeof(StackTypeSet); } uint32_t* bytecodeTypeMapHint() { return &bytecodeTypeMapHint_; } @@ -328,8 +332,7 @@ class alignas(uintptr_t) JitScript final { uint32_t* bytecodeTypeMap() { MOZ_ASSERT(IsTypeInferenceEnabled()); - uint8_t* base = reinterpret_cast(this); - return reinterpret_cast(base + bytecodeTypeMapOffset_); + return offsetToPointer(bytecodeTypeMapOffset()); } inline StackTypeSet* thisTypes(const AutoSweepJitScript& sweep, @@ -399,7 +402,7 @@ class alignas(uintptr_t) JitScript final { static void Destroy(Zone* zone, JitScript* script); - static constexpr size_t offsetOfICEntries() { return sizeof(JitScript); } + static constexpr Offset offsetOfICEntries() { return sizeof(JitScript); } static constexpr size_t offsetOfJitCodeSkipArgCheck() { return offsetof(JitScript, jitCodeSkipArgCheck_); @@ -415,9 +418,6 @@ class alignas(uintptr_t) JitScript final { } uint32_t warmUpCount() const { return warmUpCount_; } - uint32_t* addressOfWarmUpCount() { - return reinterpret_cast(&warmUpCount_); - } #ifdef DEBUG void printTypes(JSContext* cx, HandleScript script); @@ -470,7 +470,7 @@ class alignas(uintptr_t) JitScript final { void removeDependentWasmImport(wasm::Instance& instance, uint32_t idx); void unlinkDependentWasmImports(); - size_t allocBytes() const { return allocBytes_; } + size_t allocBytes() const { return endOffset(); } EnvironmentObject* templateEnvironment() const { return cachedIonData().templateEnv; diff --git a/js/src/util/Memory.h b/js/src/util/Memory.h index 2009055e6d3c..e229adbf16b4 100644 --- a/js/src/util/Memory.h +++ b/js/src/util/Memory.h @@ -41,21 +41,6 @@ static constexpr T AlignBytes(T bytes, U alignment) { return bytes + ComputeByteAlignment(bytes, alignment); } -/*****************************************************************************/ - -// Placement-new elements of an array. This should optimize away for types with -// trivial default initiation. -template -static void DefaultInitializeElements(void* arrayPtr, size_t length) { - uintptr_t elem = reinterpret_cast(arrayPtr); - MOZ_ASSERT(elem % alignof(T) == 0); - - for (size_t i = 0; i < length; ++i) { - new (reinterpret_cast(elem)) T; - elem += sizeof(T); - } -} - } /* namespace js */ #endif /* util_Memory_h */ diff --git a/js/src/util/TrailingArray.h b/js/src/util/TrailingArray.h new file mode 100644 index 000000000000..98c493aac5af --- /dev/null +++ b/js/src/util/TrailingArray.h @@ -0,0 +1,69 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * vim: set ts=8 sts=2 et sw=2 tw=80: + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#ifndef util_TrailingArray_h +#define util_TrailingArray_h + +#include "mozilla/Assertions.h" // MOZ_ASSERT + +#include // size_t +#include // uint32_t, uintptr_t + +namespace js { + +// Placement-new the elements of an array. This should optimize away for types +// with trivial default initialization. +template +static void DefaultInitializeElements(void* arrayPtr, size_t nelem) { + uintptr_t elem = reinterpret_cast(arrayPtr); + MOZ_ASSERT(elem % alignof(T) == 0); + + for (size_t i = 0; i < nelem; ++i) { + new (reinterpret_cast(elem)) T; + elem += sizeof(T); + } +} + +// This is a mixin class to use for types that have trailing arrays and use +// offsets to delimit them. It provides helper methods to do casting and +// initialization while avoiding C++ undefined behaviour. +class TrailingArray { + protected: + // Offsets are measured in bytes relative to 'this'. + using Offset = uint32_t; + + // Translate an offset into a concrete pointer. + template + T* offsetToPointer(Offset offset) { + uintptr_t base = reinterpret_cast(this); + return reinterpret_cast(base + offset); + } + template + const T* offsetToPointer(Offset offset) const { + uintptr_t base = reinterpret_cast(this); + return reinterpret_cast(base + offset); + } + + // Placement-new the elements of an array. This should optimize away for types + // with trivial default initialization. + template + void initElements(Offset offset, size_t nelem) { + void* raw = offsetToPointer(offset); + DefaultInitializeElements(raw, nelem); + } + + // Constructor is protected so a derived type is required. + TrailingArray() = default; + + public: + // Type has trailing data so isn't copyable or movable. + TrailingArray(const TrailingArray&) = delete; + TrailingArray& operator=(const TrailingArray&) = delete; +}; + +} // namespace js + +#endif // util_TrailingArray_h diff --git a/js/src/vm/JSScript.cpp b/js/src/vm/JSScript.cpp index e6d6d26bbc18..fde158afba54 100644 --- a/js/src/vm/JSScript.cpp +++ b/js/src/vm/JSScript.cpp @@ -751,14 +751,6 @@ XDRResult js::PrivateScriptData::XDR(XDRState* xdr, HandleScript script, return size; } -// Placement-new elements of an array. This should optimize away for types with -// trivial default initiation. -template -void ImmutableScriptData::initElements(size_t offset, size_t length) { - uintptr_t base = reinterpret_cast(this); - DefaultInitializeElements(reinterpret_cast(base + offset), length); -} - // Initialize the optional arrays in the trailing allocation. This is a set of // offsets that delimit each optional array followed by the arrays themselves. // See comment before 'ImmutableScriptData' for more details. @@ -955,14 +947,6 @@ static XDRResult XDRImmutableScriptData(XDRState* xdr, return size; } -// Placement-new elements of an array. This should optimize away for types with -// trivial default initiation. -template -void RuntimeScriptData::initElements(size_t offset, size_t length) { - uintptr_t base = reinterpret_cast(this); - DefaultInitializeElements(reinterpret_cast(base + offset), length); -} - RuntimeScriptData::RuntimeScriptData(uint32_t natoms) : natoms_(natoms) { // Variable-length data begins immediately after RuntimeScriptData itself. size_t cursor = sizeof(*this); @@ -4130,14 +4114,6 @@ inline size_t PrivateScriptData::allocationSize() const { return AllocationSize(ngcthings); } -// Placement-new elements of an array. This should optimize away for types with -// trivial default initiation. -template -void PrivateScriptData::initElements(size_t offset, size_t length) { - void* raw = offsetToPointer(offset); - DefaultInitializeElements(raw, length); -} - // Initialize and placement-new the trailing arrays. PrivateScriptData::PrivateScriptData(uint32_t ngcthings) : ngcthings(ngcthings) { diff --git a/js/src/vm/JSScript.h b/js/src/vm/JSScript.h index c104e5ed6827..56f5be79e628 100644 --- a/js/src/vm/JSScript.h +++ b/js/src/vm/JSScript.h @@ -37,6 +37,7 @@ #include "js/UniquePtr.h" #include "js/Utility.h" #include "util/StructuredSpewer.h" +#include "util/TrailingArray.h" #include "vm/BigIntType.h" #include "vm/BytecodeIterator.h" #include "vm/BytecodeLocation.h" @@ -1409,29 +1410,17 @@ struct FieldInitializers { // [SMDOC] - JSScript data layout (unshared) // // PrivateScriptData stores variable-length data associated with a script. -// Abstractly a PrivateScriptData consists of all these arrays: +// Abstractly a PrivateScriptData consists of the following: // // * A non-empty array of GCCellPtr in gcthings() // // Accessing this array just requires calling the appropriate public // Span-computing function. -class alignas(uintptr_t) PrivateScriptData final { +class alignas(uintptr_t) PrivateScriptData final : public TrailingArray { uint32_t ngcthings = 0; js::FieldInitializers fieldInitializers_ = js::FieldInitializers::Invalid(); - // Translate an offset into a concrete pointer. - template - T* offsetToPointer(size_t offset) { - uintptr_t base = reinterpret_cast(this); - uintptr_t elem = base + offset; - return reinterpret_cast(elem); - } - - // Helpers for creating initializing trailing data - template - void initElements(size_t offset, size_t length); - // Size to allocate static size_t AllocationSize(uint32_t ngcthings); @@ -1445,7 +1434,7 @@ class alignas(uintptr_t) PrivateScriptData final { // Accessors for typed array spans. mozilla::Span gcthings() { - size_t offset = offsetOfGCThings(); + Offset offset = offsetOfGCThings(); return mozilla::MakeSpan(offsetToPointer(offset), ngcthings); } @@ -1481,7 +1470,7 @@ class alignas(uintptr_t) PrivateScriptData final { }; // Script data that is shareable across a JSRuntime. -class RuntimeScriptData final { +class alignas(uintptr_t) RuntimeScriptData final : public TrailingArray { // This class is reference counted as follows: each pointer from a JSScript // counts as one reference plus there may be one reference from the shared // script data table. @@ -1497,15 +1486,9 @@ class RuntimeScriptData final { friend class ::JSScript; private: - // Layout of trailing arrays. - size_t atomOffset() const { return offsetOfAtoms(); } - // Size to allocate. static size_t AllocationSize(uint32_t natoms); - template - void initElements(size_t offset, size_t length); - // Initialize to GC-safe state. explicit RuntimeScriptData(uint32_t natoms); @@ -1528,15 +1511,13 @@ class RuntimeScriptData final { uint32_t natoms() const { return natoms_; } GCPtrAtom* atoms() { - uintptr_t base = reinterpret_cast(this); - return reinterpret_cast(base + atomOffset()); + Offset offset = offsetOfAtoms(); + return offsetToPointer(offset); } mozilla::Span atomsSpan() const { - uintptr_t base = reinterpret_cast(this); - const GCPtrAtom* p = - reinterpret_cast(base + atomOffset()); - return mozilla::MakeSpan(p, natoms_); + Offset offset = offsetOfAtoms(); + return mozilla::MakeSpan(offsetToPointer(offset), natoms_); } static constexpr size_t offsetOfAtoms() { return sizeof(RuntimeScriptData); } diff --git a/js/src/vm/SharedStencil.h b/js/src/vm/SharedStencil.h index 785778e34e94..248922763a98 100644 --- a/js/src/vm/SharedStencil.h +++ b/js/src/vm/SharedStencil.h @@ -14,8 +14,9 @@ #include "frontend/SourceNotes.h" // js::SrcNote #include "js/CompileOptions.h" // JS::{ReadOnlyCompileOptions,TransitiveCompileOptions} -#include "js/TypeDecls.h" // JSContext,jsbytecode -#include "js/UniquePtr.h" // js::UniquePtr +#include "js/TypeDecls.h" // JSContext,jsbytecode +#include "js/UniquePtr.h" // js::UniquePtr +#include "util/TrailingArray.h" // js::TrailingArray #include "vm/StencilEnums.h" // js::{TryNoteKind,ImmutableScriptFlagsEnum,MutableScriptFlagsEnum} // @@ -221,11 +222,8 @@ class MutableScriptFlags : public ScriptFlagBase { // In general, the length of each array is computed from subtracting the start // offset of the array from the start offset of the subsequent array. The // notable exception is that bytecode length is stored explicitly. -class alignas(uint32_t) ImmutableScriptData final { +class alignas(uint32_t) ImmutableScriptData final : public TrailingArray { private: - // Offsets are measured in bytes relative to 'this'. - using Offset = uint32_t; - Offset optArrayOffset_ = 0; // Length of bytecode @@ -269,10 +267,10 @@ class alignas(uint32_t) ImmutableScriptData final { // Offsets (in bytes) from 'this' to each component array. The delta between // each offset and the next offset is the size of each array and is defined // even if an array is empty. - size_t flagOffset() const { return offsetOfCode() - sizeof(Flags); } - size_t codeOffset() const { return offsetOfCode(); } - size_t noteOffset() const { return offsetOfCode() + codeLength_; } - size_t optionalOffsetsOffset() const { + Offset flagOffset() const { return offsetOfCode() - sizeof(Flags); } + Offset codeOffset() const { return offsetOfCode(); } + Offset noteOffset() const { return offsetOfCode() + codeLength_; } + Offset optionalOffsetsOffset() const { // Determine the location to beginning of optional-offsets array by looking // at index for try-notes. // @@ -288,14 +286,14 @@ class alignas(uint32_t) ImmutableScriptData final { return optArrayOffset_ - (numOffsets * sizeof(Offset)); } - size_t resumeOffsetsOffset() const { return optArrayOffset_; } - size_t scopeNotesOffset() const { + Offset resumeOffsetsOffset() const { return optArrayOffset_; } + Offset scopeNotesOffset() const { return getOptionalOffset(flags().resumeOffsetsEndIndex); } - size_t tryNotesOffset() const { + Offset tryNotesOffset() const { return getOptionalOffset(flags().scopeNotesEndIndex); } - size_t endOffset() const { + Offset endOffset() const { return getOptionalOffset(flags().tryNotesEndIndex); } @@ -304,16 +302,6 @@ class alignas(uint32_t) ImmutableScriptData final { uint32_t numResumeOffsets, uint32_t numScopeNotes, uint32_t numTryNotes); - // Translate an offset into a concrete pointer. - template - T* offsetToPointer(size_t offset) { - uintptr_t base = reinterpret_cast(this); - return reinterpret_cast(base + offset); - } - - template - void initElements(size_t offset, size_t length); - void initOptionalArrays(size_t* cursor, Flags* flags, uint32_t numResumeOffsets, uint32_t numScopeNotes, uint32_t numTryNotes);