Bug 1633425 - Reorder IonScript arrays to avoid padding. r=jandem
authorTed Campbell <tcampbell@mozilla.com>
Tue, 28 Apr 2020 07:59:04 +0000
changeset 526469 6158f69d21c7e5cdba5d2d6847aba2d4074fa7c1
parent 526468 b73154f817cdcc7a9f88228abdbfccb4aff96139
child 526470 5d3704bf89d5abe274c7587afbacf59d92439723
push id37357
push useropoprus@mozilla.com
push dateTue, 28 Apr 2020 21:47:47 +0000
treeherdermozilla-central@a34695d9b99d [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjandem
bugs1633425
milestone77.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1633425 - Reorder IonScript arrays to avoid padding. r=jandem Order the trailing arrays by descending alignment requirements and use a similar approach to JitScript for computing sizes and initializing. Differential Revision: https://phabricator.services.mozilla.com/D72674
js/src/jit/Ion.cpp
js/src/jit/IonCode.h
--- a/js/src/jit/Ion.cpp
+++ b/js/src/jit/Ion.cpp
@@ -1,16 +1,17 @@
 /* -*- 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/. */
 
 #include "jit/Ion.h"
 
+#include "mozilla/CheckedInt.h"
 #include "mozilla/DebugOnly.h"
 #include "mozilla/IntegerPrintfMacros.h"
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/ThreadLocal.h"
 #include "mozilla/Unused.h"
 
 #include "gc/FreeOp.h"
 #include "gc/Marking.h"
@@ -605,96 +606,95 @@ IonScript* IonScript::New(JSContext* cx,
                           uint32_t frameSlots, uint32_t argumentSlots,
                           uint32_t frameSize, size_t snapshotsListSize,
                           size_t snapshotsRVATableSize, size_t recoversSize,
                           size_t bailoutEntries, size_t constants,
                           size_t safepointIndices, size_t osiIndices,
                           size_t icEntries, size_t runtimeSize,
                           size_t safepointsSize,
                           OptimizationLevel optimizationLevel) {
-  constexpr size_t DataAlignment = sizeof(void*);
-
   if (snapshotsListSize >= MAX_BUFFER_SIZE ||
       (bailoutEntries >= MAX_BUFFER_SIZE / sizeof(uint32_t))) {
     ReportOutOfMemory(cx);
     return nullptr;
   }
 
-  // This should not overflow on x86, because the memory is already allocated
-  // *somewhere* and if their total overflowed there would be no memory left
-  // at all.
-  size_t paddedSnapshotsSize =
-      AlignBytes(snapshotsListSize + snapshotsRVATableSize, DataAlignment);
-  size_t paddedRecoversSize = AlignBytes(recoversSize, DataAlignment);
-  size_t paddedBailoutSize =
-      AlignBytes(bailoutEntries * sizeof(uint32_t), DataAlignment);
-  size_t paddedConstantsSize =
-      AlignBytes(constants * sizeof(Value), DataAlignment);
-  size_t paddedSafepointIndicesSize =
-      AlignBytes(safepointIndices * sizeof(SafepointIndex), DataAlignment);
-  size_t paddedOsiIndicesSize =
-      AlignBytes(osiIndices * sizeof(OsiIndex), DataAlignment);
-  size_t paddedICEntriesSize =
-      AlignBytes(icEntries * sizeof(uint32_t), DataAlignment);
-  size_t paddedRuntimeSize = AlignBytes(runtimeSize, DataAlignment);
-  size_t paddedSafepointSize = AlignBytes(safepointsSize, DataAlignment);
-
-  size_t bytes = paddedRuntimeSize + paddedICEntriesSize +
-                 paddedSafepointIndicesSize + paddedSafepointSize +
-                 paddedBailoutSize + paddedOsiIndicesSize +
-                 paddedSnapshotsSize + paddedRecoversSize + paddedConstantsSize;
-
-  IonScript* script = cx->pod_malloc_with_extra<IonScript, uint8_t>(bytes);
-  if (!script) {
+  CheckedInt<Offset> allocSize = sizeof(IonScript);
+  allocSize += CheckedInt<Offset>(constants) * sizeof(Value);
+  allocSize += CheckedInt<Offset>(runtimeSize);
+  allocSize += CheckedInt<Offset>(osiIndices) * sizeof(OsiIndex);
+  allocSize += CheckedInt<Offset>(safepointIndices) * sizeof(SafepointIndex);
+  allocSize += CheckedInt<Offset>(bailoutEntries) * sizeof(SnapshotOffset);
+  allocSize += CheckedInt<Offset>(icEntries) * sizeof(uint32_t);
+  allocSize += CheckedInt<Offset>(safepointsSize);
+  allocSize += CheckedInt<Offset>(snapshotsListSize);
+  allocSize += CheckedInt<Offset>(snapshotsRVATableSize);
+  allocSize += CheckedInt<Offset>(recoversSize);
+
+  if (!allocSize.isValid()) {
+    ReportAllocationOverflow(cx);
+    return nullptr;
+  }
+
+  void* raw = cx->pod_malloc<uint8_t>(allocSize.value());
+  MOZ_ASSERT(uintptr_t(raw) % alignof(IonScript) == 0);
+  if (!raw) {
     return nullptr;
   }
-  new (script) IonScript(compilationId, frameSlots, argumentSlots, frameSize,
-                         optimizationLevel);
-
-  uint32_t offsetCursor = sizeof(IonScript);
-
+  IonScript* script = new (raw) IonScript(
+      compilationId, frameSlots, argumentSlots, frameSize, optimizationLevel);
+
+  Offset offsetCursor = sizeof(IonScript);
+
+  MOZ_ASSERT(offsetCursor % alignof(Value) == 0);
+  script->constantTable_ = offsetCursor;
+  script->constantEntries_ = constants;
+  offsetCursor += constants * sizeof(Value);
+
+  MOZ_ASSERT(offsetCursor % alignof(uint64_t) == 0);
   script->runtimeData_ = offsetCursor;
   script->runtimeSize_ = runtimeSize;
-  offsetCursor += paddedRuntimeSize;
-
+  offsetCursor += runtimeSize;
+
+  MOZ_ASSERT(offsetCursor % alignof(OsiIndex) == 0);
+  script->osiIndexOffset_ = offsetCursor;
+  script->osiIndexEntries_ = osiIndices;
+  offsetCursor += osiIndices * sizeof(OsiIndex);
+
+  MOZ_ASSERT(offsetCursor % alignof(SafepointIndex) == 0);
+  script->safepointIndexOffset_ = offsetCursor;
+  script->safepointIndexEntries_ = safepointIndices;
+  offsetCursor += safepointIndices * sizeof(SafepointIndex);
+
+  MOZ_ASSERT(offsetCursor % alignof(SnapshotOffset) == 0);
+  script->bailoutTable_ = offsetCursor;
+  script->bailoutEntries_ = bailoutEntries;
+  offsetCursor += bailoutEntries * sizeof(SnapshotOffset);
+
+  MOZ_ASSERT(offsetCursor % alignof(uint32_t) == 0);
   script->icIndex_ = offsetCursor;
   script->icEntries_ = icEntries;
-  offsetCursor += paddedICEntriesSize;
-
-  script->safepointIndexOffset_ = offsetCursor;
-  script->safepointIndexEntries_ = safepointIndices;
-  offsetCursor += paddedSafepointIndicesSize;
+  offsetCursor += icEntries * sizeof(uint32_t);
 
   script->safepointsStart_ = offsetCursor;
   script->safepointsSize_ = safepointsSize;
-  offsetCursor += paddedSafepointSize;
-
-  script->bailoutTable_ = offsetCursor;
-  script->bailoutEntries_ = bailoutEntries;
-  offsetCursor += paddedBailoutSize;
-
-  script->osiIndexOffset_ = offsetCursor;
-  script->osiIndexEntries_ = osiIndices;
-  offsetCursor += paddedOsiIndicesSize;
+  offsetCursor += safepointsSize;
 
   script->snapshots_ = offsetCursor;
   script->snapshotsListSize_ = snapshotsListSize;
   script->snapshotsRVATableSize_ = snapshotsRVATableSize;
-  offsetCursor += paddedSnapshotsSize;
+  offsetCursor += snapshotsListSize;
+  offsetCursor += snapshotsRVATableSize;
 
   script->recovers_ = offsetCursor;
   script->recoversSize_ = recoversSize;
-  offsetCursor += paddedRecoversSize;
-
-  script->constantTable_ = offsetCursor;
-  script->constantEntries_ = constants;
-  offsetCursor += paddedConstantsSize;
-
-  script->allocBytes_ = sizeof(IonScript) + bytes;
-  MOZ_ASSERT(offsetCursor == script->allocBytes_);
+  offsetCursor += recoversSize;
+
+  script->allocBytes_ = offsetCursor;
+  MOZ_ASSERT(offsetCursor == allocSize.value());
 
   return script;
 }
 
 void IonScript::trace(JSTracer* trc) {
   if (method_) {
     TraceEdge(trc, &method_, "method");
   }
--- a/js/src/jit/IonCode.h
+++ b/js/src/jit/IonCode.h
@@ -12,16 +12,17 @@
 
 #include "jstypes.h"
 
 #include "jit/ExecutableAllocator.h"
 #include "jit/ICStubSpace.h"
 #include "jit/IonOptimizationLevels.h"
 #include "jit/IonTypes.h"
 #include "js/UbiNode.h"
+#include "util/TrailingArray.h"
 #include "vm/TraceLogging.h"
 
 namespace js {
 namespace jit {
 
 class IonBuilder;
 class JitAllocPolicy;
 class JitCode;
@@ -146,18 +147,79 @@ class JitCode : public gc::TenuredCell {
 class SnapshotWriter;
 class RecoverWriter;
 class SafepointWriter;
 class CodegenSafepointIndex;
 class SafepointIndex;
 class OsiIndex;
 class IonIC;
 
-// An IonScript attaches Ion-generated information to a JSScript.
-struct IonScript {
+// An IonScript attaches Ion-generated information to a JSScript. The header
+// structure is followed by several arrays of data. These trailing arrays have a
+// layout based on offsets (bytes from 'this') stored in the IonScript header.
+//
+//    <IonScript itself>
+//    --
+//    PreBarrieredValue[]   constantTable()
+//    uint8_t[]             runtimeData()
+//    OsiIndex[]            osiIndex()
+//    SafepointIndex[]      safepointIndex()
+//    SnapshotOffset[]      bailoutTable()
+//    uint32_t[]            icIndex()
+//    --
+//    uint8_t[]             safepoints()
+//    uint8_t[]             snapshots()
+//    uint8_t[]             snapshotsRVATable()
+//    uint8_t[]             recovers()
+//
+// Note: These are arranged in order of descending alignment requirements to
+// avoid the need for padding. The `runtimeData` uses uint64_t alignement due to
+// its use of mozilla::AlignedStorage2.
+struct alignas(8) IonScript final : public TrailingArray {
+ private:
+  // Table of constants referenced in snapshots. (JS::Value alignment)
+  uint32_t constantTable_ = 0;
+  uint32_t constantEntries_ = 0;
+
+  // IonIC data structures. (uint64_t alignment)
+  uint32_t runtimeData_ = 0;
+  uint32_t runtimeSize_ = 0;
+
+  // Map OSI-point displacement to snapshot.
+  uint32_t osiIndexOffset_ = 0;
+  uint32_t osiIndexEntries_ = 0;
+
+  // Map code displacement to safepoint / OSI-patch-delta.
+  uint32_t safepointIndexOffset_ = 0;
+  uint32_t safepointIndexEntries_ = 0;
+
+  // Table mapping bailout IDs to snapshot offsets.
+  uint32_t bailoutTable_ = 0;
+  uint32_t bailoutEntries_ = 0;
+
+  // Offset into `runtimeData` for each (variable-length) IonIC.
+  uint32_t icIndex_ = 0;
+  uint32_t icEntries_ = 0;
+
+  // Safepoint table as a CompactBuffer.
+  uint32_t safepointsStart_ = 0;
+  uint32_t safepointsSize_ = 0;
+
+  // Snapshot and RValueAllocation tables as CompactBuffers.
+  uint32_t snapshots_ = 0;
+  uint32_t snapshotsListSize_ = 0;
+  uint32_t snapshotsRVATableSize_ = 0;
+
+  // Recover instruction table as a CompactBuffer.
+  uint32_t recovers_ = 0;
+  uint32_t recoversSize_ = 0;
+
+  // The size of this allocation.
+  uint32_t allocBytes_ = 0;
+
  private:
   // Code pointer containing the actual method.
   HeapPtrJitCode method_ = nullptr;
 
   // Entrypoint for OSR, or nullptr.
   jsbytecode* osrPc_ = nullptr;
 
   // Offset to OSR entrypoint from method_->raw(), or 0.
@@ -180,69 +242,26 @@ struct IonScript {
   uint32_t numBailouts_ = 0;
 
   // Flag set if IonScript was compiled with profiling enabled.
   bool hasProfilingInstrumentation_ = false;
 
   // Flag for if this script is getting recompiled.
   uint32_t recompiling_ = 0;
 
-  // Any kind of data needed by the runtime, these can be either cache
-  // information or profiling info.
-  uint32_t runtimeData_ = 0;
-  uint32_t runtimeSize_ = 0;
-
-  // State for polymorphic caches in the compiled code. All caches are stored
-  // in the runtimeData buffer and indexed by the icIndex which gives a
-  // relative offset in the runtimeData array.
-  uint32_t icIndex_ = 0;
-  uint32_t icEntries_ = 0;
-
-  // Map code displacement to safepoint / OSI-patch-delta.
-  uint32_t safepointIndexOffset_ = 0;
-  uint32_t safepointIndexEntries_ = 0;
-
-  // Offset to and length of the safepoint table in bytes.
-  uint32_t safepointsStart_ = 0;
-  uint32_t safepointsSize_ = 0;
-
   // Number of bytes this function reserves on the stack.
   uint32_t frameSlots_ = 0;
 
   // Number of bytes used passed in as formal arguments or |this|.
   uint32_t argumentSlots_ = 0;
 
   // Frame size is the value that can be added to the StackPointer along
   // with the frame prefix to get a valid JitFrameLayout.
   uint32_t frameSize_ = 0;
 
-  // Table mapping bailout IDs to snapshot offsets.
-  uint32_t bailoutTable_ = 0;
-  uint32_t bailoutEntries_ = 0;
-
-  // Map OSI-point displacement to snapshot.
-  uint32_t osiIndexOffset_ = 0;
-  uint32_t osiIndexEntries_ = 0;
-
-  // Offset from the start of the code buffer to its snapshot buffer.
-  uint32_t snapshots_ = 0;
-  uint32_t snapshotsListSize_ = 0;
-  uint32_t snapshotsRVATableSize_ = 0;
-
-  // List of instructions needed to recover stack frames.
-  uint32_t recovers_ = 0;
-  uint32_t recoversSize_ = 0;
-
-  // Constant table for constants stored in snapshots.
-  uint32_t constantTable_ = 0;
-  uint32_t constantEntries_ = 0;
-
-  // The size of this allocation.
-  uint32_t allocBytes_ = 0;
-
   // Number of references from invalidation records.
   uint32_t invalidationCount_ = 0;
 
   // Identifier of the compilation which produced this code.
   IonCompilationId compilationId_;
 
   // The optimization level this script was compiled in.
   OptimizationLevel optimizationLevel_;