зеркало из https://github.com/mozilla/gecko-dev.git
Backed out 2 changesets (bug 1416723) for failures in dom/serviceworkers/test/test_serviceworker_interfaces.html on a CLOSED TREE
Backed out changeset b2242216d11b (bug 1416723) Backed out changeset bfaf82051dfd (bug 1416723)
This commit is contained in:
Родитель
f36ee2923a
Коммит
9319e91d10
|
@ -48,6 +48,12 @@
|
||||||
#define IF_BDATA(real,imaginary) imaginary
|
#define IF_BDATA(real,imaginary) imaginary
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef ENABLE_SIMD
|
||||||
|
# define IF_SIMD(real,imaginary) real
|
||||||
|
#else
|
||||||
|
# define IF_SIMD(real,imaginary) imaginary
|
||||||
|
#endif
|
||||||
|
|
||||||
#ifdef ENABLE_SHARED_ARRAY_BUFFER
|
#ifdef ENABLE_SHARED_ARRAY_BUFFER
|
||||||
#define IF_SAB(real,imaginary) real
|
#define IF_SAB(real,imaginary) real
|
||||||
#else
|
#else
|
||||||
|
@ -100,6 +106,7 @@ IF_SAB(real,imaginary)(SharedArrayBuffer, InitViaClassSpec, OCLASP(SharedA
|
||||||
IF_INTL(real,imaginary) (Intl, InitIntlClass, CLASP(Intl)) \
|
IF_INTL(real,imaginary) (Intl, InitIntlClass, CLASP(Intl)) \
|
||||||
IF_BDATA(real,imaginary)(TypedObject, InitTypedObjectModuleObject, OCLASP(TypedObjectModule)) \
|
IF_BDATA(real,imaginary)(TypedObject, InitTypedObjectModuleObject, OCLASP(TypedObjectModule)) \
|
||||||
real(Reflect, InitReflect, nullptr) \
|
real(Reflect, InitReflect, nullptr) \
|
||||||
|
IF_SIMD(real,imaginary)(SIMD, InitSimdClass, OCLASP(Simd)) \
|
||||||
real(WeakSet, InitWeakSetClass, OCLASP(WeakSet)) \
|
real(WeakSet, InitWeakSetClass, OCLASP(WeakSet)) \
|
||||||
real(TypedArray, InitViaClassSpec, &js::TypedArrayObject::sharedTypedArrayPrototypeClass) \
|
real(TypedArray, InitViaClassSpec, &js::TypedArrayObject::sharedTypedArrayPrototypeClass) \
|
||||||
IF_SAB(real,imaginary)(Atomics, InitAtomicsClass, OCLASP(Atomics)) \
|
IF_SAB(real,imaginary)(Atomics, InitAtomicsClass, OCLASP(Atomics)) \
|
||||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -0,0 +1,298 @@
|
||||||
|
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||||
|
* vim: set ts=8 sts=4 et sw=4 tw=99:
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
#ifndef builtin_SIMD_h
|
||||||
|
#define builtin_SIMD_h
|
||||||
|
|
||||||
|
#include "jsapi.h"
|
||||||
|
#include "NamespaceImports.h"
|
||||||
|
|
||||||
|
#include "builtin/SIMDConstants.h"
|
||||||
|
#include "jit/IonTypes.h"
|
||||||
|
#include "js/Conversions.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* JS SIMD functions.
|
||||||
|
* Spec matching polyfill:
|
||||||
|
* https://github.com/tc39/ecmascript_simd/blob/master/src/ecmascript_simd.js
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace js {
|
||||||
|
|
||||||
|
class GlobalObject;
|
||||||
|
|
||||||
|
// These classes implement the concept containing the following constraints:
|
||||||
|
// - requires typename Elem: this is the scalar lane type, stored in each lane
|
||||||
|
// of the SIMD vector.
|
||||||
|
// - requires static const unsigned lanes: this is the number of lanes (length)
|
||||||
|
// of the SIMD vector.
|
||||||
|
// - requires static const SimdType type: this is the SimdType enum value
|
||||||
|
// corresponding to the SIMD type.
|
||||||
|
// - requires static bool Cast(JSContext*, JS::HandleValue, Elem*): casts a
|
||||||
|
// given Value to the current scalar lane type and saves it in the Elem
|
||||||
|
// out-param.
|
||||||
|
// - requires static Value ToValue(Elem): returns a Value of the right type
|
||||||
|
// containing the given value.
|
||||||
|
//
|
||||||
|
// This concept is used in the templates above to define the functions
|
||||||
|
// associated to a given type and in their implementations, to avoid code
|
||||||
|
// redundancy.
|
||||||
|
|
||||||
|
struct Float32x4 {
|
||||||
|
typedef float Elem;
|
||||||
|
static const unsigned lanes = 4;
|
||||||
|
static const SimdType type = SimdType::Float32x4;
|
||||||
|
static MOZ_MUST_USE bool Cast(JSContext* cx, JS::HandleValue v, Elem* out) {
|
||||||
|
double d;
|
||||||
|
if (!ToNumber(cx, v, &d))
|
||||||
|
return false;
|
||||||
|
*out = float(d);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
static Value ToValue(Elem value) {
|
||||||
|
return DoubleValue(JS::CanonicalizeNaN(value));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Float64x2 {
|
||||||
|
typedef double Elem;
|
||||||
|
static const unsigned lanes = 2;
|
||||||
|
static const SimdType type = SimdType::Float64x2;
|
||||||
|
static MOZ_MUST_USE bool Cast(JSContext* cx, JS::HandleValue v, Elem* out) {
|
||||||
|
return ToNumber(cx, v, out);
|
||||||
|
}
|
||||||
|
static Value ToValue(Elem value) {
|
||||||
|
return DoubleValue(JS::CanonicalizeNaN(value));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Int8x16 {
|
||||||
|
typedef int8_t Elem;
|
||||||
|
static const unsigned lanes = 16;
|
||||||
|
static const SimdType type = SimdType::Int8x16;
|
||||||
|
static MOZ_MUST_USE bool Cast(JSContext* cx, JS::HandleValue v, Elem* out) {
|
||||||
|
return ToInt8(cx, v, out);
|
||||||
|
}
|
||||||
|
static Value ToValue(Elem value) {
|
||||||
|
return NumberValue(value);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Int16x8 {
|
||||||
|
typedef int16_t Elem;
|
||||||
|
static const unsigned lanes = 8;
|
||||||
|
static const SimdType type = SimdType::Int16x8;
|
||||||
|
static MOZ_MUST_USE bool Cast(JSContext* cx, JS::HandleValue v, Elem* out) {
|
||||||
|
return ToInt16(cx, v, out);
|
||||||
|
}
|
||||||
|
static Value ToValue(Elem value) {
|
||||||
|
return NumberValue(value);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Int32x4 {
|
||||||
|
typedef int32_t Elem;
|
||||||
|
static const unsigned lanes = 4;
|
||||||
|
static const SimdType type = SimdType::Int32x4;
|
||||||
|
static MOZ_MUST_USE bool Cast(JSContext* cx, JS::HandleValue v, Elem* out) {
|
||||||
|
return ToInt32(cx, v, out);
|
||||||
|
}
|
||||||
|
static Value ToValue(Elem value) {
|
||||||
|
return NumberValue(value);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Uint8x16 {
|
||||||
|
typedef uint8_t Elem;
|
||||||
|
static const unsigned lanes = 16;
|
||||||
|
static const SimdType type = SimdType::Uint8x16;
|
||||||
|
static MOZ_MUST_USE bool Cast(JSContext* cx, JS::HandleValue v, Elem* out) {
|
||||||
|
return ToUint8(cx, v, out);
|
||||||
|
}
|
||||||
|
static Value ToValue(Elem value) {
|
||||||
|
return NumberValue(value);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Uint16x8 {
|
||||||
|
typedef uint16_t Elem;
|
||||||
|
static const unsigned lanes = 8;
|
||||||
|
static const SimdType type = SimdType::Uint16x8;
|
||||||
|
static MOZ_MUST_USE bool Cast(JSContext* cx, JS::HandleValue v, Elem* out) {
|
||||||
|
return ToUint16(cx, v, out);
|
||||||
|
}
|
||||||
|
static Value ToValue(Elem value) {
|
||||||
|
return NumberValue(value);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Uint32x4 {
|
||||||
|
typedef uint32_t Elem;
|
||||||
|
static const unsigned lanes = 4;
|
||||||
|
static const SimdType type = SimdType::Uint32x4;
|
||||||
|
static MOZ_MUST_USE bool Cast(JSContext* cx, JS::HandleValue v, Elem* out) {
|
||||||
|
return ToUint32(cx, v, out);
|
||||||
|
}
|
||||||
|
static Value ToValue(Elem value) {
|
||||||
|
return NumberValue(value);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Bool8x16 {
|
||||||
|
typedef int8_t Elem;
|
||||||
|
static const unsigned lanes = 16;
|
||||||
|
static const SimdType type = SimdType::Bool8x16;
|
||||||
|
static MOZ_MUST_USE bool Cast(JSContext* cx, JS::HandleValue v, Elem* out) {
|
||||||
|
*out = ToBoolean(v) ? -1 : 0;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
static Value ToValue(Elem value) {
|
||||||
|
return BooleanValue(value);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Bool16x8 {
|
||||||
|
typedef int16_t Elem;
|
||||||
|
static const unsigned lanes = 8;
|
||||||
|
static const SimdType type = SimdType::Bool16x8;
|
||||||
|
static MOZ_MUST_USE bool Cast(JSContext* cx, JS::HandleValue v, Elem* out) {
|
||||||
|
*out = ToBoolean(v) ? -1 : 0;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
static Value ToValue(Elem value) {
|
||||||
|
return BooleanValue(value);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Bool32x4 {
|
||||||
|
typedef int32_t Elem;
|
||||||
|
static const unsigned lanes = 4;
|
||||||
|
static const SimdType type = SimdType::Bool32x4;
|
||||||
|
static MOZ_MUST_USE bool Cast(JSContext* cx, JS::HandleValue v, Elem* out) {
|
||||||
|
*out = ToBoolean(v) ? -1 : 0;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
static Value ToValue(Elem value) {
|
||||||
|
return BooleanValue(value);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Bool64x2 {
|
||||||
|
typedef int64_t Elem;
|
||||||
|
static const unsigned lanes = 2;
|
||||||
|
static const SimdType type = SimdType::Bool64x2;
|
||||||
|
static MOZ_MUST_USE bool Cast(JSContext* cx, JS::HandleValue v, Elem* out) {
|
||||||
|
*out = ToBoolean(v) ? -1 : 0;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
static Value ToValue(Elem value) {
|
||||||
|
return BooleanValue(value);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Get the well known name of the SIMD.* object corresponding to type.
|
||||||
|
PropertyName* SimdTypeToName(const JSAtomState& atoms, SimdType type);
|
||||||
|
|
||||||
|
// Check if name is the well known name of a SIMD type.
|
||||||
|
// Returns true and sets *type iff name is known.
|
||||||
|
bool IsSimdTypeName(const JSAtomState& atoms, const PropertyName* name, SimdType* type);
|
||||||
|
|
||||||
|
const char* SimdTypeToString(SimdType type);
|
||||||
|
|
||||||
|
template<typename V>
|
||||||
|
JSObject* CreateSimd(JSContext* cx, const typename V::Elem* data);
|
||||||
|
|
||||||
|
template<typename V>
|
||||||
|
bool IsVectorObject(HandleValue v);
|
||||||
|
|
||||||
|
template<typename V>
|
||||||
|
MOZ_MUST_USE bool ToSimdConstant(JSContext* cx, HandleValue v, jit::SimdConstant* out);
|
||||||
|
|
||||||
|
JSObject*
|
||||||
|
InitSimdClass(JSContext* cx, Handle<GlobalObject*> global);
|
||||||
|
|
||||||
|
namespace jit {
|
||||||
|
|
||||||
|
extern const JSJitInfo JitInfo_SimdInt32x4_extractLane;
|
||||||
|
extern const JSJitInfo JitInfo_SimdFloat32x4_extractLane;
|
||||||
|
|
||||||
|
} // namespace jit
|
||||||
|
|
||||||
|
#define DECLARE_SIMD_FLOAT32X4_FUNCTION(Name, Func, Operands) \
|
||||||
|
extern MOZ_MUST_USE bool \
|
||||||
|
simd_float32x4_##Name(JSContext* cx, unsigned argc, Value* vp);
|
||||||
|
FLOAT32X4_FUNCTION_LIST(DECLARE_SIMD_FLOAT32X4_FUNCTION)
|
||||||
|
#undef DECLARE_SIMD_FLOAT32X4_FUNCTION
|
||||||
|
|
||||||
|
#define DECLARE_SIMD_FLOAT64X2_FUNCTION(Name, Func, Operands) \
|
||||||
|
extern MOZ_MUST_USE bool \
|
||||||
|
simd_float64x2_##Name(JSContext* cx, unsigned argc, Value* vp);
|
||||||
|
FLOAT64X2_FUNCTION_LIST(DECLARE_SIMD_FLOAT64X2_FUNCTION)
|
||||||
|
#undef DECLARE_SIMD_FLOAT64X2_FUNCTION
|
||||||
|
|
||||||
|
#define DECLARE_SIMD_INT8X16_FUNCTION(Name, Func, Operands) \
|
||||||
|
extern MOZ_MUST_USE bool \
|
||||||
|
simd_int8x16_##Name(JSContext* cx, unsigned argc, Value* vp);
|
||||||
|
INT8X16_FUNCTION_LIST(DECLARE_SIMD_INT8X16_FUNCTION)
|
||||||
|
#undef DECLARE_SIMD_INT8X16_FUNCTION
|
||||||
|
|
||||||
|
#define DECLARE_SIMD_INT16X8_FUNCTION(Name, Func, Operands) \
|
||||||
|
extern MOZ_MUST_USE bool \
|
||||||
|
simd_int16x8_##Name(JSContext* cx, unsigned argc, Value* vp);
|
||||||
|
INT16X8_FUNCTION_LIST(DECLARE_SIMD_INT16X8_FUNCTION)
|
||||||
|
#undef DECLARE_SIMD_INT16X8_FUNCTION
|
||||||
|
|
||||||
|
#define DECLARE_SIMD_INT32X4_FUNCTION(Name, Func, Operands) \
|
||||||
|
extern MOZ_MUST_USE bool \
|
||||||
|
simd_int32x4_##Name(JSContext* cx, unsigned argc, Value* vp);
|
||||||
|
INT32X4_FUNCTION_LIST(DECLARE_SIMD_INT32X4_FUNCTION)
|
||||||
|
#undef DECLARE_SIMD_INT32X4_FUNCTION
|
||||||
|
|
||||||
|
#define DECLARE_SIMD_UINT8X16_FUNCTION(Name, Func, Operands) \
|
||||||
|
extern MOZ_MUST_USE bool \
|
||||||
|
simd_uint8x16_##Name(JSContext* cx, unsigned argc, Value* vp);
|
||||||
|
UINT8X16_FUNCTION_LIST(DECLARE_SIMD_UINT8X16_FUNCTION)
|
||||||
|
#undef DECLARE_SIMD_UINT8X16_FUNCTION
|
||||||
|
|
||||||
|
#define DECLARE_SIMD_UINT16X8_FUNCTION(Name, Func, Operands) \
|
||||||
|
extern MOZ_MUST_USE bool \
|
||||||
|
simd_uint16x8_##Name(JSContext* cx, unsigned argc, Value* vp);
|
||||||
|
UINT16X8_FUNCTION_LIST(DECLARE_SIMD_UINT16X8_FUNCTION)
|
||||||
|
#undef DECLARE_SIMD_UINT16X8_FUNCTION
|
||||||
|
|
||||||
|
#define DECLARE_SIMD_UINT32X4_FUNCTION(Name, Func, Operands) \
|
||||||
|
extern MOZ_MUST_USE bool \
|
||||||
|
simd_uint32x4_##Name(JSContext* cx, unsigned argc, Value* vp);
|
||||||
|
UINT32X4_FUNCTION_LIST(DECLARE_SIMD_UINT32X4_FUNCTION)
|
||||||
|
#undef DECLARE_SIMD_UINT32X4_FUNCTION
|
||||||
|
|
||||||
|
#define DECLARE_SIMD_BOOL8X16_FUNCTION(Name, Func, Operands) \
|
||||||
|
extern MOZ_MUST_USE bool \
|
||||||
|
simd_bool8x16_##Name(JSContext* cx, unsigned argc, Value* vp);
|
||||||
|
BOOL8X16_FUNCTION_LIST(DECLARE_SIMD_BOOL8X16_FUNCTION)
|
||||||
|
#undef DECLARE_SIMD_BOOL8X16_FUNCTION
|
||||||
|
|
||||||
|
#define DECLARE_SIMD_BOOL16X8_FUNCTION(Name, Func, Operands) \
|
||||||
|
extern MOZ_MUST_USE bool \
|
||||||
|
simd_bool16x8_##Name(JSContext* cx, unsigned argc, Value* vp);
|
||||||
|
BOOL16X8_FUNCTION_LIST(DECLARE_SIMD_BOOL16X8_FUNCTION)
|
||||||
|
#undef DECLARE_SIMD_BOOL16X8_FUNCTION
|
||||||
|
|
||||||
|
#define DECLARE_SIMD_BOOL32X4_FUNCTION(Name, Func, Operands) \
|
||||||
|
extern MOZ_MUST_USE bool \
|
||||||
|
simd_bool32x4_##Name(JSContext* cx, unsigned argc, Value* vp);
|
||||||
|
BOOL32X4_FUNCTION_LIST(DECLARE_SIMD_BOOL32X4_FUNCTION)
|
||||||
|
#undef DECLARE_SIMD_BOOL32X4_FUNCTION
|
||||||
|
|
||||||
|
#define DECLARE_SIMD_BOOL64X2_FUNCTION(Name, Func, Operands) \
|
||||||
|
extern MOZ_MUST_USE bool \
|
||||||
|
simd_bool64x2_##Name(JSContext* cx, unsigned argc, Value* vp);
|
||||||
|
BOOL64X2_FUNCTION_LIST(DECLARE_SIMD_BOOL64X2_FUNCTION)
|
||||||
|
#undef DECLARE_SIMD_BOOL64X2_FUNCTION
|
||||||
|
|
||||||
|
} /* namespace js */
|
||||||
|
|
||||||
|
#endif /* builtin_SIMD_h */
|
|
@ -0,0 +1,941 @@
|
||||||
|
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||||
|
* vim: set ts=8 sts=4 et sw=4 tw=99:
|
||||||
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
#ifndef builtin_SIMDConstants_h
|
||||||
|
#define builtin_SIMDConstants_h
|
||||||
|
|
||||||
|
#include "mozilla/Assertions.h"
|
||||||
|
|
||||||
|
#include "builtin/TypedObjectConstants.h"
|
||||||
|
|
||||||
|
// Bool8x16.
|
||||||
|
#define BOOL8X16_UNARY_FUNCTION_LIST(V) \
|
||||||
|
V(not, (UnaryFunc<Bool8x16, LogicalNot, Bool8x16>), 1) \
|
||||||
|
V(check, (UnaryFunc<Bool8x16, Identity, Bool8x16>), 1) \
|
||||||
|
V(splat, (FuncSplat<Bool8x16>), 1) \
|
||||||
|
V(allTrue, (AllTrue<Bool8x16>), 1) \
|
||||||
|
V(anyTrue, (AnyTrue<Bool8x16>), 1)
|
||||||
|
|
||||||
|
#define BOOL8X16_BINARY_FUNCTION_LIST(V) \
|
||||||
|
V(extractLane, (ExtractLane<Bool8x16>), 2) \
|
||||||
|
V(and, (BinaryFunc<Bool8x16, And, Bool8x16>), 2) \
|
||||||
|
V(or, (BinaryFunc<Bool8x16, Or, Bool8x16>), 2) \
|
||||||
|
V(xor, (BinaryFunc<Bool8x16, Xor, Bool8x16>), 2) \
|
||||||
|
|
||||||
|
#define BOOL8X16_TERNARY_FUNCTION_LIST(V) \
|
||||||
|
V(replaceLane, (ReplaceLane<Bool8x16>), 3)
|
||||||
|
|
||||||
|
#define BOOL8X16_FUNCTION_LIST(V) \
|
||||||
|
BOOL8X16_UNARY_FUNCTION_LIST(V) \
|
||||||
|
BOOL8X16_BINARY_FUNCTION_LIST(V) \
|
||||||
|
BOOL8X16_TERNARY_FUNCTION_LIST(V)
|
||||||
|
|
||||||
|
// Bool 16x8.
|
||||||
|
#define BOOL16X8_UNARY_FUNCTION_LIST(V) \
|
||||||
|
V(not, (UnaryFunc<Bool16x8, LogicalNot, Bool16x8>), 1) \
|
||||||
|
V(check, (UnaryFunc<Bool16x8, Identity, Bool16x8>), 1) \
|
||||||
|
V(splat, (FuncSplat<Bool16x8>), 1) \
|
||||||
|
V(allTrue, (AllTrue<Bool16x8>), 1) \
|
||||||
|
V(anyTrue, (AnyTrue<Bool16x8>), 1)
|
||||||
|
|
||||||
|
#define BOOL16X8_BINARY_FUNCTION_LIST(V) \
|
||||||
|
V(extractLane, (ExtractLane<Bool16x8>), 2) \
|
||||||
|
V(and, (BinaryFunc<Bool16x8, And, Bool16x8>), 2) \
|
||||||
|
V(or, (BinaryFunc<Bool16x8, Or, Bool16x8>), 2) \
|
||||||
|
V(xor, (BinaryFunc<Bool16x8, Xor, Bool16x8>), 2) \
|
||||||
|
|
||||||
|
#define BOOL16X8_TERNARY_FUNCTION_LIST(V) \
|
||||||
|
V(replaceLane, (ReplaceLane<Bool16x8>), 3)
|
||||||
|
|
||||||
|
#define BOOL16X8_FUNCTION_LIST(V) \
|
||||||
|
BOOL16X8_UNARY_FUNCTION_LIST(V) \
|
||||||
|
BOOL16X8_BINARY_FUNCTION_LIST(V) \
|
||||||
|
BOOL16X8_TERNARY_FUNCTION_LIST(V)
|
||||||
|
|
||||||
|
// Bool32x4.
|
||||||
|
#define BOOL32X4_UNARY_FUNCTION_LIST(V) \
|
||||||
|
V(not, (UnaryFunc<Bool32x4, LogicalNot, Bool32x4>), 1) \
|
||||||
|
V(check, (UnaryFunc<Bool32x4, Identity, Bool32x4>), 1) \
|
||||||
|
V(splat, (FuncSplat<Bool32x4>), 1) \
|
||||||
|
V(allTrue, (AllTrue<Bool32x4>), 1) \
|
||||||
|
V(anyTrue, (AnyTrue<Bool32x4>), 1)
|
||||||
|
|
||||||
|
#define BOOL32X4_BINARY_FUNCTION_LIST(V) \
|
||||||
|
V(extractLane, (ExtractLane<Bool32x4>), 2) \
|
||||||
|
V(and, (BinaryFunc<Bool32x4, And, Bool32x4>), 2) \
|
||||||
|
V(or, (BinaryFunc<Bool32x4, Or, Bool32x4>), 2) \
|
||||||
|
V(xor, (BinaryFunc<Bool32x4, Xor, Bool32x4>), 2) \
|
||||||
|
|
||||||
|
#define BOOL32X4_TERNARY_FUNCTION_LIST(V) \
|
||||||
|
V(replaceLane, (ReplaceLane<Bool32x4>), 3)
|
||||||
|
|
||||||
|
#define BOOL32X4_FUNCTION_LIST(V) \
|
||||||
|
BOOL32X4_UNARY_FUNCTION_LIST(V) \
|
||||||
|
BOOL32X4_BINARY_FUNCTION_LIST(V) \
|
||||||
|
BOOL32X4_TERNARY_FUNCTION_LIST(V)
|
||||||
|
|
||||||
|
// Bool64x2.
|
||||||
|
#define BOOL64X2_UNARY_FUNCTION_LIST(V) \
|
||||||
|
V(not, (UnaryFunc<Bool64x2, LogicalNot, Bool64x2>), 1) \
|
||||||
|
V(check, (UnaryFunc<Bool64x2, Identity, Bool64x2>), 1) \
|
||||||
|
V(splat, (FuncSplat<Bool64x2>), 1) \
|
||||||
|
V(allTrue, (AllTrue<Bool64x2>), 1) \
|
||||||
|
V(anyTrue, (AnyTrue<Bool64x2>), 1)
|
||||||
|
|
||||||
|
#define BOOL64X2_BINARY_FUNCTION_LIST(V) \
|
||||||
|
V(extractLane, (ExtractLane<Bool64x2>), 2) \
|
||||||
|
V(and, (BinaryFunc<Bool64x2, And, Bool64x2>), 2) \
|
||||||
|
V(or, (BinaryFunc<Bool64x2, Or, Bool64x2>), 2) \
|
||||||
|
V(xor, (BinaryFunc<Bool64x2, Xor, Bool64x2>), 2) \
|
||||||
|
|
||||||
|
#define BOOL64X2_TERNARY_FUNCTION_LIST(V) \
|
||||||
|
V(replaceLane, (ReplaceLane<Bool64x2>), 3)
|
||||||
|
|
||||||
|
#define BOOL64X2_FUNCTION_LIST(V) \
|
||||||
|
BOOL64X2_UNARY_FUNCTION_LIST(V) \
|
||||||
|
BOOL64X2_BINARY_FUNCTION_LIST(V) \
|
||||||
|
BOOL64X2_TERNARY_FUNCTION_LIST(V)
|
||||||
|
|
||||||
|
// Float32x4.
|
||||||
|
#define FLOAT32X4_UNARY_FUNCTION_LIST(V) \
|
||||||
|
V(abs, (UnaryFunc<Float32x4, Abs, Float32x4>), 1) \
|
||||||
|
V(check, (UnaryFunc<Float32x4, Identity, Float32x4>), 1) \
|
||||||
|
V(fromFloat64x2Bits, (FuncConvertBits<Float64x2, Float32x4>), 1) \
|
||||||
|
V(fromInt8x16Bits, (FuncConvertBits<Int8x16, Float32x4>), 1) \
|
||||||
|
V(fromInt16x8Bits, (FuncConvertBits<Int16x8, Float32x4>), 1) \
|
||||||
|
V(fromInt32x4, (FuncConvert<Int32x4, Float32x4>), 1) \
|
||||||
|
V(fromInt32x4Bits, (FuncConvertBits<Int32x4, Float32x4>), 1) \
|
||||||
|
V(fromUint8x16Bits, (FuncConvertBits<Uint8x16, Float32x4>), 1) \
|
||||||
|
V(fromUint16x8Bits, (FuncConvertBits<Uint16x8, Float32x4>), 1) \
|
||||||
|
V(fromUint32x4, (FuncConvert<Uint32x4, Float32x4>), 1) \
|
||||||
|
V(fromUint32x4Bits, (FuncConvertBits<Uint32x4, Float32x4>), 1) \
|
||||||
|
V(neg, (UnaryFunc<Float32x4, Neg, Float32x4>), 1) \
|
||||||
|
V(reciprocalApproximation, (UnaryFunc<Float32x4, RecApprox, Float32x4>), 1) \
|
||||||
|
V(reciprocalSqrtApproximation, (UnaryFunc<Float32x4, RecSqrtApprox, Float32x4>), 1) \
|
||||||
|
V(splat, (FuncSplat<Float32x4>), 1) \
|
||||||
|
V(sqrt, (UnaryFunc<Float32x4, Sqrt, Float32x4>), 1)
|
||||||
|
|
||||||
|
#define FLOAT32X4_BINARY_FUNCTION_LIST(V) \
|
||||||
|
V(add, (BinaryFunc<Float32x4, Add, Float32x4>), 2) \
|
||||||
|
V(div, (BinaryFunc<Float32x4, Div, Float32x4>), 2) \
|
||||||
|
V(equal, (CompareFunc<Float32x4, Equal, Bool32x4>), 2) \
|
||||||
|
V(extractLane, (ExtractLane<Float32x4>), 2) \
|
||||||
|
V(greaterThan, (CompareFunc<Float32x4, GreaterThan, Bool32x4>), 2) \
|
||||||
|
V(greaterThanOrEqual, (CompareFunc<Float32x4, GreaterThanOrEqual, Bool32x4>), 2) \
|
||||||
|
V(lessThan, (CompareFunc<Float32x4, LessThan, Bool32x4>), 2) \
|
||||||
|
V(lessThanOrEqual, (CompareFunc<Float32x4, LessThanOrEqual, Bool32x4>), 2) \
|
||||||
|
V(load, (Load<Float32x4, 4>), 2) \
|
||||||
|
V(load3, (Load<Float32x4, 3>), 2) \
|
||||||
|
V(load2, (Load<Float32x4, 2>), 2) \
|
||||||
|
V(load1, (Load<Float32x4, 1>), 2) \
|
||||||
|
V(max, (BinaryFunc<Float32x4, Maximum, Float32x4>), 2) \
|
||||||
|
V(maxNum, (BinaryFunc<Float32x4, MaxNum, Float32x4>), 2) \
|
||||||
|
V(min, (BinaryFunc<Float32x4, Minimum, Float32x4>), 2) \
|
||||||
|
V(minNum, (BinaryFunc<Float32x4, MinNum, Float32x4>), 2) \
|
||||||
|
V(mul, (BinaryFunc<Float32x4, Mul, Float32x4>), 2) \
|
||||||
|
V(notEqual, (CompareFunc<Float32x4, NotEqual, Bool32x4>), 2) \
|
||||||
|
V(sub, (BinaryFunc<Float32x4, Sub, Float32x4>), 2)
|
||||||
|
|
||||||
|
#define FLOAT32X4_TERNARY_FUNCTION_LIST(V) \
|
||||||
|
V(replaceLane, (ReplaceLane<Float32x4>), 3) \
|
||||||
|
V(select, (Select<Float32x4, Bool32x4>), 3) \
|
||||||
|
V(store, (Store<Float32x4, 4>), 3) \
|
||||||
|
V(store3, (Store<Float32x4, 3>), 3) \
|
||||||
|
V(store2, (Store<Float32x4, 2>), 3) \
|
||||||
|
V(store1, (Store<Float32x4, 1>), 3)
|
||||||
|
|
||||||
|
#define FLOAT32X4_SHUFFLE_FUNCTION_LIST(V) \
|
||||||
|
V(swizzle, Swizzle<Float32x4>, 5) \
|
||||||
|
V(shuffle, Shuffle<Float32x4>, 6)
|
||||||
|
|
||||||
|
#define FLOAT32X4_FUNCTION_LIST(V) \
|
||||||
|
FLOAT32X4_UNARY_FUNCTION_LIST(V) \
|
||||||
|
FLOAT32X4_BINARY_FUNCTION_LIST(V) \
|
||||||
|
FLOAT32X4_TERNARY_FUNCTION_LIST(V) \
|
||||||
|
FLOAT32X4_SHUFFLE_FUNCTION_LIST(V)
|
||||||
|
|
||||||
|
// Float64x2.
|
||||||
|
#define FLOAT64X2_UNARY_FUNCTION_LIST(V) \
|
||||||
|
V(abs, (UnaryFunc<Float64x2, Abs, Float64x2>), 1) \
|
||||||
|
V(check, (UnaryFunc<Float64x2, Identity, Float64x2>), 1) \
|
||||||
|
V(fromFloat32x4Bits, (FuncConvertBits<Float32x4, Float64x2>), 1) \
|
||||||
|
V(fromInt8x16Bits, (FuncConvertBits<Int8x16, Float64x2>), 1) \
|
||||||
|
V(fromInt16x8Bits, (FuncConvertBits<Int16x8, Float64x2>), 1) \
|
||||||
|
V(fromInt32x4Bits, (FuncConvertBits<Int32x4, Float64x2>), 1) \
|
||||||
|
V(fromUint8x16Bits, (FuncConvertBits<Uint8x16, Float64x2>), 1) \
|
||||||
|
V(fromUint16x8Bits, (FuncConvertBits<Uint16x8, Float64x2>), 1) \
|
||||||
|
V(fromUint32x4Bits, (FuncConvertBits<Uint32x4, Float64x2>), 1) \
|
||||||
|
V(neg, (UnaryFunc<Float64x2, Neg, Float64x2>), 1) \
|
||||||
|
V(reciprocalApproximation, (UnaryFunc<Float64x2, RecApprox, Float64x2>), 1) \
|
||||||
|
V(reciprocalSqrtApproximation, (UnaryFunc<Float64x2, RecSqrtApprox, Float64x2>), 1) \
|
||||||
|
V(splat, (FuncSplat<Float64x2>), 1) \
|
||||||
|
V(sqrt, (UnaryFunc<Float64x2, Sqrt, Float64x2>), 1)
|
||||||
|
|
||||||
|
#define FLOAT64X2_BINARY_FUNCTION_LIST(V) \
|
||||||
|
V(add, (BinaryFunc<Float64x2, Add, Float64x2>), 2) \
|
||||||
|
V(div, (BinaryFunc<Float64x2, Div, Float64x2>), 2) \
|
||||||
|
V(equal, (CompareFunc<Float64x2, Equal, Bool64x2>), 2) \
|
||||||
|
V(extractLane, (ExtractLane<Float64x2>), 2) \
|
||||||
|
V(greaterThan, (CompareFunc<Float64x2, GreaterThan, Bool64x2>), 2) \
|
||||||
|
V(greaterThanOrEqual, (CompareFunc<Float64x2, GreaterThanOrEqual, Bool64x2>), 2) \
|
||||||
|
V(lessThan, (CompareFunc<Float64x2, LessThan, Bool64x2>), 2) \
|
||||||
|
V(lessThanOrEqual, (CompareFunc<Float64x2, LessThanOrEqual, Bool64x2>), 2) \
|
||||||
|
V(load, (Load<Float64x2, 2>), 2) \
|
||||||
|
V(load1, (Load<Float64x2, 1>), 2) \
|
||||||
|
V(max, (BinaryFunc<Float64x2, Maximum, Float64x2>), 2) \
|
||||||
|
V(maxNum, (BinaryFunc<Float64x2, MaxNum, Float64x2>), 2) \
|
||||||
|
V(min, (BinaryFunc<Float64x2, Minimum, Float64x2>), 2) \
|
||||||
|
V(minNum, (BinaryFunc<Float64x2, MinNum, Float64x2>), 2) \
|
||||||
|
V(mul, (BinaryFunc<Float64x2, Mul, Float64x2>), 2) \
|
||||||
|
V(notEqual, (CompareFunc<Float64x2, NotEqual, Bool64x2>), 2) \
|
||||||
|
V(sub, (BinaryFunc<Float64x2, Sub, Float64x2>), 2)
|
||||||
|
|
||||||
|
#define FLOAT64X2_TERNARY_FUNCTION_LIST(V) \
|
||||||
|
V(replaceLane, (ReplaceLane<Float64x2>), 3) \
|
||||||
|
V(select, (Select<Float64x2, Bool64x2>), 3) \
|
||||||
|
V(store, (Store<Float64x2, 2>), 3) \
|
||||||
|
V(store1, (Store<Float64x2, 1>), 3)
|
||||||
|
|
||||||
|
#define FLOAT64X2_SHUFFLE_FUNCTION_LIST(V) \
|
||||||
|
V(swizzle, Swizzle<Float64x2>, 3) \
|
||||||
|
V(shuffle, Shuffle<Float64x2>, 4)
|
||||||
|
|
||||||
|
#define FLOAT64X2_FUNCTION_LIST(V) \
|
||||||
|
FLOAT64X2_UNARY_FUNCTION_LIST(V) \
|
||||||
|
FLOAT64X2_BINARY_FUNCTION_LIST(V) \
|
||||||
|
FLOAT64X2_TERNARY_FUNCTION_LIST(V) \
|
||||||
|
FLOAT64X2_SHUFFLE_FUNCTION_LIST(V)
|
||||||
|
|
||||||
|
// Int8x16.
|
||||||
|
#define INT8X16_UNARY_FUNCTION_LIST(V) \
|
||||||
|
V(check, (UnaryFunc<Int8x16, Identity, Int8x16>), 1) \
|
||||||
|
V(fromFloat32x4Bits, (FuncConvertBits<Float32x4, Int8x16>), 1) \
|
||||||
|
V(fromFloat64x2Bits, (FuncConvertBits<Float64x2, Int8x16>), 1) \
|
||||||
|
V(fromInt16x8Bits, (FuncConvertBits<Int16x8, Int8x16>), 1) \
|
||||||
|
V(fromInt32x4Bits, (FuncConvertBits<Int32x4, Int8x16>), 1) \
|
||||||
|
V(fromUint8x16Bits, (FuncConvertBits<Uint8x16, Int8x16>), 1) \
|
||||||
|
V(fromUint16x8Bits, (FuncConvertBits<Uint16x8, Int8x16>), 1) \
|
||||||
|
V(fromUint32x4Bits, (FuncConvertBits<Uint32x4, Int8x16>), 1) \
|
||||||
|
V(neg, (UnaryFunc<Int8x16, Neg, Int8x16>), 1) \
|
||||||
|
V(not, (UnaryFunc<Int8x16, Not, Int8x16>), 1) \
|
||||||
|
V(splat, (FuncSplat<Int8x16>), 1)
|
||||||
|
|
||||||
|
#define INT8X16_BINARY_FUNCTION_LIST(V) \
|
||||||
|
V(add, (BinaryFunc<Int8x16, Add, Int8x16>), 2) \
|
||||||
|
V(addSaturate, (BinaryFunc<Int8x16, AddSaturate, Int8x16>), 2) \
|
||||||
|
V(and, (BinaryFunc<Int8x16, And, Int8x16>), 2) \
|
||||||
|
V(equal, (CompareFunc<Int8x16, Equal, Bool8x16>), 2) \
|
||||||
|
V(extractLane, (ExtractLane<Int8x16>), 2) \
|
||||||
|
V(greaterThan, (CompareFunc<Int8x16, GreaterThan, Bool8x16>), 2) \
|
||||||
|
V(greaterThanOrEqual, (CompareFunc<Int8x16, GreaterThanOrEqual, Bool8x16>), 2) \
|
||||||
|
V(lessThan, (CompareFunc<Int8x16, LessThan, Bool8x16>), 2) \
|
||||||
|
V(lessThanOrEqual, (CompareFunc<Int8x16, LessThanOrEqual, Bool8x16>), 2) \
|
||||||
|
V(load, (Load<Int8x16, 16>), 2) \
|
||||||
|
V(mul, (BinaryFunc<Int8x16, Mul, Int8x16>), 2) \
|
||||||
|
V(notEqual, (CompareFunc<Int8x16, NotEqual, Bool8x16>), 2) \
|
||||||
|
V(or, (BinaryFunc<Int8x16, Or, Int8x16>), 2) \
|
||||||
|
V(sub, (BinaryFunc<Int8x16, Sub, Int8x16>), 2) \
|
||||||
|
V(subSaturate, (BinaryFunc<Int8x16, SubSaturate, Int8x16>), 2) \
|
||||||
|
V(shiftLeftByScalar, (BinaryScalar<Int8x16, ShiftLeft>), 2) \
|
||||||
|
V(shiftRightByScalar, (BinaryScalar<Int8x16, ShiftRightArithmetic>), 2) \
|
||||||
|
V(xor, (BinaryFunc<Int8x16, Xor, Int8x16>), 2)
|
||||||
|
|
||||||
|
#define INT8X16_TERNARY_FUNCTION_LIST(V) \
|
||||||
|
V(replaceLane, (ReplaceLane<Int8x16>), 3) \
|
||||||
|
V(select, (Select<Int8x16, Bool8x16>), 3) \
|
||||||
|
V(store, (Store<Int8x16, 16>), 3)
|
||||||
|
|
||||||
|
#define INT8X16_SHUFFLE_FUNCTION_LIST(V) \
|
||||||
|
V(swizzle, Swizzle<Int8x16>, 17) \
|
||||||
|
V(shuffle, Shuffle<Int8x16>, 18)
|
||||||
|
|
||||||
|
#define INT8X16_FUNCTION_LIST(V) \
|
||||||
|
INT8X16_UNARY_FUNCTION_LIST(V) \
|
||||||
|
INT8X16_BINARY_FUNCTION_LIST(V) \
|
||||||
|
INT8X16_TERNARY_FUNCTION_LIST(V) \
|
||||||
|
INT8X16_SHUFFLE_FUNCTION_LIST(V)
|
||||||
|
|
||||||
|
// Uint8x16.
|
||||||
|
#define UINT8X16_UNARY_FUNCTION_LIST(V) \
|
||||||
|
V(check, (UnaryFunc<Uint8x16, Identity, Uint8x16>), 1) \
|
||||||
|
V(fromFloat32x4Bits, (FuncConvertBits<Float32x4, Uint8x16>), 1) \
|
||||||
|
V(fromFloat64x2Bits, (FuncConvertBits<Float64x2, Uint8x16>), 1) \
|
||||||
|
V(fromInt8x16Bits, (FuncConvertBits<Int8x16, Uint8x16>), 1) \
|
||||||
|
V(fromInt16x8Bits, (FuncConvertBits<Int16x8, Uint8x16>), 1) \
|
||||||
|
V(fromInt32x4Bits, (FuncConvertBits<Int32x4, Uint8x16>), 1) \
|
||||||
|
V(fromUint16x8Bits, (FuncConvertBits<Uint16x8, Uint8x16>), 1) \
|
||||||
|
V(fromUint32x4Bits, (FuncConvertBits<Uint32x4, Uint8x16>), 1) \
|
||||||
|
V(neg, (UnaryFunc<Uint8x16, Neg, Uint8x16>), 1) \
|
||||||
|
V(not, (UnaryFunc<Uint8x16, Not, Uint8x16>), 1) \
|
||||||
|
V(splat, (FuncSplat<Uint8x16>), 1)
|
||||||
|
|
||||||
|
#define UINT8X16_BINARY_FUNCTION_LIST(V) \
|
||||||
|
V(add, (BinaryFunc<Uint8x16, Add, Uint8x16>), 2) \
|
||||||
|
V(addSaturate, (BinaryFunc<Uint8x16, AddSaturate, Uint8x16>), 2) \
|
||||||
|
V(and, (BinaryFunc<Uint8x16, And, Uint8x16>), 2) \
|
||||||
|
V(equal, (CompareFunc<Uint8x16, Equal, Bool8x16>), 2) \
|
||||||
|
V(extractLane, (ExtractLane<Uint8x16>), 2) \
|
||||||
|
V(greaterThan, (CompareFunc<Uint8x16, GreaterThan, Bool8x16>), 2) \
|
||||||
|
V(greaterThanOrEqual, (CompareFunc<Uint8x16, GreaterThanOrEqual, Bool8x16>), 2) \
|
||||||
|
V(lessThan, (CompareFunc<Uint8x16, LessThan, Bool8x16>), 2) \
|
||||||
|
V(lessThanOrEqual, (CompareFunc<Uint8x16, LessThanOrEqual, Bool8x16>), 2) \
|
||||||
|
V(load, (Load<Uint8x16, 16>), 2) \
|
||||||
|
V(mul, (BinaryFunc<Uint8x16, Mul, Uint8x16>), 2) \
|
||||||
|
V(notEqual, (CompareFunc<Uint8x16, NotEqual, Bool8x16>), 2) \
|
||||||
|
V(or, (BinaryFunc<Uint8x16, Or, Uint8x16>), 2) \
|
||||||
|
V(sub, (BinaryFunc<Uint8x16, Sub, Uint8x16>), 2) \
|
||||||
|
V(subSaturate, (BinaryFunc<Uint8x16, SubSaturate, Uint8x16>), 2) \
|
||||||
|
V(shiftLeftByScalar, (BinaryScalar<Uint8x16, ShiftLeft>), 2) \
|
||||||
|
V(shiftRightByScalar, (BinaryScalar<Uint8x16, ShiftRightLogical>), 2) \
|
||||||
|
V(xor, (BinaryFunc<Uint8x16, Xor, Uint8x16>), 2)
|
||||||
|
|
||||||
|
#define UINT8X16_TERNARY_FUNCTION_LIST(V) \
|
||||||
|
V(replaceLane, (ReplaceLane<Uint8x16>), 3) \
|
||||||
|
V(select, (Select<Uint8x16, Bool8x16>), 3) \
|
||||||
|
V(store, (Store<Uint8x16, 16>), 3)
|
||||||
|
|
||||||
|
#define UINT8X16_SHUFFLE_FUNCTION_LIST(V) \
|
||||||
|
V(swizzle, Swizzle<Uint8x16>, 17) \
|
||||||
|
V(shuffle, Shuffle<Uint8x16>, 18)
|
||||||
|
|
||||||
|
#define UINT8X16_FUNCTION_LIST(V) \
|
||||||
|
UINT8X16_UNARY_FUNCTION_LIST(V) \
|
||||||
|
UINT8X16_BINARY_FUNCTION_LIST(V) \
|
||||||
|
UINT8X16_TERNARY_FUNCTION_LIST(V) \
|
||||||
|
UINT8X16_SHUFFLE_FUNCTION_LIST(V)
|
||||||
|
|
||||||
|
// Int16x8.
|
||||||
|
#define INT16X8_UNARY_FUNCTION_LIST(V) \
|
||||||
|
V(check, (UnaryFunc<Int16x8, Identity, Int16x8>), 1) \
|
||||||
|
V(fromFloat32x4Bits, (FuncConvertBits<Float32x4, Int16x8>), 1) \
|
||||||
|
V(fromFloat64x2Bits, (FuncConvertBits<Float64x2, Int16x8>), 1) \
|
||||||
|
V(fromInt8x16Bits, (FuncConvertBits<Int8x16, Int16x8>), 1) \
|
||||||
|
V(fromInt32x4Bits, (FuncConvertBits<Int32x4, Int16x8>), 1) \
|
||||||
|
V(fromUint8x16Bits, (FuncConvertBits<Uint8x16, Int16x8>), 1) \
|
||||||
|
V(fromUint16x8Bits, (FuncConvertBits<Uint16x8, Int16x8>), 1) \
|
||||||
|
V(fromUint32x4Bits, (FuncConvertBits<Uint32x4, Int16x8>), 1) \
|
||||||
|
V(neg, (UnaryFunc<Int16x8, Neg, Int16x8>), 1) \
|
||||||
|
V(not, (UnaryFunc<Int16x8, Not, Int16x8>), 1) \
|
||||||
|
V(splat, (FuncSplat<Int16x8>), 1)
|
||||||
|
|
||||||
|
#define INT16X8_BINARY_FUNCTION_LIST(V) \
|
||||||
|
V(add, (BinaryFunc<Int16x8, Add, Int16x8>), 2) \
|
||||||
|
V(addSaturate, (BinaryFunc<Int16x8, AddSaturate, Int16x8>), 2) \
|
||||||
|
V(and, (BinaryFunc<Int16x8, And, Int16x8>), 2) \
|
||||||
|
V(equal, (CompareFunc<Int16x8, Equal, Bool16x8>), 2) \
|
||||||
|
V(extractLane, (ExtractLane<Int16x8>), 2) \
|
||||||
|
V(greaterThan, (CompareFunc<Int16x8, GreaterThan, Bool16x8>), 2) \
|
||||||
|
V(greaterThanOrEqual, (CompareFunc<Int16x8, GreaterThanOrEqual, Bool16x8>), 2) \
|
||||||
|
V(lessThan, (CompareFunc<Int16x8, LessThan, Bool16x8>), 2) \
|
||||||
|
V(lessThanOrEqual, (CompareFunc<Int16x8, LessThanOrEqual, Bool16x8>), 2) \
|
||||||
|
V(load, (Load<Int16x8, 8>), 2) \
|
||||||
|
V(mul, (BinaryFunc<Int16x8, Mul, Int16x8>), 2) \
|
||||||
|
V(notEqual, (CompareFunc<Int16x8, NotEqual, Bool16x8>), 2) \
|
||||||
|
V(or, (BinaryFunc<Int16x8, Or, Int16x8>), 2) \
|
||||||
|
V(sub, (BinaryFunc<Int16x8, Sub, Int16x8>), 2) \
|
||||||
|
V(subSaturate, (BinaryFunc<Int16x8, SubSaturate, Int16x8>), 2) \
|
||||||
|
V(shiftLeftByScalar, (BinaryScalar<Int16x8, ShiftLeft>), 2) \
|
||||||
|
V(shiftRightByScalar, (BinaryScalar<Int16x8, ShiftRightArithmetic>), 2) \
|
||||||
|
V(xor, (BinaryFunc<Int16x8, Xor, Int16x8>), 2)
|
||||||
|
|
||||||
|
#define INT16X8_TERNARY_FUNCTION_LIST(V) \
|
||||||
|
V(replaceLane, (ReplaceLane<Int16x8>), 3) \
|
||||||
|
V(select, (Select<Int16x8, Bool16x8>), 3) \
|
||||||
|
V(store, (Store<Int16x8, 8>), 3)
|
||||||
|
|
||||||
|
#define INT16X8_SHUFFLE_FUNCTION_LIST(V) \
|
||||||
|
V(swizzle, Swizzle<Int16x8>, 9) \
|
||||||
|
V(shuffle, Shuffle<Int16x8>, 10)
|
||||||
|
|
||||||
|
#define INT16X8_FUNCTION_LIST(V) \
|
||||||
|
INT16X8_UNARY_FUNCTION_LIST(V) \
|
||||||
|
INT16X8_BINARY_FUNCTION_LIST(V) \
|
||||||
|
INT16X8_TERNARY_FUNCTION_LIST(V) \
|
||||||
|
INT16X8_SHUFFLE_FUNCTION_LIST(V)
|
||||||
|
|
||||||
|
// Uint16x8.
|
||||||
|
#define UINT16X8_UNARY_FUNCTION_LIST(V) \
|
||||||
|
V(check, (UnaryFunc<Uint16x8, Identity, Uint16x8>), 1) \
|
||||||
|
V(fromFloat32x4Bits, (FuncConvertBits<Float32x4, Uint16x8>), 1) \
|
||||||
|
V(fromFloat64x2Bits, (FuncConvertBits<Float64x2, Uint16x8>), 1) \
|
||||||
|
V(fromInt8x16Bits, (FuncConvertBits<Int8x16, Uint16x8>), 1) \
|
||||||
|
V(fromInt16x8Bits, (FuncConvertBits<Int16x8, Uint16x8>), 1) \
|
||||||
|
V(fromInt32x4Bits, (FuncConvertBits<Int32x4, Uint16x8>), 1) \
|
||||||
|
V(fromUint8x16Bits, (FuncConvertBits<Uint8x16, Uint16x8>), 1) \
|
||||||
|
V(fromUint32x4Bits, (FuncConvertBits<Uint32x4, Uint16x8>), 1) \
|
||||||
|
V(neg, (UnaryFunc<Uint16x8, Neg, Uint16x8>), 1) \
|
||||||
|
V(not, (UnaryFunc<Uint16x8, Not, Uint16x8>), 1) \
|
||||||
|
V(splat, (FuncSplat<Uint16x8>), 1)
|
||||||
|
|
||||||
|
#define UINT16X8_BINARY_FUNCTION_LIST(V) \
|
||||||
|
V(add, (BinaryFunc<Uint16x8, Add, Uint16x8>), 2) \
|
||||||
|
V(addSaturate, (BinaryFunc<Uint16x8, AddSaturate, Uint16x8>), 2) \
|
||||||
|
V(and, (BinaryFunc<Uint16x8, And, Uint16x8>), 2) \
|
||||||
|
V(equal, (CompareFunc<Uint16x8, Equal, Bool16x8>), 2) \
|
||||||
|
V(extractLane, (ExtractLane<Uint16x8>), 2) \
|
||||||
|
V(greaterThan, (CompareFunc<Uint16x8, GreaterThan, Bool16x8>), 2) \
|
||||||
|
V(greaterThanOrEqual, (CompareFunc<Uint16x8, GreaterThanOrEqual, Bool16x8>), 2) \
|
||||||
|
V(lessThan, (CompareFunc<Uint16x8, LessThan, Bool16x8>), 2) \
|
||||||
|
V(lessThanOrEqual, (CompareFunc<Uint16x8, LessThanOrEqual, Bool16x8>), 2) \
|
||||||
|
V(load, (Load<Uint16x8, 8>), 2) \
|
||||||
|
V(mul, (BinaryFunc<Uint16x8, Mul, Uint16x8>), 2) \
|
||||||
|
V(notEqual, (CompareFunc<Uint16x8, NotEqual, Bool16x8>), 2) \
|
||||||
|
V(or, (BinaryFunc<Uint16x8, Or, Uint16x8>), 2) \
|
||||||
|
V(sub, (BinaryFunc<Uint16x8, Sub, Uint16x8>), 2) \
|
||||||
|
V(subSaturate, (BinaryFunc<Uint16x8, SubSaturate, Uint16x8>), 2) \
|
||||||
|
V(shiftLeftByScalar, (BinaryScalar<Uint16x8, ShiftLeft>), 2) \
|
||||||
|
V(shiftRightByScalar, (BinaryScalar<Uint16x8, ShiftRightLogical>), 2) \
|
||||||
|
V(xor, (BinaryFunc<Uint16x8, Xor, Uint16x8>), 2)
|
||||||
|
|
||||||
|
#define UINT16X8_TERNARY_FUNCTION_LIST(V) \
|
||||||
|
V(replaceLane, (ReplaceLane<Uint16x8>), 3) \
|
||||||
|
V(select, (Select<Uint16x8, Bool16x8>), 3) \
|
||||||
|
V(store, (Store<Uint16x8, 8>), 3)
|
||||||
|
|
||||||
|
#define UINT16X8_SHUFFLE_FUNCTION_LIST(V) \
|
||||||
|
V(swizzle, Swizzle<Uint16x8>, 9) \
|
||||||
|
V(shuffle, Shuffle<Uint16x8>, 10)
|
||||||
|
|
||||||
|
#define UINT16X8_FUNCTION_LIST(V) \
|
||||||
|
UINT16X8_UNARY_FUNCTION_LIST(V) \
|
||||||
|
UINT16X8_BINARY_FUNCTION_LIST(V) \
|
||||||
|
UINT16X8_TERNARY_FUNCTION_LIST(V) \
|
||||||
|
UINT16X8_SHUFFLE_FUNCTION_LIST(V)
|
||||||
|
|
||||||
|
// Int32x4.
|
||||||
|
#define INT32X4_UNARY_FUNCTION_LIST(V) \
|
||||||
|
V(check, (UnaryFunc<Int32x4, Identity, Int32x4>), 1) \
|
||||||
|
V(fromFloat32x4, (FuncConvert<Float32x4, Int32x4>), 1) \
|
||||||
|
V(fromFloat32x4Bits, (FuncConvertBits<Float32x4, Int32x4>), 1) \
|
||||||
|
V(fromFloat64x2Bits, (FuncConvertBits<Float64x2, Int32x4>), 1) \
|
||||||
|
V(fromInt8x16Bits, (FuncConvertBits<Int8x16, Int32x4>), 1) \
|
||||||
|
V(fromInt16x8Bits, (FuncConvertBits<Int16x8, Int32x4>), 1) \
|
||||||
|
V(fromUint8x16Bits, (FuncConvertBits<Uint8x16, Int32x4>), 1) \
|
||||||
|
V(fromUint16x8Bits, (FuncConvertBits<Uint16x8, Int32x4>), 1) \
|
||||||
|
V(fromUint32x4Bits, (FuncConvertBits<Uint32x4, Int32x4>), 1) \
|
||||||
|
V(neg, (UnaryFunc<Int32x4, Neg, Int32x4>), 1) \
|
||||||
|
V(not, (UnaryFunc<Int32x4, Not, Int32x4>), 1) \
|
||||||
|
V(splat, (FuncSplat<Int32x4>), 0)
|
||||||
|
|
||||||
|
#define INT32X4_BINARY_FUNCTION_LIST(V) \
|
||||||
|
V(add, (BinaryFunc<Int32x4, Add, Int32x4>), 2) \
|
||||||
|
V(and, (BinaryFunc<Int32x4, And, Int32x4>), 2) \
|
||||||
|
V(equal, (CompareFunc<Int32x4, Equal, Bool32x4>), 2) \
|
||||||
|
V(extractLane, (ExtractLane<Int32x4>), 2) \
|
||||||
|
V(greaterThan, (CompareFunc<Int32x4, GreaterThan, Bool32x4>), 2) \
|
||||||
|
V(greaterThanOrEqual, (CompareFunc<Int32x4, GreaterThanOrEqual, Bool32x4>), 2) \
|
||||||
|
V(lessThan, (CompareFunc<Int32x4, LessThan, Bool32x4>), 2) \
|
||||||
|
V(lessThanOrEqual, (CompareFunc<Int32x4, LessThanOrEqual, Bool32x4>), 2) \
|
||||||
|
V(load, (Load<Int32x4, 4>), 2) \
|
||||||
|
V(load3, (Load<Int32x4, 3>), 2) \
|
||||||
|
V(load2, (Load<Int32x4, 2>), 2) \
|
||||||
|
V(load1, (Load<Int32x4, 1>), 2) \
|
||||||
|
V(mul, (BinaryFunc<Int32x4, Mul, Int32x4>), 2) \
|
||||||
|
V(notEqual, (CompareFunc<Int32x4, NotEqual, Bool32x4>), 2) \
|
||||||
|
V(or, (BinaryFunc<Int32x4, Or, Int32x4>), 2) \
|
||||||
|
V(sub, (BinaryFunc<Int32x4, Sub, Int32x4>), 2) \
|
||||||
|
V(shiftLeftByScalar, (BinaryScalar<Int32x4, ShiftLeft>), 2) \
|
||||||
|
V(shiftRightByScalar, (BinaryScalar<Int32x4, ShiftRightArithmetic>), 2) \
|
||||||
|
V(xor, (BinaryFunc<Int32x4, Xor, Int32x4>), 2)
|
||||||
|
|
||||||
|
#define INT32X4_TERNARY_FUNCTION_LIST(V) \
|
||||||
|
V(replaceLane, (ReplaceLane<Int32x4>), 3) \
|
||||||
|
V(select, (Select<Int32x4, Bool32x4>), 3) \
|
||||||
|
V(store, (Store<Int32x4, 4>), 3) \
|
||||||
|
V(store3, (Store<Int32x4, 3>), 3) \
|
||||||
|
V(store2, (Store<Int32x4, 2>), 3) \
|
||||||
|
V(store1, (Store<Int32x4, 1>), 3)
|
||||||
|
|
||||||
|
#define INT32X4_SHUFFLE_FUNCTION_LIST(V) \
|
||||||
|
V(swizzle, Swizzle<Int32x4>, 5) \
|
||||||
|
V(shuffle, Shuffle<Int32x4>, 6)
|
||||||
|
|
||||||
|
#define INT32X4_FUNCTION_LIST(V) \
|
||||||
|
INT32X4_UNARY_FUNCTION_LIST(V) \
|
||||||
|
INT32X4_BINARY_FUNCTION_LIST(V) \
|
||||||
|
INT32X4_TERNARY_FUNCTION_LIST(V) \
|
||||||
|
INT32X4_SHUFFLE_FUNCTION_LIST(V)
|
||||||
|
|
||||||
|
// Uint32x4.
|
||||||
|
#define UINT32X4_UNARY_FUNCTION_LIST(V) \
|
||||||
|
V(check, (UnaryFunc<Uint32x4, Identity, Uint32x4>), 1) \
|
||||||
|
V(fromFloat32x4, (FuncConvert<Float32x4, Uint32x4>), 1) \
|
||||||
|
V(fromFloat32x4Bits, (FuncConvertBits<Float32x4, Uint32x4>), 1) \
|
||||||
|
V(fromFloat64x2Bits, (FuncConvertBits<Float64x2, Uint32x4>), 1) \
|
||||||
|
V(fromInt8x16Bits, (FuncConvertBits<Int8x16, Uint32x4>), 1) \
|
||||||
|
V(fromInt16x8Bits, (FuncConvertBits<Int16x8, Uint32x4>), 1) \
|
||||||
|
V(fromInt32x4Bits, (FuncConvertBits<Int32x4, Uint32x4>), 1) \
|
||||||
|
V(fromUint8x16Bits, (FuncConvertBits<Uint8x16, Uint32x4>), 1) \
|
||||||
|
V(fromUint16x8Bits, (FuncConvertBits<Uint16x8, Uint32x4>), 1) \
|
||||||
|
V(neg, (UnaryFunc<Uint32x4, Neg, Uint32x4>), 1) \
|
||||||
|
V(not, (UnaryFunc<Uint32x4, Not, Uint32x4>), 1) \
|
||||||
|
V(splat, (FuncSplat<Uint32x4>), 0)
|
||||||
|
|
||||||
|
#define UINT32X4_BINARY_FUNCTION_LIST(V) \
|
||||||
|
V(add, (BinaryFunc<Uint32x4, Add, Uint32x4>), 2) \
|
||||||
|
V(and, (BinaryFunc<Uint32x4, And, Uint32x4>), 2) \
|
||||||
|
V(equal, (CompareFunc<Uint32x4, Equal, Bool32x4>), 2) \
|
||||||
|
V(extractLane, (ExtractLane<Uint32x4>), 2) \
|
||||||
|
V(greaterThan, (CompareFunc<Uint32x4, GreaterThan, Bool32x4>), 2) \
|
||||||
|
V(greaterThanOrEqual, (CompareFunc<Uint32x4, GreaterThanOrEqual, Bool32x4>), 2) \
|
||||||
|
V(lessThan, (CompareFunc<Uint32x4, LessThan, Bool32x4>), 2) \
|
||||||
|
V(lessThanOrEqual, (CompareFunc<Uint32x4, LessThanOrEqual, Bool32x4>), 2) \
|
||||||
|
V(load, (Load<Uint32x4, 4>), 2) \
|
||||||
|
V(load3, (Load<Uint32x4, 3>), 2) \
|
||||||
|
V(load2, (Load<Uint32x4, 2>), 2) \
|
||||||
|
V(load1, (Load<Uint32x4, 1>), 2) \
|
||||||
|
V(mul, (BinaryFunc<Uint32x4, Mul, Uint32x4>), 2) \
|
||||||
|
V(notEqual, (CompareFunc<Uint32x4, NotEqual, Bool32x4>), 2) \
|
||||||
|
V(or, (BinaryFunc<Uint32x4, Or, Uint32x4>), 2) \
|
||||||
|
V(sub, (BinaryFunc<Uint32x4, Sub, Uint32x4>), 2) \
|
||||||
|
V(shiftLeftByScalar, (BinaryScalar<Uint32x4, ShiftLeft>), 2) \
|
||||||
|
V(shiftRightByScalar, (BinaryScalar<Uint32x4, ShiftRightLogical>), 2) \
|
||||||
|
V(xor, (BinaryFunc<Uint32x4, Xor, Uint32x4>), 2)
|
||||||
|
|
||||||
|
#define UINT32X4_TERNARY_FUNCTION_LIST(V) \
|
||||||
|
V(replaceLane, (ReplaceLane<Uint32x4>), 3) \
|
||||||
|
V(select, (Select<Uint32x4, Bool32x4>), 3) \
|
||||||
|
V(store, (Store<Uint32x4, 4>), 3) \
|
||||||
|
V(store3, (Store<Uint32x4, 3>), 3) \
|
||||||
|
V(store2, (Store<Uint32x4, 2>), 3) \
|
||||||
|
V(store1, (Store<Uint32x4, 1>), 3)
|
||||||
|
|
||||||
|
#define UINT32X4_SHUFFLE_FUNCTION_LIST(V) \
|
||||||
|
V(swizzle, Swizzle<Uint32x4>, 5) \
|
||||||
|
V(shuffle, Shuffle<Uint32x4>, 6)
|
||||||
|
|
||||||
|
#define UINT32X4_FUNCTION_LIST(V) \
|
||||||
|
UINT32X4_UNARY_FUNCTION_LIST(V) \
|
||||||
|
UINT32X4_BINARY_FUNCTION_LIST(V) \
|
||||||
|
UINT32X4_TERNARY_FUNCTION_LIST(V) \
|
||||||
|
UINT32X4_SHUFFLE_FUNCTION_LIST(V)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The FOREACH macros below partition all of the SIMD operations into disjoint
|
||||||
|
* sets.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Operations available on all SIMD types. Mixed arity.
|
||||||
|
#define FOREACH_COMMON_SIMD_OP(_) \
|
||||||
|
_(extractLane) \
|
||||||
|
_(replaceLane) \
|
||||||
|
_(check) \
|
||||||
|
_(splat)
|
||||||
|
|
||||||
|
// Lanewise operations available on numeric SIMD types.
|
||||||
|
// Include lane-wise select here since it is not arithmetic and defined on
|
||||||
|
// numeric types too.
|
||||||
|
#define FOREACH_LANE_SIMD_OP(_) \
|
||||||
|
_(select) \
|
||||||
|
_(swizzle) \
|
||||||
|
_(shuffle)
|
||||||
|
|
||||||
|
// Memory operations available on numeric SIMD types.
|
||||||
|
#define FOREACH_MEMORY_SIMD_OP(_) \
|
||||||
|
_(load) \
|
||||||
|
_(store)
|
||||||
|
|
||||||
|
// Memory operations available on numeric X4 SIMD types.
|
||||||
|
#define FOREACH_MEMORY_X4_SIMD_OP(_) \
|
||||||
|
_(load1) \
|
||||||
|
_(load2) \
|
||||||
|
_(load3) \
|
||||||
|
_(store1) \
|
||||||
|
_(store2) \
|
||||||
|
_(store3)
|
||||||
|
|
||||||
|
// Unary operations on Bool vectors.
|
||||||
|
#define FOREACH_BOOL_SIMD_UNOP(_) \
|
||||||
|
_(allTrue) \
|
||||||
|
_(anyTrue)
|
||||||
|
|
||||||
|
// Unary bitwise SIMD operators defined on all integer and boolean SIMD types.
|
||||||
|
#define FOREACH_BITWISE_SIMD_UNOP(_) \
|
||||||
|
_(not)
|
||||||
|
|
||||||
|
// Binary bitwise SIMD operators defined on all integer and boolean SIMD types.
|
||||||
|
#define FOREACH_BITWISE_SIMD_BINOP(_) \
|
||||||
|
_(and) \
|
||||||
|
_(or) \
|
||||||
|
_(xor)
|
||||||
|
|
||||||
|
// Bitwise shifts defined on integer SIMD types.
|
||||||
|
#define FOREACH_SHIFT_SIMD_OP(_) \
|
||||||
|
_(shiftLeftByScalar) \
|
||||||
|
_(shiftRightByScalar)
|
||||||
|
|
||||||
|
// Unary arithmetic operators defined on numeric SIMD types.
|
||||||
|
#define FOREACH_NUMERIC_SIMD_UNOP(_) \
|
||||||
|
_(neg)
|
||||||
|
|
||||||
|
// Binary arithmetic operators defined on numeric SIMD types.
|
||||||
|
#define FOREACH_NUMERIC_SIMD_BINOP(_) \
|
||||||
|
_(add) \
|
||||||
|
_(sub) \
|
||||||
|
_(mul)
|
||||||
|
|
||||||
|
// Unary arithmetic operators defined on floating point SIMD types.
|
||||||
|
#define FOREACH_FLOAT_SIMD_UNOP(_) \
|
||||||
|
_(abs) \
|
||||||
|
_(sqrt) \
|
||||||
|
_(reciprocalApproximation) \
|
||||||
|
_(reciprocalSqrtApproximation)
|
||||||
|
|
||||||
|
// Binary arithmetic operators defined on floating point SIMD types.
|
||||||
|
#define FOREACH_FLOAT_SIMD_BINOP(_) \
|
||||||
|
_(div) \
|
||||||
|
_(max) \
|
||||||
|
_(min) \
|
||||||
|
_(maxNum) \
|
||||||
|
_(minNum)
|
||||||
|
|
||||||
|
// Binary operations on small integer (< 32 bits) vectors.
|
||||||
|
#define FOREACH_SMINT_SIMD_BINOP(_) \
|
||||||
|
_(addSaturate) \
|
||||||
|
_(subSaturate)
|
||||||
|
|
||||||
|
// Comparison operators defined on numeric SIMD types.
|
||||||
|
#define FOREACH_COMP_SIMD_OP(_) \
|
||||||
|
_(lessThan) \
|
||||||
|
_(lessThanOrEqual) \
|
||||||
|
_(equal) \
|
||||||
|
_(notEqual) \
|
||||||
|
_(greaterThan) \
|
||||||
|
_(greaterThanOrEqual)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* All SIMD operations, excluding casts.
|
||||||
|
*/
|
||||||
|
#define FORALL_SIMD_NONCAST_OP(_) \
|
||||||
|
FOREACH_COMMON_SIMD_OP(_) \
|
||||||
|
FOREACH_LANE_SIMD_OP(_) \
|
||||||
|
FOREACH_MEMORY_SIMD_OP(_) \
|
||||||
|
FOREACH_MEMORY_X4_SIMD_OP(_) \
|
||||||
|
FOREACH_BOOL_SIMD_UNOP(_) \
|
||||||
|
FOREACH_BITWISE_SIMD_UNOP(_) \
|
||||||
|
FOREACH_BITWISE_SIMD_BINOP(_) \
|
||||||
|
FOREACH_SHIFT_SIMD_OP(_) \
|
||||||
|
FOREACH_NUMERIC_SIMD_UNOP(_) \
|
||||||
|
FOREACH_NUMERIC_SIMD_BINOP(_) \
|
||||||
|
FOREACH_FLOAT_SIMD_UNOP(_) \
|
||||||
|
FOREACH_FLOAT_SIMD_BINOP(_) \
|
||||||
|
FOREACH_SMINT_SIMD_BINOP(_) \
|
||||||
|
FOREACH_COMP_SIMD_OP(_)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* All operations on integer SIMD types, excluding casts and
|
||||||
|
* FOREACH_MEMORY_X4_OP.
|
||||||
|
*/
|
||||||
|
#define FORALL_INT_SIMD_OP(_) \
|
||||||
|
FOREACH_COMMON_SIMD_OP(_) \
|
||||||
|
FOREACH_LANE_SIMD_OP(_) \
|
||||||
|
FOREACH_MEMORY_SIMD_OP(_) \
|
||||||
|
FOREACH_BITWISE_SIMD_UNOP(_) \
|
||||||
|
FOREACH_BITWISE_SIMD_BINOP(_) \
|
||||||
|
FOREACH_SHIFT_SIMD_OP(_) \
|
||||||
|
FOREACH_NUMERIC_SIMD_UNOP(_) \
|
||||||
|
FOREACH_NUMERIC_SIMD_BINOP(_) \
|
||||||
|
FOREACH_COMP_SIMD_OP(_)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* All operations on floating point SIMD types, excluding casts and
|
||||||
|
* FOREACH_MEMORY_X4_OP.
|
||||||
|
*/
|
||||||
|
#define FORALL_FLOAT_SIMD_OP(_) \
|
||||||
|
FOREACH_COMMON_SIMD_OP(_) \
|
||||||
|
FOREACH_LANE_SIMD_OP(_) \
|
||||||
|
FOREACH_MEMORY_SIMD_OP(_) \
|
||||||
|
FOREACH_NUMERIC_SIMD_UNOP(_) \
|
||||||
|
FOREACH_NUMERIC_SIMD_BINOP(_) \
|
||||||
|
FOREACH_FLOAT_SIMD_UNOP(_) \
|
||||||
|
FOREACH_FLOAT_SIMD_BINOP(_) \
|
||||||
|
FOREACH_COMP_SIMD_OP(_)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* All operations on Bool SIMD types.
|
||||||
|
*
|
||||||
|
* These types don't have casts, so no need to specialize.
|
||||||
|
*/
|
||||||
|
#define FORALL_BOOL_SIMD_OP(_) \
|
||||||
|
FOREACH_COMMON_SIMD_OP(_) \
|
||||||
|
FOREACH_BOOL_SIMD_UNOP(_) \
|
||||||
|
FOREACH_BITWISE_SIMD_UNOP(_) \
|
||||||
|
FOREACH_BITWISE_SIMD_BINOP(_)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The sets of cast operations are listed per type below.
|
||||||
|
*
|
||||||
|
* These sets are not disjoint.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define FOREACH_INT8X16_SIMD_CAST(_) \
|
||||||
|
_(fromFloat32x4Bits) \
|
||||||
|
_(fromFloat64x2Bits) \
|
||||||
|
_(fromInt16x8Bits) \
|
||||||
|
_(fromInt32x4Bits)
|
||||||
|
|
||||||
|
#define FOREACH_INT16X8_SIMD_CAST(_) \
|
||||||
|
_(fromFloat32x4Bits) \
|
||||||
|
_(fromFloat64x2Bits) \
|
||||||
|
_(fromInt8x16Bits) \
|
||||||
|
_(fromInt32x4Bits)
|
||||||
|
|
||||||
|
#define FOREACH_INT32X4_SIMD_CAST(_) \
|
||||||
|
_(fromFloat32x4) \
|
||||||
|
_(fromFloat32x4Bits) \
|
||||||
|
_(fromFloat64x2Bits) \
|
||||||
|
_(fromInt8x16Bits) \
|
||||||
|
_(fromInt16x8Bits)
|
||||||
|
|
||||||
|
#define FOREACH_FLOAT32X4_SIMD_CAST(_)\
|
||||||
|
_(fromFloat64x2Bits) \
|
||||||
|
_(fromInt8x16Bits) \
|
||||||
|
_(fromInt16x8Bits) \
|
||||||
|
_(fromInt32x4) \
|
||||||
|
_(fromInt32x4Bits)
|
||||||
|
|
||||||
|
#define FOREACH_FLOAT64X2_SIMD_CAST(_)\
|
||||||
|
_(fromFloat32x4Bits) \
|
||||||
|
_(fromInt8x16Bits) \
|
||||||
|
_(fromInt16x8Bits) \
|
||||||
|
_(fromInt32x4Bits)
|
||||||
|
|
||||||
|
// All operations on Int32x4.
|
||||||
|
#define FORALL_INT32X4_SIMD_OP(_) \
|
||||||
|
FORALL_INT_SIMD_OP(_) \
|
||||||
|
FOREACH_MEMORY_X4_SIMD_OP(_) \
|
||||||
|
FOREACH_INT32X4_SIMD_CAST(_)
|
||||||
|
|
||||||
|
// All operations on Float32X4
|
||||||
|
#define FORALL_FLOAT32X4_SIMD_OP(_) \
|
||||||
|
FORALL_FLOAT_SIMD_OP(_) \
|
||||||
|
FOREACH_MEMORY_X4_SIMD_OP(_) \
|
||||||
|
FOREACH_FLOAT32X4_SIMD_CAST(_)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* All SIMD operations assuming only 32x4 types exist.
|
||||||
|
* This is used in the current asm.js impl.
|
||||||
|
*/
|
||||||
|
#define FORALL_SIMD_ASMJS_OP(_) \
|
||||||
|
FORALL_SIMD_NONCAST_OP(_) \
|
||||||
|
_(fromFloat32x4) \
|
||||||
|
_(fromFloat32x4Bits) \
|
||||||
|
_(fromInt8x16Bits) \
|
||||||
|
_(fromInt16x8Bits) \
|
||||||
|
_(fromInt32x4) \
|
||||||
|
_(fromInt32x4Bits) \
|
||||||
|
_(fromUint8x16Bits) \
|
||||||
|
_(fromUint16x8Bits) \
|
||||||
|
_(fromUint32x4) \
|
||||||
|
_(fromUint32x4Bits)
|
||||||
|
|
||||||
|
// All operations on Int8x16 or Uint8x16 in the asm.js world.
|
||||||
|
// Note: this does not include conversions and casts to/from Uint8x16 because
|
||||||
|
// this list is shared between Int8x16 and Uint8x16.
|
||||||
|
#define FORALL_INT8X16_ASMJS_OP(_) \
|
||||||
|
FORALL_INT_SIMD_OP(_) \
|
||||||
|
FOREACH_SMINT_SIMD_BINOP(_) \
|
||||||
|
_(fromInt16x8Bits) \
|
||||||
|
_(fromInt32x4Bits) \
|
||||||
|
_(fromFloat32x4Bits)
|
||||||
|
|
||||||
|
// All operations on Int16x8 or Uint16x8 in the asm.js world.
|
||||||
|
// Note: this does not include conversions and casts to/from Uint16x8 because
|
||||||
|
// this list is shared between Int16x8 and Uint16x8.
|
||||||
|
#define FORALL_INT16X8_ASMJS_OP(_) \
|
||||||
|
FORALL_INT_SIMD_OP(_) \
|
||||||
|
FOREACH_SMINT_SIMD_BINOP(_) \
|
||||||
|
_(fromInt8x16Bits) \
|
||||||
|
_(fromInt32x4Bits) \
|
||||||
|
_(fromFloat32x4Bits)
|
||||||
|
|
||||||
|
// All operations on Int32x4 or Uint32x4 in the asm.js world.
|
||||||
|
// Note: this does not include conversions and casts to/from Uint32x4 because
|
||||||
|
// this list is shared between Int32x4 and Uint32x4.
|
||||||
|
#define FORALL_INT32X4_ASMJS_OP(_) \
|
||||||
|
FORALL_INT_SIMD_OP(_) \
|
||||||
|
FOREACH_MEMORY_X4_SIMD_OP(_) \
|
||||||
|
_(fromInt8x16Bits) \
|
||||||
|
_(fromInt16x8Bits) \
|
||||||
|
_(fromFloat32x4) \
|
||||||
|
_(fromFloat32x4Bits)
|
||||||
|
|
||||||
|
// All operations on Float32X4 in the asm.js world.
|
||||||
|
#define FORALL_FLOAT32X4_ASMJS_OP(_) \
|
||||||
|
FORALL_FLOAT_SIMD_OP(_) \
|
||||||
|
FOREACH_MEMORY_X4_SIMD_OP(_) \
|
||||||
|
_(fromInt8x16Bits) \
|
||||||
|
_(fromInt16x8Bits) \
|
||||||
|
_(fromInt32x4Bits) \
|
||||||
|
_(fromInt32x4) \
|
||||||
|
_(fromUint32x4)
|
||||||
|
|
||||||
|
namespace js {
|
||||||
|
|
||||||
|
// Complete set of SIMD types.
|
||||||
|
// It must be kept in sync with the enumeration of values in
|
||||||
|
// TypedObjectConstants.h; in particular we need to ensure that Count is
|
||||||
|
// appropriately set with respect to the number of actual types.
|
||||||
|
enum class SimdType {
|
||||||
|
Int8x16 = JS_SIMDTYPEREPR_INT8X16,
|
||||||
|
Int16x8 = JS_SIMDTYPEREPR_INT16X8,
|
||||||
|
Int32x4 = JS_SIMDTYPEREPR_INT32X4,
|
||||||
|
Uint8x16 = JS_SIMDTYPEREPR_UINT8X16,
|
||||||
|
Uint16x8 = JS_SIMDTYPEREPR_UINT16X8,
|
||||||
|
Uint32x4 = JS_SIMDTYPEREPR_UINT32X4,
|
||||||
|
Float32x4 = JS_SIMDTYPEREPR_FLOAT32X4,
|
||||||
|
Float64x2 = JS_SIMDTYPEREPR_FLOAT64X2,
|
||||||
|
Bool8x16 = JS_SIMDTYPEREPR_BOOL8X16,
|
||||||
|
Bool16x8 = JS_SIMDTYPEREPR_BOOL16X8,
|
||||||
|
Bool32x4 = JS_SIMDTYPEREPR_BOOL32X4,
|
||||||
|
Bool64x2 = JS_SIMDTYPEREPR_BOOL64X2,
|
||||||
|
Count
|
||||||
|
};
|
||||||
|
|
||||||
|
// The integer SIMD types have a lot of operations that do the exact same thing
|
||||||
|
// for signed and unsigned integer types. Sometimes it is simpler to treat
|
||||||
|
// signed and unsigned integer SIMD types as the same type, using a SimdSign to
|
||||||
|
// distinguish the few cases where there is a difference.
|
||||||
|
enum class SimdSign {
|
||||||
|
// Signedness is not applicable to this type. (i.e., Float or Bool).
|
||||||
|
NotApplicable,
|
||||||
|
// Treat as an unsigned integer with a range 0 .. 2^N-1.
|
||||||
|
Unsigned,
|
||||||
|
// Treat as a signed integer in two's complement encoding.
|
||||||
|
Signed,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Get the signedness of a SIMD type.
|
||||||
|
inline SimdSign
|
||||||
|
GetSimdSign(SimdType t)
|
||||||
|
{
|
||||||
|
switch(t) {
|
||||||
|
case SimdType::Int8x16:
|
||||||
|
case SimdType::Int16x8:
|
||||||
|
case SimdType::Int32x4:
|
||||||
|
return SimdSign::Signed;
|
||||||
|
|
||||||
|
case SimdType::Uint8x16:
|
||||||
|
case SimdType::Uint16x8:
|
||||||
|
case SimdType::Uint32x4:
|
||||||
|
return SimdSign::Unsigned;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return SimdSign::NotApplicable;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline bool
|
||||||
|
IsSignedIntSimdType(SimdType type)
|
||||||
|
{
|
||||||
|
return GetSimdSign(type) == SimdSign::Signed;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the boolean SIMD type with the same shape as t.
|
||||||
|
//
|
||||||
|
// This is the result type of a comparison operation, and it can also be used to
|
||||||
|
// identify the geometry of a SIMD type.
|
||||||
|
inline SimdType
|
||||||
|
GetBooleanSimdType(SimdType t)
|
||||||
|
{
|
||||||
|
switch(t) {
|
||||||
|
case SimdType::Int8x16:
|
||||||
|
case SimdType::Uint8x16:
|
||||||
|
case SimdType::Bool8x16:
|
||||||
|
return SimdType::Bool8x16;
|
||||||
|
|
||||||
|
case SimdType::Int16x8:
|
||||||
|
case SimdType::Uint16x8:
|
||||||
|
case SimdType::Bool16x8:
|
||||||
|
return SimdType::Bool16x8;
|
||||||
|
|
||||||
|
case SimdType::Int32x4:
|
||||||
|
case SimdType::Uint32x4:
|
||||||
|
case SimdType::Float32x4:
|
||||||
|
case SimdType::Bool32x4:
|
||||||
|
return SimdType::Bool32x4;
|
||||||
|
|
||||||
|
case SimdType::Float64x2:
|
||||||
|
case SimdType::Bool64x2:
|
||||||
|
return SimdType::Bool64x2;
|
||||||
|
|
||||||
|
case SimdType::Count:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("Bad SIMD type");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the number of lanes in a SIMD type.
|
||||||
|
inline unsigned
|
||||||
|
GetSimdLanes(SimdType t)
|
||||||
|
{
|
||||||
|
switch(t) {
|
||||||
|
case SimdType::Int8x16:
|
||||||
|
case SimdType::Uint8x16:
|
||||||
|
case SimdType::Bool8x16:
|
||||||
|
return 16;
|
||||||
|
|
||||||
|
case SimdType::Int16x8:
|
||||||
|
case SimdType::Uint16x8:
|
||||||
|
case SimdType::Bool16x8:
|
||||||
|
return 8;
|
||||||
|
|
||||||
|
case SimdType::Int32x4:
|
||||||
|
case SimdType::Uint32x4:
|
||||||
|
case SimdType::Float32x4:
|
||||||
|
case SimdType::Bool32x4:
|
||||||
|
return 4;
|
||||||
|
|
||||||
|
case SimdType::Float64x2:
|
||||||
|
case SimdType::Bool64x2:
|
||||||
|
return 2;
|
||||||
|
|
||||||
|
case SimdType::Count:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
MOZ_MAKE_COMPILER_ASSUME_IS_UNREACHABLE("Bad SIMD type");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Complete set of SIMD operations.
|
||||||
|
//
|
||||||
|
// No SIMD types implement all of these operations.
|
||||||
|
//
|
||||||
|
// C++ defines keywords and/or/xor/not, so prepend Fn_ to all named functions to
|
||||||
|
// avoid clashes.
|
||||||
|
//
|
||||||
|
// Note: because of a gcc < v4.8's compiler bug, uint8_t can't be used as the
|
||||||
|
// storage class here. See bug 1243810. See also
|
||||||
|
// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=64037 .
|
||||||
|
enum class SimdOperation {
|
||||||
|
// The constructor call. No Fn_ prefix here.
|
||||||
|
Constructor,
|
||||||
|
|
||||||
|
// All the operations, except for casts.
|
||||||
|
#define DEFOP(x) Fn_##x,
|
||||||
|
FORALL_SIMD_NONCAST_OP(DEFOP)
|
||||||
|
#undef DEFOP
|
||||||
|
|
||||||
|
// Int <-> Float conversions.
|
||||||
|
Fn_fromInt32x4,
|
||||||
|
Fn_fromUint32x4,
|
||||||
|
Fn_fromFloat32x4,
|
||||||
|
|
||||||
|
// Bitcasts. One for each type with a memory representation.
|
||||||
|
Fn_fromInt8x16Bits,
|
||||||
|
Fn_fromInt16x8Bits,
|
||||||
|
Fn_fromInt32x4Bits,
|
||||||
|
Fn_fromUint8x16Bits,
|
||||||
|
Fn_fromUint16x8Bits,
|
||||||
|
Fn_fromUint32x4Bits,
|
||||||
|
Fn_fromFloat32x4Bits,
|
||||||
|
Fn_fromFloat64x2Bits,
|
||||||
|
|
||||||
|
Last = Fn_fromFloat64x2Bits
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace js
|
||||||
|
|
||||||
|
#endif /* builtin_SIMDConstants_h */
|
|
@ -3980,7 +3980,12 @@ static bool
|
||||||
IsSimdAvailable(JSContext* cx, unsigned argc, Value* vp)
|
IsSimdAvailable(JSContext* cx, unsigned argc, Value* vp)
|
||||||
{
|
{
|
||||||
CallArgs args = CallArgsFromVp(argc, vp);
|
CallArgs args = CallArgsFromVp(argc, vp);
|
||||||
args.rval().set(BooleanValue(cx->jitSupportsSimd()));
|
#if defined(JS_CODEGEN_NONE) || !defined(ENABLE_SIMD)
|
||||||
|
bool available = false;
|
||||||
|
#else
|
||||||
|
bool available = cx->jitSupportsSimd();
|
||||||
|
#endif
|
||||||
|
args.rval().set(BooleanValue(available));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -11,6 +11,7 @@
|
||||||
|
|
||||||
#include "jsutil.h"
|
#include "jsutil.h"
|
||||||
|
|
||||||
|
#include "builtin/SIMDConstants.h"
|
||||||
#include "gc/Marking.h"
|
#include "gc/Marking.h"
|
||||||
#include "js/Vector.h"
|
#include "js/Vector.h"
|
||||||
#include "util/StringBuffer.h"
|
#include "util/StringBuffer.h"
|
||||||
|
@ -243,6 +244,10 @@ ScalarTypeDescr::typeName(Type type)
|
||||||
JS_FOR_EACH_SCALAR_TYPE_REPR(NUMERIC_TYPE_TO_STRING)
|
JS_FOR_EACH_SCALAR_TYPE_REPR(NUMERIC_TYPE_TO_STRING)
|
||||||
#undef NUMERIC_TYPE_TO_STRING
|
#undef NUMERIC_TYPE_TO_STRING
|
||||||
case Scalar::Int64:
|
case Scalar::Int64:
|
||||||
|
case Scalar::Float32x4:
|
||||||
|
case Scalar::Int8x16:
|
||||||
|
case Scalar::Int16x8:
|
||||||
|
case Scalar::Int32x4:
|
||||||
case Scalar::MaxTypedArrayViewType:
|
case Scalar::MaxTypedArrayViewType:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -280,6 +285,10 @@ ScalarTypeDescr::call(JSContext* cx, unsigned argc, Value* vp)
|
||||||
JS_FOR_EACH_SCALAR_TYPE_REPR(SCALARTYPE_CALL)
|
JS_FOR_EACH_SCALAR_TYPE_REPR(SCALARTYPE_CALL)
|
||||||
#undef SCALARTYPE_CALL
|
#undef SCALARTYPE_CALL
|
||||||
case Scalar::Int64:
|
case Scalar::Int64:
|
||||||
|
case Scalar::Float32x4:
|
||||||
|
case Scalar::Int8x16:
|
||||||
|
case Scalar::Int16x8:
|
||||||
|
case Scalar::Int32x4:
|
||||||
case Scalar::MaxTypedArrayViewType:
|
case Scalar::MaxTypedArrayViewType:
|
||||||
MOZ_CRASH();
|
MOZ_CRASH();
|
||||||
}
|
}
|
||||||
|
@ -392,6 +401,50 @@ js::ReferenceTypeDescr::call(JSContext* cx, unsigned argc, Value* vp)
|
||||||
MOZ_CRASH("Unhandled Reference type");
|
MOZ_CRASH("Unhandled Reference type");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/***************************************************************************
|
||||||
|
* SIMD type objects
|
||||||
|
*
|
||||||
|
* Note: these are partially defined in SIMD.cpp
|
||||||
|
*/
|
||||||
|
|
||||||
|
SimdType
|
||||||
|
SimdTypeDescr::type() const {
|
||||||
|
uint32_t t = uint32_t(getReservedSlot(JS_DESCR_SLOT_TYPE).toInt32());
|
||||||
|
MOZ_ASSERT(t < uint32_t(SimdType::Count));
|
||||||
|
return SimdType(t);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t
|
||||||
|
SimdTypeDescr::size(SimdType t)
|
||||||
|
{
|
||||||
|
MOZ_ASSERT(unsigned(t) < unsigned(SimdType::Count));
|
||||||
|
switch (t) {
|
||||||
|
case SimdType::Int8x16:
|
||||||
|
case SimdType::Int16x8:
|
||||||
|
case SimdType::Int32x4:
|
||||||
|
case SimdType::Uint8x16:
|
||||||
|
case SimdType::Uint16x8:
|
||||||
|
case SimdType::Uint32x4:
|
||||||
|
case SimdType::Float32x4:
|
||||||
|
case SimdType::Float64x2:
|
||||||
|
case SimdType::Bool8x16:
|
||||||
|
case SimdType::Bool16x8:
|
||||||
|
case SimdType::Bool32x4:
|
||||||
|
case SimdType::Bool64x2:
|
||||||
|
return 16;
|
||||||
|
case SimdType::Count:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
MOZ_CRASH("unexpected SIMD type");
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t
|
||||||
|
SimdTypeDescr::alignment(SimdType t)
|
||||||
|
{
|
||||||
|
MOZ_ASSERT(unsigned(t) < unsigned(SimdType::Count));
|
||||||
|
return size(t);
|
||||||
|
}
|
||||||
|
|
||||||
/***************************************************************************
|
/***************************************************************************
|
||||||
* ArrayMetaTypeDescr class
|
* ArrayMetaTypeDescr class
|
||||||
*/
|
*/
|
||||||
|
@ -1613,6 +1666,7 @@ TypeDescr::hasProperty(const JSAtomState& names, jsid id)
|
||||||
switch (kind()) {
|
switch (kind()) {
|
||||||
case type::Scalar:
|
case type::Scalar:
|
||||||
case type::Reference:
|
case type::Reference:
|
||||||
|
case type::Simd:
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
case type::Array:
|
case type::Array:
|
||||||
|
@ -1685,6 +1739,7 @@ TypedObject::obj_hasProperty(JSContext* cx, HandleObject obj, HandleId id, bool*
|
||||||
switch (typedObj->typeDescr().kind()) {
|
switch (typedObj->typeDescr().kind()) {
|
||||||
case type::Scalar:
|
case type::Scalar:
|
||||||
case type::Reference:
|
case type::Reference:
|
||||||
|
case type::Simd:
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case type::Array: {
|
case type::Array: {
|
||||||
|
@ -1736,6 +1791,9 @@ TypedObject::obj_getProperty(JSContext* cx, HandleObject obj, HandleValue receiv
|
||||||
case type::Reference:
|
case type::Reference:
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case type::Simd:
|
||||||
|
break;
|
||||||
|
|
||||||
case type::Array:
|
case type::Array:
|
||||||
if (JSID_IS_ATOM(id, cx->names().length)) {
|
if (JSID_IS_ATOM(id, cx->names().length)) {
|
||||||
if (!typedObj->isAttached()) {
|
if (!typedObj->isAttached()) {
|
||||||
|
@ -1782,6 +1840,7 @@ TypedObject::obj_getElement(JSContext* cx, HandleObject obj, HandleValue receive
|
||||||
switch (descr->kind()) {
|
switch (descr->kind()) {
|
||||||
case type::Scalar:
|
case type::Scalar:
|
||||||
case type::Reference:
|
case type::Reference:
|
||||||
|
case type::Simd:
|
||||||
case type::Struct:
|
case type::Struct:
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
@ -1827,6 +1886,9 @@ TypedObject::obj_setProperty(JSContext* cx, HandleObject obj, HandleId id, Handl
|
||||||
case type::Reference:
|
case type::Reference:
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case type::Simd:
|
||||||
|
break;
|
||||||
|
|
||||||
case type::Array: {
|
case type::Array: {
|
||||||
if (JSID_IS_ATOM(id, cx->names().length)) {
|
if (JSID_IS_ATOM(id, cx->names().length)) {
|
||||||
if (receiver.isObject() && obj == &receiver.toObject()) {
|
if (receiver.isObject() && obj == &receiver.toObject()) {
|
||||||
|
@ -1894,6 +1956,7 @@ TypedObject::obj_getOwnPropertyDescriptor(JSContext* cx, HandleObject obj, Handl
|
||||||
switch (descr->kind()) {
|
switch (descr->kind()) {
|
||||||
case type::Scalar:
|
case type::Scalar:
|
||||||
case type::Reference:
|
case type::Reference:
|
||||||
|
case type::Simd:
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case type::Array:
|
case type::Array:
|
||||||
|
@ -1947,6 +2010,7 @@ IsOwnId(JSContext* cx, HandleObject obj, HandleId id)
|
||||||
switch (typedObj->typeDescr().kind()) {
|
switch (typedObj->typeDescr().kind()) {
|
||||||
case type::Scalar:
|
case type::Scalar:
|
||||||
case type::Reference:
|
case type::Reference:
|
||||||
|
case type::Simd:
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
case type::Array:
|
case type::Array:
|
||||||
|
@ -1985,7 +2049,8 @@ TypedObject::obj_newEnumerate(JSContext* cx, HandleObject obj, AutoIdVector& pro
|
||||||
RootedId id(cx);
|
RootedId id(cx);
|
||||||
switch (descr->kind()) {
|
switch (descr->kind()) {
|
||||||
case type::Scalar:
|
case type::Scalar:
|
||||||
case type::Reference: {
|
case type::Reference:
|
||||||
|
case type::Simd: {
|
||||||
// Nothing to enumerate.
|
// Nothing to enumerate.
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -2470,6 +2535,22 @@ js::GetTypedObjectModule(JSContext* cx, unsigned argc, Value* vp)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
js::GetSimdTypeDescr(JSContext* cx, unsigned argc, Value* vp)
|
||||||
|
{
|
||||||
|
CallArgs args = CallArgsFromVp(argc, vp);
|
||||||
|
MOZ_ASSERT(args.length() == 1);
|
||||||
|
MOZ_ASSERT(args[0].isInt32());
|
||||||
|
// One of the JS_SIMDTYPEREPR_* constants / a SimdType enum value.
|
||||||
|
// getOrCreateSimdTypeDescr() will do the range check.
|
||||||
|
int32_t simdTypeRepr = args[0].toInt32();
|
||||||
|
Rooted<GlobalObject*> global(cx, cx->global());
|
||||||
|
MOZ_ASSERT(global);
|
||||||
|
auto* obj = GlobalObject::getOrCreateSimdTypeDescr(cx, global, SimdType(simdTypeRepr));
|
||||||
|
args.rval().setObject(*obj);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
#define JS_STORE_SCALAR_CLASS_IMPL(_constant, T, _name) \
|
#define JS_STORE_SCALAR_CLASS_IMPL(_constant, T, _name) \
|
||||||
bool \
|
bool \
|
||||||
js::StoreScalar##T::Func(JSContext* cx, unsigned argc, Value* vp) \
|
js::StoreScalar##T::Func(JSContext* cx, unsigned argc, Value* vp) \
|
||||||
|
@ -2660,6 +2741,7 @@ visitReferences(TypeDescr& descr,
|
||||||
|
|
||||||
switch (descr.kind()) {
|
switch (descr.kind()) {
|
||||||
case type::Scalar:
|
case type::Scalar:
|
||||||
|
case type::Simd:
|
||||||
return;
|
return;
|
||||||
|
|
||||||
case type::Reference:
|
case type::Reference:
|
||||||
|
|
|
@ -121,6 +121,7 @@ namespace type {
|
||||||
enum Kind {
|
enum Kind {
|
||||||
Scalar = JS_TYPEREPR_SCALAR_KIND,
|
Scalar = JS_TYPEREPR_SCALAR_KIND,
|
||||||
Reference = JS_TYPEREPR_REFERENCE_KIND,
|
Reference = JS_TYPEREPR_REFERENCE_KIND,
|
||||||
|
Simd = JS_TYPEREPR_SIMD_KIND,
|
||||||
Struct = JS_TYPEREPR_STRUCT_KIND,
|
Struct = JS_TYPEREPR_STRUCT_KIND,
|
||||||
Array = JS_TYPEREPR_ARRAY_KIND
|
Array = JS_TYPEREPR_ARRAY_KIND
|
||||||
};
|
};
|
||||||
|
@ -132,6 +133,7 @@ enum Kind {
|
||||||
|
|
||||||
class SimpleTypeDescr;
|
class SimpleTypeDescr;
|
||||||
class ComplexTypeDescr;
|
class ComplexTypeDescr;
|
||||||
|
class SimdTypeDescr;
|
||||||
class StructTypeDescr;
|
class StructTypeDescr;
|
||||||
class TypedProto;
|
class TypedProto;
|
||||||
|
|
||||||
|
@ -253,6 +255,14 @@ class ScalarTypeDescr : public SimpleTypeDescr
|
||||||
"TypedObjectConstants.h must be consistent with Scalar::Type");
|
"TypedObjectConstants.h must be consistent with Scalar::Type");
|
||||||
static_assert(Scalar::Uint8Clamped == JS_SCALARTYPEREPR_UINT8_CLAMPED,
|
static_assert(Scalar::Uint8Clamped == JS_SCALARTYPEREPR_UINT8_CLAMPED,
|
||||||
"TypedObjectConstants.h must be consistent with Scalar::Type");
|
"TypedObjectConstants.h must be consistent with Scalar::Type");
|
||||||
|
static_assert(Scalar::Float32x4 == JS_SCALARTYPEREPR_FLOAT32X4,
|
||||||
|
"TypedObjectConstants.h must be consistent with Scalar::Type");
|
||||||
|
static_assert(Scalar::Int8x16 == JS_SCALARTYPEREPR_INT8X16,
|
||||||
|
"TypedObjectConstants.h must be consistent with Scalar::Type");
|
||||||
|
static_assert(Scalar::Int16x8 == JS_SCALARTYPEREPR_INT16X8,
|
||||||
|
"TypedObjectConstants.h must be consistent with Scalar::Type");
|
||||||
|
static_assert(Scalar::Int32x4 == JS_SCALARTYPEREPR_INT32X4,
|
||||||
|
"TypedObjectConstants.h must be consistent with Scalar::Type");
|
||||||
|
|
||||||
return Type(getReservedSlot(JS_DESCR_SLOT_TYPE).toInt32());
|
return Type(getReservedSlot(JS_DESCR_SLOT_TYPE).toInt32());
|
||||||
}
|
}
|
||||||
|
@ -330,6 +340,25 @@ class ComplexTypeDescr : public TypeDescr
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum class SimdType;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* SIMD Type descriptors.
|
||||||
|
*/
|
||||||
|
class SimdTypeDescr : public ComplexTypeDescr
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static const type::Kind Kind = type::Simd;
|
||||||
|
static const bool Opaque = false;
|
||||||
|
static const Class class_;
|
||||||
|
static uint32_t size(SimdType t);
|
||||||
|
static uint32_t alignment(SimdType t);
|
||||||
|
static MOZ_MUST_USE bool call(JSContext* cx, unsigned argc, Value* vp);
|
||||||
|
static bool is(const Value& v);
|
||||||
|
|
||||||
|
SimdType type() const;
|
||||||
|
};
|
||||||
|
|
||||||
bool IsTypedObjectClass(const Class* clasp); // Defined below
|
bool IsTypedObjectClass(const Class* clasp); // Defined below
|
||||||
bool IsTypedObjectArray(JSObject& obj);
|
bool IsTypedObjectArray(JSObject& obj);
|
||||||
|
|
||||||
|
@ -765,6 +794,16 @@ class InlineOpaqueTypedObject : public InlineTypedObject
|
||||||
static const Class class_;
|
static const Class class_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Class for the global SIMD object.
|
||||||
|
class SimdObject : public NativeObject
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static const Class class_;
|
||||||
|
static MOZ_MUST_USE bool toString(JSContext* cx, unsigned int argc, Value* vp);
|
||||||
|
static MOZ_MUST_USE bool resolve(JSContext* cx, JS::HandleObject obj, JS::HandleId,
|
||||||
|
bool* resolved);
|
||||||
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Usage: NewOpaqueTypedObject(typeObj)
|
* Usage: NewOpaqueTypedObject(typeObj)
|
||||||
*
|
*
|
||||||
|
@ -862,6 +901,16 @@ MOZ_MUST_USE bool ClampToUint8(JSContext* cx, unsigned argc, Value* vp);
|
||||||
*/
|
*/
|
||||||
MOZ_MUST_USE bool GetTypedObjectModule(JSContext* cx, unsigned argc, Value* vp);
|
MOZ_MUST_USE bool GetTypedObjectModule(JSContext* cx, unsigned argc, Value* vp);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Usage: GetSimdTypeDescr(simdTypeRepr)
|
||||||
|
*
|
||||||
|
* Returns one of the SIMD type objects, identified by `simdTypeRepr` which must
|
||||||
|
* be one of the JS_SIMDTYPEREPR_* constants.
|
||||||
|
*
|
||||||
|
* The SIMD pseudo-module must have been initialized for this to be safe.
|
||||||
|
*/
|
||||||
|
MOZ_MUST_USE bool GetSimdTypeDescr(JSContext* cx, unsigned argc, Value* vp);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Usage: Store_int8(targetDatum, targetOffset, value)
|
* Usage: Store_int8(targetDatum, targetOffset, value)
|
||||||
* ...
|
* ...
|
||||||
|
@ -996,7 +1045,8 @@ inline bool
|
||||||
IsComplexTypeDescrClass(const Class* clasp)
|
IsComplexTypeDescrClass(const Class* clasp)
|
||||||
{
|
{
|
||||||
return clasp == &StructTypeDescr::class_ ||
|
return clasp == &StructTypeDescr::class_ ||
|
||||||
clasp == &ArrayTypeDescr::class_;
|
clasp == &ArrayTypeDescr::class_ ||
|
||||||
|
clasp == &SimdTypeDescr::class_;
|
||||||
}
|
}
|
||||||
|
|
||||||
inline bool
|
inline bool
|
||||||
|
|
|
@ -53,6 +53,9 @@ function TypedObjectGet(descr, typedObj, offset) {
|
||||||
case JS_TYPEREPR_REFERENCE_KIND:
|
case JS_TYPEREPR_REFERENCE_KIND:
|
||||||
return TypedObjectGetReference(descr, typedObj, offset);
|
return TypedObjectGetReference(descr, typedObj, offset);
|
||||||
|
|
||||||
|
case JS_TYPEREPR_SIMD_KIND:
|
||||||
|
return TypedObjectGetSimd(descr, typedObj, offset);
|
||||||
|
|
||||||
case JS_TYPEREPR_ARRAY_KIND:
|
case JS_TYPEREPR_ARRAY_KIND:
|
||||||
case JS_TYPEREPR_STRUCT_KIND:
|
case JS_TYPEREPR_STRUCT_KIND:
|
||||||
return TypedObjectGetDerived(descr, typedObj, offset);
|
return TypedObjectGetDerived(descr, typedObj, offset);
|
||||||
|
@ -134,6 +137,144 @@ function TypedObjectGetReference(descr, typedObj, offset) {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function TypedObjectGetSimd(descr, typedObj, offset) {
|
||||||
|
var type = DESCR_TYPE(descr);
|
||||||
|
var simdTypeDescr = GetSimdTypeDescr(type);
|
||||||
|
switch (type) {
|
||||||
|
case JS_SIMDTYPEREPR_FLOAT32X4:
|
||||||
|
var x = Load_float32(typedObj, offset + 0);
|
||||||
|
var y = Load_float32(typedObj, offset + 4);
|
||||||
|
var z = Load_float32(typedObj, offset + 8);
|
||||||
|
var w = Load_float32(typedObj, offset + 12);
|
||||||
|
return simdTypeDescr(x, y, z, w);
|
||||||
|
|
||||||
|
case JS_SIMDTYPEREPR_FLOAT64X2:
|
||||||
|
var x = Load_float64(typedObj, offset + 0);
|
||||||
|
var y = Load_float64(typedObj, offset + 8);
|
||||||
|
return simdTypeDescr(x, y);
|
||||||
|
|
||||||
|
case JS_SIMDTYPEREPR_INT8X16:
|
||||||
|
var s0 = Load_int8(typedObj, offset + 0);
|
||||||
|
var s1 = Load_int8(typedObj, offset + 1);
|
||||||
|
var s2 = Load_int8(typedObj, offset + 2);
|
||||||
|
var s3 = Load_int8(typedObj, offset + 3);
|
||||||
|
var s4 = Load_int8(typedObj, offset + 4);
|
||||||
|
var s5 = Load_int8(typedObj, offset + 5);
|
||||||
|
var s6 = Load_int8(typedObj, offset + 6);
|
||||||
|
var s7 = Load_int8(typedObj, offset + 7);
|
||||||
|
var s8 = Load_int8(typedObj, offset + 8);
|
||||||
|
var s9 = Load_int8(typedObj, offset + 9);
|
||||||
|
var s10 = Load_int8(typedObj, offset + 10);
|
||||||
|
var s11 = Load_int8(typedObj, offset + 11);
|
||||||
|
var s12 = Load_int8(typedObj, offset + 12);
|
||||||
|
var s13 = Load_int8(typedObj, offset + 13);
|
||||||
|
var s14 = Load_int8(typedObj, offset + 14);
|
||||||
|
var s15 = Load_int8(typedObj, offset + 15);
|
||||||
|
return simdTypeDescr(s0, s1, s2, s3, s4, s5, s6, s7, s8, s9, s10, s11, s12, s13, s14, s15);
|
||||||
|
|
||||||
|
case JS_SIMDTYPEREPR_INT16X8:
|
||||||
|
var s0 = Load_int16(typedObj, offset + 0);
|
||||||
|
var s1 = Load_int16(typedObj, offset + 2);
|
||||||
|
var s2 = Load_int16(typedObj, offset + 4);
|
||||||
|
var s3 = Load_int16(typedObj, offset + 6);
|
||||||
|
var s4 = Load_int16(typedObj, offset + 8);
|
||||||
|
var s5 = Load_int16(typedObj, offset + 10);
|
||||||
|
var s6 = Load_int16(typedObj, offset + 12);
|
||||||
|
var s7 = Load_int16(typedObj, offset + 14);
|
||||||
|
return simdTypeDescr(s0, s1, s2, s3, s4, s5, s6, s7);
|
||||||
|
|
||||||
|
case JS_SIMDTYPEREPR_INT32X4:
|
||||||
|
var x = Load_int32(typedObj, offset + 0);
|
||||||
|
var y = Load_int32(typedObj, offset + 4);
|
||||||
|
var z = Load_int32(typedObj, offset + 8);
|
||||||
|
var w = Load_int32(typedObj, offset + 12);
|
||||||
|
return simdTypeDescr(x, y, z, w);
|
||||||
|
|
||||||
|
case JS_SIMDTYPEREPR_UINT8X16:
|
||||||
|
var s0 = Load_uint8(typedObj, offset + 0);
|
||||||
|
var s1 = Load_uint8(typedObj, offset + 1);
|
||||||
|
var s2 = Load_uint8(typedObj, offset + 2);
|
||||||
|
var s3 = Load_uint8(typedObj, offset + 3);
|
||||||
|
var s4 = Load_uint8(typedObj, offset + 4);
|
||||||
|
var s5 = Load_uint8(typedObj, offset + 5);
|
||||||
|
var s6 = Load_uint8(typedObj, offset + 6);
|
||||||
|
var s7 = Load_uint8(typedObj, offset + 7);
|
||||||
|
var s8 = Load_uint8(typedObj, offset + 8);
|
||||||
|
var s9 = Load_uint8(typedObj, offset + 9);
|
||||||
|
var s10 = Load_uint8(typedObj, offset + 10);
|
||||||
|
var s11 = Load_uint8(typedObj, offset + 11);
|
||||||
|
var s12 = Load_uint8(typedObj, offset + 12);
|
||||||
|
var s13 = Load_uint8(typedObj, offset + 13);
|
||||||
|
var s14 = Load_uint8(typedObj, offset + 14);
|
||||||
|
var s15 = Load_uint8(typedObj, offset + 15);
|
||||||
|
return simdTypeDescr(s0, s1, s2, s3, s4, s5, s6, s7, s8, s9, s10, s11, s12, s13, s14, s15);
|
||||||
|
|
||||||
|
case JS_SIMDTYPEREPR_UINT16X8:
|
||||||
|
var s0 = Load_uint16(typedObj, offset + 0);
|
||||||
|
var s1 = Load_uint16(typedObj, offset + 2);
|
||||||
|
var s2 = Load_uint16(typedObj, offset + 4);
|
||||||
|
var s3 = Load_uint16(typedObj, offset + 6);
|
||||||
|
var s4 = Load_uint16(typedObj, offset + 8);
|
||||||
|
var s5 = Load_uint16(typedObj, offset + 10);
|
||||||
|
var s6 = Load_uint16(typedObj, offset + 12);
|
||||||
|
var s7 = Load_uint16(typedObj, offset + 14);
|
||||||
|
return simdTypeDescr(s0, s1, s2, s3, s4, s5, s6, s7);
|
||||||
|
|
||||||
|
case JS_SIMDTYPEREPR_UINT32X4:
|
||||||
|
var x = Load_uint32(typedObj, offset + 0);
|
||||||
|
var y = Load_uint32(typedObj, offset + 4);
|
||||||
|
var z = Load_uint32(typedObj, offset + 8);
|
||||||
|
var w = Load_uint32(typedObj, offset + 12);
|
||||||
|
return simdTypeDescr(x, y, z, w);
|
||||||
|
|
||||||
|
case JS_SIMDTYPEREPR_BOOL8X16:
|
||||||
|
var s0 = Load_int8(typedObj, offset + 0);
|
||||||
|
var s1 = Load_int8(typedObj, offset + 1);
|
||||||
|
var s2 = Load_int8(typedObj, offset + 2);
|
||||||
|
var s3 = Load_int8(typedObj, offset + 3);
|
||||||
|
var s4 = Load_int8(typedObj, offset + 4);
|
||||||
|
var s5 = Load_int8(typedObj, offset + 5);
|
||||||
|
var s6 = Load_int8(typedObj, offset + 6);
|
||||||
|
var s7 = Load_int8(typedObj, offset + 7);
|
||||||
|
var s8 = Load_int8(typedObj, offset + 8);
|
||||||
|
var s9 = Load_int8(typedObj, offset + 9);
|
||||||
|
var s10 = Load_int8(typedObj, offset + 10);
|
||||||
|
var s11 = Load_int8(typedObj, offset + 11);
|
||||||
|
var s12 = Load_int8(typedObj, offset + 12);
|
||||||
|
var s13 = Load_int8(typedObj, offset + 13);
|
||||||
|
var s14 = Load_int8(typedObj, offset + 14);
|
||||||
|
var s15 = Load_int8(typedObj, offset + 15);
|
||||||
|
return simdTypeDescr(s0, s1, s2, s3, s4, s5, s6, s7, s8, s9, s10, s11, s12, s13, s14, s15);
|
||||||
|
|
||||||
|
case JS_SIMDTYPEREPR_BOOL16X8:
|
||||||
|
var s0 = Load_int16(typedObj, offset + 0);
|
||||||
|
var s1 = Load_int16(typedObj, offset + 2);
|
||||||
|
var s2 = Load_int16(typedObj, offset + 4);
|
||||||
|
var s3 = Load_int16(typedObj, offset + 6);
|
||||||
|
var s4 = Load_int16(typedObj, offset + 8);
|
||||||
|
var s5 = Load_int16(typedObj, offset + 10);
|
||||||
|
var s6 = Load_int16(typedObj, offset + 12);
|
||||||
|
var s7 = Load_int16(typedObj, offset + 14);
|
||||||
|
return simdTypeDescr(s0, s1, s2, s3, s4, s5, s6, s7);
|
||||||
|
|
||||||
|
case JS_SIMDTYPEREPR_BOOL32X4:
|
||||||
|
var x = Load_int32(typedObj, offset + 0);
|
||||||
|
var y = Load_int32(typedObj, offset + 4);
|
||||||
|
var z = Load_int32(typedObj, offset + 8);
|
||||||
|
var w = Load_int32(typedObj, offset + 12);
|
||||||
|
return simdTypeDescr(x, y, z, w);
|
||||||
|
|
||||||
|
case JS_SIMDTYPEREPR_BOOL64X2:
|
||||||
|
var x = Load_int32(typedObj, offset + 0);
|
||||||
|
var y = Load_int32(typedObj, offset + 8);
|
||||||
|
return simdTypeDescr(x, y);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(false, "Unhandled SIMD type: " + type);
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////
|
||||||
// Setting values
|
// Setting values
|
||||||
//
|
//
|
||||||
|
@ -155,6 +296,10 @@ function TypedObjectSet(descr, typedObj, offset, name, fromValue) {
|
||||||
TypedObjectSetReference(descr, typedObj, offset, name, fromValue);
|
TypedObjectSetReference(descr, typedObj, offset, name, fromValue);
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
case JS_TYPEREPR_SIMD_KIND:
|
||||||
|
TypedObjectSetSimd(descr, typedObj, offset, fromValue);
|
||||||
|
return;
|
||||||
|
|
||||||
case JS_TYPEREPR_ARRAY_KIND:
|
case JS_TYPEREPR_ARRAY_KIND:
|
||||||
var length = DESCR_ARRAY_LENGTH(descr);
|
var length = DESCR_ARRAY_LENGTH(descr);
|
||||||
if (TypedObjectSetArray(descr, length, typedObj, offset, fromValue))
|
if (TypedObjectSetArray(descr, length, typedObj, offset, fromValue))
|
||||||
|
@ -269,6 +414,106 @@ function TypedObjectSetReference(descr, typedObj, offset, name, fromValue) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sets `fromValue` to `this` assuming that `this` is a scalar type.
|
// Sets `fromValue` to `this` assuming that `this` is a scalar type.
|
||||||
|
function TypedObjectSetSimd(descr, typedObj, offset, fromValue) {
|
||||||
|
if (!IsObject(fromValue) || !ObjectIsTypedObject(fromValue))
|
||||||
|
ThrowTypeError(JSMSG_CANT_CONVERT_TO,
|
||||||
|
typeof(fromValue),
|
||||||
|
DESCR_STRING_REPR(descr));
|
||||||
|
|
||||||
|
if (!DescrsEquiv(descr, TypedObjectTypeDescr(fromValue)))
|
||||||
|
ThrowTypeError(JSMSG_CANT_CONVERT_TO,
|
||||||
|
typeof(fromValue),
|
||||||
|
DESCR_STRING_REPR(descr));
|
||||||
|
|
||||||
|
var type = DESCR_TYPE(descr);
|
||||||
|
switch (type) {
|
||||||
|
case JS_SIMDTYPEREPR_FLOAT32X4:
|
||||||
|
Store_float32(typedObj, offset + 0, Load_float32(fromValue, 0));
|
||||||
|
Store_float32(typedObj, offset + 4, Load_float32(fromValue, 4));
|
||||||
|
Store_float32(typedObj, offset + 8, Load_float32(fromValue, 8));
|
||||||
|
Store_float32(typedObj, offset + 12, Load_float32(fromValue, 12));
|
||||||
|
break;
|
||||||
|
case JS_SIMDTYPEREPR_FLOAT64X2:
|
||||||
|
Store_float64(typedObj, offset + 0, Load_float64(fromValue, 0));
|
||||||
|
Store_float64(typedObj, offset + 8, Load_float64(fromValue, 8));
|
||||||
|
break;
|
||||||
|
case JS_SIMDTYPEREPR_INT8X16:
|
||||||
|
case JS_SIMDTYPEREPR_BOOL8X16:
|
||||||
|
Store_int8(typedObj, offset + 0, Load_int8(fromValue, 0));
|
||||||
|
Store_int8(typedObj, offset + 1, Load_int8(fromValue, 1));
|
||||||
|
Store_int8(typedObj, offset + 2, Load_int8(fromValue, 2));
|
||||||
|
Store_int8(typedObj, offset + 3, Load_int8(fromValue, 3));
|
||||||
|
Store_int8(typedObj, offset + 4, Load_int8(fromValue, 4));
|
||||||
|
Store_int8(typedObj, offset + 5, Load_int8(fromValue, 5));
|
||||||
|
Store_int8(typedObj, offset + 6, Load_int8(fromValue, 6));
|
||||||
|
Store_int8(typedObj, offset + 7, Load_int8(fromValue, 7));
|
||||||
|
Store_int8(typedObj, offset + 8, Load_int8(fromValue, 8));
|
||||||
|
Store_int8(typedObj, offset + 9, Load_int8(fromValue, 9));
|
||||||
|
Store_int8(typedObj, offset + 10, Load_int8(fromValue, 10));
|
||||||
|
Store_int8(typedObj, offset + 11, Load_int8(fromValue, 11));
|
||||||
|
Store_int8(typedObj, offset + 12, Load_int8(fromValue, 12));
|
||||||
|
Store_int8(typedObj, offset + 13, Load_int8(fromValue, 13));
|
||||||
|
Store_int8(typedObj, offset + 14, Load_int8(fromValue, 14));
|
||||||
|
Store_int8(typedObj, offset + 15, Load_int8(fromValue, 15));
|
||||||
|
break;
|
||||||
|
case JS_SIMDTYPEREPR_INT16X8:
|
||||||
|
case JS_SIMDTYPEREPR_BOOL16X8:
|
||||||
|
Store_int16(typedObj, offset + 0, Load_int16(fromValue, 0));
|
||||||
|
Store_int16(typedObj, offset + 2, Load_int16(fromValue, 2));
|
||||||
|
Store_int16(typedObj, offset + 4, Load_int16(fromValue, 4));
|
||||||
|
Store_int16(typedObj, offset + 6, Load_int16(fromValue, 6));
|
||||||
|
Store_int16(typedObj, offset + 8, Load_int16(fromValue, 8));
|
||||||
|
Store_int16(typedObj, offset + 10, Load_int16(fromValue, 10));
|
||||||
|
Store_int16(typedObj, offset + 12, Load_int16(fromValue, 12));
|
||||||
|
Store_int16(typedObj, offset + 14, Load_int16(fromValue, 14));
|
||||||
|
break;
|
||||||
|
case JS_SIMDTYPEREPR_INT32X4:
|
||||||
|
case JS_SIMDTYPEREPR_BOOL32X4:
|
||||||
|
case JS_SIMDTYPEREPR_BOOL64X2:
|
||||||
|
Store_int32(typedObj, offset + 0, Load_int32(fromValue, 0));
|
||||||
|
Store_int32(typedObj, offset + 4, Load_int32(fromValue, 4));
|
||||||
|
Store_int32(typedObj, offset + 8, Load_int32(fromValue, 8));
|
||||||
|
Store_int32(typedObj, offset + 12, Load_int32(fromValue, 12));
|
||||||
|
break;
|
||||||
|
case JS_SIMDTYPEREPR_UINT8X16:
|
||||||
|
Store_uint8(typedObj, offset + 0, Load_uint8(fromValue, 0));
|
||||||
|
Store_uint8(typedObj, offset + 1, Load_uint8(fromValue, 1));
|
||||||
|
Store_uint8(typedObj, offset + 2, Load_uint8(fromValue, 2));
|
||||||
|
Store_uint8(typedObj, offset + 3, Load_uint8(fromValue, 3));
|
||||||
|
Store_uint8(typedObj, offset + 4, Load_uint8(fromValue, 4));
|
||||||
|
Store_uint8(typedObj, offset + 5, Load_uint8(fromValue, 5));
|
||||||
|
Store_uint8(typedObj, offset + 6, Load_uint8(fromValue, 6));
|
||||||
|
Store_uint8(typedObj, offset + 7, Load_uint8(fromValue, 7));
|
||||||
|
Store_uint8(typedObj, offset + 8, Load_uint8(fromValue, 8));
|
||||||
|
Store_uint8(typedObj, offset + 9, Load_uint8(fromValue, 9));
|
||||||
|
Store_uint8(typedObj, offset + 10, Load_uint8(fromValue, 10));
|
||||||
|
Store_uint8(typedObj, offset + 11, Load_uint8(fromValue, 11));
|
||||||
|
Store_uint8(typedObj, offset + 12, Load_uint8(fromValue, 12));
|
||||||
|
Store_uint8(typedObj, offset + 13, Load_uint8(fromValue, 13));
|
||||||
|
Store_uint8(typedObj, offset + 14, Load_uint8(fromValue, 14));
|
||||||
|
Store_uint8(typedObj, offset + 15, Load_uint8(fromValue, 15));
|
||||||
|
break;
|
||||||
|
case JS_SIMDTYPEREPR_UINT16X8:
|
||||||
|
Store_uint16(typedObj, offset + 0, Load_uint16(fromValue, 0));
|
||||||
|
Store_uint16(typedObj, offset + 2, Load_uint16(fromValue, 2));
|
||||||
|
Store_uint16(typedObj, offset + 4, Load_uint16(fromValue, 4));
|
||||||
|
Store_uint16(typedObj, offset + 6, Load_uint16(fromValue, 6));
|
||||||
|
Store_uint16(typedObj, offset + 8, Load_uint16(fromValue, 8));
|
||||||
|
Store_uint16(typedObj, offset + 10, Load_uint16(fromValue, 10));
|
||||||
|
Store_uint16(typedObj, offset + 12, Load_uint16(fromValue, 12));
|
||||||
|
Store_uint16(typedObj, offset + 14, Load_uint16(fromValue, 14));
|
||||||
|
break;
|
||||||
|
case JS_SIMDTYPEREPR_UINT32X4:
|
||||||
|
Store_uint32(typedObj, offset + 0, Load_uint32(fromValue, 0));
|
||||||
|
Store_uint32(typedObj, offset + 4, Load_uint32(fromValue, 4));
|
||||||
|
Store_uint32(typedObj, offset + 8, Load_uint32(fromValue, 8));
|
||||||
|
Store_uint32(typedObj, offset + 12, Load_uint32(fromValue, 12));
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
assert(false, "Unhandled Simd type: " + type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////
|
||||||
// C++ Wrappers
|
// C++ Wrappers
|
||||||
//
|
//
|
||||||
|
@ -384,6 +629,241 @@ function TypedObjectArrayRedimension(newArrayType) {
|
||||||
return NewDerivedTypedObject(newArrayType, this, 0);
|
return NewDerivedTypedObject(newArrayType, this, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////
|
||||||
|
// SIMD
|
||||||
|
|
||||||
|
function SimdProtoString(type) {
|
||||||
|
switch (type) {
|
||||||
|
case JS_SIMDTYPEREPR_INT8X16:
|
||||||
|
return "Int8x16";
|
||||||
|
case JS_SIMDTYPEREPR_INT16X8:
|
||||||
|
return "Int16x8";
|
||||||
|
case JS_SIMDTYPEREPR_INT32X4:
|
||||||
|
return "Int32x4";
|
||||||
|
case JS_SIMDTYPEREPR_UINT8X16:
|
||||||
|
return "Uint8x16";
|
||||||
|
case JS_SIMDTYPEREPR_UINT16X8:
|
||||||
|
return "Uint16x8";
|
||||||
|
case JS_SIMDTYPEREPR_UINT32X4:
|
||||||
|
return "Uint32x4";
|
||||||
|
case JS_SIMDTYPEREPR_FLOAT32X4:
|
||||||
|
return "Float32x4";
|
||||||
|
case JS_SIMDTYPEREPR_FLOAT64X2:
|
||||||
|
return "Float64x2";
|
||||||
|
case JS_SIMDTYPEREPR_BOOL8X16:
|
||||||
|
return "Bool8x16";
|
||||||
|
case JS_SIMDTYPEREPR_BOOL16X8:
|
||||||
|
return "Bool16x8";
|
||||||
|
case JS_SIMDTYPEREPR_BOOL32X4:
|
||||||
|
return "Bool32x4";
|
||||||
|
case JS_SIMDTYPEREPR_BOOL64X2:
|
||||||
|
return "Bool64x2";
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(false, "Unhandled type constant");
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
function SimdTypeToLength(type) {
|
||||||
|
switch (type) {
|
||||||
|
case JS_SIMDTYPEREPR_INT8X16:
|
||||||
|
case JS_SIMDTYPEREPR_BOOL8X16:
|
||||||
|
return 16;
|
||||||
|
case JS_SIMDTYPEREPR_INT16X8:
|
||||||
|
case JS_SIMDTYPEREPR_BOOL16X8:
|
||||||
|
return 8;
|
||||||
|
case JS_SIMDTYPEREPR_INT32X4:
|
||||||
|
case JS_SIMDTYPEREPR_FLOAT32X4:
|
||||||
|
case JS_SIMDTYPEREPR_BOOL32X4:
|
||||||
|
return 4;
|
||||||
|
case JS_SIMDTYPEREPR_FLOAT64X2:
|
||||||
|
case JS_SIMDTYPEREPR_BOOL64X2:
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(false, "Unhandled type constant");
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
// This implements SIMD.*.prototype.valueOf().
|
||||||
|
// Once we have proper value semantics for SIMD types, this function should just
|
||||||
|
// perform a type check and return this.
|
||||||
|
// For now, throw a TypeError unconditionally since valueOf() was probably
|
||||||
|
// called from ToNumber() which is supposed to throw when attempting to convert
|
||||||
|
// a SIMD value to a number.
|
||||||
|
function SimdValueOf() {
|
||||||
|
if (!IsObject(this) || !ObjectIsTypedObject(this))
|
||||||
|
ThrowTypeError(JSMSG_INCOMPATIBLE_PROTO, "SIMD", "valueOf", typeof this);
|
||||||
|
|
||||||
|
var descr = TypedObjectTypeDescr(this);
|
||||||
|
|
||||||
|
if (DESCR_KIND(descr) != JS_TYPEREPR_SIMD_KIND)
|
||||||
|
ThrowTypeError(JSMSG_INCOMPATIBLE_PROTO, "SIMD", "valueOf", typeof this);
|
||||||
|
|
||||||
|
ThrowTypeError(JSMSG_SIMD_TO_NUMBER);
|
||||||
|
}
|
||||||
|
|
||||||
|
function SimdToSource() {
|
||||||
|
if (!IsObject(this) || !ObjectIsTypedObject(this))
|
||||||
|
ThrowTypeError(JSMSG_INCOMPATIBLE_PROTO, "SIMD.*", "toSource", typeof this);
|
||||||
|
|
||||||
|
var descr = TypedObjectTypeDescr(this);
|
||||||
|
|
||||||
|
if (DESCR_KIND(descr) != JS_TYPEREPR_SIMD_KIND)
|
||||||
|
ThrowTypeError(JSMSG_INCOMPATIBLE_PROTO, "SIMD.*", "toSource", typeof this);
|
||||||
|
|
||||||
|
return SimdFormatString(descr, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
function SimdToString() {
|
||||||
|
if (!IsObject(this) || !ObjectIsTypedObject(this))
|
||||||
|
ThrowTypeError(JSMSG_INCOMPATIBLE_PROTO, "SIMD.*", "toString", typeof this);
|
||||||
|
|
||||||
|
var descr = TypedObjectTypeDescr(this);
|
||||||
|
|
||||||
|
if (DESCR_KIND(descr) != JS_TYPEREPR_SIMD_KIND)
|
||||||
|
ThrowTypeError(JSMSG_INCOMPATIBLE_PROTO, "SIMD.*", "toString", typeof this);
|
||||||
|
|
||||||
|
return SimdFormatString(descr, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
function SimdFormatString(descr, typedObj) {
|
||||||
|
var typerepr = DESCR_TYPE(descr);
|
||||||
|
var protoString = SimdProtoString(typerepr);
|
||||||
|
switch (typerepr) {
|
||||||
|
case JS_SIMDTYPEREPR_INT8X16: {
|
||||||
|
var s1 = callFunction(std_SIMD_Int8x16_extractLane, null, typedObj, 0);
|
||||||
|
var s2 = callFunction(std_SIMD_Int8x16_extractLane, null, typedObj, 1);
|
||||||
|
var s3 = callFunction(std_SIMD_Int8x16_extractLane, null, typedObj, 2);
|
||||||
|
var s4 = callFunction(std_SIMD_Int8x16_extractLane, null, typedObj, 3);
|
||||||
|
var s5 = callFunction(std_SIMD_Int8x16_extractLane, null, typedObj, 4);
|
||||||
|
var s6 = callFunction(std_SIMD_Int8x16_extractLane, null, typedObj, 5);
|
||||||
|
var s7 = callFunction(std_SIMD_Int8x16_extractLane, null, typedObj, 6);
|
||||||
|
var s8 = callFunction(std_SIMD_Int8x16_extractLane, null, typedObj, 7);
|
||||||
|
var s9 = callFunction(std_SIMD_Int8x16_extractLane, null, typedObj, 8);
|
||||||
|
var s10 = callFunction(std_SIMD_Int8x16_extractLane, null, typedObj, 9);
|
||||||
|
var s11 = callFunction(std_SIMD_Int8x16_extractLane, null, typedObj, 10);
|
||||||
|
var s12 = callFunction(std_SIMD_Int8x16_extractLane, null, typedObj, 11);
|
||||||
|
var s13 = callFunction(std_SIMD_Int8x16_extractLane, null, typedObj, 12);
|
||||||
|
var s14 = callFunction(std_SIMD_Int8x16_extractLane, null, typedObj, 13);
|
||||||
|
var s15 = callFunction(std_SIMD_Int8x16_extractLane, null, typedObj, 14);
|
||||||
|
var s16 = callFunction(std_SIMD_Int8x16_extractLane, null, typedObj, 15);
|
||||||
|
return `SIMD.${protoString}(${s1}, ${s2}, ${s3}, ${s4}, ${s5}, ${s6}, ${s7}, ${s8}, ${s9}, ${s10}, ${s11}, ${s12}, ${s13}, ${s14}, ${s15}, ${s16})`;
|
||||||
|
}
|
||||||
|
case JS_SIMDTYPEREPR_INT16X8: {
|
||||||
|
var s1 = callFunction(std_SIMD_Int16x8_extractLane, null, typedObj, 0);
|
||||||
|
var s2 = callFunction(std_SIMD_Int16x8_extractLane, null, typedObj, 1);
|
||||||
|
var s3 = callFunction(std_SIMD_Int16x8_extractLane, null, typedObj, 2);
|
||||||
|
var s4 = callFunction(std_SIMD_Int16x8_extractLane, null, typedObj, 3);
|
||||||
|
var s5 = callFunction(std_SIMD_Int16x8_extractLane, null, typedObj, 4);
|
||||||
|
var s6 = callFunction(std_SIMD_Int16x8_extractLane, null, typedObj, 5);
|
||||||
|
var s7 = callFunction(std_SIMD_Int16x8_extractLane, null, typedObj, 6);
|
||||||
|
var s8 = callFunction(std_SIMD_Int16x8_extractLane, null, typedObj, 7);
|
||||||
|
return `SIMD.${protoString}(${s1}, ${s2}, ${s3}, ${s4}, ${s5}, ${s6}, ${s7}, ${s8})`;
|
||||||
|
}
|
||||||
|
case JS_SIMDTYPEREPR_INT32X4: {
|
||||||
|
var x = callFunction(std_SIMD_Int32x4_extractLane, null, typedObj, 0);
|
||||||
|
var y = callFunction(std_SIMD_Int32x4_extractLane, null, typedObj, 1);
|
||||||
|
var z = callFunction(std_SIMD_Int32x4_extractLane, null, typedObj, 2);
|
||||||
|
var w = callFunction(std_SIMD_Int32x4_extractLane, null, typedObj, 3);
|
||||||
|
return `SIMD.${protoString}(${x}, ${y}, ${z}, ${w})`;
|
||||||
|
}
|
||||||
|
case JS_SIMDTYPEREPR_UINT8X16: {
|
||||||
|
var s1 = callFunction(std_SIMD_Uint8x16_extractLane, null, typedObj, 0);
|
||||||
|
var s2 = callFunction(std_SIMD_Uint8x16_extractLane, null, typedObj, 1);
|
||||||
|
var s3 = callFunction(std_SIMD_Uint8x16_extractLane, null, typedObj, 2);
|
||||||
|
var s4 = callFunction(std_SIMD_Uint8x16_extractLane, null, typedObj, 3);
|
||||||
|
var s5 = callFunction(std_SIMD_Uint8x16_extractLane, null, typedObj, 4);
|
||||||
|
var s6 = callFunction(std_SIMD_Uint8x16_extractLane, null, typedObj, 5);
|
||||||
|
var s7 = callFunction(std_SIMD_Uint8x16_extractLane, null, typedObj, 6);
|
||||||
|
var s8 = callFunction(std_SIMD_Uint8x16_extractLane, null, typedObj, 7);
|
||||||
|
var s9 = callFunction(std_SIMD_Uint8x16_extractLane, null, typedObj, 8);
|
||||||
|
var s10 = callFunction(std_SIMD_Uint8x16_extractLane, null, typedObj, 9);
|
||||||
|
var s11 = callFunction(std_SIMD_Uint8x16_extractLane, null, typedObj, 10);
|
||||||
|
var s12 = callFunction(std_SIMD_Uint8x16_extractLane, null, typedObj, 11);
|
||||||
|
var s13 = callFunction(std_SIMD_Uint8x16_extractLane, null, typedObj, 12);
|
||||||
|
var s14 = callFunction(std_SIMD_Uint8x16_extractLane, null, typedObj, 13);
|
||||||
|
var s15 = callFunction(std_SIMD_Uint8x16_extractLane, null, typedObj, 14);
|
||||||
|
var s16 = callFunction(std_SIMD_Uint8x16_extractLane, null, typedObj, 15);
|
||||||
|
return `SIMD.${protoString}(${s1}, ${s2}, ${s3}, ${s4}, ${s5}, ${s6}, ${s7}, ${s8}, ${s9}, ${s10}, ${s11}, ${s12}, ${s13}, ${s14}, ${s15}, ${s16})`;
|
||||||
|
}
|
||||||
|
case JS_SIMDTYPEREPR_UINT16X8: {
|
||||||
|
var s1 = callFunction(std_SIMD_Uint16x8_extractLane, null, typedObj, 0);
|
||||||
|
var s2 = callFunction(std_SIMD_Uint16x8_extractLane, null, typedObj, 1);
|
||||||
|
var s3 = callFunction(std_SIMD_Uint16x8_extractLane, null, typedObj, 2);
|
||||||
|
var s4 = callFunction(std_SIMD_Uint16x8_extractLane, null, typedObj, 3);
|
||||||
|
var s5 = callFunction(std_SIMD_Uint16x8_extractLane, null, typedObj, 4);
|
||||||
|
var s6 = callFunction(std_SIMD_Uint16x8_extractLane, null, typedObj, 5);
|
||||||
|
var s7 = callFunction(std_SIMD_Uint16x8_extractLane, null, typedObj, 6);
|
||||||
|
var s8 = callFunction(std_SIMD_Uint16x8_extractLane, null, typedObj, 7);
|
||||||
|
return `SIMD.${protoString}(${s1}, ${s2}, ${s3}, ${s4}, ${s5}, ${s6}, ${s7}, ${s8})`;
|
||||||
|
}
|
||||||
|
case JS_SIMDTYPEREPR_UINT32X4: {
|
||||||
|
var x = callFunction(std_SIMD_Uint32x4_extractLane, null, typedObj, 0);
|
||||||
|
var y = callFunction(std_SIMD_Uint32x4_extractLane, null, typedObj, 1);
|
||||||
|
var z = callFunction(std_SIMD_Uint32x4_extractLane, null, typedObj, 2);
|
||||||
|
var w = callFunction(std_SIMD_Uint32x4_extractLane, null, typedObj, 3);
|
||||||
|
return `SIMD.${protoString}(${x}, ${y}, ${z}, ${w})`;
|
||||||
|
}
|
||||||
|
case JS_SIMDTYPEREPR_FLOAT32X4: {
|
||||||
|
var x = callFunction(std_SIMD_Float32x4_extractLane, null, typedObj, 0);
|
||||||
|
var y = callFunction(std_SIMD_Float32x4_extractLane, null, typedObj, 1);
|
||||||
|
var z = callFunction(std_SIMD_Float32x4_extractLane, null, typedObj, 2);
|
||||||
|
var w = callFunction(std_SIMD_Float32x4_extractLane, null, typedObj, 3);
|
||||||
|
return `SIMD.${protoString}(${x}, ${y}, ${z}, ${w})`;
|
||||||
|
}
|
||||||
|
case JS_SIMDTYPEREPR_FLOAT64X2: {
|
||||||
|
var x = callFunction(std_SIMD_Float64x2_extractLane, null, typedObj, 0);
|
||||||
|
var y = callFunction(std_SIMD_Float64x2_extractLane, null, typedObj, 1);
|
||||||
|
return `SIMD.${protoString}(${x}, ${y})`;
|
||||||
|
}
|
||||||
|
case JS_SIMDTYPEREPR_BOOL8X16: {
|
||||||
|
var s1 = callFunction(std_SIMD_Bool8x16_extractLane, null, typedObj, 0);
|
||||||
|
var s2 = callFunction(std_SIMD_Bool8x16_extractLane, null, typedObj, 1);
|
||||||
|
var s3 = callFunction(std_SIMD_Bool8x16_extractLane, null, typedObj, 2);
|
||||||
|
var s4 = callFunction(std_SIMD_Bool8x16_extractLane, null, typedObj, 3);
|
||||||
|
var s5 = callFunction(std_SIMD_Bool8x16_extractLane, null, typedObj, 4);
|
||||||
|
var s6 = callFunction(std_SIMD_Bool8x16_extractLane, null, typedObj, 5);
|
||||||
|
var s7 = callFunction(std_SIMD_Bool8x16_extractLane, null, typedObj, 6);
|
||||||
|
var s8 = callFunction(std_SIMD_Bool8x16_extractLane, null, typedObj, 7);
|
||||||
|
var s9 = callFunction(std_SIMD_Bool8x16_extractLane, null, typedObj, 8);
|
||||||
|
var s10 = callFunction(std_SIMD_Bool8x16_extractLane, null, typedObj, 9);
|
||||||
|
var s11 = callFunction(std_SIMD_Bool8x16_extractLane, null, typedObj, 10);
|
||||||
|
var s12 = callFunction(std_SIMD_Bool8x16_extractLane, null, typedObj, 11);
|
||||||
|
var s13 = callFunction(std_SIMD_Bool8x16_extractLane, null, typedObj, 12);
|
||||||
|
var s14 = callFunction(std_SIMD_Bool8x16_extractLane, null, typedObj, 13);
|
||||||
|
var s15 = callFunction(std_SIMD_Bool8x16_extractLane, null, typedObj, 14);
|
||||||
|
var s16 = callFunction(std_SIMD_Bool8x16_extractLane, null, typedObj, 15);
|
||||||
|
return `SIMD.${protoString}(${s1}, ${s2}, ${s3}, ${s4}, ${s5}, ${s6}, ${s7}, ${s8}, ${s9}, ${s10}, ${s11}, ${s12}, ${s13}, ${s14}, ${s15}, ${s16})`;
|
||||||
|
}
|
||||||
|
case JS_SIMDTYPEREPR_BOOL16X8: {
|
||||||
|
var s1 = callFunction(std_SIMD_Bool16x8_extractLane, null, typedObj, 0);
|
||||||
|
var s2 = callFunction(std_SIMD_Bool16x8_extractLane, null, typedObj, 1);
|
||||||
|
var s3 = callFunction(std_SIMD_Bool16x8_extractLane, null, typedObj, 2);
|
||||||
|
var s4 = callFunction(std_SIMD_Bool16x8_extractLane, null, typedObj, 3);
|
||||||
|
var s5 = callFunction(std_SIMD_Bool16x8_extractLane, null, typedObj, 4);
|
||||||
|
var s6 = callFunction(std_SIMD_Bool16x8_extractLane, null, typedObj, 5);
|
||||||
|
var s7 = callFunction(std_SIMD_Bool16x8_extractLane, null, typedObj, 6);
|
||||||
|
var s8 = callFunction(std_SIMD_Bool16x8_extractLane, null, typedObj, 7);
|
||||||
|
return `SIMD.${protoString}(${s1}, ${s2}, ${s3}, ${s4}, ${s5}, ${s6}, ${s7}, ${s8})`;
|
||||||
|
}
|
||||||
|
case JS_SIMDTYPEREPR_BOOL32X4: {
|
||||||
|
var x = callFunction(std_SIMD_Bool32x4_extractLane, null, typedObj, 0);
|
||||||
|
var y = callFunction(std_SIMD_Bool32x4_extractLane, null, typedObj, 1);
|
||||||
|
var z = callFunction(std_SIMD_Bool32x4_extractLane, null, typedObj, 2);
|
||||||
|
var w = callFunction(std_SIMD_Bool32x4_extractLane, null, typedObj, 3);
|
||||||
|
return `SIMD.${protoString}(${x}, ${y}, ${z}, ${w})`;
|
||||||
|
}
|
||||||
|
case JS_SIMDTYPEREPR_BOOL64X2: {
|
||||||
|
var x = callFunction(std_SIMD_Bool64x2_extractLane, null, typedObj, 0);
|
||||||
|
var y = callFunction(std_SIMD_Bool64x2_extractLane, null, typedObj, 1);
|
||||||
|
return `SIMD.${protoString}(${x}, ${y})`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
assert(false, "unexpected SIMD kind");
|
||||||
|
return "?";
|
||||||
|
}
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////
|
||||||
// Miscellaneous
|
// Miscellaneous
|
||||||
|
|
||||||
|
|
|
@ -53,7 +53,7 @@
|
||||||
#define JS_DESCR_SLOT_ARRAYPROTO 6 // Lazily created prototype for arrays
|
#define JS_DESCR_SLOT_ARRAYPROTO 6 // Lazily created prototype for arrays
|
||||||
#define JS_DESCR_SLOT_TRACE_LIST 7 // List of references for use in tracing
|
#define JS_DESCR_SLOT_TRACE_LIST 7 // List of references for use in tracing
|
||||||
|
|
||||||
// Slots on scalars, references
|
// Slots on scalars, references, and SIMD objects
|
||||||
#define JS_DESCR_SLOT_TYPE 8 // Type code
|
#define JS_DESCR_SLOT_TYPE 8 // Type code
|
||||||
|
|
||||||
// Slots on array descriptors
|
// Slots on array descriptors
|
||||||
|
@ -75,6 +75,7 @@
|
||||||
#define JS_TYPEREPR_REFERENCE_KIND 2
|
#define JS_TYPEREPR_REFERENCE_KIND 2
|
||||||
#define JS_TYPEREPR_STRUCT_KIND 3
|
#define JS_TYPEREPR_STRUCT_KIND 3
|
||||||
#define JS_TYPEREPR_ARRAY_KIND 4
|
#define JS_TYPEREPR_ARRAY_KIND 4
|
||||||
|
#define JS_TYPEREPR_SIMD_KIND 5
|
||||||
|
|
||||||
// These constants are for use exclusively in JS code. In C++ code,
|
// These constants are for use exclusively in JS code. In C++ code,
|
||||||
// prefer Scalar::Int8 etc, which allows you to write a switch which will
|
// prefer Scalar::Int8 etc, which allows you to write a switch which will
|
||||||
|
@ -88,6 +89,10 @@
|
||||||
#define JS_SCALARTYPEREPR_FLOAT32 6
|
#define JS_SCALARTYPEREPR_FLOAT32 6
|
||||||
#define JS_SCALARTYPEREPR_FLOAT64 7
|
#define JS_SCALARTYPEREPR_FLOAT64 7
|
||||||
#define JS_SCALARTYPEREPR_UINT8_CLAMPED 8
|
#define JS_SCALARTYPEREPR_UINT8_CLAMPED 8
|
||||||
|
#define JS_SCALARTYPEREPR_FLOAT32X4 11
|
||||||
|
#define JS_SCALARTYPEREPR_INT8X16 12
|
||||||
|
#define JS_SCALARTYPEREPR_INT16X8 13
|
||||||
|
#define JS_SCALARTYPEREPR_INT32X4 14
|
||||||
|
|
||||||
// These constants are for use exclusively in JS code. In C++ code,
|
// These constants are for use exclusively in JS code. In C++ code,
|
||||||
// prefer ReferenceTypeRepresentation::TYPE_ANY etc, which allows
|
// prefer ReferenceTypeRepresentation::TYPE_ANY etc, which allows
|
||||||
|
@ -97,4 +102,20 @@
|
||||||
#define JS_REFERENCETYPEREPR_OBJECT 1
|
#define JS_REFERENCETYPEREPR_OBJECT 1
|
||||||
#define JS_REFERENCETYPEREPR_STRING 2
|
#define JS_REFERENCETYPEREPR_STRING 2
|
||||||
|
|
||||||
|
// These constants are for use exclusively in JS code. In C++ code, prefer
|
||||||
|
// SimdType::Int32x4 etc, since that allows you to write a switch which will
|
||||||
|
// receive a warning if you omit a case.
|
||||||
|
#define JS_SIMDTYPEREPR_INT8X16 0
|
||||||
|
#define JS_SIMDTYPEREPR_INT16X8 1
|
||||||
|
#define JS_SIMDTYPEREPR_INT32X4 2
|
||||||
|
#define JS_SIMDTYPEREPR_UINT8X16 3
|
||||||
|
#define JS_SIMDTYPEREPR_UINT16X8 4
|
||||||
|
#define JS_SIMDTYPEREPR_UINT32X4 5
|
||||||
|
#define JS_SIMDTYPEREPR_FLOAT32X4 6
|
||||||
|
#define JS_SIMDTYPEREPR_FLOAT64X2 7
|
||||||
|
#define JS_SIMDTYPEREPR_BOOL8X16 8
|
||||||
|
#define JS_SIMDTYPEREPR_BOOL16X8 9
|
||||||
|
#define JS_SIMDTYPEREPR_BOOL32X4 10
|
||||||
|
#define JS_SIMDTYPEREPR_BOOL64X2 11
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
SIMD/nursery-overflow.js
|
||||||
asm.js/testBug1117235.js
|
asm.js/testBug1117235.js
|
||||||
asm.js/testParallelCompile.js
|
asm.js/testParallelCompile.js
|
||||||
auto-regress/bug653395.js
|
auto-regress/bug653395.js
|
||||||
|
|
|
@ -152,6 +152,11 @@ Failed to do range check of element access on a typed object.
|
||||||
|
|
||||||
### AccessNotDense
|
### AccessNotDense
|
||||||
|
|
||||||
|
### AccessNotSimdObject
|
||||||
|
|
||||||
|
The observed type of the target of the property access doesn't guarantee
|
||||||
|
that it is a SIMD object.
|
||||||
|
|
||||||
### AccessNotTypedObject
|
### AccessNotTypedObject
|
||||||
|
|
||||||
The observed type of the target of the property access doesn't guarantee
|
The observed type of the target of the property access doesn't guarantee
|
||||||
|
@ -217,6 +222,15 @@ the keys have never been observed to be a String, Symbol, or Int32.
|
||||||
IonMonkey only generates inline caches for element accesses which are
|
IonMonkey only generates inline caches for element accesses which are
|
||||||
either on dense objects (e.g. dense Arrays), or Typed Arrays.
|
either on dense objects (e.g. dense Arrays), or Typed Arrays.
|
||||||
|
|
||||||
|
### NoSimdJitSupport
|
||||||
|
|
||||||
|
Optimization failed because SIMD JIT support was not enabled.
|
||||||
|
|
||||||
|
### SimdTypeNotOptimized
|
||||||
|
|
||||||
|
The type observed as being retrieved from this property access did not
|
||||||
|
match an optimizable type.
|
||||||
|
|
||||||
### HasCommonInliningPath
|
### HasCommonInliningPath
|
||||||
|
|
||||||
Inlining was abandoned because the inlining call path was repeated. A
|
Inlining was abandoned because the inlining call path was repeated. A
|
||||||
|
|
|
@ -253,6 +253,9 @@ def main(argv):
|
||||||
# This part is equivalent to:
|
# This part is equivalent to:
|
||||||
# skip-if = coverage
|
# skip-if = coverage
|
||||||
if os.getenv('GCOV_PREFIX') is not None:
|
if os.getenv('GCOV_PREFIX') is not None:
|
||||||
|
# GCOV errors.
|
||||||
|
options.exclude += [os.path.join('asm.js', 'testSIMD.js')] # Bug 1347245
|
||||||
|
|
||||||
# JSVM errors.
|
# JSVM errors.
|
||||||
options.exclude += [os.path.join('basic', 'functionnames.js')] # Bug 1369783
|
options.exclude += [os.path.join('basic', 'functionnames.js')] # Bug 1369783
|
||||||
options.exclude += [os.path.join('debug', 'Debugger-findScripts-23.js')]
|
options.exclude += [os.path.join('debug', 'Debugger-findScripts-23.js')]
|
||||||
|
|
|
@ -0,0 +1,109 @@
|
||||||
|
if (!this.hasOwnProperty("SIMD"))
|
||||||
|
quit();
|
||||||
|
|
||||||
|
function booleanBinaryX4(op, v, w) {
|
||||||
|
var arr = [];
|
||||||
|
var [varr, warr] = [simdToArray(v), simdToArray(w)];
|
||||||
|
for (var i = 0; i < 4; i++)
|
||||||
|
arr[i] = op(varr[i], warr[i]);
|
||||||
|
return arr;
|
||||||
|
}
|
||||||
|
|
||||||
|
function binaryX(op, v, w) {
|
||||||
|
var arr = [];
|
||||||
|
var [varr, warr] = [simdToArray(v), simdToArray(w)];
|
||||||
|
[varr, warr] = [varr.map(Math.fround), warr.map(Math.fround)];
|
||||||
|
for (var i = 0; i < varr.length; i++)
|
||||||
|
arr[i] = op(varr[i], warr[i]);
|
||||||
|
return arr.map(Math.fround);
|
||||||
|
}
|
||||||
|
|
||||||
|
function unaryX4(op, v, coerceFunc) {
|
||||||
|
var arr = [];
|
||||||
|
var varr = simdToArray(v).map(coerceFunc);
|
||||||
|
for (var i = 0; i < 4; i++)
|
||||||
|
arr[i] = op(varr[i]);
|
||||||
|
return arr.map(coerceFunc);
|
||||||
|
}
|
||||||
|
|
||||||
|
function assertNear(a, b) {
|
||||||
|
assertEq((a != a && b != b) || Math.abs(a - b) < 0.001, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
function GetType(v) {
|
||||||
|
var pt = Object.getPrototypeOf(v);
|
||||||
|
switch (pt) {
|
||||||
|
case SIMD.Int8x16.prototype: return SIMD.Int8x16;
|
||||||
|
case SIMD.Int16x8.prototype: return SIMD.Int16x8;
|
||||||
|
case SIMD.Int32x4.prototype: return SIMD.Int32x4;
|
||||||
|
case SIMD.Uint8x16.prototype: return SIMD.Uint8x16;
|
||||||
|
case SIMD.Uint16x8.prototype: return SIMD.Uint16x8;
|
||||||
|
case SIMD.Uint32x4.prototype: return SIMD.Uint32x4;
|
||||||
|
case SIMD.Float32x4.prototype: return SIMD.Float32x4;
|
||||||
|
case SIMD.Bool8x16.prototype: return SIMD.Bool8x16;
|
||||||
|
case SIMD.Bool16x8.prototype: return SIMD.Bool16x8;
|
||||||
|
case SIMD.Bool32x4.prototype: return SIMD.Bool32x4;
|
||||||
|
}
|
||||||
|
throw "unexpected SIMD type";
|
||||||
|
}
|
||||||
|
|
||||||
|
function GetLength(t) {
|
||||||
|
switch (t) {
|
||||||
|
case SIMD.Int8x16: return 16;
|
||||||
|
case SIMD.Int16x8: return 8;
|
||||||
|
case SIMD.Int32x4: return 4;
|
||||||
|
case SIMD.Uint8x16: return 16;
|
||||||
|
case SIMD.Uint16x8: return 8;
|
||||||
|
case SIMD.Uint32x4: return 4;
|
||||||
|
case SIMD.Float32x4: return 4;
|
||||||
|
case SIMD.Bool8x16: return 16;
|
||||||
|
case SIMD.Bool16x8: return 8;
|
||||||
|
case SIMD.Bool32x4: return 4;
|
||||||
|
}
|
||||||
|
throw "unexpected SIMD type";
|
||||||
|
}
|
||||||
|
|
||||||
|
function assertEqVec(v, w) {
|
||||||
|
var typeV = GetType(v);
|
||||||
|
var lengthV = GetLength(typeV);
|
||||||
|
var ext = typeV.extractLane;
|
||||||
|
assertEq(GetType(w), typeV);
|
||||||
|
for (var i = 0; i < lengthV; i++)
|
||||||
|
assertEq(ext(v, i), ext(w, i));
|
||||||
|
}
|
||||||
|
|
||||||
|
function assertEqVecArr(v, w) {
|
||||||
|
var typeV = GetType(v);
|
||||||
|
var lengthV = GetLength(typeV);
|
||||||
|
var ext = typeV.extractLane;
|
||||||
|
assertEq(w.length, lengthV);
|
||||||
|
|
||||||
|
for (var i = 0; i < lengthV; i++)
|
||||||
|
assertEq(ext(v, i), w[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
function assertEqX4(vec, arr, ...opts) {
|
||||||
|
|
||||||
|
var assertFunc;
|
||||||
|
if (opts.length == 1 && typeof opts[0] !== 'undefined') {
|
||||||
|
assertFunc = opts[0];
|
||||||
|
} else {
|
||||||
|
assertFunc = assertEq;
|
||||||
|
}
|
||||||
|
|
||||||
|
var Type = GetType(vec);
|
||||||
|
|
||||||
|
assertFunc(Type.extractLane(vec, 0), arr[0]);
|
||||||
|
assertFunc(Type.extractLane(vec, 1), arr[1]);
|
||||||
|
assertFunc(Type.extractLane(vec, 2), arr[2]);
|
||||||
|
assertFunc(Type.extractLane(vec, 3), arr[3]);
|
||||||
|
}
|
||||||
|
|
||||||
|
function simdToArray(vec) {
|
||||||
|
var Type = GetType(vec);
|
||||||
|
var Length = GetLength(Type);
|
||||||
|
var a = [];
|
||||||
|
for (var i = 0; i < Length; i++)
|
||||||
|
a.push(Type.extractLane(vec, i));
|
||||||
|
return a;
|
||||||
|
}
|
|
@ -0,0 +1,38 @@
|
||||||
|
load(libdir + 'simd.js');
|
||||||
|
|
||||||
|
setJitCompilerOption("ion.warmup.trigger", 50);
|
||||||
|
|
||||||
|
function all(B, n) {
|
||||||
|
var a = B.splat(true);
|
||||||
|
for (var i = 0; i < n; i++) {
|
||||||
|
var b = B.replaceLane(a, i, false);
|
||||||
|
assertEq(B.allTrue(b), false);
|
||||||
|
var c = B.replaceLane(b, i, true);
|
||||||
|
assertEq(B.allTrue(c), true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function any(B, n) {
|
||||||
|
var a = B.splat(false);
|
||||||
|
for (var i = 0; i < n; i++) {
|
||||||
|
var b = B.replaceLane(a, i, true);
|
||||||
|
assertEq(B.anyTrue(b), true);
|
||||||
|
var c = B.replaceLane(b, i, false);
|
||||||
|
assertEq(B.anyTrue(c), false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function f() {
|
||||||
|
for (var j = 0; j < 200; j++) {
|
||||||
|
all(SIMD.Bool64x2, 2)
|
||||||
|
any(SIMD.Bool64x2, 2)
|
||||||
|
all(SIMD.Bool32x4, 4)
|
||||||
|
any(SIMD.Bool32x4, 4)
|
||||||
|
all(SIMD.Bool16x8, 8)
|
||||||
|
any(SIMD.Bool16x8, 8)
|
||||||
|
all(SIMD.Bool8x16, 16)
|
||||||
|
any(SIMD.Bool8x16, 16)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
f()
|
|
@ -0,0 +1,30 @@
|
||||||
|
load(libdir + 'simd.js');
|
||||||
|
|
||||||
|
setJitCompilerOption("ion.warmup.trigger", 50);
|
||||||
|
|
||||||
|
function f() {
|
||||||
|
var i1 = SIMD.Int32x4(1, 2, 3, 4);
|
||||||
|
var i2 = SIMD.Int32x4(4, 3, 2, 1);
|
||||||
|
|
||||||
|
var f1 = SIMD.Float32x4(1, 2, 3, 4);
|
||||||
|
var f2 = SIMD.Float32x4(4, 3, 2, 1);
|
||||||
|
|
||||||
|
var i8_1 = SIMD.Int8x16(1, 2, 3, 4, 20, 30, 40, 50, 100, 115, 120, 125);
|
||||||
|
var i8_2 = SIMD.Int8x16(4, 3, 2, 1, 8, 7, 6, 5, 12, 11, 10, 9);
|
||||||
|
|
||||||
|
for (var i = 0; i < 150; i++) {
|
||||||
|
assertEqX4(SIMD.Float32x4.add(f1, f2), binaryX((x, y) => x + y, f1, f2));
|
||||||
|
assertEqX4(SIMD.Float32x4.sub(f1, f2), binaryX((x, y) => x - y, f1, f2));
|
||||||
|
assertEqX4(SIMD.Float32x4.mul(f1, f2), binaryX((x, y) => x * y, f1, f2));
|
||||||
|
|
||||||
|
assertEqX4(SIMD.Int32x4.add(i1, i2), binaryX((x, y) => x + y, i1, i2));
|
||||||
|
assertEqX4(SIMD.Int32x4.sub(i1, i2), binaryX((x, y) => x - y, i1, i2));
|
||||||
|
assertEqX4(SIMD.Int32x4.mul(i1, i2), binaryX((x, y) => x * y, i1, i2));
|
||||||
|
|
||||||
|
assertEqX4(SIMD.Int8x16.add(i8_1, i8_2), binaryX((x, y) => (x + y) << 24 >> 24, i8_1, i8_2));
|
||||||
|
assertEqX4(SIMD.Int8x16.sub(i8_1, i8_2), binaryX((x, y) => (x - y) << 24 >> 24, i8_1, i8_2));
|
||||||
|
assertEqX4(SIMD.Int8x16.mul(i8_1, i8_2), binaryX((x, y) => (x * y) << 24 >> 24, i8_1, i8_2));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
f();
|
|
@ -0,0 +1,15 @@
|
||||||
|
load(libdir + "simd.js");
|
||||||
|
|
||||||
|
setJitCompilerOption("ion.warmup.trigger", 50);
|
||||||
|
|
||||||
|
function f() {
|
||||||
|
var b1 = SIMD.Bool32x4(true, false, true, false);
|
||||||
|
var b2 = SIMD.Bool32x4(true, true, true, true);
|
||||||
|
do {
|
||||||
|
assertEqX4(SIMD.Bool32x4.and(b1, b2), booleanBinaryX4((x, y) => x && y, b1, b2));
|
||||||
|
assertEqX4(SIMD.Bool32x4.or(b1, b2), booleanBinaryX4((x, y) => x || y, b1, b2));
|
||||||
|
assertEqX4(SIMD.Bool32x4.xor(b1, b2), booleanBinaryX4((x, y) => x != y, b1, b2));
|
||||||
|
} while (!inIon());
|
||||||
|
}
|
||||||
|
|
||||||
|
f();
|
|
@ -0,0 +1,65 @@
|
||||||
|
load(libdir + "simd.js");
|
||||||
|
|
||||||
|
setJitCompilerOption("ion.warmup.trigger", 50);
|
||||||
|
|
||||||
|
// Test constant folding into the Bool32x4 constructor.
|
||||||
|
// Verify that we get the truthiness right, c.f. the ECMA ToBoolean() function.
|
||||||
|
function f1() {
|
||||||
|
var B = SIMD.Bool32x4;
|
||||||
|
var S = SIMD.Bool32x4.splat;
|
||||||
|
return [
|
||||||
|
B(false, false, false, true),
|
||||||
|
B(true),
|
||||||
|
B(undefined, null, "", "x"),
|
||||||
|
B({}, 0, 1, -0.0),
|
||||||
|
B(NaN, -NaN, Symbol(), createIsHTMLDDA()),
|
||||||
|
|
||||||
|
S(false),
|
||||||
|
S(true),
|
||||||
|
S(undefined),
|
||||||
|
S(null),
|
||||||
|
|
||||||
|
S(""),
|
||||||
|
S("x"),
|
||||||
|
S(0),
|
||||||
|
S(1),
|
||||||
|
|
||||||
|
S({}),
|
||||||
|
S(-0.0),
|
||||||
|
S(NaN),
|
||||||
|
S(Symbol()),
|
||||||
|
|
||||||
|
S(createIsHTMLDDA())
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
function f() {
|
||||||
|
for (var i = 0; i < 100; i++) {
|
||||||
|
var a = f1()
|
||||||
|
assertEqX4(a[0], [false, false, false, true]);
|
||||||
|
assertEqX4(a[1], [true, false, false, false]);
|
||||||
|
assertEqX4(a[2], [false, false, false, true]);
|
||||||
|
assertEqX4(a[3], [true, false, true, false]);
|
||||||
|
assertEqX4(a[4], [false, false, true, false]);
|
||||||
|
|
||||||
|
// Splats.
|
||||||
|
assertEqX4(a[5], [false, false, false, false]);
|
||||||
|
assertEqX4(a[6], [true, true, true, true]);
|
||||||
|
assertEqX4(a[7], [false, false, false, false]);
|
||||||
|
assertEqX4(a[8], [false, false, false, false]);
|
||||||
|
|
||||||
|
assertEqX4(a[9], [false, false, false, false]);
|
||||||
|
assertEqX4(a[10], [true, true, true, true]);
|
||||||
|
assertEqX4(a[11], [false, false, false, false]);
|
||||||
|
assertEqX4(a[12], [true, true, true, true]);
|
||||||
|
|
||||||
|
assertEqX4(a[13], [true, true, true, true]);
|
||||||
|
assertEqX4(a[14], [false, false, false, false]);
|
||||||
|
assertEqX4(a[15], [false, false, false, false]);
|
||||||
|
assertEqX4(a[16], [true, true, true, true]);
|
||||||
|
|
||||||
|
assertEqX4(a[17], [false, false, false, false]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
f();
|
|
@ -0,0 +1,11 @@
|
||||||
|
if (typeof TypedObject === "undefined" || typeof SIMD === 'undefined')
|
||||||
|
quit();
|
||||||
|
|
||||||
|
var Int32x4 = SIMD.Int32x4;
|
||||||
|
var a = Int32x4((4294967295), 200, 300, 400);
|
||||||
|
addCase( new Array(Math.pow(2,12)) );
|
||||||
|
for ( var arg = "", i = 0; i < Math.pow(2,12); i++ ) {}
|
||||||
|
addCase( a );
|
||||||
|
function addCase(object) {
|
||||||
|
object.length
|
||||||
|
}
|
|
@ -0,0 +1,31 @@
|
||||||
|
if (!this.hasOwnProperty("SIMD"))
|
||||||
|
quit();
|
||||||
|
|
||||||
|
setJitCompilerOption("baseline.warmup.trigger", 10);
|
||||||
|
setJitCompilerOption("ion.warmup.trigger", 30);
|
||||||
|
|
||||||
|
function test_1(i) {
|
||||||
|
if (i >= 40)
|
||||||
|
return;
|
||||||
|
var a = SIMD.Float32x4(1.1, 2.2, 3.3, 4.6);
|
||||||
|
SIMD.Int32x4.fromFloat32x4(a);
|
||||||
|
test_1(i + 1);
|
||||||
|
}
|
||||||
|
test_1(0);
|
||||||
|
|
||||||
|
|
||||||
|
var Float32x4 = SIMD.Float32x4;
|
||||||
|
function test_2() {
|
||||||
|
var Array = Float32x4.array(3);
|
||||||
|
var array = new Array([
|
||||||
|
Float32x4(1, 2, 3, 4),
|
||||||
|
Float32x4(5, 6, 7, 8),
|
||||||
|
Float32x4(9, 10, 11, 12)
|
||||||
|
]);
|
||||||
|
if (typeof reportCompare === "function")
|
||||||
|
reportCompare(true, true);
|
||||||
|
}
|
||||||
|
test_2();
|
||||||
|
evaluate("test_2(); test_2();", {
|
||||||
|
isRunOnce: true,
|
||||||
|
});
|
|
@ -0,0 +1,9 @@
|
||||||
|
if (!this.hasOwnProperty("SIMD"))
|
||||||
|
quit();
|
||||||
|
|
||||||
|
var Float64x2 = SIMD.Float64x2;
|
||||||
|
function test() {
|
||||||
|
var a = Float64x2(1, 2);
|
||||||
|
}
|
||||||
|
test();
|
||||||
|
test();
|
|
@ -0,0 +1,15 @@
|
||||||
|
if (!this.hasOwnProperty("SIMD"))
|
||||||
|
quit();
|
||||||
|
|
||||||
|
var Int32x4 = SIMD.Int32x4;
|
||||||
|
function test() {
|
||||||
|
var a = Int32x4();
|
||||||
|
var b = Int32x4(10, 20, 30, 40);
|
||||||
|
var c = SIMD.Int32x4.and(a, b);
|
||||||
|
assertEq(Int32x4.extractLane(c, 0), 0);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
test();
|
||||||
|
var u = [], v = [];
|
||||||
|
for (var j=0; j<u.length; ++j)
|
||||||
|
v[test()] = t;
|
|
@ -0,0 +1,10 @@
|
||||||
|
if (typeof SIMD !== 'object')
|
||||||
|
quit(0);
|
||||||
|
|
||||||
|
function test() {
|
||||||
|
return SIMD.Float32x4().toSource();
|
||||||
|
}
|
||||||
|
|
||||||
|
var r = '';
|
||||||
|
for (var i = 0; i < 10000; i++)
|
||||||
|
r = test();
|
|
@ -0,0 +1,16 @@
|
||||||
|
if (typeof SIMD !== 'object')
|
||||||
|
quit(0);
|
||||||
|
|
||||||
|
function assertEqVec(v, w) {
|
||||||
|
[0].forEach(i => v, w);
|
||||||
|
function assertEqX4(...opts) {}
|
||||||
|
}
|
||||||
|
gczeal(1);
|
||||||
|
function f() {
|
||||||
|
SIMD.Float32x4();
|
||||||
|
var i1 = SIMD.Int32x4();
|
||||||
|
for (j = 0; j < 100000; ++j, eval.eval)
|
||||||
|
assertEqVec(SIMD.Int32x4.check(i1), i1);
|
||||||
|
}
|
||||||
|
f();
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
if (typeof SIMD === 'undefined')
|
||||||
|
quit();
|
||||||
|
|
||||||
|
Int8x16 = SIMD.Int8x16;
|
||||||
|
var Int32x4 = SIMD.Int32x4;
|
||||||
|
function testSwizzleForType(type) { return type(); }
|
||||||
|
testSwizzleForType(Int8x16);
|
||||||
|
function testSwizzleInt32x4() { return testSwizzleForType(Int32x4); }
|
||||||
|
testSwizzleInt32x4();
|
|
@ -0,0 +1,9 @@
|
||||||
|
if (typeof gczeal === 'undefined' || typeof SIMD === 'undefined') {
|
||||||
|
quit();
|
||||||
|
}
|
||||||
|
|
||||||
|
gczeal(9, 2);
|
||||||
|
var Int8x16 = SIMD.Int8x16;
|
||||||
|
var v = Int8x16();
|
||||||
|
var good = { valueOf: () => 21 };
|
||||||
|
Int8x16.shiftLeftByScalar(v, good);
|
|
@ -0,0 +1,12 @@
|
||||||
|
if (typeof gczeal === 'undefined' || typeof SIMD === 'undefined') {
|
||||||
|
quit();
|
||||||
|
}
|
||||||
|
|
||||||
|
gczeal(14,2);
|
||||||
|
var Float32x4 = SIMD.Float32x4;
|
||||||
|
function test() {
|
||||||
|
var v = Float32x4(1,2,3,4);
|
||||||
|
var good = {valueOf: () => 42};
|
||||||
|
Float32x4.replaceLane(v, 0, good);
|
||||||
|
}
|
||||||
|
test();
|
|
@ -0,0 +1,25 @@
|
||||||
|
load(libdir + 'simd.js');
|
||||||
|
|
||||||
|
var ab = new ArrayBuffer(64 * 1024);
|
||||||
|
var arr = new Uint8Array(ab);
|
||||||
|
|
||||||
|
(function(glob, imp, b) {
|
||||||
|
"use asm";
|
||||||
|
var arr = new glob.Uint8Array(b);
|
||||||
|
return {}
|
||||||
|
})(this, null, ab);
|
||||||
|
|
||||||
|
function testSimdX4() {
|
||||||
|
for (var i = 10; i --> 0;) {
|
||||||
|
var caught;
|
||||||
|
try {
|
||||||
|
v = SIMD.Int32x4.load(arr, 65534);
|
||||||
|
} catch (e) {
|
||||||
|
caught = e;
|
||||||
|
}
|
||||||
|
assertEq(caught instanceof RangeError, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setJitCompilerOption('ion.warmup.trigger', 0);
|
||||||
|
testSimdX4();
|
|
@ -0,0 +1,10 @@
|
||||||
|
/*
|
||||||
|
* Any copyright is dedicated to the Public Domain.
|
||||||
|
* http://creativecommons.org/licenses/publicdomain/
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (!this.hasOwnProperty("TypedObject") || !this.hasOwnProperty("SIMD"))
|
||||||
|
quit();
|
||||||
|
|
||||||
|
var Float32x4 = SIMD.Float32x4;
|
||||||
|
Float32x4.array(1);
|
|
@ -0,0 +1,25 @@
|
||||||
|
load(libdir + 'simd.js');
|
||||||
|
|
||||||
|
setJitCompilerOption("ion.warmup.trigger", 50);
|
||||||
|
|
||||||
|
function f() {
|
||||||
|
var f1 = SIMD.Float32x4(1, 2, 3, 4);
|
||||||
|
var i1 = SIMD.Int32x4(1, 2, -3, 4);
|
||||||
|
var b1 = SIMD.Bool32x4(true, true, false, true);
|
||||||
|
var i = 0;
|
||||||
|
try {
|
||||||
|
for (; i < 150; i++) {
|
||||||
|
if (i > 148)
|
||||||
|
i1 = f1;
|
||||||
|
assertEqVec(SIMD.Int32x4.check(i1), i1);
|
||||||
|
assertEqVec(SIMD.Float32x4.check(f1), f1);
|
||||||
|
assertEqVec(SIMD.Bool32x4.check(b1), b1);
|
||||||
|
}
|
||||||
|
} catch (ex) {
|
||||||
|
assertEq(i, 149);
|
||||||
|
assertEq(ex instanceof TypeError, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
f();
|
||||||
|
|
|
@ -0,0 +1,39 @@
|
||||||
|
load(libdir + 'simd.js');
|
||||||
|
|
||||||
|
setJitCompilerOption("ion.warmup.trigger", 50);
|
||||||
|
|
||||||
|
function f() {
|
||||||
|
var f1 = SIMD.Float32x4(1, 2, 3, 4);
|
||||||
|
var f2 = SIMD.Float32x4(NaN, Infinity, 3.14, -0);
|
||||||
|
|
||||||
|
var i1 = SIMD.Int32x4(1, 2, -3, 4);
|
||||||
|
var i2 = SIMD.Int32x4(1, -2, 3, 0);
|
||||||
|
|
||||||
|
var u1 = SIMD.Uint32x4(1, 2, -3, 4);
|
||||||
|
var u2 = SIMD.Uint32x4(1, -2, 3, 0x80000000);
|
||||||
|
|
||||||
|
for (var i = 0; i < 150; i++) {
|
||||||
|
assertEqX4(SIMD.Int32x4.lessThan(i1, i2), [false, false, true, false]);
|
||||||
|
assertEqX4(SIMD.Int32x4.lessThanOrEqual(i1, i2), [true, false, true, false]);
|
||||||
|
assertEqX4(SIMD.Int32x4.equal(i1, i2), [true, false, false, false]);
|
||||||
|
assertEqX4(SIMD.Int32x4.notEqual(i1, i2), [false, true, true, true]);
|
||||||
|
assertEqX4(SIMD.Int32x4.greaterThan(i1, i2), [false, true, false, true]);
|
||||||
|
assertEqX4(SIMD.Int32x4.greaterThanOrEqual(i1, i2), [true, true, false, true]);
|
||||||
|
|
||||||
|
assertEqX4(SIMD.Uint32x4.lessThan(u1, u2), [false, true, false, true]);
|
||||||
|
assertEqX4(SIMD.Uint32x4.lessThanOrEqual(u1, u2), [true, true, false, true]);
|
||||||
|
assertEqX4(SIMD.Uint32x4.equal(u1, u2), [true, false, false, false]);
|
||||||
|
assertEqX4(SIMD.Uint32x4.notEqual(u1, u2), [false, true, true, true]);
|
||||||
|
assertEqX4(SIMD.Uint32x4.greaterThan(u1, u2), [false, false, true, false]);
|
||||||
|
assertEqX4(SIMD.Uint32x4.greaterThanOrEqual(u1, u2), [true, false, true, false]);
|
||||||
|
|
||||||
|
assertEqX4(SIMD.Float32x4.lessThan(f1, f2), [false, true, true, false]);
|
||||||
|
assertEqX4(SIMD.Float32x4.lessThanOrEqual(f1, f2), [false, true, true, false]);
|
||||||
|
assertEqX4(SIMD.Float32x4.equal(f1, f2), [false, false, false, false]);
|
||||||
|
assertEqX4(SIMD.Float32x4.notEqual(f1, f2), [true, true, true, true]);
|
||||||
|
assertEqX4(SIMD.Float32x4.greaterThan(f1, f2), [false, false, false, true]);
|
||||||
|
assertEqX4(SIMD.Float32x4.greaterThanOrEqual(f1, f2), [false, false, false, true]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
f();
|
|
@ -0,0 +1,70 @@
|
||||||
|
load(libdir + 'simd.js');
|
||||||
|
|
||||||
|
if (typeof SIMD === "undefined")
|
||||||
|
quit();
|
||||||
|
|
||||||
|
setJitCompilerOption("baseline.warmup.trigger", 10);
|
||||||
|
setJitCompilerOption("ion.warmup.trigger", 90);
|
||||||
|
var max = 100; // Make have the warm-up counter high enough to
|
||||||
|
// consider inlining functions.
|
||||||
|
|
||||||
|
var f4 = SIMD.Int32x4; // :TODO: Support Float32x4 arith.
|
||||||
|
var f4add = f4.add;
|
||||||
|
var f4sub = f4.sub;
|
||||||
|
var f4mul = f4.mul;
|
||||||
|
|
||||||
|
function c4mul(z1, z2) {
|
||||||
|
var { re: re1, im: im1 } = z1;
|
||||||
|
var { re: re2, im: im2 } = z2;
|
||||||
|
var rere = f4mul(re1, re2);
|
||||||
|
var reim = f4mul(re1, im2);
|
||||||
|
var imre = f4mul(im1, re2);
|
||||||
|
var imim = f4mul(im1, im2);
|
||||||
|
return { re: f4sub(rere, imim), im: f4add(reim, imre) };
|
||||||
|
}
|
||||||
|
|
||||||
|
function c4inv(z) {
|
||||||
|
var { re: re, im: im } = z;
|
||||||
|
var minus = f4(-1, -1, -1, -1);
|
||||||
|
return { re: re, im: f4mul(im, minus) };
|
||||||
|
}
|
||||||
|
|
||||||
|
function c4inv_inplace(z) {
|
||||||
|
var res = c4inv(z);
|
||||||
|
z.re = res.re;
|
||||||
|
z.im = res.im;
|
||||||
|
}
|
||||||
|
|
||||||
|
function c4norm(z) {
|
||||||
|
var { re: re, im: im } = c4mul(z, c4inv(z));
|
||||||
|
return re;
|
||||||
|
}
|
||||||
|
|
||||||
|
function c4scale(z, s) {
|
||||||
|
var { re: re, im: im } = z;
|
||||||
|
var f4s = f4(s, s, s, s);
|
||||||
|
return { re: f4mul(re, f4s), im: f4mul(im, f4s) };
|
||||||
|
}
|
||||||
|
|
||||||
|
var rotate90 = { re: f4(0, 0, 0, 0), im: f4(1, 1, 1, 1) };
|
||||||
|
var cardinals = { re: f4(1, 0, -1, 0), im: f4(0, 1, 0, -1) };
|
||||||
|
|
||||||
|
function test(dots) {
|
||||||
|
for (var j = 0; j < 4; j++) {
|
||||||
|
dots = c4mul(rotate90, dots);
|
||||||
|
if (j % 2 == 0) // Magic !
|
||||||
|
c4inv_inplace(dots);
|
||||||
|
dots = c4scale(dots, 2);
|
||||||
|
}
|
||||||
|
return dots;
|
||||||
|
}
|
||||||
|
|
||||||
|
assertEqX4(c4norm(cardinals), simdToArray(f4.splat(1)));
|
||||||
|
var cardinals16 = c4scale(cardinals, 16);
|
||||||
|
|
||||||
|
for (var i = 0; i < max; i++) {
|
||||||
|
var res = test(cardinals);
|
||||||
|
assertEqX4(c4norm(res), simdToArray(f4.splat(16 * 16)));
|
||||||
|
assertEqX4(res.re, simdToArray(cardinals16.re));
|
||||||
|
assertEqX4(res.im, simdToArray(cardinals16.im));
|
||||||
|
}
|
|
@ -0,0 +1,68 @@
|
||||||
|
load(libdir + 'simd.js');
|
||||||
|
|
||||||
|
setJitCompilerOption("ion.warmup.trigger", 30);
|
||||||
|
|
||||||
|
var cast = (function() {
|
||||||
|
var i32 = new Int32Array(1);
|
||||||
|
var f32 = new Float32Array(i32.buffer);
|
||||||
|
return {
|
||||||
|
fromInt32Bits(x) {
|
||||||
|
i32[0] = x;
|
||||||
|
return f32[0];
|
||||||
|
},
|
||||||
|
|
||||||
|
fromFloat32Bits(x) {
|
||||||
|
f32[0] = x;
|
||||||
|
return i32[0];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
|
||||||
|
function f() {
|
||||||
|
// No bailout here.
|
||||||
|
var f4 = SIMD.Float32x4(1, 2, 3, 4);
|
||||||
|
var i4 = SIMD.Int32x4(1, 2, 3, 4);
|
||||||
|
var BitOrZero = (x) => x | 0;
|
||||||
|
for (var i = 0; i < 150; i++) {
|
||||||
|
assertEqX4(SIMD.Float32x4.fromInt32x4(i4), unaryX4(BitOrZero, f4, Math.fround));
|
||||||
|
assertEqX4(SIMD.Float32x4.fromInt32x4Bits(i4), unaryX4(cast.fromInt32Bits, f4, Math.fround));
|
||||||
|
assertEqX4(SIMD.Int32x4.fromFloat32x4(f4), unaryX4(Math.fround, i4, BitOrZero));
|
||||||
|
assertEqX4(SIMD.Int32x4.fromFloat32x4Bits(f4), unaryX4(cast.fromFloat32Bits, i4, BitOrZero));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function uglyDuckling(val) {
|
||||||
|
// We bail out when i == 149 because the conversion will return
|
||||||
|
// 0x80000000 and the input actually wasn't in bounds.
|
||||||
|
val = Math.fround(val);
|
||||||
|
for (var i = 0; i < 150; i++) {
|
||||||
|
var caught = false;
|
||||||
|
try {
|
||||||
|
var v = SIMD.Float32x4(i < 149 ? 0 : val, 0, 0, 0)
|
||||||
|
SIMD.Int32x4.fromFloat32x4(v);
|
||||||
|
} catch(e) {
|
||||||
|
assertEq(e instanceof RangeError, true);
|
||||||
|
assertEq(i, 149);
|
||||||
|
caught = true;
|
||||||
|
}
|
||||||
|
assertEq(i < 149 || caught, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function dontBail() {
|
||||||
|
// On x86, the conversion will return 0x80000000, which will imply that we
|
||||||
|
// check the input values. However, we shouldn't bail out in this case.
|
||||||
|
for (var i = 0; i < 150; i++) {
|
||||||
|
var v = SIMD.Float32x4(i < 149 ? 0 : -Math.pow(2, 31), 0, 0, 0)
|
||||||
|
SIMD.Int32x4.fromFloat32x4(v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
f();
|
||||||
|
|
||||||
|
dontBail();
|
||||||
|
dontBail();
|
||||||
|
|
||||||
|
uglyDuckling(Math.pow(2, 31));
|
||||||
|
uglyDuckling(NaN);
|
||||||
|
uglyDuckling(-Math.pow(2, 32));
|
|
@ -0,0 +1,33 @@
|
||||||
|
load(libdir + 'simd.js');
|
||||||
|
|
||||||
|
setJitCompilerOption("ion.warmup.trigger", 50);
|
||||||
|
|
||||||
|
function maxNum(x, y) {
|
||||||
|
if (x != x)
|
||||||
|
return y;
|
||||||
|
if (y != y)
|
||||||
|
return x;
|
||||||
|
return Math.max(x, y);
|
||||||
|
}
|
||||||
|
|
||||||
|
function minNum(x, y) {
|
||||||
|
if (x != x)
|
||||||
|
return y;
|
||||||
|
if (y != y)
|
||||||
|
return x;
|
||||||
|
return Math.min(x, y);
|
||||||
|
}
|
||||||
|
|
||||||
|
function f() {
|
||||||
|
var f1 = SIMD.Float32x4(1, 2, 3, 4);
|
||||||
|
var f2 = SIMD.Float32x4(4, 3, 2, 1);
|
||||||
|
for (var i = 0; i < 150; i++) {
|
||||||
|
assertEqX4(SIMD.Float32x4.div(f1, f2), binaryX((x, y) => x / y, f1, f2));
|
||||||
|
assertEqX4(SIMD.Float32x4.min(f1, f2), binaryX(Math.min, f1, f2));
|
||||||
|
assertEqX4(SIMD.Float32x4.max(f1, f2), binaryX(Math.max, f1, f2));
|
||||||
|
assertEqX4(SIMD.Float32x4.minNum(f1, f2), binaryX(minNum, f1, f2));
|
||||||
|
assertEqX4(SIMD.Float32x4.maxNum(f1, f2), binaryX(maxNum, f1, f2));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
f();
|
|
@ -0,0 +1,48 @@
|
||||||
|
load(libdir + 'simd.js');
|
||||||
|
|
||||||
|
setJitCompilerOption("ion.warmup.trigger", 50);
|
||||||
|
|
||||||
|
function f() {
|
||||||
|
var i4 = SIMD.Int32x4(1, -2, 3, -4);
|
||||||
|
var u4 = SIMD.Uint32x4(1, -2, 3, 0x88000000);
|
||||||
|
var b4 = SIMD.Bool32x4(true, true, false, true);
|
||||||
|
|
||||||
|
|
||||||
|
var bt4 = SIMD.Bool32x4(true, true, true, true);
|
||||||
|
var bf4 = SIMD.Bool32x4(false, false, false, false);
|
||||||
|
|
||||||
|
var v = Math.fround(13.37);
|
||||||
|
var f4 = SIMD.Float32x4(13.37, NaN, Infinity, -0);
|
||||||
|
|
||||||
|
for (var i = 0; i < 150; i++) {
|
||||||
|
assertEq(SIMD.Int32x4.extractLane(i4, 0), 1);
|
||||||
|
assertEq(SIMD.Int32x4.extractLane(i4, 1), -2);
|
||||||
|
assertEq(SIMD.Int32x4.extractLane(i4, 2), 3);
|
||||||
|
assertEq(SIMD.Int32x4.extractLane(i4, 3), -4);
|
||||||
|
|
||||||
|
assertEq(SIMD.Uint32x4.extractLane(u4, 0), 1);
|
||||||
|
assertEq(SIMD.Uint32x4.extractLane(u4, 1), -2 >>> 0);
|
||||||
|
assertEq(SIMD.Uint32x4.extractLane(u4, 2), 3);
|
||||||
|
assertEq(SIMD.Uint32x4.extractLane(u4, 3), 0x88000000);
|
||||||
|
|
||||||
|
assertEq(SIMD.Float32x4.extractLane(f4, 0), v);
|
||||||
|
assertEq(SIMD.Float32x4.extractLane(f4, 1), NaN);
|
||||||
|
assertEq(SIMD.Float32x4.extractLane(f4, 2), Infinity);
|
||||||
|
assertEq(SIMD.Float32x4.extractLane(f4, 3), -0);
|
||||||
|
|
||||||
|
assertEq(SIMD.Bool32x4.extractLane(b4, 0), true);
|
||||||
|
assertEq(SIMD.Bool32x4.extractLane(b4, 1), true);
|
||||||
|
assertEq(SIMD.Bool32x4.extractLane(b4, 2), false);
|
||||||
|
assertEq(SIMD.Bool32x4.extractLane(b4, 3), true);
|
||||||
|
|
||||||
|
assertEq(SIMD.Bool32x4.anyTrue(b4), true);
|
||||||
|
assertEq(SIMD.Bool32x4.allTrue(b4), false);
|
||||||
|
|
||||||
|
assertEq(SIMD.Bool32x4.anyTrue(bt4), true);
|
||||||
|
assertEq(SIMD.Bool32x4.allTrue(bt4), true);
|
||||||
|
assertEq(SIMD.Bool32x4.anyTrue(bf4), false);
|
||||||
|
assertEq(SIMD.Bool32x4.allTrue(bf4), false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
f();
|
|
@ -0,0 +1,81 @@
|
||||||
|
load(libdir + 'simd.js');
|
||||||
|
|
||||||
|
setJitCompilerOption("ion.warmup.trigger", 50);
|
||||||
|
|
||||||
|
function test(i) {
|
||||||
|
assertEqX4(SIMD.Int32x4(), [0, 0, 0, 0]);
|
||||||
|
assertEqX4(SIMD.Int32x4(i), [i, 0, 0, 0]);
|
||||||
|
assertEqX4(SIMD.Int32x4(i, 1), [i, 1, 0, 0]);
|
||||||
|
assertEqX4(SIMD.Int32x4(i, 1, 2), [i, 1, 2, 0]);
|
||||||
|
assertEqX4(SIMD.Int32x4(i, 1, 2, 3), [i, 1, 2, 3]);
|
||||||
|
assertEqX4(SIMD.Int32x4(i, 1, 2, 3, 4), [i, 1, 2, 3]);
|
||||||
|
|
||||||
|
assertEqVecArr(SIMD.Int16x8(), [0, 0, 0, 0, 0, 0, 0, 0]);
|
||||||
|
assertEqVecArr(SIMD.Int16x8(i), [i, 0, 0, 0, 0, 0, 0, 0]);
|
||||||
|
assertEqVecArr(SIMD.Int16x8(i, 1), [i, 1, 0, 0, 0, 0, 0, 0]);
|
||||||
|
assertEqVecArr(SIMD.Int16x8(i, 1, 2), [i, 1, 2, 0, 0, 0, 0, 0]);
|
||||||
|
assertEqVecArr(SIMD.Int16x8(i, 1, 2, 3), [i, 1, 2, 3, 0, 0, 0, 0]);
|
||||||
|
assertEqVecArr(SIMD.Int16x8(i, 1, 2, 3, 4), [i, 1, 2, 3, 4, 0, 0, 0]);
|
||||||
|
assertEqVecArr(SIMD.Int16x8(i, 1, 2, 3, 4, 5, 6),
|
||||||
|
[i, 1, 2, 3, 4, 5, 6, 0]);
|
||||||
|
j = i & 32
|
||||||
|
assertEqVecArr(SIMD.Int8x16(), [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
|
||||||
|
assertEqVecArr(SIMD.Int8x16(j), [j, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
|
||||||
|
assertEqVecArr(SIMD.Int8x16(j, 1), [j, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
|
||||||
|
assertEqVecArr(SIMD.Int8x16(j, 1, 2), [j, 1, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
|
||||||
|
assertEqVecArr(SIMD.Int8x16(j, 1, 2, 3), [j, 1, 2, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
|
||||||
|
assertEqVecArr(SIMD.Int8x16(j, 1, 2, 3, 4), [j, 1, 2, 3, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
|
||||||
|
assertEqVecArr(SIMD.Int8x16(j, 1, 2, 3, 4, 5, 6),
|
||||||
|
[j, 1, 2, 3, 4, 5, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
|
||||||
|
assertEqVecArr(SIMD.Int8x16(j, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12),
|
||||||
|
[j, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 0, 0, 0]);
|
||||||
|
|
||||||
|
assertEqX4(SIMD.Float32x4(), [NaN, NaN, NaN, NaN]);
|
||||||
|
assertEqX4(SIMD.Float32x4(i), [i, NaN, NaN, NaN]);
|
||||||
|
assertEqX4(SIMD.Float32x4(i, 1), [i, 1, NaN, NaN]);
|
||||||
|
assertEqX4(SIMD.Float32x4(i, 1, 2), [i, 1, 2, NaN]);
|
||||||
|
assertEqX4(SIMD.Float32x4(i, 1, 2, 3), [i, 1, 2, 3 ]);
|
||||||
|
assertEqX4(SIMD.Float32x4(i, 1, 2, 3, 4), [i, 1, 2, 3 ]);
|
||||||
|
|
||||||
|
var b = i % 2 > 0 ;
|
||||||
|
assertEqX4(SIMD.Bool32x4(), [false, false, false, false]);
|
||||||
|
assertEqX4(SIMD.Bool32x4(b), [b, false, false, false]);
|
||||||
|
assertEqX4(SIMD.Bool32x4(b, true), [b, true, false, false]);
|
||||||
|
assertEqX4(SIMD.Bool32x4(b, false, true), [b, false, true, false]);
|
||||||
|
assertEqX4(SIMD.Bool32x4(b, false, true, true), [b, false, true, true ]);
|
||||||
|
assertEqX4(SIMD.Bool32x4(b, false, true, true, true), [b, false, true, true ]);
|
||||||
|
|
||||||
|
assertEqVecArr(SIMD.Bool16x8(),
|
||||||
|
[false, false, false, false, false, false, false, false]);
|
||||||
|
assertEqVecArr(SIMD.Bool16x8(b),
|
||||||
|
[b, false, false, false, false, false, false, false]);
|
||||||
|
assertEqVecArr(SIMD.Bool16x8(b, true),
|
||||||
|
[b, true, false, false, false, false, false, false]);
|
||||||
|
assertEqVecArr(SIMD.Bool16x8(b, false, true),
|
||||||
|
[b, false, true, false, false, false, false, false]);
|
||||||
|
assertEqVecArr(SIMD.Bool16x8(b, false, true, true),
|
||||||
|
[b, false, true, true, false, false, false, false]);
|
||||||
|
assertEqVecArr(SIMD.Bool16x8(b, false, true, true, true),
|
||||||
|
[b, false, true, true, true, false, false, false]);
|
||||||
|
assertEqVecArr(SIMD.Bool16x8(b, false, true, true, true, true),
|
||||||
|
[b, false, true, true, true, true, false, false]);
|
||||||
|
|
||||||
|
assertEqVecArr(SIMD.Bool8x16(),
|
||||||
|
[false, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false]);
|
||||||
|
assertEqVecArr(SIMD.Bool8x16(b),
|
||||||
|
[b, false, false, false, false, false, false, false, false, false, false, false, false, false, false, false]);
|
||||||
|
assertEqVecArr(SIMD.Bool8x16(b, true),
|
||||||
|
[b, true, false, false, false, false, false, false, false, false, false, false, false, false, false, false]);
|
||||||
|
assertEqVecArr(SIMD.Bool8x16(b, false, true),
|
||||||
|
[b, false, true, false, false, false, false, false, false, false, false, false, false, false, false, false]);
|
||||||
|
assertEqVecArr(SIMD.Bool8x16(b, false, true, true),
|
||||||
|
[b, false, true, true, false, false, false, false, false, false, false, false, false, false, false, false]);
|
||||||
|
assertEqVecArr(SIMD.Bool8x16(b, false, true, true, true),
|
||||||
|
[b, false, true, true, true, false, false, false, false, false, false, false, false, false, false, false]);
|
||||||
|
assertEqVecArr(SIMD.Bool8x16(b, false, true, true, true, true, false, true, true, true),
|
||||||
|
[b, false, true, true, true, true, false, true, true, true, false, false, false, false, false, false]);
|
||||||
|
}
|
||||||
|
|
||||||
|
for(var i=0; i<300; i++) {
|
||||||
|
test(i);
|
||||||
|
}
|
|
@ -0,0 +1,123 @@
|
||||||
|
load(libdir + 'simd.js');
|
||||||
|
|
||||||
|
setJitCompilerOption("ion.warmup.trigger", 40);
|
||||||
|
|
||||||
|
function f() {
|
||||||
|
var f32 = new Float32Array(16);
|
||||||
|
for (var i = 0; i < 16; i++)
|
||||||
|
f32[i] = i + 1;
|
||||||
|
|
||||||
|
var f64 = new Float64Array(f32.buffer);
|
||||||
|
var i32 = new Int32Array(f32.buffer);
|
||||||
|
var u32 = new Uint32Array(f32.buffer);
|
||||||
|
var i16 = new Int16Array(f32.buffer);
|
||||||
|
var u16 = new Uint16Array(f32.buffer);
|
||||||
|
var i8 = new Int8Array(f32.buffer);
|
||||||
|
var u8 = new Uint8Array(f32.buffer);
|
||||||
|
|
||||||
|
function testLoad() {
|
||||||
|
assertEqX4(SIMD.Float32x4.load(f64, 0), [1,2,3,4]);
|
||||||
|
assertEqX4(SIMD.Float32x4.load(f32, 1), [2,3,4,5]);
|
||||||
|
assertEqX4(SIMD.Float32x4.load(i32, 2), [3,4,5,6]);
|
||||||
|
assertEqX4(SIMD.Float32x4.load(i16, 3 << 1), [4,5,6,7]);
|
||||||
|
assertEqX4(SIMD.Float32x4.load(u16, 4 << 1), [5,6,7,8]);
|
||||||
|
assertEqX4(SIMD.Float32x4.load(i8 , 5 << 2), [6,7,8,9]);
|
||||||
|
assertEqX4(SIMD.Float32x4.load(u8 , 6 << 2), [7,8,9,10]);
|
||||||
|
|
||||||
|
assertEqX4(SIMD.Float32x4.load(f64, (16 >> 1) - (4 >> 1)), [13,14,15,16]);
|
||||||
|
assertEqX4(SIMD.Float32x4.load(f32, 16 - 4), [13,14,15,16]);
|
||||||
|
assertEqX4(SIMD.Float32x4.load(i32, 16 - 4), [13,14,15,16]);
|
||||||
|
assertEqX4(SIMD.Float32x4.load(i16, (16 << 1) - (4 << 1)), [13,14,15,16]);
|
||||||
|
assertEqX4(SIMD.Float32x4.load(u16, (16 << 1) - (4 << 1)), [13,14,15,16]);
|
||||||
|
assertEqX4(SIMD.Float32x4.load(i8, (16 << 2) - (4 << 2)), [13,14,15,16]);
|
||||||
|
assertEqX4(SIMD.Float32x4.load(u8, (16 << 2) - (4 << 2)), [13,14,15,16]);
|
||||||
|
}
|
||||||
|
|
||||||
|
function testLoad1() {
|
||||||
|
assertEqX4(SIMD.Float32x4.load1(f64, 0), [1,0,0,0]);
|
||||||
|
assertEqX4(SIMD.Float32x4.load1(f32, 1), [2,0,0,0]);
|
||||||
|
assertEqX4(SIMD.Float32x4.load1(i32, 2), [3,0,0,0]);
|
||||||
|
assertEqX4(SIMD.Float32x4.load1(i16, 3 << 1), [4,0,0,0]);
|
||||||
|
assertEqX4(SIMD.Float32x4.load1(u16, 4 << 1), [5,0,0,0]);
|
||||||
|
assertEqX4(SIMD.Float32x4.load1(i8 , 5 << 2), [6,0,0,0]);
|
||||||
|
assertEqX4(SIMD.Float32x4.load1(u8 , 6 << 2), [7,0,0,0]);
|
||||||
|
|
||||||
|
assertEqX4(SIMD.Float32x4.load1(f64, (16 >> 1) - (4 >> 1)), [13,0,0,0]);
|
||||||
|
assertEqX4(SIMD.Float32x4.load1(f32, 16 - 4), [13,0,0,0]);
|
||||||
|
assertEqX4(SIMD.Float32x4.load1(i32, 16 - 4), [13,0,0,0]);
|
||||||
|
assertEqX4(SIMD.Float32x4.load1(i16, (16 << 1) - (4 << 1)), [13,0,0,0]);
|
||||||
|
assertEqX4(SIMD.Float32x4.load1(u16, (16 << 1) - (4 << 1)), [13,0,0,0]);
|
||||||
|
assertEqX4(SIMD.Float32x4.load1(i8, (16 << 2) - (4 << 2)), [13,0,0,0]);
|
||||||
|
assertEqX4(SIMD.Float32x4.load1(u8, (16 << 2) - (4 << 2)), [13,0,0,0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
function testLoad2() {
|
||||||
|
assertEqX4(SIMD.Float32x4.load2(f64, 0), [1,2,0,0]);
|
||||||
|
assertEqX4(SIMD.Float32x4.load2(f32, 1), [2,3,0,0]);
|
||||||
|
assertEqX4(SIMD.Float32x4.load2(i32, 2), [3,4,0,0]);
|
||||||
|
assertEqX4(SIMD.Float32x4.load2(i16, 3 << 1), [4,5,0,0]);
|
||||||
|
assertEqX4(SIMD.Float32x4.load2(u16, 4 << 1), [5,6,0,0]);
|
||||||
|
assertEqX4(SIMD.Float32x4.load2(i8 , 5 << 2), [6,7,0,0]);
|
||||||
|
assertEqX4(SIMD.Float32x4.load2(u8 , 6 << 2), [7,8,0,0]);
|
||||||
|
|
||||||
|
assertEqX4(SIMD.Float32x4.load2(f64, (16 >> 1) - (4 >> 1)), [13,14,0,0]);
|
||||||
|
assertEqX4(SIMD.Float32x4.load2(f32, 16 - 4), [13,14,0,0]);
|
||||||
|
assertEqX4(SIMD.Float32x4.load2(i32, 16 - 4), [13,14,0,0]);
|
||||||
|
assertEqX4(SIMD.Float32x4.load2(i16, (16 << 1) - (4 << 1)), [13,14,0,0]);
|
||||||
|
assertEqX4(SIMD.Float32x4.load2(u16, (16 << 1) - (4 << 1)), [13,14,0,0]);
|
||||||
|
assertEqX4(SIMD.Float32x4.load2(i8, (16 << 2) - (4 << 2)), [13,14,0,0]);
|
||||||
|
assertEqX4(SIMD.Float32x4.load2(u8, (16 << 2) - (4 << 2)), [13,14,0,0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
function testLoad3() {
|
||||||
|
assertEqX4(SIMD.Float32x4.load3(f64, 0), [1,2,3,0]);
|
||||||
|
assertEqX4(SIMD.Float32x4.load3(f32, 1), [2,3,4,0]);
|
||||||
|
assertEqX4(SIMD.Float32x4.load3(i32, 2), [3,4,5,0]);
|
||||||
|
assertEqX4(SIMD.Float32x4.load3(i16, 3 << 1), [4,5,6,0]);
|
||||||
|
assertEqX4(SIMD.Float32x4.load3(u16, 4 << 1), [5,6,7,0]);
|
||||||
|
assertEqX4(SIMD.Float32x4.load3(i8 , 5 << 2), [6,7,8,0]);
|
||||||
|
assertEqX4(SIMD.Float32x4.load3(u8 , 6 << 2), [7,8,9,0]);
|
||||||
|
|
||||||
|
assertEqX4(SIMD.Float32x4.load3(f64, (16 >> 1) - (4 >> 1)), [13,14,15,0]);
|
||||||
|
assertEqX4(SIMD.Float32x4.load3(f32, 16 - 4), [13,14,15,0]);
|
||||||
|
assertEqX4(SIMD.Float32x4.load3(i32, 16 - 4), [13,14,15,0]);
|
||||||
|
assertEqX4(SIMD.Float32x4.load3(i16, (16 << 1) - (4 << 1)), [13,14,15,0]);
|
||||||
|
assertEqX4(SIMD.Float32x4.load3(u16, (16 << 1) - (4 << 1)), [13,14,15,0]);
|
||||||
|
assertEqX4(SIMD.Float32x4.load3(i8, (16 << 2) - (4 << 2)), [13,14,15,0]);
|
||||||
|
assertEqX4(SIMD.Float32x4.load3(u8, (16 << 2) - (4 << 2)), [13,14,15,0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var i = 0; i < 150; i++) {
|
||||||
|
testLoad();
|
||||||
|
testLoad1();
|
||||||
|
testLoad2();
|
||||||
|
testLoad3();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
f();
|
||||||
|
|
||||||
|
function testBailout(uglyDuckling) {
|
||||||
|
var f32 = new Float32Array(16);
|
||||||
|
for (var i = 0; i < 16; i++)
|
||||||
|
f32[i] = i + 1;
|
||||||
|
|
||||||
|
var i8 = new Int8Array(f32.buffer);
|
||||||
|
|
||||||
|
for (var i = 0; i < 150; i++) {
|
||||||
|
var caught = false;
|
||||||
|
try {
|
||||||
|
SIMD.Float32x4.load(i8, (i < 149) ? 0 : uglyDuckling);
|
||||||
|
} catch (e) {
|
||||||
|
print(e);
|
||||||
|
assertEq(e instanceof RangeError, true);
|
||||||
|
caught = true;
|
||||||
|
}
|
||||||
|
assertEq(i < 149 || caught, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
print('Testing range checks...');
|
||||||
|
testBailout(-1);
|
||||||
|
testBailout(-15);
|
||||||
|
testBailout(12 * 4 + 1);
|
|
@ -0,0 +1,29 @@
|
||||||
|
load(libdir + 'simd.js');
|
||||||
|
|
||||||
|
if (typeof SIMD === "undefined")
|
||||||
|
quit();
|
||||||
|
|
||||||
|
setJitCompilerOption("baseline.warmup.trigger", 10);
|
||||||
|
setJitCompilerOption("ion.warmup.trigger", 30);
|
||||||
|
|
||||||
|
var i4 = SIMD.Int32x4;
|
||||||
|
var i4sub = SIMD.Int32x4.sub;
|
||||||
|
|
||||||
|
function simdbox(i) {
|
||||||
|
return i4(i, i, i, i);
|
||||||
|
}
|
||||||
|
|
||||||
|
function test() {
|
||||||
|
var arr = [];
|
||||||
|
|
||||||
|
// overflow the nursery with live SIMD objects.
|
||||||
|
for (var i = 0; i < 100000; i++) {
|
||||||
|
arr.push(simdbox(i));
|
||||||
|
}
|
||||||
|
|
||||||
|
return arr;
|
||||||
|
}
|
||||||
|
|
||||||
|
var arr = test();
|
||||||
|
for (var i = 0; i < arr.length; i++)
|
||||||
|
assertEqX4(arr[i], [i, i, i, i]);
|
|
@ -0,0 +1,70 @@
|
||||||
|
load(libdir + 'simd.js');
|
||||||
|
|
||||||
|
if (!this.hasOwnProperty("SIMD"))
|
||||||
|
quit();
|
||||||
|
|
||||||
|
// This test case ensure that if we are able to optimize SIMD, then we can use
|
||||||
|
// recover instructions to get rid of the allocations. So, there is no value
|
||||||
|
// (and the test case would fail) if we are not able to inline SIMD
|
||||||
|
// constructors.
|
||||||
|
if (!isSimdAvailable())
|
||||||
|
quit();
|
||||||
|
|
||||||
|
setJitCompilerOption("baseline.warmup.trigger", 10);
|
||||||
|
setJitCompilerOption("ion.warmup.trigger", 20);
|
||||||
|
|
||||||
|
// This function is used to cause an invalidation after having removed a branch
|
||||||
|
// after DCE. This is made to check if we correctly recover an array
|
||||||
|
// allocation.
|
||||||
|
var uceFault = function (i) {
|
||||||
|
if (i > 98)
|
||||||
|
uceFault = function (i) { return true; };
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Check that we can correctly recover a boxed value.
|
||||||
|
var uceFault_simdBox_i4 = eval(uneval(uceFault).replace('uceFault', 'uceFault_simdBox_i4'));
|
||||||
|
function simdBox_i4(i) {
|
||||||
|
var a = SIMD.Int32x4(i, i, i, i);
|
||||||
|
if (uceFault_simdBox_i4(i) || uceFault_simdBox_i4(i))
|
||||||
|
assertEqX4(a, [i, i, i, i]);
|
||||||
|
assertRecoveredOnBailout(a, true);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
var uceFault_simdBox_u4 = eval(uneval(uceFault).replace('uceFault', 'uceFault_simdBox_u4'));
|
||||||
|
function simdBox_u4(i) {
|
||||||
|
var a = SIMD.Uint32x4(i, 98 - i, i + 0x7ffffff0, i + 0xffffff00);
|
||||||
|
if (uceFault_simdBox_u4(i) || uceFault_simdBox_u4(i))
|
||||||
|
assertEqX4(a, [i, 98 - i, i + 0x7ffffff0, i + 0xffffff00].map(x => x >>> 0));
|
||||||
|
assertRecoveredOnBailout(a, true);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
var uceFault_simdBox_f4 = eval(uneval(uceFault).replace('uceFault', 'uceFault_simdBox_f4'));
|
||||||
|
function simdBox_f4(i) {
|
||||||
|
var a = SIMD.Float32x4(i, i + 0.1, i + 0.2, i + 0.3);
|
||||||
|
if (uceFault_simdBox_f4(i) || uceFault_simdBox_f4(i))
|
||||||
|
assertEqX4(a, [i, i + 0.1, i + 0.2, i + 0.3].map(Math.fround));
|
||||||
|
assertRecoveredOnBailout(a, true);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
var uceFault_simdBox_b4 = eval(uneval(uceFault).replace('uceFault', 'uceFault_simdBox_b4'));
|
||||||
|
function simdBox_b4(i) {
|
||||||
|
var val1 = i%2 === 0,
|
||||||
|
val2 = !val1;
|
||||||
|
|
||||||
|
var a = SIMD.Bool32x4(val1, val2, val1, val2);
|
||||||
|
if (uceFault_simdBox_b4(i) || uceFault_simdBox_b4(i))
|
||||||
|
assertEqX4(a, [val1, val2, val1, val2]);
|
||||||
|
assertRecoveredOnBailout(a, true);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var i = 0; i < 100; i++) {
|
||||||
|
simdBox_i4(i);
|
||||||
|
simdBox_u4(i);
|
||||||
|
simdBox_f4(i);
|
||||||
|
simdBox_b4(i);
|
||||||
|
}
|
|
@ -0,0 +1,181 @@
|
||||||
|
load(libdir + 'simd.js');
|
||||||
|
|
||||||
|
setJitCompilerOption("ion.warmup.trigger", 50);
|
||||||
|
|
||||||
|
function f() {
|
||||||
|
var f4 = SIMD.Float32x4(1, 2, 3, 4);
|
||||||
|
var i4 = SIMD.Int32x4(1, 2, 3, 4);
|
||||||
|
var b4 = SIMD.Bool32x4(true, false, true, false);
|
||||||
|
|
||||||
|
for (var i = 0; i < 150; i++) {
|
||||||
|
assertEqX4(SIMD.Int32x4.replaceLane(i4, 0, 42), [42, 2, 3, 4]);
|
||||||
|
assertEqX4(SIMD.Int32x4.replaceLane(i4, 1, 42), [1, 42, 3, 4]);
|
||||||
|
assertEqX4(SIMD.Int32x4.replaceLane(i4, 2, 42), [1, 2, 42, 4]);
|
||||||
|
assertEqX4(SIMD.Int32x4.replaceLane(i4, 3, 42), [1, 2, 3, 42]);
|
||||||
|
|
||||||
|
assertEqX4(SIMD.Float32x4.replaceLane(f4, 0, 42), [42, 2, 3, 4]);
|
||||||
|
assertEqX4(SIMD.Float32x4.replaceLane(f4, 1, 42), [1, 42, 3, 4]);
|
||||||
|
assertEqX4(SIMD.Float32x4.replaceLane(f4, 2, 42), [1, 2, 42, 4]);
|
||||||
|
assertEqX4(SIMD.Float32x4.replaceLane(f4, 3, 42), [1, 2, 3, 42]);
|
||||||
|
|
||||||
|
assertEqX4(SIMD.Bool32x4.replaceLane(b4, 0, false), [false, false, true, false]);
|
||||||
|
assertEqX4(SIMD.Bool32x4.replaceLane(b4, 1, true), [true, true, true, false]);
|
||||||
|
assertEqX4(SIMD.Bool32x4.replaceLane(b4, 2, false), [true, false, false, false]);
|
||||||
|
assertEqX4(SIMD.Bool32x4.replaceLane(b4, 3, true), [true, false, true, true]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
f();
|
||||||
|
|
||||||
|
function e() {
|
||||||
|
var f4 = SIMD.Float32x4(1, 2, 3, 4);
|
||||||
|
var i4 = SIMD.Int32x4(1, 2, 3, 4);
|
||||||
|
var b4 = SIMD.Bool32x4(true, false, true, false);
|
||||||
|
|
||||||
|
for (let i = 0; i < 150; i++) {
|
||||||
|
let caught = false;
|
||||||
|
try {
|
||||||
|
let x = SIMD.Int32x4.replaceLane(i < 149 ? i4 : f4, 0, 42);
|
||||||
|
} catch(e) {
|
||||||
|
assertEq(e instanceof TypeError, true);
|
||||||
|
assertEq(i, 149);
|
||||||
|
caught = true;
|
||||||
|
}
|
||||||
|
assertEq(i < 149 || caught, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let i = 0; i < 150; i++) {
|
||||||
|
let caught = false;
|
||||||
|
try {
|
||||||
|
let x = SIMD.Int32x4.replaceLane(i < 149 ? i4 : b4, 0, 42);
|
||||||
|
} catch(e) {
|
||||||
|
assertEq(e instanceof TypeError, true);
|
||||||
|
assertEq(i, 149);
|
||||||
|
caught = true;
|
||||||
|
}
|
||||||
|
assertEq(i < 149 || caught, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let i = 0; i < 150; i++) {
|
||||||
|
let caught = false;
|
||||||
|
try {
|
||||||
|
let x = SIMD.Int32x4.replaceLane(i4, i < 149 ? 0 : 4, 42);
|
||||||
|
} catch(e) {
|
||||||
|
assertEq(e instanceof RangeError, true);
|
||||||
|
assertEq(i, 149);
|
||||||
|
caught = true;
|
||||||
|
}
|
||||||
|
assertEq(i < 149 || caught, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let i = 0; i < 150; i++) {
|
||||||
|
let caught = false;
|
||||||
|
try {
|
||||||
|
let x = SIMD.Int32x4.replaceLane(i4, i < 149 ? 0 : 1.1, 42);
|
||||||
|
} catch(e) {
|
||||||
|
assertEq(e instanceof RangeError, true);
|
||||||
|
assertEq(i, 149);
|
||||||
|
caught = true;
|
||||||
|
}
|
||||||
|
assertEq(i < 149 || caught, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let i = 0; i < 150; i++) {
|
||||||
|
let caught = false;
|
||||||
|
try {
|
||||||
|
let x = SIMD.Float32x4.replaceLane(i < 149 ? f4 : i4, 0, 42);
|
||||||
|
} catch(e) {
|
||||||
|
assertEq(e instanceof TypeError, true);
|
||||||
|
assertEq(i, 149);
|
||||||
|
caught = true;
|
||||||
|
}
|
||||||
|
assertEq(i < 149 || caught, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let i = 0; i < 150; i++) {
|
||||||
|
let caught = false;
|
||||||
|
try {
|
||||||
|
let x = SIMD.Float32x4.replaceLane(i < 149 ? f4 : b4, 0, 42);
|
||||||
|
} catch(e) {
|
||||||
|
assertEq(e instanceof TypeError, true);
|
||||||
|
assertEq(i, 149);
|
||||||
|
caught = true;
|
||||||
|
}
|
||||||
|
assertEq(i < 149 || caught, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let i = 0; i < 150; i++) {
|
||||||
|
let caught = false;
|
||||||
|
try {
|
||||||
|
let x = SIMD.Float32x4.replaceLane(f4, i < 149 ? 0 : 4, 42);
|
||||||
|
} catch(e) {
|
||||||
|
assertEq(e instanceof RangeError, true);
|
||||||
|
assertEq(i, 149);
|
||||||
|
caught = true;
|
||||||
|
}
|
||||||
|
assertEq(i < 149 || caught, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let i = 0; i < 150; i++) {
|
||||||
|
let caught = false;
|
||||||
|
try {
|
||||||
|
let x = SIMD.Float32x4.replaceLane(f4, i < 149 ? 0 : 1.1, 42);
|
||||||
|
} catch(e) {
|
||||||
|
assertEq(e instanceof RangeError, true);
|
||||||
|
assertEq(i, 149);
|
||||||
|
caught = true;
|
||||||
|
}
|
||||||
|
assertEq(i < 149 || caught, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let i = 0; i < 150; i++) {
|
||||||
|
let caught = false;
|
||||||
|
try {
|
||||||
|
let x = SIMD.Bool32x4.replaceLane(i < 149 ? b4 : i4, 0, true);
|
||||||
|
} catch(e) {
|
||||||
|
assertEq(e instanceof TypeError, true);
|
||||||
|
assertEq(i, 149);
|
||||||
|
caught = true;
|
||||||
|
}
|
||||||
|
assertEq(i < 149 || caught, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let i = 0; i < 150; i++) {
|
||||||
|
let caught = false;
|
||||||
|
try {
|
||||||
|
let x = SIMD.Bool32x4.replaceLane(i < 149 ? b4 : f4, 0, true);
|
||||||
|
} catch(e) {
|
||||||
|
assertEq(e instanceof TypeError, true);
|
||||||
|
assertEq(i, 149);
|
||||||
|
caught = true;
|
||||||
|
}
|
||||||
|
assertEq(i < 149 || caught, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let i = 0; i < 150; i++) {
|
||||||
|
let caught = false;
|
||||||
|
try {
|
||||||
|
let x = SIMD.Bool32x4.replaceLane(b4, i < 149 ? 0 : 4, true);
|
||||||
|
} catch(e) {
|
||||||
|
assertEq(e instanceof RangeError, true);
|
||||||
|
assertEq(i, 149);
|
||||||
|
caught = true;
|
||||||
|
}
|
||||||
|
assertEq(i < 149 || caught, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let i = 0; i < 150; i++) {
|
||||||
|
let caught = false;
|
||||||
|
try {
|
||||||
|
let x = SIMD.Bool32x4.replaceLane(b4, i < 149 ? 0 : 1.1, true);
|
||||||
|
} catch(e) {
|
||||||
|
assertEq(e instanceof RangeError, true);
|
||||||
|
assertEq(i, 149);
|
||||||
|
caught = true;
|
||||||
|
}
|
||||||
|
assertEq(i < 149 || caught, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
e();
|
|
@ -0,0 +1,37 @@
|
||||||
|
load(libdir + 'simd.js');
|
||||||
|
|
||||||
|
setJitCompilerOption("ion.warmup.trigger", 50);
|
||||||
|
|
||||||
|
const INT8_MIN = -128;
|
||||||
|
const INT8_MAX = 127;
|
||||||
|
const UINT8_MAX = 255;
|
||||||
|
|
||||||
|
function sat8(x) {
|
||||||
|
if (x < INT8_MIN) return INT8_MIN;
|
||||||
|
if (x > INT8_MAX) return INT8_MAX;
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
|
||||||
|
function usat8(x) {
|
||||||
|
if (x < 0) return 0;
|
||||||
|
if (x > UINT8_MAX) return UINT8_MAX;
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
|
||||||
|
function f() {
|
||||||
|
var i1 = SIMD.Int8x16(1, 100, 3, 4);
|
||||||
|
var i2 = SIMD.Int8x16(4, 30, 2, 1);
|
||||||
|
|
||||||
|
var u1 = SIMD.Uint8x16(1, 2, 3, 4);
|
||||||
|
var u2 = SIMD.Uint8x16(4, 3, 2, 1);
|
||||||
|
|
||||||
|
for (var i = 0; i < 150; i++) {
|
||||||
|
assertEqX4(SIMD.Int8x16.addSaturate(i1, i2), binaryX((x, y) => sat8(x + y), i1, i2));
|
||||||
|
assertEqX4(SIMD.Int8x16.subSaturate(i1, i2), binaryX((x, y) => sat8(x - y), i1, i2));
|
||||||
|
|
||||||
|
assertEqX4(SIMD.Uint8x16.addSaturate(u1, u2), binaryX((x, y) => usat8(x + y), u1, u2));
|
||||||
|
assertEqX4(SIMD.Uint8x16.subSaturate(u1, u2), binaryX((x, y) => usat8(x - y), u1, u2));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
f();
|
|
@ -0,0 +1,35 @@
|
||||||
|
load(libdir + 'simd.js');
|
||||||
|
|
||||||
|
setJitCompilerOption("ion.warmup.trigger", 50);
|
||||||
|
|
||||||
|
function select(type, mask, ifTrue, ifFalse) {
|
||||||
|
var arr = [];
|
||||||
|
for (var i = 0; i < 4; i++) {
|
||||||
|
var selector = SIMD.Bool32x4.extractLane(mask, i);
|
||||||
|
arr.push(type.extractLane(selector ? ifTrue : ifFalse, i));
|
||||||
|
}
|
||||||
|
return arr;
|
||||||
|
}
|
||||||
|
|
||||||
|
function f() {
|
||||||
|
var f1 = SIMD.Float32x4(1, 2, 3, 4);
|
||||||
|
var f2 = SIMD.Float32x4(NaN, Infinity, 3.14, -0);
|
||||||
|
|
||||||
|
var i1 = SIMD.Int32x4(2, 3, 5, 8);
|
||||||
|
var i2 = SIMD.Int32x4(13, 37, 24, 42);
|
||||||
|
|
||||||
|
var TTFT = SIMD.Bool32x4(true, true, false, true);
|
||||||
|
var TFTF = SIMD.Bool32x4(true, false, true, false);
|
||||||
|
|
||||||
|
var mask = SIMD.Int32x4(0xdeadbeef, 0xbaadf00d, 0x00ff1ce, 0xdeadc0de);
|
||||||
|
|
||||||
|
for (var i = 0; i < 150; i++) {
|
||||||
|
assertEqX4(SIMD.Float32x4.select(TTFT, f1, f2), select(SIMD.Float32x4, TTFT, f1, f2));
|
||||||
|
assertEqX4(SIMD.Float32x4.select(TFTF, f1, f2), select(SIMD.Float32x4, TFTF, f1, f2));
|
||||||
|
|
||||||
|
assertEqX4(SIMD.Int32x4.select(TFTF, i1, i2), select(SIMD.Int32x4, TFTF, i1, i2));
|
||||||
|
assertEqX4(SIMD.Int32x4.select(TTFT, i1, i2), select(SIMD.Int32x4, TTFT, i1, i2));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
f();
|
|
@ -0,0 +1,75 @@
|
||||||
|
load(libdir + 'simd.js');
|
||||||
|
|
||||||
|
setJitCompilerOption("ion.warmup.trigger", 50);
|
||||||
|
|
||||||
|
function curry(f, arg) { return f.bind(null, arg); }
|
||||||
|
|
||||||
|
function binaryLsh(count, v) { count &= 31; return (v << count) | 0; }
|
||||||
|
function lsh(count) { return curry(binaryLsh, count); }
|
||||||
|
|
||||||
|
function binaryRsh(count, v) { count &= 31; return (v >> count) | 0; }
|
||||||
|
function rsh(count) { return curry(binaryRsh, count); }
|
||||||
|
|
||||||
|
function binaryUlsh(count, v) { count &= 31; return (v << count) >>> 0; }
|
||||||
|
function ulsh(count) { return curry(binaryUlsh, count); }
|
||||||
|
|
||||||
|
function binaryUrsh(count, v) { count &= 31; return v >>> count; }
|
||||||
|
function ursh(count) { return curry(binaryUrsh, count); }
|
||||||
|
|
||||||
|
function f() {
|
||||||
|
var v = SIMD.Int32x4(1, 2, -3, 4);
|
||||||
|
var u = SIMD.Uint32x4(1, 0x55005500, -3, 0xaa00aa00);
|
||||||
|
var a = [1, 2, -3, 4];
|
||||||
|
var b = [1, 0x55005500, -3, 0xaa00aa00];
|
||||||
|
|
||||||
|
var shifts = [-2, -1, 0, 1, 31, 32, 33];
|
||||||
|
|
||||||
|
var r;
|
||||||
|
for (var i = 0; i < 150; i++) {
|
||||||
|
// Constant shift counts
|
||||||
|
assertEqX4(SIMD.Int32x4.shiftLeftByScalar(v, -1), a.map(lsh(-1)));
|
||||||
|
assertEqX4(SIMD.Int32x4.shiftLeftByScalar(v, 0), a.map(lsh(0)));
|
||||||
|
assertEqX4(SIMD.Int32x4.shiftLeftByScalar(v, 1), a.map(lsh(1)));
|
||||||
|
assertEqX4(SIMD.Int32x4.shiftLeftByScalar(v, 2), a.map(lsh(2)));
|
||||||
|
assertEqX4(SIMD.Int32x4.shiftLeftByScalar(v, 31), a.map(lsh(31)));
|
||||||
|
assertEqX4(SIMD.Int32x4.shiftLeftByScalar(v, 32), a.map(lsh(32)));
|
||||||
|
assertEqX4(SIMD.Int32x4.shiftLeftByScalar(v, 33), a.map(lsh(33)));
|
||||||
|
|
||||||
|
assertEqX4(SIMD.Int32x4.shiftRightByScalar(v, -1), a.map(rsh(31)));
|
||||||
|
assertEqX4(SIMD.Int32x4.shiftRightByScalar(v, 0), a.map(rsh(0)));
|
||||||
|
assertEqX4(SIMD.Int32x4.shiftRightByScalar(v, 1), a.map(rsh(1)));
|
||||||
|
assertEqX4(SIMD.Int32x4.shiftRightByScalar(v, 2), a.map(rsh(2)));
|
||||||
|
assertEqX4(SIMD.Int32x4.shiftRightByScalar(v, 31), a.map(rsh(31)));
|
||||||
|
assertEqX4(SIMD.Int32x4.shiftRightByScalar(v, 32), a.map(rsh(32)));
|
||||||
|
assertEqX4(SIMD.Int32x4.shiftRightByScalar(v, 33), a.map(rsh(33)));
|
||||||
|
|
||||||
|
assertEqX4(SIMD.Uint32x4.shiftLeftByScalar(u, -1), b.map(ulsh(-1)));
|
||||||
|
assertEqX4(SIMD.Uint32x4.shiftLeftByScalar(u, 0), b.map(ulsh(0)));
|
||||||
|
assertEqX4(SIMD.Uint32x4.shiftLeftByScalar(u, 1), b.map(ulsh(1)));
|
||||||
|
assertEqX4(SIMD.Uint32x4.shiftLeftByScalar(u, 2), b.map(ulsh(2)));
|
||||||
|
assertEqX4(SIMD.Uint32x4.shiftLeftByScalar(u, 31), b.map(ulsh(31)));
|
||||||
|
assertEqX4(SIMD.Uint32x4.shiftLeftByScalar(u, 32), b.map(ulsh(32)));
|
||||||
|
assertEqX4(SIMD.Uint32x4.shiftLeftByScalar(u, 33), b.map(ulsh(33)));
|
||||||
|
|
||||||
|
assertEqX4(SIMD.Uint32x4.shiftRightByScalar(u, -1), b.map(ursh(-1)));
|
||||||
|
assertEqX4(SIMD.Uint32x4.shiftRightByScalar(u, 0), b.map(ursh(0)));
|
||||||
|
assertEqX4(SIMD.Uint32x4.shiftRightByScalar(u, 1), b.map(ursh(1)));
|
||||||
|
assertEqX4(SIMD.Uint32x4.shiftRightByScalar(u, 2), b.map(ursh(2)));
|
||||||
|
assertEqX4(SIMD.Uint32x4.shiftRightByScalar(u, 31), b.map(ursh(31)));
|
||||||
|
assertEqX4(SIMD.Uint32x4.shiftRightByScalar(u, 32), b.map(ursh(32)));
|
||||||
|
assertEqX4(SIMD.Uint32x4.shiftRightByScalar(u, 33), b.map(ursh(33)));
|
||||||
|
|
||||||
|
// Non constant shift counts
|
||||||
|
var c = shifts[i % shifts.length];
|
||||||
|
|
||||||
|
assertEqX4(SIMD.Int32x4.shiftLeftByScalar(v, c), a.map(lsh(c)));
|
||||||
|
assertEqX4(SIMD.Int32x4.shiftRightByScalar(v, c), a.map(rsh(c)));
|
||||||
|
|
||||||
|
assertEqX4(SIMD.Uint32x4.shiftLeftByScalar(u, c), b.map(ulsh(c)));
|
||||||
|
assertEqX4(SIMD.Uint32x4.shiftRightByScalar(u, c), b.map(ursh(c)));
|
||||||
|
}
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
f();
|
||||||
|
|
|
@ -0,0 +1,86 @@
|
||||||
|
load(libdir + 'simd.js');
|
||||||
|
|
||||||
|
setJitCompilerOption("ion.warmup.trigger", 50);
|
||||||
|
|
||||||
|
function f() {
|
||||||
|
var i1 = SIMD.Int32x4(1, 2, 3, 4);
|
||||||
|
var i2 = SIMD.Int32x4(5, 6, 7, 8);
|
||||||
|
|
||||||
|
var leet = Math.fround(13.37);
|
||||||
|
var f1 = SIMD.Float32x4(-.5, -0, Infinity, leet);
|
||||||
|
var f2 = SIMD.Float32x4(42, .5, 23, -10);
|
||||||
|
|
||||||
|
// computes all rotations of a given array
|
||||||
|
function *gen(arr) {
|
||||||
|
var previous = arr.slice().splice(0, 4);
|
||||||
|
var i = 4;
|
||||||
|
for (var j = 0; j < 8; j++) {
|
||||||
|
yield previous.slice();
|
||||||
|
previous = previous.splice(1, previous.length - 1);
|
||||||
|
previous.push(arr[i]);
|
||||||
|
i = (i + 1) % arr.length;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var compI = [];
|
||||||
|
var baseI = [];
|
||||||
|
for (var i = 0; i < 8; i++)
|
||||||
|
baseI.push(SIMD.Int32x4.extractLane(i < 4 ? i1 : i2, i % 4));
|
||||||
|
for (var k of gen(baseI))
|
||||||
|
compI.push(k);
|
||||||
|
|
||||||
|
var compF = [];
|
||||||
|
var baseF = [];
|
||||||
|
for (var i = 0; i < 8; i++)
|
||||||
|
baseF.push(SIMD.Float32x4.extractLane(i < 4 ? f1 : f2, i % 4));
|
||||||
|
for (var k of gen(baseF))
|
||||||
|
compF.push(k);
|
||||||
|
|
||||||
|
for (var i = 0; i < 150; i++) {
|
||||||
|
// Variable lanes
|
||||||
|
var r = SIMD.Float32x4.shuffle(f1, f2, i % 8, (i + 1) % 8, (i + 2) % 8, (i + 3) % 8);
|
||||||
|
assertEqX4(r, compF[i % 8]);
|
||||||
|
|
||||||
|
// Constant lanes
|
||||||
|
assertEqX4(SIMD.Float32x4.shuffle(f1, f2, 3, 2, 4, 5), [leet, Infinity, 42, .5]);
|
||||||
|
|
||||||
|
// Variable lanes
|
||||||
|
var r = SIMD.Int32x4.shuffle(i1, i2, i % 8, (i + 1) % 8, (i + 2) % 8, (i + 3) % 8);
|
||||||
|
assertEqX4(r, compI[i % 8]);
|
||||||
|
|
||||||
|
// Constant lanes
|
||||||
|
assertEqX4(SIMD.Int32x4.shuffle(i1, i2, 3, 2, 4, 5), [4, 3, 5, 6]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function testBailouts(expectException, uglyDuckling) {
|
||||||
|
var i1 = SIMD.Int32x4(1, 2, 3, 4);
|
||||||
|
var i2 = SIMD.Int32x4(5, 6, 7, 8);
|
||||||
|
|
||||||
|
for (var i = 0; i < 150; i++) {
|
||||||
|
// Test bailouts
|
||||||
|
var value = i == 149 ? uglyDuckling : 0;
|
||||||
|
var caught = false;
|
||||||
|
try {
|
||||||
|
assertEqX4(SIMD.Int32x4.shuffle(i1, i2, value, 2, 4, 5), [1, 3, 5, 6]);
|
||||||
|
} catch(e) {
|
||||||
|
print(e);
|
||||||
|
caught = true;
|
||||||
|
assertEq(i, 149);
|
||||||
|
assertEq(e instanceof TypeError || e instanceof RangeError, true);
|
||||||
|
}
|
||||||
|
if (i == 149)
|
||||||
|
assertEq(caught, expectException);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
f();
|
||||||
|
testBailouts(true, -1);
|
||||||
|
testBailouts(true, 8);
|
||||||
|
testBailouts(true, 2.5);
|
||||||
|
testBailouts(true, undefined);
|
||||||
|
testBailouts(true, {});
|
||||||
|
testBailouts(true, 'one');
|
||||||
|
testBailouts(false, false);
|
||||||
|
testBailouts(false, null);
|
||||||
|
testBailouts(false, " 0.0 ");
|
|
@ -0,0 +1,15 @@
|
||||||
|
load(libdir + 'simd.js');
|
||||||
|
|
||||||
|
setJitCompilerOption("ion.warmup.trigger", 50);
|
||||||
|
|
||||||
|
function f() {
|
||||||
|
for (var i = 0; i < 150; i++) {
|
||||||
|
assertEqX4(SIMD.Int32x4.splat(42), [42, 42, 42, 42]);
|
||||||
|
assertEqX4(SIMD.Float32x4.splat(42), [42, 42, 42, 42]);
|
||||||
|
assertEqX4(SIMD.Bool32x4.splat(true), [true, true, true, true]);
|
||||||
|
assertEqX4(SIMD.Bool32x4.splat(false), [false, false, false, false]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
f();
|
||||||
|
|
|
@ -0,0 +1,143 @@
|
||||||
|
load(libdir + 'simd.js');
|
||||||
|
|
||||||
|
setJitCompilerOption("ion.warmup.trigger", 40);
|
||||||
|
|
||||||
|
function f() {
|
||||||
|
var f32 = new Float32Array(16);
|
||||||
|
for (var i = 0; i < 16; i++)
|
||||||
|
f32[i] = i + 1;
|
||||||
|
|
||||||
|
var f64 = new Float64Array(f32.buffer);
|
||||||
|
var i32 = new Int32Array(f32.buffer);
|
||||||
|
var u32 = new Uint32Array(f32.buffer);
|
||||||
|
var i16 = new Int16Array(f32.buffer);
|
||||||
|
var u16 = new Uint16Array(f32.buffer);
|
||||||
|
var i8 = new Int8Array(f32.buffer);
|
||||||
|
var u8 = new Uint8Array(f32.buffer);
|
||||||
|
|
||||||
|
var f4 = SIMD.Float32x4(42, 43, 44, 45);
|
||||||
|
|
||||||
|
function check(n) {
|
||||||
|
assertEq(f32[0], 42);
|
||||||
|
assertEq(f32[1], n > 1 ? 43 : 2);
|
||||||
|
assertEq(f32[2], n > 2 ? 44 : 3);
|
||||||
|
assertEq(f32[3], n > 3 ? 45 : 4);
|
||||||
|
|
||||||
|
f32[0] = 1;
|
||||||
|
f32[1] = 2;
|
||||||
|
f32[2] = 3;
|
||||||
|
f32[3] = 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
function testStore() {
|
||||||
|
SIMD.Float32x4.store(f64, 0, f4);
|
||||||
|
check(4);
|
||||||
|
SIMD.Float32x4.store(f32, 0, f4);
|
||||||
|
check(4);
|
||||||
|
SIMD.Float32x4.store(i32, 0, f4);
|
||||||
|
check(4);
|
||||||
|
SIMD.Float32x4.store(u32, 0, f4);
|
||||||
|
check(4);
|
||||||
|
SIMD.Float32x4.store(i16, 0, f4);
|
||||||
|
check(4);
|
||||||
|
SIMD.Float32x4.store(u16, 0, f4);
|
||||||
|
check(4);
|
||||||
|
SIMD.Float32x4.store(i8, 0, f4);
|
||||||
|
check(4);
|
||||||
|
SIMD.Float32x4.store(u8, 0, f4);
|
||||||
|
check(4);
|
||||||
|
}
|
||||||
|
|
||||||
|
function testStore1() {
|
||||||
|
SIMD.Float32x4.store1(f64, 0, f4);
|
||||||
|
check(1);
|
||||||
|
SIMD.Float32x4.store1(f32, 0, f4);
|
||||||
|
check(1);
|
||||||
|
SIMD.Float32x4.store1(i32, 0, f4);
|
||||||
|
check(1);
|
||||||
|
SIMD.Float32x4.store1(u32, 0, f4);
|
||||||
|
check(1);
|
||||||
|
SIMD.Float32x4.store1(i16, 0, f4);
|
||||||
|
check(1);
|
||||||
|
SIMD.Float32x4.store1(u16, 0, f4);
|
||||||
|
check(1);
|
||||||
|
SIMD.Float32x4.store1(i8, 0, f4);
|
||||||
|
check(1);
|
||||||
|
SIMD.Float32x4.store1(u8, 0, f4);
|
||||||
|
check(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
function testStore2() {
|
||||||
|
SIMD.Float32x4.store2(f64, 0, f4);
|
||||||
|
check(2);
|
||||||
|
SIMD.Float32x4.store2(f32, 0, f4);
|
||||||
|
check(2);
|
||||||
|
SIMD.Float32x4.store2(i32, 0, f4);
|
||||||
|
check(2);
|
||||||
|
SIMD.Float32x4.store2(u32, 0, f4);
|
||||||
|
check(2);
|
||||||
|
SIMD.Float32x4.store2(i16, 0, f4);
|
||||||
|
check(2);
|
||||||
|
SIMD.Float32x4.store2(u16, 0, f4);
|
||||||
|
check(2);
|
||||||
|
SIMD.Float32x4.store2(i8, 0, f4);
|
||||||
|
check(2);
|
||||||
|
SIMD.Float32x4.store2(u8, 0, f4);
|
||||||
|
check(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
function testStore3() {
|
||||||
|
SIMD.Float32x4.store3(f64, 0, f4);
|
||||||
|
check(3);
|
||||||
|
SIMD.Float32x4.store3(f32, 0, f4);
|
||||||
|
check(3);
|
||||||
|
SIMD.Float32x4.store3(i32, 0, f4);
|
||||||
|
check(3);
|
||||||
|
SIMD.Float32x4.store3(u32, 0, f4);
|
||||||
|
check(3);
|
||||||
|
SIMD.Float32x4.store3(i16, 0, f4);
|
||||||
|
check(3);
|
||||||
|
SIMD.Float32x4.store3(u16, 0, f4);
|
||||||
|
check(3);
|
||||||
|
SIMD.Float32x4.store3(i8, 0, f4);
|
||||||
|
check(3);
|
||||||
|
SIMD.Float32x4.store3(u8, 0, f4);
|
||||||
|
check(3);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var i = 0; i < 150; i++) {
|
||||||
|
testStore();
|
||||||
|
testStore1();
|
||||||
|
testStore2();
|
||||||
|
testStore3();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
f();
|
||||||
|
|
||||||
|
function testBailout(uglyDuckling) {
|
||||||
|
var f32 = new Float32Array(16);
|
||||||
|
for (var i = 0; i < 16; i++)
|
||||||
|
f32[i] = i + 1;
|
||||||
|
|
||||||
|
var i8 = new Int8Array(f32.buffer);
|
||||||
|
|
||||||
|
var f4 = SIMD.Float32x4(42, 43, 44, 45);
|
||||||
|
|
||||||
|
for (var i = 0; i < 150; i++) {
|
||||||
|
var caught = false;
|
||||||
|
try {
|
||||||
|
SIMD.Float32x4.store(i8, (i < 149) ? 0 : (16 << 2) - (4 << 2) + 1, f4);
|
||||||
|
} catch (e) {
|
||||||
|
print(e);
|
||||||
|
assertEq(e instanceof RangeError, true);
|
||||||
|
caught = true;
|
||||||
|
}
|
||||||
|
assertEq(i < 149 || caught, true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
print('Testing range checks...');
|
||||||
|
testBailout(-1);
|
||||||
|
testBailout(-15);
|
||||||
|
testBailout(12 * 4 + 1);
|
|
@ -0,0 +1,104 @@
|
||||||
|
if (!this.hasOwnProperty("SIMD"))
|
||||||
|
quit();
|
||||||
|
|
||||||
|
load(libdir + 'simd.js');
|
||||||
|
|
||||||
|
setJitCompilerOption("ion.warmup.trigger", 50);
|
||||||
|
|
||||||
|
function f() {
|
||||||
|
var i4 = SIMD.Int32x4(1, 2, 3, 4);
|
||||||
|
|
||||||
|
var leet = Math.fround(13.37);
|
||||||
|
var f4 = SIMD.Float32x4(-.5, -0, Infinity, leet);
|
||||||
|
|
||||||
|
var compI = [
|
||||||
|
[1,2,3,4],
|
||||||
|
[2,3,4,1],
|
||||||
|
[3,4,1,2],
|
||||||
|
[4,1,2,3]
|
||||||
|
];
|
||||||
|
|
||||||
|
var compF = [
|
||||||
|
[-.5, -0, Infinity, leet],
|
||||||
|
[-0, Infinity, leet, -.5],
|
||||||
|
[Infinity, leet, -.5, -0],
|
||||||
|
[leet, -.5, -0, Infinity]
|
||||||
|
];
|
||||||
|
|
||||||
|
for (var i = 0; i < 150; i++) {
|
||||||
|
// Variable lanes
|
||||||
|
var r = SIMD.Float32x4.swizzle(f4, i % 4, (i + 1) % 4, (i + 2) % 4, (i + 3) % 4);
|
||||||
|
assertEqX4(r, compF[i % 4]);
|
||||||
|
|
||||||
|
// Constant lanes
|
||||||
|
assertEqX4(SIMD.Float32x4.swizzle(f4, 3, 2, 1, 0), [leet, Infinity, -0, -.5]);
|
||||||
|
|
||||||
|
// Variable lanes
|
||||||
|
var r = SIMD.Int32x4.swizzle(i4, i % 4, (i + 1) % 4, (i + 2) % 4, (i + 3) % 4);
|
||||||
|
assertEqX4(r, compI[i % 4]);
|
||||||
|
|
||||||
|
// Constant lanes
|
||||||
|
assertEqX4(SIMD.Int32x4.swizzle(i4, 3, 2, 1, 0), [4, 3, 2, 1]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function testBailouts(expectException, uglyDuckling) {
|
||||||
|
var i4 = SIMD.Int32x4(1, 2, 3, 4);
|
||||||
|
for (var i = 0; i < 150; i++) {
|
||||||
|
// Test bailouts
|
||||||
|
var value = i == 149 ? uglyDuckling : 0;
|
||||||
|
var caught = false;
|
||||||
|
try {
|
||||||
|
assertEqX4(SIMD.Int32x4.swizzle(i4, value, 3, 2, 0), [1, 4, 3, 1]);
|
||||||
|
} catch(e) {
|
||||||
|
print(e);
|
||||||
|
caught = true;
|
||||||
|
assertEq(i, 149);
|
||||||
|
assertEq(e instanceof TypeError || e instanceof RangeError, true);
|
||||||
|
}
|
||||||
|
if (i == 149)
|
||||||
|
assertEq(caught, expectException);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function testInt32x4SwizzleBailout() {
|
||||||
|
// Test out-of-bounds non-constant indices. This is expected to throw.
|
||||||
|
var i4 = SIMD.Int32x4(1, 2, 3, 4);
|
||||||
|
for (var i = 0; i < 150; i++) {
|
||||||
|
assertEqX4(SIMD.Int32x4.swizzle(i4, i, 3, 2, 0), [i + 1, 4, 3, 1]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
f();
|
||||||
|
testBailouts(true, -1);
|
||||||
|
testBailouts(true, 4);
|
||||||
|
testBailouts(true, 2.5);
|
||||||
|
testBailouts(true, undefined);
|
||||||
|
testBailouts(true, {});
|
||||||
|
testBailouts(true, 'one');
|
||||||
|
testBailouts(false, false);
|
||||||
|
testBailouts(false, null);
|
||||||
|
testBailouts(false, " 0.0 ");
|
||||||
|
|
||||||
|
try {
|
||||||
|
testInt32x4SwizzleBailout();
|
||||||
|
throw 'not caught';
|
||||||
|
} catch(e) {
|
||||||
|
assertEq(e instanceof RangeError, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
(function() {
|
||||||
|
var zappa = 0;
|
||||||
|
|
||||||
|
function testBailouts() {
|
||||||
|
var i4 = SIMD.Int32x4(1, 2, 3, 4);
|
||||||
|
for (var i = 0; i < 300; i++) {
|
||||||
|
var value = i == 299 ? 2.5 : 1;
|
||||||
|
SIMD.Int32x4.swizzle(i4, value, 3, 2, 0);
|
||||||
|
zappa = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
try { testBailouts(); } catch (e) {}
|
||||||
|
assertEq(zappa, 298);
|
||||||
|
})();
|
|
@ -0,0 +1,86 @@
|
||||||
|
load(libdir + 'simd.js');
|
||||||
|
|
||||||
|
setJitCompilerOption("ion.warmup.trigger", 30);
|
||||||
|
|
||||||
|
// Testing Uint32 <-> Float32 conversions.
|
||||||
|
// These conversions deserve special attention because SSE doesn't provide
|
||||||
|
// simple conversion instructions.
|
||||||
|
|
||||||
|
// Convert an Uint32Array to a Float32Array using scalar conversions.
|
||||||
|
function cvt_utof_scalar(u32s, f32s) {
|
||||||
|
assertEq(u32s.length, f32s.length);
|
||||||
|
for (var i = 0; i < u32s.length; i++) {
|
||||||
|
f32s[i] = u32s[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert an Uint32Array to a Float32Array using simd conversions.
|
||||||
|
function cvt_utof_simd(u32s, f32s) {
|
||||||
|
assertEq(u32s.length, f32s.length);
|
||||||
|
for (var i = 0; i < u32s.length; i += 4) {
|
||||||
|
SIMD.Float32x4.store(f32s, i, SIMD.Float32x4.fromUint32x4(SIMD.Uint32x4.load(u32s, i)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert a Float32Array to an Uint32Array using scalar conversions.
|
||||||
|
function cvt_ftou_scalar(f32s, u32s) {
|
||||||
|
assertEq(f32s.length, u32s.length);
|
||||||
|
for (var i = 0; i < f32s.length; i++) {
|
||||||
|
u32s[i] = f32s[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert a Float32Array to an Uint32Array using simd conversions.
|
||||||
|
function cvt_ftou_simd(f32s, u32s) {
|
||||||
|
assertEq(f32s.length, u32s.length);
|
||||||
|
for (var i = 0; i < f32s.length; i += 4) {
|
||||||
|
SIMD.Uint32x4.store(u32s, i, SIMD.Uint32x4.fromFloat32x4(SIMD.Float32x4.load(f32s, i)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function check(a, b) {
|
||||||
|
assertEq(a.length, b.length);
|
||||||
|
for (var i = 0; i < a.length; i++) {
|
||||||
|
assertEq(a[i], b[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Uint32x4 --> Float32x4 tests.
|
||||||
|
var src = new Uint32Array(8000);
|
||||||
|
var dst1 = new Float32Array(8000);
|
||||||
|
var dst2 = new Float32Array(8000);
|
||||||
|
|
||||||
|
for (var i = 0; i < 2000; i++) {
|
||||||
|
src[i] = i;
|
||||||
|
src[i + 2000] = 0x7fffffff - i;
|
||||||
|
src[i + 4000] = 0x80000000 + i;
|
||||||
|
src[i + 6000] = 0xffffffff - i;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var n = 0; n < 10; n++) {
|
||||||
|
cvt_utof_scalar(src, dst1);
|
||||||
|
cvt_utof_simd(src, dst2);
|
||||||
|
check(dst1, dst2);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Float32x4 --> Uint32x4 tests.
|
||||||
|
var fsrc = dst1;
|
||||||
|
var fdst1 = new Uint32Array(8000);
|
||||||
|
var fdst2 = new Uint32Array(8000);
|
||||||
|
|
||||||
|
// The 0xffffffff entries in fsrc round to 0x1.0p32f which throws.
|
||||||
|
// Go as high as 0x0.ffffffp32f.
|
||||||
|
for (var i = 0; i < 2000; i++) {
|
||||||
|
fsrc[i + 6000] = 0xffffff7f - i;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Truncation towards 0.
|
||||||
|
fsrc[1990] = -0.9
|
||||||
|
fsrc[1991] = 0.9
|
||||||
|
fsrc[1992] = 1.9
|
||||||
|
|
||||||
|
for (var n = 0; n < 10; n++) {
|
||||||
|
cvt_ftou_scalar(fsrc, fdst1);
|
||||||
|
cvt_ftou_simd(fsrc, fdst2);
|
||||||
|
check(fdst1, fdst2);
|
||||||
|
}
|
|
@ -0,0 +1,35 @@
|
||||||
|
load(libdir + 'simd.js');
|
||||||
|
|
||||||
|
setJitCompilerOption("ion.warmup.trigger", 50);
|
||||||
|
|
||||||
|
var notf = (function() {
|
||||||
|
var i32 = new Int32Array(1);
|
||||||
|
var f32 = new Float32Array(i32.buffer);
|
||||||
|
return function(x) {
|
||||||
|
f32[0] = x;
|
||||||
|
i32[0] = ~i32[0];
|
||||||
|
return f32[0];
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
|
||||||
|
function f() {
|
||||||
|
var f4 = SIMD.Float32x4(1, 2, 3, 4);
|
||||||
|
var i4 = SIMD.Int32x4(1, 2, 3, 4);
|
||||||
|
var b4 = SIMD.Bool32x4(true, false, true, false);
|
||||||
|
var BitOrZero = (x) => x | 0;
|
||||||
|
for (var i = 0; i < 150; i++) {
|
||||||
|
assertEqX4(SIMD.Float32x4.neg(f4), unaryX4((x) => -x, f4, Math.fround));
|
||||||
|
assertEqX4(SIMD.Float32x4.abs(f4), unaryX4(Math.abs, f4, Math.fround));
|
||||||
|
assertEqX4(SIMD.Float32x4.sqrt(f4), unaryX4(Math.sqrt, f4, Math.fround));
|
||||||
|
|
||||||
|
assertEqX4(SIMD.Float32x4.reciprocalApproximation(f4), unaryX4((x) => 1 / x, f4, Math.fround), assertNear);
|
||||||
|
assertEqX4(SIMD.Float32x4.reciprocalSqrtApproximation(f4), unaryX4((x) => 1 / Math.sqrt(x), f4, Math.fround), assertNear);
|
||||||
|
|
||||||
|
assertEqX4(SIMD.Int32x4.not(i4), unaryX4((x) => ~x, i4, BitOrZero));
|
||||||
|
assertEqX4(SIMD.Int32x4.neg(i4), unaryX4((x) => -x, i4, BitOrZero));
|
||||||
|
|
||||||
|
assertEqX4(SIMD.Bool32x4.not(b4), unaryX4((x) => !x, b4, (x) => x ));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
f();
|
|
@ -0,0 +1,144 @@
|
||||||
|
load(libdir + 'simd.js');
|
||||||
|
|
||||||
|
setJitCompilerOption("baseline.warmup.trigger", 10);
|
||||||
|
setJitCompilerOption("ion.warmup.trigger", 30);
|
||||||
|
|
||||||
|
var max = 40, pivot = 35;
|
||||||
|
|
||||||
|
var i32x4 = SIMD.Int32x4;
|
||||||
|
var f32x4 = SIMD.Float32x4;
|
||||||
|
var i32x4Add = SIMD.Int32x4.add;
|
||||||
|
|
||||||
|
var FakeSIMDType = function (o) { this.x = o.x; this.y = o.y; this.z = o.z; this.w = o.w; };
|
||||||
|
if (this.hasOwnProperty("TypedObject")) {
|
||||||
|
var TO = TypedObject;
|
||||||
|
FakeSIMDType = new TO.StructType({ x: TO.int32, y: TO.int32, z: TO.int32, w: TO.int32 });
|
||||||
|
}
|
||||||
|
|
||||||
|
function simdunbox_bail_undef(i, lhs, rhs) {
|
||||||
|
return i32x4Add(lhs, rhs);
|
||||||
|
}
|
||||||
|
|
||||||
|
function simdunbox_bail_object(i, lhs, rhs) {
|
||||||
|
return i32x4Add(lhs, rhs);
|
||||||
|
}
|
||||||
|
|
||||||
|
function simdunbox_bail_typeobj(i, lhs, rhs) {
|
||||||
|
return i32x4Add(lhs, rhs);
|
||||||
|
}
|
||||||
|
|
||||||
|
function simdunbox_bail_badsimd(i, lhs, rhs) {
|
||||||
|
return i32x4Add(lhs, rhs);
|
||||||
|
}
|
||||||
|
|
||||||
|
var arr_undef = [ i32x4(0, 1, 1, 2), i32x4(1, 1, 2, 3) ];
|
||||||
|
var fail_undef = 0;
|
||||||
|
var arr_object = [ i32x4(0, 1, 1, 2), i32x4(1, 1, 2, 3) ];
|
||||||
|
var fail_object = 0;
|
||||||
|
var arr_typeobj = [ i32x4(0, 1, 1, 2), i32x4(1, 1, 2, 3) ];
|
||||||
|
var fail_typeobj = 0;
|
||||||
|
var arr_badsimd = [ i32x4(0, 1, 1, 2), i32x4(1, 1, 2, 3) ];
|
||||||
|
var fail_badsimd = 0;
|
||||||
|
for (var i = 0; i < max; i++) {
|
||||||
|
try {
|
||||||
|
arr_undef[i + 2] = simdunbox_bail_undef(i, arr_undef[i], arr_undef[i + 1]);
|
||||||
|
} catch (x) {
|
||||||
|
arr_undef[i + 2] = arr_undef[i - 1];
|
||||||
|
fail_undef++;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
arr_object[i + 2] = simdunbox_bail_object(i, arr_object[i], arr_object[i + 1]);
|
||||||
|
} catch (x) {
|
||||||
|
arr_object[i + 2] = arr_object[i - 1];
|
||||||
|
fail_object++;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
arr_typeobj[i + 2] = simdunbox_bail_typeobj(i, arr_typeobj[i], arr_typeobj[i + 1]);
|
||||||
|
} catch (x) {
|
||||||
|
arr_typeobj[i + 2] = arr_typeobj[i - 1];
|
||||||
|
fail_typeobj++;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
arr_badsimd[i + 2] = simdunbox_bail_badsimd(i, arr_badsimd[i], arr_badsimd[i + 1]);
|
||||||
|
} catch (x) {
|
||||||
|
arr_badsimd[i + 2] = arr_badsimd[i - 1];
|
||||||
|
fail_badsimd++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i + 2 == pivot) {
|
||||||
|
arr_undef[pivot] = undefined;
|
||||||
|
arr_object[pivot] = { x: 0, y: 1, z: 2, w: 3 };
|
||||||
|
arr_typeobj[pivot] = new FakeSIMDType({ x: 0, y: 1, z: 2, w: 3 });
|
||||||
|
arr_badsimd[pivot] = f32x4(0, 1, 2, 3);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
assertEq(fail_undef, 2);
|
||||||
|
assertEq(fail_object, 2);
|
||||||
|
assertEq(fail_typeobj, 2);
|
||||||
|
assertEq(fail_badsimd, 2);
|
||||||
|
|
||||||
|
// Assert that all SIMD values are correct.
|
||||||
|
function assertEqX4(real, expected, assertFunc) {
|
||||||
|
if (typeof assertFunc === 'undefined')
|
||||||
|
assertFunc = assertEq;
|
||||||
|
|
||||||
|
assertFunc(real.x, expected[0]);
|
||||||
|
assertFunc(real.y, expected[1]);
|
||||||
|
assertFunc(real.z, expected[2]);
|
||||||
|
assertFunc(real.w, expected[3]);
|
||||||
|
}
|
||||||
|
|
||||||
|
var fib = [0, 1];
|
||||||
|
for (i = 0; i < max + 5; i++)
|
||||||
|
fib[i+2] = (fib[i] + fib[i+1]) | 0;
|
||||||
|
|
||||||
|
for (i = 0; i < max; i++) {
|
||||||
|
if (i == pivot)
|
||||||
|
continue;
|
||||||
|
var ref = fib.slice(i < pivot ? i : i - 3);
|
||||||
|
assertEqX4(arr_undef[i], ref);
|
||||||
|
assertEqX4(arr_object[i], ref);
|
||||||
|
assertEqX4(arr_typeobj[i], ref);
|
||||||
|
assertEqX4(arr_badsimd[i], ref);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check that unbox operations aren't removed
|
||||||
|
(function() {
|
||||||
|
|
||||||
|
function add(i, v, w) {
|
||||||
|
if (i % 2 == 0) {
|
||||||
|
SIMD.Int32x4.add(v, w);
|
||||||
|
} else {
|
||||||
|
SIMD.Float32x4.add(v, w);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var i = 0;
|
||||||
|
var caught = false;
|
||||||
|
var f4 = SIMD.Float32x4(1,2,3,4);
|
||||||
|
var i4 = SIMD.Int32x4(1,2,3,4);
|
||||||
|
try {
|
||||||
|
for (; i < 200; i++) {
|
||||||
|
if (i % 2 == 0) {
|
||||||
|
add(i, i4, i4);
|
||||||
|
} else if (i == 199) {
|
||||||
|
add(i, i4, f4);
|
||||||
|
} else {
|
||||||
|
add(i, f4, f4);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch(e) {
|
||||||
|
print(e);
|
||||||
|
assertEq(e instanceof TypeError, true);
|
||||||
|
assertEq(i, 199);
|
||||||
|
caught = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
assertEq(i < 199 || caught, true);
|
||||||
|
|
||||||
|
})();
|
||||||
|
|
|
@ -13,3 +13,41 @@ var v = asmLink(asmCompile('global', `
|
||||||
`), this)();
|
`), this)();
|
||||||
|
|
||||||
assertEq(v, NaN);
|
assertEq(v, NaN);
|
||||||
|
|
||||||
|
if (!isSimdAvailable() || typeof SIMD === 'undefined') {
|
||||||
|
quit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
var v = asmLink(asmCompile('global', `
|
||||||
|
"use asm";
|
||||||
|
var frd = global.Math.fround;
|
||||||
|
var Float32x4 = global.SIMD.Float32x4;
|
||||||
|
var splat = Float32x4.splat;
|
||||||
|
var ext = Float32x4.extractLane;
|
||||||
|
function e() {
|
||||||
|
var v = Float32x4(0,0,0,0);
|
||||||
|
var x = frd(0.);
|
||||||
|
v = splat(.1e+71);
|
||||||
|
x = ext(v,0);
|
||||||
|
x = frd(x / x);
|
||||||
|
return +x;
|
||||||
|
}
|
||||||
|
return e;
|
||||||
|
`), this)();
|
||||||
|
|
||||||
|
assertEq(v, NaN);
|
||||||
|
|
||||||
|
// Bug 1130618: without GVN
|
||||||
|
setJitCompilerOption("ion.gvn.enable", 0);
|
||||||
|
var v = asmLink(asmCompile('global', `
|
||||||
|
"use asm";
|
||||||
|
var Float32x4 = global.SIMD.Float32x4;
|
||||||
|
var splat = Float32x4.splat;
|
||||||
|
var ext = Float32x4.extractLane;
|
||||||
|
function e() {
|
||||||
|
return +ext(splat(.1e+71),0);
|
||||||
|
}
|
||||||
|
return e;
|
||||||
|
`), this)();
|
||||||
|
|
||||||
|
assertEq(v, Infinity);
|
||||||
|
|
|
@ -0,0 +1,28 @@
|
||||||
|
load(libdir + "asm.js");
|
||||||
|
load(libdir + "asserts.js");
|
||||||
|
|
||||||
|
if (typeof newGlobal !== 'function' ||
|
||||||
|
!isSimdAvailable() ||
|
||||||
|
typeof SIMD === 'undefined')
|
||||||
|
{
|
||||||
|
quit();
|
||||||
|
}
|
||||||
|
|
||||||
|
var stdlib = new (newGlobal().Proxy)(this, new Proxy({
|
||||||
|
simdGet: 0,
|
||||||
|
getOwnPropertyDescriptor(t, pk) {
|
||||||
|
if (pk === "SIMD" && this.simdGet++ === 1) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
return Reflect.getOwnPropertyDescriptor(t, pk);
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
get(t, pk, r) {
|
||||||
|
print("trap", pk);
|
||||||
|
return Reflect.get(t, pk, r);
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
var m = asmCompile('stdlib', '"use asm"; var i4=stdlib.SIMD.Int32x4; var i4add=i4.add; return {}');
|
||||||
|
|
||||||
|
assertAsmLinkFail(m, stdlib);
|
|
@ -0,0 +1,198 @@
|
||||||
|
/* -*- Mode: javascript; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 ; js-indent-level : 2 ; js-curly-indent-offset: 0 -*- */
|
||||||
|
/* vim: set ts=4 et sw=4 tw=80: */
|
||||||
|
|
||||||
|
// Author: Peter Jensen
|
||||||
|
|
||||||
|
load(libdir + "asm.js");
|
||||||
|
if (!isSimdAvailable() || typeof SIMD === 'undefined') {
|
||||||
|
print("won't run tests as simd extensions aren't activated yet");
|
||||||
|
quit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
const NUM_BIRDS = 30;
|
||||||
|
const NUM_UPDATES = 20;
|
||||||
|
const ACCEL_DATA_STEPS = 30;
|
||||||
|
|
||||||
|
var buffer = new ArrayBuffer(0x200000);
|
||||||
|
var bufferF32 = new Float32Array(buffer);
|
||||||
|
|
||||||
|
var actualBirds = 0;
|
||||||
|
|
||||||
|
function init() {
|
||||||
|
actualBirds = 0;
|
||||||
|
// Make it a power of two, for quick modulo wrapping.
|
||||||
|
var accelDataValues = [10.0, 9.5, 9.0, 8.0, 7.0, 6.0, 5.5, 5.0, 5.0, 5.0, 5.5, 6.0, 7.0, 8.0, 9.0, 10.0];
|
||||||
|
accelDataValues = accelDataValues.map(function(v) { return 50*v; });
|
||||||
|
var accelDataValuesLength = accelDataValues.length;
|
||||||
|
assertEq(accelDataValuesLength, 16); // Hard coded in the asm.js module
|
||||||
|
for (i = 0; i < accelDataValuesLength; i++)
|
||||||
|
bufferF32[i + NUM_BIRDS * 2] = accelDataValues[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
function addBird(pos, vel) {
|
||||||
|
bufferF32[actualBirds] = pos;
|
||||||
|
bufferF32[actualBirds + NUM_BIRDS] = vel;
|
||||||
|
actualBirds++;
|
||||||
|
return actualBirds - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getActualBirds() {
|
||||||
|
return actualBirds;
|
||||||
|
}
|
||||||
|
|
||||||
|
var code = `
|
||||||
|
"use asm";
|
||||||
|
var toF = global.Math.fround;
|
||||||
|
var u8 = new global.Uint8Array(buffer);
|
||||||
|
var f32 = new global.Float32Array(buffer);
|
||||||
|
const maxBirds = 100000;
|
||||||
|
const maxBirdsx4 = 400000;
|
||||||
|
const maxBirdsx8 = 800000;
|
||||||
|
const accelMask = 0x3c;
|
||||||
|
const mk4 = 0x000ffff0;
|
||||||
|
|
||||||
|
const getMaxPos = 1000.0;
|
||||||
|
const getAccelDataSteps = imp.accelDataSteps | 0;
|
||||||
|
var getActualBirds = imp.getActualBirds;
|
||||||
|
|
||||||
|
var i4 = global.SIMD.Int32x4;
|
||||||
|
var f4 = global.SIMD.Float32x4;
|
||||||
|
var b4 = global.SIMD.Bool32x4;
|
||||||
|
var i4add = i4.add;
|
||||||
|
var i4and = i4.and;
|
||||||
|
var f4select = f4.select;
|
||||||
|
var f4add = f4.add;
|
||||||
|
var f4sub = f4.sub;
|
||||||
|
var f4mul = f4.mul;
|
||||||
|
var f4greaterThan = f4.greaterThan;
|
||||||
|
var f4splat = f4.splat;
|
||||||
|
var f4load = f4.load;
|
||||||
|
var f4store = f4.store;
|
||||||
|
var b4any = b4.anyTrue;
|
||||||
|
|
||||||
|
const zerox4 = f4(0.0,0.0,0.0,0.0);
|
||||||
|
|
||||||
|
function declareHeapSize() {
|
||||||
|
f32[0x0007ffff] = toF(0.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
function update(timeDelta) {
|
||||||
|
timeDelta = toF(timeDelta);
|
||||||
|
// var steps = Math.ceil(timeDelta/accelData.interval);
|
||||||
|
var steps = 0;
|
||||||
|
var subTimeDelta = toF(0.0);
|
||||||
|
var actualBirds = 0;
|
||||||
|
var maxPos = toF(0.0);
|
||||||
|
var maxPosx4 = f4(0.0,0.0,0.0,0.0);
|
||||||
|
var subTimeDeltax4 = f4(0.0,0.0,0.0,0.0);
|
||||||
|
var subTimeDeltaSquaredx4 = f4(0.0,0.0,0.0,0.0);
|
||||||
|
var point5x4 = f4(0.5, 0.5, 0.5, 0.5);
|
||||||
|
var i = 0;
|
||||||
|
var len = 0;
|
||||||
|
var accelIndex = 0;
|
||||||
|
var newPosx4 = f4(0.0,0.0,0.0,0.0);
|
||||||
|
var newVelx4 = f4(0.0,0.0,0.0,0.0);
|
||||||
|
var accel = toF(0.0);
|
||||||
|
var accelx4 = f4(0.0,0.0,0.0,0.0);
|
||||||
|
var a = 0;
|
||||||
|
var posDeltax4 = f4(0.0,0.0,0.0,0.0);
|
||||||
|
var cmpx4 = b4(0,0,0,0);
|
||||||
|
var newVelTruex4 = f4(0.0,0.0,0.0,0.0);
|
||||||
|
|
||||||
|
steps = getAccelDataSteps | 0;
|
||||||
|
subTimeDelta = toF(toF(timeDelta / toF(steps | 0)) / toF(1000.0));
|
||||||
|
actualBirds = getActualBirds() | 0;
|
||||||
|
maxPos = toF(+getMaxPos);
|
||||||
|
maxPosx4 = f4splat(maxPos);
|
||||||
|
subTimeDeltax4 = f4splat(subTimeDelta);
|
||||||
|
subTimeDeltaSquaredx4 = f4mul(subTimeDeltax4, subTimeDeltax4);
|
||||||
|
|
||||||
|
len = ((actualBirds + 3) >> 2) << 4;
|
||||||
|
|
||||||
|
for (i = 0; (i | 0) < (len | 0); i = (i + 16) | 0) {
|
||||||
|
accelIndex = 0;
|
||||||
|
newPosx4 = f4load(u8, i & mk4);
|
||||||
|
newVelx4 = f4load(u8, (i & mk4) + maxBirdsx4);
|
||||||
|
for (a = 0; (a | 0) < (steps | 0); a = (a + 1) | 0) {
|
||||||
|
accel = toF(f32[(accelIndex & accelMask) + maxBirdsx8 >> 2]);
|
||||||
|
accelx4 = f4splat(accel);
|
||||||
|
accelIndex = (accelIndex + 4) | 0;
|
||||||
|
posDeltax4 = f4mul(point5x4, f4mul(accelx4, subTimeDeltaSquaredx4));
|
||||||
|
posDeltax4 = f4add(posDeltax4, f4mul(newVelx4, subTimeDeltax4));
|
||||||
|
newPosx4 = f4add(newPosx4, posDeltax4);
|
||||||
|
newVelx4 = f4add(newVelx4, f4mul(accelx4, subTimeDeltax4));
|
||||||
|
cmpx4 = f4greaterThan(newPosx4, maxPosx4);
|
||||||
|
|
||||||
|
if (b4any(cmpx4)) {
|
||||||
|
// Work around unimplemented 'neg' operation, using 0 - x.
|
||||||
|
newVelTruex4 = f4sub(zerox4, newVelx4);
|
||||||
|
newVelx4 = f4select(cmpx4, newVelTruex4, newVelx4);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
f4store(u8, i & mk4, newPosx4);
|
||||||
|
f4store(u8, (i & mk4) + maxBirdsx4, newVelx4);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return update;
|
||||||
|
`
|
||||||
|
|
||||||
|
var ffi = {
|
||||||
|
getActualBirds,
|
||||||
|
accelDataSteps: ACCEL_DATA_STEPS
|
||||||
|
};
|
||||||
|
|
||||||
|
var fbirds = asmLink(asmCompile('global', 'imp', 'buffer', code), this, ffi, buffer);
|
||||||
|
|
||||||
|
init();
|
||||||
|
for (var i = 0; i < NUM_BIRDS; i++) {
|
||||||
|
addBird(i / 10, Math.exp(2, NUM_BIRDS - i));
|
||||||
|
}
|
||||||
|
|
||||||
|
var b = dateNow();
|
||||||
|
for (var j = 0; j < NUM_UPDATES; j++) {
|
||||||
|
fbirds(16);
|
||||||
|
}
|
||||||
|
print(dateNow() - b);
|
||||||
|
|
||||||
|
assertEq(bufferF32[0], 0);
|
||||||
|
assertEq(bufferF32[1], 0.10000000149011612);
|
||||||
|
assertEq(bufferF32[2], 0.20000000298023224);
|
||||||
|
assertEq(bufferF32[3], 0.30000001192092896);
|
||||||
|
assertEq(bufferF32[4], 0.4000000059604645);
|
||||||
|
assertEq(bufferF32[5], 0.5);
|
||||||
|
assertEq(bufferF32[6], 0.6000000238418579);
|
||||||
|
assertEq(bufferF32[7], 0.699999988079071);
|
||||||
|
assertEq(bufferF32[8], 0.800000011920929);
|
||||||
|
assertEq(bufferF32[9], 0.8999999761581421);
|
||||||
|
assertEq(bufferF32[10], 1);
|
||||||
|
assertEq(bufferF32[11], 1.100000023841858);
|
||||||
|
assertEq(bufferF32[12], 1.2000000476837158);
|
||||||
|
assertEq(bufferF32[13], 1.2999999523162842);
|
||||||
|
assertEq(bufferF32[14], 1.399999976158142);
|
||||||
|
assertEq(bufferF32[15], 1.5);
|
||||||
|
assertEq(bufferF32[16], 1.600000023841858);
|
||||||
|
assertEq(bufferF32[17], 1.7000000476837158);
|
||||||
|
assertEq(bufferF32[18], 1.7999999523162842);
|
||||||
|
assertEq(bufferF32[19], 1.899999976158142);
|
||||||
|
assertEq(bufferF32[20], 2);
|
||||||
|
assertEq(bufferF32[21], 2.0999999046325684);
|
||||||
|
assertEq(bufferF32[22], 2.200000047683716);
|
||||||
|
assertEq(bufferF32[23], 2.299999952316284);
|
||||||
|
assertEq(bufferF32[24], 2.4000000953674316);
|
||||||
|
assertEq(bufferF32[25], 2.5);
|
||||||
|
assertEq(bufferF32[26], 2.5999999046325684);
|
||||||
|
assertEq(bufferF32[27], 2.700000047683716);
|
||||||
|
assertEq(bufferF32[28], 2.799999952316284);
|
||||||
|
assertEq(bufferF32[29], 2.9000000953674316);
|
||||||
|
|
||||||
|
|
||||||
|
// Code used to generate the assertEq list above.
|
||||||
|
function generateAssertList() {
|
||||||
|
var buf = '';
|
||||||
|
for (var k = 0; k < NUM_BIRDS; k++) {
|
||||||
|
buf += 'assertEq(bufferF32['+ k + '], ' + bufferF32[k] + ');\n';
|
||||||
|
}
|
||||||
|
print(buf);
|
||||||
|
}
|
||||||
|
//generateAssertList();
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -13,6 +13,17 @@ asmLink(asmJS, this, null, asmJSBuf);
|
||||||
var wasmMem = wasmEvalText('(module (memory 1 1) (export "mem" memory))').exports.mem;
|
var wasmMem = wasmEvalText('(module (memory 1 1) (export "mem" memory))').exports.mem;
|
||||||
assertAsmLinkFail(asmJS, this, null, wasmMem.buffer);
|
assertAsmLinkFail(asmJS, this, null, wasmMem.buffer);
|
||||||
|
|
||||||
|
if (!getBuildConfiguration().x64 && isSimdAvailable() && this["SIMD"]) {
|
||||||
|
var simdJS = asmCompile('stdlib', 'ffis', 'buf', USE_ASM + 'var i32 = new stdlib.Int32Array(buf); var i32x4 = stdlib.SIMD.Int32x4; return {}');
|
||||||
|
assertAsmLinkFail(simdJS, this, null, asmJSBuf);
|
||||||
|
assertAsmLinkFail(simdJS, this, null, wasmMem.buffer);
|
||||||
|
|
||||||
|
var simdJSBuf = new ArrayBuffer(BUF_MIN);
|
||||||
|
asmLink(simdJS, this, null, simdJSBuf);
|
||||||
|
asmLink(simdJS, this, null, simdJSBuf); // multiple SIMD.js instantiations succeed
|
||||||
|
assertAsmLinkFail(asmJS, this, null, simdJSBuf); // but not asm.js
|
||||||
|
}
|
||||||
|
|
||||||
setJitCompilerOption('asmjs.atomics.enable', 1);
|
setJitCompilerOption('asmjs.atomics.enable', 1);
|
||||||
|
|
||||||
var sharedAsmJS = asmCompile('stdlib', 'ffis', 'buf',
|
var sharedAsmJS = asmCompile('stdlib', 'ffis', 'buf',
|
||||||
|
|
|
@ -0,0 +1,61 @@
|
||||||
|
if (typeof SIMD === 'undefined' || !isSimdAvailable()) {
|
||||||
|
print("won't run tests as simd extensions aren't activated yet");
|
||||||
|
quit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
(function(global) {
|
||||||
|
"use asm";
|
||||||
|
var frd = global.Math.fround;
|
||||||
|
var fx4 = global.SIMD.Float32x4;
|
||||||
|
var fc4 = fx4.check;
|
||||||
|
var fsp = fx4.splat;
|
||||||
|
function s(){}
|
||||||
|
function d(x){x=fc4(x);}
|
||||||
|
function e() {
|
||||||
|
var x = frd(0);
|
||||||
|
x = frd(x / x);
|
||||||
|
s();
|
||||||
|
d(fsp(x));
|
||||||
|
}
|
||||||
|
return e;
|
||||||
|
})(this)();
|
||||||
|
|
||||||
|
(function(m) {
|
||||||
|
"use asm"
|
||||||
|
var k = m.SIMD.Bool32x4
|
||||||
|
var g = m.SIMD.Int32x4
|
||||||
|
var gc = g.check;
|
||||||
|
var h = g.select
|
||||||
|
function f() {
|
||||||
|
var x = k(0, 0, 0, 0)
|
||||||
|
var y = g(1, 2, 3, 4)
|
||||||
|
return gc(h(x, y, y))
|
||||||
|
}
|
||||||
|
return f;
|
||||||
|
})(this)();
|
||||||
|
|
||||||
|
t = (function(global) {
|
||||||
|
"use asm"
|
||||||
|
var toF = global.Math.fround
|
||||||
|
var f4 = global.SIMD.Float32x4
|
||||||
|
var f4c = f4.check
|
||||||
|
function p(x, y, width, value, max_iterations) {
|
||||||
|
x = x | 0
|
||||||
|
y = y | 0
|
||||||
|
width = width | 0
|
||||||
|
value = value | 0
|
||||||
|
max_iterations = max_iterations | 0
|
||||||
|
}
|
||||||
|
function m(xf, yf, yd, max_iterations) {
|
||||||
|
xf = toF(xf)
|
||||||
|
yf = toF(yf)
|
||||||
|
yd = toF(yd)
|
||||||
|
max_iterations = max_iterations | 0
|
||||||
|
var _ = f4(0, 0, 0, 0), c_im4 = f4(0, 0, 0, 0)
|
||||||
|
c_im4 = f4(yf, yd, yd, yf)
|
||||||
|
return f4c(c_im4);
|
||||||
|
}
|
||||||
|
return {p:p,m:m};
|
||||||
|
})(this)
|
||||||
|
t.p();
|
||||||
|
t.m();
|
|
@ -18,6 +18,26 @@ for (let threshold of [0, 50, 100, 5000, -1]) {
|
||||||
return h
|
return h
|
||||||
`)()(), 45);
|
`)()(), 45);
|
||||||
|
|
||||||
|
if (isSimdAvailable() && this.SIMD) {
|
||||||
|
var buf = new ArrayBuffer(BUF_MIN);
|
||||||
|
new Int32Array(buf)[0] = 10;
|
||||||
|
new Float32Array(buf)[1] = 42;
|
||||||
|
assertEq(asmCompile('stdlib', 'ffis', 'buf',
|
||||||
|
USE_ASM + `
|
||||||
|
var H = new stdlib.Uint8Array(buf);
|
||||||
|
var i4 = stdlib.SIMD.Int32x4;
|
||||||
|
var f4 = stdlib.SIMD.Float32x4;
|
||||||
|
var i4load = i4.load;
|
||||||
|
var f4load = f4.load;
|
||||||
|
var toi4 = i4.fromFloat32x4;
|
||||||
|
var i4ext = i4.extractLane;
|
||||||
|
function f(i) { i=i|0; return i4ext(i4load(H, i), 0)|0 }
|
||||||
|
function g(i) { i=i|0; return (i4ext(toi4(f4load(H, i)),1) + (f(i)|0))|0 }
|
||||||
|
function h(i) { i=i|0; return g(i)|0 }
|
||||||
|
return h
|
||||||
|
`)(this, null, buf)(0), 52);
|
||||||
|
}
|
||||||
|
|
||||||
enableGeckoProfiling();
|
enableGeckoProfiling();
|
||||||
asmLink(asmCompile(USE_ASM + 'function f() {} function g() { f() } function h() { g() } return h'))();
|
asmLink(asmCompile(USE_ASM + 'function f() {} function g() { f() } function h() { g() } return h'))();
|
||||||
disableGeckoProfiling();
|
disableGeckoProfiling();
|
||||||
|
|
|
@ -210,6 +210,20 @@ if (jitOptions['baseline.enable']) {
|
||||||
assertStackContainsSeq(stacks, ">,f1,>,<,f1,>,>,<,f1,>,f2,>,<,f1,>,<,f2,>,<,f1,>,f2,>,<,f1,>,>,<,f1,>,<,f1,>,f1,>,>");
|
assertStackContainsSeq(stacks, ">,f1,>,<,f1,>,>,<,f1,>,f2,>,<,f1,>,<,f2,>,<,f1,>,f2,>,<,f1,>,>,<,f1,>,<,f1,>,f1,>,>");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (isSimdAvailable() && typeof SIMD !== 'undefined') {
|
||||||
|
// SIMD out-of-bounds exit
|
||||||
|
var buf = new ArrayBuffer(0x10000);
|
||||||
|
var f = asmLink(asmCompile('g','ffi','buf', USE_ASM + 'var f4=g.SIMD.float32x4; var f4l=f4.load; var u8=new g.Uint8Array(buf); function f(i) { i=i|0; return f4l(u8, 0xFFFF + i | 0); } return f'), this, {}, buf);
|
||||||
|
enableSingleStepProfiling();
|
||||||
|
assertThrowsInstanceOf(() => f(4), RangeError);
|
||||||
|
var stacks = disableSingleStepProfiling();
|
||||||
|
// TODO check that expected is actually the correctly expected string, when
|
||||||
|
// SIMD is implemented on ARM.
|
||||||
|
assertStackContainsSeq(stacks, ">,f,>,inline stub,f,>");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// Thunks
|
// Thunks
|
||||||
setJitCompilerOption("jump-threshold", 0);
|
setJitCompilerOption("jump-threshold", 0);
|
||||||
var h = asmLink(asmCompile(USE_ASM + 'function f() {} function g() { f() } function h() { g() } return h'));
|
var h = asmLink(asmCompile(USE_ASM + 'function f() {} function g() { f() } function h() { g() } return h'));
|
||||||
|
|
|
@ -0,0 +1,525 @@
|
||||||
|
load(libdir + "asm.js");
|
||||||
|
load(libdir + "simd.js");
|
||||||
|
load(libdir + "asserts.js");
|
||||||
|
|
||||||
|
// Set to true to see more JS debugging spew.
|
||||||
|
const DEBUG = false;
|
||||||
|
|
||||||
|
if (!isSimdAvailable()) {
|
||||||
|
DEBUG && print("won't run tests as simd extensions aren't activated yet");
|
||||||
|
quit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tests for 16x8 SIMD types: Int16x8, Uint16x8, Bool16x8.
|
||||||
|
|
||||||
|
const I16x8 = 'var i16x8 = glob.SIMD.Int16x8;'
|
||||||
|
const I16x8CHK = 'var i16x8chk = i16x8.check;'
|
||||||
|
const I16x8EXT = 'var i16x8ext = i16x8.extractLane;'
|
||||||
|
const I16x8REP = 'var i16x8rep = i16x8.replaceLane;'
|
||||||
|
const I16x8U16x8 = 'var i16x8u16x8 = i16x8.fromUint16x8Bits;'
|
||||||
|
|
||||||
|
const U16x8 = 'var u16x8 = glob.SIMD.Uint16x8;'
|
||||||
|
const U16x8CHK = 'var u16x8chk = u16x8.check;'
|
||||||
|
const U16x8EXT = 'var u16x8ext = u16x8.extractLane;'
|
||||||
|
const U16x8REP = 'var u16x8rep = u16x8.replaceLane;'
|
||||||
|
const U16x8I16x8 = 'var u16x8i16x8 = u16x8.fromInt16x8Bits;'
|
||||||
|
|
||||||
|
const B16x8 = 'var b16x8 = glob.SIMD.Bool16x8;'
|
||||||
|
const B16x8CHK = 'var b16x8chk = b16x8.check;'
|
||||||
|
const B16x8EXT = 'var b16x8ext = b16x8.extractLane;'
|
||||||
|
const B16x8REP = 'var b16x8rep = b16x8.replaceLane;'
|
||||||
|
|
||||||
|
const INT16_MAX = 0x7fff
|
||||||
|
const INT16_MIN = -0x10000
|
||||||
|
const UINT16_MAX = 0xffff
|
||||||
|
|
||||||
|
// Linking
|
||||||
|
assertEq(asmLink(asmCompile('glob', USE_ASM + I16x8 + "function f() {} return f"), {SIMD:{Int16x8: SIMD.Int16x8}})(), undefined);
|
||||||
|
assertEq(asmLink(asmCompile('glob', USE_ASM + U16x8 + "function f() {} return f"), {SIMD:{Uint16x8: SIMD.Uint16x8}})(), undefined);
|
||||||
|
assertEq(asmLink(asmCompile('glob', USE_ASM + B16x8 + "function f() {} return f"), {SIMD:{Bool16x8: SIMD.Bool16x8}})(), undefined);
|
||||||
|
|
||||||
|
// Local variable of Int16x8 type.
|
||||||
|
assertAsmTypeFail('glob', USE_ASM + "function f() {var x=Int16x8(1,2,3,4,5,6,7,8);} return f");
|
||||||
|
assertAsmTypeFail('glob', USE_ASM + I16x8 + "function f() {var x=i16x8;} return f");
|
||||||
|
assertAsmTypeFail('glob', USE_ASM + I16x8 + "function f() {var x=i16x8();} return f");
|
||||||
|
assertAsmTypeFail('glob', USE_ASM + I16x8 + "function f() {var x=i16x8(1);} return f");
|
||||||
|
assertAsmTypeFail('glob', USE_ASM + I16x8 + "function f() {var x=i16x8(1,2,3,4);} return f");
|
||||||
|
assertAsmTypeFail('glob', USE_ASM + I16x8 + "function f() {var x=i16x8(1,2,3,4,5,6,7,8.0);} return f");
|
||||||
|
assertAsmTypeFail('glob', USE_ASM + I16x8 + "function f() {var x=i16x8(1,2,3,4,5,6,7,8,9);} return f");
|
||||||
|
assertAsmTypeFail('glob', USE_ASM + I16x8 + "function f() {var x=i16x8(1,2,3,4,5,6,7,8|0);} return f");
|
||||||
|
assertEq(asmLink(asmCompile('glob', USE_ASM + I16x8 + "function f() {var x=i16x8(1,2,3,4,5,6,7,8);} return f"), this)(), undefined);
|
||||||
|
assertEq(asmLink(asmCompile('glob', USE_ASM + I16x8 + "function f() {var x=i16x8(1,2,3,4,5,6,7," + (INT16_MAX + 1) + ");} return f"), this)(), undefined);
|
||||||
|
|
||||||
|
// Local variable of Uint16x8 type.
|
||||||
|
assertAsmTypeFail('glob', USE_ASM + "function f() {var x=Uint16x8(1,2,3,4,5,6,7,8);} return f");
|
||||||
|
assertAsmTypeFail('glob', USE_ASM + U16x8 + "function f() {var x=u16x8;} return f");
|
||||||
|
assertAsmTypeFail('glob', USE_ASM + U16x8 + "function f() {var x=u16x8();} return f");
|
||||||
|
assertAsmTypeFail('glob', USE_ASM + U16x8 + "function f() {var x=u16x8(1);} return f");
|
||||||
|
assertAsmTypeFail('glob', USE_ASM + U16x8 + "function f() {var x=u16x8(1,2,3,4);} return f");
|
||||||
|
assertAsmTypeFail('glob', USE_ASM + U16x8 + "function f() {var x=u16x8(1,2,3,4,5,6,7,8.0);} return f");
|
||||||
|
assertAsmTypeFail('glob', USE_ASM + U16x8 + "function f() {var x=u16x8(1,2,3,4,5,6,7,8,9);} return f");
|
||||||
|
assertAsmTypeFail('glob', USE_ASM + U16x8 + "function f() {var x=u16x8(1,2,3,4,5,6,7,8|0);} return f");
|
||||||
|
assertEq(asmLink(asmCompile('glob', USE_ASM + U16x8 + "function f() {var x=u16x8(1,2,3,4,5,6,7,8);} return f"), this)(), undefined);
|
||||||
|
assertEq(asmLink(asmCompile('glob', USE_ASM + U16x8 + "function f() {var x=u16x8(1,2,3,4,5,6,7," + (UINT16_MAX + 1) + ");} return f"), this)(), undefined);
|
||||||
|
|
||||||
|
// Local variable of Bool16x8 type.
|
||||||
|
assertAsmTypeFail('glob', USE_ASM + "function f() {var x=Bool16x8(1,0,0,0, 0,0,0,0);} return f");
|
||||||
|
assertAsmTypeFail('glob', USE_ASM + B16x8 + "function f() {var x=b16x8;} return f");
|
||||||
|
assertAsmTypeFail('glob', USE_ASM + B16x8 + "function f() {var x=b16x8();} return f");
|
||||||
|
assertAsmTypeFail('glob', USE_ASM + B16x8 + "function f() {var x=b16x8(1);} return f");
|
||||||
|
assertAsmTypeFail('glob', USE_ASM + B16x8 + "function f() {var x=b16x8(1,0,0,0);} return f");
|
||||||
|
assertAsmTypeFail('glob', USE_ASM + B16x8 + "function f() {var x=b16x8(1,0,0,0, 0,0,0,1.0);} return f");
|
||||||
|
assertAsmTypeFail('glob', USE_ASM + B16x8 + "function f() {var x=b16x8(1,0,0,0, 0,0,0,0|0);} return f");
|
||||||
|
assertAsmTypeFail('glob', USE_ASM + B16x8 + "function f() {var x=b16x8(1,0,0,0, 0,0,0,0, 1);} return f");
|
||||||
|
assertEq(asmLink(asmCompile('glob', USE_ASM + B16x8 + "function f() {var x=b16x8(1,0,0,0, 0,-1,-2,0);} return f"), this)(), undefined);
|
||||||
|
|
||||||
|
// Global variable of Int16x8 type.
|
||||||
|
assertEqVecArr(asmLink(asmCompile('glob', 'ffi', USE_ASM + I16x8 + I16x8CHK + "var g=i16x8chk(ffi.g); function f() { return i16x8chk(g); } return f"), this,
|
||||||
|
{g: SIMD.Int16x8(1,2,3,4,5,6,7,8)})(), [1,2,3,4,5,6,7,8]);
|
||||||
|
assertEqVecArr(asmLink(asmCompile('glob', 'ffi', USE_ASM + I16x8 + I16x8CHK + "var g=i16x8chk(ffi.g); function f() { g=i16x8(5,6,7,8,9,10,11,12); return i16x8chk(g); } return f"), this,
|
||||||
|
{g: SIMD.Int16x8(1,2,3,4,5,6,7,8)})(), [5,6,7,8,9,10,11,12]);
|
||||||
|
|
||||||
|
// Global variable of Bool16x8 type.
|
||||||
|
assertEqVecArr(asmLink(asmCompile('glob', 'ffi', USE_ASM + B16x8 + B16x8CHK + "var g=b16x8chk(ffi.g); function f() { return b16x8chk(g); } return f"), this,
|
||||||
|
{g: SIMD.Bool16x8(1,1,0,1,0,0,1,0)})(), [true,true,false,true,false,false,true,false]);
|
||||||
|
assertEqVecArr(asmLink(asmCompile('glob', 'ffi', USE_ASM + B16x8 + B16x8CHK + "var g=b16x8chk(ffi.g); function f() { g=b16x8(1,1,0,1,0,1,1,1); return b16x8chk(g); } return f"), this,
|
||||||
|
{g: SIMD.Bool16x8(1,1,0,1,0,0,1,0)})(), [true,true,false,true,false,true,true,true]);
|
||||||
|
|
||||||
|
// Unsigned SIMD globals are not allowed.
|
||||||
|
assertAsmTypeFail('glob', 'ffi', USE_ASM + U16x8 + U16x8CHK + "var g=u16x8chk(ffi.g); function f() { } return f");
|
||||||
|
|
||||||
|
// Only signed Int16x8 allowed as return value.
|
||||||
|
assertEqVecArr(asmLink(asmCompile('glob', USE_ASM + I16x8 + "function f() {return i16x8(1,2,3,4,5,6,7,8);} return f"), this)(),
|
||||||
|
[1, 2, 3, 4, 5, 6, 7, 8]);
|
||||||
|
assertEqVecArr(asmLink(asmCompile('glob', USE_ASM + I16x8 + I16x8CHK + "function f() {return i16x8chk(i16x8(1,2,3,32771,5,6,7,8));} return f"), this)(),
|
||||||
|
[1, 2, 3, -32765, 5, 6, 7, 8]);
|
||||||
|
assertAsmTypeFail('glob', USE_ASM + U16x8 + "function f() {return u16x8(1,2,3,4,5,6,7,8);} return f");
|
||||||
|
assertAsmTypeFail('glob', USE_ASM + U16x8 + U16x8CHK + "function f() {return u16x8chk(u16x8(1,2,3,4,5,6,7,8));} return f");
|
||||||
|
|
||||||
|
// Test splat.
|
||||||
|
function splat(x) {
|
||||||
|
let r = []
|
||||||
|
for (let i = 0; i < 8; i++)
|
||||||
|
r.push(x);
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
splatB = asmLink(asmCompile('glob', USE_ASM + B16x8 +
|
||||||
|
'var splat = b16x8.splat;' +
|
||||||
|
'function f(x) { x = x|0; return splat(x); } return f'), this);
|
||||||
|
assertEqVecArr(splatB(true), splat(true));
|
||||||
|
assertEqVecArr(splatB(false), splat(false));
|
||||||
|
|
||||||
|
|
||||||
|
splatB0 = asmLink(asmCompile('glob', USE_ASM + B16x8 +
|
||||||
|
'var splat = b16x8.splat;' +
|
||||||
|
'function f() { var x = 0; return splat(x); } return f'), this);
|
||||||
|
assertEqVecArr(splatB0(), splat(false));
|
||||||
|
splatB1 = asmLink(asmCompile('glob', USE_ASM + B16x8 +
|
||||||
|
'var splat = b16x8.splat;' +
|
||||||
|
'function f() { var x = 1; return splat(x); } return f'), this);
|
||||||
|
assertEqVecArr(splatB1(), splat(true));
|
||||||
|
|
||||||
|
splatI = asmLink(asmCompile('glob', USE_ASM + I16x8 +
|
||||||
|
'var splat = i16x8.splat;' +
|
||||||
|
'function f(x) { x = x|0; return splat(x); } return f'), this);
|
||||||
|
for (let x of [0, 1, -1, 0x12345, 0x1234, -1000, -1000000]) {
|
||||||
|
assertEqVecArr(splatI(x), splat(x << 16 >> 16));
|
||||||
|
}
|
||||||
|
|
||||||
|
splatIc = asmLink(asmCompile('glob', USE_ASM + I16x8 +
|
||||||
|
'var splat = i16x8.splat;' +
|
||||||
|
'function f() { var x = 100; return splat(x); } return f'), this);
|
||||||
|
assertEqVecArr(splatIc(), splat(100))
|
||||||
|
|
||||||
|
splatU = asmLink(asmCompile('glob', USE_ASM + U16x8 + I16x8 + I16x8U16x8 +
|
||||||
|
'var splat = u16x8.splat;' +
|
||||||
|
'function f(x) { x = x|0; return i16x8u16x8(splat(x)); } return f'), this);
|
||||||
|
for (let x of [0, 1, -1, 0x12345, 0x1234, -1000, -1000000]) {
|
||||||
|
assertEqVecArr(SIMD.Uint16x8.fromInt16x8Bits(splatI(x)), splat(x << 16 >>> 16));
|
||||||
|
}
|
||||||
|
|
||||||
|
splatUc = asmLink(asmCompile('glob', USE_ASM + U16x8 + I16x8 + I16x8U16x8 +
|
||||||
|
'var splat = u16x8.splat;' +
|
||||||
|
'function f() { var x = 200; return i16x8u16x8(splat(x)); } return f'), this);
|
||||||
|
assertEqVecArr(SIMD.Uint16x8.fromInt16x8Bits(splatUc()), splat(200))
|
||||||
|
|
||||||
|
|
||||||
|
// Test extractLane.
|
||||||
|
//
|
||||||
|
// The lane index must be a literal int, and we generate different code for
|
||||||
|
// different lanes.
|
||||||
|
function extractI(a, i) {
|
||||||
|
return asmLink(asmCompile('glob', USE_ASM + I16x8 + I16x8EXT +
|
||||||
|
`function f() {var x=i16x8(${a.join(',')}); return i16x8ext(x, ${i})|0; } return f`), this)();
|
||||||
|
}
|
||||||
|
a = [-1,2,-3,4,-5,6,-7,-8];
|
||||||
|
for (var i = 0; i < 8; i++)
|
||||||
|
assertEq(extractI(a, i), a[i]);
|
||||||
|
a = a.map(x => -x);
|
||||||
|
for (var i = 0; i < 8; i++)
|
||||||
|
assertEq(extractI(a, i), a[i]);
|
||||||
|
|
||||||
|
function extractU(a, i) {
|
||||||
|
return asmLink(asmCompile('glob', USE_ASM + U16x8 + U16x8EXT +
|
||||||
|
`function f() {var x=u16x8(${a.join(',')}); return u16x8ext(x, ${i})|0; } return f`), this)();
|
||||||
|
}
|
||||||
|
a = [1,255,12,13,14,150,200,3];
|
||||||
|
for (var i = 0; i < 8; i++)
|
||||||
|
assertEq(extractU(a, i), a[i]);
|
||||||
|
a = a.map(x => UINT16_MAX-x);
|
||||||
|
for (var i = 0; i < 8; i++)
|
||||||
|
assertEq(extractU(a, i), a[i]);
|
||||||
|
|
||||||
|
function extractB(a, i) {
|
||||||
|
return asmLink(asmCompile('glob', USE_ASM + B16x8 + B16x8EXT +
|
||||||
|
`function f() {var x=b16x8(${a.join(',')}); return b16x8ext(x, ${i})|0; } return f`), this)();
|
||||||
|
}
|
||||||
|
a = [1,1,0,1, 1,0,0,0];
|
||||||
|
for (var i = 0; i < 8; i++)
|
||||||
|
assertEq(extractB(a, i), a[i]);
|
||||||
|
a = a.map(x => 1-x);
|
||||||
|
for (var i = 0; i < 8; i++)
|
||||||
|
assertEq(extractB(a, i), a[i]);
|
||||||
|
|
||||||
|
// Test replaceLane.
|
||||||
|
function replaceI(a, i) {
|
||||||
|
return asmLink(asmCompile('glob', USE_ASM + I16x8 + I16x8REP +
|
||||||
|
`function f(v) {v=v|0; var x=i16x8(${a.join(',')}); return i16x8rep(x,${i},v); } return f`), this);
|
||||||
|
}
|
||||||
|
a = [-1,2,-3,4,-5,6,-7,-9];
|
||||||
|
for (var i = 0; i < 8; i++) {
|
||||||
|
var f = replaceI(a, i);
|
||||||
|
var b = a.slice(0);
|
||||||
|
b[i] = -20;
|
||||||
|
assertEqVecArr(f(-20), b);
|
||||||
|
}
|
||||||
|
|
||||||
|
function replaceU(a, i) {
|
||||||
|
return asmLink(asmCompile('glob', USE_ASM + U16x8 + U16x8REP + I16x8 + I16x8U16x8 +
|
||||||
|
`function f(v) {v=v|0; var x=u16x8(${a.join(',')}); return i16x8u16x8(u16x8rep(x,${i},v)); } return f`), this);
|
||||||
|
}
|
||||||
|
a = [65000-1,2,65000-3,4,65000-5,6,65000-7,65000-9];
|
||||||
|
for (var i = 0; i < 8; i++) {
|
||||||
|
var rawf = replaceU(a, i);
|
||||||
|
var f = x => SIMD.Uint16x8.fromInt16x8Bits(rawf(x))
|
||||||
|
var b = a.slice(0);
|
||||||
|
b[i] = 1000;
|
||||||
|
assertEqVecArr(f(1000), b);
|
||||||
|
}
|
||||||
|
|
||||||
|
function replaceB(a, i) {
|
||||||
|
return asmLink(asmCompile('glob', USE_ASM + B16x8 + B16x8REP +
|
||||||
|
`function f(v) {v=v|0; var x=b16x8(${a.join(',')}); return b16x8rep(x,${i},v); } return f`), this);
|
||||||
|
}
|
||||||
|
a = [1,1,0,1,1,0,0,0];
|
||||||
|
for (var i = 0; i < 8; i++) {
|
||||||
|
var f = replaceB(a, i);
|
||||||
|
var b = a.slice(0);
|
||||||
|
let v = 1 - a[i];
|
||||||
|
b[i] = v;
|
||||||
|
assertEqVecArr(f(v), b.map(x => !!x));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Test select.
|
||||||
|
selectI = asmLink(asmCompile('glob', USE_ASM + I16x8 + B16x8 + B16x8CHK +
|
||||||
|
'var select = i16x8.select;' +
|
||||||
|
'var a = i16x8(-1,2,-3,4,-5, 6,-7, 8);' +
|
||||||
|
'var b = i16x8( 5,6, 7,8, 9,10,11,12);' +
|
||||||
|
'function f(x) { x = b16x8chk(x); return select(x, a, b); } return f'), this);
|
||||||
|
assertEqVecArr(selectI(SIMD.Bool16x8( 0,0, 1,0, 1,1, 1, 0)),
|
||||||
|
[ 5,6,-3,8,-5,6,-7,12]);
|
||||||
|
|
||||||
|
selectU = asmLink(asmCompile('glob', USE_ASM + I16x8 + B16x8 + B16x8CHK + U16x8 + I16x8U16x8 + U16x8I16x8 +
|
||||||
|
'var select = u16x8.select;' +
|
||||||
|
'var a = i16x8(-1,2,-3,4,-5, 6,-7, 8);' +
|
||||||
|
'var b = i16x8( 5,6, 7,8, 9,10,11,12);' +
|
||||||
|
'function f(x) { x = b16x8chk(x); return i16x8u16x8(select(x, u16x8i16x8(a), u16x8i16x8(b))); } return f'), this);
|
||||||
|
assertEqVecArr(selectU(SIMD.Bool16x8( 0,0, 1,0, 1,1, 1, 0)),
|
||||||
|
[ 5,6,-3,8,-5,6,-7,12]);
|
||||||
|
|
||||||
|
// Test swizzle.
|
||||||
|
function swizzle(vec, lanes) {
|
||||||
|
let r = [];
|
||||||
|
for (let i = 0; i < 8; i++)
|
||||||
|
r.push(vec[lanes[i]]);
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
function swizzleI(lanes) {
|
||||||
|
let asm = asmLink(asmCompile('glob', USE_ASM + I16x8 + I16x8CHK +
|
||||||
|
'var swz = i16x8.swizzle;' +
|
||||||
|
`function f(a) { a = i16x8chk(a); return swz(a, ${lanes.join()}); } return f`), this);
|
||||||
|
let a1 = [ -1,2, -3,0x8000,0x7f,6,-7, 8].map(x => x << 16 >> 16);
|
||||||
|
let a2 = [0x8000,2,0x8000,0x7fff, 0,0, 8,-9].map(x => x << 16 >> 16);
|
||||||
|
let v1 = SIMD.Int16x8(...a1);
|
||||||
|
let v2 = SIMD.Int16x8(...a2);
|
||||||
|
assertEqVecArr(asm(v1), swizzle(a1, lanes));
|
||||||
|
assertEqVecArr(asm(v2), swizzle(a2, lanes));
|
||||||
|
}
|
||||||
|
|
||||||
|
swizzleI([3, 4, 7, 1, 4, 3, 1, 2]);
|
||||||
|
swizzleI([0, 0, 0, 0, 0, 0, 0, 0]);
|
||||||
|
swizzleI([7, 7, 7, 7, 7, 7, 7, 7]);
|
||||||
|
|
||||||
|
function swizzleU(lanes) {
|
||||||
|
let asm = asmLink(asmCompile('glob', USE_ASM + I16x8 + I16x8CHK + U16x8 + U16x8I16x8 + I16x8U16x8 +
|
||||||
|
'var swz = u16x8.swizzle;' +
|
||||||
|
`function f(a) { a = i16x8chk(a); return i16x8u16x8(swz(u16x8i16x8(a), ${lanes.join()})); } return f`), this);
|
||||||
|
let a1 = [ -1,2, -3,0x8000,0x7f,6,-7, 8].map(x => x << 16 >> 16);
|
||||||
|
let a2 = [0x8000,2,0x8000,0x7fff, 0,0, 8,-9].map(x => x << 16 >> 16);
|
||||||
|
let v1 = SIMD.Int16x8(...a1);
|
||||||
|
let v2 = SIMD.Int16x8(...a2);
|
||||||
|
assertEqVecArr(asm(v1), swizzle(a1, lanes));
|
||||||
|
assertEqVecArr(asm(v2), swizzle(a2, lanes));
|
||||||
|
}
|
||||||
|
|
||||||
|
swizzleU([3, 4, 7, 1, 4, 3, 1, 2]);
|
||||||
|
swizzleU([0, 0, 0, 0, 0, 0, 0, 0]);
|
||||||
|
swizzleU([7, 7, 7, 7, 7, 7, 7, 7]);
|
||||||
|
|
||||||
|
// Out-of-range lane indexes.
|
||||||
|
assertAsmTypeFail('glob', USE_ASM + I16x8 + 'var swz = i16x8.swizzle; ' +
|
||||||
|
'function f() { var x=i16x8(0,0,0,0,0,0,0,0); swz(x,1,2,3,4,5,6,7,8); } return f');
|
||||||
|
assertAsmTypeFail('glob', USE_ASM + U16x8 + 'var swz = u16x8.swizzle; ' +
|
||||||
|
'function f() { var x=u16x8(0,0,0,0,0,0,0,0); swz(x,1,2,3,4,5,6,7,8); } return f');
|
||||||
|
// Missing lane indexes.
|
||||||
|
assertAsmTypeFail('glob', USE_ASM + I16x8 + 'var swz = i16x8.swizzle; ' +
|
||||||
|
'function f() { var x=i16x8(0,0,0,0,0,0,0,0); swz(x,1,2,3,4,5,6,7); } return f');
|
||||||
|
assertAsmTypeFail('glob', USE_ASM + U16x8 + 'var swz = u16x8.swizzle; ' +
|
||||||
|
'function f() { var x=u16x8(0,0,0,0,0,0,0,0); swz(x,1,2,3,4,5,6,7); } return f');
|
||||||
|
|
||||||
|
|
||||||
|
// Test shuffle.
|
||||||
|
function shuffle(vec1, vec2, lanes) {
|
||||||
|
let r = [];
|
||||||
|
let vec = vec1.concat(vec2)
|
||||||
|
for (let i = 0; i < 8; i++)
|
||||||
|
r.push(vec[lanes[i]]);
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
function shuffleI(lanes) {
|
||||||
|
let asm = asmLink(asmCompile('glob', USE_ASM + I16x8 + I16x8CHK +
|
||||||
|
'var shuf = i16x8.shuffle;' +
|
||||||
|
`function f(a1, a2) { a1 = i16x8chk(a1); a2 = i16x8chk(a2); return shuf(a1, a2, ${lanes.join()}); } return f`), this);
|
||||||
|
let a1 = [ -1,2, -3,0x8000,0x7f,6,-7, 8].map(x => x << 16 >> 16);
|
||||||
|
let a2 = [0x8000,2,0x8000,0x7fff, 0,0, 8,-9].map(x => x << 16 >> 16);
|
||||||
|
let v1 = SIMD.Int16x8(...a1);
|
||||||
|
let v2 = SIMD.Int16x8(...a2);
|
||||||
|
assertEqVecArr(asm(v1, v2), shuffle(a1, a2, lanes));
|
||||||
|
}
|
||||||
|
|
||||||
|
function shuffleU(lanes) {
|
||||||
|
let asm = asmLink(asmCompile('glob', USE_ASM + I16x8 + I16x8CHK + U16x8 + U16x8I16x8 + I16x8U16x8 +
|
||||||
|
'var shuf = u16x8.shuffle;' +
|
||||||
|
'function f(a1, a2) { a1 = i16x8chk(a1); a2 = i16x8chk(a2); ' +
|
||||||
|
`return i16x8u16x8(shuf(u16x8i16x8(a1), u16x8i16x8(a2), ${lanes.join()})); } return f`), this);
|
||||||
|
let a1 = [ -1,2, -3,0x8000,0x7f,6,-7, 8].map(x => x << 16 >> 16);
|
||||||
|
let a2 = [0x8000,2,0x8000,0x7fff, 0,0, 8,-9].map(x => x << 16 >> 16);
|
||||||
|
let v1 = SIMD.Int16x8(...a1);
|
||||||
|
let v2 = SIMD.Int16x8(...a2);
|
||||||
|
assertEqVecArr(asm(v1, v2), shuffle(a1, a2, lanes));
|
||||||
|
}
|
||||||
|
|
||||||
|
shuffleI([0, 0, 0, 0, 0, 0, 0, 0])
|
||||||
|
shuffleI([15, 15, 15, 15, 15, 15, 15, 15])
|
||||||
|
shuffleI([6, 2, 0, 14, 6, 10, 11, 1])
|
||||||
|
|
||||||
|
shuffleU([7, 7, 7, 7, 7, 7, 7, 7])
|
||||||
|
shuffleU([8, 15, 15, 15, 15, 15, 15, 15])
|
||||||
|
shuffleU([6, 2, 0, 14, 6, 10, 11, 1])
|
||||||
|
|
||||||
|
// Test unary operators.
|
||||||
|
function unaryI(opname, lanefunc) {
|
||||||
|
let simdfunc = asmLink(asmCompile('glob', USE_ASM + I16x8 + I16x8CHK +
|
||||||
|
`var fut = i16x8.${opname};` +
|
||||||
|
'function f(v) { v = i16x8chk(v); return fut(v); } return f'), this);
|
||||||
|
let a = [65000-1,2,65000-3,4,65000-5,6,65000-7,65000-9];
|
||||||
|
let v = SIMD.Int16x8(...a);
|
||||||
|
assertEqVecArr(simdfunc(v), a.map(lanefunc));
|
||||||
|
}
|
||||||
|
|
||||||
|
function unaryU(opname, lanefunc) {
|
||||||
|
let simdfunc = asmLink(asmCompile('glob', USE_ASM + U16x8 + I16x8 + I16x8CHK + U16x8I16x8 + I16x8U16x8 +
|
||||||
|
`var fut = u16x8.${opname};` +
|
||||||
|
'function f(v) { v = i16x8chk(v); return i16x8u16x8(fut(u16x8i16x8(v))); } return f'), this);
|
||||||
|
let a = [65000-1,2,65000-3,4,65000-5,6,65000-7,65000-9];
|
||||||
|
let v = SIMD.Int16x8(...a);
|
||||||
|
assertEqVecArr(SIMD.Uint16x8.fromInt16x8Bits(simdfunc(v)), a.map(lanefunc));
|
||||||
|
}
|
||||||
|
|
||||||
|
function unaryB(opname, lanefunc) {
|
||||||
|
let simdfunc = asmLink(asmCompile('glob', USE_ASM + B16x8 + B16x8CHK +
|
||||||
|
`var fut = b16x8.${opname};` +
|
||||||
|
'function f(v) { v = b16x8chk(v); return fut(v); } return f'), this);
|
||||||
|
let a = [1,1,0,1,1,0,0,0];
|
||||||
|
let v = SIMD.Bool16x8(...a);
|
||||||
|
assertEqVecArr(simdfunc(v), a.map(lanefunc));
|
||||||
|
}
|
||||||
|
|
||||||
|
unaryI('not', x => ~x << 16 >> 16);
|
||||||
|
unaryU('not', x => ~x << 16 >>> 16);
|
||||||
|
unaryB('not', x => !x);
|
||||||
|
unaryI('neg', x => -x << 16 >> 16);
|
||||||
|
unaryU('neg', x => -x << 16 >>> 16);
|
||||||
|
|
||||||
|
|
||||||
|
// Test binary operators.
|
||||||
|
function zipmap(a1, a2, f) {
|
||||||
|
assertEq(a1.length, a2.length);
|
||||||
|
let r = [];
|
||||||
|
for (var i = 0; i < a1.length; i++)
|
||||||
|
r.push(f(a1[i], a2[i]));
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
function binaryI(opname, lanefunc) {
|
||||||
|
let simdfunc = asmLink(asmCompile('glob', USE_ASM + I16x8 + I16x8CHK +
|
||||||
|
`var fut = i16x8.${opname};` +
|
||||||
|
'function f(v1, v2) { v1 = i16x8chk(v1); v2 = i16x8chk(v2); return fut(v1, v2); } return f'), this);
|
||||||
|
let a1 = [ -1,2, -3,0x8000,0x7f,6,-7, 8].map(x => x << 16 >> 16);
|
||||||
|
let a2 = [0x8000,2,0x8000,0x7fff, 0,0, 8,-9].map(x => x << 16 >> 16);
|
||||||
|
let ref = zipmap(a1, a2, lanefunc);
|
||||||
|
let v1 = SIMD.Int16x8(...a1);
|
||||||
|
let v2 = SIMD.Int16x8(...a2);
|
||||||
|
assertEqVecArr(simdfunc(v1, v2), ref);
|
||||||
|
}
|
||||||
|
|
||||||
|
function binaryU(opname, lanefunc) {
|
||||||
|
let simdfunc = asmLink(asmCompile('glob', USE_ASM + U16x8 + I16x8 + I16x8CHK + U16x8I16x8 + I16x8U16x8 +
|
||||||
|
`var fut = u16x8.${opname};` +
|
||||||
|
'function f(v1, v2) { v1 = i16x8chk(v1); v2 = i16x8chk(v2); return i16x8u16x8(fut(u16x8i16x8(v1), u16x8i16x8(v2))); } return f'), this);
|
||||||
|
let a1 = [ -1,2, -3,0x8000,0x7f,6,-7, 8].map(x => x << 16 >>> 16);
|
||||||
|
let a2 = [0x8000,2,0x8000,0x7fff, 0,0, 8,-9].map(x => x << 16 >>> 16);
|
||||||
|
let ref = zipmap(a1, a2, lanefunc);
|
||||||
|
let v1 = SIMD.Int16x8(...a1);
|
||||||
|
let v2 = SIMD.Int16x8(...a2);
|
||||||
|
let res = SIMD.Uint16x8.fromInt16x8Bits(simdfunc(v1, v2));
|
||||||
|
assertEqVecArr(res, ref);
|
||||||
|
}
|
||||||
|
|
||||||
|
function binaryB(opname, lanefunc) {
|
||||||
|
let simdfunc = asmLink(asmCompile('glob', USE_ASM + B16x8 + B16x8CHK +
|
||||||
|
`var fut = b16x8.${opname};` +
|
||||||
|
'function f(v1, v2) { v1 = b16x8chk(v1); v2 = b16x8chk(v2); return fut(v1, v2); } return f'), this);
|
||||||
|
let a = [1,1,0,1,1,0,0,0];
|
||||||
|
let v = SIMD.Bool16x8(...a);
|
||||||
|
assertEqVecArr(simdfunc(v), a.map(lanefunc));
|
||||||
|
}
|
||||||
|
|
||||||
|
binaryI('add', (x, y) => (x + y) << 16 >> 16);
|
||||||
|
binaryI('sub', (x, y) => (x - y) << 16 >> 16);
|
||||||
|
binaryI('mul', (x, y) => (x * y) << 16 >> 16);
|
||||||
|
binaryU('add', (x, y) => (x + y) << 16 >>> 16);
|
||||||
|
binaryU('sub', (x, y) => (x - y) << 16 >>> 16);
|
||||||
|
binaryU('mul', (x, y) => (x * y) << 16 >>> 16);
|
||||||
|
|
||||||
|
binaryI('and', (x, y) => (x & y) << 16 >> 16);
|
||||||
|
binaryI('or', (x, y) => (x | y) << 16 >> 16);
|
||||||
|
binaryI('xor', (x, y) => (x ^ y) << 16 >> 16);
|
||||||
|
binaryU('and', (x, y) => (x & y) << 16 >>> 16);
|
||||||
|
binaryU('or', (x, y) => (x | y) << 16 >>> 16);
|
||||||
|
binaryU('xor', (x, y) => (x ^ y) << 16 >>> 16);
|
||||||
|
|
||||||
|
function sat(x, lo, hi) {
|
||||||
|
if (x < lo) return lo;
|
||||||
|
if (x > hi) return hi;
|
||||||
|
return x
|
||||||
|
}
|
||||||
|
function isat(x) { return sat(x, -32768, 32767); }
|
||||||
|
function usat(x) { return sat(x, 0, 0xffff); }
|
||||||
|
|
||||||
|
binaryI('addSaturate', (x, y) => isat(x + y))
|
||||||
|
binaryI('subSaturate', (x, y) => isat(x - y))
|
||||||
|
binaryU('addSaturate', (x, y) => usat(x + y))
|
||||||
|
binaryU('subSaturate', (x, y) => usat(x - y))
|
||||||
|
|
||||||
|
|
||||||
|
// Test shift operators.
|
||||||
|
function zip1map(a, s, f) {
|
||||||
|
return a.map(x => f(x, s));
|
||||||
|
}
|
||||||
|
|
||||||
|
function shiftI(opname, lanefunc) {
|
||||||
|
let simdfunc = asmLink(asmCompile('glob', USE_ASM + I16x8 + I16x8CHK +
|
||||||
|
`var fut = i16x8.${opname};` +
|
||||||
|
'function f(v, s) { v = i16x8chk(v); s = s|0; return fut(v, s); } return f'), this);
|
||||||
|
let a = [-1,2,-3,0x80,0x7f,6,0x8000,0x7fff];
|
||||||
|
let v = SIMD.Int16x8(...a);
|
||||||
|
for (let s of [0, 1, 2, 6, 7, 8, 9, 10, 16, 255, -1, -8, -7, -1000]) {
|
||||||
|
let ref = zip1map(a, s, lanefunc);
|
||||||
|
// 1. Test dynamic shift amount.
|
||||||
|
assertEqVecArr(simdfunc(v, s), ref);
|
||||||
|
|
||||||
|
// 2. Test constant shift amount.
|
||||||
|
let cstf = asmLink(asmCompile('glob', USE_ASM + I16x8 + I16x8CHK +
|
||||||
|
`var fut = i16x8.${opname};` +
|
||||||
|
`function f(v) { v = i16x8chk(v); return fut(v, ${s}); } return f`), this);
|
||||||
|
assertEqVecArr(cstf(v, s), ref);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function shiftU(opname, lanefunc) {
|
||||||
|
let simdfunc = asmLink(asmCompile('glob', USE_ASM + U16x8 + I16x8 + I16x8CHK + U16x8I16x8 + I16x8U16x8 +
|
||||||
|
`var fut = u16x8.${opname};` +
|
||||||
|
'function f(v, s) { v = i16x8chk(v); s = s|0; return i16x8u16x8(fut(u16x8i16x8(v), s)); } return f'), this);
|
||||||
|
let a = [-1,2,-3,0x80,0x7f,6,0x8000,0x7fff];
|
||||||
|
let v = SIMD.Int16x8(...a);
|
||||||
|
for (let s of [0, 1, 2, 6, 7, 8, 9, 10, 16, 255, -1, -8, -7, -1000]) {
|
||||||
|
let ref = zip1map(a, s, lanefunc);
|
||||||
|
// 1. Test dynamic shift amount.
|
||||||
|
assertEqVecArr(SIMD.Uint16x8.fromInt16x8Bits(simdfunc(v, s)), ref);
|
||||||
|
|
||||||
|
// 2. Test constant shift amount.
|
||||||
|
let cstf = asmLink(asmCompile('glob', USE_ASM + U16x8 + I16x8 + I16x8CHK + U16x8I16x8 + I16x8U16x8 +
|
||||||
|
`var fut = u16x8.${opname};` +
|
||||||
|
`function f(v) { v = i16x8chk(v); return i16x8u16x8(fut(u16x8i16x8(v), ${s})); } return f`), this);
|
||||||
|
assertEqVecArr(SIMD.Uint16x8.fromInt16x8Bits(cstf(v, s)), ref);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
shiftI('shiftLeftByScalar', (x,s) => (x << (s & 15)) << 16 >> 16);
|
||||||
|
shiftU('shiftLeftByScalar', (x,s) => (x << (s & 15)) << 16 >>> 16);
|
||||||
|
shiftI('shiftRightByScalar', (x,s) => ((x << 16 >> 16) >> (s & 15)) << 16 >> 16);
|
||||||
|
shiftU('shiftRightByScalar', (x,s) => ((x << 16 >>> 16) >>> (s & 15)) << 16 >>> 16);
|
||||||
|
|
||||||
|
|
||||||
|
// Comparisons.
|
||||||
|
function compareI(opname, lanefunc) {
|
||||||
|
let simdfunc = asmLink(asmCompile('glob', USE_ASM + I16x8 + I16x8CHK +
|
||||||
|
`var fut = i16x8.${opname};` +
|
||||||
|
'function f(v1, v2) { v1 = i16x8chk(v1); v2 = i16x8chk(v2); return fut(v1, v2); } return f'), this);
|
||||||
|
let a1 = [ -1,2, -3,0x8000,0x7f,6,-7, 8].map(x => x << 16 >> 16);
|
||||||
|
let a2 = [0x8000,2,0x8000,0x7fff, 0,0, 8,-9].map(x => x << 16 >> 16);
|
||||||
|
let ref = zipmap(a1, a2, lanefunc);
|
||||||
|
let v1 = SIMD.Int16x8(...a1);
|
||||||
|
let v2 = SIMD.Int16x8(...a2);
|
||||||
|
assertEqVecArr(simdfunc(v1, v2), ref);
|
||||||
|
}
|
||||||
|
|
||||||
|
function compareU(opname, lanefunc) {
|
||||||
|
let simdfunc = asmLink(asmCompile('glob', USE_ASM + I16x8 + I16x8CHK + U16x8 + U16x8I16x8 +
|
||||||
|
`var fut = u16x8.${opname};` +
|
||||||
|
'function f(v1, v2) { v1 = i16x8chk(v1); v2 = i16x8chk(v2); return fut(u16x8i16x8(v1), u16x8i16x8(v2)); } return f'), this);
|
||||||
|
let a1 = [ -1,2, -3,0x8000,0x7f,6,-7, 8].map(x => x << 16 >>> 16);
|
||||||
|
let a2 = [0x8000,2,0x8000,0x7fff, 0,0, 8,-9].map(x => x << 16 >>> 16);
|
||||||
|
let ref = zipmap(a1, a2, lanefunc);
|
||||||
|
let v1 = SIMD.Int16x8(...a1);
|
||||||
|
let v2 = SIMD.Int16x8(...a2);
|
||||||
|
assertEqVecArr(simdfunc(v1, v2), ref);
|
||||||
|
}
|
||||||
|
|
||||||
|
compareI("equal", (x,y) => x == y);
|
||||||
|
compareU("equal", (x,y) => x == y);
|
||||||
|
compareI("notEqual", (x,y) => x != y);
|
||||||
|
compareU("notEqual", (x,y) => x != y);
|
||||||
|
compareI("lessThan", (x,y) => x < y);
|
||||||
|
compareU("lessThan", (x,y) => x < y);
|
||||||
|
compareI("lessThanOrEqual", (x,y) => x <= y);
|
||||||
|
compareU("lessThanOrEqual", (x,y) => x <= y);
|
||||||
|
compareI("greaterThan", (x,y) => x > y);
|
||||||
|
compareU("greaterThan", (x,y) => x > y);
|
||||||
|
compareI("greaterThanOrEqual", (x,y) => x >= y);
|
||||||
|
compareU("greaterThanOrEqual", (x,y) => x >= y);
|
|
@ -0,0 +1,539 @@
|
||||||
|
load(libdir + "asm.js");
|
||||||
|
load(libdir + "simd.js");
|
||||||
|
load(libdir + "asserts.js");
|
||||||
|
|
||||||
|
// Set to true to see more JS debugging spew.
|
||||||
|
const DEBUG = false;
|
||||||
|
|
||||||
|
if (!isSimdAvailable()) {
|
||||||
|
DEBUG && print("won't run tests as simd extensions aren't activated yet");
|
||||||
|
quit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tests for 8x16 SIMD types: Int8x16, Uint8x16, Bool8x16.
|
||||||
|
|
||||||
|
const I8x16 = 'var i8x16 = glob.SIMD.Int8x16;'
|
||||||
|
const I8x16CHK = 'var i8x16chk = i8x16.check;'
|
||||||
|
const I8x16EXT = 'var i8x16ext = i8x16.extractLane;'
|
||||||
|
const I8x16REP = 'var i8x16rep = i8x16.replaceLane;'
|
||||||
|
const I8x16U8x16 = 'var i8x16u8x16 = i8x16.fromUint8x16Bits;'
|
||||||
|
|
||||||
|
const U8x16 = 'var u8x16 = glob.SIMD.Uint8x16;'
|
||||||
|
const U8x16CHK = 'var u8x16chk = u8x16.check;'
|
||||||
|
const U8x16EXT = 'var u8x16ext = u8x16.extractLane;'
|
||||||
|
const U8x16REP = 'var u8x16rep = u8x16.replaceLane;'
|
||||||
|
const U8x16I8x16 = 'var u8x16i8x16 = u8x16.fromInt8x16Bits;'
|
||||||
|
|
||||||
|
const B8x16 = 'var b8x16 = glob.SIMD.Bool8x16;'
|
||||||
|
const B8x16CHK = 'var b8x16chk = b8x16.check;'
|
||||||
|
const B8x16EXT = 'var b8x16ext = b8x16.extractLane;'
|
||||||
|
const B8x16REP = 'var b8x16rep = b8x16.replaceLane;'
|
||||||
|
|
||||||
|
const INT8_MAX = 127
|
||||||
|
const INT8_MIN = -128
|
||||||
|
const UINT8_MAX = 255
|
||||||
|
|
||||||
|
// Linking
|
||||||
|
assertEq(asmLink(asmCompile('glob', USE_ASM + I8x16 + "function f() {} return f"), {SIMD:{Int8x16: SIMD.Int8x16}})(), undefined);
|
||||||
|
assertEq(asmLink(asmCompile('glob', USE_ASM + U8x16 + "function f() {} return f"), {SIMD:{Uint8x16: SIMD.Uint8x16}})(), undefined);
|
||||||
|
assertEq(asmLink(asmCompile('glob', USE_ASM + B8x16 + "function f() {} return f"), {SIMD:{Bool8x16: SIMD.Bool8x16}})(), undefined);
|
||||||
|
|
||||||
|
// Local variable of Int8x16 type.
|
||||||
|
assertAsmTypeFail('glob', USE_ASM + "function f() {var x=Int8x16(1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16);} return f");
|
||||||
|
assertAsmTypeFail('glob', USE_ASM + I8x16 + "function f() {var x=i8x16;} return f");
|
||||||
|
assertAsmTypeFail('glob', USE_ASM + I8x16 + "function f() {var x=i8x16();} return f");
|
||||||
|
assertAsmTypeFail('glob', USE_ASM + I8x16 + "function f() {var x=i8x16(1);} return f");
|
||||||
|
assertAsmTypeFail('glob', USE_ASM + I8x16 + "function f() {var x=i8x16(1,2,3,4);} return f");
|
||||||
|
assertAsmTypeFail('glob', USE_ASM + I8x16 + "function f() {var x=i8x16(1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16.0);} return f");
|
||||||
|
assertAsmTypeFail('glob', USE_ASM + I8x16 + "function f() {var x=i8x16(1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17);} return f");
|
||||||
|
assertAsmTypeFail('glob', USE_ASM + I8x16 + "function f() {var x=i8x16(1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16|0);} return f");
|
||||||
|
assertEq(asmLink(asmCompile('glob', USE_ASM + I8x16 + "function f() {var x=i8x16(1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16);} return f"), this)(), undefined);
|
||||||
|
assertEq(asmLink(asmCompile('glob', USE_ASM + I8x16 + "function f() {var x=i8x16(1,2,3,4,5,6,7,8,9,10,11,12,13,14,15," + (INT8_MAX + 1) + ");} return f"), this)(), undefined);
|
||||||
|
|
||||||
|
// Local variable of Uint8x16 type.
|
||||||
|
assertAsmTypeFail('glob', USE_ASM + "function f() {var x=Uint8x16(1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16);} return f");
|
||||||
|
assertAsmTypeFail('glob', USE_ASM + U8x16 + "function f() {var x=u8x16;} return f");
|
||||||
|
assertAsmTypeFail('glob', USE_ASM + U8x16 + "function f() {var x=u8x16();} return f");
|
||||||
|
assertAsmTypeFail('glob', USE_ASM + U8x16 + "function f() {var x=u8x16(1);} return f");
|
||||||
|
assertAsmTypeFail('glob', USE_ASM + U8x16 + "function f() {var x=u8x16(1,2,3,4);} return f");
|
||||||
|
assertAsmTypeFail('glob', USE_ASM + U8x16 + "function f() {var x=u8x16(1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16.0);} return f");
|
||||||
|
assertAsmTypeFail('glob', USE_ASM + U8x16 + "function f() {var x=u8x16(1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17);} return f");
|
||||||
|
assertAsmTypeFail('glob', USE_ASM + U8x16 + "function f() {var x=u8x16(1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16|0);} return f");
|
||||||
|
assertEq(asmLink(asmCompile('glob', USE_ASM + U8x16 + "function f() {var x=u8x16(1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16);} return f"), this)(), undefined);
|
||||||
|
assertEq(asmLink(asmCompile('glob', USE_ASM + U8x16 + "function f() {var x=u8x16(1,2,3,4,5,6,7,8,9,10,11,12,13,14,15," + (UINT8_MAX + 1) + ");} return f"), this)(), undefined);
|
||||||
|
|
||||||
|
// Local variable of Bool8x16 type.
|
||||||
|
assertAsmTypeFail('glob', USE_ASM + "function f() {var x=Bool8x16(1,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1);} return f");
|
||||||
|
assertAsmTypeFail('glob', USE_ASM + B8x16 + "function f() {var x=b8x16;} return f");
|
||||||
|
assertAsmTypeFail('glob', USE_ASM + B8x16 + "function f() {var x=b8x16();} return f");
|
||||||
|
assertAsmTypeFail('glob', USE_ASM + B8x16 + "function f() {var x=b8x16(1);} return f");
|
||||||
|
assertAsmTypeFail('glob', USE_ASM + B8x16 + "function f() {var x=b8x16(1,0,0,0);} return f");
|
||||||
|
assertAsmTypeFail('glob', USE_ASM + B8x16 + "function f() {var x=b8x16(1,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1.0);} return f");
|
||||||
|
assertAsmTypeFail('glob', USE_ASM + B8x16 + "function f() {var x=b8x16(1,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1|0);} return f");
|
||||||
|
assertAsmTypeFail('glob', USE_ASM + B8x16 + "function f() {var x=b8x16(1,0,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1);} return f");
|
||||||
|
assertEq(asmLink(asmCompile('glob', USE_ASM + B8x16 + "function f() {var x=b8x16(1,0,0,0,0,0,0,0,0,1,-1,2,-2,1,1,1);} return f"), this)(), undefined);
|
||||||
|
|
||||||
|
// Global variable of Int8x16 type.
|
||||||
|
assertEqVecArr(asmLink(asmCompile('glob', 'ffi', USE_ASM + I8x16 + I8x16CHK + "var g=i8x16chk(ffi.g); function f() { return i8x16chk(g); } return f"), this,
|
||||||
|
{g: SIMD.Int8x16(1,2,3,4,5,6,7,8,10,11,12,13,14,15,16,17)})(), [1,2,3,4,5,6,7,8,10,11,12,13,14,15,16,17]);
|
||||||
|
assertEqVecArr(asmLink(asmCompile('glob', 'ffi', USE_ASM + I8x16 + I8x16CHK + "var g=i8x16chk(ffi.g); function f() { g=i8x16(5,6,7,8,9,10,11,12,1,2,3,4,5,6,7,8); return i8x16chk(g); } return f"), this,
|
||||||
|
{g: SIMD.Int8x16(1,2,3,4,5,6,7,8,10,11,12,13,14,15,16,17)})(), [5,6,7,8,9,10,11,12,1,2,3,4,5,6,7,8]);
|
||||||
|
|
||||||
|
// Global variable of Bool8x16 type.
|
||||||
|
assertEqVecArr(asmLink(asmCompile('glob', 'ffi', USE_ASM + B8x16 + B8x16CHK + "var g=b8x16chk(ffi.g); function f() { return b8x16chk(g); } return f"), this,
|
||||||
|
{g: SIMD.Bool8x16(1,1,0,1,0,0,1,0,0,1,0,1,0,0,1,0)})(), [true,true,false,true,false,false,true,false,false,true,false,true,false,false,true,false]);
|
||||||
|
assertEqVecArr(asmLink(asmCompile('glob', 'ffi', USE_ASM + B8x16 + B8x16CHK + "var g=b8x16chk(ffi.g); function f() { g=b8x16(1,1,0,1,0,1,1,1,0,1,0,1,1,1,0,0); return b8x16chk(g); } return f"), this,
|
||||||
|
{g: SIMD.Bool8x16(1,1,0,1,0,0,1,0)})(), [true,true,false,true,false,true,true,true,false,true,false,true,true,true,false,false]);
|
||||||
|
|
||||||
|
// Unsigned SIMD globals are not allowed.
|
||||||
|
assertAsmTypeFail('glob', 'ffi', USE_ASM + U8x16 + U8x16CHK + "var g=u8x16chk(ffi.g); function f() { } return f");
|
||||||
|
|
||||||
|
// Only signed Int8x16 allowed as return value.
|
||||||
|
assertEqVecArr(asmLink(asmCompile('glob', USE_ASM + I8x16 + "function f() {return i8x16(1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16);} return f"), this)(),
|
||||||
|
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]);
|
||||||
|
assertEqVecArr(asmLink(asmCompile('glob', USE_ASM + I8x16 + I8x16CHK + "function f() {return i8x16chk(i8x16(1,2,3,132,5,6,7,8,9,10,11,12,13,14,15,16));} return f"), this)(),
|
||||||
|
[1, 2, 3, -124, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16]);
|
||||||
|
assertAsmTypeFail('glob', USE_ASM + U8x16 + "function f() {return u8x16(1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16);} return f");
|
||||||
|
assertAsmTypeFail('glob', USE_ASM + U8x16 + U8x16CHK + "function f() {return u8x16chk(u8x16(1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16));} return f");
|
||||||
|
|
||||||
|
// Test splat.
|
||||||
|
function splat(x) {
|
||||||
|
let r = []
|
||||||
|
for (let i = 0; i < 16; i++)
|
||||||
|
r.push(x);
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
splatB = asmLink(asmCompile('glob', USE_ASM + B8x16 +
|
||||||
|
'var splat = b8x16.splat;' +
|
||||||
|
'function f(x) { x = x|0; return splat(x); } return f'), this);
|
||||||
|
assertEqVecArr(splatB(true), splat(true));
|
||||||
|
assertEqVecArr(splatB(false), splat(false));
|
||||||
|
|
||||||
|
|
||||||
|
splatB0 = asmLink(asmCompile('glob', USE_ASM + B8x16 +
|
||||||
|
'var splat = b8x16.splat;' +
|
||||||
|
'function f() { var x = 0; return splat(x); } return f'), this);
|
||||||
|
assertEqVecArr(splatB0(), splat(false));
|
||||||
|
splatB1 = asmLink(asmCompile('glob', USE_ASM + B8x16 +
|
||||||
|
'var splat = b8x16.splat;' +
|
||||||
|
'function f() { var x = 1; return splat(x); } return f'), this);
|
||||||
|
assertEqVecArr(splatB1(), splat(true));
|
||||||
|
|
||||||
|
splatI = asmLink(asmCompile('glob', USE_ASM + I8x16 +
|
||||||
|
'var splat = i8x16.splat;' +
|
||||||
|
'function f(x) { x = x|0; return splat(x); } return f'), this);
|
||||||
|
for (let x of [0, 1, -1, 0x1234, 0x12, 1000, -1000000]) {
|
||||||
|
assertEqVecArr(splatI(x), splat(x << 24 >> 24));
|
||||||
|
}
|
||||||
|
|
||||||
|
splatIc = asmLink(asmCompile('glob', USE_ASM + I8x16 +
|
||||||
|
'var splat = i8x16.splat;' +
|
||||||
|
'function f() { var x = 100; return splat(x); } return f'), this);
|
||||||
|
assertEqVecArr(splatIc(), splat(100))
|
||||||
|
|
||||||
|
splatU = asmLink(asmCompile('glob', USE_ASM + U8x16 + I8x16 + I8x16U8x16 +
|
||||||
|
'var splat = u8x16.splat;' +
|
||||||
|
'function f(x) { x = x|0; return i8x16u8x16(splat(x)); } return f'), this);
|
||||||
|
for (let x of [0, 1, -1, 0x1234, 0x12, 1000, -1000000]) {
|
||||||
|
assertEqVecArr(SIMD.Uint8x16.fromInt8x16Bits(splatI(x)), splat(x << 24 >>> 24));
|
||||||
|
}
|
||||||
|
|
||||||
|
splatUc = asmLink(asmCompile('glob', USE_ASM + U8x16 + I8x16 + I8x16U8x16 +
|
||||||
|
'var splat = u8x16.splat;' +
|
||||||
|
'function f() { var x = 200; return i8x16u8x16(splat(x)); } return f'), this);
|
||||||
|
assertEqVecArr(SIMD.Uint8x16.fromInt8x16Bits(splatUc()), splat(200))
|
||||||
|
|
||||||
|
|
||||||
|
// Test extractLane.
|
||||||
|
//
|
||||||
|
// The lane index must be a literal int, and we generate different code for
|
||||||
|
// different lanes.
|
||||||
|
function extractI(a, i) {
|
||||||
|
return asmLink(asmCompile('glob', USE_ASM + I8x16 + I8x16EXT +
|
||||||
|
`function f() {var x=i8x16(${a.join(',')}); return i8x16ext(x, ${i})|0; } return f`), this)();
|
||||||
|
}
|
||||||
|
a = [-1,2,-3,4,-5,6,-7,8,-9,10,-11,12,-13,-14,-15,-16];
|
||||||
|
for (var i = 0; i < 16; i++)
|
||||||
|
assertEq(extractI(a, i), a[i]);
|
||||||
|
a = a.map(x => -x);
|
||||||
|
for (var i = 0; i < 16; i++)
|
||||||
|
assertEq(extractI(a, i), a[i]);
|
||||||
|
|
||||||
|
function extractU(a, i) {
|
||||||
|
return asmLink(asmCompile('glob', USE_ASM + U8x16 + U8x16EXT +
|
||||||
|
`function f() {var x=u8x16(${a.join(',')}); return u8x16ext(x, ${i})|0; } return f`), this)();
|
||||||
|
}
|
||||||
|
a = [1,255,12,13,14,150,200,3,4,5,6,7,8,9,10,16];
|
||||||
|
for (var i = 0; i < 16; i++)
|
||||||
|
assertEq(extractU(a, i), a[i]);
|
||||||
|
a = a.map(x => 255-x);
|
||||||
|
for (var i = 0; i < 16; i++)
|
||||||
|
assertEq(extractU(a, i), a[i]);
|
||||||
|
|
||||||
|
function extractB(a, i) {
|
||||||
|
return asmLink(asmCompile('glob', USE_ASM + B8x16 + B8x16EXT +
|
||||||
|
`function f() {var x=b8x16(${a.join(',')}); return b8x16ext(x, ${i})|0; } return f`), this)();
|
||||||
|
}
|
||||||
|
a = [1,1,0,1,1,0,0,0,1,1,1,1,0,0,0,1];
|
||||||
|
for (var i = 0; i < 16; i++)
|
||||||
|
assertEq(extractB(a, i), a[i]);
|
||||||
|
a = a.map(x => 1-x);
|
||||||
|
for (var i = 0; i < 16; i++)
|
||||||
|
assertEq(extractB(a, i), a[i]);
|
||||||
|
|
||||||
|
// Test replaceLane.
|
||||||
|
function replaceI(a, i) {
|
||||||
|
return asmLink(asmCompile('glob', USE_ASM + I8x16 + I8x16REP +
|
||||||
|
`function f(v) {v=v|0; var x=i8x16(${a.join(',')}); return i8x16rep(x,${i},v); } return f`), this);
|
||||||
|
}
|
||||||
|
a = [-1,2,-3,4,-5,6,-7,8,-9,10,-11,12,-13,-14,-15,-16];
|
||||||
|
for (var i = 0; i < 16; i++) {
|
||||||
|
var f = replaceI(a, i);
|
||||||
|
var b = a.slice(0);
|
||||||
|
b[i] = -20;
|
||||||
|
assertEqVecArr(f(-20), b);
|
||||||
|
}
|
||||||
|
|
||||||
|
function replaceU(a, i) {
|
||||||
|
return asmLink(asmCompile('glob', USE_ASM + U8x16 + U8x16REP + I8x16 + I8x16U8x16 +
|
||||||
|
`function f(v) {v=v|0; var x=u8x16(${a.join(',')}); x=u8x16rep(x,${i},v); return i8x16u8x16(x); } return f`), this);
|
||||||
|
}
|
||||||
|
a = [256-1,2,256-3,4,256-5,6,256-7,8,256-9,10,256-11,12,256-13,256-14,256-15,256-16];
|
||||||
|
for (var i = 0; i < 16; i++) {
|
||||||
|
// Result returned as Int8x16, convert back.
|
||||||
|
var rawf = replaceU(a, i);
|
||||||
|
var f = x => SIMD.Uint8x16.fromInt8x16Bits(rawf(x));
|
||||||
|
var b = a.slice(0);
|
||||||
|
b[i] = 100;
|
||||||
|
assertEqVecArr(f(100), b);
|
||||||
|
}
|
||||||
|
|
||||||
|
function replaceB(a, i) {
|
||||||
|
return asmLink(asmCompile('glob', USE_ASM + B8x16 + B8x16REP +
|
||||||
|
`function f(v) {v=v|0; var x=b8x16(${a.join(',')}); return b8x16rep(x,${i},v); } return f`), this);
|
||||||
|
}
|
||||||
|
a = [1,1,0,1,1,0,0,0,1,1,1,1,0,0,0,1];
|
||||||
|
for (var i = 0; i < 16; i++) {
|
||||||
|
var f = replaceB(a, i);
|
||||||
|
var b = a.slice(0);
|
||||||
|
v = 1 - a[i];
|
||||||
|
b[i] = v;
|
||||||
|
assertEqVecArr(f(v), b.map(x => !!x));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// Test select.
|
||||||
|
selectI = asmLink(asmCompile('glob', USE_ASM + I8x16 + B8x16 + B8x16CHK +
|
||||||
|
'var select = i8x16.select;' +
|
||||||
|
'var a = i8x16(-1,2,-3,4,-5, 6,-7, 8,-9,10,-11,12,-13,-14,-15,-16);' +
|
||||||
|
'var b = i8x16( 5,6, 7,8, 9,10,11,12,13,14, 15,16,-77, 45, 32, 0);' +
|
||||||
|
'function f(x) { x = b8x16chk(x); return select(x, a, b); } return f'), this);
|
||||||
|
assertEqVecArr(selectI(SIMD.Bool8x16( 0,0, 1,0, 1,1, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1)),
|
||||||
|
[ 5,6,-3,8,-5,6,-7,12,-9,10,15,16,-13,-14,32,-16]);
|
||||||
|
|
||||||
|
selectU = asmLink(asmCompile('glob', USE_ASM + I8x16 + B8x16 + B8x16CHK + U8x16 + I8x16U8x16 + U8x16I8x16 +
|
||||||
|
'var select = u8x16.select;' +
|
||||||
|
'var a = i8x16(-1,2,-3,4,-5, 6,-7, 8,-9,10,-11,12,-13,-14,-15,-16);' +
|
||||||
|
'var b = i8x16( 5,6, 7,8, 9,10,11,12,13,14, 15,16,-77, 45, 32, 0);' +
|
||||||
|
'function f(x) { x = b8x16chk(x); return i8x16u8x16(select(x, u8x16i8x16(a), u8x16i8x16(b))); } return f'), this);
|
||||||
|
assertEqVecArr(selectU(SIMD.Bool8x16( 0,0, 1,0, 1,1, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1)),
|
||||||
|
[ 5,6,-3,8,-5,6,-7,12,-9,10,15,16,-13,-14,32,-16]);
|
||||||
|
|
||||||
|
|
||||||
|
// Test swizzle.
|
||||||
|
function swizzle(vec, lanes) {
|
||||||
|
let r = [];
|
||||||
|
for (let i = 0; i < 16; i++)
|
||||||
|
r.push(vec[lanes[i]]);
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
function swizzleI(lanes) {
|
||||||
|
let asm = asmLink(asmCompile('glob', USE_ASM + I8x16 + I8x16CHK +
|
||||||
|
'var swz = i8x16.swizzle;' +
|
||||||
|
`function f(a) { a = i8x16chk(a); return swz(a, ${lanes.join()}); } return f`), this);
|
||||||
|
let a1 = [ -1,2, -3,-128,0x7f,6,-7, 8,-9, 10,-11, 12,-13,-14,-15, -16];
|
||||||
|
let a2 = [-128,2,-128,0x7f, 0,0, 8,-9,10,-11, 12,-13,-14,-15,-16, -1];
|
||||||
|
let v1 = SIMD.Int8x16(...a1);
|
||||||
|
let v2 = SIMD.Int8x16(...a2);
|
||||||
|
assertEqVecArr(asm(v1), swizzle(a1, lanes));
|
||||||
|
assertEqVecArr(asm(v2), swizzle(a2, lanes));
|
||||||
|
}
|
||||||
|
|
||||||
|
swizzleI([10, 1, 7, 5, 1, 2, 6, 8, 5, 13, 0, 6, 2, 8, 0, 9]);
|
||||||
|
swizzleI([ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
|
||||||
|
swizzleI([15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15]);
|
||||||
|
|
||||||
|
function swizzleU(lanes) {
|
||||||
|
let asm = asmLink(asmCompile('glob', USE_ASM + I8x16 + I8x16CHK + U8x16 + U8x16I8x16 + I8x16U8x16 +
|
||||||
|
'var swz = u8x16.swizzle;' +
|
||||||
|
`function f(a) { a = i8x16chk(a); return i8x16u8x16(swz(u8x16i8x16(a), ${lanes.join()})); } return f`), this);
|
||||||
|
let a1 = [ -1,2, -3,-128,0x7f,6,-7, 8,-9, 10,-11, 12,-13,-14,-15, -16];
|
||||||
|
let a2 = [-128,2,-128,0x7f, 0,0, 8,-9,10,-11, 12,-13,-14,-15,-16, -1];
|
||||||
|
let v1 = SIMD.Int8x16(...a1);
|
||||||
|
let v2 = SIMD.Int8x16(...a2);
|
||||||
|
assertEqVecArr(asm(v1), swizzle(a1, lanes));
|
||||||
|
assertEqVecArr(asm(v2), swizzle(a2, lanes));
|
||||||
|
}
|
||||||
|
|
||||||
|
swizzleU([10, 1, 7, 5, 1, 2, 6, 8, 5, 13, 0, 6, 2, 8, 0, 9]);
|
||||||
|
swizzleU([ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
|
||||||
|
swizzleU([15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15]);
|
||||||
|
|
||||||
|
// Out-of-range lane indexes.
|
||||||
|
assertAsmTypeFail('glob', USE_ASM + I8x16 + 'var swz = i8x16.swizzle; ' +
|
||||||
|
'function f() { var x=i8x16(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0); swz(x,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16); } return f');
|
||||||
|
assertAsmTypeFail('glob', USE_ASM + U8x16 + 'var swz = u8x16.swizzle; ' +
|
||||||
|
'function f() { var x=u8x16(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0); swz(x,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16); } return f');
|
||||||
|
// Missing lane indexes.
|
||||||
|
assertAsmTypeFail('glob', USE_ASM + I8x16 + 'var swz = i8x16.swizzle; ' +
|
||||||
|
'function f() { var x=i8x16(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0); swz(x,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15); } return f');
|
||||||
|
assertAsmTypeFail('glob', USE_ASM + U8x16 + 'var swz = u8x16.swizzle; ' +
|
||||||
|
'function f() { var x=u8x16(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0); swz(x,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15); } return f');
|
||||||
|
|
||||||
|
|
||||||
|
// Test shuffle.
|
||||||
|
function shuffle(vec1, vec2, lanes) {
|
||||||
|
let r = [];
|
||||||
|
let vec = vec1.concat(vec2);
|
||||||
|
for (let i = 0; i < 16; i++)
|
||||||
|
r.push(vec[lanes[i]]);
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
function shuffleI(lanes) {
|
||||||
|
let asm = asmLink(asmCompile('glob', USE_ASM + I8x16 + I8x16CHK +
|
||||||
|
'var shuf = i8x16.shuffle;' +
|
||||||
|
`function f(a1, a2) { a1 = i8x16chk(a1); a2 = i8x16chk(a2); return shuf(a1, a2, ${lanes.join()}); } return f`), this);
|
||||||
|
let a1 = [ -1,2, -3,-128,0x7f,6,-7, 8,-9, 10,-11, 12,-13,-14,-15, -16];
|
||||||
|
let a2 = [-128,2,-128,0x7f, 0,0, 8,-9,10,-11, 12,-13,-14,-15,-16, -1];
|
||||||
|
let v1 = SIMD.Int8x16(...a1);
|
||||||
|
let v2 = SIMD.Int8x16(...a2);
|
||||||
|
assertEqVecArr(asm(v1, v2), shuffle(a1, a2, lanes));
|
||||||
|
}
|
||||||
|
|
||||||
|
shuffleI([31, 9, 5, 4, 29, 12, 19, 10, 16, 22, 10, 9, 6, 18, 9, 8]);
|
||||||
|
shuffleI([ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
|
||||||
|
shuffleI([31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31]);
|
||||||
|
|
||||||
|
function shuffleU(lanes) {
|
||||||
|
let asm = asmLink(asmCompile('glob', USE_ASM + I8x16 + I8x16CHK + U8x16 + U8x16I8x16 + I8x16U8x16 +
|
||||||
|
'var shuf = u8x16.shuffle;' +
|
||||||
|
'function f(a1, a2) { a1 = i8x16chk(a1); a2 = i8x16chk(a2); ' +
|
||||||
|
`return i8x16u8x16(shuf(u8x16i8x16(a1), u8x16i8x16(a2), ${lanes.join()})); } return f`), this);
|
||||||
|
let a1 = [ -1,2, -3,-128,0x7f,6,-7, 8,-9, 10,-11, 12,-13,-14,-15, -16];
|
||||||
|
let a2 = [-128,2,-128,0x7f, 0,0, 8,-9,10,-11, 12,-13,-14,-15,-16, -1];
|
||||||
|
let v1 = SIMD.Int8x16(...a1);
|
||||||
|
let v2 = SIMD.Int8x16(...a2);
|
||||||
|
assertEqVecArr(asm(v1, v2), shuffle(a1, a2, lanes));
|
||||||
|
}
|
||||||
|
|
||||||
|
shuffleU([31, 9, 5, 4, 29, 12, 19, 10, 16, 22, 10, 9, 6, 18, 9, 8]);
|
||||||
|
shuffleU([ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
|
||||||
|
shuffleU([31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31]);
|
||||||
|
|
||||||
|
|
||||||
|
// Out-of-range lane indexes.
|
||||||
|
assertAsmTypeFail('glob', USE_ASM + I8x16 + 'var shuf = i8x16.shuffle; ' +
|
||||||
|
'function f() { var x=i8x16(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0); shuf(x,x,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,32); } return f');
|
||||||
|
assertAsmTypeFail('glob', USE_ASM + U8x16 + 'var shuf = u8x16.shuffle; ' +
|
||||||
|
'function f() { var x=u8x16(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0); shuf(x,x,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,32); } return f');
|
||||||
|
// Missing lane indexes.
|
||||||
|
assertAsmTypeFail('glob', USE_ASM + I8x16 + 'var shuf = i8x16.shuffle; ' +
|
||||||
|
'function f() { var x=i8x16(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0); shuf(x,x,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15); } return f');
|
||||||
|
assertAsmTypeFail('glob', USE_ASM + U8x16 + 'var shuf = u8x16.shuffle; ' +
|
||||||
|
'function f() { var x=u8x16(0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0); shuf(x,x,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15); } return f');
|
||||||
|
|
||||||
|
|
||||||
|
// Test unary operators.
|
||||||
|
function unaryI(opname, lanefunc) {
|
||||||
|
let simdfunc = asmLink(asmCompile('glob', USE_ASM + I8x16 + I8x16CHK +
|
||||||
|
`var fut = i8x16.${opname};` +
|
||||||
|
'function f(v) { v = i8x16chk(v); return fut(v); } return f'), this);
|
||||||
|
let a = [-1,2,-3,4,-5,6,-7,8,-9,10,-11,12,-13,-14,-15,-16];
|
||||||
|
let v = SIMD.Int8x16(...a);
|
||||||
|
assertEqVecArr(simdfunc(v), a.map(lanefunc));
|
||||||
|
}
|
||||||
|
|
||||||
|
function unaryU(opname, lanefunc) {
|
||||||
|
let simdfunc = asmLink(asmCompile('glob', USE_ASM + U8x16 + I8x16 + I8x16CHK + U8x16I8x16 + I8x16U8x16 +
|
||||||
|
`var fut = u8x16.${opname};` +
|
||||||
|
'function f(v) { v = i8x16chk(v); return i8x16u8x16(fut(u8x16i8x16(v))); } return f'), this);
|
||||||
|
let a = [256-1,2,256-3,4,256-5,6,256-7,8,256-9,10,256-11,12,256-13,256-14,256-15,256-16];
|
||||||
|
let v = SIMD.Int8x16(...a);
|
||||||
|
assertEqVecArr(SIMD.Uint8x16.fromInt8x16Bits(simdfunc(v)), a.map(lanefunc));
|
||||||
|
}
|
||||||
|
|
||||||
|
function unaryB(opname, lanefunc) {
|
||||||
|
let simdfunc = asmLink(asmCompile('glob', USE_ASM + B8x16 + B8x16CHK +
|
||||||
|
`var fut = b8x16.${opname};` +
|
||||||
|
'function f(v) { v = b8x16chk(v); return fut(v); } return f'), this);
|
||||||
|
let a = [1,1,0,1,1,0,0,0,1,1,1,1,0,0,0,1];
|
||||||
|
let v = SIMD.Bool8x16(...a);
|
||||||
|
assertEqVecArr(simdfunc(v), a.map(lanefunc));
|
||||||
|
}
|
||||||
|
|
||||||
|
unaryI('not', x => ~x << 24 >> 24);
|
||||||
|
unaryU('not', x => ~x << 24 >>> 24);
|
||||||
|
unaryB('not', x => !x);
|
||||||
|
unaryI('neg', x => -x << 24 >> 24);
|
||||||
|
unaryU('neg', x => -x << 24 >>> 24);
|
||||||
|
|
||||||
|
|
||||||
|
// Test binary operators.
|
||||||
|
function zipmap(a1, a2, f) {
|
||||||
|
assertEq(a1.length, a2.length);
|
||||||
|
let r = [];
|
||||||
|
for (var i = 0; i < a1.length; i++)
|
||||||
|
r.push(f(a1[i], a2[i]));
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
function binaryI(opname, lanefunc) {
|
||||||
|
let simdfunc = asmLink(asmCompile('glob', USE_ASM + I8x16 + I8x16CHK +
|
||||||
|
`var fut = i8x16.${opname};` +
|
||||||
|
'function f(v1, v2) { v1 = i8x16chk(v1); v2 = i8x16chk(v2); return fut(v1, v2); } return f'), this);
|
||||||
|
let a1 = [ -1,2, -3,-128,0x7f,6,-7, 8,-9, 10,-11, 12,-13,-14,-15, -16];
|
||||||
|
let a2 = [-128,2,-128,0x7f, 0,0, 8,-9,10,-11, 12,-13,-14,-15,-16, -1];
|
||||||
|
let ref = zipmap(a1, a2, lanefunc);
|
||||||
|
let v1 = SIMD.Int8x16(...a1);
|
||||||
|
let v2 = SIMD.Int8x16(...a2);
|
||||||
|
assertEqVecArr(simdfunc(v1, v2), ref);
|
||||||
|
}
|
||||||
|
|
||||||
|
function binaryU(opname, lanefunc) {
|
||||||
|
let simdfunc = asmLink(asmCompile('glob', USE_ASM + U8x16 + I8x16 + I8x16CHK + U8x16I8x16 + I8x16U8x16 +
|
||||||
|
`var fut = u8x16.${opname};` +
|
||||||
|
'function f(v1, v2) { v1 = i8x16chk(v1); v2 = i8x16chk(v2); return i8x16u8x16(fut(u8x16i8x16(v1), u8x16i8x16(v2))); } return f'), this);
|
||||||
|
let a1 = [ -1,2, -3,0x80,0x7f,6,-7, 8,-9, 10,-11, 12,-13,-14,-15, -16].map(x => x & 0xff);
|
||||||
|
let a2 = [0x80,2,0x80,0x7f, 0,0, 8,-9,10,-11, 12,-13,-14,-15,-16,0xff].map(x => x & 0xff);
|
||||||
|
let ref = zipmap(a1, a2, lanefunc);
|
||||||
|
let v1 = SIMD.Int8x16(...a1);
|
||||||
|
let v2 = SIMD.Int8x16(...a2);
|
||||||
|
let res = SIMD.Uint8x16.fromInt8x16Bits(simdfunc(v1, v2));
|
||||||
|
assertEqVecArr(res, ref);
|
||||||
|
}
|
||||||
|
|
||||||
|
function binaryB(opname, lanefunc) {
|
||||||
|
let simdfunc = asmLink(asmCompile('glob', USE_ASM + B8x16 + B8x16CHK +
|
||||||
|
`var fut = b8x16.${opname};` +
|
||||||
|
'function f(v1, v2) { v1 = b8x16chk(v1); v2 = b8x16chk(v2); return fut(v1, v2); } return f'), this);
|
||||||
|
let a = [1,1,0,1,1,0,0,0,1,1,1,1,0,0,0,1];
|
||||||
|
let v = SIMD.Bool8x16(...a);
|
||||||
|
assertEqVecArr(simdfunc(v), a.map(lanefunc));
|
||||||
|
}
|
||||||
|
|
||||||
|
binaryI('add', (x, y) => (x + y) << 24 >> 24);
|
||||||
|
binaryI('sub', (x, y) => (x - y) << 24 >> 24);
|
||||||
|
binaryI('mul', (x, y) => (x * y) << 24 >> 24);
|
||||||
|
binaryU('add', (x, y) => (x + y) << 24 >>> 24);
|
||||||
|
binaryU('sub', (x, y) => (x - y) << 24 >>> 24);
|
||||||
|
binaryU('mul', (x, y) => (x * y) << 24 >>> 24);
|
||||||
|
|
||||||
|
binaryI('and', (x, y) => (x & y) << 24 >> 24);
|
||||||
|
binaryI('or', (x, y) => (x | y) << 24 >> 24);
|
||||||
|
binaryI('xor', (x, y) => (x ^ y) << 24 >> 24);
|
||||||
|
binaryU('and', (x, y) => (x & y) << 24 >>> 24);
|
||||||
|
binaryU('or', (x, y) => (x | y) << 24 >>> 24);
|
||||||
|
binaryU('xor', (x, y) => (x ^ y) << 24 >>> 24);
|
||||||
|
|
||||||
|
function sat(x, lo, hi) {
|
||||||
|
if (x < lo) return lo;
|
||||||
|
if (x > hi) return hi;
|
||||||
|
return x
|
||||||
|
}
|
||||||
|
function isat(x) { return sat(x, -128, 127); }
|
||||||
|
function usat(x) { return sat(x, 0, 255); }
|
||||||
|
|
||||||
|
binaryI('addSaturate', (x, y) => isat(x + y))
|
||||||
|
binaryI('subSaturate', (x, y) => isat(x - y))
|
||||||
|
binaryU('addSaturate', (x, y) => usat(x + y))
|
||||||
|
binaryU('subSaturate', (x, y) => usat(x - y))
|
||||||
|
|
||||||
|
// Test shift operators.
|
||||||
|
function zip1map(a, s, f) {
|
||||||
|
return a.map(x => f(x, s));
|
||||||
|
}
|
||||||
|
|
||||||
|
function shiftI(opname, lanefunc) {
|
||||||
|
let simdfunc = asmLink(asmCompile('glob', USE_ASM + I8x16 + I8x16CHK +
|
||||||
|
`var fut = i8x16.${opname};` +
|
||||||
|
'function f(v, s) { v = i8x16chk(v); s = s|0; return fut(v, s); } return f'), this);
|
||||||
|
let a = [0x80,2,0x80,0x7f, 0,0, 8,-9,10,-11, 12,-13,-14,-15,-16,0xff];
|
||||||
|
let v = SIMD.Int8x16(...a);
|
||||||
|
for (let s of [0, 1, 2, 6, 7, 8, 9, 10, 16, 255, -1, -8, -7, -1000]) {
|
||||||
|
let ref = zip1map(a, s, lanefunc);
|
||||||
|
// 1. Test dynamic shift amount.
|
||||||
|
assertEqVecArr(simdfunc(v, s), ref);
|
||||||
|
|
||||||
|
// 2. Test constant shift amount.
|
||||||
|
let cstf = asmLink(asmCompile('glob', USE_ASM + I8x16 + I8x16CHK +
|
||||||
|
`var fut = i8x16.${opname};` +
|
||||||
|
`function f(v) { v = i8x16chk(v); return fut(v, ${s}); } return f`), this);
|
||||||
|
assertEqVecArr(cstf(v, s), ref);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function shiftU(opname, lanefunc) {
|
||||||
|
let simdfunc = asmLink(asmCompile('glob', USE_ASM + U8x16 + I8x16 + I8x16CHK + U8x16I8x16 + I8x16U8x16 +
|
||||||
|
`var fut = u8x16.${opname};` +
|
||||||
|
'function f(v, s) { v = i8x16chk(v); s = s|0; return i8x16u8x16(fut(u8x16i8x16(v), s)); } return f'), this);
|
||||||
|
let a = [0x80,2,0x80,0x7f, 0,0, 8,-9,10,-11, 12,-13,-14,-15,-16,0xff];
|
||||||
|
let v = SIMD.Int8x16(...a);
|
||||||
|
for (let s of [0, 1, 2, 6, 7, 8, 9, 10, 16, 255, -1, -8, -7, -1000]) {
|
||||||
|
let ref = zip1map(a, s, lanefunc);
|
||||||
|
// 1. Test dynamic shift amount.
|
||||||
|
assertEqVecArr(SIMD.Uint8x16.fromInt8x16Bits(simdfunc(v, s)), ref);
|
||||||
|
|
||||||
|
// 2. Test constant shift amount.
|
||||||
|
let cstf = asmLink(asmCompile('glob', USE_ASM + U8x16 + I8x16 + I8x16CHK + U8x16I8x16 + I8x16U8x16 +
|
||||||
|
`var fut = u8x16.${opname};` +
|
||||||
|
`function f(v) { v = i8x16chk(v); return i8x16u8x16(fut(u8x16i8x16(v), ${s})); } return f`), this);
|
||||||
|
assertEqVecArr(SIMD.Uint8x16.fromInt8x16Bits(cstf(v, s)), ref);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
shiftI('shiftLeftByScalar', (x,s) => (x << (s & 7)) << 24 >> 24);
|
||||||
|
shiftU('shiftLeftByScalar', (x,s) => (x << (s & 7)) << 24 >>> 24);
|
||||||
|
shiftI('shiftRightByScalar', (x,s) => ((x << 24 >> 24) >> (s & 7)) << 24 >> 24);
|
||||||
|
shiftU('shiftRightByScalar', (x,s) => ((x << 24 >>> 24) >>> (s & 7)) << 24 >>> 24);
|
||||||
|
|
||||||
|
|
||||||
|
// Comparisons.
|
||||||
|
function compareI(opname, lanefunc) {
|
||||||
|
let simdfunc = asmLink(asmCompile('glob', USE_ASM + I8x16 + I8x16CHK +
|
||||||
|
`var fut = i8x16.${opname};` +
|
||||||
|
'function f(v1, v2) { v1 = i8x16chk(v1); v2 = i8x16chk(v2); return fut(v1, v2); } return f'), this);
|
||||||
|
let a1 = [ -1,2, -3,-128,0x7f,6,-7, 8,-9, 10,-11, 12,-13,-14,-15, -16];
|
||||||
|
let a2 = [-128,2,-128,0x7f, 0,0, 8,-9,10,-11, 12,-13,-14,-15,-16, -1];
|
||||||
|
let ref = zipmap(a1, a2, lanefunc);
|
||||||
|
let v1 = SIMD.Int8x16(...a1);
|
||||||
|
let v2 = SIMD.Int8x16(...a2);
|
||||||
|
assertEqVecArr(simdfunc(v1, v2), ref);
|
||||||
|
}
|
||||||
|
|
||||||
|
function compareU(opname, lanefunc) {
|
||||||
|
let simdfunc = asmLink(asmCompile('glob', USE_ASM + I8x16 + I8x16CHK + U8x16 + U8x16I8x16 +
|
||||||
|
`var fut = u8x16.${opname};` +
|
||||||
|
'function f(v1, v2) { v1 = i8x16chk(v1); v2 = i8x16chk(v2); return fut(u8x16i8x16(v1), u8x16i8x16(v2)); } return f'), this);
|
||||||
|
let a1 = [ -1,2, -3,-128,0x7f,6,-7, 8,-9, 10,-11, 12,-13,-14,-15, -16].map(x => x << 24 >>> 24);
|
||||||
|
let a2 = [-128,2,-128,0x7f, 0,0, 8,-9,10,-11, 12,-13,-14,-15,-16, -1].map(x => x << 24 >>> 24);
|
||||||
|
let ref = zipmap(a1, a2, lanefunc);
|
||||||
|
let v1 = SIMD.Int8x16(...a1);
|
||||||
|
let v2 = SIMD.Int8x16(...a2);
|
||||||
|
assertEqVecArr(simdfunc(v1, v2), ref);
|
||||||
|
}
|
||||||
|
|
||||||
|
compareI("equal", (x,y) => x == y);
|
||||||
|
compareU("equal", (x,y) => x == y);
|
||||||
|
compareI("notEqual", (x,y) => x != y);
|
||||||
|
compareU("notEqual", (x,y) => x != y);
|
||||||
|
compareI("lessThan", (x,y) => x < y);
|
||||||
|
compareU("lessThan", (x,y) => x < y);
|
||||||
|
compareI("lessThanOrEqual", (x,y) => x <= y);
|
||||||
|
compareU("lessThanOrEqual", (x,y) => x <= y);
|
||||||
|
compareI("greaterThan", (x,y) => x > y);
|
||||||
|
compareU("greaterThan", (x,y) => x > y);
|
||||||
|
compareI("greaterThanOrEqual", (x,y) => x >= y);
|
||||||
|
compareU("greaterThanOrEqual", (x,y) => x >= y);
|
|
@ -0,0 +1,84 @@
|
||||||
|
load(libdir + "asm.js");
|
||||||
|
load(libdir + "simd.js");
|
||||||
|
load(libdir + "asserts.js");
|
||||||
|
|
||||||
|
// Set to true to see more JS debugging spew.
|
||||||
|
const DEBUG = false;
|
||||||
|
|
||||||
|
if (!isSimdAvailable()) {
|
||||||
|
DEBUG && print("won't run tests as simd extensions aren't activated yet");
|
||||||
|
quit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test all bit-casts and normal loads and stores.
|
||||||
|
var heap = new ArrayBuffer(BUF_MIN);
|
||||||
|
var asU8 = new Uint8Array(heap);
|
||||||
|
var allTypes = [
|
||||||
|
"Int8x16",
|
||||||
|
"Int16x8",
|
||||||
|
"Int32x4",
|
||||||
|
"Uint8x16",
|
||||||
|
"Uint16x8",
|
||||||
|
"Uint32x4",
|
||||||
|
"Float32x4"
|
||||||
|
];
|
||||||
|
|
||||||
|
// Generate a load bit-cast store test function that performs:
|
||||||
|
//
|
||||||
|
// function f(a, b) {
|
||||||
|
// vec = src.load(H, a);
|
||||||
|
// cast = dst.from«src»Bits(vec);
|
||||||
|
// store(H, b, cast);
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// Here, `H` is the heap provided by `heap`.
|
||||||
|
function test_func(src, dst) {
|
||||||
|
text = `
|
||||||
|
"use asm";
|
||||||
|
var src = glob.SIMD.${src};
|
||||||
|
var dst = glob.SIMD.${dst};
|
||||||
|
var ld = src.load;
|
||||||
|
var st = dst.store;
|
||||||
|
var bc = dst.from${src}Bits;
|
||||||
|
|
||||||
|
var H = new glob.Uint8Array(heap);
|
||||||
|
|
||||||
|
function f(a, b) {
|
||||||
|
a = a|0;
|
||||||
|
b = b|0;
|
||||||
|
|
||||||
|
st(H, b, bc(ld(H, a)));
|
||||||
|
}
|
||||||
|
|
||||||
|
return f;
|
||||||
|
`;
|
||||||
|
return asmLink(asmCompile('glob', 'ffi', 'heap', text), this, null, heap);
|
||||||
|
}
|
||||||
|
|
||||||
|
function assertBuf16(a, b) {
|
||||||
|
for (let i=0; i < 16; i++) {
|
||||||
|
assertEq(asU8[a+i], asU8[b+i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (let src of allTypes) {
|
||||||
|
for (let dst of allTypes) {
|
||||||
|
// Skip identity conversions.
|
||||||
|
if (src == dst) continue;
|
||||||
|
|
||||||
|
print(src, dst);
|
||||||
|
let f = test_func(src, dst);
|
||||||
|
// Initialize with pseudo-random data.
|
||||||
|
for (let i = 0; i < 64; i++) {
|
||||||
|
asU8[i] = (i + 17) * 97;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Aligned load/store.
|
||||||
|
f(0, 16);
|
||||||
|
assertBuf16(0, 16);
|
||||||
|
|
||||||
|
// Unaligned access.
|
||||||
|
f(1, 27);
|
||||||
|
assertBuf16(1, 27);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,457 @@
|
||||||
|
// |jit-test|
|
||||||
|
load(libdir + "asm.js");
|
||||||
|
load(libdir + "simd.js");
|
||||||
|
load(libdir + "asserts.js");
|
||||||
|
|
||||||
|
// Avoid pathological --ion-eager compile times due to bails in loops
|
||||||
|
setJitCompilerOption('ion.warmup.trigger', 1000000);
|
||||||
|
|
||||||
|
// Set to true to see more JS debugging spew
|
||||||
|
const DEBUG = false;
|
||||||
|
|
||||||
|
if (!isSimdAvailable() || typeof SIMD === 'undefined' || !isAsmJSCompilationAvailable()) {
|
||||||
|
DEBUG && print("won't run tests as simd extensions aren't activated yet");
|
||||||
|
quit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
const RuntimeError = WebAssembly.RuntimeError;
|
||||||
|
|
||||||
|
const INT32_MAX = Math.pow(2, 31) - 1;
|
||||||
|
const INT32_MIN = INT32_MAX + 1 | 0;
|
||||||
|
|
||||||
|
try {
|
||||||
|
|
||||||
|
// Load / Store
|
||||||
|
var IMPORTS = USE_ASM + 'var H=new glob.Uint8Array(heap); var i4=glob.SIMD.Int32x4; var ci4=i4.check; var load=i4.load; var store=i4.store;';
|
||||||
|
|
||||||
|
// Bad number of args
|
||||||
|
assertAsmTypeFail('glob', 'ffi', 'heap', IMPORTS + "function f(){load();} return f");
|
||||||
|
assertAsmTypeFail('glob', 'ffi', 'heap', IMPORTS + "function f(){load(3);} return f");
|
||||||
|
assertAsmTypeFail('glob', 'ffi', 'heap', IMPORTS + "function f(){load(3, 4, 5);} return f");
|
||||||
|
|
||||||
|
// Bad type of args
|
||||||
|
assertAsmTypeFail('glob', 'ffi', 'heap', IMPORTS + "function f(){load(3, 5);} return f");
|
||||||
|
assertAsmTypeFail('glob', 'ffi', 'heap', IMPORTS + "function f(){load(H, 5.0);} return f");
|
||||||
|
assertAsmTypeFail('glob', 'ffi', 'heap', IMPORTS + "function f(){var i=0.;load(H, i);} return f");
|
||||||
|
assertAsmTypeFail('glob', 'ffi', 'heap', IMPORTS + "var H2=new glob.Int32Array(heap); function f(){var i=0;load(H2, i)} return f");
|
||||||
|
assertAsmTypeFail('glob', 'ffi', 'heap', IMPORTS + "var H2=42; function f(){var i=0;load(H2, i)} return f");
|
||||||
|
assertAsmTypeFail('glob', 'ffi', 'heap', IMPORTS + "function f(){var i=0;load(H2, i)} return f");
|
||||||
|
assertAsmTypeFail('glob', 'ffi', 'heap', IMPORTS + "var f4=glob.SIMD.Float32x4; function f(){var i=0;var vec=f4(1,2,3,4); store(H, i, vec)} return f");
|
||||||
|
|
||||||
|
// Bad coercions of returned values
|
||||||
|
assertAsmTypeFail('glob', 'ffi', 'heap', IMPORTS + "function f(){var i=0;return load(H, i)|0;} return f");
|
||||||
|
assertAsmTypeFail('glob', 'ffi', 'heap', IMPORTS + "function f(){var i=0;return +load(H, i);} return f");
|
||||||
|
|
||||||
|
// Literal index constants
|
||||||
|
var buf = new ArrayBuffer(BUF_MIN);
|
||||||
|
var SIZE_TA = BUF_MIN >> 2
|
||||||
|
var asI32 = new Int32Array(buf);
|
||||||
|
asI32[SIZE_TA - 4] = 4;
|
||||||
|
asI32[SIZE_TA - 3] = 3;
|
||||||
|
asI32[SIZE_TA - 2] = 2;
|
||||||
|
asI32[SIZE_TA - 1] = 1;
|
||||||
|
|
||||||
|
assertAsmTypeFail('glob', 'ffi', 'heap', IMPORTS + "function f(){load(H, -1);} return f");
|
||||||
|
assertAsmTypeFail('glob', 'ffi', 'heap', IMPORTS + "function f(){load(H, " + (INT32_MAX + 1) + ");} return f");
|
||||||
|
assertAsmTypeFail('glob', 'ffi', 'heap', IMPORTS + "function f(){load(H, " + (INT32_MAX + 1 - 15) + ");} return f");
|
||||||
|
asmCompile('glob', 'ffi', 'heap', IMPORTS + "function f(){load(H, " + (INT32_MAX + 1 - 16) + ");} return f");
|
||||||
|
|
||||||
|
assertAsmLinkFail(asmCompile('glob', 'ffi', 'heap', IMPORTS + "function f() {return ci4(load(H, " + (BUF_MIN - 15) + "));} return f"), this, {}, buf);
|
||||||
|
assertEqX4(asmLink(asmCompile('glob', 'ffi', 'heap', IMPORTS + "function f() {return ci4(load(H, " + (BUF_MIN - 16) + "));} return f"), this, {}, buf)(), [4, 3, 2, 1]);
|
||||||
|
assertEqX4(asmLink(asmCompile('glob', 'ffi', 'heap', IMPORTS + "function f() {return ci4(load(H, " + BUF_MIN + " - 16 | 0));} return f"), this, {}, buf)(), [4, 3, 2, 1]);
|
||||||
|
|
||||||
|
var CONSTANT_INDEX = 42;
|
||||||
|
var CONSTANT_BYTE_INDEX = CONSTANT_INDEX << 2;
|
||||||
|
|
||||||
|
var loadStoreCode = `
|
||||||
|
"use asm";
|
||||||
|
|
||||||
|
var H = new glob.Uint8Array(heap);
|
||||||
|
|
||||||
|
var i4 = glob.SIMD.Int32x4;
|
||||||
|
var i4load = i4.load;
|
||||||
|
var i4store = i4.store;
|
||||||
|
var ci4 = i4.check;
|
||||||
|
|
||||||
|
var f4 = glob.SIMD.Float32x4;
|
||||||
|
var f4load = f4.load;
|
||||||
|
var f4store = f4.store;
|
||||||
|
var cf4 = f4.check;
|
||||||
|
|
||||||
|
function f32l(i) { i=i|0; return cf4(f4load(H, i|0)); }
|
||||||
|
function f32lcst() { return cf4(f4load(H, ${CONSTANT_BYTE_INDEX})); }
|
||||||
|
function f32s(i, vec) { i=i|0; vec=cf4(vec); f4store(H, i|0, vec); }
|
||||||
|
function f32scst(vec) { vec=cf4(vec); f4store(H, ${CONSTANT_BYTE_INDEX}, vec); }
|
||||||
|
|
||||||
|
function i32l(i) { i=i|0; return ci4(i4load(H, i|0)); }
|
||||||
|
function i32lcst() { return ci4(i4load(H, ${CONSTANT_BYTE_INDEX})); }
|
||||||
|
function i32s(i, vec) { i=i|0; vec=ci4(vec); i4store(H, i|0, vec); }
|
||||||
|
function i32scst(vec) { vec=ci4(vec); i4store(H, ${CONSTANT_BYTE_INDEX}, vec); }
|
||||||
|
|
||||||
|
function f32lbndcheck(i) {
|
||||||
|
i=i|0;
|
||||||
|
if ((i|0) > ${CONSTANT_BYTE_INDEX}) i=${CONSTANT_BYTE_INDEX};
|
||||||
|
if ((i|0) < 0) i = 0;
|
||||||
|
return cf4(f4load(H, i|0));
|
||||||
|
}
|
||||||
|
function f32sbndcheck(i, vec) {
|
||||||
|
i=i|0;
|
||||||
|
vec=cf4(vec);
|
||||||
|
if ((i|0) > ${CONSTANT_BYTE_INDEX}) i=${CONSTANT_BYTE_INDEX};
|
||||||
|
if ((i|0) < 0) i = 0;
|
||||||
|
return cf4(f4store(H, i|0, vec));
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
f32l: f32l,
|
||||||
|
f32lcst: f32lcst,
|
||||||
|
f32s: f32s,
|
||||||
|
f32scst: f32scst,
|
||||||
|
f32lbndcheck: f32lbndcheck,
|
||||||
|
f32sbndcheck: f32sbndcheck,
|
||||||
|
i32l: i32l,
|
||||||
|
i32lcst: i32lcst,
|
||||||
|
i32s: i32s,
|
||||||
|
i32scst: i32scst
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
const SIZE = 0x8000;
|
||||||
|
|
||||||
|
var F32 = new Float32Array(SIZE);
|
||||||
|
var reset = function() {
|
||||||
|
for (var i = 0; i < SIZE; i++)
|
||||||
|
F32[i] = i + 1;
|
||||||
|
};
|
||||||
|
reset();
|
||||||
|
|
||||||
|
var buf = F32.buffer;
|
||||||
|
var m = asmLink(asmCompile('glob', 'ffi', 'heap', loadStoreCode), this, null, buf);
|
||||||
|
|
||||||
|
function slice(TA, i, n) { return Array.prototype.slice.call(TA, i, i + n); }
|
||||||
|
|
||||||
|
// Float32x4.load
|
||||||
|
function f32l(n) { return m.f32l((n|0) << 2 | 0); };
|
||||||
|
|
||||||
|
// Correct accesses
|
||||||
|
assertEqX4(f32l(0), slice(F32, 0, 4));
|
||||||
|
assertEqX4(f32l(1), slice(F32, 1, 4));
|
||||||
|
assertEqX4(f32l(SIZE - 4), slice(F32, SIZE - 4, 4));
|
||||||
|
|
||||||
|
assertEqX4(m.f32lcst(), slice(F32, CONSTANT_INDEX, 4));
|
||||||
|
assertEqX4(m.f32lbndcheck(CONSTANT_BYTE_INDEX), slice(F32, CONSTANT_INDEX, 4));
|
||||||
|
|
||||||
|
// OOB
|
||||||
|
assertThrowsInstanceOf(() => f32l(-1), RuntimeError);
|
||||||
|
assertThrowsInstanceOf(() => f32l(SIZE), RuntimeError);
|
||||||
|
assertThrowsInstanceOf(() => f32l(SIZE - 1), RuntimeError);
|
||||||
|
assertThrowsInstanceOf(() => f32l(SIZE - 2), RuntimeError);
|
||||||
|
assertThrowsInstanceOf(() => f32l(SIZE - 3), RuntimeError);
|
||||||
|
|
||||||
|
var code = `
|
||||||
|
"use asm";
|
||||||
|
var f4 = glob.SIMD.Float32x4;
|
||||||
|
var f4l = f4.load;
|
||||||
|
var u8 = new glob.Uint8Array(heap);
|
||||||
|
|
||||||
|
function g(x) {
|
||||||
|
x = x|0;
|
||||||
|
// set a constraint on the size of the heap
|
||||||
|
var ptr = 0;
|
||||||
|
ptr = u8[0xFFFF] | 0;
|
||||||
|
// give a precise range to x
|
||||||
|
x = (x>>0) > 5 ? 5 : x;
|
||||||
|
x = (x>>0) < 0 ? 0 : x;
|
||||||
|
// ptr value gets a precise range but the bounds check shouldn't get
|
||||||
|
// eliminated.
|
||||||
|
return f4l(u8, 0xFFFA + x | 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
return g;
|
||||||
|
`;
|
||||||
|
assertThrowsInstanceOf(() => asmLink(asmCompile('glob', 'ffi', 'heap', code), this, {}, new ArrayBuffer(0x10000))(0), RuntimeError);
|
||||||
|
|
||||||
|
// Float32x4.store
|
||||||
|
function f32s(n, v) { return m.f32s((n|0) << 2 | 0, v); };
|
||||||
|
|
||||||
|
var vec = SIMD.Float32x4(5,6,7,8);
|
||||||
|
var vec2 = SIMD.Float32x4(0,1,2,3);
|
||||||
|
var vecWithNaN = SIMD.Float32x4(NaN, 2, NaN, 4);
|
||||||
|
|
||||||
|
reset();
|
||||||
|
f32s(0, vec);
|
||||||
|
assertEqX4(vec, slice(F32, 0, 4));
|
||||||
|
|
||||||
|
reset();
|
||||||
|
f32s(0, vec2);
|
||||||
|
assertEqX4(vec2, slice(F32, 0, 4));
|
||||||
|
|
||||||
|
reset();
|
||||||
|
f32s(4, vec);
|
||||||
|
assertEqX4(vec, slice(F32, 4, 4));
|
||||||
|
|
||||||
|
reset();
|
||||||
|
f32s(4, vecWithNaN);
|
||||||
|
assertEqX4(vecWithNaN, slice(F32, 4, 4));
|
||||||
|
|
||||||
|
reset();
|
||||||
|
m.f32scst(vec2);
|
||||||
|
assertEqX4(vec2, slice(F32, CONSTANT_INDEX, 4));
|
||||||
|
|
||||||
|
reset();
|
||||||
|
m.f32sbndcheck(CONSTANT_BYTE_INDEX, vec);
|
||||||
|
assertEqX4(vec, slice(F32, CONSTANT_INDEX, 4));
|
||||||
|
|
||||||
|
// OOB
|
||||||
|
reset();
|
||||||
|
assertThrowsInstanceOf(() => f32s(SIZE - 3, vec), RuntimeError);
|
||||||
|
assertThrowsInstanceOf(() => f32s(SIZE - 2, vec), RuntimeError);
|
||||||
|
assertThrowsInstanceOf(() => f32s(SIZE - 1, vec), RuntimeError);
|
||||||
|
assertThrowsInstanceOf(() => f32s(SIZE, vec), RuntimeError);
|
||||||
|
for (var i = 0; i < SIZE; i++)
|
||||||
|
assertEq(F32[i], i + 1);
|
||||||
|
|
||||||
|
// Int32x4.load
|
||||||
|
var I32 = new Int32Array(buf);
|
||||||
|
reset = function () {
|
||||||
|
for (var i = 0; i < SIZE; i++)
|
||||||
|
I32[i] = i + 1;
|
||||||
|
};
|
||||||
|
reset();
|
||||||
|
|
||||||
|
function i32(n) { return m.i32l((n|0) << 2 | 0); };
|
||||||
|
|
||||||
|
// Correct accesses
|
||||||
|
assertEqX4(i32(0), slice(I32, 0, 4));
|
||||||
|
assertEqX4(i32(1), slice(I32, 1, 4));
|
||||||
|
assertEqX4(i32(SIZE - 4), slice(I32, SIZE - 4, 4));
|
||||||
|
|
||||||
|
assertEqX4(m.i32lcst(), slice(I32, CONSTANT_INDEX, 4));
|
||||||
|
|
||||||
|
// OOB
|
||||||
|
assertThrowsInstanceOf(() => i32(-1), RuntimeError);
|
||||||
|
assertThrowsInstanceOf(() => i32(SIZE), RuntimeError);
|
||||||
|
assertThrowsInstanceOf(() => i32(SIZE - 1), RuntimeError);
|
||||||
|
assertThrowsInstanceOf(() => i32(SIZE - 2), RuntimeError);
|
||||||
|
assertThrowsInstanceOf(() => i32(SIZE - 3), RuntimeError);
|
||||||
|
|
||||||
|
// Int32x4.store
|
||||||
|
function i32s(n, v) { return m.i32s((n|0) << 2 | 0, v); };
|
||||||
|
|
||||||
|
var vec = SIMD.Int32x4(5,6,7,8);
|
||||||
|
var vec2 = SIMD.Int32x4(0,1,2,3);
|
||||||
|
|
||||||
|
reset();
|
||||||
|
i32s(0, vec);
|
||||||
|
assertEqX4(vec, slice(I32, 0, 4));
|
||||||
|
|
||||||
|
reset();
|
||||||
|
i32s(0, vec2);
|
||||||
|
assertEqX4(vec2, slice(I32, 0, 4));
|
||||||
|
|
||||||
|
reset();
|
||||||
|
i32s(4, vec);
|
||||||
|
assertEqX4(vec, slice(I32, 4, 4));
|
||||||
|
|
||||||
|
reset();
|
||||||
|
m.i32scst(vec2);
|
||||||
|
assertEqX4(vec2, slice(I32, CONSTANT_INDEX, 4));
|
||||||
|
|
||||||
|
// OOB
|
||||||
|
reset();
|
||||||
|
assertThrowsInstanceOf(() => i32s(SIZE - 3, vec), RuntimeError);
|
||||||
|
assertThrowsInstanceOf(() => i32s(SIZE - 2, vec), RuntimeError);
|
||||||
|
assertThrowsInstanceOf(() => i32s(SIZE - 1, vec), RuntimeError);
|
||||||
|
assertThrowsInstanceOf(() => i32s(SIZE - 0, vec), RuntimeError);
|
||||||
|
for (var i = 0; i < SIZE; i++)
|
||||||
|
assertEq(I32[i], i + 1);
|
||||||
|
|
||||||
|
// Partial loads and stores
|
||||||
|
(function() {
|
||||||
|
|
||||||
|
// Variable indexes
|
||||||
|
function MakeCodeFor(typeName) {
|
||||||
|
return `
|
||||||
|
"use asm";
|
||||||
|
var type = glob.SIMD.${typeName};
|
||||||
|
var c = type.check;
|
||||||
|
|
||||||
|
var l1 = type.load1;
|
||||||
|
var l2 = type.load2;
|
||||||
|
|
||||||
|
var s1 = type.store1;
|
||||||
|
var s2 = type.store2;
|
||||||
|
|
||||||
|
var u8 = new glob.Uint8Array(heap);
|
||||||
|
|
||||||
|
function load1(i) { i=i|0; return l1(u8, i); }
|
||||||
|
function load2(i) { i=i|0; return l2(u8, i); }
|
||||||
|
|
||||||
|
function loadCst1() { return l1(u8, 41 << 2); }
|
||||||
|
function loadCst2() { return l2(u8, 41 << 2); }
|
||||||
|
|
||||||
|
function store1(i, x) { i=i|0; x=c(x); return s1(u8, i, x); }
|
||||||
|
function store2(i, x) { i=i|0; x=c(x); return s2(u8, i, x); }
|
||||||
|
|
||||||
|
function storeCst1(x) { x=c(x); return s1(u8, 41 << 2, x); }
|
||||||
|
function storeCst2(x) { x=c(x); return s2(u8, 41 << 2, x); }
|
||||||
|
|
||||||
|
return {
|
||||||
|
load1: load1,
|
||||||
|
load2: load2,
|
||||||
|
loadCst1: loadCst1,
|
||||||
|
loadCst2: loadCst2,
|
||||||
|
store1: store1,
|
||||||
|
store2: store2,
|
||||||
|
storeCst1: storeCst1,
|
||||||
|
storeCst2: storeCst2,
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
|
||||||
|
var SIZE = 0x10000;
|
||||||
|
|
||||||
|
function TestPartialLoads(m, typedArray, x, y, z, w) {
|
||||||
|
// Fill array with predictable values
|
||||||
|
for (var i = 0; i < SIZE; i += 4) {
|
||||||
|
typedArray[i] = x(i);
|
||||||
|
typedArray[i + 1] = y(i);
|
||||||
|
typedArray[i + 2] = z(i);
|
||||||
|
typedArray[i + 3] = w(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test correct loads
|
||||||
|
var i = 0, j = 0; // i in elems, j in bytes
|
||||||
|
assertEqX4(m.load1(j), [x(i), 0, 0, 0]);
|
||||||
|
assertEqX4(m.load2(j), [x(i), y(i), 0, 0]);
|
||||||
|
|
||||||
|
j += 4;
|
||||||
|
assertEqX4(m.load1(j), [y(i), 0, 0, 0]);
|
||||||
|
assertEqX4(m.load2(j), [y(i), z(i), 0, 0]);
|
||||||
|
|
||||||
|
j += 4;
|
||||||
|
assertEqX4(m.load1(j), [z(i), 0, 0, 0]);
|
||||||
|
assertEqX4(m.load2(j), [z(i), w(i), 0, 0]);
|
||||||
|
|
||||||
|
j += 4;
|
||||||
|
assertEqX4(m.load1(j), [w(i), 0, 0, 0]);
|
||||||
|
assertEqX4(m.load2(j), [w(i), x(i+4), 0, 0]);
|
||||||
|
|
||||||
|
j += 4;
|
||||||
|
i += 4;
|
||||||
|
assertEqX4(m.load1(j), [x(i), 0, 0, 0]);
|
||||||
|
assertEqX4(m.load2(j), [x(i), y(i), 0, 0]);
|
||||||
|
|
||||||
|
// Test loads with constant indexes (41)
|
||||||
|
assertEqX4(m.loadCst1(), [y(40), 0, 0, 0]);
|
||||||
|
assertEqX4(m.loadCst2(), [y(40), z(40), 0, 0]);
|
||||||
|
|
||||||
|
// Test limit and OOB accesses
|
||||||
|
assertEqX4(m.load1((SIZE - 1) << 2), [w(SIZE - 4), 0, 0, 0]);
|
||||||
|
assertThrowsInstanceOf(() => m.load1(((SIZE - 1) << 2) + 1), RuntimeError);
|
||||||
|
|
||||||
|
assertEqX4(m.load2((SIZE - 2) << 2), [z(SIZE - 4), w(SIZE - 4), 0, 0]);
|
||||||
|
assertThrowsInstanceOf(() => m.load2(((SIZE - 2) << 2) + 1), RuntimeError);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Partial stores
|
||||||
|
function TestPartialStores(m, typedArray, typeName, x, y, z, w) {
|
||||||
|
var val = SIMD[typeName](x, y, z, w);
|
||||||
|
|
||||||
|
function Reset() {
|
||||||
|
for (var i = 0; i < SIZE; i++)
|
||||||
|
typedArray[i] = i + 1;
|
||||||
|
}
|
||||||
|
function CheckNotModified(low, high) {
|
||||||
|
for (var i = low; i < high; i++)
|
||||||
|
assertEq(typedArray[i], i + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
function TestStore1(i) {
|
||||||
|
m.store1(i, val);
|
||||||
|
CheckNotModified(0, i >> 2);
|
||||||
|
assertEq(typedArray[i >> 2], x);
|
||||||
|
CheckNotModified((i >> 2) + 1, SIZE);
|
||||||
|
typedArray[i >> 2] = (i >> 2) + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
function TestStore2(i) {
|
||||||
|
m.store2(i, val);
|
||||||
|
CheckNotModified(0, i >> 2);
|
||||||
|
assertEq(typedArray[i >> 2], x);
|
||||||
|
assertEq(typedArray[(i >> 2) + 1], y);
|
||||||
|
CheckNotModified((i >> 2) + 2, SIZE);
|
||||||
|
typedArray[i >> 2] = (i >> 2) + 1;
|
||||||
|
typedArray[(i >> 2) + 1] = (i >> 2) + 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
function TestOOBStore(f) {
|
||||||
|
assertThrowsInstanceOf(f, RuntimeError);
|
||||||
|
CheckNotModified(0, SIZE);
|
||||||
|
}
|
||||||
|
|
||||||
|
Reset();
|
||||||
|
|
||||||
|
TestStore1(0);
|
||||||
|
TestStore1(1 << 2);
|
||||||
|
TestStore1(2 << 2);
|
||||||
|
TestStore1(3 << 2);
|
||||||
|
TestStore1(1337 << 2);
|
||||||
|
|
||||||
|
var i = (SIZE - 1) << 2;
|
||||||
|
TestStore1(i);
|
||||||
|
TestOOBStore(() => m.store1(i + 1, val));
|
||||||
|
TestOOBStore(() => m.store1(-1, val));
|
||||||
|
|
||||||
|
TestStore2(0);
|
||||||
|
TestStore2(1 << 2);
|
||||||
|
TestStore2(2 << 2);
|
||||||
|
TestStore2(3 << 2);
|
||||||
|
TestStore2(1337 << 2);
|
||||||
|
|
||||||
|
var i = (SIZE - 2) << 2;
|
||||||
|
TestStore2(i);
|
||||||
|
TestOOBStore(() => m.store2(i + 1, val));
|
||||||
|
TestOOBStore(() => m.store2(-1, val));
|
||||||
|
|
||||||
|
// Constant indexes (41)
|
||||||
|
m.storeCst1(val);
|
||||||
|
CheckNotModified(0, 41);
|
||||||
|
assertEq(typedArray[41], x);
|
||||||
|
CheckNotModified(42, SIZE);
|
||||||
|
typedArray[41] = 42;
|
||||||
|
|
||||||
|
m.storeCst2(val);
|
||||||
|
CheckNotModified(0, 41);
|
||||||
|
assertEq(typedArray[41], x);
|
||||||
|
assertEq(typedArray[42], y);
|
||||||
|
CheckNotModified(43, SIZE);
|
||||||
|
typedArray[41] = 42;
|
||||||
|
typedArray[42] = 43;
|
||||||
|
}
|
||||||
|
|
||||||
|
var f32 = new Float32Array(SIZE);
|
||||||
|
var mFloat32x4 = asmLink(asmCompile('glob', 'ffi', 'heap', MakeCodeFor('Float32x4')), this, null, f32.buffer);
|
||||||
|
|
||||||
|
TestPartialLoads(mFloat32x4, f32,
|
||||||
|
(i) => i + 1,
|
||||||
|
(i) => Math.fround(13.37),
|
||||||
|
(i) => Math.fround(1/i),
|
||||||
|
(i) => Math.fround(Math.sqrt(0x2000 - i)));
|
||||||
|
|
||||||
|
TestPartialStores(mFloat32x4, f32, 'Float32x4', 42, -0, NaN, 0.1337);
|
||||||
|
|
||||||
|
var i32 = new Int32Array(f32.buffer);
|
||||||
|
var mInt32x4 = asmLink(asmCompile('glob', 'ffi', 'heap', MakeCodeFor('Int32x4')), this, null, i32.buffer);
|
||||||
|
|
||||||
|
TestPartialLoads(mInt32x4, i32,
|
||||||
|
(i) => i + 1 | 0,
|
||||||
|
(i) => -i | 0,
|
||||||
|
(i) => i * 2 | 0,
|
||||||
|
(i) => 42);
|
||||||
|
|
||||||
|
TestPartialStores(mInt32x4, i32, 'Int32x4', 42, -3, 13, 37);
|
||||||
|
|
||||||
|
})();
|
||||||
|
|
||||||
|
} catch (e) { print('stack: ', e.stack); throw e }
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -100,6 +100,106 @@ function assertEqX4(observed, expected) {
|
||||||
assertEq(observed.w, expected.w);
|
assertEq(observed.w, expected.w);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function testSimdX4(ctor, shift, scale, disp, simdName, simdCtor) {
|
||||||
|
var arr = new ctor(ab);
|
||||||
|
|
||||||
|
var c = asmCompile('glob', 'imp', 'b',
|
||||||
|
USE_ASM +
|
||||||
|
'var arr=new glob.' + ctor.name + '(b); ' +
|
||||||
|
'var SIMD_' + simdName + ' = glob.SIMD.' + simdName + '; ' +
|
||||||
|
'var SIMD_' + simdName + '_check = SIMD_' + simdName + '.check; ' +
|
||||||
|
'var SIMD_' + simdName + '_load = SIMD_' + simdName + '.load; ' +
|
||||||
|
'var SIMD_' + simdName + '_load2 = SIMD_' + simdName + '.load2; ' +
|
||||||
|
'var SIMD_' + simdName + '_load1 = SIMD_' + simdName + '.load1; ' +
|
||||||
|
'var SIMD_' + simdName + '_store = SIMD_' + simdName + '.store; ' +
|
||||||
|
'var SIMD_' + simdName + '_store2 = SIMD_' + simdName + '.store2; ' +
|
||||||
|
'var SIMD_' + simdName + '_store1 = SIMD_' + simdName + '.store1; ' +
|
||||||
|
'function load(i) {i=i|0; return SIMD_' + simdName + '_check(SIMD_' + simdName + '_load(arr, ((i<<' + scale + ')+' + disp + ')>>' + shift + ')) } ' +
|
||||||
|
'function load2(i) {i=i|0; return SIMD_' + simdName + '_check(SIMD_' + simdName + '_load2(arr, ((i<<' + scale + ')+' + disp + ')>>' + shift + ')) } ' +
|
||||||
|
'function load1(i) {i=i|0; return SIMD_' + simdName + '_check(SIMD_' + simdName + '_load1(arr, ((i<<' + scale + ')+' + disp + ')>>' + shift + ')) } ' +
|
||||||
|
'function store(i,j) {i=i|0;j=SIMD_' + simdName + '_check(j); SIMD_' + simdName + '_store(arr, ((i<<' + scale + ')+' + disp + ')>>' + shift + ', j) } ' +
|
||||||
|
'function store2(i,j) {i=i|0;j=SIMD_' + simdName + '_check(j); SIMD_' + simdName + '_store2(arr, ((i<<' + scale + ')+' + disp + ')>>' + shift + ', j) } ' +
|
||||||
|
'function store1(i,j) {i=i|0;j=SIMD_' + simdName + '_check(j); SIMD_' + simdName + '_store1(arr, ((i<<' + scale + ')+' + disp + ')>>' + shift + ', j) } ' +
|
||||||
|
'return { load: load, load2: load2, load1: load1, store: store, store2 : store2, store1 : store1 }');
|
||||||
|
var f = asmLink(c, this, null, ab);
|
||||||
|
|
||||||
|
const RuntimeError = WebAssembly.RuntimeError;
|
||||||
|
|
||||||
|
for (var i of indices) {
|
||||||
|
var index = ((i<<scale)+disp)>>shift;
|
||||||
|
|
||||||
|
var v, v2, v1;
|
||||||
|
var t = false, t2 = false, t1 = false;
|
||||||
|
try { v = simdCtor.load(arr, index); }
|
||||||
|
catch (e) {
|
||||||
|
assertEq(e instanceof RangeError, true);
|
||||||
|
t = true;
|
||||||
|
}
|
||||||
|
try { v2 = simdCtor.load2(arr, index); }
|
||||||
|
catch (e) {
|
||||||
|
assertEq(e instanceof RangeError, true);
|
||||||
|
t2 = true;
|
||||||
|
}
|
||||||
|
try { v1 = simdCtor.load1(arr, index); }
|
||||||
|
catch (e) {
|
||||||
|
assertEq(e instanceof RangeError, true);
|
||||||
|
t1 = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Loads
|
||||||
|
var l, l2, l1;
|
||||||
|
var r = false, r2 = false, r1 = false;
|
||||||
|
try { l = f.load(i); }
|
||||||
|
catch (e) {
|
||||||
|
assertEq(e instanceof RuntimeError, true);
|
||||||
|
r = true;
|
||||||
|
}
|
||||||
|
try { l2 = f.load2(i); }
|
||||||
|
catch (e) {
|
||||||
|
assertEq(e instanceof RuntimeError, true);
|
||||||
|
r2 = true;
|
||||||
|
}
|
||||||
|
try { l1 = f.load1(i); }
|
||||||
|
catch (e) {
|
||||||
|
assertEq(e instanceof RuntimeError, true);
|
||||||
|
r1 = true;
|
||||||
|
}
|
||||||
|
assertEq(t, r);
|
||||||
|
assertEq(t2, r2);
|
||||||
|
assertEq(t1, r1);
|
||||||
|
if (!t) assertEqX4(v, l);
|
||||||
|
if (!t2) assertEqX4(v2, l2);
|
||||||
|
if (!t1) assertEqX4(v1, l1);
|
||||||
|
|
||||||
|
// Stores
|
||||||
|
if (!t) {
|
||||||
|
simdCtor.store(arr, index, simdCtor.neg(v));
|
||||||
|
f.store(i, v);
|
||||||
|
assertEqX4(simdCtor.load(arr, index), v);
|
||||||
|
} else
|
||||||
|
assertThrowsInstanceOf(() => f.store(i, simdCtor()), RuntimeError);
|
||||||
|
if (!t2) {
|
||||||
|
simdCtor.store2(arr, index, simdCtor.neg(v2));
|
||||||
|
f.store2(i, v2);
|
||||||
|
assertEqX4(simdCtor.load2(arr, index), v2);
|
||||||
|
} else
|
||||||
|
assertThrowsInstanceOf(() => f.store2(i, simdCtor()), RuntimeError);
|
||||||
|
if (!t1) {
|
||||||
|
simdCtor.store1(arr, index, simdCtor.neg(v1));
|
||||||
|
f.store1(i, v1);
|
||||||
|
assertEqX4(simdCtor.load1(arr, index), v1);
|
||||||
|
} else
|
||||||
|
assertThrowsInstanceOf(() => f.store1(i, simdCtor()), RuntimeError);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function testFloat32x4(ctor, shift, scale, disp) {
|
||||||
|
testSimdX4(ctor, shift, scale, disp, 'Float32x4', SIMD.Float32x4);
|
||||||
|
}
|
||||||
|
function testInt32x4(ctor, shift, scale, disp) {
|
||||||
|
testSimdX4(ctor, shift, scale, disp, 'Int32x4', SIMD.Int32x4);
|
||||||
|
}
|
||||||
|
|
||||||
function test(tester, ctor, shift) {
|
function test(tester, ctor, shift) {
|
||||||
var arr = new ctor(ab);
|
var arr = new ctor(ab);
|
||||||
for (var i = 0; i < arr.length; i++)
|
for (var i = 0; i < arr.length; i++)
|
||||||
|
@ -123,3 +223,15 @@ test(testInt, Int32Array, 2);
|
||||||
test(testInt, Uint32Array, 2);
|
test(testInt, Uint32Array, 2);
|
||||||
test(testFloat32, Float32Array, 2);
|
test(testFloat32, Float32Array, 2);
|
||||||
test(testFloat64, Float64Array, 3);
|
test(testFloat64, Float64Array, 3);
|
||||||
|
if (typeof SIMD !== 'undefined' && isSimdAvailable()) {
|
||||||
|
// Avoid pathological --ion-eager compile times due to bails in loops
|
||||||
|
setJitCompilerOption('ion.warmup.trigger', 1000000);
|
||||||
|
|
||||||
|
// Use a fresh ArrayBuffer so prepareForAsmJS can allocated a guard page
|
||||||
|
// which SIMD.js needs. Since the original ArrayBuffer was prepared for
|
||||||
|
// asm.js that didn't use SIMD.js, it has no guard page (on 32-bit).
|
||||||
|
ab = new ArrayBuffer(BUF_MIN);
|
||||||
|
|
||||||
|
test(testInt32x4, Uint8Array, 0);
|
||||||
|
test(testFloat32x4, Uint8Array, 0);
|
||||||
|
}
|
||||||
|
|
|
@ -2012,6 +2012,7 @@ jit::FinishBailoutToBaseline(BaselineBailoutInfo* bailoutInfo)
|
||||||
case Bailout_NonObjectInput:
|
case Bailout_NonObjectInput:
|
||||||
case Bailout_NonStringInput:
|
case Bailout_NonStringInput:
|
||||||
case Bailout_NonSymbolInput:
|
case Bailout_NonSymbolInput:
|
||||||
|
case Bailout_UnexpectedSimdInput:
|
||||||
case Bailout_NonSharedTypedArrayInput:
|
case Bailout_NonSharedTypedArrayInput:
|
||||||
case Bailout_Debugger:
|
case Bailout_Debugger:
|
||||||
case Bailout_UninitializedThis:
|
case Bailout_UninitializedThis:
|
||||||
|
|
|
@ -14,6 +14,7 @@
|
||||||
#include "jstypes.h"
|
#include "jstypes.h"
|
||||||
|
|
||||||
#include "builtin/Eval.h"
|
#include "builtin/Eval.h"
|
||||||
|
#include "builtin/SIMDConstants.h"
|
||||||
#include "gc/Policy.h"
|
#include "gc/Policy.h"
|
||||||
#include "jit/BaselineCacheIRCompiler.h"
|
#include "jit/BaselineCacheIRCompiler.h"
|
||||||
#include "jit/BaselineDebugModeOSR.h"
|
#include "jit/BaselineDebugModeOSR.h"
|
||||||
|
@ -2012,6 +2013,71 @@ TryAttachFunCallStub(JSContext* cx, ICCall_Fallback* stub, HandleScript script,
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check if target is a native SIMD operation which returns a SIMD type.
|
||||||
|
// If so, set res to a template object matching the SIMD type produced and return true.
|
||||||
|
static bool
|
||||||
|
GetTemplateObjectForSimd(JSContext* cx, JSFunction* target, MutableHandleObject res)
|
||||||
|
{
|
||||||
|
if (!target->hasJitInfo())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
const JSJitInfo* jitInfo = target->jitInfo();
|
||||||
|
if (jitInfo->type() != JSJitInfo::InlinableNative)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Check if this is a native inlinable SIMD operation.
|
||||||
|
SimdType ctrlType;
|
||||||
|
switch (jitInfo->inlinableNative) {
|
||||||
|
case InlinableNative::SimdInt8x16: ctrlType = SimdType::Int8x16; break;
|
||||||
|
case InlinableNative::SimdUint8x16: ctrlType = SimdType::Uint8x16; break;
|
||||||
|
case InlinableNative::SimdInt16x8: ctrlType = SimdType::Int16x8; break;
|
||||||
|
case InlinableNative::SimdUint16x8: ctrlType = SimdType::Uint16x8; break;
|
||||||
|
case InlinableNative::SimdInt32x4: ctrlType = SimdType::Int32x4; break;
|
||||||
|
case InlinableNative::SimdUint32x4: ctrlType = SimdType::Uint32x4; break;
|
||||||
|
case InlinableNative::SimdFloat32x4: ctrlType = SimdType::Float32x4; break;
|
||||||
|
case InlinableNative::SimdBool8x16: ctrlType = SimdType::Bool8x16; break;
|
||||||
|
case InlinableNative::SimdBool16x8: ctrlType = SimdType::Bool16x8; break;
|
||||||
|
case InlinableNative::SimdBool32x4: ctrlType = SimdType::Bool32x4; break;
|
||||||
|
// This is not an inlinable SIMD operation.
|
||||||
|
default: return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The controlling type is not necessarily the return type.
|
||||||
|
// Check the actual operation.
|
||||||
|
SimdOperation simdOp = SimdOperation(jitInfo->nativeOp);
|
||||||
|
SimdType retType;
|
||||||
|
|
||||||
|
switch(simdOp) {
|
||||||
|
case SimdOperation::Fn_allTrue:
|
||||||
|
case SimdOperation::Fn_anyTrue:
|
||||||
|
case SimdOperation::Fn_extractLane:
|
||||||
|
// These operations return a scalar. No template object needed.
|
||||||
|
return false;
|
||||||
|
|
||||||
|
case SimdOperation::Fn_lessThan:
|
||||||
|
case SimdOperation::Fn_lessThanOrEqual:
|
||||||
|
case SimdOperation::Fn_equal:
|
||||||
|
case SimdOperation::Fn_notEqual:
|
||||||
|
case SimdOperation::Fn_greaterThan:
|
||||||
|
case SimdOperation::Fn_greaterThanOrEqual:
|
||||||
|
// These operations return a boolean vector with the same shape as the
|
||||||
|
// controlling type.
|
||||||
|
retType = GetBooleanSimdType(ctrlType);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
// All other operations return the controlling type.
|
||||||
|
retType = ctrlType;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create a template object based on retType.
|
||||||
|
RootedGlobalObject global(cx, cx->global());
|
||||||
|
Rooted<SimdTypeDescr*> descr(cx, GlobalObject::getOrCreateSimdTypeDescr(cx, global, retType));
|
||||||
|
res.set(cx->realm()->jitRealm()->getSimdTemplateObjectFor(cx, descr));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
GetTemplateObjectForNative(JSContext* cx, HandleFunction target, const CallArgs& args,
|
GetTemplateObjectForNative(JSContext* cx, HandleFunction target, const CallArgs& args,
|
||||||
MutableHandleObject res, bool* skipAttach)
|
MutableHandleObject res, bool* skipAttach)
|
||||||
|
@ -2095,6 +2161,9 @@ GetTemplateObjectForNative(JSContext* cx, HandleFunction target, const CallArgs&
|
||||||
return !!res;
|
return !!res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (JitSupportsSimd() && GetTemplateObjectForSimd(cx, target, res))
|
||||||
|
return !!res;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2111,6 +2180,12 @@ GetTemplateObjectForClassHook(JSContext* cx, JSNative hook, CallArgs& args,
|
||||||
return !!templateObject;
|
return !!templateObject;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (hook == SimdTypeDescr::call && JitSupportsSimd()) {
|
||||||
|
Rooted<SimdTypeDescr*> descr(cx, &args.callee().as<SimdTypeDescr>());
|
||||||
|
templateObject.set(cx->realm()->jitRealm()->getSimdTemplateObjectFor(cx, descr));
|
||||||
|
return !!templateObject;
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -663,6 +663,25 @@ BaselineInspector::getTemplateObjectForClassHook(jsbytecode* pc, const Class* cl
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
JSObject*
|
||||||
|
BaselineInspector::getTemplateObjectForSimdCtor(jsbytecode* pc, SimdType simdType)
|
||||||
|
{
|
||||||
|
if (!hasBaselineScript())
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
|
const ICEntry& entry = icEntryFromPC(pc);
|
||||||
|
for (ICStub* stub = entry.firstStub(); stub; stub = stub->next()) {
|
||||||
|
if (stub->isCall_ClassHook() && stub->toCall_ClassHook()->clasp() == &SimdTypeDescr::class_) {
|
||||||
|
JSObject* templateObj = stub->toCall_ClassHook()->templateObject();
|
||||||
|
InlineTypedObject& typedObj = templateObj->as<InlineTypedObject>();
|
||||||
|
if (typedObj.typeDescr().as<SimdTypeDescr>().type() == simdType)
|
||||||
|
return templateObj;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
LexicalEnvironmentObject*
|
LexicalEnvironmentObject*
|
||||||
BaselineInspector::templateNamedLambdaObject()
|
BaselineInspector::templateNamedLambdaObject()
|
||||||
{
|
{
|
||||||
|
|
|
@ -131,6 +131,7 @@ class BaselineInspector
|
||||||
JSObject* getTemplateObject(jsbytecode* pc);
|
JSObject* getTemplateObject(jsbytecode* pc);
|
||||||
JSObject* getTemplateObjectForNative(jsbytecode* pc, Native native);
|
JSObject* getTemplateObjectForNative(jsbytecode* pc, Native native);
|
||||||
JSObject* getTemplateObjectForClassHook(jsbytecode* pc, const Class* clasp);
|
JSObject* getTemplateObjectForClassHook(jsbytecode* pc, const Class* clasp);
|
||||||
|
JSObject* getTemplateObjectForSimdCtor(jsbytecode* pc, SimdType simdType);
|
||||||
|
|
||||||
// Sometimes the group a template object will have is known, even if the
|
// Sometimes the group a template object will have is known, even if the
|
||||||
// object itself isn't.
|
// object itself isn't.
|
||||||
|
|
|
@ -422,6 +422,7 @@ CodeGenerator::CodeGenerator(MIRGenerator* gen, LIRGraph* graph, MacroAssembler*
|
||||||
: CodeGeneratorSpecific(gen, graph, masm)
|
: CodeGeneratorSpecific(gen, graph, masm)
|
||||||
, ionScriptLabels_(gen->alloc())
|
, ionScriptLabels_(gen->alloc())
|
||||||
, scriptCounts_(nullptr)
|
, scriptCounts_(nullptr)
|
||||||
|
, simdTemplatesToReadBarrier_(0)
|
||||||
, realmStubsToReadBarrier_(0)
|
, realmStubsToReadBarrier_(0)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
@ -6426,6 +6427,82 @@ CodeGenerator::visitNewTypedObject(LNewTypedObject* lir)
|
||||||
masm.bind(ool->rejoin());
|
masm.bind(ool->rejoin());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
CodeGenerator::visitSimdBox(LSimdBox* lir)
|
||||||
|
{
|
||||||
|
FloatRegister in = ToFloatRegister(lir->input());
|
||||||
|
Register object = ToRegister(lir->output());
|
||||||
|
Register temp = ToRegister(lir->temp());
|
||||||
|
InlineTypedObject* templateObject = lir->mir()->templateObject();
|
||||||
|
gc::InitialHeap initialHeap = lir->mir()->initialHeap();
|
||||||
|
MIRType type = lir->mir()->input()->type();
|
||||||
|
|
||||||
|
addSimdTemplateToReadBarrier(lir->mir()->simdType());
|
||||||
|
|
||||||
|
MOZ_ASSERT(lir->safepoint()->liveRegs().has(in), "Save the input register across oolCallVM");
|
||||||
|
OutOfLineCode* ool = oolCallVM(NewTypedObjectInfo, lir,
|
||||||
|
ArgList(ImmGCPtr(templateObject), Imm32(initialHeap)),
|
||||||
|
StoreRegisterTo(object));
|
||||||
|
|
||||||
|
TemplateObject templateObj(templateObject);
|
||||||
|
masm.createGCObject(object, temp, templateObj, initialHeap, ool->entry());
|
||||||
|
masm.bind(ool->rejoin());
|
||||||
|
|
||||||
|
Address objectData(object, InlineTypedObject::offsetOfDataStart());
|
||||||
|
switch (type) {
|
||||||
|
case MIRType::Int8x16:
|
||||||
|
case MIRType::Int16x8:
|
||||||
|
case MIRType::Int32x4:
|
||||||
|
case MIRType::Bool8x16:
|
||||||
|
case MIRType::Bool16x8:
|
||||||
|
case MIRType::Bool32x4:
|
||||||
|
masm.storeUnalignedSimd128Int(in, objectData);
|
||||||
|
break;
|
||||||
|
case MIRType::Float32x4:
|
||||||
|
masm.storeUnalignedSimd128Float(in, objectData);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
MOZ_CRASH("Unknown SIMD kind when generating code for SimdBox.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
CodeGenerator::addSimdTemplateToReadBarrier(SimdType simdType)
|
||||||
|
{
|
||||||
|
simdTemplatesToReadBarrier_ |= 1 << uint32_t(simdType);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
CodeGenerator::visitSimdUnbox(LSimdUnbox* lir)
|
||||||
|
{
|
||||||
|
Register object = ToRegister(lir->input());
|
||||||
|
FloatRegister simd = ToFloatRegister(lir->output());
|
||||||
|
Register temp = ToRegister(lir->temp());
|
||||||
|
Label bail;
|
||||||
|
|
||||||
|
masm.branchIfNotSimdObject(object, temp, lir->mir()->simdType(), &bail);
|
||||||
|
|
||||||
|
// Load the value from the data of the InlineTypedObject.
|
||||||
|
Address objectData(object, InlineTypedObject::offsetOfDataStart());
|
||||||
|
switch (lir->mir()->type()) {
|
||||||
|
case MIRType::Int8x16:
|
||||||
|
case MIRType::Int16x8:
|
||||||
|
case MIRType::Int32x4:
|
||||||
|
case MIRType::Bool8x16:
|
||||||
|
case MIRType::Bool16x8:
|
||||||
|
case MIRType::Bool32x4:
|
||||||
|
masm.loadUnalignedSimd128Int(objectData, simd);
|
||||||
|
break;
|
||||||
|
case MIRType::Float32x4:
|
||||||
|
masm.loadUnalignedSimd128Float(objectData, simd);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
MOZ_CRASH("The impossible happened!");
|
||||||
|
}
|
||||||
|
|
||||||
|
bailoutFrom(&bail, lir->snapshot());
|
||||||
|
}
|
||||||
|
|
||||||
typedef js::NamedLambdaObject* (*NewNamedLambdaObjectFn)(JSContext*, HandleFunction, gc::InitialHeap);
|
typedef js::NamedLambdaObject* (*NewNamedLambdaObjectFn)(JSContext*, HandleFunction, gc::InitialHeap);
|
||||||
static const VMFunction NewNamedLambdaObjectInfo =
|
static const VMFunction NewNamedLambdaObjectInfo =
|
||||||
FunctionInfo<NewNamedLambdaObjectFn>(NamedLambdaObject::createTemplateObject,
|
FunctionInfo<NewNamedLambdaObjectFn>(NamedLambdaObject::createTemplateObject,
|
||||||
|
@ -7156,7 +7233,7 @@ CodeGenerator::visitWasmLoadGlobalVar(LWasmLoadGlobalVar* ins)
|
||||||
MWasmLoadGlobalVar* mir = ins->mir();
|
MWasmLoadGlobalVar* mir = ins->mir();
|
||||||
|
|
||||||
MIRType type = mir->type();
|
MIRType type = mir->type();
|
||||||
MOZ_ASSERT(IsNumberType(type));
|
MOZ_ASSERT(IsNumberType(type) || IsSimdType(type));
|
||||||
|
|
||||||
Register tls = ToRegister(ins->tlsPtr());
|
Register tls = ToRegister(ins->tlsPtr());
|
||||||
Address addr(tls, offsetof(wasm::TlsData, globalArea) + mir->globalDataOffset());
|
Address addr(tls, offsetof(wasm::TlsData, globalArea) + mir->globalDataOffset());
|
||||||
|
@ -7183,7 +7260,11 @@ CodeGenerator::visitWasmLoadGlobalVar(LWasmLoadGlobalVar* ins)
|
||||||
case MIRType::Bool8x16:
|
case MIRType::Bool8x16:
|
||||||
case MIRType::Bool16x8:
|
case MIRType::Bool16x8:
|
||||||
case MIRType::Bool32x4:
|
case MIRType::Bool32x4:
|
||||||
|
masm.loadInt32x4(addr, ToFloatRegister(ins->output()));
|
||||||
|
break;
|
||||||
case MIRType::Float32x4:
|
case MIRType::Float32x4:
|
||||||
|
masm.loadFloat32x4(addr, ToFloatRegister(ins->output()));
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
MOZ_CRASH("unexpected type in visitWasmLoadGlobalVar");
|
MOZ_CRASH("unexpected type in visitWasmLoadGlobalVar");
|
||||||
}
|
}
|
||||||
|
@ -7195,7 +7276,7 @@ CodeGenerator::visitWasmStoreGlobalVar(LWasmStoreGlobalVar* ins)
|
||||||
MWasmStoreGlobalVar* mir = ins->mir();
|
MWasmStoreGlobalVar* mir = ins->mir();
|
||||||
|
|
||||||
MIRType type = mir->value()->type();
|
MIRType type = mir->value()->type();
|
||||||
MOZ_ASSERT(IsNumberType(type));
|
MOZ_ASSERT(IsNumberType(type) || IsSimdType(type));
|
||||||
|
|
||||||
Register tls = ToRegister(ins->tlsPtr());
|
Register tls = ToRegister(ins->tlsPtr());
|
||||||
Address addr(tls, offsetof(wasm::TlsData, globalArea) + mir->globalDataOffset());
|
Address addr(tls, offsetof(wasm::TlsData, globalArea) + mir->globalDataOffset());
|
||||||
|
@ -7222,7 +7303,11 @@ CodeGenerator::visitWasmStoreGlobalVar(LWasmStoreGlobalVar* ins)
|
||||||
case MIRType::Bool8x16:
|
case MIRType::Bool8x16:
|
||||||
case MIRType::Bool16x8:
|
case MIRType::Bool16x8:
|
||||||
case MIRType::Bool32x4:
|
case MIRType::Bool32x4:
|
||||||
|
masm.storeInt32x4(ToFloatRegister(ins->value()), addr);
|
||||||
|
break;
|
||||||
case MIRType::Float32x4:
|
case MIRType::Float32x4:
|
||||||
|
masm.storeFloat32x4(ToFloatRegister(ins->value()), addr);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
MOZ_CRASH("unexpected type in visitWasmStoreGlobalVar");
|
MOZ_CRASH("unexpected type in visitWasmStoreGlobalVar");
|
||||||
}
|
}
|
||||||
|
@ -10277,6 +10362,7 @@ CodeGenerator::link(JSContext* cx, CompilerConstraintList* constraints)
|
||||||
// script, which may have happened off-thread.
|
// script, which may have happened off-thread.
|
||||||
const JitRealm* jr = gen->realm->jitRealm();
|
const JitRealm* jr = gen->realm->jitRealm();
|
||||||
jr->performStubReadBarriers(realmStubsToReadBarrier_);
|
jr->performStubReadBarriers(realmStubsToReadBarrier_);
|
||||||
|
jr->performSIMDTemplateReadBarriers(simdTemplatesToReadBarrier_);
|
||||||
|
|
||||||
// We finished the new IonScript. Invalidate the current active IonScript,
|
// We finished the new IonScript. Invalidate the current active IonScript,
|
||||||
// so we can replace it with this new (probably higher optimized) version.
|
// so we can replace it with this new (probably higher optimized) version.
|
||||||
|
@ -11500,17 +11586,19 @@ CodeGenerator::visitLoadUnboxedScalar(LLoadUnboxedScalar* lir)
|
||||||
const MLoadUnboxedScalar* mir = lir->mir();
|
const MLoadUnboxedScalar* mir = lir->mir();
|
||||||
|
|
||||||
Scalar::Type readType = mir->readType();
|
Scalar::Type readType = mir->readType();
|
||||||
|
unsigned numElems = mir->numElems();
|
||||||
|
|
||||||
int width = Scalar::byteSize(mir->storageType());
|
int width = Scalar::byteSize(mir->storageType());
|
||||||
bool canonicalizeDouble = mir->canonicalizeDoubles();
|
bool canonicalizeDouble = mir->canonicalizeDoubles();
|
||||||
|
|
||||||
Label fail;
|
Label fail;
|
||||||
if (lir->index()->isConstant()) {
|
if (lir->index()->isConstant()) {
|
||||||
Address source(elements, ToInt32(lir->index()) * width + mir->offsetAdjustment());
|
Address source(elements, ToInt32(lir->index()) * width + mir->offsetAdjustment());
|
||||||
masm.loadFromTypedArray(readType, source, out, temp, &fail, canonicalizeDouble);
|
masm.loadFromTypedArray(readType, source, out, temp, &fail, canonicalizeDouble, numElems);
|
||||||
} else {
|
} else {
|
||||||
BaseIndex source(elements, ToRegister(lir->index()), ScaleFromElemWidth(width),
|
BaseIndex source(elements, ToRegister(lir->index()), ScaleFromElemWidth(width),
|
||||||
mir->offsetAdjustment());
|
mir->offsetAdjustment());
|
||||||
masm.loadFromTypedArray(readType, source, out, temp, &fail, canonicalizeDouble);
|
masm.loadFromTypedArray(readType, source, out, temp, &fail, canonicalizeDouble, numElems);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (fail.used())
|
if (fail.used())
|
||||||
|
@ -11789,10 +11877,13 @@ CodeGenerator::visitLoadElementFromStateV(LLoadElementFromStateV* lir)
|
||||||
template <typename T>
|
template <typename T>
|
||||||
static inline void
|
static inline void
|
||||||
StoreToTypedArray(MacroAssembler& masm, Scalar::Type writeType, const LAllocation* value,
|
StoreToTypedArray(MacroAssembler& masm, Scalar::Type writeType, const LAllocation* value,
|
||||||
const T& dest)
|
const T& dest, unsigned numElems = 0)
|
||||||
{
|
{
|
||||||
if (writeType == Scalar::Float32 || writeType == Scalar::Float64) {
|
if (Scalar::isSimdType(writeType) ||
|
||||||
masm.storeToTypedFloatArray(writeType, ToFloatRegister(value), dest);
|
writeType == Scalar::Float32 ||
|
||||||
|
writeType == Scalar::Float64)
|
||||||
|
{
|
||||||
|
masm.storeToTypedFloatArray(writeType, ToFloatRegister(value), dest, numElems);
|
||||||
} else {
|
} else {
|
||||||
if (value->isConstant())
|
if (value->isConstant())
|
||||||
masm.storeToTypedIntArray(writeType, Imm32(ToInt32(value)), dest);
|
masm.storeToTypedIntArray(writeType, Imm32(ToInt32(value)), dest);
|
||||||
|
@ -11810,16 +11901,17 @@ CodeGenerator::visitStoreUnboxedScalar(LStoreUnboxedScalar* lir)
|
||||||
const MStoreUnboxedScalar* mir = lir->mir();
|
const MStoreUnboxedScalar* mir = lir->mir();
|
||||||
|
|
||||||
Scalar::Type writeType = mir->writeType();
|
Scalar::Type writeType = mir->writeType();
|
||||||
|
unsigned numElems = mir->numElems();
|
||||||
|
|
||||||
int width = Scalar::byteSize(mir->storageType());
|
int width = Scalar::byteSize(mir->storageType());
|
||||||
|
|
||||||
if (lir->index()->isConstant()) {
|
if (lir->index()->isConstant()) {
|
||||||
Address dest(elements, ToInt32(lir->index()) * width + mir->offsetAdjustment());
|
Address dest(elements, ToInt32(lir->index()) * width + mir->offsetAdjustment());
|
||||||
StoreToTypedArray(masm, writeType, value, dest);
|
StoreToTypedArray(masm, writeType, value, dest, numElems);
|
||||||
} else {
|
} else {
|
||||||
BaseIndex dest(elements, ToRegister(lir->index()), ScaleFromElemWidth(width),
|
BaseIndex dest(elements, ToRegister(lir->index()), ScaleFromElemWidth(width),
|
||||||
mir->offsetAdjustment());
|
mir->offsetAdjustment());
|
||||||
StoreToTypedArray(masm, writeType, value, dest);
|
StoreToTypedArray(masm, writeType, value, dest, numElems);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -317,9 +317,24 @@ class CodeGenerator final : public CodeGeneratorSpecific
|
||||||
PerfSpewer perfSpewer_;
|
PerfSpewer perfSpewer_;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// This integer is a bit mask of all SimdTypeDescr::Type indexes. When a
|
||||||
|
// MSimdBox instruction is encoded, it might have either been created by
|
||||||
|
// IonBuilder, or by the Eager Simd Unbox phase.
|
||||||
|
//
|
||||||
|
// As the template objects are weak references, the JitRealm is using
|
||||||
|
// Read Barriers, but such barrier cannot be used during the compilation. To
|
||||||
|
// work around this issue, the barriers are captured during
|
||||||
|
// CodeGenerator::link.
|
||||||
|
//
|
||||||
|
// Instead of saving the pointers, we just save the index of the Read
|
||||||
|
// Barriered objects in a bit mask.
|
||||||
|
uint32_t simdTemplatesToReadBarrier_;
|
||||||
|
|
||||||
// Bit mask of JitRealm stubs that are to be read-barriered.
|
// Bit mask of JitRealm stubs that are to be read-barriered.
|
||||||
uint32_t realmStubsToReadBarrier_;
|
uint32_t realmStubsToReadBarrier_;
|
||||||
|
|
||||||
|
void addSimdTemplateToReadBarrier(SimdType simdType);
|
||||||
|
|
||||||
#define LIR_OP(op) void visit##op(L##op* ins);
|
#define LIR_OP(op) void visit##op(L##op* ins);
|
||||||
LIR_OPCODE_LIST(LIR_OP)
|
LIR_OPCODE_LIST(LIR_OP)
|
||||||
#undef LIR_OP
|
#undef LIR_OP
|
||||||
|
|
|
@ -0,0 +1,128 @@
|
||||||
|
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||||
|
* vim: set ts=8 sts=4 et sw=4 tw=99:
|
||||||
|
* 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/EagerSimdUnbox.h"
|
||||||
|
|
||||||
|
#include "jit/MIR.h"
|
||||||
|
#include "jit/MIRGenerator.h"
|
||||||
|
#include "jit/MIRGraph.h"
|
||||||
|
|
||||||
|
namespace js {
|
||||||
|
namespace jit {
|
||||||
|
|
||||||
|
// Do not optimize any Phi instruction which has conflicting Unbox operations,
|
||||||
|
// as this might imply some intended polymorphism.
|
||||||
|
static bool
|
||||||
|
CanUnboxSimdPhi(const JitRealm* jitRealm, MPhi* phi, SimdType unboxType)
|
||||||
|
{
|
||||||
|
MOZ_ASSERT(phi->type() == MIRType::Object);
|
||||||
|
|
||||||
|
// If we are unboxing, we are more than likely to have boxed this SIMD type
|
||||||
|
// once in baseline, otherwise, we cannot create a MSimdBox as we have no
|
||||||
|
// template object to use.
|
||||||
|
if (!jitRealm->maybeGetSimdTemplateObjectFor(unboxType))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
MResumePoint* entry = phi->block()->entryResumePoint();
|
||||||
|
MIRType mirType = SimdTypeToMIRType(unboxType);
|
||||||
|
for (MUseIterator i(phi->usesBegin()), e(phi->usesEnd()); i != e; i++) {
|
||||||
|
// If we cannot recover the Simd object at the entry of the basic block,
|
||||||
|
// then we would have to box the content anyways.
|
||||||
|
if ((*i)->consumer() == entry && !entry->isRecoverableOperand(*i))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!(*i)->consumer()->isDefinition())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
MDefinition* def = (*i)->consumer()->toDefinition();
|
||||||
|
if (def->isSimdUnbox() && def->toSimdUnbox()->type() != mirType)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
UnboxSimdPhi(const JitRealm* jitRealm, MIRGraph& graph, MPhi* phi, SimdType unboxType)
|
||||||
|
{
|
||||||
|
TempAllocator& alloc = graph.alloc();
|
||||||
|
|
||||||
|
// Unbox and replace all operands.
|
||||||
|
for (size_t i = 0, e = phi->numOperands(); i < e; i++) {
|
||||||
|
MDefinition* op = phi->getOperand(i);
|
||||||
|
MSimdUnbox* unbox = MSimdUnbox::New(alloc, op, unboxType);
|
||||||
|
op->block()->insertAtEnd(unbox);
|
||||||
|
phi->replaceOperand(i, unbox);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Change the MIRType of the Phi.
|
||||||
|
MIRType mirType = SimdTypeToMIRType(unboxType);
|
||||||
|
phi->setResultType(mirType);
|
||||||
|
|
||||||
|
MBasicBlock* phiBlock = phi->block();
|
||||||
|
MInstruction* atRecover = phiBlock->safeInsertTop(nullptr, MBasicBlock::IgnoreRecover);
|
||||||
|
MInstruction* at = phiBlock->safeInsertTop(atRecover);
|
||||||
|
|
||||||
|
// Note, we capture the uses-list now, as new instructions are not visited.
|
||||||
|
MUseIterator i(phi->usesBegin()), e(phi->usesEnd());
|
||||||
|
|
||||||
|
// Add a MSimdBox, and replace all the Phi uses with it.
|
||||||
|
JSObject* templateObject = jitRealm->maybeGetSimdTemplateObjectFor(unboxType);
|
||||||
|
InlineTypedObject* inlineTypedObject = &templateObject->as<InlineTypedObject>();
|
||||||
|
MSimdBox* recoverBox = MSimdBox::New(alloc, nullptr, phi, inlineTypedObject, unboxType, gc::DefaultHeap);
|
||||||
|
recoverBox->setRecoveredOnBailout();
|
||||||
|
phiBlock->insertBefore(atRecover, recoverBox);
|
||||||
|
|
||||||
|
MSimdBox* box = nullptr;
|
||||||
|
while (i != e) {
|
||||||
|
MUse* use = *i++;
|
||||||
|
MNode* ins = use->consumer();
|
||||||
|
|
||||||
|
if ((ins->isDefinition() && ins->toDefinition()->isRecoveredOnBailout()) ||
|
||||||
|
(ins->isResumePoint() && ins->toResumePoint()->isRecoverableOperand(use)))
|
||||||
|
{
|
||||||
|
use->replaceProducer(recoverBox);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!box) {
|
||||||
|
box = MSimdBox::New(alloc, nullptr, phi, inlineTypedObject, unboxType, gc::DefaultHeap);
|
||||||
|
phiBlock->insertBefore(at, box);
|
||||||
|
}
|
||||||
|
|
||||||
|
use->replaceProducer(box);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
EagerSimdUnbox(MIRGenerator* mir, MIRGraph& graph)
|
||||||
|
{
|
||||||
|
const JitRealm* jitRealm = mir->realm->jitRealm();
|
||||||
|
for (PostorderIterator block = graph.poBegin(); block != graph.poEnd(); block++) {
|
||||||
|
if (mir->shouldCancel("Eager Simd Unbox"))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
for (MInstructionReverseIterator ins = block->rbegin(); ins != block->rend(); ins++) {
|
||||||
|
if (!ins->isSimdUnbox())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
MSimdUnbox* unbox = ins->toSimdUnbox();
|
||||||
|
if (!unbox->input()->isPhi())
|
||||||
|
continue;
|
||||||
|
|
||||||
|
MPhi* phi = unbox->input()->toPhi();
|
||||||
|
if (!CanUnboxSimdPhi(jitRealm, phi, unbox->simdType()))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
UnboxSimdPhi(jitRealm, graph, phi, unbox->simdType());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
} /* namespace jit */
|
||||||
|
} /* namespace js */
|
|
@ -0,0 +1,25 @@
|
||||||
|
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
||||||
|
* vim: set ts=8 sts=4 et sw=4 tw=99:
|
||||||
|
* 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/. */
|
||||||
|
|
||||||
|
// This file declares eager SIMD unboxing.
|
||||||
|
#ifndef jit_EagerSimdUnbox_h
|
||||||
|
#define jit_EagerSimdUnbox_h
|
||||||
|
|
||||||
|
#include "mozilla/Attributes.h"
|
||||||
|
|
||||||
|
namespace js {
|
||||||
|
namespace jit {
|
||||||
|
|
||||||
|
class MIRGenerator;
|
||||||
|
class MIRGraph;
|
||||||
|
|
||||||
|
MOZ_MUST_USE bool
|
||||||
|
EagerSimdUnbox(MIRGenerator* mir, MIRGraph& graph);
|
||||||
|
|
||||||
|
} // namespace jit
|
||||||
|
} // namespace js
|
||||||
|
|
||||||
|
#endif /* jit_EagerSimdUnbox_h */
|
|
@ -97,6 +97,17 @@
|
||||||
_(ObjectIs) \
|
_(ObjectIs) \
|
||||||
_(ObjectToString) \
|
_(ObjectToString) \
|
||||||
\
|
\
|
||||||
|
_(SimdInt32x4) \
|
||||||
|
_(SimdUint32x4) \
|
||||||
|
_(SimdInt16x8) \
|
||||||
|
_(SimdUint16x8) \
|
||||||
|
_(SimdInt8x16) \
|
||||||
|
_(SimdUint8x16) \
|
||||||
|
_(SimdFloat32x4) \
|
||||||
|
_(SimdBool32x4) \
|
||||||
|
_(SimdBool16x8) \
|
||||||
|
_(SimdBool8x16) \
|
||||||
|
\
|
||||||
_(TestBailout) \
|
_(TestBailout) \
|
||||||
_(TestAssertFloat32) \
|
_(TestAssertFloat32) \
|
||||||
_(TestAssertRecoveredOnBailout) \
|
_(TestAssertRecoveredOnBailout) \
|
||||||
|
|
|
@ -21,6 +21,7 @@
|
||||||
#include "jit/BaselineJIT.h"
|
#include "jit/BaselineJIT.h"
|
||||||
#include "jit/CacheIRSpewer.h"
|
#include "jit/CacheIRSpewer.h"
|
||||||
#include "jit/CodeGenerator.h"
|
#include "jit/CodeGenerator.h"
|
||||||
|
#include "jit/EagerSimdUnbox.h"
|
||||||
#include "jit/EdgeCaseAnalysis.h"
|
#include "jit/EdgeCaseAnalysis.h"
|
||||||
#include "jit/EffectiveAddressAnalysis.h"
|
#include "jit/EffectiveAddressAnalysis.h"
|
||||||
#include "jit/FoldLinearArithConstants.h"
|
#include "jit/FoldLinearArithConstants.h"
|
||||||
|
@ -443,6 +444,17 @@ JitRealm::performStubReadBarriers(uint32_t stubsToBarrier) const
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
JitRealm::performSIMDTemplateReadBarriers(uint32_t simdTemplatesToBarrier) const
|
||||||
|
{
|
||||||
|
while (simdTemplatesToBarrier) {
|
||||||
|
auto type = PopNextBitmaskValue<SimdType>(&simdTemplatesToBarrier);
|
||||||
|
const ReadBarrieredObject& tpl = simdTemplateObjects_[type];
|
||||||
|
MOZ_ASSERT(tpl);
|
||||||
|
tpl.get();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
JitZone::init(JSContext* cx)
|
JitZone::init(JSContext* cx)
|
||||||
{
|
{
|
||||||
|
@ -637,6 +649,11 @@ JitRealm::sweep(JS::Realm* realm)
|
||||||
if (stub && IsAboutToBeFinalized(&stub))
|
if (stub && IsAboutToBeFinalized(&stub))
|
||||||
stub.set(nullptr);
|
stub.set(nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (ReadBarrieredObject& obj : simdTemplateObjects_) {
|
||||||
|
if (obj && IsAboutToBeFinalized(&obj))
|
||||||
|
obj.set(nullptr);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
@ -1467,6 +1484,17 @@ OptimizeMIR(MIRGenerator* mir)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!JitOptions.disableRecoverIns && mir->optimizationInfo().eagerSimdUnboxEnabled()) {
|
||||||
|
AutoTraceLog log(logger, TraceLogger_EagerSimdUnbox);
|
||||||
|
if (!EagerSimdUnbox(mir, graph))
|
||||||
|
return false;
|
||||||
|
gs.spewPass("Eager Simd Unbox");
|
||||||
|
AssertGraphCoherency(graph);
|
||||||
|
|
||||||
|
if (mir->shouldCancel("Eager Simd Unbox"))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (mir->optimizationInfo().amaEnabled()) {
|
if (mir->optimizationInfo().amaEnabled()) {
|
||||||
AutoTraceLog log(logger, TraceLogger_AlignmentMaskAnalysis);
|
AutoTraceLog log(logger, TraceLogger_AlignmentMaskAnalysis);
|
||||||
AlignmentMaskAnalysis ama(graph);
|
AlignmentMaskAnalysis ama(graph);
|
||||||
|
|
|
@ -7902,6 +7902,11 @@ IonBuilder::getElemTryTypedObject(bool* emitted, MDefinition* obj, MDefinition*
|
||||||
return Ok();
|
return Ok();
|
||||||
|
|
||||||
switch (elemPrediction.kind()) {
|
switch (elemPrediction.kind()) {
|
||||||
|
case type::Simd:
|
||||||
|
// FIXME (bug 894105): load into a MIRType::float32x4 etc
|
||||||
|
trackOptimizationOutcome(TrackedOutcome::GenericFailure);
|
||||||
|
return Ok();
|
||||||
|
|
||||||
case type::Struct:
|
case type::Struct:
|
||||||
case type::Array:
|
case type::Array:
|
||||||
return getElemTryComplexElemOfTypedObject(emitted,
|
return getElemTryComplexElemOfTypedObject(emitted,
|
||||||
|
@ -8959,6 +8964,11 @@ IonBuilder::setElemTryTypedObject(bool* emitted, MDefinition* obj,
|
||||||
return Ok();
|
return Ok();
|
||||||
|
|
||||||
switch (elemPrediction.kind()) {
|
switch (elemPrediction.kind()) {
|
||||||
|
case type::Simd:
|
||||||
|
// FIXME (bug 894105): store a MIRType::float32x4 etc
|
||||||
|
trackOptimizationOutcome(TrackedOutcome::GenericFailure);
|
||||||
|
return Ok();
|
||||||
|
|
||||||
case type::Reference:
|
case type::Reference:
|
||||||
return setElemTryReferenceElemOfTypedObject(emitted, obj, index,
|
return setElemTryReferenceElemOfTypedObject(emitted, obj, index,
|
||||||
objPrediction, value, elemPrediction);
|
objPrediction, value, elemPrediction);
|
||||||
|
@ -10586,6 +10596,10 @@ IonBuilder::getPropTryTypedObject(bool* emitted,
|
||||||
return Ok();
|
return Ok();
|
||||||
|
|
||||||
switch (fieldPrediction.kind()) {
|
switch (fieldPrediction.kind()) {
|
||||||
|
case type::Simd:
|
||||||
|
// FIXME (bug 894104): load into a MIRType::float32x4 etc
|
||||||
|
return Ok();
|
||||||
|
|
||||||
case type::Struct:
|
case type::Struct:
|
||||||
case type::Array:
|
case type::Array:
|
||||||
return getPropTryComplexPropOfTypedObject(emitted,
|
return getPropTryComplexPropOfTypedObject(emitted,
|
||||||
|
@ -11728,6 +11742,10 @@ IonBuilder::setPropTryTypedObject(bool* emitted, MDefinition* obj,
|
||||||
return Ok();
|
return Ok();
|
||||||
|
|
||||||
switch (fieldPrediction.kind()) {
|
switch (fieldPrediction.kind()) {
|
||||||
|
case type::Simd:
|
||||||
|
// FIXME (bug 894104): store into a MIRType::float32x4 etc
|
||||||
|
return Ok();
|
||||||
|
|
||||||
case type::Reference:
|
case type::Reference:
|
||||||
return setPropTryReferencePropOfTypedObject(emitted, obj, fieldOffset,
|
return setPropTryReferencePropOfTypedObject(emitted, obj, fieldOffset,
|
||||||
value, fieldPrediction, name);
|
value, fieldPrediction, name);
|
||||||
|
|
|
@ -730,6 +730,51 @@ class IonBuilder
|
||||||
InliningResult inlineSetTypedObjectOffset(CallInfo& callInfo);
|
InliningResult inlineSetTypedObjectOffset(CallInfo& callInfo);
|
||||||
InliningResult inlineConstructTypedObject(CallInfo& callInfo, TypeDescr* target);
|
InliningResult inlineConstructTypedObject(CallInfo& callInfo, TypeDescr* target);
|
||||||
|
|
||||||
|
// SIMD intrinsics and natives.
|
||||||
|
InliningResult inlineConstructSimdObject(CallInfo& callInfo, SimdTypeDescr* target);
|
||||||
|
|
||||||
|
// SIMD helpers.
|
||||||
|
bool canInlineSimd(CallInfo& callInfo, JSNative native, unsigned numArgs,
|
||||||
|
InlineTypedObject** templateObj);
|
||||||
|
MDefinition* unboxSimd(MDefinition* ins, SimdType type);
|
||||||
|
InliningResult boxSimd(CallInfo& callInfo, MDefinition* ins, InlineTypedObject* templateObj);
|
||||||
|
MDefinition* convertToBooleanSimdLane(MDefinition* scalar);
|
||||||
|
|
||||||
|
InliningResult inlineSimd(CallInfo& callInfo, JSFunction* target, SimdType type);
|
||||||
|
|
||||||
|
InliningResult inlineSimdBinaryArith(CallInfo& callInfo, JSNative native,
|
||||||
|
MSimdBinaryArith::Operation op, SimdType type);
|
||||||
|
InliningResult inlineSimdBinaryBitwise(CallInfo& callInfo, JSNative native,
|
||||||
|
MSimdBinaryBitwise::Operation op, SimdType type);
|
||||||
|
InliningResult inlineSimdBinarySaturating(CallInfo& callInfo, JSNative native,
|
||||||
|
MSimdBinarySaturating::Operation op, SimdType type);
|
||||||
|
InliningResult inlineSimdShift(CallInfo& callInfo, JSNative native, MSimdShift::Operation op,
|
||||||
|
SimdType type);
|
||||||
|
InliningResult inlineSimdComp(CallInfo& callInfo, JSNative native,
|
||||||
|
MSimdBinaryComp::Operation op, SimdType type);
|
||||||
|
InliningResult inlineSimdUnary(CallInfo& callInfo, JSNative native,
|
||||||
|
MSimdUnaryArith::Operation op, SimdType type);
|
||||||
|
InliningResult inlineSimdExtractLane(CallInfo& callInfo, JSNative native, SimdType type);
|
||||||
|
InliningResult inlineSimdReplaceLane(CallInfo& callInfo, JSNative native, SimdType type);
|
||||||
|
InliningResult inlineSimdSplat(CallInfo& callInfo, JSNative native, SimdType type);
|
||||||
|
InliningResult inlineSimdShuffle(CallInfo& callInfo, JSNative native, SimdType type,
|
||||||
|
unsigned numVectors);
|
||||||
|
InliningResult inlineSimdCheck(CallInfo& callInfo, JSNative native, SimdType type);
|
||||||
|
InliningResult inlineSimdConvert(CallInfo& callInfo, JSNative native, bool isCast,
|
||||||
|
SimdType from, SimdType to);
|
||||||
|
InliningResult inlineSimdSelect(CallInfo& callInfo, JSNative native, SimdType type);
|
||||||
|
|
||||||
|
bool prepareForSimdLoadStore(CallInfo& callInfo, Scalar::Type simdType,
|
||||||
|
MInstruction** elements, MDefinition** index,
|
||||||
|
Scalar::Type* arrayType);
|
||||||
|
InliningResult inlineSimdLoad(CallInfo& callInfo, JSNative native, SimdType type,
|
||||||
|
unsigned numElems);
|
||||||
|
InliningResult inlineSimdStore(CallInfo& callInfo, JSNative native, SimdType type,
|
||||||
|
unsigned numElems);
|
||||||
|
|
||||||
|
InliningResult inlineSimdAnyAllTrue(CallInfo& callInfo, bool IsAllTrue, JSNative native,
|
||||||
|
SimdType type);
|
||||||
|
|
||||||
// Utility intrinsics.
|
// Utility intrinsics.
|
||||||
InliningResult inlineIsCallable(CallInfo& callInfo);
|
InliningResult inlineIsCallable(CallInfo& callInfo);
|
||||||
InliningResult inlineIsConstructor(CallInfo& callInfo);
|
InliningResult inlineIsConstructor(CallInfo& callInfo);
|
||||||
|
|
|
@ -27,6 +27,7 @@ OptimizationInfo::initNormalOptimizationInfo()
|
||||||
|
|
||||||
autoTruncate_ = true;
|
autoTruncate_ = true;
|
||||||
eaa_ = true;
|
eaa_ = true;
|
||||||
|
eagerSimdUnbox_ = true;
|
||||||
edgeCaseAnalysis_ = true;
|
edgeCaseAnalysis_ = true;
|
||||||
eliminateRedundantChecks_ = true;
|
eliminateRedundantChecks_ = true;
|
||||||
inlineInterpreted_ = true;
|
inlineInterpreted_ = true;
|
||||||
|
@ -68,6 +69,7 @@ OptimizationInfo::initWasmOptimizationInfo()
|
||||||
|
|
||||||
ama_ = true;
|
ama_ = true;
|
||||||
autoTruncate_ = false;
|
autoTruncate_ = false;
|
||||||
|
eagerSimdUnbox_ = false; // wasm has no boxing / unboxing.
|
||||||
edgeCaseAnalysis_ = false;
|
edgeCaseAnalysis_ = false;
|
||||||
eliminateRedundantChecks_ = false;
|
eliminateRedundantChecks_ = false;
|
||||||
scalarReplacement_ = false; // wasm has no objects.
|
scalarReplacement_ = false; // wasm has no objects.
|
||||||
|
|
|
@ -65,6 +65,9 @@ class OptimizationInfo
|
||||||
// Toggles whether native scripts get inlined.
|
// Toggles whether native scripts get inlined.
|
||||||
bool inlineNative_;
|
bool inlineNative_;
|
||||||
|
|
||||||
|
// Toggles whether eager unboxing of SIMD is used.
|
||||||
|
bool eagerSimdUnbox_;
|
||||||
|
|
||||||
// Toggles whether global value numbering is used.
|
// Toggles whether global value numbering is used.
|
||||||
bool gvn_;
|
bool gvn_;
|
||||||
|
|
||||||
|
@ -154,6 +157,7 @@ class OptimizationInfo
|
||||||
eliminateRedundantChecks_(false),
|
eliminateRedundantChecks_(false),
|
||||||
inlineInterpreted_(false),
|
inlineInterpreted_(false),
|
||||||
inlineNative_(false),
|
inlineNative_(false),
|
||||||
|
eagerSimdUnbox_(false),
|
||||||
gvn_(false),
|
gvn_(false),
|
||||||
licm_(false),
|
licm_(false),
|
||||||
rangeAnalysis_(false),
|
rangeAnalysis_(false),
|
||||||
|
@ -194,6 +198,10 @@ class OptimizationInfo
|
||||||
|
|
||||||
uint32_t compilerWarmUpThreshold(JSScript* script, jsbytecode* pc = nullptr) const;
|
uint32_t compilerWarmUpThreshold(JSScript* script, jsbytecode* pc = nullptr) const;
|
||||||
|
|
||||||
|
bool eagerSimdUnboxEnabled() const {
|
||||||
|
return eagerSimdUnbox_ && !JitOptions.disableEagerSimdUnbox;
|
||||||
|
}
|
||||||
|
|
||||||
bool gvnEnabled() const {
|
bool gvnEnabled() const {
|
||||||
return gvn_ && !JitOptions.disableGvn;
|
return gvn_ && !JitOptions.disableGvn;
|
||||||
}
|
}
|
||||||
|
|
|
@ -104,6 +104,9 @@ enum BailoutKind
|
||||||
Bailout_NonStringInput,
|
Bailout_NonStringInput,
|
||||||
Bailout_NonSymbolInput,
|
Bailout_NonSymbolInput,
|
||||||
|
|
||||||
|
// SIMD Unbox expects a given type, bails out if it doesn't match.
|
||||||
|
Bailout_UnexpectedSimdInput,
|
||||||
|
|
||||||
// Atomic operations require shared memory, bail out if the typed array
|
// Atomic operations require shared memory, bail out if the typed array
|
||||||
// maps unshared memory.
|
// maps unshared memory.
|
||||||
Bailout_NonSharedTypedArrayInput,
|
Bailout_NonSharedTypedArrayInput,
|
||||||
|
@ -205,6 +208,8 @@ BailoutKindString(BailoutKind kind)
|
||||||
return "Bailout_NonStringInput";
|
return "Bailout_NonStringInput";
|
||||||
case Bailout_NonSymbolInput:
|
case Bailout_NonSymbolInput:
|
||||||
return "Bailout_NonSymbolInput";
|
return "Bailout_NonSymbolInput";
|
||||||
|
case Bailout_UnexpectedSimdInput:
|
||||||
|
return "Bailout_UnexpectedSimdInput";
|
||||||
case Bailout_NonSharedTypedArrayInput:
|
case Bailout_NonSharedTypedArrayInput:
|
||||||
return "Bailout_NonSharedTypedArrayInput";
|
return "Bailout_NonSharedTypedArrayInput";
|
||||||
case Bailout_Debugger:
|
case Bailout_Debugger:
|
||||||
|
@ -247,19 +252,6 @@ static const uint32_t VECTOR_SCALE_BITS = 3;
|
||||||
static const uint32_t VECTOR_SCALE_SHIFT = ELEMENT_TYPE_BITS + ELEMENT_TYPE_SHIFT;
|
static const uint32_t VECTOR_SCALE_SHIFT = ELEMENT_TYPE_BITS + ELEMENT_TYPE_SHIFT;
|
||||||
static const uint32_t VECTOR_SCALE_MASK = (1 << VECTOR_SCALE_BITS) - 1;
|
static const uint32_t VECTOR_SCALE_MASK = (1 << VECTOR_SCALE_BITS) - 1;
|
||||||
|
|
||||||
// The integer SIMD types have a lot of operations that do the exact same thing
|
|
||||||
// for signed and unsigned integer types. Sometimes it is simpler to treat
|
|
||||||
// signed and unsigned integer SIMD types as the same type, using a SimdSign to
|
|
||||||
// distinguish the few cases where there is a difference.
|
|
||||||
enum class SimdSign {
|
|
||||||
// Signedness is not applicable to this type. (i.e., Float or Bool).
|
|
||||||
NotApplicable,
|
|
||||||
// Treat as an unsigned integer with a range 0 .. 2^N-1.
|
|
||||||
Unsigned,
|
|
||||||
// Treat as a signed integer in two's complement encoding.
|
|
||||||
Signed,
|
|
||||||
};
|
|
||||||
|
|
||||||
class SimdConstant {
|
class SimdConstant {
|
||||||
public:
|
public:
|
||||||
enum Type {
|
enum Type {
|
||||||
|
@ -465,6 +457,39 @@ IsSimdType(MIRType type)
|
||||||
return ((unsigned(type) >> VECTOR_SCALE_SHIFT) & VECTOR_SCALE_MASK) != 0;
|
return ((unsigned(type) >> VECTOR_SCALE_SHIFT) & VECTOR_SCALE_MASK) != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Returns the number of vector elements (hereby called "length") for a given
|
||||||
|
// SIMD kind. It is the Y part of the name "Foo x Y".
|
||||||
|
static inline unsigned
|
||||||
|
SimdTypeToLength(MIRType type)
|
||||||
|
{
|
||||||
|
MOZ_ASSERT(IsSimdType(type));
|
||||||
|
return 1 << ((unsigned(type) >> VECTOR_SCALE_SHIFT) & VECTOR_SCALE_MASK);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the type of the individual lanes in a SIMD type.
|
||||||
|
// For example, Int32x4 -> Int32, Float32x4 -> Float32 etc.
|
||||||
|
static inline MIRType
|
||||||
|
SimdTypeToLaneType(MIRType type)
|
||||||
|
{
|
||||||
|
MOZ_ASSERT(IsSimdType(type));
|
||||||
|
static_assert(unsigned(MIRType::Last) <= ELEMENT_TYPE_MASK,
|
||||||
|
"ELEMENT_TYPE_MASK should be larger than the last MIRType");
|
||||||
|
return MIRType((unsigned(type) >> ELEMENT_TYPE_SHIFT) & ELEMENT_TYPE_MASK);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the type expected when inserting a lane into a SIMD type.
|
||||||
|
// This is the argument type expected by the MSimdValue constructors as well as
|
||||||
|
// MSimdSplat and MSimdInsertElement.
|
||||||
|
static inline MIRType
|
||||||
|
SimdTypeToLaneArgumentType(MIRType type)
|
||||||
|
{
|
||||||
|
MIRType laneType = SimdTypeToLaneType(type);
|
||||||
|
|
||||||
|
// Boolean lanes should be pre-converted to an Int32 with the values 0 or -1.
|
||||||
|
// All other lane types are inserted directly.
|
||||||
|
return laneType == MIRType::Boolean ? MIRType::Int32 : laneType;
|
||||||
|
}
|
||||||
|
|
||||||
static inline MIRType
|
static inline MIRType
|
||||||
MIRTypeFromValueType(JSValueType type)
|
MIRTypeFromValueType(JSValueType type)
|
||||||
{
|
{
|
||||||
|
@ -664,6 +689,24 @@ IsNullOrUndefined(MIRType type)
|
||||||
return type == MIRType::Null || type == MIRType::Undefined;
|
return type == MIRType::Null || type == MIRType::Undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline bool
|
||||||
|
IsFloatingPointSimdType(MIRType type)
|
||||||
|
{
|
||||||
|
return type == MIRType::Float32x4;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline bool
|
||||||
|
IsIntegerSimdType(MIRType type)
|
||||||
|
{
|
||||||
|
return IsSimdType(type) && SimdTypeToLaneType(type) == MIRType::Int32;
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline bool
|
||||||
|
IsBooleanSimdType(MIRType type)
|
||||||
|
{
|
||||||
|
return IsSimdType(type) && SimdTypeToLaneType(type) == MIRType::Boolean;
|
||||||
|
}
|
||||||
|
|
||||||
static inline bool
|
static inline bool
|
||||||
IsMagicType(MIRType type)
|
IsMagicType(MIRType type)
|
||||||
{
|
{
|
||||||
|
@ -692,10 +735,18 @@ ScalarTypeToMIRType(Scalar::Type type)
|
||||||
return MIRType::Float32;
|
return MIRType::Float32;
|
||||||
case Scalar::Float64:
|
case Scalar::Float64:
|
||||||
return MIRType::Double;
|
return MIRType::Double;
|
||||||
|
case Scalar::Float32x4:
|
||||||
|
return MIRType::Float32x4;
|
||||||
|
case Scalar::Int8x16:
|
||||||
|
return MIRType::Int8x16;
|
||||||
|
case Scalar::Int16x8:
|
||||||
|
return MIRType::Int16x8;
|
||||||
|
case Scalar::Int32x4:
|
||||||
|
return MIRType::Int32x4;
|
||||||
case Scalar::MaxTypedArrayViewType:
|
case Scalar::MaxTypedArrayViewType:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
MOZ_CRASH("unexpected kind");
|
MOZ_CRASH("unexpected SIMD kind");
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline unsigned
|
static inline unsigned
|
||||||
|
@ -713,10 +764,17 @@ ScalarTypeToLength(Scalar::Type type)
|
||||||
case Scalar::Float64:
|
case Scalar::Float64:
|
||||||
case Scalar::Uint8Clamped:
|
case Scalar::Uint8Clamped:
|
||||||
return 1;
|
return 1;
|
||||||
|
case Scalar::Float32x4:
|
||||||
|
case Scalar::Int32x4:
|
||||||
|
return 4;
|
||||||
|
case Scalar::Int16x8:
|
||||||
|
return 8;
|
||||||
|
case Scalar::Int8x16:
|
||||||
|
return 16;
|
||||||
case Scalar::MaxTypedArrayViewType:
|
case Scalar::MaxTypedArrayViewType:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
MOZ_CRASH("unexpected kind");
|
MOZ_CRASH("unexpected SIMD kind");
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline const char*
|
static inline const char*
|
||||||
|
|
|
@ -407,7 +407,9 @@ struct MaybeReadFallback
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
class RResumePoint;
|
class RResumePoint;
|
||||||
|
class RSimdBox;
|
||||||
|
|
||||||
// Reads frame information in snapshot-encoding order (that is, outermost frame
|
// Reads frame information in snapshot-encoding order (that is, outermost frame
|
||||||
// to innermost frame).
|
// to innermost frame).
|
||||||
|
@ -469,6 +471,7 @@ class SnapshotIterator
|
||||||
void warnUnreadableAllocation();
|
void warnUnreadableAllocation();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
friend class RSimdBox;
|
||||||
const FloatRegisters::RegisterContent* floatAllocationPointer(const RValueAllocation& a) const;
|
const FloatRegisters::RegisterContent* floatAllocationPointer(const RValueAllocation& a) const;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
|
@ -86,6 +86,9 @@ DefaultJitOptions::DefaultJitOptions()
|
||||||
// Toggles whether Effective Address Analysis is globally disabled.
|
// Toggles whether Effective Address Analysis is globally disabled.
|
||||||
SET_DEFAULT(disableEaa, false);
|
SET_DEFAULT(disableEaa, false);
|
||||||
|
|
||||||
|
// Toggle whether eager simd unboxing is globally disabled.
|
||||||
|
SET_DEFAULT(disableEagerSimdUnbox, false);
|
||||||
|
|
||||||
// Toggles whether Edge Case Analysis is gobally disabled.
|
// Toggles whether Edge Case Analysis is gobally disabled.
|
||||||
SET_DEFAULT(disableEdgeCaseAnalysis, false);
|
SET_DEFAULT(disableEdgeCaseAnalysis, false);
|
||||||
|
|
||||||
|
|
|
@ -50,6 +50,7 @@ struct DefaultJitOptions
|
||||||
bool disableInlineBacktracking;
|
bool disableInlineBacktracking;
|
||||||
bool disableAma;
|
bool disableAma;
|
||||||
bool disableEaa;
|
bool disableEaa;
|
||||||
|
bool disableEagerSimdUnbox;
|
||||||
bool disableEdgeCaseAnalysis;
|
bool disableEdgeCaseAnalysis;
|
||||||
bool disableGvn;
|
bool disableGvn;
|
||||||
bool disableInlining;
|
bool disableInlining;
|
||||||
|
|
|
@ -507,6 +507,10 @@ class JitRealm
|
||||||
|
|
||||||
mozilla::EnumeratedArray<StubIndex, StubIndex::Count, ReadBarrieredJitCode> stubs_;
|
mozilla::EnumeratedArray<StubIndex, StubIndex::Count, ReadBarrieredJitCode> stubs_;
|
||||||
|
|
||||||
|
// The same approach is taken for SIMD template objects.
|
||||||
|
|
||||||
|
mozilla::EnumeratedArray<SimdType, SimdType::Count, ReadBarrieredObject> simdTemplateObjects_;
|
||||||
|
|
||||||
JitCode* generateStringConcatStub(JSContext* cx);
|
JitCode* generateStringConcatStub(JSContext* cx);
|
||||||
JitCode* generateRegExpMatcherStub(JSContext* cx);
|
JitCode* generateRegExpMatcherStub(JSContext* cx);
|
||||||
JitCode* generateRegExpSearcherStub(JSContext* cx);
|
JitCode* generateRegExpSearcherStub(JSContext* cx);
|
||||||
|
@ -519,6 +523,23 @@ class JitRealm
|
||||||
}
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
JSObject* getSimdTemplateObjectFor(JSContext* cx, Handle<SimdTypeDescr*> descr) {
|
||||||
|
ReadBarrieredObject& tpl = simdTemplateObjects_[descr->type()];
|
||||||
|
if (!tpl)
|
||||||
|
tpl.set(TypedObject::createZeroed(cx, descr, gc::TenuredHeap));
|
||||||
|
return tpl.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
JSObject* maybeGetSimdTemplateObjectFor(SimdType type) const {
|
||||||
|
// This function is used by Eager Simd Unbox phase which can run
|
||||||
|
// off-thread, so we cannot use the usual read barrier. For more
|
||||||
|
// information, see the comment above
|
||||||
|
// CodeGenerator::simdRefreshTemplatesDuringLink_.
|
||||||
|
|
||||||
|
MOZ_ASSERT(CurrentThreadIsIonCompiling());
|
||||||
|
return simdTemplateObjects_[type].unbarrieredGet();
|
||||||
|
}
|
||||||
|
|
||||||
JitCode* getStubCode(uint32_t key) {
|
JitCode* getStubCode(uint32_t key) {
|
||||||
ICStubCodeMap::Ptr p = stubCodes_->lookup(key);
|
ICStubCodeMap::Ptr p = stubCodes_->lookup(key);
|
||||||
if (p)
|
if (p)
|
||||||
|
@ -599,13 +620,15 @@ class JitRealm
|
||||||
return stubs_[RegExpTester];
|
return stubs_[RegExpTester];
|
||||||
}
|
}
|
||||||
|
|
||||||
// Perform the necessary read barriers on stubs described by the bitmasks
|
// Perform the necessary read barriers on stubs and SIMD template object
|
||||||
// passed in. This function can only be called from the main thread.
|
// described by the bitmasks passed in. This function can only be called
|
||||||
|
// from the main thread.
|
||||||
//
|
//
|
||||||
// The stub pointers must still be valid by the time these methods are
|
// The stub and template object pointers must still be valid by the time
|
||||||
// called. This is arranged by cancelling off-thread Ion compilation at the
|
// these methods are called. This is arranged by cancelling off-thread Ion
|
||||||
// start of GC and at the start of sweeping.
|
// compilation at the start of GC and at the start of sweeping.
|
||||||
void performStubReadBarriers(uint32_t stubsToBarrier) const;
|
void performStubReadBarriers(uint32_t stubsToBarrier) const;
|
||||||
|
void performSIMDTemplateReadBarriers(uint32_t simdTemplatesToBarrier) const;
|
||||||
|
|
||||||
size_t sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const;
|
size_t sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const;
|
||||||
|
|
||||||
|
|
|
@ -3689,7 +3689,8 @@ LIRGenerator::visitLoadUnboxedScalar(MLoadUnboxedScalar* ins)
|
||||||
const LUse elements = useRegister(ins->elements());
|
const LUse elements = useRegister(ins->elements());
|
||||||
const LAllocation index = useRegisterOrConstant(ins->index());
|
const LAllocation index = useRegisterOrConstant(ins->index());
|
||||||
|
|
||||||
MOZ_ASSERT(IsNumberType(ins->type()) || ins->type() == MIRType::Boolean);
|
MOZ_ASSERT(IsNumberType(ins->type()) || IsSimdType(ins->type()) ||
|
||||||
|
ins->type() == MIRType::Boolean);
|
||||||
|
|
||||||
// We need a temp register for Uint32Array with known double result.
|
// We need a temp register for Uint32Array with known double result.
|
||||||
LDefinition tempDef = LDefinition::BogusTemp();
|
LDefinition tempDef = LDefinition::BogusTemp();
|
||||||
|
@ -3770,7 +3771,12 @@ LIRGenerator::visitStoreUnboxedScalar(MStoreUnboxedScalar* ins)
|
||||||
MOZ_ASSERT(IsValidElementsType(ins->elements(), ins->offsetAdjustment()));
|
MOZ_ASSERT(IsValidElementsType(ins->elements(), ins->offsetAdjustment()));
|
||||||
MOZ_ASSERT(ins->index()->type() == MIRType::Int32);
|
MOZ_ASSERT(ins->index()->type() == MIRType::Int32);
|
||||||
|
|
||||||
if (ins->isFloatWrite()) {
|
if (ins->isSimdWrite()) {
|
||||||
|
MOZ_ASSERT_IF(ins->writeType() == Scalar::Float32x4, ins->value()->type() == MIRType::Float32x4);
|
||||||
|
MOZ_ASSERT_IF(ins->writeType() == Scalar::Int8x16, ins->value()->type() == MIRType::Int8x16);
|
||||||
|
MOZ_ASSERT_IF(ins->writeType() == Scalar::Int16x8, ins->value()->type() == MIRType::Int16x8);
|
||||||
|
MOZ_ASSERT_IF(ins->writeType() == Scalar::Int32x4, ins->value()->type() == MIRType::Int32x4);
|
||||||
|
} else if (ins->isFloatWrite()) {
|
||||||
MOZ_ASSERT_IF(ins->writeType() == Scalar::Float32, ins->value()->type() == MIRType::Float32);
|
MOZ_ASSERT_IF(ins->writeType() == Scalar::Float32, ins->value()->type() == MIRType::Float32);
|
||||||
MOZ_ASSERT_IF(ins->writeType() == Scalar::Float64, ins->value()->type() == MIRType::Double);
|
MOZ_ASSERT_IF(ins->writeType() == Scalar::Float64, ins->value()->type() == MIRType::Double);
|
||||||
} else {
|
} else {
|
||||||
|
@ -4704,7 +4710,7 @@ LIRGenerator::visitWasmParameter(MWasmParameter* ins)
|
||||||
#endif
|
#endif
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
MOZ_ASSERT(IsNumberType(ins->type()));
|
MOZ_ASSERT(IsNumberType(ins->type()) || IsSimdType(ins->type()));
|
||||||
defineFixed(new(alloc()) LWasmParameter, ins, LArgument(abi.offsetFromArgBase()));
|
defineFixed(new(alloc()) LWasmParameter, ins, LArgument(abi.offsetFromArgBase()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4724,6 +4730,8 @@ LIRGenerator::visitWasmReturn(MWasmReturn* ins)
|
||||||
lir->setOperand(0, useFixed(rval, ReturnFloat32Reg));
|
lir->setOperand(0, useFixed(rval, ReturnFloat32Reg));
|
||||||
else if (rval->type() == MIRType::Double)
|
else if (rval->type() == MIRType::Double)
|
||||||
lir->setOperand(0, useFixed(rval, ReturnDoubleReg));
|
lir->setOperand(0, useFixed(rval, ReturnDoubleReg));
|
||||||
|
else if (IsSimdType(rval->type()))
|
||||||
|
lir->setOperand(0, useFixed(rval, ReturnSimd128Reg));
|
||||||
else if (rval->type() == MIRType::Int32)
|
else if (rval->type() == MIRType::Int32)
|
||||||
lir->setOperand(0, useFixed(rval, ReturnReg));
|
lir->setOperand(0, useFixed(rval, ReturnReg));
|
||||||
else
|
else
|
||||||
|
@ -4743,7 +4751,7 @@ LIRGenerator::visitWasmStackArg(MWasmStackArg* ins)
|
||||||
{
|
{
|
||||||
if (ins->arg()->type() == MIRType::Int64) {
|
if (ins->arg()->type() == MIRType::Int64) {
|
||||||
add(new(alloc()) LWasmStackArgI64(useInt64RegisterOrConstantAtStart(ins->arg())), ins);
|
add(new(alloc()) LWasmStackArgI64(useInt64RegisterOrConstantAtStart(ins->arg())), ins);
|
||||||
} else if (IsFloatingPointType(ins->arg()->type())) {
|
} else if (IsFloatingPointType(ins->arg()->type()) || IsSimdType(ins->arg()->type())) {
|
||||||
MOZ_ASSERT(!ins->arg()->isEmittedAtUses());
|
MOZ_ASSERT(!ins->arg()->isEmittedAtUses());
|
||||||
add(new(alloc()) LWasmStackArg(useRegisterAtStart(ins->arg())), ins);
|
add(new(alloc()) LWasmStackArg(useRegisterAtStart(ins->arg())), ins);
|
||||||
} else {
|
} else {
|
||||||
|
@ -4878,6 +4886,217 @@ LIRGenerator::visitRecompileCheck(MRecompileCheck* ins)
|
||||||
assignSafepoint(lir, ins);
|
assignSafepoint(lir, ins);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
LIRGenerator::visitSimdBox(MSimdBox* ins)
|
||||||
|
{
|
||||||
|
MOZ_ASSERT(IsSimdType(ins->input()->type()));
|
||||||
|
LUse in = useRegister(ins->input());
|
||||||
|
LSimdBox* lir = new(alloc()) LSimdBox(in, temp());
|
||||||
|
define(lir, ins);
|
||||||
|
assignSafepoint(lir, ins);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
LIRGenerator::visitSimdUnbox(MSimdUnbox* ins)
|
||||||
|
{
|
||||||
|
MOZ_ASSERT(ins->input()->type() == MIRType::Object);
|
||||||
|
MOZ_ASSERT(IsSimdType(ins->type()));
|
||||||
|
LUse in = useRegister(ins->input());
|
||||||
|
LSimdUnbox* lir = new(alloc()) LSimdUnbox(in, temp());
|
||||||
|
assignSnapshot(lir, Bailout_UnexpectedSimdInput);
|
||||||
|
define(lir, ins);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
LIRGenerator::visitSimdConstant(MSimdConstant* ins)
|
||||||
|
{
|
||||||
|
MOZ_ASSERT(IsSimdType(ins->type()));
|
||||||
|
|
||||||
|
switch (ins->type()) {
|
||||||
|
case MIRType::Int8x16:
|
||||||
|
case MIRType::Int16x8:
|
||||||
|
case MIRType::Int32x4:
|
||||||
|
case MIRType::Bool8x16:
|
||||||
|
case MIRType::Bool16x8:
|
||||||
|
case MIRType::Bool32x4:
|
||||||
|
define(new(alloc()) LSimd128Int(), ins);
|
||||||
|
break;
|
||||||
|
case MIRType::Float32x4:
|
||||||
|
define(new(alloc()) LSimd128Float(), ins);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
MOZ_CRASH("Unknown SIMD kind when generating constant");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
LIRGenerator::visitSimdConvert(MSimdConvert* ins)
|
||||||
|
{
|
||||||
|
MOZ_ASSERT(IsSimdType(ins->type()));
|
||||||
|
MDefinition* input = ins->input();
|
||||||
|
LUse use = useRegister(input);
|
||||||
|
if (ins->type() == MIRType::Int32x4) {
|
||||||
|
MOZ_ASSERT(input->type() == MIRType::Float32x4);
|
||||||
|
switch (ins->signedness()) {
|
||||||
|
case SimdSign::Signed: {
|
||||||
|
LFloat32x4ToInt32x4* lir = new(alloc()) LFloat32x4ToInt32x4(use, temp());
|
||||||
|
if (!gen->compilingWasm())
|
||||||
|
assignSnapshot(lir, Bailout_BoundsCheck);
|
||||||
|
define(lir, ins);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case SimdSign::Unsigned: {
|
||||||
|
LFloat32x4ToUint32x4* lir =
|
||||||
|
new (alloc()) LFloat32x4ToUint32x4(use, temp(), temp(LDefinition::SIMD128INT));
|
||||||
|
if (!gen->compilingWasm())
|
||||||
|
assignSnapshot(lir, Bailout_BoundsCheck);
|
||||||
|
define(lir, ins);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
MOZ_CRASH("Unexpected SimdConvert sign");
|
||||||
|
}
|
||||||
|
} else if (ins->type() == MIRType::Float32x4) {
|
||||||
|
MOZ_ASSERT(input->type() == MIRType::Int32x4);
|
||||||
|
MOZ_ASSERT(ins->signedness() == SimdSign::Signed, "Unexpected SimdConvert sign");
|
||||||
|
define(new(alloc()) LInt32x4ToFloat32x4(use), ins);
|
||||||
|
} else {
|
||||||
|
MOZ_CRASH("Unknown SIMD kind when generating constant");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
LIRGenerator::visitSimdReinterpretCast(MSimdReinterpretCast* ins)
|
||||||
|
{
|
||||||
|
MOZ_ASSERT(IsSimdType(ins->type()) && IsSimdType(ins->input()->type()));
|
||||||
|
MDefinition* input = ins->input();
|
||||||
|
LUse use = useRegisterAtStart(input);
|
||||||
|
// :TODO: (Bug 1132894) We have to allocate a different register as redefine
|
||||||
|
// and/or defineReuseInput are not yet capable of reusing the same register
|
||||||
|
// with a different register type.
|
||||||
|
define(new(alloc()) LSimdReinterpretCast(use), ins);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
LIRGenerator::visitSimdAllTrue(MSimdAllTrue* ins)
|
||||||
|
{
|
||||||
|
MDefinition* input = ins->input();
|
||||||
|
MOZ_ASSERT(IsBooleanSimdType(input->type()));
|
||||||
|
|
||||||
|
LUse use = useRegisterAtStart(input);
|
||||||
|
define(new(alloc()) LSimdAllTrue(use), ins);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
LIRGenerator::visitSimdAnyTrue(MSimdAnyTrue* ins)
|
||||||
|
{
|
||||||
|
MDefinition* input = ins->input();
|
||||||
|
MOZ_ASSERT(IsBooleanSimdType(input->type()));
|
||||||
|
|
||||||
|
LUse use = useRegisterAtStart(input);
|
||||||
|
define(new(alloc()) LSimdAnyTrue(use), ins);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
LIRGenerator::visitSimdUnaryArith(MSimdUnaryArith* ins)
|
||||||
|
{
|
||||||
|
MOZ_ASSERT(IsSimdType(ins->input()->type()));
|
||||||
|
MOZ_ASSERT(IsSimdType(ins->type()));
|
||||||
|
|
||||||
|
// Cannot be at start, as the ouput is used as a temporary to store values.
|
||||||
|
LUse in = use(ins->input());
|
||||||
|
|
||||||
|
switch (ins->type()) {
|
||||||
|
case MIRType::Int8x16:
|
||||||
|
case MIRType::Bool8x16:
|
||||||
|
define(new (alloc()) LSimdUnaryArithIx16(in), ins);
|
||||||
|
break;
|
||||||
|
case MIRType::Int16x8:
|
||||||
|
case MIRType::Bool16x8:
|
||||||
|
define(new (alloc()) LSimdUnaryArithIx8(in), ins);
|
||||||
|
break;
|
||||||
|
case MIRType::Int32x4:
|
||||||
|
case MIRType::Bool32x4:
|
||||||
|
define(new (alloc()) LSimdUnaryArithIx4(in), ins);
|
||||||
|
break;
|
||||||
|
case MIRType::Float32x4:
|
||||||
|
define(new (alloc()) LSimdUnaryArithFx4(in), ins);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
MOZ_CRASH("Unknown SIMD kind for unary operation");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
LIRGenerator::visitSimdBinaryComp(MSimdBinaryComp* ins)
|
||||||
|
{
|
||||||
|
MOZ_ASSERT(IsSimdType(ins->lhs()->type()));
|
||||||
|
MOZ_ASSERT(IsSimdType(ins->rhs()->type()));
|
||||||
|
MOZ_ASSERT(IsBooleanSimdType(ins->type()));
|
||||||
|
|
||||||
|
if (ShouldReorderCommutative(ins->lhs(), ins->rhs(), ins))
|
||||||
|
ins->reverse();
|
||||||
|
|
||||||
|
switch (ins->specialization()) {
|
||||||
|
case MIRType::Int8x16: {
|
||||||
|
MOZ_ASSERT(ins->signedness() == SimdSign::Signed);
|
||||||
|
LSimdBinaryCompIx16* add = new (alloc()) LSimdBinaryCompIx16();
|
||||||
|
lowerForFPU(add, ins, ins->lhs(), ins->rhs());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
case MIRType::Int16x8: {
|
||||||
|
MOZ_ASSERT(ins->signedness() == SimdSign::Signed);
|
||||||
|
LSimdBinaryCompIx8* add = new (alloc()) LSimdBinaryCompIx8();
|
||||||
|
lowerForFPU(add, ins, ins->lhs(), ins->rhs());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
case MIRType::Int32x4: {
|
||||||
|
MOZ_ASSERT(ins->signedness() == SimdSign::Signed);
|
||||||
|
LSimdBinaryCompIx4* add = new (alloc()) LSimdBinaryCompIx4();
|
||||||
|
lowerForCompIx4(add, ins, ins->lhs(), ins->rhs());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
case MIRType::Float32x4: {
|
||||||
|
MOZ_ASSERT(ins->signedness() == SimdSign::NotApplicable);
|
||||||
|
LSimdBinaryCompFx4* add = new (alloc()) LSimdBinaryCompFx4();
|
||||||
|
lowerForCompFx4(add, ins, ins->lhs(), ins->rhs());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
MOZ_CRASH("Unknown compare type when comparing values");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
LIRGenerator::visitSimdBinaryBitwise(MSimdBinaryBitwise* ins)
|
||||||
|
{
|
||||||
|
MOZ_ASSERT(IsSimdType(ins->lhs()->type()));
|
||||||
|
MOZ_ASSERT(IsSimdType(ins->rhs()->type()));
|
||||||
|
MOZ_ASSERT(IsSimdType(ins->type()));
|
||||||
|
|
||||||
|
MDefinition* lhs = ins->lhs();
|
||||||
|
MDefinition* rhs = ins->rhs();
|
||||||
|
ReorderCommutative(&lhs, &rhs, ins);
|
||||||
|
LSimdBinaryBitwise* lir = new(alloc()) LSimdBinaryBitwise;
|
||||||
|
lowerForFPU(lir, ins, lhs, rhs);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
LIRGenerator::visitSimdShift(MSimdShift* ins)
|
||||||
|
{
|
||||||
|
MOZ_ASSERT(IsIntegerSimdType(ins->type()));
|
||||||
|
MOZ_ASSERT(ins->lhs()->type() == ins->type());
|
||||||
|
MOZ_ASSERT(ins->rhs()->type() == MIRType::Int32);
|
||||||
|
|
||||||
|
LUse vector = useRegisterAtStart(ins->lhs());
|
||||||
|
LAllocation value = useRegisterOrConstant(ins->rhs());
|
||||||
|
// We need a temp register to mask the shift amount, but not if the shift
|
||||||
|
// amount is a constant.
|
||||||
|
LDefinition tempReg = value.isConstant() ? LDefinition::BogusTemp() : temp();
|
||||||
|
LSimdShift* lir = new(alloc()) LSimdShift(vector, value, tempReg);
|
||||||
|
defineReuseInput(lir, ins, 0);
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
LIRGenerator::visitLexicalCheck(MLexicalCheck* ins)
|
LIRGenerator::visitLexicalCheck(MLexicalCheck* ins)
|
||||||
{
|
{
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
#include "builtin/intl/PluralRules.h"
|
#include "builtin/intl/PluralRules.h"
|
||||||
#include "builtin/intl/RelativeTimeFormat.h"
|
#include "builtin/intl/RelativeTimeFormat.h"
|
||||||
#include "builtin/MapObject.h"
|
#include "builtin/MapObject.h"
|
||||||
|
#include "builtin/SIMDConstants.h"
|
||||||
#include "builtin/String.h"
|
#include "builtin/String.h"
|
||||||
#include "builtin/TestingFunctions.h"
|
#include "builtin/TestingFunctions.h"
|
||||||
#include "builtin/TypedObject.h"
|
#include "builtin/TypedObject.h"
|
||||||
|
@ -262,6 +263,28 @@ IonBuilder::inlineNativeCall(CallInfo& callInfo, JSFunction* target)
|
||||||
case InlinableNative::ObjectToString:
|
case InlinableNative::ObjectToString:
|
||||||
return inlineObjectToString(callInfo);
|
return inlineObjectToString(callInfo);
|
||||||
|
|
||||||
|
// SIMD natives.
|
||||||
|
case InlinableNative::SimdInt32x4:
|
||||||
|
return inlineSimd(callInfo, target, SimdType::Int32x4);
|
||||||
|
case InlinableNative::SimdUint32x4:
|
||||||
|
return inlineSimd(callInfo, target, SimdType::Uint32x4);
|
||||||
|
case InlinableNative::SimdInt16x8:
|
||||||
|
return inlineSimd(callInfo, target, SimdType::Int16x8);
|
||||||
|
case InlinableNative::SimdUint16x8:
|
||||||
|
return inlineSimd(callInfo, target, SimdType::Uint16x8);
|
||||||
|
case InlinableNative::SimdInt8x16:
|
||||||
|
return inlineSimd(callInfo, target, SimdType::Int8x16);
|
||||||
|
case InlinableNative::SimdUint8x16:
|
||||||
|
return inlineSimd(callInfo, target, SimdType::Uint8x16);
|
||||||
|
case InlinableNative::SimdFloat32x4:
|
||||||
|
return inlineSimd(callInfo, target, SimdType::Float32x4);
|
||||||
|
case InlinableNative::SimdBool32x4:
|
||||||
|
return inlineSimd(callInfo, target, SimdType::Bool32x4);
|
||||||
|
case InlinableNative::SimdBool16x8:
|
||||||
|
return inlineSimd(callInfo, target, SimdType::Bool16x8);
|
||||||
|
case InlinableNative::SimdBool8x16:
|
||||||
|
return inlineSimd(callInfo, target, SimdType::Bool8x16);
|
||||||
|
|
||||||
// Testing functions.
|
// Testing functions.
|
||||||
case InlinableNative::TestBailout:
|
case InlinableNative::TestBailout:
|
||||||
return inlineBailout(callInfo);
|
return inlineBailout(callInfo);
|
||||||
|
@ -456,6 +479,9 @@ IonBuilder::inlineNonFunctionCall(CallInfo& callInfo, JSObject* target)
|
||||||
if (callInfo.constructing() && target->constructHook() == TypedObject::construct)
|
if (callInfo.constructing() && target->constructHook() == TypedObject::construct)
|
||||||
return inlineConstructTypedObject(callInfo, &target->as<TypeDescr>());
|
return inlineConstructTypedObject(callInfo, &target->as<TypeDescr>());
|
||||||
|
|
||||||
|
if (!callInfo.constructing() && target->callHook() == SimdTypeDescr::call)
|
||||||
|
return inlineConstructSimdObject(callInfo, &target->as<SimdTypeDescr>());
|
||||||
|
|
||||||
return InliningStatus_NotInlined;
|
return InliningStatus_NotInlined;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3771,6 +3797,766 @@ IonBuilder::inlineConstructTypedObject(CallInfo& callInfo, TypeDescr* descr)
|
||||||
return InliningStatus_Inlined;
|
return InliningStatus_Inlined;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Main entry point for SIMD inlining.
|
||||||
|
// When the controlling simdType is an integer type, sign indicates whether the lanes should
|
||||||
|
// be treated as signed or unsigned integers.
|
||||||
|
IonBuilder::InliningResult
|
||||||
|
IonBuilder::inlineSimd(CallInfo& callInfo, JSFunction* target, SimdType type)
|
||||||
|
{
|
||||||
|
if (!JitSupportsSimd()) {
|
||||||
|
trackOptimizationOutcome(TrackedOutcome::NoSimdJitSupport);
|
||||||
|
return InliningStatus_NotInlined;
|
||||||
|
}
|
||||||
|
|
||||||
|
JSNative native = target->native();
|
||||||
|
SimdOperation simdOp = SimdOperation(target->jitInfo()->nativeOp);
|
||||||
|
|
||||||
|
switch(simdOp) {
|
||||||
|
case SimdOperation::Constructor:
|
||||||
|
// SIMD constructor calls are handled via inlineNonFunctionCall(), so
|
||||||
|
// they won't show up here where target is required to be a JSFunction.
|
||||||
|
// See also inlineConstructSimdObject().
|
||||||
|
MOZ_CRASH("SIMD constructor call not expected.");
|
||||||
|
case SimdOperation::Fn_check:
|
||||||
|
return inlineSimdCheck(callInfo, native, type);
|
||||||
|
case SimdOperation::Fn_splat:
|
||||||
|
return inlineSimdSplat(callInfo, native, type);
|
||||||
|
case SimdOperation::Fn_extractLane:
|
||||||
|
return inlineSimdExtractLane(callInfo, native, type);
|
||||||
|
case SimdOperation::Fn_replaceLane:
|
||||||
|
return inlineSimdReplaceLane(callInfo, native, type);
|
||||||
|
case SimdOperation::Fn_select:
|
||||||
|
return inlineSimdSelect(callInfo, native, type);
|
||||||
|
case SimdOperation::Fn_swizzle:
|
||||||
|
return inlineSimdShuffle(callInfo, native, type, 1);
|
||||||
|
case SimdOperation::Fn_shuffle:
|
||||||
|
return inlineSimdShuffle(callInfo, native, type, 2);
|
||||||
|
|
||||||
|
// Unary arithmetic.
|
||||||
|
case SimdOperation::Fn_abs:
|
||||||
|
return inlineSimdUnary(callInfo, native, MSimdUnaryArith::abs, type);
|
||||||
|
case SimdOperation::Fn_neg:
|
||||||
|
return inlineSimdUnary(callInfo, native, MSimdUnaryArith::neg, type);
|
||||||
|
case SimdOperation::Fn_not:
|
||||||
|
return inlineSimdUnary(callInfo, native, MSimdUnaryArith::not_, type);
|
||||||
|
case SimdOperation::Fn_reciprocalApproximation:
|
||||||
|
return inlineSimdUnary(callInfo, native, MSimdUnaryArith::reciprocalApproximation,
|
||||||
|
type);
|
||||||
|
case SimdOperation::Fn_reciprocalSqrtApproximation:
|
||||||
|
return inlineSimdUnary(callInfo, native, MSimdUnaryArith::reciprocalSqrtApproximation,
|
||||||
|
type);
|
||||||
|
case SimdOperation::Fn_sqrt:
|
||||||
|
return inlineSimdUnary(callInfo, native, MSimdUnaryArith::sqrt, type);
|
||||||
|
|
||||||
|
// Binary arithmetic.
|
||||||
|
case SimdOperation::Fn_add:
|
||||||
|
return inlineSimdBinaryArith(callInfo, native, MSimdBinaryArith::Op_add, type);
|
||||||
|
case SimdOperation::Fn_sub:
|
||||||
|
return inlineSimdBinaryArith(callInfo, native, MSimdBinaryArith::Op_sub, type);
|
||||||
|
case SimdOperation::Fn_mul:
|
||||||
|
return inlineSimdBinaryArith(callInfo, native, MSimdBinaryArith::Op_mul, type);
|
||||||
|
case SimdOperation::Fn_div:
|
||||||
|
return inlineSimdBinaryArith(callInfo, native, MSimdBinaryArith::Op_div, type);
|
||||||
|
case SimdOperation::Fn_max:
|
||||||
|
return inlineSimdBinaryArith(callInfo, native, MSimdBinaryArith::Op_max, type);
|
||||||
|
case SimdOperation::Fn_min:
|
||||||
|
return inlineSimdBinaryArith(callInfo, native, MSimdBinaryArith::Op_min, type);
|
||||||
|
case SimdOperation::Fn_maxNum:
|
||||||
|
return inlineSimdBinaryArith(callInfo, native, MSimdBinaryArith::Op_maxNum, type);
|
||||||
|
case SimdOperation::Fn_minNum:
|
||||||
|
return inlineSimdBinaryArith(callInfo, native, MSimdBinaryArith::Op_minNum, type);
|
||||||
|
|
||||||
|
// Binary saturating.
|
||||||
|
case SimdOperation::Fn_addSaturate:
|
||||||
|
return inlineSimdBinarySaturating(callInfo, native, MSimdBinarySaturating::add, type);
|
||||||
|
case SimdOperation::Fn_subSaturate:
|
||||||
|
return inlineSimdBinarySaturating(callInfo, native, MSimdBinarySaturating::sub, type);
|
||||||
|
|
||||||
|
// Binary bitwise.
|
||||||
|
case SimdOperation::Fn_and:
|
||||||
|
return inlineSimdBinaryBitwise(callInfo, native, MSimdBinaryBitwise::and_, type);
|
||||||
|
case SimdOperation::Fn_or:
|
||||||
|
return inlineSimdBinaryBitwise(callInfo, native, MSimdBinaryBitwise::or_, type);
|
||||||
|
case SimdOperation::Fn_xor:
|
||||||
|
return inlineSimdBinaryBitwise(callInfo, native, MSimdBinaryBitwise::xor_, type);
|
||||||
|
|
||||||
|
// Shifts.
|
||||||
|
case SimdOperation::Fn_shiftLeftByScalar:
|
||||||
|
return inlineSimdShift(callInfo, native, MSimdShift::lsh, type);
|
||||||
|
case SimdOperation::Fn_shiftRightByScalar:
|
||||||
|
return inlineSimdShift(callInfo, native, MSimdShift::rshForSign(GetSimdSign(type)), type);
|
||||||
|
|
||||||
|
// Boolean unary.
|
||||||
|
case SimdOperation::Fn_allTrue:
|
||||||
|
return inlineSimdAnyAllTrue(callInfo, /* IsAllTrue= */true, native, type);
|
||||||
|
case SimdOperation::Fn_anyTrue:
|
||||||
|
return inlineSimdAnyAllTrue(callInfo, /* IsAllTrue= */false, native, type);
|
||||||
|
|
||||||
|
// Comparisons.
|
||||||
|
case SimdOperation::Fn_lessThan:
|
||||||
|
return inlineSimdComp(callInfo, native, MSimdBinaryComp::lessThan, type);
|
||||||
|
case SimdOperation::Fn_lessThanOrEqual:
|
||||||
|
return inlineSimdComp(callInfo, native, MSimdBinaryComp::lessThanOrEqual, type);
|
||||||
|
case SimdOperation::Fn_equal:
|
||||||
|
return inlineSimdComp(callInfo, native, MSimdBinaryComp::equal, type);
|
||||||
|
case SimdOperation::Fn_notEqual:
|
||||||
|
return inlineSimdComp(callInfo, native, MSimdBinaryComp::notEqual, type);
|
||||||
|
case SimdOperation::Fn_greaterThan:
|
||||||
|
return inlineSimdComp(callInfo, native, MSimdBinaryComp::greaterThan, type);
|
||||||
|
case SimdOperation::Fn_greaterThanOrEqual:
|
||||||
|
return inlineSimdComp(callInfo, native, MSimdBinaryComp::greaterThanOrEqual, type);
|
||||||
|
|
||||||
|
// Int <-> Float conversions.
|
||||||
|
case SimdOperation::Fn_fromInt32x4:
|
||||||
|
return inlineSimdConvert(callInfo, native, false, SimdType::Int32x4, type);
|
||||||
|
case SimdOperation::Fn_fromUint32x4:
|
||||||
|
return inlineSimdConvert(callInfo, native, false, SimdType::Uint32x4, type);
|
||||||
|
case SimdOperation::Fn_fromFloat32x4:
|
||||||
|
return inlineSimdConvert(callInfo, native, false, SimdType::Float32x4, type);
|
||||||
|
|
||||||
|
// Load/store.
|
||||||
|
case SimdOperation::Fn_load:
|
||||||
|
return inlineSimdLoad(callInfo, native, type, GetSimdLanes(type));
|
||||||
|
case SimdOperation::Fn_load1:
|
||||||
|
return inlineSimdLoad(callInfo, native, type, 1);
|
||||||
|
case SimdOperation::Fn_load2:
|
||||||
|
return inlineSimdLoad(callInfo, native, type, 2);
|
||||||
|
case SimdOperation::Fn_load3:
|
||||||
|
return inlineSimdLoad(callInfo, native, type, 3);
|
||||||
|
case SimdOperation::Fn_store:
|
||||||
|
return inlineSimdStore(callInfo, native, type, GetSimdLanes(type));
|
||||||
|
case SimdOperation::Fn_store1:
|
||||||
|
return inlineSimdStore(callInfo, native, type, 1);
|
||||||
|
case SimdOperation::Fn_store2:
|
||||||
|
return inlineSimdStore(callInfo, native, type, 2);
|
||||||
|
case SimdOperation::Fn_store3:
|
||||||
|
return inlineSimdStore(callInfo, native, type, 3);
|
||||||
|
|
||||||
|
// Bitcasts. One for each type with a memory representation.
|
||||||
|
case SimdOperation::Fn_fromInt32x4Bits:
|
||||||
|
return inlineSimdConvert(callInfo, native, true, SimdType::Int32x4, type);
|
||||||
|
case SimdOperation::Fn_fromUint32x4Bits:
|
||||||
|
return inlineSimdConvert(callInfo, native, true, SimdType::Uint32x4, type);
|
||||||
|
case SimdOperation::Fn_fromInt16x8Bits:
|
||||||
|
return inlineSimdConvert(callInfo, native, true, SimdType::Int16x8, type);
|
||||||
|
case SimdOperation::Fn_fromUint16x8Bits:
|
||||||
|
return inlineSimdConvert(callInfo, native, true, SimdType::Uint16x8, type);
|
||||||
|
case SimdOperation::Fn_fromInt8x16Bits:
|
||||||
|
return inlineSimdConvert(callInfo, native, true, SimdType::Int8x16, type);
|
||||||
|
case SimdOperation::Fn_fromUint8x16Bits:
|
||||||
|
return inlineSimdConvert(callInfo, native, true, SimdType::Uint8x16, type);
|
||||||
|
case SimdOperation::Fn_fromFloat32x4Bits:
|
||||||
|
return inlineSimdConvert(callInfo, native, true, SimdType::Float32x4, type);
|
||||||
|
case SimdOperation::Fn_fromFloat64x2Bits:
|
||||||
|
return InliningStatus_NotInlined;
|
||||||
|
}
|
||||||
|
|
||||||
|
MOZ_CRASH("Unexpected SIMD opcode");
|
||||||
|
}
|
||||||
|
|
||||||
|
// The representation of boolean SIMD vectors is the same as the corresponding
|
||||||
|
// integer SIMD vectors with -1 lanes meaning true and 0 lanes meaning false.
|
||||||
|
//
|
||||||
|
// Functions that set the value of a boolean vector lane work by applying
|
||||||
|
// ToBoolean on the input argument, so they accept any argument type, just like
|
||||||
|
// the MNot and MTest instructions.
|
||||||
|
//
|
||||||
|
// Convert any scalar value into an appropriate SIMD lane value: An Int32 value
|
||||||
|
// that is either 0 for false or -1 for true.
|
||||||
|
MDefinition*
|
||||||
|
IonBuilder::convertToBooleanSimdLane(MDefinition* scalar)
|
||||||
|
{
|
||||||
|
MSub* result;
|
||||||
|
|
||||||
|
if (scalar->type() == MIRType::Boolean) {
|
||||||
|
// The input scalar is already a boolean with the int32 values 0 / 1.
|
||||||
|
// Compute result = 0 - scalar.
|
||||||
|
result = MSub::New(alloc(), constant(Int32Value(0)), scalar);
|
||||||
|
} else {
|
||||||
|
// For any other type, let MNot handle the conversion to boolean.
|
||||||
|
// Compute result = !scalar - 1.
|
||||||
|
MNot* inv = MNot::New(alloc(), scalar);
|
||||||
|
current->add(inv);
|
||||||
|
result = MSub::New(alloc(), inv, constant(Int32Value(1)));
|
||||||
|
}
|
||||||
|
|
||||||
|
result->setInt32Specialization();
|
||||||
|
current->add(result);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
IonBuilder::InliningResult
|
||||||
|
IonBuilder::inlineConstructSimdObject(CallInfo& callInfo, SimdTypeDescr* descr)
|
||||||
|
{
|
||||||
|
if (!JitSupportsSimd()) {
|
||||||
|
trackOptimizationOutcome(TrackedOutcome::NoSimdJitSupport);
|
||||||
|
return InliningStatus_NotInlined;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generic constructor of SIMD valuesX4.
|
||||||
|
MIRType simdType;
|
||||||
|
if (!MaybeSimdTypeToMIRType(descr->type(), &simdType)) {
|
||||||
|
trackOptimizationOutcome(TrackedOutcome::SimdTypeNotOptimized);
|
||||||
|
return InliningStatus_NotInlined;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Take the templateObject out of Baseline ICs, such that we can box
|
||||||
|
// SIMD value type in the same kind of objects.
|
||||||
|
MOZ_ASSERT(InlineTypedObject::canAccommodateType(descr));
|
||||||
|
MOZ_ASSERT(descr->getClass() == &SimdTypeDescr::class_,
|
||||||
|
"getTemplateObjectForSimdCtor needs an update");
|
||||||
|
|
||||||
|
JSObject* templateObject = inspector->getTemplateObjectForSimdCtor(pc, descr->type());
|
||||||
|
if (!templateObject)
|
||||||
|
return InliningStatus_NotInlined;
|
||||||
|
|
||||||
|
// The previous assertion ensures this will never fail if we were able to
|
||||||
|
// allocate a templateObject in Baseline.
|
||||||
|
InlineTypedObject* inlineTypedObject = &templateObject->as<InlineTypedObject>();
|
||||||
|
MOZ_ASSERT(&inlineTypedObject->typeDescr() == descr);
|
||||||
|
|
||||||
|
// When there are missing arguments, provide a default value
|
||||||
|
// containing the coercion of 'undefined' to the right type.
|
||||||
|
MConstant* defVal = nullptr;
|
||||||
|
MIRType laneType = SimdTypeToLaneType(simdType);
|
||||||
|
unsigned lanes = SimdTypeToLength(simdType);
|
||||||
|
if (lanes != 4 || callInfo.argc() < lanes) {
|
||||||
|
if (laneType == MIRType::Int32 || laneType == MIRType::Boolean) {
|
||||||
|
// The default lane for a boolean vector is |false|, but
|
||||||
|
// |MSimdSplat|, |MSimdValueX4|, and |MSimdInsertElement| all
|
||||||
|
// require an Int32 argument with the value 0 or 01 to initialize a
|
||||||
|
// boolean lane. See also convertToBooleanSimdLane() which is
|
||||||
|
// idempotent with a 0 argument after constant folding.
|
||||||
|
defVal = constant(Int32Value(0));
|
||||||
|
} else if (laneType == MIRType::Double) {
|
||||||
|
defVal = constant(DoubleNaNValue());
|
||||||
|
} else {
|
||||||
|
MOZ_ASSERT(laneType == MIRType::Float32);
|
||||||
|
defVal = MConstant::NewFloat32(alloc(), JS::GenericNaN());
|
||||||
|
current->add(defVal);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MInstruction *values = nullptr;
|
||||||
|
|
||||||
|
// Use the MSimdValueX4 constructor for X4 vectors.
|
||||||
|
if (lanes == 4) {
|
||||||
|
MDefinition* lane[4];
|
||||||
|
for (unsigned i = 0; i < 4; i++)
|
||||||
|
lane[i] = callInfo.getArgWithDefault(i, defVal);
|
||||||
|
|
||||||
|
// Convert boolean lanes into Int32 0 / -1.
|
||||||
|
if (laneType == MIRType::Boolean) {
|
||||||
|
for (unsigned i = 0; i < 4; i++)
|
||||||
|
lane[i] = convertToBooleanSimdLane(lane[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
values = MSimdValueX4::New(alloc(), simdType, lane[0], lane[1], lane[2], lane[3]);
|
||||||
|
current->add(values);
|
||||||
|
} else {
|
||||||
|
// For general constructor calls, start from splat(defVal), insert one
|
||||||
|
// lane at a time.
|
||||||
|
values = MSimdSplat::New(alloc(), defVal, simdType);
|
||||||
|
current->add(values);
|
||||||
|
|
||||||
|
// Stop early if constructor doesn't have enough arguments. These lanes
|
||||||
|
// then get the default value.
|
||||||
|
if (callInfo.argc() < lanes)
|
||||||
|
lanes = callInfo.argc();
|
||||||
|
|
||||||
|
for (unsigned i = 0; i < lanes; i++) {
|
||||||
|
MDefinition* lane = callInfo.getArg(i);
|
||||||
|
if (laneType == MIRType::Boolean)
|
||||||
|
lane = convertToBooleanSimdLane(lane);
|
||||||
|
values = MSimdInsertElement::New(alloc(), values, lane, i);
|
||||||
|
current->add(values);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MSimdBox* obj = MSimdBox::New(alloc(), constraints(), values, inlineTypedObject, descr->type(),
|
||||||
|
inlineTypedObject->group()->initialHeap(constraints()));
|
||||||
|
current->add(obj);
|
||||||
|
current->push(obj);
|
||||||
|
|
||||||
|
callInfo.setImplicitlyUsedUnchecked();
|
||||||
|
return InliningStatus_Inlined;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
IonBuilder::canInlineSimd(CallInfo& callInfo, JSNative native, unsigned numArgs,
|
||||||
|
InlineTypedObject** templateObj)
|
||||||
|
{
|
||||||
|
if (callInfo.argc() != numArgs)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
JSObject* templateObject = inspector->getTemplateObjectForNative(pc, native);
|
||||||
|
if (!templateObject)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
*templateObj = &templateObject->as<InlineTypedObject>();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
IonBuilder::InliningResult
|
||||||
|
IonBuilder::inlineSimdCheck(CallInfo& callInfo, JSNative native, SimdType type)
|
||||||
|
{
|
||||||
|
InlineTypedObject* templateObj = nullptr;
|
||||||
|
if (!canInlineSimd(callInfo, native, 1, &templateObj))
|
||||||
|
return InliningStatus_NotInlined;
|
||||||
|
|
||||||
|
// Unboxing checks the SIMD object type and throws a TypeError if it doesn't
|
||||||
|
// match type.
|
||||||
|
MDefinition *arg = unboxSimd(callInfo.getArg(0), type);
|
||||||
|
|
||||||
|
// Create an unbox/box pair, expecting the box to be optimized away if
|
||||||
|
// anyone use the return value from this check() call. This is what you want
|
||||||
|
// for code like this:
|
||||||
|
//
|
||||||
|
// function f(x) {
|
||||||
|
// x = Int32x4.check(x)
|
||||||
|
// for(...) {
|
||||||
|
// y = Int32x4.add(x, ...)
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// The unboxing of x happens as early as possible, and only once.
|
||||||
|
return boxSimd(callInfo, arg, templateObj);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Given a value or object, insert a dynamic check that this is a SIMD object of
|
||||||
|
// the required SimdType, and unbox it into the corresponding SIMD MIRType.
|
||||||
|
//
|
||||||
|
// This represents the standard type checking that all the SIMD operations
|
||||||
|
// perform on their arguments.
|
||||||
|
MDefinition*
|
||||||
|
IonBuilder::unboxSimd(MDefinition* ins, SimdType type)
|
||||||
|
{
|
||||||
|
// Trivial optimization: If ins is a MSimdBox of the same SIMD type, there
|
||||||
|
// is no way the unboxing could fail, and we can skip it altogether.
|
||||||
|
// This is the same thing MSimdUnbox::foldsTo() does, but we can save the
|
||||||
|
// memory allocation here.
|
||||||
|
if (ins->isSimdBox()) {
|
||||||
|
MSimdBox* box = ins->toSimdBox();
|
||||||
|
if (box->simdType() == type) {
|
||||||
|
MDefinition* value = box->input();
|
||||||
|
MOZ_ASSERT(value->type() == SimdTypeToMIRType(type));
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MSimdUnbox* unbox = MSimdUnbox::New(alloc(), ins, type);
|
||||||
|
current->add(unbox);
|
||||||
|
return unbox;
|
||||||
|
}
|
||||||
|
|
||||||
|
IonBuilder::InliningResult
|
||||||
|
IonBuilder::boxSimd(CallInfo& callInfo, MDefinition* ins, InlineTypedObject* templateObj)
|
||||||
|
{
|
||||||
|
SimdType simdType = templateObj->typeDescr().as<SimdTypeDescr>().type();
|
||||||
|
MSimdBox* obj = MSimdBox::New(alloc(), constraints(), ins, templateObj, simdType,
|
||||||
|
templateObj->group()->initialHeap(constraints()));
|
||||||
|
|
||||||
|
// In some cases, ins has already been added to current.
|
||||||
|
if (!ins->block() && ins->isInstruction())
|
||||||
|
current->add(ins->toInstruction());
|
||||||
|
current->add(obj);
|
||||||
|
current->push(obj);
|
||||||
|
|
||||||
|
callInfo.setImplicitlyUsedUnchecked();
|
||||||
|
return InliningStatus_Inlined;
|
||||||
|
}
|
||||||
|
|
||||||
|
IonBuilder::InliningResult
|
||||||
|
IonBuilder::inlineSimdBinaryArith(CallInfo& callInfo, JSNative native,
|
||||||
|
MSimdBinaryArith::Operation op, SimdType type)
|
||||||
|
{
|
||||||
|
InlineTypedObject* templateObj = nullptr;
|
||||||
|
if (!canInlineSimd(callInfo, native, 2, &templateObj))
|
||||||
|
return InliningStatus_NotInlined;
|
||||||
|
|
||||||
|
MDefinition* lhs = unboxSimd(callInfo.getArg(0), type);
|
||||||
|
MDefinition* rhs = unboxSimd(callInfo.getArg(1), type);
|
||||||
|
|
||||||
|
auto* ins = MSimdBinaryArith::AddLegalized(alloc(), current, lhs, rhs, op);
|
||||||
|
return boxSimd(callInfo, ins, templateObj);
|
||||||
|
}
|
||||||
|
|
||||||
|
IonBuilder::InliningResult
|
||||||
|
IonBuilder::inlineSimdBinaryBitwise(CallInfo& callInfo, JSNative native,
|
||||||
|
MSimdBinaryBitwise::Operation op, SimdType type)
|
||||||
|
{
|
||||||
|
InlineTypedObject* templateObj = nullptr;
|
||||||
|
if (!canInlineSimd(callInfo, native, 2, &templateObj))
|
||||||
|
return InliningStatus_NotInlined;
|
||||||
|
|
||||||
|
MDefinition* lhs = unboxSimd(callInfo.getArg(0), type);
|
||||||
|
MDefinition* rhs = unboxSimd(callInfo.getArg(1), type);
|
||||||
|
|
||||||
|
auto* ins = MSimdBinaryBitwise::New(alloc(), lhs, rhs, op);
|
||||||
|
return boxSimd(callInfo, ins, templateObj);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Inline a binary SIMD operation where both arguments are SIMD types.
|
||||||
|
IonBuilder::InliningResult
|
||||||
|
IonBuilder::inlineSimdBinarySaturating(CallInfo& callInfo, JSNative native,
|
||||||
|
MSimdBinarySaturating::Operation op, SimdType type)
|
||||||
|
{
|
||||||
|
InlineTypedObject* templateObj = nullptr;
|
||||||
|
if (!canInlineSimd(callInfo, native, 2, &templateObj))
|
||||||
|
return InliningStatus_NotInlined;
|
||||||
|
|
||||||
|
MDefinition* lhs = unboxSimd(callInfo.getArg(0), type);
|
||||||
|
MDefinition* rhs = unboxSimd(callInfo.getArg(1), type);
|
||||||
|
|
||||||
|
MSimdBinarySaturating* ins =
|
||||||
|
MSimdBinarySaturating::New(alloc(), lhs, rhs, op, GetSimdSign(type));
|
||||||
|
return boxSimd(callInfo, ins, templateObj);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Inline a SIMD shiftByScalar operation.
|
||||||
|
IonBuilder::InliningResult
|
||||||
|
IonBuilder::inlineSimdShift(CallInfo& callInfo, JSNative native, MSimdShift::Operation op,
|
||||||
|
SimdType type)
|
||||||
|
{
|
||||||
|
InlineTypedObject* templateObj = nullptr;
|
||||||
|
if (!canInlineSimd(callInfo, native, 2, &templateObj))
|
||||||
|
return InliningStatus_NotInlined;
|
||||||
|
|
||||||
|
MDefinition* vec = unboxSimd(callInfo.getArg(0), type);
|
||||||
|
|
||||||
|
MInstruction* ins = MSimdShift::AddLegalized(alloc(), current, vec, callInfo.getArg(1), op);
|
||||||
|
return boxSimd(callInfo, ins, templateObj);
|
||||||
|
}
|
||||||
|
|
||||||
|
IonBuilder::InliningResult
|
||||||
|
IonBuilder::inlineSimdComp(CallInfo& callInfo, JSNative native, MSimdBinaryComp::Operation op,
|
||||||
|
SimdType type)
|
||||||
|
{
|
||||||
|
InlineTypedObject* templateObj = nullptr;
|
||||||
|
if (!canInlineSimd(callInfo, native, 2, &templateObj))
|
||||||
|
return InliningStatus_NotInlined;
|
||||||
|
|
||||||
|
MDefinition* lhs = unboxSimd(callInfo.getArg(0), type);
|
||||||
|
MDefinition* rhs = unboxSimd(callInfo.getArg(1), type);
|
||||||
|
MInstruction* ins =
|
||||||
|
MSimdBinaryComp::AddLegalized(alloc(), current, lhs, rhs, op, GetSimdSign(type));
|
||||||
|
return boxSimd(callInfo, ins, templateObj);
|
||||||
|
}
|
||||||
|
|
||||||
|
IonBuilder::InliningResult
|
||||||
|
IonBuilder::inlineSimdUnary(CallInfo& callInfo, JSNative native, MSimdUnaryArith::Operation op,
|
||||||
|
SimdType type)
|
||||||
|
{
|
||||||
|
InlineTypedObject* templateObj = nullptr;
|
||||||
|
if (!canInlineSimd(callInfo, native, 1, &templateObj))
|
||||||
|
return InliningStatus_NotInlined;
|
||||||
|
|
||||||
|
MDefinition* arg = unboxSimd(callInfo.getArg(0), type);
|
||||||
|
|
||||||
|
MSimdUnaryArith* ins = MSimdUnaryArith::New(alloc(), arg, op);
|
||||||
|
return boxSimd(callInfo, ins, templateObj);
|
||||||
|
}
|
||||||
|
|
||||||
|
IonBuilder::InliningResult
|
||||||
|
IonBuilder::inlineSimdSplat(CallInfo& callInfo, JSNative native, SimdType type)
|
||||||
|
{
|
||||||
|
InlineTypedObject* templateObj = nullptr;
|
||||||
|
if (!canInlineSimd(callInfo, native, 1, &templateObj))
|
||||||
|
return InliningStatus_NotInlined;
|
||||||
|
|
||||||
|
MIRType mirType = SimdTypeToMIRType(type);
|
||||||
|
MDefinition* arg = callInfo.getArg(0);
|
||||||
|
|
||||||
|
// Convert to 0 / -1 before splatting a boolean lane.
|
||||||
|
if (SimdTypeToLaneType(mirType) == MIRType::Boolean)
|
||||||
|
arg = convertToBooleanSimdLane(arg);
|
||||||
|
|
||||||
|
MSimdSplat* ins = MSimdSplat::New(alloc(), arg, mirType);
|
||||||
|
return boxSimd(callInfo, ins, templateObj);
|
||||||
|
}
|
||||||
|
|
||||||
|
IonBuilder::InliningResult
|
||||||
|
IonBuilder::inlineSimdExtractLane(CallInfo& callInfo, JSNative native, SimdType type)
|
||||||
|
{
|
||||||
|
// extractLane() returns a scalar, so don't use canInlineSimd() which looks
|
||||||
|
// for a template object.
|
||||||
|
if (callInfo.argc() != 2 || callInfo.constructing()) {
|
||||||
|
trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
|
||||||
|
return InliningStatus_NotInlined;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Lane index.
|
||||||
|
MDefinition* arg = callInfo.getArg(1);
|
||||||
|
if (!arg->isConstant() || arg->type() != MIRType::Int32)
|
||||||
|
return InliningStatus_NotInlined;
|
||||||
|
unsigned lane = arg->toConstant()->toInt32();
|
||||||
|
if (lane >= GetSimdLanes(type))
|
||||||
|
return InliningStatus_NotInlined;
|
||||||
|
|
||||||
|
// Original vector.
|
||||||
|
MDefinition* orig = unboxSimd(callInfo.getArg(0), type);
|
||||||
|
MIRType vecType = orig->type();
|
||||||
|
MIRType laneType = SimdTypeToLaneType(vecType);
|
||||||
|
SimdSign sign = GetSimdSign(type);
|
||||||
|
|
||||||
|
// An Uint32 lane can't be represented in MIRType::Int32. Get it as a double.
|
||||||
|
if (type == SimdType::Uint32x4)
|
||||||
|
laneType = MIRType::Double;
|
||||||
|
|
||||||
|
MSimdExtractElement* ins =
|
||||||
|
MSimdExtractElement::New(alloc(), orig, laneType, lane, sign);
|
||||||
|
current->add(ins);
|
||||||
|
current->push(ins);
|
||||||
|
callInfo.setImplicitlyUsedUnchecked();
|
||||||
|
return InliningStatus_Inlined;
|
||||||
|
}
|
||||||
|
|
||||||
|
IonBuilder::InliningResult
|
||||||
|
IonBuilder::inlineSimdReplaceLane(CallInfo& callInfo, JSNative native, SimdType type)
|
||||||
|
{
|
||||||
|
InlineTypedObject* templateObj = nullptr;
|
||||||
|
if (!canInlineSimd(callInfo, native, 3, &templateObj))
|
||||||
|
return InliningStatus_NotInlined;
|
||||||
|
|
||||||
|
// Lane index.
|
||||||
|
MDefinition* arg = callInfo.getArg(1);
|
||||||
|
if (!arg->isConstant() || arg->type() != MIRType::Int32)
|
||||||
|
return InliningStatus_NotInlined;
|
||||||
|
|
||||||
|
unsigned lane = arg->toConstant()->toInt32();
|
||||||
|
if (lane >= GetSimdLanes(type))
|
||||||
|
return InliningStatus_NotInlined;
|
||||||
|
|
||||||
|
// Original vector.
|
||||||
|
MDefinition* orig = unboxSimd(callInfo.getArg(0), type);
|
||||||
|
MIRType vecType = orig->type();
|
||||||
|
|
||||||
|
// Convert to 0 / -1 before inserting a boolean lane.
|
||||||
|
MDefinition* value = callInfo.getArg(2);
|
||||||
|
if (SimdTypeToLaneType(vecType) == MIRType::Boolean)
|
||||||
|
value = convertToBooleanSimdLane(value);
|
||||||
|
|
||||||
|
MSimdInsertElement* ins = MSimdInsertElement::New(alloc(), orig, value, lane);
|
||||||
|
return boxSimd(callInfo, ins, templateObj);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Inline a SIMD conversion or bitcast. When isCast==false, one of the types
|
||||||
|
// must be floating point and the other integer. In this case, sign indicates if
|
||||||
|
// the integer lanes should be treated as signed or unsigned integers.
|
||||||
|
IonBuilder::InliningResult
|
||||||
|
IonBuilder::inlineSimdConvert(CallInfo& callInfo, JSNative native, bool isCast, SimdType fromType,
|
||||||
|
SimdType toType)
|
||||||
|
{
|
||||||
|
InlineTypedObject* templateObj = nullptr;
|
||||||
|
if (!canInlineSimd(callInfo, native, 1, &templateObj))
|
||||||
|
return InliningStatus_NotInlined;
|
||||||
|
|
||||||
|
MDefinition* arg = unboxSimd(callInfo.getArg(0), fromType);
|
||||||
|
MIRType mirType = SimdTypeToMIRType(toType);
|
||||||
|
|
||||||
|
MInstruction* ins;
|
||||||
|
if (isCast) {
|
||||||
|
// Signed/Unsigned doesn't matter for bitcasts.
|
||||||
|
ins = MSimdReinterpretCast::New(alloc(), arg, mirType);
|
||||||
|
} else {
|
||||||
|
// Exactly one of fromType, toType must be an integer type.
|
||||||
|
SimdSign sign = GetSimdSign(fromType);
|
||||||
|
if (sign == SimdSign::NotApplicable)
|
||||||
|
sign = GetSimdSign(toType);
|
||||||
|
|
||||||
|
// Possibly expand into multiple instructions.
|
||||||
|
ins = MSimdConvert::AddLegalized(alloc(), current, arg, mirType, sign);
|
||||||
|
}
|
||||||
|
|
||||||
|
return boxSimd(callInfo, ins, templateObj);
|
||||||
|
}
|
||||||
|
|
||||||
|
IonBuilder::InliningResult
|
||||||
|
IonBuilder::inlineSimdSelect(CallInfo& callInfo, JSNative native, SimdType type)
|
||||||
|
{
|
||||||
|
InlineTypedObject* templateObj = nullptr;
|
||||||
|
if (!canInlineSimd(callInfo, native, 3, &templateObj))
|
||||||
|
return InliningStatus_NotInlined;
|
||||||
|
|
||||||
|
MDefinition* mask = unboxSimd(callInfo.getArg(0), GetBooleanSimdType(type));
|
||||||
|
MDefinition* tval = unboxSimd(callInfo.getArg(1), type);
|
||||||
|
MDefinition* fval = unboxSimd(callInfo.getArg(2), type);
|
||||||
|
|
||||||
|
MSimdSelect* ins = MSimdSelect::New(alloc(), mask, tval, fval);
|
||||||
|
return boxSimd(callInfo, ins, templateObj);
|
||||||
|
}
|
||||||
|
|
||||||
|
IonBuilder::InliningResult
|
||||||
|
IonBuilder::inlineSimdShuffle(CallInfo& callInfo, JSNative native, SimdType type,
|
||||||
|
unsigned numVectors)
|
||||||
|
{
|
||||||
|
unsigned numLanes = GetSimdLanes(type);
|
||||||
|
InlineTypedObject* templateObj = nullptr;
|
||||||
|
if (!canInlineSimd(callInfo, native, numVectors + numLanes, &templateObj))
|
||||||
|
return InliningStatus_NotInlined;
|
||||||
|
|
||||||
|
MIRType mirType = SimdTypeToMIRType(type);
|
||||||
|
|
||||||
|
MSimdGeneralShuffle* ins = MSimdGeneralShuffle::New(alloc(), numVectors, numLanes, mirType);
|
||||||
|
|
||||||
|
if (!ins->init(alloc()))
|
||||||
|
return abort(AbortReason::Alloc);
|
||||||
|
|
||||||
|
for (unsigned i = 0; i < numVectors; i++)
|
||||||
|
ins->setVector(i, unboxSimd(callInfo.getArg(i), type));
|
||||||
|
for (size_t i = 0; i < numLanes; i++)
|
||||||
|
ins->setLane(i, callInfo.getArg(numVectors + i));
|
||||||
|
|
||||||
|
return boxSimd(callInfo, ins, templateObj);
|
||||||
|
}
|
||||||
|
|
||||||
|
IonBuilder::InliningResult
|
||||||
|
IonBuilder::inlineSimdAnyAllTrue(CallInfo& callInfo, bool IsAllTrue, JSNative native,
|
||||||
|
SimdType type)
|
||||||
|
{
|
||||||
|
// anyTrue() / allTrue() return a scalar, so don't use canInlineSimd() which looks
|
||||||
|
// for a template object.
|
||||||
|
if (callInfo.argc() != 1 || callInfo.constructing()) {
|
||||||
|
trackOptimizationOutcome(TrackedOutcome::CantInlineNativeBadForm);
|
||||||
|
return InliningStatus_NotInlined;
|
||||||
|
}
|
||||||
|
|
||||||
|
MDefinition* arg = unboxSimd(callInfo.getArg(0), type);
|
||||||
|
|
||||||
|
MUnaryInstruction* ins;
|
||||||
|
if (IsAllTrue)
|
||||||
|
ins = MSimdAllTrue::New(alloc(), arg, MIRType::Boolean);
|
||||||
|
else
|
||||||
|
ins = MSimdAnyTrue::New(alloc(), arg, MIRType::Boolean);
|
||||||
|
|
||||||
|
current->add(ins);
|
||||||
|
current->push(ins);
|
||||||
|
callInfo.setImplicitlyUsedUnchecked();
|
||||||
|
return InliningStatus_Inlined;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the typed array element type corresponding to the lanes in a SIMD vector type.
|
||||||
|
// This only applies to SIMD types that can be loaded and stored to a typed array.
|
||||||
|
static Scalar::Type
|
||||||
|
SimdTypeToArrayElementType(SimdType type)
|
||||||
|
{
|
||||||
|
switch (type) {
|
||||||
|
case SimdType::Float32x4: return Scalar::Float32x4;
|
||||||
|
case SimdType::Int8x16:
|
||||||
|
case SimdType::Uint8x16: return Scalar::Int8x16;
|
||||||
|
case SimdType::Int16x8:
|
||||||
|
case SimdType::Uint16x8: return Scalar::Int16x8;
|
||||||
|
case SimdType::Int32x4:
|
||||||
|
case SimdType::Uint32x4: return Scalar::Int32x4;
|
||||||
|
default: MOZ_CRASH("unexpected simd type");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
IonBuilder::prepareForSimdLoadStore(CallInfo& callInfo, Scalar::Type simdType,
|
||||||
|
MInstruction** elements, MDefinition** index,
|
||||||
|
Scalar::Type* arrayType)
|
||||||
|
{
|
||||||
|
MDefinition* array = callInfo.getArg(0);
|
||||||
|
*index = callInfo.getArg(1);
|
||||||
|
|
||||||
|
if (!ElementAccessIsTypedArray(constraints(), array, *index, arrayType))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
MInstruction* indexAsInt32 = MToNumberInt32::New(alloc(), *index);
|
||||||
|
current->add(indexAsInt32);
|
||||||
|
*index = indexAsInt32;
|
||||||
|
|
||||||
|
MDefinition* indexLoadEnd = *index;
|
||||||
|
|
||||||
|
MOZ_ASSERT(Scalar::byteSize(simdType) % Scalar::byteSize(*arrayType) == 0);
|
||||||
|
int32_t byteLoadSize = Scalar::byteSize(simdType) / Scalar::byteSize(*arrayType);
|
||||||
|
if (byteLoadSize > 1) {
|
||||||
|
// Add the number of supplementary needed slots. Overflows are fine
|
||||||
|
// because the bounds check code uses an unsigned comparison.
|
||||||
|
MAdd* addedIndex = MAdd::New(alloc(), *index, constant(Int32Value(byteLoadSize - 1)));
|
||||||
|
addedIndex->setInt32Specialization();
|
||||||
|
current->add(addedIndex);
|
||||||
|
indexLoadEnd = addedIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
MInstruction* length;
|
||||||
|
addTypedArrayLengthAndData(array, SkipBoundsCheck, index, &length, elements);
|
||||||
|
|
||||||
|
// If the index+size addition overflows, then indexLoadEnd might be
|
||||||
|
// in bounds while the actual index isn't, so we need two bounds checks
|
||||||
|
// here.
|
||||||
|
if (byteLoadSize > 1) {
|
||||||
|
indexLoadEnd = addBoundsCheck(indexLoadEnd, length);
|
||||||
|
auto* sub = MSub::New(alloc(), indexLoadEnd, constant(Int32Value(byteLoadSize - 1)));
|
||||||
|
sub->setInt32Specialization();
|
||||||
|
current->add(sub);
|
||||||
|
*index = sub;
|
||||||
|
}
|
||||||
|
|
||||||
|
*index = addBoundsCheck(*index, length);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
IonBuilder::InliningResult
|
||||||
|
IonBuilder::inlineSimdLoad(CallInfo& callInfo, JSNative native, SimdType type, unsigned numElems)
|
||||||
|
{
|
||||||
|
InlineTypedObject* templateObj = nullptr;
|
||||||
|
if (!canInlineSimd(callInfo, native, 2, &templateObj))
|
||||||
|
return InliningStatus_NotInlined;
|
||||||
|
|
||||||
|
Scalar::Type elemType = SimdTypeToArrayElementType(type);
|
||||||
|
|
||||||
|
MDefinition* index = nullptr;
|
||||||
|
MInstruction* elements = nullptr;
|
||||||
|
Scalar::Type arrayType;
|
||||||
|
if (!prepareForSimdLoadStore(callInfo, elemType, &elements, &index, &arrayType))
|
||||||
|
return InliningStatus_NotInlined;
|
||||||
|
|
||||||
|
MLoadUnboxedScalar* load = MLoadUnboxedScalar::New(alloc(), elements, index, arrayType);
|
||||||
|
load->setResultType(SimdTypeToMIRType(type));
|
||||||
|
load->setSimdRead(elemType, numElems);
|
||||||
|
|
||||||
|
return boxSimd(callInfo, load, templateObj);
|
||||||
|
}
|
||||||
|
|
||||||
|
IonBuilder::InliningResult
|
||||||
|
IonBuilder::inlineSimdStore(CallInfo& callInfo, JSNative native, SimdType type, unsigned numElems)
|
||||||
|
{
|
||||||
|
InlineTypedObject* templateObj = nullptr;
|
||||||
|
if (!canInlineSimd(callInfo, native, 3, &templateObj))
|
||||||
|
return InliningStatus_NotInlined;
|
||||||
|
|
||||||
|
Scalar::Type elemType = SimdTypeToArrayElementType(type);
|
||||||
|
|
||||||
|
MDefinition* index = nullptr;
|
||||||
|
MInstruction* elements = nullptr;
|
||||||
|
Scalar::Type arrayType;
|
||||||
|
if (!prepareForSimdLoadStore(callInfo, elemType, &elements, &index, &arrayType))
|
||||||
|
return InliningStatus_NotInlined;
|
||||||
|
|
||||||
|
MDefinition* valueToWrite = unboxSimd(callInfo.getArg(2), type);
|
||||||
|
MStoreUnboxedScalar* store = MStoreUnboxedScalar::New(alloc(), elements, index,
|
||||||
|
valueToWrite, arrayType,
|
||||||
|
MStoreUnboxedScalar::TruncateInput);
|
||||||
|
store->setSimdWrite(elemType, numElems);
|
||||||
|
|
||||||
|
current->add(store);
|
||||||
|
// Produce the original boxed value as our return value.
|
||||||
|
// This is unlikely to be used, so don't bother reboxing valueToWrite.
|
||||||
|
current->push(callInfo.getArg(2));
|
||||||
|
|
||||||
|
callInfo.setImplicitlyUsedUnchecked();
|
||||||
|
|
||||||
|
MOZ_TRY(resumeAfter(store));
|
||||||
|
return InliningStatus_Inlined;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Note that SIMD.cpp provides its own JSJitInfo objects for SIMD.foo.* functions.
|
||||||
|
// The Simd* objects defined here represent SIMD.foo() constructor calls.
|
||||||
|
// They are encoded with .nativeOp = 0. That is the sub-opcode within the SIMD type.
|
||||||
|
static_assert(uint16_t(SimdOperation::Constructor) == 0, "Constructor opcode must be 0");
|
||||||
|
|
||||||
#define ADD_NATIVE(native) const JSJitInfo JitInfo_##native { \
|
#define ADD_NATIVE(native) const JSJitInfo JitInfo_##native { \
|
||||||
{ nullptr }, { uint16_t(InlinableNative::native) }, { 0 }, JSJitInfo::InlinableNative };
|
{ nullptr }, { uint16_t(InlinableNative::native) }, { 0 }, JSJitInfo::InlinableNative };
|
||||||
INLINABLE_NATIVE_LIST(ADD_NATIVE)
|
INLINABLE_NATIVE_LIST(ADD_NATIVE)
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
#include "jslibmath.h"
|
#include "jslibmath.h"
|
||||||
|
|
||||||
#include "builtin/RegExp.h"
|
#include "builtin/RegExp.h"
|
||||||
|
#include "builtin/SIMD.h"
|
||||||
#include "builtin/String.h"
|
#include "builtin/String.h"
|
||||||
#include "jit/AtomicOperations.h"
|
#include "jit/AtomicOperations.h"
|
||||||
#include "jit/BaselineInspector.h"
|
#include "jit/BaselineInspector.h"
|
||||||
|
@ -1311,7 +1312,531 @@ MWasmFloatConstant::congruentTo(const MDefinition* ins) const
|
||||||
u.bits_ == ins->toWasmFloatConstant()->u.bits_;
|
u.bits_ == ins->toWasmFloatConstant()->u.bits_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MDefinition*
|
||||||
|
MSimdValueX4::foldsTo(TempAllocator& alloc)
|
||||||
|
{
|
||||||
|
#ifdef DEBUG
|
||||||
|
MIRType laneType = SimdTypeToLaneArgumentType(type());
|
||||||
|
#endif
|
||||||
|
bool allConstants = true;
|
||||||
|
bool allSame = true;
|
||||||
|
|
||||||
|
for (size_t i = 0; i < 4; ++i) {
|
||||||
|
MDefinition* op = getOperand(i);
|
||||||
|
MOZ_ASSERT(op->type() == laneType);
|
||||||
|
if (!op->isConstant())
|
||||||
|
allConstants = false;
|
||||||
|
if (i > 0 && op != getOperand(i - 1))
|
||||||
|
allSame = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!allConstants && !allSame)
|
||||||
|
return this;
|
||||||
|
|
||||||
|
if (allConstants) {
|
||||||
|
SimdConstant cst;
|
||||||
|
switch (type()) {
|
||||||
|
case MIRType::Bool32x4: {
|
||||||
|
int32_t a[4];
|
||||||
|
for (size_t i = 0; i < 4; ++i)
|
||||||
|
a[i] = getOperand(i)->toConstant()->valueToBooleanInfallible() ? -1 : 0;
|
||||||
|
cst = SimdConstant::CreateX4(a);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case MIRType::Int32x4: {
|
||||||
|
int32_t a[4];
|
||||||
|
for (size_t i = 0; i < 4; ++i)
|
||||||
|
a[i] = getOperand(i)->toConstant()->toInt32();
|
||||||
|
cst = SimdConstant::CreateX4(a);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case MIRType::Float32x4: {
|
||||||
|
float a[4];
|
||||||
|
for (size_t i = 0; i < 4; ++i)
|
||||||
|
a[i] = getOperand(i)->toConstant()->numberToDouble();
|
||||||
|
cst = SimdConstant::CreateX4(a);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default: MOZ_CRASH("unexpected type in MSimdValueX4::foldsTo");
|
||||||
|
}
|
||||||
|
|
||||||
|
return MSimdConstant::New(alloc, cst, type());
|
||||||
|
}
|
||||||
|
|
||||||
|
MOZ_ASSERT(allSame);
|
||||||
|
return MSimdSplat::New(alloc, getOperand(0), type());
|
||||||
|
}
|
||||||
|
|
||||||
|
MDefinition*
|
||||||
|
MSimdSplat::foldsTo(TempAllocator& alloc)
|
||||||
|
{
|
||||||
|
#ifdef DEBUG
|
||||||
|
MIRType laneType = SimdTypeToLaneArgumentType(type());
|
||||||
|
#endif
|
||||||
|
MDefinition* op = getOperand(0);
|
||||||
|
if (!op->isConstant())
|
||||||
|
return this;
|
||||||
|
MOZ_ASSERT(op->type() == laneType);
|
||||||
|
|
||||||
|
SimdConstant cst;
|
||||||
|
switch (type()) {
|
||||||
|
case MIRType::Bool8x16: {
|
||||||
|
int8_t v = op->toConstant()->valueToBooleanInfallible() ? -1 : 0;
|
||||||
|
cst = SimdConstant::SplatX16(v);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case MIRType::Bool16x8: {
|
||||||
|
int16_t v = op->toConstant()->valueToBooleanInfallible() ? -1 : 0;
|
||||||
|
cst = SimdConstant::SplatX8(v);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case MIRType::Bool32x4: {
|
||||||
|
int32_t v = op->toConstant()->valueToBooleanInfallible() ? -1 : 0;
|
||||||
|
cst = SimdConstant::SplatX4(v);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case MIRType::Int8x16: {
|
||||||
|
int32_t v = op->toConstant()->toInt32();
|
||||||
|
cst = SimdConstant::SplatX16(v);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case MIRType::Int16x8: {
|
||||||
|
int32_t v = op->toConstant()->toInt32();
|
||||||
|
cst = SimdConstant::SplatX8(v);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case MIRType::Int32x4: {
|
||||||
|
int32_t v = op->toConstant()->toInt32();
|
||||||
|
cst = SimdConstant::SplatX4(v);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case MIRType::Float32x4: {
|
||||||
|
float v = op->toConstant()->numberToDouble();
|
||||||
|
cst = SimdConstant::SplatX4(v);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default: MOZ_CRASH("unexpected type in MSimdSplat::foldsTo");
|
||||||
|
}
|
||||||
|
|
||||||
|
return MSimdConstant::New(alloc, cst, type());
|
||||||
|
}
|
||||||
|
|
||||||
|
MDefinition*
|
||||||
|
MSimdUnbox::foldsTo(TempAllocator& alloc)
|
||||||
|
{
|
||||||
|
MDefinition* in = input();
|
||||||
|
|
||||||
|
if (in->isSimdBox()) {
|
||||||
|
MSimdBox* box = in->toSimdBox();
|
||||||
|
// If the operand is a MSimdBox, then we just reuse the operand of the
|
||||||
|
// MSimdBox as long as the type corresponds to what we are supposed to
|
||||||
|
// unbox.
|
||||||
|
in = box->input();
|
||||||
|
if (box->simdType() != simdType())
|
||||||
|
return this;
|
||||||
|
MOZ_ASSERT(in->type() == type());
|
||||||
|
return in;
|
||||||
|
}
|
||||||
|
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
MDefinition*
|
||||||
|
MSimdSwizzle::foldsTo(TempAllocator& alloc)
|
||||||
|
{
|
||||||
|
if (lanesMatch(0, 1, 2, 3))
|
||||||
|
return input();
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
MDefinition*
|
||||||
|
MSimdGeneralShuffle::foldsTo(TempAllocator& alloc)
|
||||||
|
{
|
||||||
|
FixedList<uint8_t> lanes;
|
||||||
|
if (!lanes.init(alloc, numLanes()))
|
||||||
|
return this;
|
||||||
|
|
||||||
|
for (size_t i = 0; i < numLanes(); i++) {
|
||||||
|
if (!lane(i)->isConstant() || lane(i)->type() != MIRType::Int32)
|
||||||
|
return this;
|
||||||
|
int32_t temp = lane(i)->toConstant()->toInt32();
|
||||||
|
if (temp < 0 || unsigned(temp) >= numLanes() * numVectors())
|
||||||
|
return this;
|
||||||
|
lanes[i] = uint8_t(temp);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (numVectors() == 1)
|
||||||
|
return MSimdSwizzle::New(alloc, vector(0), lanes.data());
|
||||||
|
|
||||||
|
MOZ_ASSERT(numVectors() == 2);
|
||||||
|
return MSimdShuffle::New(alloc, vector(0), vector(1), lanes.data());
|
||||||
|
}
|
||||||
|
|
||||||
|
MInstruction*
|
||||||
|
MSimdConvert::AddLegalized(TempAllocator& alloc, MBasicBlock* addTo, MDefinition* obj,
|
||||||
|
MIRType toType, SimdSign sign, wasm::BytecodeOffset bytecodeOffset)
|
||||||
|
{
|
||||||
|
MIRType fromType = obj->type();
|
||||||
|
|
||||||
|
if (SupportsUint32x4FloatConversions || sign != SimdSign::Unsigned) {
|
||||||
|
MInstruction* ins = New(alloc, obj, toType, sign, bytecodeOffset);
|
||||||
|
addTo->add(ins);
|
||||||
|
return ins;
|
||||||
|
}
|
||||||
|
|
||||||
|
// This architecture can't do Uint32x4 <-> Float32x4 conversions (Hi SSE!)
|
||||||
|
MOZ_ASSERT(sign == SimdSign::Unsigned);
|
||||||
|
if (fromType == MIRType::Int32x4 && toType == MIRType::Float32x4) {
|
||||||
|
// Converting Uint32x4 -> Float32x4. This algorithm is from LLVM.
|
||||||
|
//
|
||||||
|
// Split the input number into high and low parts:
|
||||||
|
//
|
||||||
|
// uint32_t hi = x >> 16;
|
||||||
|
// uint32_t lo = x & 0xffff;
|
||||||
|
//
|
||||||
|
// Insert these parts as the low mantissa bits in a float32 number with
|
||||||
|
// the corresponding exponent:
|
||||||
|
//
|
||||||
|
// float fhi = (bits-as-float)(hi | 0x53000000); // 0x1.0p39f + hi*2^16
|
||||||
|
// float flo = (bits-as-float)(lo | 0x4b000000); // 0x1.0p23f + lo
|
||||||
|
//
|
||||||
|
// Subtract the bias from the hi part:
|
||||||
|
//
|
||||||
|
// fhi -= (0x1.0p39 + 0x1.0p23) // hi*2^16 - 0x1.0p23
|
||||||
|
//
|
||||||
|
// And finally combine:
|
||||||
|
//
|
||||||
|
// result = flo + fhi // lo + hi*2^16.
|
||||||
|
|
||||||
|
// Compute hi = obj >> 16 (lane-wise unsigned shift).
|
||||||
|
MInstruction* c16 = MConstant::New(alloc, Int32Value(16));
|
||||||
|
addTo->add(c16);
|
||||||
|
MInstruction* hi = MSimdShift::AddLegalized(alloc, addTo, obj, c16, MSimdShift::ursh);
|
||||||
|
|
||||||
|
// Compute lo = obj & 0xffff (lane-wise).
|
||||||
|
MInstruction* m16 =
|
||||||
|
MSimdConstant::New(alloc, SimdConstant::SplatX4(0xffff), MIRType::Int32x4);
|
||||||
|
addTo->add(m16);
|
||||||
|
MInstruction* lo = MSimdBinaryBitwise::New(alloc, obj, m16, MSimdBinaryBitwise::and_);
|
||||||
|
addTo->add(lo);
|
||||||
|
|
||||||
|
// Mix in the exponents.
|
||||||
|
MInstruction* exphi =
|
||||||
|
MSimdConstant::New(alloc, SimdConstant::SplatX4(0x53000000), MIRType::Int32x4);
|
||||||
|
addTo->add(exphi);
|
||||||
|
MInstruction* mhi = MSimdBinaryBitwise::New(alloc, hi, exphi, MSimdBinaryBitwise::or_);
|
||||||
|
addTo->add(mhi);
|
||||||
|
MInstruction* explo =
|
||||||
|
MSimdConstant::New(alloc, SimdConstant::SplatX4(0x4b000000), MIRType::Int32x4);
|
||||||
|
addTo->add(explo);
|
||||||
|
MInstruction* mlo = MSimdBinaryBitwise::New(alloc, lo, explo, MSimdBinaryBitwise::or_);
|
||||||
|
addTo->add(mlo);
|
||||||
|
|
||||||
|
// Bit-cast both to Float32x4.
|
||||||
|
MInstruction* fhi = MSimdReinterpretCast::New(alloc, mhi, MIRType::Float32x4);
|
||||||
|
addTo->add(fhi);
|
||||||
|
MInstruction* flo = MSimdReinterpretCast::New(alloc, mlo, MIRType::Float32x4);
|
||||||
|
addTo->add(flo);
|
||||||
|
|
||||||
|
// Subtract out the bias: 0x1.0p39f + 0x1.0p23f.
|
||||||
|
// MSVC doesn't support the hexadecimal float syntax.
|
||||||
|
const float BiasValue = 549755813888.f + 8388608.f;
|
||||||
|
MInstruction* bias =
|
||||||
|
MSimdConstant::New(alloc, SimdConstant::SplatX4(BiasValue), MIRType::Float32x4);
|
||||||
|
addTo->add(bias);
|
||||||
|
MInstruction* fhi_debiased =
|
||||||
|
MSimdBinaryArith::AddLegalized(alloc, addTo, fhi, bias, MSimdBinaryArith::Op_sub);
|
||||||
|
|
||||||
|
// Compute the final result.
|
||||||
|
return MSimdBinaryArith::AddLegalized(alloc, addTo, fhi_debiased, flo,
|
||||||
|
MSimdBinaryArith::Op_add);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fromType == MIRType::Float32x4 && toType == MIRType::Int32x4) {
|
||||||
|
// The Float32x4 -> Uint32x4 conversion can throw if the input is out of
|
||||||
|
// range. This is handled by the LFloat32x4ToUint32x4 expansion.
|
||||||
|
MInstruction* ins = New(alloc, obj, toType, sign, bytecodeOffset);
|
||||||
|
addTo->add(ins);
|
||||||
|
return ins;
|
||||||
|
}
|
||||||
|
|
||||||
|
MOZ_CRASH("Unhandled SIMD type conversion");
|
||||||
|
}
|
||||||
|
|
||||||
|
MInstruction*
|
||||||
|
MSimdBinaryComp::AddLegalized(TempAllocator& alloc, MBasicBlock* addTo, MDefinition* left,
|
||||||
|
MDefinition* right, Operation op, SimdSign sign)
|
||||||
|
{
|
||||||
|
MOZ_ASSERT(left->type() == right->type());
|
||||||
|
MIRType opType = left->type();
|
||||||
|
MOZ_ASSERT(IsSimdType(opType));
|
||||||
|
bool IsEquality = op == equal || op == notEqual;
|
||||||
|
|
||||||
|
// Check if this is an unsupported unsigned compare that needs to be biased.
|
||||||
|
// If so, put the bias vector in `bias`.
|
||||||
|
if (sign == SimdSign::Unsigned && !IsEquality) {
|
||||||
|
MInstruction* bias = nullptr;
|
||||||
|
|
||||||
|
// This is an order comparison of Uint32x4 vectors which are not supported on this target.
|
||||||
|
// Simply offset |left| and |right| by INT_MIN, then do a signed comparison.
|
||||||
|
if (!SupportsUint32x4Compares && opType == MIRType::Int32x4)
|
||||||
|
bias = MSimdConstant::New(alloc, SimdConstant::SplatX4(int32_t(0x80000000)), opType);
|
||||||
|
else if (!SupportsUint16x8Compares && opType == MIRType::Int16x8)
|
||||||
|
bias = MSimdConstant::New(alloc, SimdConstant::SplatX8(int16_t(0x8000)), opType);
|
||||||
|
if (!SupportsUint8x16Compares && opType == MIRType::Int8x16)
|
||||||
|
bias = MSimdConstant::New(alloc, SimdConstant::SplatX16(int8_t(0x80)), opType);
|
||||||
|
|
||||||
|
if (bias) {
|
||||||
|
addTo->add(bias);
|
||||||
|
|
||||||
|
// Add the bias.
|
||||||
|
MInstruction* bleft =
|
||||||
|
MSimdBinaryArith::AddLegalized(alloc, addTo, left, bias, MSimdBinaryArith::Op_add);
|
||||||
|
MInstruction* bright =
|
||||||
|
MSimdBinaryArith::AddLegalized(alloc, addTo, right, bias, MSimdBinaryArith::Op_add);
|
||||||
|
|
||||||
|
// Do the equivalent signed comparison.
|
||||||
|
MInstruction* result =
|
||||||
|
MSimdBinaryComp::New(alloc, bleft, bright, op, SimdSign::Signed);
|
||||||
|
addTo->add(result);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sign == SimdSign::Unsigned &&
|
||||||
|
((!SupportsUint32x4Compares && opType == MIRType::Int32x4) ||
|
||||||
|
(!SupportsUint16x8Compares && opType == MIRType::Int16x8) ||
|
||||||
|
(!SupportsUint8x16Compares && opType == MIRType::Int8x16))) {
|
||||||
|
// The sign doesn't matter for equality tests. Flip it to make the
|
||||||
|
// backend assertions happy.
|
||||||
|
MOZ_ASSERT(IsEquality);
|
||||||
|
sign = SimdSign::Signed;
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is a legal operation already. Just create the instruction requested.
|
||||||
|
MInstruction* result = MSimdBinaryComp::New(alloc, left, right, op, sign);
|
||||||
|
addTo->add(result);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
MInstruction*
|
||||||
|
MSimdBinaryArith::AddLegalized(TempAllocator& alloc, MBasicBlock* addTo, MDefinition* left,
|
||||||
|
MDefinition* right, Operation op)
|
||||||
|
{
|
||||||
|
MOZ_ASSERT(left->type() == right->type());
|
||||||
|
MIRType opType = left->type();
|
||||||
|
MOZ_ASSERT(IsSimdType(opType));
|
||||||
|
|
||||||
|
// SSE does not have 8x16 multiply instructions.
|
||||||
|
if (opType == MIRType::Int8x16 && op == Op_mul) {
|
||||||
|
// Express the multiply in terms of Int16x8 multiplies by handling the
|
||||||
|
// even and odd lanes separately.
|
||||||
|
|
||||||
|
MInstruction* wideL = MSimdReinterpretCast::New(alloc, left, MIRType::Int16x8);
|
||||||
|
addTo->add(wideL);
|
||||||
|
MInstruction* wideR = MSimdReinterpretCast::New(alloc, right, MIRType::Int16x8);
|
||||||
|
addTo->add(wideR);
|
||||||
|
|
||||||
|
// wideL = yyxx yyxx yyxx yyxx yyxx yyxx yyxx yyxx
|
||||||
|
// wideR = bbaa bbaa bbaa bbaa bbaa bbaa bbaa bbaa
|
||||||
|
|
||||||
|
// Shift the odd lanes down to the low bits of the 16x8 vectors.
|
||||||
|
MInstruction* eight = MConstant::New(alloc, Int32Value(8));
|
||||||
|
addTo->add(eight);
|
||||||
|
MInstruction* evenL = wideL;
|
||||||
|
MInstruction* evenR = wideR;
|
||||||
|
MInstruction* oddL =
|
||||||
|
MSimdShift::AddLegalized(alloc, addTo, wideL, eight, MSimdShift::ursh);
|
||||||
|
MInstruction* oddR =
|
||||||
|
MSimdShift::AddLegalized(alloc, addTo, wideR, eight, MSimdShift::ursh);
|
||||||
|
|
||||||
|
// evenL = yyxx yyxx yyxx yyxx yyxx yyxx yyxx yyxx
|
||||||
|
// evenR = bbaa bbaa bbaa bbaa bbaa bbaa bbaa bbaa
|
||||||
|
// oddL = 00yy 00yy 00yy 00yy 00yy 00yy 00yy 00yy
|
||||||
|
// oddR = 00bb 00bb 00bb 00bb 00bb 00bb 00bb 00bb
|
||||||
|
|
||||||
|
// Now do two 16x8 multiplications. We can use the low bits of each.
|
||||||
|
MInstruction* even = MSimdBinaryArith::AddLegalized(alloc, addTo, evenL, evenR, Op_mul);
|
||||||
|
MInstruction* odd = MSimdBinaryArith::AddLegalized(alloc, addTo, oddL, oddR, Op_mul);
|
||||||
|
|
||||||
|
// even = ~~PP ~~PP ~~PP ~~PP ~~PP ~~PP ~~PP ~~PP
|
||||||
|
// odd = ~~QQ ~~QQ ~~QQ ~~QQ ~~QQ ~~QQ ~~QQ ~~QQ
|
||||||
|
|
||||||
|
MInstruction* mask =
|
||||||
|
MSimdConstant::New(alloc, SimdConstant::SplatX8(int16_t(0x00ff)), MIRType::Int16x8);
|
||||||
|
addTo->add(mask);
|
||||||
|
even = MSimdBinaryBitwise::New(alloc, even, mask, MSimdBinaryBitwise::and_);
|
||||||
|
addTo->add(even);
|
||||||
|
odd = MSimdShift::AddLegalized(alloc, addTo, odd, eight, MSimdShift::lsh);
|
||||||
|
|
||||||
|
// even = 00PP 00PP 00PP 00PP 00PP 00PP 00PP 00PP
|
||||||
|
// odd = QQ00 QQ00 QQ00 QQ00 QQ00 QQ00 QQ00 QQ00
|
||||||
|
|
||||||
|
// Combine:
|
||||||
|
MInstruction* result = MSimdBinaryBitwise::New(alloc, even, odd, MSimdBinaryBitwise::or_);
|
||||||
|
addTo->add(result);
|
||||||
|
result = MSimdReinterpretCast::New(alloc, result, opType);
|
||||||
|
addTo->add(result);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is a legal operation already. Just create the instruction requested.
|
||||||
|
MInstruction* result = MSimdBinaryArith::New(alloc, left, right, op);
|
||||||
|
addTo->add(result);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
MInstruction*
|
||||||
|
MSimdShift::AddLegalized(TempAllocator& alloc, MBasicBlock* addTo, MDefinition* left,
|
||||||
|
MDefinition* right, Operation op)
|
||||||
|
{
|
||||||
|
MIRType opType = left->type();
|
||||||
|
MOZ_ASSERT(IsIntegerSimdType(opType));
|
||||||
|
|
||||||
|
// SSE does not provide 8x16 shift instructions.
|
||||||
|
if (opType == MIRType::Int8x16) {
|
||||||
|
// Express the shift in terms of Int16x8 shifts by splitting into even
|
||||||
|
// and odd lanes, place 8-bit lanes into the high bits of Int16x8
|
||||||
|
// vectors `even` and `odd`. Shift, mask, combine.
|
||||||
|
//
|
||||||
|
// wide = Int16x8.fromInt8x16Bits(left);
|
||||||
|
// shiftBy = right & 7
|
||||||
|
// mask = Int16x8.splat(0xff00);
|
||||||
|
//
|
||||||
|
MInstruction* wide = MSimdReinterpretCast::New(alloc, left, MIRType::Int16x8);
|
||||||
|
addTo->add(wide);
|
||||||
|
|
||||||
|
// wide = yyxx yyxx yyxx yyxx yyxx yyxx yyxx yyxx
|
||||||
|
|
||||||
|
MInstruction* shiftMask = MConstant::New(alloc, Int32Value(7));
|
||||||
|
addTo->add(shiftMask);
|
||||||
|
MBinaryBitwiseInstruction* shiftBy = MBitAnd::New(alloc, right, shiftMask);
|
||||||
|
shiftBy->setInt32Specialization();
|
||||||
|
addTo->add(shiftBy);
|
||||||
|
|
||||||
|
// Move the even 8x16 lanes into the high bits of the 16x8 lanes.
|
||||||
|
MInstruction* eight = MConstant::New(alloc, Int32Value(8));
|
||||||
|
addTo->add(eight);
|
||||||
|
MInstruction* even = MSimdShift::AddLegalized(alloc, addTo, wide, eight, lsh);
|
||||||
|
|
||||||
|
// Leave the odd lanes in place.
|
||||||
|
MInstruction* odd = wide;
|
||||||
|
|
||||||
|
// even = xx00 xx00 xx00 xx00 xx00 xx00 xx00 xx00
|
||||||
|
// odd = yyxx yyxx yyxx yyxx yyxx yyxx yyxx yyxx
|
||||||
|
|
||||||
|
MInstruction* mask =
|
||||||
|
MSimdConstant::New(alloc, SimdConstant::SplatX8(int16_t(0xff00)), MIRType::Int16x8);
|
||||||
|
addTo->add(mask);
|
||||||
|
|
||||||
|
// Left-shift: Clear the low bits in `odd` before shifting.
|
||||||
|
if (op == lsh) {
|
||||||
|
odd = MSimdBinaryBitwise::New(alloc, odd, mask, MSimdBinaryBitwise::and_);
|
||||||
|
addTo->add(odd);
|
||||||
|
// odd = yy00 yy00 yy00 yy00 yy00 yy00 yy00 yy00
|
||||||
|
}
|
||||||
|
|
||||||
|
// Do the real shift twice: once for the even lanes, once for the odd
|
||||||
|
// lanes. This is a recursive call, but with a different type.
|
||||||
|
even = MSimdShift::AddLegalized(alloc, addTo, even, shiftBy, op);
|
||||||
|
odd = MSimdShift::AddLegalized(alloc, addTo, odd, shiftBy, op);
|
||||||
|
|
||||||
|
// even = XX~~ XX~~ XX~~ XX~~ XX~~ XX~~ XX~~ XX~~
|
||||||
|
// odd = YY~~ YY~~ YY~~ YY~~ YY~~ YY~~ YY~~ YY~~
|
||||||
|
|
||||||
|
// Right-shift: Clear the low bits in `odd` after shifting.
|
||||||
|
if (op != lsh) {
|
||||||
|
odd = MSimdBinaryBitwise::New(alloc, odd, mask, MSimdBinaryBitwise::and_);
|
||||||
|
addTo->add(odd);
|
||||||
|
// odd = YY00 YY00 YY00 YY00 YY00 YY00 YY00 YY00
|
||||||
|
}
|
||||||
|
|
||||||
|
// Move the even lanes back to their original place.
|
||||||
|
even = MSimdShift::AddLegalized(alloc, addTo, even, eight, ursh);
|
||||||
|
|
||||||
|
// Now, `odd` contains the odd lanes properly shifted, and `even`
|
||||||
|
// contains the even lanes properly shifted:
|
||||||
|
//
|
||||||
|
// even = 00XX 00XX 00XX 00XX 00XX 00XX 00XX 00XX
|
||||||
|
// odd = YY00 YY00 YY00 YY00 YY00 YY00 YY00 YY00
|
||||||
|
//
|
||||||
|
// Combine:
|
||||||
|
MInstruction* result = MSimdBinaryBitwise::New(alloc, even, odd, MSimdBinaryBitwise::or_);
|
||||||
|
addTo->add(result);
|
||||||
|
result = MSimdReinterpretCast::New(alloc, result, opType);
|
||||||
|
addTo->add(result);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
// This is a legal operation already. Just create the instruction requested.
|
||||||
|
MInstruction* result = MSimdShift::New(alloc, left, right, op);
|
||||||
|
addTo->add(result);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
static void
|
||||||
|
PrintOpcodeOperation(T* mir, GenericPrinter& out)
|
||||||
|
{
|
||||||
|
mir->MDefinition::printOpcode(out);
|
||||||
|
out.printf(" (%s)", T::OperationName(mir->operation()));
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef JS_JITSPEW
|
#ifdef JS_JITSPEW
|
||||||
|
void
|
||||||
|
MSimdBinaryArith::printOpcode(GenericPrinter& out) const
|
||||||
|
{
|
||||||
|
PrintOpcodeOperation(this, out);
|
||||||
|
}
|
||||||
|
void
|
||||||
|
MSimdBinarySaturating::printOpcode(GenericPrinter& out) const
|
||||||
|
{
|
||||||
|
PrintOpcodeOperation(this, out);
|
||||||
|
}
|
||||||
|
void
|
||||||
|
MSimdBinaryBitwise::printOpcode(GenericPrinter& out) const
|
||||||
|
{
|
||||||
|
PrintOpcodeOperation(this, out);
|
||||||
|
}
|
||||||
|
void
|
||||||
|
MSimdUnaryArith::printOpcode(GenericPrinter& out) const
|
||||||
|
{
|
||||||
|
PrintOpcodeOperation(this, out);
|
||||||
|
}
|
||||||
|
void
|
||||||
|
MSimdBinaryComp::printOpcode(GenericPrinter& out) const
|
||||||
|
{
|
||||||
|
PrintOpcodeOperation(this, out);
|
||||||
|
}
|
||||||
|
void
|
||||||
|
MSimdShift::printOpcode(GenericPrinter& out) const
|
||||||
|
{
|
||||||
|
PrintOpcodeOperation(this, out);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
MSimdInsertElement::printOpcode(GenericPrinter& out) const
|
||||||
|
{
|
||||||
|
MDefinition::printOpcode(out);
|
||||||
|
out.printf(" (lane %u)", lane());
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
MSimdBox::printOpcode(GenericPrinter& out) const
|
||||||
|
{
|
||||||
|
MDefinition::printOpcode(out);
|
||||||
|
out.printf(" (%s%s)", SimdTypeToString(simdType()),
|
||||||
|
initialHeap() == gc::TenuredHeap ? ", tenured" : "");
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
MSimdUnbox::printOpcode(GenericPrinter& out) const
|
||||||
|
{
|
||||||
|
MDefinition::printOpcode(out);
|
||||||
|
out.printf(" (%s)", SimdTypeToString(simdType()));
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
MControlInstruction::printOpcode(GenericPrinter& out) const
|
MControlInstruction::printOpcode(GenericPrinter& out) const
|
||||||
{
|
{
|
||||||
|
|
1256
js/src/jit/MIR.h
1256
js/src/jit/MIR.h
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -158,6 +158,10 @@ class MIRGenerator
|
||||||
return needsOverrecursedCheck_;
|
return needsOverrecursedCheck_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Traverses the graph to find if there's any SIMD instruction. Costful but
|
||||||
|
// the value is cached, so don't worry about calling it several times.
|
||||||
|
bool usesSimd();
|
||||||
|
|
||||||
bool modifiesFrameArguments() const {
|
bool modifiesFrameArguments() const {
|
||||||
return modifiesFrameArguments_;
|
return modifiesFrameArguments_;
|
||||||
}
|
}
|
||||||
|
@ -187,6 +191,8 @@ class MIRGenerator
|
||||||
uint32_t wasmMaxStackArgBytes_;
|
uint32_t wasmMaxStackArgBytes_;
|
||||||
bool needsOverrecursedCheck_;
|
bool needsOverrecursedCheck_;
|
||||||
bool needsStaticStackAlignment_;
|
bool needsStaticStackAlignment_;
|
||||||
|
bool usesSimd_;
|
||||||
|
bool cachedUsesSimd_;
|
||||||
|
|
||||||
// Keep track of whether frame arguments are modified during execution.
|
// Keep track of whether frame arguments are modified during execution.
|
||||||
// RegAlloc needs to know this as spilling values back to their register
|
// RegAlloc needs to know this as spilling values back to their register
|
||||||
|
|
|
@ -32,6 +32,8 @@ MIRGenerator::MIRGenerator(CompileRealm* realm, const JitCompileOptions& options
|
||||||
wasmMaxStackArgBytes_(0),
|
wasmMaxStackArgBytes_(0),
|
||||||
needsOverrecursedCheck_(false),
|
needsOverrecursedCheck_(false),
|
||||||
needsStaticStackAlignment_(false),
|
needsStaticStackAlignment_(false),
|
||||||
|
usesSimd_(false),
|
||||||
|
cachedUsesSimd_(false),
|
||||||
modifiesFrameArguments_(false),
|
modifiesFrameArguments_(false),
|
||||||
instrumentedProfiling_(false),
|
instrumentedProfiling_(false),
|
||||||
instrumentedProfilingIsCached_(false),
|
instrumentedProfilingIsCached_(false),
|
||||||
|
@ -42,6 +44,37 @@ MIRGenerator::MIRGenerator(CompileRealm* realm, const JitCompileOptions& options
|
||||||
gs_(alloc)
|
gs_(alloc)
|
||||||
{ }
|
{ }
|
||||||
|
|
||||||
|
bool
|
||||||
|
MIRGenerator::usesSimd()
|
||||||
|
{
|
||||||
|
if (cachedUsesSimd_)
|
||||||
|
return usesSimd_;
|
||||||
|
|
||||||
|
cachedUsesSimd_ = true;
|
||||||
|
for (ReversePostorderIterator block = graph_->rpoBegin(),
|
||||||
|
end = graph_->rpoEnd();
|
||||||
|
block != end;
|
||||||
|
block++)
|
||||||
|
{
|
||||||
|
// It's fine to use MInstructionIterator here because we don't have to
|
||||||
|
// worry about Phis, since any reachable phi (or phi cycle) will have at
|
||||||
|
// least one instruction as an input.
|
||||||
|
for (MInstructionIterator inst = block->begin(); inst != block->end(); inst++) {
|
||||||
|
// Instructions that have SIMD inputs but not a SIMD type are fine
|
||||||
|
// to ignore, as their inputs are also reached at some point. By
|
||||||
|
// induction, at least one instruction with a SIMD type is reached
|
||||||
|
// at some point.
|
||||||
|
if (IsSimdType(inst->type())) {
|
||||||
|
MOZ_ASSERT(SupportsSimd);
|
||||||
|
usesSimd_ = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
usesSimd_ = false;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
mozilla::GenericErrorResult<AbortReason>
|
mozilla::GenericErrorResult<AbortReason>
|
||||||
MIRGenerator::abort(AbortReason r)
|
MIRGenerator::abort(AbortReason r)
|
||||||
{
|
{
|
||||||
|
|
|
@ -340,7 +340,8 @@ template void MacroAssembler::guardTypeSet(const TypedOrValueRegister& value, co
|
||||||
|
|
||||||
template<typename S, typename T>
|
template<typename S, typename T>
|
||||||
static void
|
static void
|
||||||
StoreToTypedFloatArray(MacroAssembler& masm, int arrayType, const S& value, const T& dest)
|
StoreToTypedFloatArray(MacroAssembler& masm, int arrayType, const S& value, const T& dest,
|
||||||
|
unsigned numElems)
|
||||||
{
|
{
|
||||||
switch (arrayType) {
|
switch (arrayType) {
|
||||||
case Scalar::Float32:
|
case Scalar::Float32:
|
||||||
|
@ -349,6 +350,48 @@ StoreToTypedFloatArray(MacroAssembler& masm, int arrayType, const S& value, cons
|
||||||
case Scalar::Float64:
|
case Scalar::Float64:
|
||||||
masm.storeDouble(value, dest);
|
masm.storeDouble(value, dest);
|
||||||
break;
|
break;
|
||||||
|
case Scalar::Float32x4:
|
||||||
|
switch (numElems) {
|
||||||
|
case 1:
|
||||||
|
masm.storeFloat32(value, dest);
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
masm.storeDouble(value, dest);
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
masm.storeFloat32x3(value, dest);
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
masm.storeUnalignedSimd128Float(value, dest);
|
||||||
|
break;
|
||||||
|
default: MOZ_CRASH("unexpected number of elements in simd write");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case Scalar::Int32x4:
|
||||||
|
switch (numElems) {
|
||||||
|
case 1:
|
||||||
|
masm.storeInt32x1(value, dest);
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
masm.storeInt32x2(value, dest);
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
masm.storeInt32x3(value, dest);
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
masm.storeUnalignedSimd128Int(value, dest);
|
||||||
|
break;
|
||||||
|
default: MOZ_CRASH("unexpected number of elements in simd write");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case Scalar::Int8x16:
|
||||||
|
MOZ_ASSERT(numElems == 16, "unexpected partial store");
|
||||||
|
masm.storeUnalignedSimd128Int(value, dest);
|
||||||
|
break;
|
||||||
|
case Scalar::Int16x8:
|
||||||
|
MOZ_ASSERT(numElems == 8, "unexpected partial store");
|
||||||
|
masm.storeUnalignedSimd128Int(value, dest);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
MOZ_CRASH("Invalid typed array type");
|
MOZ_CRASH("Invalid typed array type");
|
||||||
}
|
}
|
||||||
|
@ -356,21 +399,21 @@ StoreToTypedFloatArray(MacroAssembler& masm, int arrayType, const S& value, cons
|
||||||
|
|
||||||
void
|
void
|
||||||
MacroAssembler::storeToTypedFloatArray(Scalar::Type arrayType, FloatRegister value,
|
MacroAssembler::storeToTypedFloatArray(Scalar::Type arrayType, FloatRegister value,
|
||||||
const BaseIndex& dest)
|
const BaseIndex& dest, unsigned numElems)
|
||||||
{
|
{
|
||||||
StoreToTypedFloatArray(*this, arrayType, value, dest);
|
StoreToTypedFloatArray(*this, arrayType, value, dest, numElems);
|
||||||
}
|
}
|
||||||
void
|
void
|
||||||
MacroAssembler::storeToTypedFloatArray(Scalar::Type arrayType, FloatRegister value,
|
MacroAssembler::storeToTypedFloatArray(Scalar::Type arrayType, FloatRegister value,
|
||||||
const Address& dest)
|
const Address& dest, unsigned numElems)
|
||||||
{
|
{
|
||||||
StoreToTypedFloatArray(*this, arrayType, value, dest);
|
StoreToTypedFloatArray(*this, arrayType, value, dest, numElems);
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
void
|
void
|
||||||
MacroAssembler::loadFromTypedArray(Scalar::Type arrayType, const T& src, AnyRegister dest, Register temp,
|
MacroAssembler::loadFromTypedArray(Scalar::Type arrayType, const T& src, AnyRegister dest, Register temp,
|
||||||
Label* fail, bool canonicalizeDoubles)
|
Label* fail, bool canonicalizeDoubles, unsigned numElems)
|
||||||
{
|
{
|
||||||
switch (arrayType) {
|
switch (arrayType) {
|
||||||
case Scalar::Int8:
|
case Scalar::Int8:
|
||||||
|
@ -411,17 +454,59 @@ MacroAssembler::loadFromTypedArray(Scalar::Type arrayType, const T& src, AnyRegi
|
||||||
if (canonicalizeDoubles)
|
if (canonicalizeDoubles)
|
||||||
canonicalizeDouble(dest.fpu());
|
canonicalizeDouble(dest.fpu());
|
||||||
break;
|
break;
|
||||||
|
case Scalar::Int32x4:
|
||||||
|
switch (numElems) {
|
||||||
|
case 1:
|
||||||
|
loadInt32x1(src, dest.fpu());
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
loadInt32x2(src, dest.fpu());
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
loadInt32x3(src, dest.fpu());
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
loadUnalignedSimd128Int(src, dest.fpu());
|
||||||
|
break;
|
||||||
|
default: MOZ_CRASH("unexpected number of elements in SIMD load");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case Scalar::Float32x4:
|
||||||
|
switch (numElems) {
|
||||||
|
case 1:
|
||||||
|
loadFloat32(src, dest.fpu());
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
loadDouble(src, dest.fpu());
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
loadFloat32x3(src, dest.fpu());
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
loadUnalignedSimd128Float(src, dest.fpu());
|
||||||
|
break;
|
||||||
|
default: MOZ_CRASH("unexpected number of elements in SIMD load");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case Scalar::Int8x16:
|
||||||
|
MOZ_ASSERT(numElems == 16, "unexpected partial load");
|
||||||
|
loadUnalignedSimd128Int(src, dest.fpu());
|
||||||
|
break;
|
||||||
|
case Scalar::Int16x8:
|
||||||
|
MOZ_ASSERT(numElems == 8, "unexpected partial load");
|
||||||
|
loadUnalignedSimd128Int(src, dest.fpu());
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
MOZ_CRASH("Invalid typed array type");
|
MOZ_CRASH("Invalid typed array type");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
template void MacroAssembler::loadFromTypedArray(Scalar::Type arrayType, const Address& src,
|
template void MacroAssembler::loadFromTypedArray(Scalar::Type arrayType, const Address& src, AnyRegister dest,
|
||||||
AnyRegister dest, Register temp, Label* fail,
|
Register temp, Label* fail, bool canonicalizeDoubles,
|
||||||
bool canonicalizeDoubles);
|
unsigned numElems);
|
||||||
template void MacroAssembler::loadFromTypedArray(Scalar::Type arrayType, const BaseIndex& src,
|
template void MacroAssembler::loadFromTypedArray(Scalar::Type arrayType, const BaseIndex& src, AnyRegister dest,
|
||||||
AnyRegister dest, Register temp, Label* fail,
|
Register temp, Label* fail, bool canonicalizeDoubles,
|
||||||
bool canonicalizeDoubles);
|
unsigned numElems);
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
void
|
void
|
||||||
|
@ -3310,6 +3395,41 @@ MacroAssembler::branchIfInlineTypedObject(Register obj, Register scratch, Label*
|
||||||
branchPtr(Assembler::Equal, scratch, ImmPtr(&InlineTransparentTypedObject::class_), label);
|
branchPtr(Assembler::Equal, scratch, ImmPtr(&InlineTransparentTypedObject::class_), label);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
MacroAssembler::branchIfNotSimdObject(Register obj, Register scratch, SimdType simdType,
|
||||||
|
Label* label)
|
||||||
|
{
|
||||||
|
loadPtr(Address(obj, JSObject::offsetOfGroup()), scratch);
|
||||||
|
|
||||||
|
// Guard that the object has the same representation as the one produced for
|
||||||
|
// SIMD value-type.
|
||||||
|
Address clasp(scratch, ObjectGroup::offsetOfClasp());
|
||||||
|
static_assert(!SimdTypeDescr::Opaque, "SIMD objects are transparent");
|
||||||
|
branchPtr(Assembler::NotEqual, clasp, ImmPtr(&InlineTransparentTypedObject::class_),
|
||||||
|
label);
|
||||||
|
|
||||||
|
// obj->type()->typeDescr()
|
||||||
|
// The previous class pointer comparison implies that the addendumKind is
|
||||||
|
// Addendum_TypeDescr.
|
||||||
|
loadPtr(Address(scratch, ObjectGroup::offsetOfAddendum()), scratch);
|
||||||
|
|
||||||
|
// Check for the /Kind/ reserved slot of the TypeDescr. This is an Int32
|
||||||
|
// Value which is equivalent to the object class check.
|
||||||
|
static_assert(JS_DESCR_SLOT_KIND < NativeObject::MAX_FIXED_SLOTS, "Load from fixed slots");
|
||||||
|
Address typeDescrKind(scratch, NativeObject::getFixedSlotOffset(JS_DESCR_SLOT_KIND));
|
||||||
|
assertTestInt32(Assembler::Equal, typeDescrKind,
|
||||||
|
"MOZ_ASSERT(obj->type()->typeDescr()->getReservedSlot(JS_DESCR_SLOT_KIND).isInt32())");
|
||||||
|
branch32(Assembler::NotEqual, ToPayload(typeDescrKind), Imm32(js::type::Simd), label);
|
||||||
|
|
||||||
|
// Check if the SimdTypeDescr /Type/ matches the specialization of this
|
||||||
|
// MSimdUnbox instruction.
|
||||||
|
static_assert(JS_DESCR_SLOT_TYPE < NativeObject::MAX_FIXED_SLOTS, "Load from fixed slots");
|
||||||
|
Address typeDescrType(scratch, NativeObject::getFixedSlotOffset(JS_DESCR_SLOT_TYPE));
|
||||||
|
assertTestInt32(Assembler::Equal, typeDescrType,
|
||||||
|
"MOZ_ASSERT(obj->type()->typeDescr()->getReservedSlot(JS_DESCR_SLOT_TYPE).isInt32())");
|
||||||
|
branch32(Assembler::NotEqual, ToPayload(typeDescrType), Imm32(int32_t(simdType)), label);
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
MacroAssembler::copyObjGroupNoPreBarrier(Register sourceObj, Register destObj, Register scratch)
|
MacroAssembler::copyObjGroupNoPreBarrier(Register sourceObj, Register destObj, Register scratch)
|
||||||
{
|
{
|
||||||
|
|
|
@ -1158,6 +1158,8 @@ class MacroAssembler : public MacroAssemblerSpecific
|
||||||
|
|
||||||
void branchIfInlineTypedObject(Register obj, Register scratch, Label* label);
|
void branchIfInlineTypedObject(Register obj, Register scratch, Label* label);
|
||||||
|
|
||||||
|
void branchIfNotSimdObject(Register obj, Register scratch, SimdType simdType, Label* label);
|
||||||
|
|
||||||
inline void branchTestClassIsProxy(bool proxy, Register clasp, Label* label);
|
inline void branchTestClassIsProxy(bool proxy, Register clasp, Label* label);
|
||||||
|
|
||||||
inline void branchTestObjectIsProxy(bool proxy, Register object, Register scratch, Label* label);
|
inline void branchTestObjectIsProxy(bool proxy, Register object, Register scratch, Label* label);
|
||||||
|
@ -1374,6 +1376,9 @@ class MacroAssembler : public MacroAssemblerSpecific
|
||||||
inline void canonicalizeFloat(FloatRegister reg);
|
inline void canonicalizeFloat(FloatRegister reg);
|
||||||
inline void canonicalizeFloatIfDeterministic(FloatRegister reg);
|
inline void canonicalizeFloatIfDeterministic(FloatRegister reg);
|
||||||
|
|
||||||
|
inline void canonicalizeFloat32x4(FloatRegister reg, FloatRegister scratch)
|
||||||
|
DEFINED_ON(x86_shared);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
// ========================================================================
|
// ========================================================================
|
||||||
// Memory access primitives.
|
// Memory access primitives.
|
||||||
|
@ -2148,7 +2153,7 @@ class MacroAssembler : public MacroAssemblerSpecific
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
void loadFromTypedArray(Scalar::Type arrayType, const T& src, AnyRegister dest, Register temp, Label* fail,
|
void loadFromTypedArray(Scalar::Type arrayType, const T& src, AnyRegister dest, Register temp, Label* fail,
|
||||||
bool canonicalizeDoubles = true);
|
bool canonicalizeDoubles = true, unsigned numElems = 0);
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
void loadFromTypedArray(Scalar::Type arrayType, const T& src, const ValueOperand& dest, bool allowDouble,
|
void loadFromTypedArray(Scalar::Type arrayType, const T& src, const ValueOperand& dest, bool allowDouble,
|
||||||
|
@ -2175,8 +2180,10 @@ class MacroAssembler : public MacroAssemblerSpecific
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void storeToTypedFloatArray(Scalar::Type arrayType, FloatRegister value, const BaseIndex& dest);
|
void storeToTypedFloatArray(Scalar::Type arrayType, FloatRegister value, const BaseIndex& dest,
|
||||||
void storeToTypedFloatArray(Scalar::Type arrayType, FloatRegister value, const Address& dest);
|
unsigned numElems = 0);
|
||||||
|
void storeToTypedFloatArray(Scalar::Type arrayType, FloatRegister value, const Address& dest,
|
||||||
|
unsigned numElems = 0);
|
||||||
|
|
||||||
void memoryBarrierBefore(const Synchronization& sync);
|
void memoryBarrierBefore(const Synchronization& sync);
|
||||||
void memoryBarrierAfter(const Synchronization& sync);
|
void memoryBarrierAfter(const Synchronization& sync);
|
||||||
|
|
|
@ -1785,6 +1785,10 @@ GetTypedArrayRange(TempAllocator& alloc, Scalar::Type type)
|
||||||
case Scalar::Int64:
|
case Scalar::Int64:
|
||||||
case Scalar::Float32:
|
case Scalar::Float32:
|
||||||
case Scalar::Float64:
|
case Scalar::Float64:
|
||||||
|
case Scalar::Float32x4:
|
||||||
|
case Scalar::Int8x16:
|
||||||
|
case Scalar::Int16x8:
|
||||||
|
case Scalar::Int32x4:
|
||||||
case Scalar::MaxTypedArrayViewType:
|
case Scalar::MaxTypedArrayViewType:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,6 +10,7 @@
|
||||||
#include "jsmath.h"
|
#include "jsmath.h"
|
||||||
|
|
||||||
#include "builtin/RegExp.h"
|
#include "builtin/RegExp.h"
|
||||||
|
#include "builtin/SIMD.h"
|
||||||
#include "builtin/String.h"
|
#include "builtin/String.h"
|
||||||
#include "builtin/TypedObject.h"
|
#include "builtin/TypedObject.h"
|
||||||
#include "gc/Heap.h"
|
#include "gc/Heap.h"
|
||||||
|
@ -1661,6 +1662,79 @@ RNewCallObject::recover(JSContext* cx, SnapshotIterator& iter) const
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
MSimdBox::writeRecoverData(CompactBufferWriter& writer) const
|
||||||
|
{
|
||||||
|
MOZ_ASSERT(canRecoverOnBailout());
|
||||||
|
writer.writeUnsigned(uint32_t(RInstruction::Recover_SimdBox));
|
||||||
|
static_assert(unsigned(SimdType::Count) < 0x100, "assuming SimdType fits in 8 bits");
|
||||||
|
writer.writeByte(uint8_t(simdType()));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
RSimdBox::RSimdBox(CompactBufferReader& reader)
|
||||||
|
{
|
||||||
|
type_ = reader.readByte();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
RSimdBox::recover(JSContext* cx, SnapshotIterator& iter) const
|
||||||
|
{
|
||||||
|
JSObject* resultObject = nullptr;
|
||||||
|
RValueAllocation a = iter.readAllocation();
|
||||||
|
MOZ_ASSERT(iter.allocationReadable(a));
|
||||||
|
MOZ_ASSERT_IF(a.mode() == RValueAllocation::ANY_FLOAT_REG, a.fpuReg().isSimd128());
|
||||||
|
const FloatRegisters::RegisterContent* raw = iter.floatAllocationPointer(a);
|
||||||
|
switch (SimdType(type_)) {
|
||||||
|
case SimdType::Bool8x16:
|
||||||
|
resultObject = js::CreateSimd<Bool8x16>(cx, (const Bool8x16::Elem*) raw);
|
||||||
|
break;
|
||||||
|
case SimdType::Int8x16:
|
||||||
|
resultObject = js::CreateSimd<Int8x16>(cx, (const Int8x16::Elem*) raw);
|
||||||
|
break;
|
||||||
|
case SimdType::Uint8x16:
|
||||||
|
resultObject = js::CreateSimd<Uint8x16>(cx, (const Uint8x16::Elem*) raw);
|
||||||
|
break;
|
||||||
|
case SimdType::Bool16x8:
|
||||||
|
resultObject = js::CreateSimd<Bool16x8>(cx, (const Bool16x8::Elem*) raw);
|
||||||
|
break;
|
||||||
|
case SimdType::Int16x8:
|
||||||
|
resultObject = js::CreateSimd<Int16x8>(cx, (const Int16x8::Elem*) raw);
|
||||||
|
break;
|
||||||
|
case SimdType::Uint16x8:
|
||||||
|
resultObject = js::CreateSimd<Uint16x8>(cx, (const Uint16x8::Elem*) raw);
|
||||||
|
break;
|
||||||
|
case SimdType::Bool32x4:
|
||||||
|
resultObject = js::CreateSimd<Bool32x4>(cx, (const Bool32x4::Elem*) raw);
|
||||||
|
break;
|
||||||
|
case SimdType::Int32x4:
|
||||||
|
resultObject = js::CreateSimd<Int32x4>(cx, (const Int32x4::Elem*) raw);
|
||||||
|
break;
|
||||||
|
case SimdType::Uint32x4:
|
||||||
|
resultObject = js::CreateSimd<Uint32x4>(cx, (const Uint32x4::Elem*) raw);
|
||||||
|
break;
|
||||||
|
case SimdType::Float32x4:
|
||||||
|
resultObject = js::CreateSimd<Float32x4>(cx, (const Float32x4::Elem*) raw);
|
||||||
|
break;
|
||||||
|
case SimdType::Float64x2:
|
||||||
|
MOZ_CRASH("NYI, RSimdBox of Float64x2");
|
||||||
|
break;
|
||||||
|
case SimdType::Bool64x2:
|
||||||
|
MOZ_CRASH("NYI, RSimdBox of Bool64x2");
|
||||||
|
break;
|
||||||
|
case SimdType::Count:
|
||||||
|
MOZ_CRASH("RSimdBox of Count is unreachable");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!resultObject)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
RootedValue result(cx);
|
||||||
|
result.setObject(*resultObject);
|
||||||
|
iter.storeInstructionResult(result);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
MObjectState::writeRecoverData(CompactBufferWriter& writer) const
|
MObjectState::writeRecoverData(CompactBufferWriter& writer) const
|
||||||
{
|
{
|
||||||
|
|
|
@ -112,6 +112,7 @@ namespace jit {
|
||||||
_(CreateThisWithTemplate) \
|
_(CreateThisWithTemplate) \
|
||||||
_(Lambda) \
|
_(Lambda) \
|
||||||
_(LambdaArrow) \
|
_(LambdaArrow) \
|
||||||
|
_(SimdBox) \
|
||||||
_(ObjectState) \
|
_(ObjectState) \
|
||||||
_(ArrayState) \
|
_(ArrayState) \
|
||||||
_(SetArrayLength) \
|
_(SetArrayLength) \
|
||||||
|
@ -689,6 +690,17 @@ class RNewCallObject final : public RInstruction
|
||||||
MOZ_MUST_USE bool recover(JSContext* cx, SnapshotIterator& iter) const override;
|
MOZ_MUST_USE bool recover(JSContext* cx, SnapshotIterator& iter) const override;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class RSimdBox final : public RInstruction
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
uint8_t type_;
|
||||||
|
|
||||||
|
public:
|
||||||
|
RINSTRUCTION_HEADER_NUM_OP_(SimdBox, 1)
|
||||||
|
|
||||||
|
MOZ_MUST_USE bool recover(JSContext* cx, SnapshotIterator& iter) const override;
|
||||||
|
};
|
||||||
|
|
||||||
class RObjectState final : public RInstruction
|
class RObjectState final : public RInstruction
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
|
|
|
@ -637,6 +637,45 @@ template bool NoFloatPolicyAfter<0>::adjustInputs(TempAllocator& alloc, MInstruc
|
||||||
template bool NoFloatPolicyAfter<1>::adjustInputs(TempAllocator& alloc, MInstruction* def) const;
|
template bool NoFloatPolicyAfter<1>::adjustInputs(TempAllocator& alloc, MInstruction* def) const;
|
||||||
template bool NoFloatPolicyAfter<2>::adjustInputs(TempAllocator& alloc, MInstruction* def) const;
|
template bool NoFloatPolicyAfter<2>::adjustInputs(TempAllocator& alloc, MInstruction* def) const;
|
||||||
|
|
||||||
|
template <unsigned Op>
|
||||||
|
bool
|
||||||
|
SimdScalarPolicy<Op>::staticAdjustInputs(TempAllocator& alloc, MInstruction* ins)
|
||||||
|
{
|
||||||
|
MOZ_ASSERT(IsSimdType(ins->type()));
|
||||||
|
MIRType laneType = SimdTypeToLaneType(ins->type());
|
||||||
|
|
||||||
|
MDefinition* in = ins->getOperand(Op);
|
||||||
|
|
||||||
|
// A vector with boolean lanes requires Int32 inputs that have already been
|
||||||
|
// converted to 0/-1.
|
||||||
|
// We can't insert a MIRType::Boolean lane directly - it requires conversion.
|
||||||
|
if (laneType == MIRType::Boolean) {
|
||||||
|
MOZ_ASSERT(in->type() == MIRType::Int32, "Boolean SIMD vector requires Int32 lanes.");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (in->type() == laneType)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
MInstruction* replace;
|
||||||
|
if (laneType == MIRType::Int32) {
|
||||||
|
replace = MTruncateToInt32::New(alloc, in);
|
||||||
|
} else {
|
||||||
|
MOZ_ASSERT(laneType == MIRType::Float32);
|
||||||
|
replace = MToFloat32::New(alloc, in);
|
||||||
|
}
|
||||||
|
|
||||||
|
ins->block()->insertBefore(ins, replace);
|
||||||
|
ins->replaceOperand(Op, replace);
|
||||||
|
|
||||||
|
return replace->typePolicy()->adjustInputs(alloc, replace);
|
||||||
|
}
|
||||||
|
|
||||||
|
template bool SimdScalarPolicy<0>::staticAdjustInputs(TempAllocator& alloc, MInstruction* def);
|
||||||
|
template bool SimdScalarPolicy<1>::staticAdjustInputs(TempAllocator& alloc, MInstruction* def);
|
||||||
|
template bool SimdScalarPolicy<2>::staticAdjustInputs(TempAllocator& alloc, MInstruction* def);
|
||||||
|
template bool SimdScalarPolicy<3>::staticAdjustInputs(TempAllocator& alloc, MInstruction* def);
|
||||||
|
|
||||||
template <unsigned Op>
|
template <unsigned Op>
|
||||||
bool
|
bool
|
||||||
BoxPolicy<Op>::staticAdjustInputs(TempAllocator& alloc, MInstruction* ins)
|
BoxPolicy<Op>::staticAdjustInputs(TempAllocator& alloc, MInstruction* ins)
|
||||||
|
@ -818,6 +857,75 @@ template bool ObjectPolicy<1>::staticAdjustInputs(TempAllocator& alloc, MInstruc
|
||||||
template bool ObjectPolicy<2>::staticAdjustInputs(TempAllocator& alloc, MInstruction* ins);
|
template bool ObjectPolicy<2>::staticAdjustInputs(TempAllocator& alloc, MInstruction* ins);
|
||||||
template bool ObjectPolicy<3>::staticAdjustInputs(TempAllocator& alloc, MInstruction* ins);
|
template bool ObjectPolicy<3>::staticAdjustInputs(TempAllocator& alloc, MInstruction* ins);
|
||||||
|
|
||||||
|
template <unsigned Op>
|
||||||
|
bool
|
||||||
|
SimdSameAsReturnedTypePolicy<Op>::staticAdjustInputs(TempAllocator& alloc, MInstruction* ins)
|
||||||
|
{
|
||||||
|
MOZ_ASSERT(ins->type() == ins->getOperand(Op)->type());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
template bool
|
||||||
|
SimdSameAsReturnedTypePolicy<0>::staticAdjustInputs(TempAllocator& alloc, MInstruction* ins);
|
||||||
|
template bool
|
||||||
|
SimdSameAsReturnedTypePolicy<1>::staticAdjustInputs(TempAllocator& alloc, MInstruction* ins);
|
||||||
|
|
||||||
|
bool
|
||||||
|
SimdAllPolicy::adjustInputs(TempAllocator& alloc, MInstruction* ins) const
|
||||||
|
{
|
||||||
|
for (unsigned i = 0, e = ins->numOperands(); i < e; i++)
|
||||||
|
MOZ_ASSERT(ins->getOperand(i)->type() == ins->typePolicySpecialization());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <unsigned Op>
|
||||||
|
bool
|
||||||
|
SimdPolicy<Op>::adjustInputs(TempAllocator& alloc, MInstruction* ins) const
|
||||||
|
{
|
||||||
|
MOZ_ASSERT(ins->typePolicySpecialization() == ins->getOperand(Op)->type());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
template bool
|
||||||
|
SimdPolicy<0>::adjustInputs(TempAllocator& alloc, MInstruction* ins) const;
|
||||||
|
|
||||||
|
bool
|
||||||
|
SimdShufflePolicy::adjustInputs(TempAllocator& alloc, MInstruction* ins) const
|
||||||
|
{
|
||||||
|
MSimdGeneralShuffle* s = ins->toSimdGeneralShuffle();
|
||||||
|
|
||||||
|
for (unsigned i = 0; i < s->numVectors(); i++)
|
||||||
|
MOZ_ASSERT(ins->getOperand(i)->type() == ins->typePolicySpecialization());
|
||||||
|
|
||||||
|
// Next inputs are the lanes, which need to be int32
|
||||||
|
for (unsigned i = 0; i < s->numLanes(); i++) {
|
||||||
|
MDefinition* in = ins->getOperand(s->numVectors() + i);
|
||||||
|
if (in->type() == MIRType::Int32)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
auto* replace = MToNumberInt32::New(alloc, in, IntConversionInputKind::NumbersOnly);
|
||||||
|
ins->block()->insertBefore(ins, replace);
|
||||||
|
ins->replaceOperand(s->numVectors() + i, replace);
|
||||||
|
if (!replace->typePolicy()->adjustInputs(alloc, replace))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
SimdSelectPolicy::adjustInputs(TempAllocator& alloc, MInstruction* ins) const
|
||||||
|
{
|
||||||
|
// First input is the mask, which has to be a boolean.
|
||||||
|
MOZ_ASSERT(IsBooleanSimdType(ins->getOperand(0)->type()));
|
||||||
|
|
||||||
|
// Next inputs are the two vectors of a particular type.
|
||||||
|
for (unsigned i = 1; i < 3; i++)
|
||||||
|
MOZ_ASSERT(ins->getOperand(i)->type() == ins->typePolicySpecialization());
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
CallPolicy::adjustInputs(TempAllocator& alloc, MInstruction* ins) const
|
CallPolicy::adjustInputs(TempAllocator& alloc, MInstruction* ins) const
|
||||||
{
|
{
|
||||||
|
@ -875,6 +983,13 @@ StoreUnboxedScalarPolicy::adjustValueInput(TempAllocator& alloc, MInstruction* i
|
||||||
Scalar::Type writeType, MDefinition* value,
|
Scalar::Type writeType, MDefinition* value,
|
||||||
int valueOperand)
|
int valueOperand)
|
||||||
{
|
{
|
||||||
|
// Storing a SIMD value requires a valueOperand that has already been
|
||||||
|
// SimdUnboxed. See IonBuilder::inlineSimdStore(()
|
||||||
|
if (Scalar::isSimdType(writeType)) {
|
||||||
|
MOZ_ASSERT(IsSimdType(value->type()));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
MDefinition* curValue = value;
|
MDefinition* curValue = value;
|
||||||
// First, ensure the value is int32, boolean, double or Value.
|
// First, ensure the value is int32, boolean, double or Value.
|
||||||
// The conversion is based on TypedArrayObjectTemplate::setElementTail.
|
// The conversion is based on TypedArrayObjectTemplate::setElementTail.
|
||||||
|
@ -1155,6 +1270,9 @@ FilterTypeSetPolicy::adjustInputs(TempAllocator& alloc, MInstruction* ins) const
|
||||||
_(PowPolicy) \
|
_(PowPolicy) \
|
||||||
_(SameValuePolicy) \
|
_(SameValuePolicy) \
|
||||||
_(SignPolicy) \
|
_(SignPolicy) \
|
||||||
|
_(SimdAllPolicy) \
|
||||||
|
_(SimdSelectPolicy) \
|
||||||
|
_(SimdShufflePolicy) \
|
||||||
_(StoreTypedArrayHolePolicy) \
|
_(StoreTypedArrayHolePolicy) \
|
||||||
_(StoreUnboxedScalarPolicy) \
|
_(StoreUnboxedScalarPolicy) \
|
||||||
_(StoreUnboxedObjectOrNullPolicy) \
|
_(StoreUnboxedObjectOrNullPolicy) \
|
||||||
|
@ -1192,6 +1310,7 @@ FilterTypeSetPolicy::adjustInputs(TempAllocator& alloc, MInstruction* ins) const
|
||||||
_(MixPolicy<ObjectPolicy<0>, UnboxedInt32Policy<1>, UnboxedInt32Policy<2>, UnboxedInt32Policy<3>>) \
|
_(MixPolicy<ObjectPolicy<0>, UnboxedInt32Policy<1>, UnboxedInt32Policy<2>, UnboxedInt32Policy<3>>) \
|
||||||
_(MixPolicy<ObjectPolicy<0>, UnboxedInt32Policy<1>, TruncateToInt32Policy<2>, TruncateToInt32Policy<3> >) \
|
_(MixPolicy<ObjectPolicy<0>, UnboxedInt32Policy<1>, TruncateToInt32Policy<2>, TruncateToInt32Policy<3> >) \
|
||||||
_(MixPolicy<ObjectPolicy<0>, CacheIdPolicy<1>, NoFloatPolicy<2>>) \
|
_(MixPolicy<ObjectPolicy<0>, CacheIdPolicy<1>, NoFloatPolicy<2>>) \
|
||||||
|
_(MixPolicy<SimdScalarPolicy<0>, SimdScalarPolicy<1>, SimdScalarPolicy<2>, SimdScalarPolicy<3> >) \
|
||||||
_(MixPolicy<ObjectPolicy<0>, BoxExceptPolicy<1, MIRType::Object>, CacheIdPolicy<2>>) \
|
_(MixPolicy<ObjectPolicy<0>, BoxExceptPolicy<1, MIRType::Object>, CacheIdPolicy<2>>) \
|
||||||
_(MixPolicy<BoxPolicy<0>, ObjectPolicy<1> >) \
|
_(MixPolicy<BoxPolicy<0>, ObjectPolicy<1> >) \
|
||||||
_(MixPolicy<ConvertToStringPolicy<0>, ConvertToStringPolicy<1> >) \
|
_(MixPolicy<ConvertToStringPolicy<0>, ConvertToStringPolicy<1> >) \
|
||||||
|
@ -1211,6 +1330,8 @@ FilterTypeSetPolicy::adjustInputs(TempAllocator& alloc, MInstruction* ins) const
|
||||||
_(MixPolicy<ObjectPolicy<0>, StringPolicy<1> >) \
|
_(MixPolicy<ObjectPolicy<0>, StringPolicy<1> >) \
|
||||||
_(MixPolicy<ObjectPolicy<0>, ConvertToStringPolicy<2> >) \
|
_(MixPolicy<ObjectPolicy<0>, ConvertToStringPolicy<2> >) \
|
||||||
_(MixPolicy<ObjectPolicy<1>, ConvertToStringPolicy<0> >) \
|
_(MixPolicy<ObjectPolicy<1>, ConvertToStringPolicy<0> >) \
|
||||||
|
_(MixPolicy<SimdSameAsReturnedTypePolicy<0>, SimdSameAsReturnedTypePolicy<1> >) \
|
||||||
|
_(MixPolicy<SimdSameAsReturnedTypePolicy<0>, SimdScalarPolicy<1> >) \
|
||||||
_(MixPolicy<StringPolicy<0>, UnboxedInt32Policy<1> >) \
|
_(MixPolicy<StringPolicy<0>, UnboxedInt32Policy<1> >) \
|
||||||
_(MixPolicy<StringPolicy<0>, StringPolicy<1> >) \
|
_(MixPolicy<StringPolicy<0>, StringPolicy<1> >) \
|
||||||
_(MixPolicy<BoxPolicy<0>, BoxPolicy<1> >) \
|
_(MixPolicy<BoxPolicy<0>, BoxPolicy<1> >) \
|
||||||
|
@ -1221,6 +1342,9 @@ FilterTypeSetPolicy::adjustInputs(TempAllocator& alloc, MInstruction* ins) const
|
||||||
_(ObjectPolicy<0>) \
|
_(ObjectPolicy<0>) \
|
||||||
_(ObjectPolicy<1>) \
|
_(ObjectPolicy<1>) \
|
||||||
_(ObjectPolicy<3>) \
|
_(ObjectPolicy<3>) \
|
||||||
|
_(SimdPolicy<0>) \
|
||||||
|
_(SimdSameAsReturnedTypePolicy<0>) \
|
||||||
|
_(SimdScalarPolicy<0>) \
|
||||||
_(StringPolicy<0>)
|
_(StringPolicy<0>)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -365,6 +365,67 @@ class ObjectPolicy final : public TypePolicy
|
||||||
// a primitive, we use ValueToNonNullObject.
|
// a primitive, we use ValueToNonNullObject.
|
||||||
typedef ObjectPolicy<0> SingleObjectPolicy;
|
typedef ObjectPolicy<0> SingleObjectPolicy;
|
||||||
|
|
||||||
|
// Convert an operand to have a type identical to the scalar type of the
|
||||||
|
// returned type of the instruction.
|
||||||
|
template <unsigned Op>
|
||||||
|
class SimdScalarPolicy final : public TypePolicy
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
constexpr SimdScalarPolicy() { }
|
||||||
|
EMPTY_DATA_;
|
||||||
|
static MOZ_MUST_USE bool staticAdjustInputs(TempAllocator& alloc, MInstruction* def);
|
||||||
|
MOZ_MUST_USE bool adjustInputs(TempAllocator& alloc, MInstruction* def) const override {
|
||||||
|
return staticAdjustInputs(alloc, def);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class SimdAllPolicy final : public TypePolicy
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
constexpr SimdAllPolicy () { }
|
||||||
|
SPECIALIZATION_DATA_;
|
||||||
|
MOZ_MUST_USE bool adjustInputs(TempAllocator& alloc, MInstruction* ins) const override;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <unsigned Op>
|
||||||
|
class SimdPolicy final : public TypePolicy
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
constexpr SimdPolicy() { }
|
||||||
|
SPECIALIZATION_DATA_;
|
||||||
|
MOZ_MUST_USE bool adjustInputs(TempAllocator& alloc, MInstruction* ins) const override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class SimdSelectPolicy final : public TypePolicy
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
constexpr SimdSelectPolicy() { }
|
||||||
|
SPECIALIZATION_DATA_;
|
||||||
|
MOZ_MUST_USE bool adjustInputs(TempAllocator& alloc, MInstruction* ins) const override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class SimdShufflePolicy final : public TypePolicy
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
constexpr SimdShufflePolicy() { }
|
||||||
|
SPECIALIZATION_DATA_;
|
||||||
|
MOZ_MUST_USE bool adjustInputs(TempAllocator& alloc, MInstruction* ins) const override;
|
||||||
|
};
|
||||||
|
|
||||||
|
// SIMD value-type policy, use the returned type of the instruction to determine
|
||||||
|
// how to unbox its operand.
|
||||||
|
template <unsigned Op>
|
||||||
|
class SimdSameAsReturnedTypePolicy final : public TypePolicy
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
constexpr SimdSameAsReturnedTypePolicy() { }
|
||||||
|
EMPTY_DATA_;
|
||||||
|
static MOZ_MUST_USE bool staticAdjustInputs(TempAllocator& alloc, MInstruction* ins);
|
||||||
|
MOZ_MUST_USE bool adjustInputs(TempAllocator& alloc, MInstruction* ins) const override {
|
||||||
|
return staticAdjustInputs(alloc, ins);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
template <unsigned Op>
|
template <unsigned Op>
|
||||||
class BoxPolicy final : public TypePolicy
|
class BoxPolicy final : public TypePolicy
|
||||||
{
|
{
|
||||||
|
|
|
@ -119,6 +119,7 @@ TypedObjectPrediction::ofArrayKind() const
|
||||||
switch (kind()) {
|
switch (kind()) {
|
||||||
case type::Scalar:
|
case type::Scalar:
|
||||||
case type::Reference:
|
case type::Reference:
|
||||||
|
case type::Simd:
|
||||||
case type::Struct:
|
case type::Struct:
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
@ -206,6 +207,12 @@ TypedObjectPrediction::referenceType() const
|
||||||
return extractType<ReferenceTypeDescr>();
|
return extractType<ReferenceTypeDescr>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SimdType
|
||||||
|
TypedObjectPrediction::simdType() const
|
||||||
|
{
|
||||||
|
return descr().as<SimdTypeDescr>().type();
|
||||||
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
TypedObjectPrediction::hasKnownArrayLength(int32_t* length) const
|
TypedObjectPrediction::hasKnownArrayLength(int32_t* length) const
|
||||||
{
|
{
|
||||||
|
|
|
@ -165,10 +165,11 @@ class TypedObjectPrediction {
|
||||||
//////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////
|
||||||
// Simple operations
|
// Simple operations
|
||||||
//
|
//
|
||||||
// Only valid when |kind()| is Scalar or Reference.
|
// Only valid when |kind()| is Scalar, Reference, or Simd (as appropriate).
|
||||||
|
|
||||||
Scalar::Type scalarType() const;
|
Scalar::Type scalarType() const;
|
||||||
ReferenceType referenceType() const;
|
ReferenceType referenceType() const;
|
||||||
|
SimdType simdType() const;
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////
|
||||||
// Queries valid only for arrays.
|
// Queries valid only for arrays.
|
||||||
|
|
|
@ -3156,14 +3156,248 @@ CodeGenerator::visitWasmAtomicExchangeI64(LWasmAtomicExchangeI64* lir)
|
||||||
masm.atomicExchange64(Synchronization::Full(), addr, value, out);
|
masm.atomicExchange64(Synchronization::Full(), addr, value, out);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
CodeGenerator::visitSimdSplatX4(LSimdSplatX4* lir)
|
||||||
|
{
|
||||||
|
MOZ_CRASH("NYI");
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
CodeGenerator::visitSimd128Int(LSimd128Int* ins)
|
||||||
|
{
|
||||||
|
MOZ_CRASH("NYI");
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
CodeGenerator::visitSimd128Float(LSimd128Float* ins)
|
||||||
|
{
|
||||||
|
MOZ_CRASH("NYI");
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
CodeGenerator::visitSimdExtractElementI(LSimdExtractElementI* ins)
|
||||||
|
{
|
||||||
|
MOZ_CRASH("NYI");
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
CodeGenerator::visitSimdExtractElementF(LSimdExtractElementF* ins)
|
||||||
|
{
|
||||||
|
MOZ_CRASH("NYI");
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
CodeGenerator::visitSimdBinaryCompIx4(LSimdBinaryCompIx4* lir)
|
||||||
|
{
|
||||||
|
MOZ_CRASH("NYI");
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
CodeGenerator::visitSimdBinaryCompFx4(LSimdBinaryCompFx4* lir)
|
||||||
|
{
|
||||||
|
MOZ_CRASH("NYI");
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
CodeGenerator::visitSimdBinaryArithIx4(LSimdBinaryArithIx4* lir)
|
||||||
|
{
|
||||||
|
MOZ_CRASH("NYI");
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
CodeGenerator::visitSimdBinaryArithFx4(LSimdBinaryArithFx4* lir)
|
||||||
|
{
|
||||||
|
MOZ_CRASH("NYI");
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
CodeGenerator::visitSimdBinaryBitwise(LSimdBinaryBitwise* lir)
|
||||||
|
{
|
||||||
|
MOZ_CRASH("NYI");
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
CodeGenerator::visitNearbyInt(LNearbyInt*)
|
CodeGenerator::visitNearbyInt(LNearbyInt*)
|
||||||
{
|
{
|
||||||
MOZ_CRASH("NYI");
|
MOZ_CRASH("NYI");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
CodeGenerator::visitSimdShift(LSimdShift*)
|
||||||
|
{
|
||||||
|
MOZ_CRASH("NYI");
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
CodeGenerator::visitNearbyIntF(LNearbyIntF*)
|
CodeGenerator::visitNearbyIntF(LNearbyIntF*)
|
||||||
{
|
{
|
||||||
MOZ_CRASH("NYI");
|
MOZ_CRASH("NYI");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
CodeGenerator::visitSimdSelect(LSimdSelect*)
|
||||||
|
{
|
||||||
|
MOZ_CRASH("NYI");
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
CodeGenerator::visitSimdAllTrue(LSimdAllTrue*)
|
||||||
|
{
|
||||||
|
MOZ_CRASH("NYI");
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
CodeGenerator::visitSimdAnyTrue(LSimdAnyTrue*)
|
||||||
|
{
|
||||||
|
MOZ_CRASH("NYI");
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
CodeGenerator::visitSimdShuffle(LSimdShuffle*)
|
||||||
|
{
|
||||||
|
MOZ_CRASH("NYI");
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
CodeGenerator::visitSimdSplatX8(LSimdSplatX8*)
|
||||||
|
{
|
||||||
|
MOZ_CRASH("NYI");
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
CodeGenerator::visitSimdSplatX16(LSimdSplatX16*)
|
||||||
|
{
|
||||||
|
MOZ_CRASH("NYI");
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
CodeGenerator::visitSimdSwizzleF(LSimdSwizzleF*)
|
||||||
|
{
|
||||||
|
MOZ_CRASH("NYI");
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
CodeGenerator::visitSimdSwizzleI(LSimdSwizzleI*)
|
||||||
|
{
|
||||||
|
MOZ_CRASH("NYI");
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
CodeGenerator::visitSimdShuffleX4(LSimdShuffleX4*)
|
||||||
|
{
|
||||||
|
MOZ_CRASH("NYI");
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
CodeGenerator::visitSimdBinaryCompIx8(LSimdBinaryCompIx8*)
|
||||||
|
{
|
||||||
|
MOZ_CRASH("NYI");
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
CodeGenerator::visitSimdUnaryArithFx4(LSimdUnaryArithFx4*)
|
||||||
|
{
|
||||||
|
MOZ_CRASH("NYI");
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
CodeGenerator::visitSimdUnaryArithIx4(LSimdUnaryArithIx4*)
|
||||||
|
{
|
||||||
|
MOZ_CRASH("NYI");
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
CodeGenerator::visitSimdUnaryArithIx8(LSimdUnaryArithIx8*)
|
||||||
|
{
|
||||||
|
MOZ_CRASH("NYI");
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
CodeGenerator::visitFloat32x4ToInt32x4(LFloat32x4ToInt32x4*)
|
||||||
|
{
|
||||||
|
MOZ_CRASH("NYI");
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
CodeGenerator::visitInt32x4ToFloat32x4(LInt32x4ToFloat32x4*)
|
||||||
|
{
|
||||||
|
MOZ_CRASH("NYI");
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
CodeGenerator::visitSimdBinaryArithIx8(LSimdBinaryArithIx8*)
|
||||||
|
{
|
||||||
|
MOZ_CRASH("NYI");
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
CodeGenerator::visitSimdBinaryCompIx16(LSimdBinaryCompIx16*)
|
||||||
|
{
|
||||||
|
MOZ_CRASH("NYI");
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
CodeGenerator::visitSimdInsertElementF(LSimdInsertElementF*)
|
||||||
|
{
|
||||||
|
MOZ_CRASH("NYI");
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
CodeGenerator::visitSimdInsertElementI(LSimdInsertElementI*)
|
||||||
|
{
|
||||||
|
MOZ_CRASH("NYI");
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
CodeGenerator::visitSimdUnaryArithIx16(LSimdUnaryArithIx16*)
|
||||||
|
{
|
||||||
|
MOZ_CRASH("NYI");
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
CodeGenerator::visitFloat32x4ToUint32x4(LFloat32x4ToUint32x4*)
|
||||||
|
{
|
||||||
|
MOZ_CRASH("NYI");
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
CodeGenerator::visitSimdBinaryArithIx16(LSimdBinaryArithIx16*)
|
||||||
|
{
|
||||||
|
MOZ_CRASH("NYI");
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
CodeGenerator::visitSimdExtractElementB(LSimdExtractElementB*)
|
||||||
|
{
|
||||||
|
MOZ_CRASH("NYI");
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
CodeGenerator::visitSimdGeneralShuffleF(LSimdGeneralShuffleF*)
|
||||||
|
{
|
||||||
|
MOZ_CRASH("NYI");
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
CodeGenerator::visitSimdGeneralShuffleI(LSimdGeneralShuffleI*)
|
||||||
|
{
|
||||||
|
MOZ_CRASH("NYI");
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
CodeGenerator::visitSimdReinterpretCast(LSimdReinterpretCast*)
|
||||||
|
{
|
||||||
|
MOZ_CRASH("NYI");
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
CodeGenerator::visitSimdBinarySaturating(LSimdBinarySaturating*)
|
||||||
|
{
|
||||||
|
MOZ_CRASH("NYI");
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
CodeGenerator::visitSimdExtractElementU2D(LSimdExtractElementU2D*)
|
||||||
|
{
|
||||||
|
MOZ_CRASH("NYI");
|
||||||
|
}
|
||||||
|
|
|
@ -1058,3 +1058,63 @@ LIRGenerator::visitSignExtendInt64(MSignExtendInt64* ins)
|
||||||
{
|
{
|
||||||
defineInt64(new(alloc()) LSignExtendInt64(useInt64RegisterAtStart(ins->input())), ins);
|
defineInt64(new(alloc()) LSignExtendInt64(useInt64RegisterAtStart(ins->input())), ins);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
LIRGenerator::visitSimdInsertElement(MSimdInsertElement*)
|
||||||
|
{
|
||||||
|
MOZ_CRASH("NYI");
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
LIRGenerator::visitSimdExtractElement(MSimdExtractElement*)
|
||||||
|
{
|
||||||
|
MOZ_CRASH("NYI");
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
LIRGenerator::visitSimdBinaryArith(MSimdBinaryArith*)
|
||||||
|
{
|
||||||
|
MOZ_CRASH("NYI");
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
LIRGenerator::visitSimdSelect(MSimdSelect*)
|
||||||
|
{
|
||||||
|
MOZ_CRASH("NYI");
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
LIRGenerator::visitSimdSplat(MSimdSplat*)
|
||||||
|
{
|
||||||
|
MOZ_CRASH("NYI");
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
LIRGenerator::visitSimdValueX4(MSimdValueX4*)
|
||||||
|
{
|
||||||
|
MOZ_CRASH("NYI");
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
LIRGenerator::visitSimdBinarySaturating(MSimdBinarySaturating*)
|
||||||
|
{
|
||||||
|
MOZ_CRASH("NYI");
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
LIRGenerator::visitSimdSwizzle(MSimdSwizzle*)
|
||||||
|
{
|
||||||
|
MOZ_CRASH("NYI");
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
LIRGenerator::visitSimdShuffle(MSimdShuffle*)
|
||||||
|
{
|
||||||
|
MOZ_CRASH("NYI");
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
LIRGenerator::visitSimdGeneralShuffle(MSimdGeneralShuffle*)
|
||||||
|
{
|
||||||
|
MOZ_CRASH("NYI");
|
||||||
|
}
|
||||||
|
|
|
@ -63,6 +63,17 @@ class LIRGeneratorARM : public LIRGeneratorShared
|
||||||
void lowerForFPU(LInstructionHelper<1, 2, Temps>* ins, MDefinition* mir,
|
void lowerForFPU(LInstructionHelper<1, 2, Temps>* ins, MDefinition* mir,
|
||||||
MDefinition* lhs, MDefinition* rhs);
|
MDefinition* lhs, MDefinition* rhs);
|
||||||
|
|
||||||
|
void lowerForCompIx4(LSimdBinaryCompIx4* ins, MSimdBinaryComp* mir,
|
||||||
|
MDefinition* lhs, MDefinition* rhs)
|
||||||
|
{
|
||||||
|
return lowerForFPU(ins, mir, lhs, rhs);
|
||||||
|
}
|
||||||
|
void lowerForCompFx4(LSimdBinaryCompFx4* ins, MSimdBinaryComp* mir,
|
||||||
|
MDefinition* lhs, MDefinition* rhs)
|
||||||
|
{
|
||||||
|
return lowerForFPU(ins, mir, lhs, rhs);
|
||||||
|
}
|
||||||
|
|
||||||
void lowerForBitAndAndBranch(LBitAndAndBranch* baab, MInstruction* mir,
|
void lowerForBitAndAndBranch(LBitAndAndBranch* baab, MInstruction* mir,
|
||||||
MDefinition* lhs, MDefinition* rhs);
|
MDefinition* lhs, MDefinition* rhs);
|
||||||
void lowerTruncateDToInt32(MTruncateToInt32* ins);
|
void lowerTruncateDToInt32(MTruncateToInt32* ins);
|
||||||
|
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче