SpiderMonkey optimizations

This commit is contained in:
ownedbywuigi 2026-03-27 18:54:19 +00:00
commit 966fe69c7d
5 changed files with 451 additions and 65 deletions

View file

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

View file

@ -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>();
double conservativeUpper = PositiveInfinity<double>();

View file

@ -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<TextChar, PatChar>::value)
? Matcher<MemCmp<TextChar, PatChar>, TextChar, PatChar>(text, textLen, pat, patLen)
:
#if defined(__linux__)
const bool useMemCmp = patLen > 512 && IsSame<TextChar, PatChar>::value;
#else
const bool useMemCmp = patLen > 128 && IsSame<TextChar, PatChar>::value;
#endif
Matcher<ManualCmp<TextChar, PatChar>, TextChar, PatChar>(text, textLen, pat, patLen);
return useMemCmp
? Matcher<MemCmp<TextChar, PatChar>, TextChar, PatChar>(text, textLen, pat, patLen)
: Matcher<ManualCmp<TextChar, PatChar>, TextChar, PatChar>(text, textLen, pat, patLen);
}
static int32_t

View file

@ -12,6 +12,14 @@
#include "mozilla/FloatingPoint.h"
#include <algorithm>
#include <cstring>
#include <type_traits>
#if (defined(JS_CODEGEN_X64) || defined(JS_CODEGEN_X86)) && \
(defined(_M_X64) || defined(__SSE2__) || (defined(_M_IX86_FP) && _M_IX86_FP >= 2))
# include <emmintrin.h>
# 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<uint32_t>(_mm_cvtsi128_si32(packed8));
::memcpy(reinterpret_cast<uint8_t*>(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<uint16_t>(static_cast<uint32_t>(_mm_cvtsi128_si32(packed8)) & 0xFFFFu);
::memcpy(reinterpret_cast<uint8_t*>(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<uint8_t*>(dest);
uint32_t i = 0;
for (; i + 16 <= count; i += 16) {
__m128i values = _mm_loadu_si128(reinterpret_cast<const __m128i*>(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<uint8_t*>(dest);
uint32_t i = 0;
for (; i + 16 <= count; i += 16) {
__m128i a = _mm_loadu_si128(reinterpret_cast<const __m128i*>(src + i));
__m128i b = _mm_loadu_si128(reinterpret_cast<const __m128i*>(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<uint8_t*>(dest);
uint32_t i = 0;
for (; i + 16 <= count; i += 16) {
__m128i a = _mm_loadu_si128(reinterpret_cast<const __m128i*>(src + i));
__m128i b = _mm_loadu_si128(reinterpret_cast<const __m128i*>(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<uint8_t*>(dest);
uint32_t i = 0;
for (; i + 8 <= count; i += 8) {
__m128i a = _mm_loadu_si128(reinterpret_cast<const __m128i*>(src + i));
__m128i b = _mm_loadu_si128(reinterpret_cast<const __m128i*>(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<uint8_t*>(dest);
uint32_t i = 0;
for (; i + 8 <= count; i += 8) {
__m128i a = _mm_loadu_si128(reinterpret_cast<const __m128i*>(src + i));
__m128i b = _mm_loadu_si128(reinterpret_cast<const __m128i*>(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<typename NativeType> struct TypeIDOfType;
template<> struct TypeIDOfType<int8_t> { static const Scalar::Type id = Scalar::Int8; };
template<> struct TypeIDOfType<uint8_t> { static const Scalar::Type id = Scalar::Uint8; };
@ -275,24 +463,25 @@ class UnsharedOps
template<typename T>
static void podCopy(SharedMem<T*> dest, SharedMem<T*> 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<T>::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<typename T>
static void podMove(SharedMem<T*> dest, SharedMem<T*> 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<T>::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<void*> extract(TypedArrayObject* obj) {
@ -350,6 +539,14 @@ class ElementSpecific
switch (source->as<TypedArrayObject>().type()) {
case Scalar::Int8: {
SharedMem<JS_VOLATILE_ARM int8_t*> src = data.cast<JS_VOLATILE_ARM int8_t*>();
#ifdef JS_TYPEDARRAY_HAS_SSE2
if (std::is_same<T, uint8_clamped>::value && std::is_same<Ops, UnsharedOps>::value) {
SSE2ConvertInt8ToUint8Clamped(reinterpret_cast<uint8_clamped*>(dest.unwrapUnshared()),
data.cast<int8_t*>().unwrapUnshared(),
count);
break;
}
#endif
for (uint32_t i = 0; i < count; ++i)
Ops::store(dest++, ConvertNumber<T>(Ops::load(src++)));
break;
@ -357,30 +554,66 @@ class ElementSpecific
case Scalar::Uint8:
case Scalar::Uint8Clamped: {
SharedMem<JS_VOLATILE_ARM uint8_t*> src = data.cast<JS_VOLATILE_ARM uint8_t*>();
if (std::is_same<T, uint8_clamped>::value && std::is_same<Ops, UnsharedOps>::value) {
Ops::podCopy(dest, data.cast<T*>(), count);
break;
}
for (uint32_t i = 0; i < count; ++i)
Ops::store(dest++, ConvertNumber<T>(Ops::load(src++)));
break;
}
case Scalar::Int16: {
SharedMem<JS_VOLATILE_ARM int16_t*> src = data.cast<JS_VOLATILE_ARM int16_t*>();
#ifdef JS_TYPEDARRAY_HAS_SSE2
if (std::is_same<T, uint8_clamped>::value && std::is_same<Ops, UnsharedOps>::value) {
SSE2ConvertInt16ToUint8Clamped(reinterpret_cast<uint8_clamped*>(dest.unwrapUnshared()),
data.cast<int16_t*>().unwrapUnshared(),
count);
break;
}
#endif
for (uint32_t i = 0; i < count; ++i)
Ops::store(dest++, ConvertNumber<T>(Ops::load(src++)));
break;
}
case Scalar::Uint16: {
SharedMem<JS_VOLATILE_ARM uint16_t*> src = data.cast<JS_VOLATILE_ARM uint16_t*>();
#ifdef JS_TYPEDARRAY_HAS_SSE2
if (std::is_same<T, uint8_clamped>::value && std::is_same<Ops, UnsharedOps>::value) {
SSE2ConvertUint16ToUint8Clamped(reinterpret_cast<uint8_clamped*>(dest.unwrapUnshared()),
data.cast<uint16_t*>().unwrapUnshared(),
count);
break;
}
#endif
for (uint32_t i = 0; i < count; ++i)
Ops::store(dest++, ConvertNumber<T>(Ops::load(src++)));
break;
}
case Scalar::Int32: {
SharedMem<JS_VOLATILE_ARM int32_t*> src = data.cast<JS_VOLATILE_ARM int32_t*>();
#ifdef JS_TYPEDARRAY_HAS_SSE2
if (std::is_same<T, uint8_clamped>::value && std::is_same<Ops, UnsharedOps>::value) {
SSE2ConvertInt32ToUint8Clamped(reinterpret_cast<uint8_clamped*>(dest.unwrapUnshared()),
data.cast<int32_t*>().unwrapUnshared(),
count);
break;
}
#endif
for (uint32_t i = 0; i < count; ++i)
Ops::store(dest++, ConvertNumber<T>(Ops::load(src++)));
break;
}
case Scalar::Uint32: {
SharedMem<JS_VOLATILE_ARM uint32_t*> src = data.cast<JS_VOLATILE_ARM uint32_t*>();
#ifdef JS_TYPEDARRAY_HAS_SSE2
if (std::is_same<T, uint8_clamped>::value && std::is_same<Ops, UnsharedOps>::value) {
SSE2ConvertUint32ToUint8Clamped(reinterpret_cast<uint8_clamped*>(dest.unwrapUnshared()),
data.cast<uint32_t*>().unwrapUnshared(),
count);
break;
}
#endif
for (uint32_t i = 0; i < count; ++i)
Ops::store(dest++, ConvertNumber<T>(Ops::load(src++)));
break;
@ -399,12 +632,28 @@ class ElementSpecific
}
case Scalar::Float32: {
SharedMem<JS_VOLATILE_ARM float*> src = data.cast<JS_VOLATILE_ARM float*>();
#ifdef JS_TYPEDARRAY_HAS_SSE2
if (std::is_same<T, uint8_clamped>::value && std::is_same<Ops, UnsharedOps>::value) {
SSE2ConvertFloatToUint8Clamped(reinterpret_cast<uint8_clamped*>(dest.unwrapUnshared()),
data.cast<float*>().unwrapUnshared(),
count);
break;
}
#endif
for (uint32_t i = 0; i < count; ++i)
Ops::store(dest++, ConvertNumber<T>(Ops::load(src++)));
break;
}
case Scalar::Float64: {
SharedMem<JS_VOLATILE_ARM double*> src = data.cast<JS_VOLATILE_ARM double*>();
#ifdef JS_TYPEDARRAY_HAS_SSE2
if (std::is_same<T, uint8_clamped>::value && std::is_same<Ops, UnsharedOps>::value) {
SSE2ConvertDoubleToUint8Clamped(reinterpret_cast<uint8_clamped*>(dest.unwrapUnshared()),
data.cast<double*>().unwrapUnshared(),
count);
break;
}
#endif
for (uint32_t i = 0; i < count; ++i)
Ops::store(dest++, ConvertNumber<T>(Ops::load(src++)));
break;
@ -563,6 +812,15 @@ class ElementSpecific
return true;
}
if (std::is_same<T, uint8_clamped>::value &&
(source->type() == Scalar::Uint8 || source->type() == Scalar::Uint8Clamped))
{
SharedMem<T*> src =
source->template as<TypedArrayObject>().viewDataEither().template cast<T*>();
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<uint8_t>(sourceByteLen);
@ -575,6 +833,12 @@ class ElementSpecific
switch (source->type()) {
case Scalar::Int8: {
int8_t* src = static_cast<int8_t*>(data);
#ifdef JS_TYPEDARRAY_HAS_SSE2
if (std::is_same<T, uint8_clamped>::value && std::is_same<Ops, UnsharedOps>::value) {
SSE2ConvertInt8ToUint8Clamped(reinterpret_cast<uint8_clamped*>(dest.unwrapUnshared()), src, len);
break;
}
#endif
for (uint32_t i = 0; i < len; ++i)
Ops::store(dest++, ConvertNumber<T>(*src++));
break;
@ -582,30 +846,58 @@ class ElementSpecific
case Scalar::Uint8:
case Scalar::Uint8Clamped: {
uint8_t* src = static_cast<uint8_t*>(data);
if (std::is_same<T, uint8_clamped>::value && std::is_same<Ops, UnsharedOps>::value) {
Ops::podCopy(dest, SharedMem<void*>::unshared(src).template cast<T*>(), len);
break;
}
for (uint32_t i = 0; i < len; ++i)
Ops::store(dest++, ConvertNumber<T>(*src++));
break;
}
case Scalar::Int16: {
int16_t* src = static_cast<int16_t*>(data);
#ifdef JS_TYPEDARRAY_HAS_SSE2
if (std::is_same<T, uint8_clamped>::value && std::is_same<Ops, UnsharedOps>::value) {
SSE2ConvertInt16ToUint8Clamped(reinterpret_cast<uint8_clamped*>(dest.unwrapUnshared()), src, len);
break;
}
#endif
for (uint32_t i = 0; i < len; ++i)
Ops::store(dest++, ConvertNumber<T>(*src++));
break;
}
case Scalar::Uint16: {
uint16_t* src = static_cast<uint16_t*>(data);
#ifdef JS_TYPEDARRAY_HAS_SSE2
if (std::is_same<T, uint8_clamped>::value && std::is_same<Ops, UnsharedOps>::value) {
SSE2ConvertUint16ToUint8Clamped(reinterpret_cast<uint8_clamped*>(dest.unwrapUnshared()), src, len);
break;
}
#endif
for (uint32_t i = 0; i < len; ++i)
Ops::store(dest++, ConvertNumber<T>(*src++));
break;
}
case Scalar::Int32: {
int32_t* src = static_cast<int32_t*>(data);
#ifdef JS_TYPEDARRAY_HAS_SSE2
if (std::is_same<T, uint8_clamped>::value && std::is_same<Ops, UnsharedOps>::value) {
SSE2ConvertInt32ToUint8Clamped(reinterpret_cast<uint8_clamped*>(dest.unwrapUnshared()), src, len);
break;
}
#endif
for (uint32_t i = 0; i < len; ++i)
Ops::store(dest++, ConvertNumber<T>(*src++));
break;
}
case Scalar::Uint32: {
uint32_t* src = static_cast<uint32_t*>(data);
#ifdef JS_TYPEDARRAY_HAS_SSE2
if (std::is_same<T, uint8_clamped>::value && std::is_same<Ops, UnsharedOps>::value) {
SSE2ConvertUint32ToUint8Clamped(reinterpret_cast<uint8_clamped*>(dest.unwrapUnshared()), src, len);
break;
}
#endif
for (uint32_t i = 0; i < len; ++i)
Ops::store(dest++, ConvertNumber<T>(*src++));
break;
@ -624,12 +916,24 @@ class ElementSpecific
}
case Scalar::Float32: {
float* src = static_cast<float*>(data);
#ifdef JS_TYPEDARRAY_HAS_SSE2
if (std::is_same<T, uint8_clamped>::value && std::is_same<Ops, UnsharedOps>::value) {
SSE2ConvertFloatToUint8Clamped(reinterpret_cast<uint8_clamped*>(dest.unwrapUnshared()), src, len);
break;
}
#endif
for (uint32_t i = 0; i < len; ++i)
Ops::store(dest++, ConvertNumber<T>(*src++));
break;
}
case Scalar::Float64: {
double* src = static_cast<double*>(data);
#ifdef JS_TYPEDARRAY_HAS_SSE2
if (std::is_same<T, uint8_clamped>::value && std::is_same<Ops, UnsharedOps>::value) {
SSE2ConvertDoubleToUint8Clamped(reinterpret_cast<uint8_clamped*>(dest.unwrapUnshared()), src, len);
break;
}
#endif
for (uint32_t i = 0; i < len; ++i)
Ops::store(dest++, ConvertNumber<T>(*src++));
break;

View file

@ -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 <typename DataType> struct DataToRepType { typedef DataType result; };
template <> struct DataToRepType<int8_t> { typedef uint8_t result; };
template <> struct DataToRepType<uint8_t> { typedef uint8_t result; };
@ -2155,23 +2127,111 @@ struct DataViewIO
{
typedef typename DataToRepType<DataType>::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<uintptr_t>(dest) & (Min<size_t>(MOZ_ALIGNOF(void*), sizeof(DataType)) - 1)) == 0);
js_memcpy((void*) dest, unalignedBuffer, sizeof(ReadWriteType));
if (wantSwap) {
ReadWriteType* rwDest = reinterpret_cast<ReadWriteType*>(dest);
*rwDest = swapBytes(*rwDest);
}
*reinterpret_cast<ReadWriteType*>(dest) = fromBytes(unalignedBuffer, wantSwap);
}
static void toBuffer(uint8_t* unalignedBuffer, const DataType* src, bool wantSwap)
{
MOZ_ASSERT((reinterpret_cast<uintptr_t>(src) & (Min<size_t>(MOZ_ALIGNOF(void*), sizeof(DataType)) - 1)) == 0);
ReadWriteType temp = *reinterpret_cast<const ReadWriteType*>(src);
if (wantSwap)
temp = swapBytes(temp);
js_memcpy(unalignedBuffer, (void*) &temp, sizeof(ReadWriteType));
toBytes(unalignedBuffer, temp, wantSwap);
}
};