diff --git a/js/src/jit/IonAnalysis.cpp b/js/src/jit/IonAnalysis.cpp index 303b2d1568..6f424098e8 100644 --- a/js/src/jit/IonAnalysis.cpp +++ b/js/src/jit/IonAnalysis.cpp @@ -17,6 +17,7 @@ #include "jit/LIR.h" #include "jit/Lowering.h" #include "jit/MIRGraph.h" +#include "jit/RangeAnalysis.h" #include "vm/RegExpObject.h" #include "vm/SelfHosting.h" @@ -2955,9 +2956,19 @@ jit::ExtractLinearInequality(MTest* test, BranchDirection direction, MDefinition* lhs = compare->getOperand(0); MDefinition* rhs = compare->getOperand(1); - // TODO: optimize Compare_UInt32 - if (!compare->isInt32Comparison()) - return false; + if (!compare->isInt32Comparison()) { + if (compare->compareType() != MCompare::Compare_UInt32) + return false; + + Range* lhsRange = lhs->range(); + Range* rhsRange = rhs->range(); + if (!lhsRange || !rhsRange || + !lhsRange->isFiniteNonNegative() || + !rhsRange->isFiniteNonNegative()) + { + return false; + } + } MOZ_ASSERT(lhs->type() == MIRType::Int32); MOZ_ASSERT(rhs->type() == MIRType::Int32); diff --git a/js/src/jit/RangeAnalysis.cpp b/js/src/jit/RangeAnalysis.cpp index bf1ea72d2a..7b8863da1e 100644 --- a/js/src/jit/RangeAnalysis.cpp +++ b/js/src/jit/RangeAnalysis.cpp @@ -174,12 +174,20 @@ RangeAnalysis::addBetaNodes() if (!compare->isNumericComparison()) continue; - // TODO: support unsigned comparisons - if (compare->compareType() == MCompare::Compare_UInt32) - continue; - MDefinition* left = compare->getOperand(0); MDefinition* right = compare->getOperand(1); + + if (compare->compareType() == MCompare::Compare_UInt32) { + Range* leftRange = left->range(); + Range* rightRange = right->range(); + if (!leftRange || !rightRange || + !leftRange->isFiniteNonNegative() || + !rightRange->isFiniteNonNegative()) + { + continue; + } + } + double bound; double conservativeLower = NegativeInfinity(); double conservativeUpper = PositiveInfinity(); diff --git a/js/src/jsstr.cpp b/js/src/jsstr.cpp index 593cf4d708..a5829f9b9e 100644 --- a/js/src/jsstr.cpp +++ b/js/src/jsstr.cpp @@ -1772,15 +1772,18 @@ StringMatch(const TextChar* text, uint32_t textLen, const PatChar* pat, uint32_t * speed of memcmp. For small patterns, a simple loop is faster. We also can't * use memcmp if one of the strings is TwoByte and the other is Latin-1. * - * FIXME: Linux memcmp performance is sad and the manual loop is faster. + * On Linux, keep the manual path for moderate patterns and only enable + * memcmp for very large patterns where it tends to amortize call overhead. */ - return -#if !defined(__linux__) - (patLen > 128 && IsSame::value) - ? Matcher, TextChar, PatChar>(text, textLen, pat, patLen) - : +#if defined(__linux__) + const bool useMemCmp = patLen > 512 && IsSame::value; +#else + const bool useMemCmp = patLen > 128 && IsSame::value; #endif - Matcher, TextChar, PatChar>(text, textLen, pat, patLen); + + return useMemCmp + ? Matcher, TextChar, PatChar>(text, textLen, pat, patLen) + : Matcher, TextChar, PatChar>(text, textLen, pat, patLen); } static int32_t diff --git a/js/src/vm/TypedArrayCommon.h b/js/src/vm/TypedArrayCommon.h index 59ffd78b24..8b200e4398 100644 --- a/js/src/vm/TypedArrayCommon.h +++ b/js/src/vm/TypedArrayCommon.h @@ -12,6 +12,14 @@ #include "mozilla/FloatingPoint.h" #include +#include +#include + +#if (defined(JS_CODEGEN_X64) || defined(JS_CODEGEN_X86)) && \ + (defined(_M_X64) || defined(__SSE2__) || (defined(_M_IX86_FP) && _M_IX86_FP >= 2)) +# include +# define JS_TYPEDARRAY_HAS_SSE2 1 +#endif #include "jsarray.h" #include "jscntxt.h" @@ -199,6 +207,186 @@ ConvertNumber(From src) return To(src); } +#ifdef JS_TYPEDARRAY_HAS_SSE2 +static inline void +SSE2ConvertFloatToUint8Clamped(uint8_clamped* dest, const float* src, uint32_t count) +{ + const __m128 fzero = _mm_set1_ps(0.0f); + const __m128 fmax = _mm_set1_ps(255.0f); + const __m128i izero = _mm_setzero_si128(); + const __m128i i255 = _mm_set1_epi16(255); + + uint32_t i = 0; + for (; i + 4 <= count; i += 4) { + __m128 values = _mm_loadu_ps(src + i); + + // Keep exact scalar behavior for NaN lanes. + if (_mm_movemask_ps(_mm_cmpunord_ps(values, values))) { + for (uint32_t j = 0; j < 4; ++j) + dest[i + j] = uint8_clamped(src[i + j]); + continue; + } + + values = _mm_min_ps(_mm_max_ps(values, fzero), fmax); + __m128i ints = _mm_cvtps_epi32(values); + __m128i packed16 = _mm_packs_epi32(ints, izero); + packed16 = _mm_min_epi16(_mm_max_epi16(packed16, izero), i255); + __m128i packed8 = _mm_packus_epi16(packed16, izero); + + uint32_t out = static_cast(_mm_cvtsi128_si32(packed8)); + ::memcpy(reinterpret_cast(dest + i), &out, sizeof(out)); + } + + for (; i < count; ++i) + dest[i] = uint8_clamped(src[i]); +} + +static inline void +SSE2ConvertDoubleToUint8Clamped(uint8_clamped* dest, const double* src, uint32_t count) +{ + const __m128d dzero = _mm_set1_pd(0.0); + const __m128d dmax = _mm_set1_pd(255.0); + const __m128i izero = _mm_setzero_si128(); + const __m128i i255 = _mm_set1_epi16(255); + + uint32_t i = 0; + for (; i + 2 <= count; i += 2) { + __m128d values = _mm_loadu_pd(src + i); + + // Keep exact scalar behavior for NaN lanes. + if (_mm_movemask_pd(_mm_cmpunord_pd(values, values))) { + for (uint32_t j = 0; j < 2; ++j) + dest[i + j] = uint8_clamped(src[i + j]); + continue; + } + + values = _mm_min_pd(_mm_max_pd(values, dzero), dmax); + __m128i ints = _mm_cvtpd_epi32(values); + __m128i packed16 = _mm_packs_epi32(ints, izero); + packed16 = _mm_min_epi16(_mm_max_epi16(packed16, izero), i255); + __m128i packed8 = _mm_packus_epi16(packed16, izero); + + uint16_t out = static_cast(static_cast(_mm_cvtsi128_si32(packed8)) & 0xFFFFu); + ::memcpy(reinterpret_cast(dest + i), &out, sizeof(out)); + } + + for (; i < count; ++i) + dest[i] = uint8_clamped(src[i]); +} + +static inline void +SSE2ConvertInt8ToUint8Clamped(uint8_clamped* dest, const int8_t* src, uint32_t count) +{ + const __m128i izero = _mm_setzero_si128(); + uint8_t* out = reinterpret_cast(dest); + + uint32_t i = 0; + for (; i + 16 <= count; i += 16) { + __m128i values = _mm_loadu_si128(reinterpret_cast(src + i)); + __m128i negatives = _mm_cmpgt_epi8(izero, values); + __m128i clamped = _mm_andnot_si128(negatives, values); + _mm_storeu_si128(reinterpret_cast<__m128i*>(out + i), clamped); + } + + for (; i < count; ++i) + dest[i] = uint8_clamped(src[i]); +} + +static inline void +SSE2ConvertInt16ToUint8Clamped(uint8_clamped* dest, const int16_t* src, uint32_t count) +{ + uint8_t* out = reinterpret_cast(dest); + + uint32_t i = 0; + for (; i + 16 <= count; i += 16) { + __m128i a = _mm_loadu_si128(reinterpret_cast(src + i)); + __m128i b = _mm_loadu_si128(reinterpret_cast(src + i + 8)); + __m128i packed = _mm_packus_epi16(a, b); + _mm_storeu_si128(reinterpret_cast<__m128i*>(out + i), packed); + } + + for (; i < count; ++i) + dest[i] = uint8_clamped(src[i]); +} + +static inline void +SSE2ConvertUint16ToUint8Clamped(uint8_clamped* dest, const uint16_t* src, uint32_t count) +{ + const __m128i i255 = _mm_set1_epi16(255); + uint8_t* out = reinterpret_cast(dest); + + uint32_t i = 0; + for (; i + 16 <= count; i += 16) { + __m128i a = _mm_loadu_si128(reinterpret_cast(src + i)); + __m128i b = _mm_loadu_si128(reinterpret_cast(src + i + 8)); + + __m128i aExcess = _mm_subs_epu16(a, i255); + __m128i bExcess = _mm_subs_epu16(b, i255); + __m128i aClamped = _mm_sub_epi16(a, aExcess); + __m128i bClamped = _mm_sub_epi16(b, bExcess); + + __m128i packed = _mm_packus_epi16(aClamped, bClamped); + _mm_storeu_si128(reinterpret_cast<__m128i*>(out + i), packed); + } + + for (; i < count; ++i) + dest[i] = uint8_clamped(src[i]); +} + +static inline void +SSE2ConvertInt32ToUint8Clamped(uint8_clamped* dest, const int32_t* src, uint32_t count) +{ + const __m128i izero = _mm_setzero_si128(); + const __m128i i255 = _mm_set1_epi16(255); + uint8_t* out = reinterpret_cast(dest); + + uint32_t i = 0; + for (; i + 8 <= count; i += 8) { + __m128i a = _mm_loadu_si128(reinterpret_cast(src + i)); + __m128i b = _mm_loadu_si128(reinterpret_cast(src + i + 4)); + __m128i words = _mm_packs_epi32(a, b); + words = _mm_min_epi16(_mm_max_epi16(words, izero), i255); + __m128i bytes = _mm_packus_epi16(words, izero); + _mm_storel_epi64(reinterpret_cast<__m128i*>(out + i), bytes); + } + + for (; i < count; ++i) + dest[i] = uint8_clamped(src[i]); +} + +static inline void +SSE2ConvertUint32ToUint8Clamped(uint8_clamped* dest, const uint32_t* src, uint32_t count) +{ + const __m128i izero = _mm_setzero_si128(); + const __m128i maskHigh = _mm_set1_epi32(0xFFFFFF00u); + const __m128i maskLow = _mm_set1_epi32(0x000000FFu); + const __m128i i255d = _mm_set1_epi32(255); + uint8_t* out = reinterpret_cast(dest); + + uint32_t i = 0; + for (; i + 8 <= count; i += 8) { + __m128i a = _mm_loadu_si128(reinterpret_cast(src + i)); + __m128i b = _mm_loadu_si128(reinterpret_cast(src + i + 4)); + + __m128i aFits = _mm_cmpeq_epi32(_mm_and_si128(a, maskHigh), izero); + __m128i bFits = _mm_cmpeq_epi32(_mm_and_si128(b, maskHigh), izero); + + __m128i aLow = _mm_and_si128(a, maskLow); + __m128i bLow = _mm_and_si128(b, maskLow); + + __m128i aClamped = _mm_or_si128(_mm_and_si128(aFits, aLow), _mm_andnot_si128(aFits, i255d)); + __m128i bClamped = _mm_or_si128(_mm_and_si128(bFits, bLow), _mm_andnot_si128(bFits, i255d)); + + __m128i words = _mm_packs_epi32(aClamped, bClamped); + __m128i bytes = _mm_packus_epi16(words, izero); + _mm_storel_epi64(reinterpret_cast<__m128i*>(out + i), bytes); + } + + for (; i < count; ++i) + dest[i] = uint8_clamped(src[i]); +} +#endif + template struct TypeIDOfType; template<> struct TypeIDOfType { static const Scalar::Type id = Scalar::Int8; }; template<> struct TypeIDOfType { static const Scalar::Type id = Scalar::Uint8; }; @@ -275,24 +463,25 @@ class UnsharedOps template static void podCopy(SharedMem dest, SharedMem src, size_t nelem) { - // std::copy_n better matches the argument values/types of this - // function, but as noted below it allows the input/output ranges to - // overlap. std::copy does not, so use it so the compiler has extra - // ability to optimize. - const auto* first = src.unwrapUnshared(); - const auto* last = first + nelem; - auto* result = dest.unwrapUnshared(); - std::copy(first, last, result); + static_assert(std::is_trivially_copyable::value, + "podCopy requires trivially copyable element type"); + if (nelem == 0) + return; + + // Keep this on memcpy so platform CRT implementations can use their + // best vectorized copy routines (SSE2/AVX/etc.) where available. + ::memcpy(dest.unwrapUnshared(), src.unwrapUnshared(), nelem * sizeof(T)); } template static void podMove(SharedMem dest, SharedMem src, size_t n) { - // std::copy_n copies from |src| to |dest| starting from |src|, so - // input/output ranges *may* permissibly overlap, as this function - // allows. - const auto* start = src.unwrapUnshared(); - auto* result = dest.unwrapUnshared(); - std::copy_n(start, n, result); + static_assert(std::is_trivially_copyable::value, + "podMove requires trivially copyable element type"); + if (n == 0) + return; + + // memmove handles overlap and still maps to optimized runtime copies. + ::memmove(dest.unwrapUnshared(), src.unwrapUnshared(), n * sizeof(T)); } static SharedMem extract(TypedArrayObject* obj) { @@ -350,6 +539,14 @@ class ElementSpecific switch (source->as().type()) { case Scalar::Int8: { SharedMem src = data.cast(); +#ifdef JS_TYPEDARRAY_HAS_SSE2 + if (std::is_same::value && std::is_same::value) { + SSE2ConvertInt8ToUint8Clamped(reinterpret_cast(dest.unwrapUnshared()), + data.cast().unwrapUnshared(), + count); + break; + } +#endif for (uint32_t i = 0; i < count; ++i) Ops::store(dest++, ConvertNumber(Ops::load(src++))); break; @@ -357,30 +554,66 @@ class ElementSpecific case Scalar::Uint8: case Scalar::Uint8Clamped: { SharedMem src = data.cast(); + if (std::is_same::value && std::is_same::value) { + Ops::podCopy(dest, data.cast(), count); + break; + } for (uint32_t i = 0; i < count; ++i) Ops::store(dest++, ConvertNumber(Ops::load(src++))); break; } case Scalar::Int16: { SharedMem src = data.cast(); +#ifdef JS_TYPEDARRAY_HAS_SSE2 + if (std::is_same::value && std::is_same::value) { + SSE2ConvertInt16ToUint8Clamped(reinterpret_cast(dest.unwrapUnshared()), + data.cast().unwrapUnshared(), + count); + break; + } +#endif for (uint32_t i = 0; i < count; ++i) Ops::store(dest++, ConvertNumber(Ops::load(src++))); break; } case Scalar::Uint16: { SharedMem src = data.cast(); +#ifdef JS_TYPEDARRAY_HAS_SSE2 + if (std::is_same::value && std::is_same::value) { + SSE2ConvertUint16ToUint8Clamped(reinterpret_cast(dest.unwrapUnshared()), + data.cast().unwrapUnshared(), + count); + break; + } +#endif for (uint32_t i = 0; i < count; ++i) Ops::store(dest++, ConvertNumber(Ops::load(src++))); break; } case Scalar::Int32: { SharedMem src = data.cast(); +#ifdef JS_TYPEDARRAY_HAS_SSE2 + if (std::is_same::value && std::is_same::value) { + SSE2ConvertInt32ToUint8Clamped(reinterpret_cast(dest.unwrapUnshared()), + data.cast().unwrapUnshared(), + count); + break; + } +#endif for (uint32_t i = 0; i < count; ++i) Ops::store(dest++, ConvertNumber(Ops::load(src++))); break; } case Scalar::Uint32: { SharedMem src = data.cast(); +#ifdef JS_TYPEDARRAY_HAS_SSE2 + if (std::is_same::value && std::is_same::value) { + SSE2ConvertUint32ToUint8Clamped(reinterpret_cast(dest.unwrapUnshared()), + data.cast().unwrapUnshared(), + count); + break; + } +#endif for (uint32_t i = 0; i < count; ++i) Ops::store(dest++, ConvertNumber(Ops::load(src++))); break; @@ -399,12 +632,28 @@ class ElementSpecific } case Scalar::Float32: { SharedMem src = data.cast(); +#ifdef JS_TYPEDARRAY_HAS_SSE2 + if (std::is_same::value && std::is_same::value) { + SSE2ConvertFloatToUint8Clamped(reinterpret_cast(dest.unwrapUnshared()), + data.cast().unwrapUnshared(), + count); + break; + } +#endif for (uint32_t i = 0; i < count; ++i) Ops::store(dest++, ConvertNumber(Ops::load(src++))); break; } case Scalar::Float64: { SharedMem src = data.cast(); +#ifdef JS_TYPEDARRAY_HAS_SSE2 + if (std::is_same::value && std::is_same::value) { + SSE2ConvertDoubleToUint8Clamped(reinterpret_cast(dest.unwrapUnshared()), + data.cast().unwrapUnshared(), + count); + break; + } +#endif for (uint32_t i = 0; i < count; ++i) Ops::store(dest++, ConvertNumber(Ops::load(src++))); break; @@ -563,6 +812,15 @@ class ElementSpecific return true; } + if (std::is_same::value && + (source->type() == Scalar::Uint8 || source->type() == Scalar::Uint8Clamped)) + { + SharedMem src = + source->template as().viewDataEither().template cast(); + Ops::podMove(dest, src, len); + return true; + } + // Copy |source| in case it overlaps the target elements being set. size_t sourceByteLen = len * source->bytesPerElement(); void* data = target->zone()->template pod_malloc(sourceByteLen); @@ -575,6 +833,12 @@ class ElementSpecific switch (source->type()) { case Scalar::Int8: { int8_t* src = static_cast(data); +#ifdef JS_TYPEDARRAY_HAS_SSE2 + if (std::is_same::value && std::is_same::value) { + SSE2ConvertInt8ToUint8Clamped(reinterpret_cast(dest.unwrapUnshared()), src, len); + break; + } +#endif for (uint32_t i = 0; i < len; ++i) Ops::store(dest++, ConvertNumber(*src++)); break; @@ -582,30 +846,58 @@ class ElementSpecific case Scalar::Uint8: case Scalar::Uint8Clamped: { uint8_t* src = static_cast(data); + if (std::is_same::value && std::is_same::value) { + Ops::podCopy(dest, SharedMem::unshared(src).template cast(), len); + break; + } for (uint32_t i = 0; i < len; ++i) Ops::store(dest++, ConvertNumber(*src++)); break; } case Scalar::Int16: { int16_t* src = static_cast(data); +#ifdef JS_TYPEDARRAY_HAS_SSE2 + if (std::is_same::value && std::is_same::value) { + SSE2ConvertInt16ToUint8Clamped(reinterpret_cast(dest.unwrapUnshared()), src, len); + break; + } +#endif for (uint32_t i = 0; i < len; ++i) Ops::store(dest++, ConvertNumber(*src++)); break; } case Scalar::Uint16: { uint16_t* src = static_cast(data); +#ifdef JS_TYPEDARRAY_HAS_SSE2 + if (std::is_same::value && std::is_same::value) { + SSE2ConvertUint16ToUint8Clamped(reinterpret_cast(dest.unwrapUnshared()), src, len); + break; + } +#endif for (uint32_t i = 0; i < len; ++i) Ops::store(dest++, ConvertNumber(*src++)); break; } case Scalar::Int32: { int32_t* src = static_cast(data); +#ifdef JS_TYPEDARRAY_HAS_SSE2 + if (std::is_same::value && std::is_same::value) { + SSE2ConvertInt32ToUint8Clamped(reinterpret_cast(dest.unwrapUnshared()), src, len); + break; + } +#endif for (uint32_t i = 0; i < len; ++i) Ops::store(dest++, ConvertNumber(*src++)); break; } case Scalar::Uint32: { uint32_t* src = static_cast(data); +#ifdef JS_TYPEDARRAY_HAS_SSE2 + if (std::is_same::value && std::is_same::value) { + SSE2ConvertUint32ToUint8Clamped(reinterpret_cast(dest.unwrapUnshared()), src, len); + break; + } +#endif for (uint32_t i = 0; i < len; ++i) Ops::store(dest++, ConvertNumber(*src++)); break; @@ -624,12 +916,24 @@ class ElementSpecific } case Scalar::Float32: { float* src = static_cast(data); +#ifdef JS_TYPEDARRAY_HAS_SSE2 + if (std::is_same::value && std::is_same::value) { + SSE2ConvertFloatToUint8Clamped(reinterpret_cast(dest.unwrapUnshared()), src, len); + break; + } +#endif for (uint32_t i = 0; i < len; ++i) Ops::store(dest++, ConvertNumber(*src++)); break; } case Scalar::Float64: { double* src = static_cast(data); +#ifdef JS_TYPEDARRAY_HAS_SSE2 + if (std::is_same::value && std::is_same::value) { + SSE2ConvertDoubleToUint8Clamped(reinterpret_cast(dest.unwrapUnshared()), src, len); + break; + } +#endif for (uint32_t i = 0; i < len; ++i) Ops::store(dest++, ConvertNumber(*src++)); break; diff --git a/js/src/vm/TypedArrayObject.cpp b/js/src/vm/TypedArrayObject.cpp index ebe785aa2e..3e9e82c844 100644 --- a/js/src/vm/TypedArrayObject.cpp +++ b/js/src/vm/TypedArrayObject.cpp @@ -7,6 +7,7 @@ #include "mozilla/Alignment.h" #include "mozilla/Casting.h" +#include "mozilla/EndianUtils.h" #include "mozilla/FloatingPoint.h" #include "mozilla/PodOperations.h" @@ -2109,35 +2110,6 @@ needToSwapBytes(bool littleEndian) #endif } -static inline uint8_t -swapBytes(uint8_t x) -{ - return x; -} - -static inline uint16_t -swapBytes(uint16_t x) -{ - return ((x & 0xff) << 8) | (x >> 8); -} - -static inline uint32_t -swapBytes(uint32_t x) -{ - return ((x & 0xff) << 24) | - ((x & 0xff00) << 8) | - ((x & 0xff0000) >> 8) | - ((x & 0xff000000) >> 24); -} - -static inline uint64_t -swapBytes(uint64_t x) -{ - uint32_t a = x & UINT32_MAX; - uint32_t b = x >> 32; - return (uint64_t(swapBytes(a)) << 32) | swapBytes(b); -} - template struct DataToRepType { typedef DataType result; }; template <> struct DataToRepType { typedef uint8_t result; }; template <> struct DataToRepType { typedef uint8_t result; }; @@ -2155,23 +2127,111 @@ struct DataViewIO { typedef typename DataToRepType::result ReadWriteType; + static MOZ_ALWAYS_INLINE ReadWriteType fromBytes(const uint8_t* unalignedBuffer, bool wantSwap) + { + if (sizeof(ReadWriteType) == 1) + return ReadWriteType(*unalignedBuffer); + + if (sizeof(ReadWriteType) == 2) { +#if MOZ_LITTLE_ENDIAN + return ReadWriteType(wantSwap ? mozilla::BigEndian::readUint16(unalignedBuffer) + : mozilla::LittleEndian::readUint16(unalignedBuffer)); +#else + return ReadWriteType(wantSwap ? mozilla::LittleEndian::readUint16(unalignedBuffer) + : mozilla::BigEndian::readUint16(unalignedBuffer)); +#endif + } + + if (sizeof(ReadWriteType) == 4) { +#if MOZ_LITTLE_ENDIAN + return ReadWriteType(wantSwap ? mozilla::BigEndian::readUint32(unalignedBuffer) + : mozilla::LittleEndian::readUint32(unalignedBuffer)); +#else + return ReadWriteType(wantSwap ? mozilla::LittleEndian::readUint32(unalignedBuffer) + : mozilla::BigEndian::readUint32(unalignedBuffer)); +#endif + } + + if (sizeof(ReadWriteType) == 8) { + #if MOZ_LITTLE_ENDIAN + return ReadWriteType(wantSwap ? mozilla::BigEndian::readUint64(unalignedBuffer) + : mozilla::LittleEndian::readUint64(unalignedBuffer)); + #else + return ReadWriteType(wantSwap ? mozilla::LittleEndian::readUint64(unalignedBuffer) + : mozilla::BigEndian::readUint64(unalignedBuffer)); + #endif + } + + MOZ_CRASH("unsupported DataView element size"); + } + + static MOZ_ALWAYS_INLINE void toBytes(uint8_t* unalignedBuffer, ReadWriteType value, + bool wantSwap) + { + if (sizeof(ReadWriteType) == 1) { + *unalignedBuffer = uint8_t(value); + return; + } + + if (sizeof(ReadWriteType) == 2) { +#if MOZ_LITTLE_ENDIAN + if (wantSwap) + mozilla::BigEndian::writeUint16(unalignedBuffer, uint16_t(value)); + else + mozilla::LittleEndian::writeUint16(unalignedBuffer, uint16_t(value)); +#else + if (wantSwap) + mozilla::LittleEndian::writeUint16(unalignedBuffer, uint16_t(value)); + else + mozilla::BigEndian::writeUint16(unalignedBuffer, uint16_t(value)); +#endif + return; + } + + if (sizeof(ReadWriteType) == 4) { +#if MOZ_LITTLE_ENDIAN + if (wantSwap) + mozilla::BigEndian::writeUint32(unalignedBuffer, uint32_t(value)); + else + mozilla::LittleEndian::writeUint32(unalignedBuffer, uint32_t(value)); +#else + if (wantSwap) + mozilla::LittleEndian::writeUint32(unalignedBuffer, uint32_t(value)); + else + mozilla::BigEndian::writeUint32(unalignedBuffer, uint32_t(value)); +#endif + return; + } + + if (sizeof(ReadWriteType) == 8) { +#if MOZ_LITTLE_ENDIAN + if (wantSwap) + mozilla::BigEndian::writeUint64(unalignedBuffer, uint64_t(value)); + else + mozilla::LittleEndian::writeUint64(unalignedBuffer, uint64_t(value)); +#else + if (wantSwap) + mozilla::LittleEndian::writeUint64(unalignedBuffer, uint64_t(value)); + else + mozilla::BigEndian::writeUint64(unalignedBuffer, uint64_t(value)); +#endif + return; + } + + MOZ_CRASH("unsupported DataView element size"); + } + static void fromBuffer(DataType* dest, const uint8_t* unalignedBuffer, bool wantSwap) { MOZ_ASSERT((reinterpret_cast(dest) & (Min(MOZ_ALIGNOF(void*), sizeof(DataType)) - 1)) == 0); - js_memcpy((void*) dest, unalignedBuffer, sizeof(ReadWriteType)); - if (wantSwap) { - ReadWriteType* rwDest = reinterpret_cast(dest); - *rwDest = swapBytes(*rwDest); - } + *reinterpret_cast(dest) = fromBytes(unalignedBuffer, wantSwap); } static void toBuffer(uint8_t* unalignedBuffer, const DataType* src, bool wantSwap) { MOZ_ASSERT((reinterpret_cast(src) & (Min(MOZ_ALIGNOF(void*), sizeof(DataType)) - 1)) == 0); ReadWriteType temp = *reinterpret_cast(src); - if (wantSwap) - temp = swapBytes(temp); - js_memcpy(unalignedBuffer, (void*) &temp, sizeof(ReadWriteType)); + toBytes(unalignedBuffer, temp, wantSwap); } };