Bug 1633425 - Add js::TrailingArray type for script data structures. r=jandem

This type is a mixin with helper methods for casting and initializing arrays
that follow a struct in the same allocation. Use this for existing JitScript,
PrivateScriptData, RuntimeScriptData, ImmutableScriptData types.

Differential Revision: https://phabricator.services.mozilla.com/D72672
This commit is contained in:
Ted Campbell 2020-04-28 13:53:52 +00:00
Родитель 5903f79933
Коммит 6513f435fd
8 изменённых файлов: 121 добавлений и 124 удалений

Просмотреть файл

@ -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);

Просмотреть файл

@ -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<uint8_t*>(this);
DefaultInitializeElements<StackTypeSet>(base + typeSetOffset,
numTypeSets());
initElements<StackTypeSet>(typeSetOffset, numTypeSets());
}
// Initialize the warm-up count from the count stored in the script.

Просмотреть файл

@ -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<uint32_t, mozilla::Relaxed> 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<uint8_t*>(this);
return reinterpret_cast<ICEntry*>(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<ICEntry>(icEntriesOffset()); }
StackTypeSet* typeArrayDontCheckGeneration() {
MOZ_ASSERT(IsTypeInferenceEnabled());
uint8_t* base = reinterpret_cast<uint8_t*>(this);
return reinterpret_cast<StackTypeSet*>(base + typeSetOffset_);
return offsetToPointer<StackTypeSet>(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<uint8_t*>(this);
return reinterpret_cast<uint32_t*>(base + bytecodeTypeMapOffset_);
return offsetToPointer<uint32_t>(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<uint32_t*>(&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;

Просмотреть файл

@ -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 <typename T>
static void DefaultInitializeElements(void* arrayPtr, size_t length) {
uintptr_t elem = reinterpret_cast<uintptr_t>(arrayPtr);
MOZ_ASSERT(elem % alignof(T) == 0);
for (size_t i = 0; i < length; ++i) {
new (reinterpret_cast<void*>(elem)) T;
elem += sizeof(T);
}
}
} /* namespace js */
#endif /* util_Memory_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 <stddef.h> // size_t
#include <stdint.h> // uint32_t, uintptr_t
namespace js {
// Placement-new the elements of an array. This should optimize away for types
// with trivial default initialization.
template <typename T>
static void DefaultInitializeElements(void* arrayPtr, size_t nelem) {
uintptr_t elem = reinterpret_cast<uintptr_t>(arrayPtr);
MOZ_ASSERT(elem % alignof(T) == 0);
for (size_t i = 0; i < nelem; ++i) {
new (reinterpret_cast<void*>(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 <typename T>
T* offsetToPointer(Offset offset) {
uintptr_t base = reinterpret_cast<uintptr_t>(this);
return reinterpret_cast<T*>(base + offset);
}
template <typename T>
const T* offsetToPointer(Offset offset) const {
uintptr_t base = reinterpret_cast<uintptr_t>(this);
return reinterpret_cast<const T*>(base + offset);
}
// Placement-new the elements of an array. This should optimize away for types
// with trivial default initialization.
template <typename T>
void initElements(Offset offset, size_t nelem) {
void* raw = offsetToPointer<void>(offset);
DefaultInitializeElements<T>(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

Просмотреть файл

@ -751,14 +751,6 @@ XDRResult js::PrivateScriptData::XDR(XDRState<mode>* xdr, HandleScript script,
return size;
}
// Placement-new elements of an array. This should optimize away for types with
// trivial default initiation.
template <typename T>
void ImmutableScriptData::initElements(size_t offset, size_t length) {
uintptr_t base = reinterpret_cast<uintptr_t>(this);
DefaultInitializeElements<T>(reinterpret_cast<void*>(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<mode>* xdr,
return size;
}
// Placement-new elements of an array. This should optimize away for types with
// trivial default initiation.
template <typename T>
void RuntimeScriptData::initElements(size_t offset, size_t length) {
uintptr_t base = reinterpret_cast<uintptr_t>(this);
DefaultInitializeElements<T>(reinterpret_cast<void*>(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 <typename T>
void PrivateScriptData::initElements(size_t offset, size_t length) {
void* raw = offsetToPointer<void>(offset);
DefaultInitializeElements<T>(raw, length);
}
// Initialize and placement-new the trailing arrays.
PrivateScriptData::PrivateScriptData(uint32_t ngcthings)
: ngcthings(ngcthings) {

Просмотреть файл

@ -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 <typename T>
T* offsetToPointer(size_t offset) {
uintptr_t base = reinterpret_cast<uintptr_t>(this);
uintptr_t elem = base + offset;
return reinterpret_cast<T*>(elem);
}
// Helpers for creating initializing trailing data
template <typename T>
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<JS::GCCellPtr> gcthings() {
size_t offset = offsetOfGCThings();
Offset offset = offsetOfGCThings();
return mozilla::MakeSpan(offsetToPointer<JS::GCCellPtr>(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 <typename T>
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<uintptr_t>(this);
return reinterpret_cast<GCPtrAtom*>(base + atomOffset());
Offset offset = offsetOfAtoms();
return offsetToPointer<GCPtrAtom>(offset);
}
mozilla::Span<const GCPtrAtom> atomsSpan() const {
uintptr_t base = reinterpret_cast<uintptr_t>(this);
const GCPtrAtom* p =
reinterpret_cast<const GCPtrAtom*>(base + atomOffset());
return mozilla::MakeSpan(p, natoms_);
Offset offset = offsetOfAtoms();
return mozilla::MakeSpan(offsetToPointer<GCPtrAtom>(offset), natoms_);
}
static constexpr size_t offsetOfAtoms() { return sizeof(RuntimeScriptData); }

Просмотреть файл

@ -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<MutableScriptFlagsEnum> {
// 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 <typename T>
T* offsetToPointer(size_t offset) {
uintptr_t base = reinterpret_cast<uintptr_t>(this);
return reinterpret_cast<T*>(base + offset);
}
template <typename T>
void initElements(size_t offset, size_t length);
void initOptionalArrays(size_t* cursor, Flags* flags,
uint32_t numResumeOffsets, uint32_t numScopeNotes,
uint32_t numTryNotes);