Bug 1533636 - Don't bother explicitly aligning when XDRing char16_t data: the operations we use to perform the little/native-endian conversion will correctly translate into, or translate out of, unaligned memory. r=tcampbell
authorJeff Walden <jwalden@mit.edu>
Wed, 06 Mar 2019 13:36:47 -0800
changeset 521368 b7c332bee855b657db8f0bcbba9dc98465891a1d
parent 521367 8ffeb0cdf0e58989498414a1f9b7c22495f7f998
child 521369 abcc9ef3e73da68b0fdbe9674364df36b3c25fd4
push id10866
push usernerli@mozilla.com
push dateTue, 12 Mar 2019 18:59:09 +0000
treeherdermozilla-beta@445c24a51727 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerstcampbell
bugs1533636
milestone67.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 1533636 - Don't bother explicitly aligning when XDRing char16_t data: the operations we use to perform the little/native-endian conversion will correctly translate into, or translate out of, unaligned memory. r=tcampbell Differential Revision: https://phabricator.services.mozilla.com/D22654
js/src/vm/AtomsTable.h
js/src/vm/JSAtom.cpp
js/src/vm/StringType-inl.h
js/src/vm/StringType.cpp
js/src/vm/StringType.h
js/src/vm/Xdr.cpp
js/src/vm/Xdr.h
js/xpconnect/loader/ScriptPreloader.cpp
mfbt/EndianUtils.h
--- a/js/src/vm/AtomsTable.h
+++ b/js/src/vm/AtomsTable.h
@@ -6,16 +6,18 @@
 
 /*
  * Implementation details of the atoms table.
  */
 
 #ifndef vm_AtomsTable_h
 #define vm_AtomsTable_h
 
+#include <type_traits>  // std::{enable_if,is_const}
+
 #include "js/GCHashTable.h"
 #include "js/TypeDecls.h"
 #include "vm/JSAtom.h"
 
 /*
  * The atoms table is a mapping from strings to JSAtoms that supports concurrent
  * access and incremental sweeping.
  *
@@ -157,22 +159,32 @@ class AtomsTable {
     JSAtom* front() const;
     void removeFront();
     void popFront();
   };
 
   ~AtomsTable();
   bool init();
 
-  template <typename CharT>
+  template <typename Chars>
   MOZ_ALWAYS_INLINE JSAtom* atomizeAndCopyChars(
-      JSContext* cx, const CharT* tbchars, size_t length, PinningBehavior pin,
+      JSContext* cx, Chars chars, size_t length, PinningBehavior pin,
       const mozilla::Maybe<uint32_t>& indexValue,
       const AtomHasher::Lookup& lookup);
 
+  template <typename CharT, typename = typename std::enable_if<
+                                !std::is_const<CharT>::value>::type>
+  MOZ_ALWAYS_INLINE JSAtom* atomizeAndCopyChars(
+      JSContext* cx, CharT* chars, size_t length, PinningBehavior pin,
+      const mozilla::Maybe<uint32_t>& indexValue,
+      const AtomHasher::Lookup& lookup) {
+    return atomizeAndCopyChars(cx, const_cast<const CharT*>(chars), length, pin,
+                               indexValue, lookup);
+  }
+
   void pinExistingAtom(JSContext* cx, JSAtom* atom);
 
   void tracePinnedAtoms(JSTracer* trc, const AutoAccessAtomsZone& access);
 
   // Sweep all atoms non-incrementally.
   void sweepAll(JSRuntime* rt);
 
   bool startIncrementalSweep();
--- a/js/src/vm/JSAtom.cpp
+++ b/js/src/vm/JSAtom.cpp
@@ -55,19 +55,20 @@ extern bool GetUTF8AtomizationData(JSCon
                                    size_t* outlen,
                                    JS::SmallestEncoding* encoding,
                                    HashNumber* hashNum);
 
 struct js::AtomHasher::Lookup {
   union {
     const JS::Latin1Char* latin1Chars;
     const char16_t* twoByteChars;
+    LittleEndianChars littleEndianChars;
     const char* utf8Bytes;
   };
-  enum { TwoByteChar, Latin1, UTF8, WTF8 } type;
+  enum { TwoByteChar, LittleEndianTwoByte, Latin1, UTF8, WTF8 } type;
   size_t length;
   size_t byteLength;
   const JSAtom* atom; /* Optional. */
   JS::AutoCheckCannotGC nogc;
 
   HashNumber hash;
 
   MOZ_ALWAYS_INLINE Lookup(const char* utf8Bytes, size_t byteLen, size_t length,
@@ -102,37 +103,55 @@ struct js::AtomHasher::Lookup {
       latin1Chars = atom->latin1Chars(nogc);
       MOZ_ASSERT(mozilla::HashString(latin1Chars, length) == hash);
     } else {
       MOZ_ASSERT(type == TwoByteChar);
       twoByteChars = atom->twoByteChars(nogc);
       MOZ_ASSERT(mozilla::HashString(twoByteChars, length) == hash);
     }
   }
+
+  MOZ_ALWAYS_INLINE Lookup(LittleEndianChars chars, size_t length)
+      : littleEndianChars(chars),
+        type(LittleEndianTwoByte),
+        length(length),
+        atom(nullptr),
+        hash(mozilla::HashStringKnownLength(chars, length)) {}
 };
 
 inline HashNumber js::AtomHasher::hash(const Lookup& l) { return l.hash; }
 
 MOZ_ALWAYS_INLINE bool js::AtomHasher::match(const AtomStateEntry& entry,
                                              const Lookup& lookup) {
   JSAtom* key = entry.asPtrUnbarriered();
   if (lookup.atom) {
     return lookup.atom == key;
   }
   if (key->length() != lookup.length || key->hash() != lookup.hash) {
     return false;
   }
 
+  auto EqualsLittleEndianChars = [&lookup](auto keyChars) {
+    for (size_t i = 0, len = lookup.length; i < len; i++) {
+      if (keyChars[i] != lookup.littleEndianChars[i]) {
+        return false;
+      }
+    }
+    return true;
+  };
+
   if (key->hasLatin1Chars()) {
     const Latin1Char* keyChars = key->latin1Chars(lookup.nogc);
     switch (lookup.type) {
       case Lookup::Latin1:
         return mozilla::ArrayEqual(keyChars, lookup.latin1Chars, lookup.length);
       case Lookup::TwoByteChar:
         return EqualChars(keyChars, lookup.twoByteChars, lookup.length);
+      case Lookup::LittleEndianTwoByte:
+        return EqualsLittleEndianChars(keyChars);
       case Lookup::UTF8: {
         JS::UTF8Chars utf8(lookup.utf8Bytes, lookup.byteLength);
         return UTF8OrWTF8EqualsChars(utf8, keyChars);
       }
       case Lookup::WTF8: {
         JS::WTF8Chars wtf8(lookup.utf8Bytes, lookup.byteLength);
         return UTF8OrWTF8EqualsChars(wtf8, keyChars);
       }
@@ -140,16 +159,18 @@ MOZ_ALWAYS_INLINE bool js::AtomHasher::m
   }
 
   const char16_t* keyChars = key->twoByteChars(lookup.nogc);
   switch (lookup.type) {
     case Lookup::Latin1:
       return EqualChars(lookup.latin1Chars, keyChars, lookup.length);
     case Lookup::TwoByteChar:
       return mozilla::ArrayEqual(keyChars, lookup.twoByteChars, lookup.length);
+    case Lookup::LittleEndianTwoByte:
+      return EqualsLittleEndianChars(keyChars);
     case Lookup::UTF8: {
       JS::UTF8Chars utf8(lookup.utf8Bytes, lookup.byteLength);
       return UTF8OrWTF8EqualsChars(utf8, keyChars);
     }
     case Lookup::WTF8: {
       JS::WTF8Chars wtf8(lookup.utf8Bytes, lookup.byteLength);
       return UTF8OrWTF8EqualsChars(wtf8, keyChars);
     }
@@ -602,52 +623,49 @@ bool JSRuntime::initMainAtomsTables(JSCo
   permanentAtomsDuringInit_ = nullptr;
 
   // Initialize the main atoms table.
   MOZ_ASSERT(!atoms_);
   atoms_ = js_new<AtomsTable>();
   return atoms_ && atoms_->init();
 }
 
-template <typename CharT>
-MOZ_NEVER_INLINE static JSAtom* PermanentlyAtomizeAndCopyChars(
-    JSContext* cx, Maybe<AtomSet::AddPtr>& zonePtr, const CharT* tbchars,
-    size_t length, const Maybe<uint32_t>& indexValue,
-    const AtomHasher::Lookup& lookup);
+template <typename Chars>
+static MOZ_ALWAYS_INLINE JSAtom* AtomizeAndCopyCharsFromLookup(
+    JSContext* cx, Chars chars, size_t length, const AtomHasher::Lookup& lookup,
+    PinningBehavior pin, const Maybe<uint32_t>& indexValue);
 
-template <typename CharT>
-MOZ_ALWAYS_INLINE static JSAtom* AllocateNewAtom(
-    JSContext* cx, const CharT* tbchars, size_t length, PinningBehavior pin,
+template <typename CharT, typename = typename std::enable_if<
+                              !std::is_const<CharT>::value>::type>
+static MOZ_ALWAYS_INLINE JSAtom* AtomizeAndCopyCharsFromLookup(
+    JSContext* cx, CharT* chars, size_t length,
+    const AtomHasher::Lookup& lookup, PinningBehavior pin,
+    const Maybe<uint32_t>& indexValue) {
+  return AtomizeAndCopyCharsFromLookup(cx, const_cast<const CharT*>(chars),
+                                       length, lookup, pin, indexValue);
+}
+
+template <typename Chars>
+static MOZ_NEVER_INLINE JSAtom* PermanentlyAtomizeAndCopyChars(
+    JSContext* cx, Maybe<AtomSet::AddPtr>& zonePtr, Chars chars, size_t length,
     const Maybe<uint32_t>& indexValue, const AtomHasher::Lookup& lookup);
 
-template <typename CharT>
-MOZ_ALWAYS_INLINE static JSAtom* AtomizeAndCopyCharsFromLookup(
-    JSContext* cx, const CharT* tbchars, size_t length,
-    const AtomHasher::Lookup& lookup, PinningBehavior pin,
-    const Maybe<uint32_t>& indexValue);
-
-/* |tbchars| must not point into an inline or short string. */
-template <typename CharT>
-MOZ_ALWAYS_INLINE static JSAtom* AtomizeAndCopyChars(
-    JSContext* cx, const CharT* tbchars, size_t length, PinningBehavior pin,
-    const Maybe<uint32_t>& indexValue) {
-  if (JSAtom* s = cx->staticStrings().lookup(tbchars, length)) {
-    return s;
-  }
-
-  AtomHasher::Lookup lookup(tbchars, length);
-  return AtomizeAndCopyCharsFromLookup(cx, tbchars, length, lookup, pin,
-                                       indexValue);
+template <typename CharT, typename = typename std::enable_if<
+                              !std::is_const<CharT>::value>::type>
+static JSAtom* PermanentlyAtomizeAndCopyChars(
+    JSContext* cx, Maybe<AtomSet::AddPtr>& zonePtr, CharT* chars, size_t length,
+    const Maybe<uint32_t>& indexValue, const AtomHasher::Lookup& lookup) {
+  return PermanentlyAtomizeAndCopyChars(
+      cx, zonePtr, const_cast<const CharT*>(chars), length, indexValue, lookup);
 }
 
-template <typename CharT>
-MOZ_ALWAYS_INLINE static JSAtom* AtomizeAndCopyCharsFromLookup(
-    JSContext* cx, const CharT* tbchars, size_t length,
-    const AtomHasher::Lookup& lookup, PinningBehavior pin,
-    const Maybe<uint32_t>& indexValue) {
+template <typename Chars>
+static MOZ_ALWAYS_INLINE JSAtom* AtomizeAndCopyCharsFromLookup(
+    JSContext* cx, Chars chars, size_t length, const AtomHasher::Lookup& lookup,
+    PinningBehavior pin, const Maybe<uint32_t>& indexValue) {
   // Try the per-Zone cache first. If we find the atom there we can avoid the
   // atoms lock, the markAtom call, and the multiple HashSet lookups below.
   // We don't use the per-Zone cache if we want a pinned atom: handling that
   // is more complicated and pinning atoms is relatively uncommon.
   Zone* zone = cx->zone();
   Maybe<AtomSet::AddPtr> zonePtr;
   if (MOZ_LIKELY(zone && pin == DoNotPinAtom)) {
     zonePtr.emplace(zone->atomCache().lookupForAdd(lookup));
@@ -660,17 +678,17 @@ MOZ_ALWAYS_INLINE static JSAtom* Atomize
       return atom;
     }
   }
 
   // This function can be called during initialization, while the permanent
   // atoms table is being created. In this case all atoms created are added to
   // the permanent atoms table.
   if (!cx->permanentAtomsPopulated()) {
-    return PermanentlyAtomizeAndCopyChars(cx, zonePtr, tbchars, length,
+    return PermanentlyAtomizeAndCopyChars(cx, zonePtr, chars, length,
                                           indexValue, lookup);
   }
 
   AtomSet::Ptr pp = cx->permanentAtoms().readonlyThreadsafeLookup(lookup);
   if (pp) {
     JSAtom* atom = pp->asPtr(cx);
     if (zonePtr && MOZ_UNLIKELY(!zone->atomCache().add(
                        *zonePtr, AtomStateEntry(atom, false)))) {
@@ -682,17 +700,17 @@ MOZ_ALWAYS_INLINE static JSAtom* Atomize
   }
 
   // Validate the length before taking an atoms partition lock, as throwing an
   // exception here may reenter this code.
   if (MOZ_UNLIKELY(!JSString::validateLength(cx, length))) {
     return nullptr;
   }
 
-  JSAtom* atom = cx->atoms().atomizeAndCopyChars(cx, tbchars, length, pin,
+  JSAtom* atom = cx->atoms().atomizeAndCopyChars(cx, chars, length, pin,
                                                  indexValue, lookup);
   if (!atom) {
     return nullptr;
   }
 
   if (MOZ_UNLIKELY(!cx->atomMarking().inlinedMarkAtomFallible(cx, atom))) {
     ReportOutOfMemory(cx);
     return nullptr;
@@ -702,19 +720,33 @@ MOZ_ALWAYS_INLINE static JSAtom* Atomize
                      *zonePtr, AtomStateEntry(atom, false)))) {
     ReportOutOfMemory(cx);
     return nullptr;
   }
 
   return atom;
 }
 
-template <typename CharT>
+template <typename Chars>
+static MOZ_ALWAYS_INLINE JSAtom* AllocateNewAtom(
+    JSContext* cx, Chars chars, size_t length, PinningBehavior pin,
+    const Maybe<uint32_t>& indexValue, const AtomHasher::Lookup& lookup);
+
+template <typename CharT, typename = typename std::enable_if<
+                              !std::is_const<CharT>::value>::type>
+static MOZ_ALWAYS_INLINE JSAtom* AllocateNewAtom(
+    JSContext* cx, CharT* chars, size_t length, PinningBehavior pin,
+    const Maybe<uint32_t>& indexValue, const AtomHasher::Lookup& lookup) {
+  return AllocateNewAtom(cx, const_cast<const CharT*>(chars), length, pin,
+                         indexValue, lookup);
+}
+
+template <typename Chars>
 MOZ_ALWAYS_INLINE JSAtom* AtomsTable::atomizeAndCopyChars(
-    JSContext* cx, const CharT* tbchars, size_t length, PinningBehavior pin,
+    JSContext* cx, Chars chars, size_t length, PinningBehavior pin,
     const Maybe<uint32_t>& indexValue, const AtomHasher::Lookup& lookup) {
   Partition& part = *partitions[getPartitionIndex(lookup)];
   AutoLock lock(cx->runtime(), part.lock);
 
   AtomSet& atoms = part.atoms;
   AtomSet* atomsAddedWhileSweeping = part.atomsAddedWhileSweeping;
   AtomSet::AddPtr p;
 
@@ -741,17 +773,17 @@ MOZ_ALWAYS_INLINE JSAtom* AtomsTable::at
     JSAtom* atom = p->asPtr(cx);
     if (pin && !atom->isPinned()) {
       atom->setPinned();
       p->setPinned(true);
     }
     return atom;
   }
 
-  JSAtom* atom = AllocateNewAtom(cx, tbchars, length, pin, indexValue, lookup);
+  JSAtom* atom = AllocateNewAtom(cx, chars, length, pin, indexValue, lookup);
   if (!atom) {
     return nullptr;
   }
 
   // We have held the lock since looking up p, and the operations we've done
   // since then can't GC; therefore the atoms table has not been modified and
   // p is still valid.
   AtomSet* addSet =
@@ -759,41 +791,46 @@ MOZ_ALWAYS_INLINE JSAtom* AtomsTable::at
   if (MOZ_UNLIKELY(!addSet->add(p, AtomStateEntry(atom, bool(pin))))) {
     ReportOutOfMemory(cx); /* SystemAllocPolicy does not report OOM. */
     return nullptr;
   }
 
   return atom;
 }
 
-template JSAtom* AtomizeAndCopyChars(JSContext* cx, const char16_t* tbchars,
-                                     size_t length, PinningBehavior pin,
-                                     const Maybe<uint32_t>& indexValue);
+/* |chars| must not point into an inline or short string. */
+template <typename CharT>
+static MOZ_ALWAYS_INLINE JSAtom* AtomizeAndCopyChars(
+    JSContext* cx, const CharT* chars, size_t length, PinningBehavior pin,
+    const Maybe<uint32_t>& indexValue) {
+  if (JSAtom* s = cx->staticStrings().lookup(chars, length)) {
+    return s;
+  }
 
-template JSAtom* AtomizeAndCopyChars(JSContext* cx, const Latin1Char* tbchars,
-                                     size_t length, PinningBehavior pin,
-                                     const Maybe<uint32_t>& indexValue);
+  AtomHasher::Lookup lookup(chars, length);
+  return AtomizeAndCopyCharsFromLookup(cx, chars, length, lookup, pin,
+                                       indexValue);
+}
 
-template <typename CharT>
-MOZ_NEVER_INLINE static JSAtom* PermanentlyAtomizeAndCopyChars(
-    JSContext* cx, Maybe<AtomSet::AddPtr>& zonePtr, const CharT* tbchars,
-    size_t length, const Maybe<uint32_t>& indexValue,
-    const AtomHasher::Lookup& lookup) {
+template <typename Chars>
+static MOZ_NEVER_INLINE JSAtom* PermanentlyAtomizeAndCopyChars(
+    JSContext* cx, Maybe<AtomSet::AddPtr>& zonePtr, Chars chars, size_t length,
+    const Maybe<uint32_t>& indexValue, const AtomHasher::Lookup& lookup) {
   MOZ_ASSERT(!cx->permanentAtomsPopulated());
   MOZ_ASSERT(CurrentThreadCanAccessRuntime(cx->runtime()));
 
   JSRuntime* rt = cx->runtime();
   AtomSet& atoms = *rt->permanentAtomsDuringInit();
   AtomSet::AddPtr p = atoms.lookupForAdd(lookup);
   if (p) {
     return p->asPtr(cx);
   }
 
   JSAtom* atom =
-      AllocateNewAtom(cx, tbchars, length, DoNotPinAtom, indexValue, lookup);
+      AllocateNewAtom(cx, chars, length, DoNotPinAtom, indexValue, lookup);
   if (!atom) {
     return nullptr;
   }
 
   atom->morphIntoPermanentAtom();
 
   // We are single threaded at this point, and the operations we've done since
   // then can't GC; therefore the atoms table has not been modified and p is
@@ -820,23 +857,30 @@ struct AtomizeUTF8OrWTF8CharsWrapper {
   AtomizeUTF8OrWTF8CharsWrapper(const CharsT& chars,
                                 JS::SmallestEncoding minEncode)
       : utf8(chars), encoding(minEncode) {}
 };
 
 // MakeFlatStringForAtomization has 4 variants.
 // This is used by Latin1Char and char16_t.
 template <typename CharT>
-MOZ_ALWAYS_INLINE static JSFlatString* MakeFlatStringForAtomization(
-    JSContext* cx, const CharT* tbchars, size_t length) {
-  return NewStringCopyN<NoGC>(cx, tbchars, length);
+static MOZ_ALWAYS_INLINE JSFlatString* MakeFlatStringForAtomization(
+    JSContext* cx, const CharT* chars, size_t length) {
+  return NewStringCopyN<NoGC>(cx, chars, length);
+}
+
+// MakeFlatStringForAtomization has one further variant -- a non-template
+// overload accepting LittleEndianChars.
+static MOZ_ALWAYS_INLINE JSFlatString* MakeFlatStringForAtomization(
+    JSContext* cx, LittleEndianChars chars, size_t length) {
+  return NewStringFromLittleEndianNoGC(cx, chars, length);
 }
 
 template <typename CharT, typename WrapperT>
-MOZ_ALWAYS_INLINE static JSFlatString* MakeUTF8AtomHelper(JSContext* cx,
+static MOZ_ALWAYS_INLINE JSFlatString* MakeUTF8AtomHelper(JSContext* cx,
                                                           const WrapperT* chars,
                                                           size_t length) {
   if (JSInlineString::lengthFits<CharT>(length)) {
     CharT* storage;
     JSInlineString* str = AllocateInlineString<NoGC>(cx, length, &storage);
     if (!str) {
       return nullptr;
     }
@@ -865,38 +909,36 @@ MOZ_ALWAYS_INLINE static JSFlatString* M
 
   mozilla::Unused << newStr.release();
   return str;
 }
 
 // Another 2 variants of MakeFlatStringForAtomization.
 // This is used by AtomizeUTF8OrWTF8CharsWrapper with UTF8Chars or WTF8Chars.
 template <typename InputCharsT>
-MOZ_ALWAYS_INLINE
-    /* static */ JSFlatString*
-    MakeFlatStringForAtomization(
-        JSContext* cx, const AtomizeUTF8OrWTF8CharsWrapper<InputCharsT>* chars,
-        size_t length) {
+/* static */ MOZ_ALWAYS_INLINE JSFlatString* MakeFlatStringForAtomization(
+    JSContext* cx, const AtomizeUTF8OrWTF8CharsWrapper<InputCharsT>* chars,
+    size_t length) {
   if (length == 0) {
     return cx->emptyString();
   }
 
   if (chars->encoding == JS::SmallestEncoding::UTF16) {
     return MakeUTF8AtomHelper<char16_t>(cx, chars, length);
   }
   return MakeUTF8AtomHelper<JS::Latin1Char>(cx, chars, length);
 }
 
-template <typename CharT>
-MOZ_ALWAYS_INLINE static JSAtom* AllocateNewAtom(
-    JSContext* cx, const CharT* tbchars, size_t length, PinningBehavior pin,
+template <typename Chars>
+static MOZ_ALWAYS_INLINE JSAtom* AllocateNewAtom(
+    JSContext* cx, Chars chars, size_t length, PinningBehavior pin,
     const Maybe<uint32_t>& indexValue, const AtomHasher::Lookup& lookup) {
   AutoAllocInAtomsZone ac(cx);
 
-  JSFlatString* flat = MakeFlatStringForAtomization(cx, tbchars, length);
+  JSFlatString* flat = MakeFlatStringForAtomization(cx, chars, length);
   if (!flat) {
     // Grudgingly forgo last-ditch GC. The alternative would be to release
     // the lock, manually GC here, and retry from the top. If you fix this,
     // please also fix or comment the similar case in Symbol::new_.
     ReportOutOfMemory(cx);
     return nullptr;
   }
 
@@ -1121,16 +1163,32 @@ JSAtom* js::ToAtom(JSContext* cx,
   }
   return atom;
 }
 
 template JSAtom* js::ToAtom<CanGC>(JSContext* cx, HandleValue v);
 
 template JSAtom* js::ToAtom<NoGC>(JSContext* cx, const Value& v);
 
+static JSAtom* AtomizeLittleEndianTwoByteChars(JSContext* cx,
+                                               const uint8_t* leTwoByte,
+                                               size_t length) {
+  CHECK_THREAD(cx);
+
+  LittleEndianChars chars(leTwoByte);
+
+  if (JSAtom* s = cx->staticStrings().lookup(chars, length)) {
+    return s;
+  }
+
+  AtomHasher::Lookup lookup(chars, length);
+  return AtomizeAndCopyCharsFromLookup(cx, chars, length, lookup, DoNotPinAtom,
+                                       Nothing());
+}
+
 template <XDRMode mode>
 XDRResult js::XDRAtom(XDRState<mode>* xdr, MutableHandleAtom atomp) {
   bool latin1 = false;
   uint32_t length = 0;
   uint32_t lengthAndEncoding = 0;
   if (mode == XDR_ENCODE) {
     static_assert(JSString::MAX_LENGTH <= INT32_MAX,
                   "String length must fit in 31 bits");
@@ -1165,61 +1223,23 @@ XDRResult js::XDRAtom(XDRState<mode>* xd
     if (length) {
       const uint8_t* ptr;
       size_t nbyte = length * sizeof(Latin1Char);
       MOZ_TRY(xdr->peekData(&ptr, nbyte));
       chars = reinterpret_cast<const Latin1Char*>(ptr);
     }
     atom = AtomizeChars(cx, chars, length);
   } else {
-#if MOZ_LITTLE_ENDIAN
-    /* Directly access the little endian chars in the XDR buffer. */
-    const char16_t* chars = nullptr;
+    const uint8_t* twoByteCharsLE = nullptr;
     if (length) {
-      // In the |mode == XDR_ENCODE| case above, when |nchars > 0|,
-      // |XDRState::codeChars(char16_t*, size_t nchars)| will align the
-      // buffer.  This code never calls that function, but it must act
-      // *as if* it had, so we must align manually here.
-      MOZ_TRY(xdr->codeAlign(sizeof(char16_t)));
-
-      const uint8_t* ptr;
       size_t nbyte = length * sizeof(char16_t);
-      MOZ_TRY(xdr->peekData(&ptr, nbyte));
-      MOZ_ASSERT(reinterpret_cast<uintptr_t>(ptr) % sizeof(char16_t) == 0,
-                 "non-aligned buffer during JSAtom decoding");
-      chars = reinterpret_cast<const char16_t*>(ptr);
-    }
-    atom = AtomizeChars(cx, chars, length);
-#else
-    /*
-     * We must copy chars to a temporary buffer to convert between little and
-     * big endian data.
-     */
-    char16_t* chars;
-    char16_t stackChars[256];
-    UniqueTwoByteChars heapChars;
-    if (length <= ArrayLength(stackChars)) {
-      chars = stackChars;
-    } else {
-      /*
-       * This is very uncommon. Don't use the tempLifoAlloc arena for this as
-       * most allocations here will be bigger than tempLifoAlloc's default
-       * chunk size.
-       */
-      heapChars.reset(cx->pod_malloc<char16_t>(length));
-      if (!heapChars) {
-        return xdr->fail(JS::TranscodeResult_Throw);
-      }
-
-      chars = heapChars.get();
+      MOZ_TRY(xdr->peekData(&twoByteCharsLE, nbyte));
     }
 
-    MOZ_TRY(xdr->codeChars(chars, length));
-    atom = AtomizeChars(cx, chars, length);
-#endif /* !MOZ_LITTLE_ENDIAN */
+    atom = AtomizeLittleEndianTwoByteChars(cx, twoByteCharsLE, length);
   }
 
   if (!atom) {
     return xdr->fail(JS::TranscodeResult_Throw);
   }
   atomp.set(atom);
   return Ok();
 }
--- a/js/src/vm/StringType-inl.h
+++ b/js/src/vm/StringType-inl.h
@@ -81,35 +81,44 @@ static MOZ_ALWAYS_INLINE JSInlineString*
   }
 
   JS::AutoCheckCannotGC nogc;
   mozilla::PodCopy(chars, base->chars<CharT>(nogc) + start, length);
   chars[length] = 0;
   return s;
 }
 
-template <typename CharT>
-static MOZ_ALWAYS_INLINE JSFlatString* TryEmptyOrStaticString(
-    JSContext* cx, const CharT* chars, size_t n) {
+template <typename Chars>
+static MOZ_ALWAYS_INLINE JSFlatString* TryEmptyOrStaticString(JSContext* cx,
+                                                              Chars chars,
+                                                              size_t n) {
   // Measurements on popular websites indicate empty strings are pretty common
   // and most strings with length 1 or 2 are in the StaticStrings table. For
   // length 3 strings that's only about 1%, so we check n <= 2.
   if (n <= 2) {
     if (n == 0) {
       return cx->emptyString();
     }
 
     if (JSFlatString* str = cx->staticStrings().lookup(chars, n)) {
       return str;
     }
   }
 
   return nullptr;
 }
 
+template <typename CharT, typename = typename std::enable_if<
+                              !std::is_const<CharT>::value>::type>
+static MOZ_ALWAYS_INLINE JSFlatString* TryEmptyOrStaticString(JSContext* cx,
+                                                              CharT* chars,
+                                                              size_t n) {
+  return TryEmptyOrStaticString(cx, const_cast<const CharT*>(chars), n);
+}
+
 } /* namespace js */
 
 MOZ_ALWAYS_INLINE bool JSString::validateLength(JSContext* maybecx,
                                                 size_t length) {
   if (MOZ_UNLIKELY(length > JSString::MAX_LENGTH)) {
     js::ReportAllocationOverflow(maybecx);
     return false;
   }
--- a/js/src/vm/StringType.cpp
+++ b/js/src/vm/StringType.cpp
@@ -1534,16 +1534,26 @@ static bool CanStoreCharsAsLatin1(const 
 
   return true;
 }
 
 static bool CanStoreCharsAsLatin1(const Latin1Char* s, size_t length) {
   MOZ_CRASH("Shouldn't be called for Latin1 chars");
 }
 
+static bool CanStoreCharsAsLatin1(LittleEndianChars chars, size_t length) {
+  for (size_t i = 0; i < length; i++) {
+    if (chars[i] > JSString::MAX_LATIN1_CHAR) {
+      return false;
+    }
+  }
+
+  return true;
+}
+
 template <AllowGC allowGC>
 static MOZ_ALWAYS_INLINE JSInlineString* NewInlineStringDeflated(
     JSContext* cx, mozilla::Range<const char16_t> chars) {
   size_t len = chars.length();
   Latin1Char* storage;
   JSInlineString* str = AllocateInlineString<allowGC>(cx, len, &storage);
   if (!str) {
     return nullptr;
@@ -1584,16 +1594,48 @@ static JSFlatString* NewStringDeflated(J
 }
 
 template <AllowGC allowGC>
 static JSFlatString* NewStringDeflated(JSContext* cx, const Latin1Char* s,
                                        size_t n) {
   MOZ_CRASH("Shouldn't be called for Latin1 chars");
 }
 
+static JSFlatString* NewStringDeflatedFromLittleEndianNoGC(
+    JSContext* cx, LittleEndianChars chars, size_t length) {
+  MOZ_ASSERT(CanStoreCharsAsLatin1(chars, length));
+
+  if (JSInlineString::lengthFits<Latin1Char>(length)) {
+    Latin1Char* storage;
+    JSInlineString* str = AllocateInlineString<NoGC>(cx, length, &storage);
+    if (!str) {
+      return nullptr;
+    }
+
+    FillFromCompatibleAndTerminate(storage, chars, length);
+    return str;
+  }
+
+  auto news = cx->make_pod_array<Latin1Char>(length + 1);
+  if (!news) {
+    cx->recoverFromOutOfMemory();
+    return nullptr;
+  }
+
+  FillFromCompatibleAndTerminate(news.get(), chars, length);
+
+  JSFlatString* str = JSFlatString::new_<NoGC>(cx, news.get(), length);
+  if (!str) {
+    return nullptr;
+  }
+
+  mozilla::Unused << news.release();
+  return str;
+}
+
 template <typename CharT>
 JSFlatString* js::NewStringDontDeflate(JSContext* cx, CharT* chars,
                                        size_t length) {
   if (JSFlatString* str = TryEmptyOrStaticString(cx, chars, length)) {
     // Free |chars| because we're taking possession of it, but it's no
     // longer needed because we use the static string instead.
     js_free(chars);
     return str;
@@ -1751,16 +1793,46 @@ template JSFlatString* NewStringCopyNDon
 template JSFlatString* NewStringCopyNDontDeflate<CanGC>(JSContext* cx,
                                                         const Latin1Char* s,
                                                         size_t n);
 
 template JSFlatString* NewStringCopyNDontDeflate<NoGC>(JSContext* cx,
                                                        const Latin1Char* s,
                                                        size_t n);
 
+static JSFlatString* NewUndeflatedStringFromLittleEndianNoGC(
+    JSContext* cx, LittleEndianChars chars, size_t length) {
+  if (JSInlineString::lengthFits<char16_t>(length)) {
+    char16_t* storage;
+    JSInlineString* str = AllocateInlineString<NoGC>(cx, length, &storage);
+    if (!str) {
+      return nullptr;
+    }
+
+    FillAndTerminate(storage, chars, length);
+    return str;
+  }
+
+  auto news = cx->make_pod_array<char16_t>(length + 1);
+  if (!news) {
+    cx->recoverFromOutOfMemory();
+    return nullptr;
+  }
+
+  FillAndTerminate(news.get(), chars, length);
+
+  if (JSFlatString* str = JSFlatString::new_<NoGC>(cx, news.get(), length)) {
+    // Successful JSFlatString::new_ took ownership of |news.release()|.
+    mozilla::Unused << news.release();
+    return str;
+  }
+
+  return nullptr;
+}
+
 JSFlatString* NewLatin1StringZ(JSContext* cx, UniqueChars chars) {
   size_t length = strlen(chars.get());
   UniqueLatin1Chars latin1(reinterpret_cast<Latin1Char*>(chars.release()));
   return NewString<CanGC>(cx, std::move(latin1), length);
 }
 
 template <AllowGC allowGC, typename CharT>
 JSFlatString* NewStringCopyN(JSContext* cx, const CharT* s, size_t n) {
@@ -1778,16 +1850,30 @@ template JSFlatString* NewStringCopyN<No
                                             size_t n);
 
 template JSFlatString* NewStringCopyN<CanGC>(JSContext* cx, const Latin1Char* s,
                                              size_t n);
 
 template JSFlatString* NewStringCopyN<NoGC>(JSContext* cx, const Latin1Char* s,
                                             size_t n);
 
+JSFlatString* NewStringFromLittleEndianNoGC(JSContext* cx,
+                                            LittleEndianChars chars,
+                                            size_t length) {
+  if (JSFlatString* str = TryEmptyOrStaticString(cx, chars, length)) {
+    return str;
+  }
+
+  if (CanStoreCharsAsLatin1(chars, length)) {
+    return NewStringDeflatedFromLittleEndianNoGC(cx, chars, length);
+  }
+
+  return NewUndeflatedStringFromLittleEndianNoGC(cx, chars, length);
+}
+
 template <js::AllowGC allowGC>
 JSFlatString* NewStringCopyUTF8N(JSContext* cx, const JS::UTF8Chars utf8) {
   JS::SmallestEncoding encoding = JS::FindSmallestEncoding(utf8);
   if (encoding == JS::SmallestEncoding::ASCII) {
     return NewStringCopyN<allowGC>(cx, utf8.begin().get(), utf8.length());
   }
 
   size_t length;
--- a/js/src/vm/StringType.h
+++ b/js/src/vm/StringType.h
@@ -6,16 +6,18 @@
 
 #ifndef vm_StringType_h
 #define vm_StringType_h
 
 #include "mozilla/MemoryReporting.h"
 #include "mozilla/Range.h"
 #include "mozilla/TextUtils.h"
 
+#include <type_traits>  // std::is_same
+
 #include "jsapi.h"
 #include "jsfriendapi.h"
 
 #include "builtin/String.h"
 #include "gc/Barrier.h"
 #include "gc/Cell.h"
 #include "gc/Heap.h"
 #include "gc/Nursery.h"
@@ -1326,16 +1328,34 @@ MOZ_ALWAYS_INLINE JSAtom* JSFlatString::
   clearFlagBit(NON_ATOM_BIT);
   JSAtom* atom = &asAtom();
   atom->initHash(hash);
   return atom;
 }
 
 namespace js {
 
+/**
+ * An indexable characters class exposing unaligned, little-endian encoded
+ * char16_t data.
+ */
+class LittleEndianChars {
+ public:
+  explicit constexpr LittleEndianChars(const uint8_t* leTwoByte)
+      : current(leTwoByte) {}
+
+  constexpr char16_t operator[](size_t index) const {
+    size_t offset = index * sizeof(char16_t);
+    return (current[offset + 1] << 8) | current[offset];
+  }
+
+ private:
+  const uint8_t* current;
+};
+
 class StaticStrings {
  private:
   /* Bigger chars cannot be in a length-2 string. */
   static const size_t SMALL_CHAR_LIMIT = 128U;
   static const size_t NUM_SMALL_CHARS = 64U;
 
   JSAtom* length2StaticTable[NUM_SMALL_CHARS * NUM_SMALL_CHARS] = {};  // zeroes
 
@@ -1377,18 +1397,24 @@ class StaticStrings {
   inline JSLinearString* getUnitStringForElement(JSContext* cx, JSString* str,
                                                  size_t index);
 
   template <typename CharT>
   static bool isStatic(const CharT* chars, size_t len);
   static bool isStatic(JSAtom* atom);
 
   /* Return null if no static atom exists for the given (chars, length). */
-  template <typename CharT>
-  MOZ_ALWAYS_INLINE JSAtom* lookup(const CharT* chars, size_t length) {
+  template <typename Chars>
+  MOZ_ALWAYS_INLINE JSAtom* lookup(Chars chars, size_t length) {
+    static_assert(std::is_same<Chars, const Latin1Char*>::value ||
+                      std::is_same<Chars, const char16_t*>::value ||
+                      std::is_same<Chars, LittleEndianChars>::value,
+                  "for understandability, |chars| must be one of a few "
+                  "identified types");
+
     switch (length) {
       case 1: {
         char16_t c = chars[0];
         if (c < UNIT_STATIC_LIMIT) {
           return getUnit(c);
         }
         return nullptr;
       }
@@ -1418,16 +1444,29 @@ class StaticStrings {
           }
         }
         return nullptr;
     }
 
     return nullptr;
   }
 
+  template <typename CharT, typename = typename std::enable_if<
+                                std::is_same<CharT, char>::value ||
+                                std::is_same<CharT, const char>::value ||
+                                !std::is_const<CharT>::value>::type>
+  MOZ_ALWAYS_INLINE JSAtom* lookup(CharT* chars, size_t length) {
+    // Collapse calls for |char*| or |const char*| into |const unsigned char*|
+    // to avoid excess instantiations.  Collapse the remaining |CharT*| to
+    // |const CharT*| for the same reason.
+    using UnsignedCharT = typename std::make_unsigned<CharT>::type;
+    UnsignedCharT* unsignedChars = reinterpret_cast<UnsignedCharT*>(chars);
+    return lookup(const_cast<const UnsignedCharT*>(unsignedChars), length);
+  }
+
  private:
   typedef uint8_t SmallChar;
   static const SmallChar INVALID_SMALL_CHAR = -1;
 
   static bool fitsInSmallChar(char16_t c) {
     return c < SMALL_CHAR_LIMIT && toSmallChar[c] != INVALID_SMALL_CHAR;
   }
 
@@ -1555,16 +1594,23 @@ inline JSFlatString* NewStringCopyUTF8Z(
   return NewStringCopyUTF8N<allowGC>(
       cx, JS::UTF8Chars(utf8.c_str(), strlen(utf8.c_str())));
 }
 
 JSString* NewMaybeExternalString(JSContext* cx, const char16_t* s, size_t n,
                                  const JSStringFinalizer* fin,
                                  bool* allocatedExternal);
 
+/**
+ * Allocate a new string consisting of |chars[0..length]| characters.
+ */
+extern JSFlatString* NewStringFromLittleEndianNoGC(JSContext* cx,
+                                                   LittleEndianChars chars,
+                                                   size_t length);
+
 JS_STATIC_ASSERT(sizeof(HashNumber) == 4);
 
 template <AllowGC allowGC>
 extern JSString* ConcatStrings(
     JSContext* cx, typename MaybeRooted<JSString*, allowGC>::HandleType left,
     typename MaybeRooted<JSString*, allowGC>::HandleType right);
 
 /*
--- a/js/src/vm/Xdr.cpp
+++ b/js/src/vm/Xdr.cpp
@@ -81,31 +81,32 @@ XDRResult XDRState<mode>::codeChars(Utf8
 }
 
 template <XDRMode mode>
 XDRResult XDRState<mode>::codeChars(char16_t* chars, size_t nchars) {
   if (nchars == 0) {
     return Ok();
   }
 
-  // Align the buffer to avoid unaligned loads.
-  MOZ_TRY(codeAlign(sizeof(char16_t)));
-
   size_t nbytes = nchars * sizeof(char16_t);
   if (mode == XDR_ENCODE) {
     uint8_t* ptr = buf.write(nbytes);
     if (!ptr) {
       return fail(JS::TranscodeResult_Throw);
     }
+
+    // |mozilla::NativeEndian| correctly handles writing into unaligned |ptr|.
     mozilla::NativeEndian::copyAndSwapToLittleEndian(ptr, chars, nchars);
   } else {
     const uint8_t* ptr = buf.read(nbytes);
     if (!ptr) {
       return fail(JS::TranscodeResult_Failure_BadDecode);
     }
+
+    // |mozilla::NativeEndian| correctly handles reading from unaligned |ptr|.
     mozilla::NativeEndian::copyAndSwapFromLittleEndian(chars, ptr, nchars);
   }
   return Ok();
 }
 
 template <XDRMode mode>
 static XDRResult VersionCheck(XDRState<mode>* xdr) {
   JS::BuildIdCharVector buildId;
--- a/js/src/vm/Xdr.h
+++ b/js/src/vm/Xdr.h
@@ -479,18 +479,16 @@ class XDRState : public XDRCoderBase {
       *sp = reinterpret_cast<const char*>(ptr);
     }
     return Ok();
   }
 
   XDRResult codeChars(JS::Latin1Char* chars, size_t nchars);
   XDRResult codeChars(mozilla::Utf8Unit* units, size_t nchars);
 
-  // If |nchars > 0|, this calls |codeAlign(sizeof(char16_t))| so callers
-  // don't have to.
   XDRResult codeChars(char16_t* chars, size_t nchars);
 
   XDRResult codeFunction(JS::MutableHandleFunction objp,
                          HandleScriptSourceObject sourceObject = nullptr);
   XDRResult codeScript(MutableHandleScript scriptp);
 };
 
 using XDREncoder = XDRState<XDR_ENCODE>;
--- a/js/xpconnect/loader/ScriptPreloader.cpp
+++ b/js/xpconnect/loader/ScriptPreloader.cpp
@@ -55,25 +55,16 @@ static LazyLogModule gLog("ScriptPreload
 
 using mozilla::dom::AutoJSAPI;
 using mozilla::dom::ContentChild;
 using mozilla::dom::ContentParent;
 using namespace mozilla::loader;
 
 ProcessType ScriptPreloader::sProcessType;
 
-// This type correspond to js::vm::XDRAlignment type, which is used as a size
-// reference for alignment of XDR buffers.
-using XDRAlign = uint16_t;
-static const uint8_t sAlignPadding[sizeof(XDRAlign)] = {0, 0};
-
-static inline size_t ComputeByteAlignment(size_t bytes, size_t align) {
-  return (align - (bytes % align)) % align;
-}
-
 nsresult ScriptPreloader::CollectReports(nsIHandleReportCallback* aHandleReport,
                                          nsISupports* aData, bool aAnonymize) {
   MOZ_COLLECT_REPORT(
       "explicit/script-preloader/heap/saved-scripts", KIND_HEAP, UNITS_BYTES,
       SizeOfHashEntries<ScriptStatus::Saved>(mScripts, MallocSizeOf),
       "Memory used to hold the scripts which have been executed in this "
       "session, and will be written to the startup script cache file.");
 
@@ -535,18 +526,16 @@ Result<Ok, nsresult> ScriptPreloader::In
   auto size = mCacheData.size();
 
   uint32_t headerSize;
   if (size < sizeof(MAGIC) + sizeof(headerSize)) {
     return Err(NS_ERROR_UNEXPECTED);
   }
 
   auto data = mCacheData.get<uint8_t>();
-  uint8_t* start = data.get();
-  MOZ_ASSERT(reinterpret_cast<uintptr_t>(start) % sizeof(XDRAlign) == 0);
   auto end = data + size;
 
   if (memcmp(MAGIC, data.get(), sizeof(MAGIC))) {
     return Err(NS_ERROR_UNEXPECTED);
   }
   data += sizeof(MAGIC);
 
   headerSize = LittleEndian::readUint32(data.get());
@@ -561,20 +550,16 @@ Result<Ok, nsresult> ScriptPreloader::In
 
     LinkedList<CachedScript> scripts;
 
     Range<uint8_t> header(data, data + headerSize);
     data += headerSize;
 
     InputBuffer buf(header);
 
-    size_t len = data.get() - start;
-    size_t alignLen = ComputeByteAlignment(len, sizeof(XDRAlign));
-    data += alignLen;
-
     size_t offset = 0;
     while (!buf.finished()) {
       auto script = MakeUnique<CachedScript>(*this, buf);
       MOZ_RELEASE_ASSERT(script);
 
       auto scriptData = data + script->mOffset;
       if (scriptData + script->mSize > end) {
         return Err(NS_ERROR_UNEXPECTED);
@@ -582,19 +567,16 @@ Result<Ok, nsresult> ScriptPreloader::In
 
       // Make sure offsets match what we'd expect based on script ordering and
       // size, as a basic sanity check.
       if (script->mOffset != offset) {
         return Err(NS_ERROR_UNEXPECTED);
       }
       offset += script->mSize;
 
-      MOZ_ASSERT(reinterpret_cast<uintptr_t>(scriptData.get()) %
-                     sizeof(XDRAlign) ==
-                 0);
       script->mXDRRange.emplace(scriptData, scriptData + script->mSize);
 
       // Don't pre-decode the script unless it was used in this process type
       // during the previous session.
       if (script->mOriginalProcessTypes.contains(CurrentProcessType())) {
         scripts.insertBack(script.get());
       } else {
         script->mReadyToExecute = true;
@@ -734,42 +716,30 @@ Result<Ok, nsresult> ScriptPreloader::Wr
     // Sort scripts by load time, with async loaded scripts before sync scripts.
     // Since async scripts are always loaded immediately at startup, it helps to
     // have them stored contiguously.
     scripts.Sort(CachedScript::Comparator());
 
     OutputBuffer buf;
     size_t offset = 0;
     for (auto script : scripts) {
-      MOZ_ASSERT(offset % sizeof(XDRAlign) == 0);
       script->mOffset = offset;
       script->Code(buf);
 
       offset += script->mSize;
     }
 
     uint8_t headerSize[4];
     LittleEndian::writeUint32(headerSize, buf.cursor());
 
-    size_t len = 0;
     MOZ_TRY(Write(fd, MAGIC, sizeof(MAGIC)));
-    len += sizeof(MAGIC);
     MOZ_TRY(Write(fd, headerSize, sizeof(headerSize)));
-    len += sizeof(headerSize);
     MOZ_TRY(Write(fd, buf.Get(), buf.cursor()));
-    len += buf.cursor();
-    size_t alignLen = ComputeByteAlignment(len, sizeof(XDRAlign));
-    if (alignLen) {
-      MOZ_TRY(Write(fd, sAlignPadding, alignLen));
-      len += alignLen;
-    }
     for (auto script : scripts) {
-      MOZ_ASSERT(script->mSize % sizeof(XDRAlign) == 0);
       MOZ_TRY(Write(fd, script->Range().begin().get(), script->mSize));
-      len += script->mSize;
 
       if (script->mScript) {
         script->FreeData();
       }
     }
   }
 
   MOZ_TRY(cacheFile->MoveTo(nullptr, mBaseName + NS_LITERAL_STRING(".bin")));
--- a/mfbt/EndianUtils.h
+++ b/mfbt/EndianUtils.h
@@ -399,17 +399,19 @@ class Endian : private EndianUtils {
    */
   template <typename T>
   MOZ_MUST_USE static T swapToLittleEndian(T aValue) {
     return maybeSwap<ThisEndian, Little>(aValue);
   }
 
   /*
    * Copies |aCount| values of type T starting at |aSrc| to |aDest|, converting
-   * them to little-endian format if ThisEndian is Big.
+   * them to little-endian format if ThisEndian is Big.  |aSrc| as a typed
+   * pointer must be aligned; |aDest| need not be.
+   *
    * As with memcpy, |aDest| and |aSrc| must not overlap.
    */
   template <typename T>
   static void copyAndSwapToLittleEndian(void* aDest, const T* aSrc,
                                         size_t aCount) {
     copyAndSwapTo<ThisEndian, Little>(aDest, aSrc, aCount);
   }
 
@@ -426,17 +428,19 @@ class Endian : private EndianUtils {
    */
   template <typename T>
   MOZ_MUST_USE static T swapToBigEndian(T aValue) {
     return maybeSwap<ThisEndian, Big>(aValue);
   }
 
   /*
    * Copies |aCount| values of type T starting at |aSrc| to |aDest|, converting
-   * them to big-endian format if ThisEndian is Little.
+   * them to big-endian format if ThisEndian is Little.  |aSrc| as a typed
+   * pointer must be aligned; |aDest| need not be.
+   *
    * As with memcpy, |aDest| and |aSrc| must not overlap.
    */
   template <typename T>
   static void copyAndSwapToBigEndian(void* aDest, const T* aSrc,
                                      size_t aCount) {
     copyAndSwapTo<ThisEndian, Big>(aDest, aSrc, aCount);
   }
 
@@ -474,17 +478,19 @@ class Endian : private EndianUtils {
    */
   template <typename T>
   MOZ_MUST_USE static T swapFromLittleEndian(T aValue) {
     return maybeSwap<Little, ThisEndian>(aValue);
   }
 
   /*
    * Copies |aCount| values of type T starting at |aSrc| to |aDest|, converting
-   * them to little-endian format if ThisEndian is Big.
+   * them to little-endian format if ThisEndian is Big.  |aDest| as a typed
+   * pointer must be aligned; |aSrc| need not be.
+   *
    * As with memcpy, |aDest| and |aSrc| must not overlap.
    */
   template <typename T>
   static void copyAndSwapFromLittleEndian(T* aDest, const void* aSrc,
                                           size_t aCount) {
     copyAndSwapFrom<Little, ThisEndian>(aDest, aSrc, aCount);
   }
 
@@ -501,17 +507,19 @@ class Endian : private EndianUtils {
    */
   template <typename T>
   MOZ_MUST_USE static T swapFromBigEndian(T aValue) {
     return maybeSwap<Big, ThisEndian>(aValue);
   }
 
   /*
    * Copies |aCount| values of type T starting at |aSrc| to |aDest|, converting
-   * them to big-endian format if ThisEndian is Little.
+   * them to big-endian format if ThisEndian is Little.  |aDest| as a typed
+   * pointer must be aligned; |aSrc| need not be.
+   *
    * As with memcpy, |aDest| and |aSrc| must not overlap.
    */
   template <typename T>
   static void copyAndSwapFromBigEndian(T* aDest, const void* aSrc,
                                        size_t aCount) {
     copyAndSwapFrom<Big, ThisEndian>(aDest, aSrc, aCount);
   }