summaryrefslogtreecommitdiffstats
path: root/js/src/builtin/SIMD.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'js/src/builtin/SIMD.cpp')
-rw-r--r--js/src/builtin/SIMD.cpp1552
1 files changed, 1552 insertions, 0 deletions
diff --git a/js/src/builtin/SIMD.cpp b/js/src/builtin/SIMD.cpp
new file mode 100644
index 000000000..2383922db
--- /dev/null
+++ b/js/src/builtin/SIMD.cpp
@@ -0,0 +1,1552 @@
+/* -*- 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/. */
+
+/*
+ * JS SIMD pseudo-module.
+ * Specification matches polyfill:
+ * https://github.com/johnmccutchan/ecmascript_simd/blob/master/src/ecmascript_simd.js
+ * The objects float32x4 and int32x4 are installed on the SIMD pseudo-module.
+ */
+
+#include "builtin/SIMD.h"
+
+#include "mozilla/FloatingPoint.h"
+#include "mozilla/IntegerTypeTraits.h"
+#include "mozilla/Sprintf.h"
+
+#include "jsapi.h"
+#include "jsfriendapi.h"
+#include "jsnum.h"
+#include "jsprf.h"
+
+#include "builtin/TypedObject.h"
+#include "jit/InlinableNatives.h"
+#include "js/GCAPI.h"
+#include "js/Value.h"
+
+#include "jsobjinlines.h"
+
+using namespace js;
+
+using mozilla::ArrayLength;
+using mozilla::IsFinite;
+using mozilla::IsNaN;
+using mozilla::FloorLog2;
+using mozilla::NumberIsInt32;
+
+///////////////////////////////////////////////////////////////////////////
+// SIMD
+
+static_assert(unsigned(SimdType::Count) == 12, "sync with TypedObjectConstants.h");
+
+static bool ArgumentToLaneIndex(JSContext* cx, JS::HandleValue v, unsigned limit, unsigned* lane);
+
+static bool
+CheckVectorObject(HandleValue v, SimdType expectedType)
+{
+ if (!v.isObject())
+ return false;
+
+ JSObject& obj = v.toObject();
+ if (!obj.is<TypedObject>())
+ return false;
+
+ TypeDescr& typeRepr = obj.as<TypedObject>().typeDescr();
+ if (typeRepr.kind() != type::Simd)
+ return false;
+
+ return typeRepr.as<SimdTypeDescr>().type() == expectedType;
+}
+
+template<class V>
+bool
+js::IsVectorObject(HandleValue v)
+{
+ return CheckVectorObject(v, V::type);
+}
+
+#define FOR_EACH_SIMD(macro) \
+ macro(Int8x16) \
+ macro(Int16x8) \
+ macro(Int32x4) \
+ macro(Uint8x16) \
+ macro(Uint16x8) \
+ macro(Uint32x4) \
+ macro(Float32x4) \
+ macro(Float64x2) \
+ macro(Bool8x16) \
+ macro(Bool16x8) \
+ macro(Bool32x4) \
+ macro(Bool64x2)
+
+#define InstantiateIsVectorObject_(T) \
+ template bool js::IsVectorObject<T>(HandleValue v);
+FOR_EACH_SIMD(InstantiateIsVectorObject_)
+#undef InstantiateIsVectorObject_
+
+const char*
+js::SimdTypeToString(SimdType type)
+{
+ switch (type) {
+#define RETSTR_(TYPE) case SimdType::TYPE: return #TYPE;
+ FOR_EACH_SIMD(RETSTR_)
+#undef RETSTR_
+ case SimdType::Count: break;
+ }
+ return "<bad SimdType>";
+}
+
+PropertyName*
+js::SimdTypeToName(const JSAtomState& atoms, SimdType type)
+{
+ switch (type) {
+#define CASE_(TypeName) case SimdType::TypeName: return atoms.TypeName;
+ FOR_EACH_SIMD(CASE_)
+#undef CASE_
+ case SimdType::Count: break;
+ }
+ MOZ_CRASH("bad SIMD type");
+}
+
+bool
+js::IsSimdTypeName(const JSAtomState& atoms, const PropertyName* name, SimdType* type)
+{
+#define CHECK_(TypeName) if (name == atoms.TypeName) { \
+ *type = SimdType::TypeName; \
+ return true; \
+ }
+ FOR_EACH_SIMD(CHECK_)
+#undef CHECK_
+ return false;
+}
+
+static inline bool
+ErrorBadArgs(JSContext* cx)
+{
+ JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_BAD_ARGS);
+ return false;
+}
+
+static inline bool
+ErrorWrongTypeArg(JSContext* cx, unsigned argIndex, Handle<TypeDescr*> typeDescr)
+{
+ MOZ_ASSERT(argIndex < 10);
+ char charArgIndex[2];
+ SprintfLiteral(charArgIndex, "%u", argIndex);
+
+ HeapSlot& typeNameSlot = typeDescr->getReservedSlotRef(JS_DESCR_SLOT_STRING_REPR);
+ char* typeNameStr = JS_EncodeString(cx, typeNameSlot.toString());
+ if (!typeNameStr)
+ return false;
+
+ JS_ReportErrorNumberLatin1(cx, GetErrorMessage, nullptr, JSMSG_SIMD_NOT_A_VECTOR,
+ typeNameStr, charArgIndex);
+ JS_free(cx, typeNameStr);
+ return false;
+}
+
+static inline bool
+ErrorBadIndex(JSContext* cx)
+{
+ JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_BAD_INDEX);
+ return false;
+}
+
+template<typename T>
+static SimdTypeDescr*
+GetTypeDescr(JSContext* cx)
+{
+ RootedGlobalObject global(cx, cx->global());
+ return GlobalObject::getOrCreateSimdTypeDescr(cx, global, T::type);
+}
+
+template<typename V>
+bool
+js::ToSimdConstant(JSContext* cx, HandleValue v, jit::SimdConstant* out)
+{
+ typedef typename V::Elem Elem;
+ Rooted<TypeDescr*> typeDescr(cx, GetTypeDescr<V>(cx));
+ if (!typeDescr)
+ return false;
+ if (!IsVectorObject<V>(v))
+ return ErrorWrongTypeArg(cx, 1, typeDescr);
+
+ JS::AutoCheckCannotGC nogc(cx);
+ Elem* mem = reinterpret_cast<Elem*>(v.toObject().as<TypedObject>().typedMem(nogc));
+ *out = jit::SimdConstant::CreateSimd128(mem);
+ return true;
+}
+
+template bool js::ToSimdConstant<Int8x16>(JSContext* cx, HandleValue v, jit::SimdConstant* out);
+template bool js::ToSimdConstant<Int16x8>(JSContext* cx, HandleValue v, jit::SimdConstant* out);
+template bool js::ToSimdConstant<Int32x4>(JSContext* cx, HandleValue v, jit::SimdConstant* out);
+template bool js::ToSimdConstant<Float32x4>(JSContext* cx, HandleValue v, jit::SimdConstant* out);
+template bool js::ToSimdConstant<Bool8x16>(JSContext* cx, HandleValue v, jit::SimdConstant* out);
+template bool js::ToSimdConstant<Bool16x8>(JSContext* cx, HandleValue v, jit::SimdConstant* out);
+template bool js::ToSimdConstant<Bool32x4>(JSContext* cx, HandleValue v, jit::SimdConstant* out);
+
+template<typename Elem>
+static Elem
+TypedObjectMemory(HandleValue v, const JS::AutoRequireNoGC& nogc)
+{
+ TypedObject& obj = v.toObject().as<TypedObject>();
+ return reinterpret_cast<Elem>(obj.typedMem(nogc));
+}
+
+static const ClassOps SimdTypeDescrClassOps = {
+ nullptr, /* addProperty */
+ nullptr, /* delProperty */
+ nullptr, /* getProperty */
+ nullptr, /* setProperty */
+ nullptr, /* enumerate */
+ nullptr, /* resolve */
+ nullptr, /* mayResolve */
+ TypeDescr::finalize,
+ SimdTypeDescr::call
+};
+
+const Class SimdTypeDescr::class_ = {
+ "SIMD",
+ JSCLASS_HAS_RESERVED_SLOTS(JS_DESCR_SLOTS) | JSCLASS_BACKGROUND_FINALIZE,
+ &SimdTypeDescrClassOps
+};
+
+namespace {
+
+// Define classes (Int8x16Defn, Int16x8Defn, etc.) to group together various
+// properties and so on.
+#define DEFINE_DEFN_(TypeName) \
+class TypeName##Defn { \
+ public: \
+ static const JSFunctionSpec Methods[]; \
+};
+
+FOR_EACH_SIMD(DEFINE_DEFN_)
+#undef DEFINE_DEFN_
+
+} // namespace
+
+// Shared type descriptor methods for all SIMD types.
+static const JSFunctionSpec TypeDescriptorMethods[] = {
+ JS_SELF_HOSTED_FN("toSource", "DescrToSource", 0, 0),
+ JS_SELF_HOSTED_FN("array", "ArrayShorthand", 1, 0),
+ JS_SELF_HOSTED_FN("equivalent", "TypeDescrEquivalent", 1, 0),
+ JS_FS_END
+};
+
+// Shared TypedObject methods for all SIMD types.
+static const JSFunctionSpec SimdTypedObjectMethods[] = {
+ JS_SELF_HOSTED_FN("toString", "SimdToString", 0, 0),
+ JS_SELF_HOSTED_FN("valueOf", "SimdValueOf", 0, 0),
+ JS_SELF_HOSTED_FN("toSource", "SimdToSource", 0, 0),
+ JS_FS_END
+};
+
+// Provide JSJitInfo structs for those types that are supported by Ion.
+// The controlling SIMD type is encoded as the InlinableNative primary opcode.
+// The SimdOperation within the type is encoded in the .depth field.
+//
+// The JS_INLINABLE_FN macro refers to js::JitInfo_##native which we provide as
+// Simd##Type##_##Operation
+//
+// /!\ Don't forget to keep this list in sync with the SIMD instrinics used in
+// SelfHosting.cpp.
+
+namespace js {
+namespace jit {
+
+static_assert(uint64_t(SimdOperation::Last) <= UINT16_MAX, "SimdOperation must fit in uint16_t");
+
+// See also JitInfo_* in MCallOptimize.cpp. We provide a JSJitInfo for all the
+// named functions here. The default JitInfo_SimdInt32x4 etc structs represent the
+// SimdOperation::Constructor.
+#define DEFN(TYPE, OP) const JSJitInfo JitInfo_Simd##TYPE##_##OP = { \
+ /* .getter, unused for inlinable natives. */ \
+ { nullptr }, \
+ /* .inlinableNative, but we have to init first union member: .protoID. */ \
+ { uint16_t(InlinableNative::Simd##TYPE) }, \
+ /* .nativeOp. Actually initializing first union member .depth. */ \
+ { uint16_t(SimdOperation::Fn_##OP) }, \
+ /* .type_ bitfield says this in an inlinable native function. */ \
+ JSJitInfo::InlinableNative \
+ /* Remaining fields are not used for inlinable natives. They are zero-initialized. */ \
+};
+
+// This list of inlinable types should match the one in jit/InlinableNatives.h.
+#define TDEFN(Name, Func, Operands) DEFN(Float32x4, Name)
+FLOAT32X4_FUNCTION_LIST(TDEFN)
+#undef TDEFN
+
+#define TDEFN(Name, Func, Operands) DEFN(Int8x16, Name)
+INT8X16_FUNCTION_LIST(TDEFN)
+#undef TDEFN
+
+#define TDEFN(Name, Func, Operands) DEFN(Uint8x16, Name)
+UINT8X16_FUNCTION_LIST(TDEFN)
+#undef TDEFN
+
+#define TDEFN(Name, Func, Operands) DEFN(Int16x8, Name)
+INT16X8_FUNCTION_LIST(TDEFN)
+#undef TDEFN
+
+#define TDEFN(Name, Func, Operands) DEFN(Uint16x8, Name)
+UINT16X8_FUNCTION_LIST(TDEFN)
+#undef TDEFN
+
+#define TDEFN(Name, Func, Operands) DEFN(Int32x4, Name)
+INT32X4_FUNCTION_LIST(TDEFN)
+#undef TDEFN
+
+#define TDEFN(Name, Func, Operands) DEFN(Uint32x4, Name)
+UINT32X4_FUNCTION_LIST(TDEFN)
+#undef TDEFN
+
+#define TDEFN(Name, Func, Operands) DEFN(Bool8x16, Name)
+BOOL8X16_FUNCTION_LIST(TDEFN)
+#undef TDEFN
+
+#define TDEFN(Name, Func, Operands) DEFN(Bool16x8, Name)
+BOOL16X8_FUNCTION_LIST(TDEFN)
+#undef TDEFN
+
+#define TDEFN(Name, Func, Operands) DEFN(Bool32x4, Name)
+BOOL32X4_FUNCTION_LIST(TDEFN)
+#undef TDEFN
+
+} // namespace jit
+} // namespace js
+
+const JSFunctionSpec Float32x4Defn::Methods[] = {
+#define SIMD_FLOAT32X4_FUNCTION_ITEM(Name, Func, Operands) \
+ JS_INLINABLE_FN(#Name, js::simd_float32x4_##Name, Operands, 0, SimdFloat32x4_##Name),
+ FLOAT32X4_FUNCTION_LIST(SIMD_FLOAT32X4_FUNCTION_ITEM)
+#undef SIMD_FLOAT32x4_FUNCTION_ITEM
+ JS_FS_END
+};
+
+const JSFunctionSpec Float64x2Defn::Methods[] = {
+#define SIMD_FLOAT64X2_FUNCTION_ITEM(Name, Func, Operands) \
+ JS_FN(#Name, js::simd_float64x2_##Name, Operands, 0),
+ FLOAT64X2_FUNCTION_LIST(SIMD_FLOAT64X2_FUNCTION_ITEM)
+#undef SIMD_FLOAT64X2_FUNCTION_ITEM
+ JS_FS_END
+};
+
+const JSFunctionSpec Int8x16Defn::Methods[] = {
+#define SIMD_INT8X16_FUNCTION_ITEM(Name, Func, Operands) \
+ JS_INLINABLE_FN(#Name, js::simd_int8x16_##Name, Operands, 0, SimdInt8x16_##Name),
+ INT8X16_FUNCTION_LIST(SIMD_INT8X16_FUNCTION_ITEM)
+#undef SIMD_INT8X16_FUNCTION_ITEM
+ JS_FS_END
+};
+
+const JSFunctionSpec Int16x8Defn::Methods[] = {
+#define SIMD_INT16X8_FUNCTION_ITEM(Name, Func, Operands) \
+ JS_INLINABLE_FN(#Name, js::simd_int16x8_##Name, Operands, 0, SimdInt16x8_##Name),
+ INT16X8_FUNCTION_LIST(SIMD_INT16X8_FUNCTION_ITEM)
+#undef SIMD_INT16X8_FUNCTION_ITEM
+ JS_FS_END
+};
+
+const JSFunctionSpec Int32x4Defn::Methods[] = {
+#define SIMD_INT32X4_FUNCTION_ITEM(Name, Func, Operands) \
+ JS_INLINABLE_FN(#Name, js::simd_int32x4_##Name, Operands, 0, SimdInt32x4_##Name),
+ INT32X4_FUNCTION_LIST(SIMD_INT32X4_FUNCTION_ITEM)
+#undef SIMD_INT32X4_FUNCTION_ITEM
+ JS_FS_END
+};
+
+const JSFunctionSpec Uint8x16Defn::Methods[] = {
+#define SIMD_UINT8X16_FUNCTION_ITEM(Name, Func, Operands) \
+ JS_INLINABLE_FN(#Name, js::simd_uint8x16_##Name, Operands, 0, SimdUint8x16_##Name),
+ UINT8X16_FUNCTION_LIST(SIMD_UINT8X16_FUNCTION_ITEM)
+#undef SIMD_UINT8X16_FUNCTION_ITEM
+ JS_FS_END
+};
+
+const JSFunctionSpec Uint16x8Defn::Methods[] = {
+#define SIMD_UINT16X8_FUNCTION_ITEM(Name, Func, Operands) \
+ JS_INLINABLE_FN(#Name, js::simd_uint16x8_##Name, Operands, 0, SimdUint16x8_##Name),
+ UINT16X8_FUNCTION_LIST(SIMD_UINT16X8_FUNCTION_ITEM)
+#undef SIMD_UINT16X8_FUNCTION_ITEM
+ JS_FS_END
+};
+
+const JSFunctionSpec Uint32x4Defn::Methods[] = {
+#define SIMD_UINT32X4_FUNCTION_ITEM(Name, Func, Operands) \
+ JS_INLINABLE_FN(#Name, js::simd_uint32x4_##Name, Operands, 0, SimdUint32x4_##Name),
+ UINT32X4_FUNCTION_LIST(SIMD_UINT32X4_FUNCTION_ITEM)
+#undef SIMD_UINT32X4_FUNCTION_ITEM
+ JS_FS_END
+};
+
+const JSFunctionSpec Bool8x16Defn::Methods[] = {
+#define SIMD_BOOL8X16_FUNCTION_ITEM(Name, Func, Operands) \
+ JS_INLINABLE_FN(#Name, js::simd_bool8x16_##Name, Operands, 0, SimdBool8x16_##Name),
+ BOOL8X16_FUNCTION_LIST(SIMD_BOOL8X16_FUNCTION_ITEM)
+#undef SIMD_BOOL8X16_FUNCTION_ITEM
+ JS_FS_END
+};
+
+const JSFunctionSpec Bool16x8Defn::Methods[] = {
+#define SIMD_BOOL16X8_FUNCTION_ITEM(Name, Func, Operands) \
+ JS_INLINABLE_FN(#Name, js::simd_bool16x8_##Name, Operands, 0, SimdBool16x8_##Name),
+ BOOL16X8_FUNCTION_LIST(SIMD_BOOL16X8_FUNCTION_ITEM)
+#undef SIMD_BOOL16X8_FUNCTION_ITEM
+ JS_FS_END
+};
+
+const JSFunctionSpec Bool32x4Defn::Methods[] = {
+#define SIMD_BOOL32X4_FUNCTION_ITEM(Name, Func, Operands) \
+ JS_INLINABLE_FN(#Name, js::simd_bool32x4_##Name, Operands, 0, SimdBool32x4_##Name),
+ BOOL32X4_FUNCTION_LIST(SIMD_BOOL32X4_FUNCTION_ITEM)
+#undef SIMD_BOOL32X4_FUNCTION_ITEM
+ JS_FS_END
+};
+
+const JSFunctionSpec Bool64x2Defn::Methods[] = {
+#define SIMD_BOOL64X2_FUNCTION_ITEM(Name, Func, Operands) \
+ JS_FN(#Name, js::simd_bool64x2_##Name, Operands, 0),
+ BOOL64X2_FUNCTION_LIST(SIMD_BOOL64X2_FUNCTION_ITEM)
+#undef SIMD_BOOL64x2_FUNCTION_ITEM
+ JS_FS_END
+};
+
+template <typename T>
+static bool
+FillLanes(JSContext* cx, Handle<TypedObject*> result, const CallArgs& args)
+{
+ typedef typename T::Elem Elem;
+ Elem tmp;
+ for (unsigned i = 0; i < T::lanes; i++) {
+ if (!T::Cast(cx, args.get(i), &tmp))
+ return false;
+ // Reassure typedMem() that we won't GC while holding onto the returned
+ // pointer, even though we could GC on every iteration of this loop
+ // (but it is safe because we re-fetch each time.)
+ JS::AutoCheckCannotGC nogc(cx);
+ reinterpret_cast<Elem*>(result->typedMem(nogc))[i] = tmp;
+ }
+ args.rval().setObject(*result);
+ return true;
+}
+
+bool
+SimdTypeDescr::call(JSContext* cx, unsigned argc, Value* vp)
+{
+ CallArgs args = CallArgsFromVp(argc, vp);
+
+ Rooted<SimdTypeDescr*> descr(cx, &args.callee().as<SimdTypeDescr>());
+ Rooted<TypedObject*> result(cx, TypedObject::createZeroed(cx, descr, 0));
+ if (!result)
+ return false;
+
+#define CASE_CALL_(Type) \
+ case SimdType::Type: return FillLanes< ::Type>(cx, result, args);
+
+ switch (descr->type()) {
+ FOR_EACH_SIMD(CASE_CALL_)
+ case SimdType::Count: break;
+ }
+
+#undef CASE_CALL_
+ MOZ_CRASH("unexpected SIMD descriptor");
+ return false;
+}
+
+///////////////////////////////////////////////////////////////////////////
+// SIMD class
+
+static const ClassOps SimdObjectClassOps = {
+ nullptr, /* addProperty */
+ nullptr, /* delProperty */
+ nullptr, /* getProperty */
+ nullptr, /* setProperty */
+ nullptr, /* enumerate */
+ SimdObject::resolve
+};
+
+const Class SimdObject::class_ = {
+ "SIMD",
+ JSCLASS_HAS_RESERVED_SLOTS(uint32_t(SimdType::Count)),
+ &SimdObjectClassOps
+};
+
+bool
+GlobalObject::initSimdObject(JSContext* cx, Handle<GlobalObject*> global)
+{
+ // SIMD relies on the TypedObject module being initialized.
+ // In particular, the self-hosted code for array() wants
+ // to be able to call GetTypedObjectModule(). It is NOT necessary
+ // to install the TypedObjectModule global, but at the moment
+ // those two things are not separable.
+ if (!global->getOrCreateTypedObjectModule(cx))
+ return false;
+
+ RootedObject globalSimdObject(cx);
+ RootedObject objProto(cx, global->getOrCreateObjectPrototype(cx));
+ if (!objProto)
+ return false;
+
+ globalSimdObject = NewObjectWithGivenProto(cx, &SimdObject::class_, objProto, SingletonObject);
+ if (!globalSimdObject)
+ return false;
+
+ RootedValue globalSimdValue(cx, ObjectValue(*globalSimdObject));
+ if (!DefineProperty(cx, global, cx->names().SIMD, globalSimdValue, nullptr, nullptr,
+ JSPROP_RESOLVING))
+ {
+ return false;
+ }
+
+ global->setConstructor(JSProto_SIMD, globalSimdValue);
+ return true;
+}
+
+static bool
+CreateSimdType(JSContext* cx, Handle<GlobalObject*> global, HandlePropertyName stringRepr,
+ SimdType simdType, const JSFunctionSpec* methods)
+{
+ RootedObject funcProto(cx, global->getOrCreateFunctionPrototype(cx));
+ if (!funcProto)
+ return false;
+
+ // Create type constructor itself and initialize its reserved slots.
+ Rooted<SimdTypeDescr*> typeDescr(cx);
+ typeDescr = NewObjectWithGivenProto<SimdTypeDescr>(cx, funcProto, SingletonObject);
+ if (!typeDescr)
+ return false;
+
+ typeDescr->initReservedSlot(JS_DESCR_SLOT_KIND, Int32Value(type::Simd));
+ typeDescr->initReservedSlot(JS_DESCR_SLOT_STRING_REPR, StringValue(stringRepr));
+ typeDescr->initReservedSlot(JS_DESCR_SLOT_ALIGNMENT, Int32Value(SimdTypeDescr::alignment(simdType)));
+ typeDescr->initReservedSlot(JS_DESCR_SLOT_SIZE, Int32Value(SimdTypeDescr::size(simdType)));
+ typeDescr->initReservedSlot(JS_DESCR_SLOT_OPAQUE, BooleanValue(false));
+ typeDescr->initReservedSlot(JS_DESCR_SLOT_TYPE, Int32Value(uint8_t(simdType)));
+
+ if (!CreateUserSizeAndAlignmentProperties(cx, typeDescr))
+ return false;
+
+ // Create prototype property, which inherits from Object.prototype.
+ RootedObject objProto(cx, global->getOrCreateObjectPrototype(cx));
+ if (!objProto)
+ return false;
+ Rooted<TypedProto*> proto(cx);
+ proto = NewObjectWithGivenProto<TypedProto>(cx, objProto, SingletonObject);
+ if (!proto)
+ return false;
+ typeDescr->initReservedSlot(JS_DESCR_SLOT_TYPROTO, ObjectValue(*proto));
+
+ // Link constructor to prototype and install properties.
+ if (!JS_DefineFunctions(cx, typeDescr, TypeDescriptorMethods))
+ return false;
+
+ if (!LinkConstructorAndPrototype(cx, typeDescr, proto) ||
+ !JS_DefineFunctions(cx, proto, SimdTypedObjectMethods))
+ {
+ return false;
+ }
+
+ // Bind type descriptor to the global SIMD object
+ RootedObject globalSimdObject(cx, global->getOrCreateSimdGlobalObject(cx));
+ MOZ_ASSERT(globalSimdObject);
+
+ RootedValue typeValue(cx, ObjectValue(*typeDescr));
+ if (!JS_DefineFunctions(cx, typeDescr, methods) ||
+ !DefineProperty(cx, globalSimdObject, stringRepr, typeValue, nullptr, nullptr,
+ JSPROP_READONLY | JSPROP_PERMANENT | JSPROP_RESOLVING))
+ {
+ return false;
+ }
+
+ uint32_t slot = uint32_t(typeDescr->type());
+ MOZ_ASSERT(globalSimdObject->as<NativeObject>().getReservedSlot(slot).isUndefined());
+ globalSimdObject->as<NativeObject>().setReservedSlot(slot, ObjectValue(*typeDescr));
+ return !!typeDescr;
+}
+
+bool
+GlobalObject::initSimdType(JSContext* cx, Handle<GlobalObject*> global, SimdType simdType)
+{
+#define CREATE_(Type) \
+ case SimdType::Type: \
+ return CreateSimdType(cx, global, cx->names().Type, simdType, Type##Defn::Methods);
+
+ switch (simdType) {
+ FOR_EACH_SIMD(CREATE_)
+ case SimdType::Count: break;
+ }
+ MOZ_CRASH("unexpected simd type");
+
+#undef CREATE_
+}
+
+SimdTypeDescr*
+GlobalObject::getOrCreateSimdTypeDescr(JSContext* cx, Handle<GlobalObject*> global,
+ SimdType simdType)
+{
+ MOZ_ASSERT(unsigned(simdType) < unsigned(SimdType::Count), "Invalid SIMD type");
+
+ RootedObject globalSimdObject(cx, global->getOrCreateSimdGlobalObject(cx));
+ if (!globalSimdObject)
+ return nullptr;
+
+ uint32_t typeSlotIndex = uint32_t(simdType);
+ if (globalSimdObject->as<NativeObject>().getReservedSlot(typeSlotIndex).isUndefined() &&
+ !GlobalObject::initSimdType(cx, global, simdType))
+ {
+ return nullptr;
+ }
+
+ const Value& slot = globalSimdObject->as<NativeObject>().getReservedSlot(typeSlotIndex);
+ MOZ_ASSERT(slot.isObject());
+ return &slot.toObject().as<SimdTypeDescr>();
+}
+
+bool
+SimdObject::resolve(JSContext* cx, JS::HandleObject obj, JS::HandleId id, bool* resolved)
+{
+ *resolved = false;
+ if (!JSID_IS_ATOM(id))
+ return true;
+ JSAtom* str = JSID_TO_ATOM(id);
+ Rooted<GlobalObject*> global(cx, cx->global());
+#define TRY_RESOLVE_(Type) \
+ if (str == cx->names().Type) { \
+ *resolved = CreateSimdType(cx, global, cx->names().Type, \
+ SimdType::Type, Type##Defn::Methods); \
+ return *resolved; \
+ }
+ FOR_EACH_SIMD(TRY_RESOLVE_)
+#undef TRY_RESOLVE_
+ return true;
+}
+
+JSObject*
+js::InitSimdClass(JSContext* cx, HandleObject obj)
+{
+ Rooted<GlobalObject*> global(cx, &obj->as<GlobalObject>());
+ return global->getOrCreateSimdGlobalObject(cx);
+}
+
+template<typename V>
+JSObject*
+js::CreateSimd(JSContext* cx, const typename V::Elem* data)
+{
+ typedef typename V::Elem Elem;
+ Rooted<TypeDescr*> typeDescr(cx, GetTypeDescr<V>(cx));
+ if (!typeDescr)
+ return nullptr;
+
+ Rooted<TypedObject*> result(cx, TypedObject::createZeroed(cx, typeDescr, 0));
+ if (!result)
+ return nullptr;
+
+ JS::AutoCheckCannotGC nogc(cx);
+ Elem* resultMem = reinterpret_cast<Elem*>(result->typedMem(nogc));
+ memcpy(resultMem, data, sizeof(Elem) * V::lanes);
+ return result;
+}
+
+#define InstantiateCreateSimd_(Type) \
+ template JSObject* js::CreateSimd<Type>(JSContext* cx, const Type::Elem* data);
+
+FOR_EACH_SIMD(InstantiateCreateSimd_)
+
+#undef InstantiateCreateSimd_
+
+#undef FOR_EACH_SIMD
+
+namespace js {
+// Unary SIMD operators
+template<typename T>
+struct Identity {
+ static T apply(T x) { return x; }
+};
+template<typename T>
+struct Abs {
+ static T apply(T x) { return mozilla::Abs(x); }
+};
+template<typename T>
+struct Neg {
+ static T apply(T x) { return -1 * x; }
+};
+template<typename T>
+struct Not {
+ static T apply(T x) { return ~x; }
+};
+template<typename T>
+struct LogicalNot {
+ static T apply(T x) { return !x; }
+};
+template<typename T>
+struct RecApprox {
+ static T apply(T x) { return 1 / x; }
+};
+template<typename T>
+struct RecSqrtApprox {
+ static T apply(T x) { return 1 / sqrt(x); }
+};
+template<typename T>
+struct Sqrt {
+ static T apply(T x) { return sqrt(x); }
+};
+
+// Binary SIMD operators
+template<typename T>
+struct Add {
+ static T apply(T l, T r) { return l + r; }
+};
+template<typename T>
+struct Sub {
+ static T apply(T l, T r) { return l - r; }
+};
+template<typename T>
+struct Div {
+ static T apply(T l, T r) { return l / r; }
+};
+template<typename T>
+struct Mul {
+ static T apply(T l, T r) { return l * r; }
+};
+template<typename T>
+struct Minimum {
+ static T apply(T l, T r) { return math_min_impl(l, r); }
+};
+template<typename T>
+struct MinNum {
+ static T apply(T l, T r) { return IsNaN(l) ? r : (IsNaN(r) ? l : math_min_impl(l, r)); }
+};
+template<typename T>
+struct Maximum {
+ static T apply(T l, T r) { return math_max_impl(l, r); }
+};
+template<typename T>
+struct MaxNum {
+ static T apply(T l, T r) { return IsNaN(l) ? r : (IsNaN(r) ? l : math_max_impl(l, r)); }
+};
+template<typename T>
+struct LessThan {
+ static bool apply(T l, T r) { return l < r; }
+};
+template<typename T>
+struct LessThanOrEqual {
+ static bool apply(T l, T r) { return l <= r; }
+};
+template<typename T>
+struct GreaterThan {
+ static bool apply(T l, T r) { return l > r; }
+};
+template<typename T>
+struct GreaterThanOrEqual {
+ static bool apply(T l, T r) { return l >= r; }
+};
+template<typename T>
+struct Equal {
+ static bool apply(T l, T r) { return l == r; }
+};
+template<typename T>
+struct NotEqual {
+ static bool apply(T l, T r) { return l != r; }
+};
+template<typename T>
+struct Xor {
+ static T apply(T l, T r) { return l ^ r; }
+};
+template<typename T>
+struct And {
+ static T apply(T l, T r) { return l & r; }
+};
+template<typename T>
+struct Or {
+ static T apply(T l, T r) { return l | r; }
+};
+
+// For the following three operators, if the value v we're trying to shift is
+// such that v << bits can't fit in the int32 range, then we have undefined
+// behavior, according to C++11 [expr.shift]p2. However, left-shifting an
+// unsigned type is well-defined.
+//
+// In C++, shifting by an amount outside the range [0;N-1] is undefined
+// behavior. SIMD.js reduces the shift amount modulo the number of bits in a
+// lane and has defined behavior for all shift amounts.
+template<typename T>
+struct ShiftLeft {
+ static T apply(T v, int32_t bits) {
+ typedef typename mozilla::MakeUnsigned<T>::Type UnsignedT;
+ uint32_t maskedBits = uint32_t(bits) % (sizeof(T) * 8);
+ return UnsignedT(v) << maskedBits;
+ }
+};
+template<typename T>
+struct ShiftRightArithmetic {
+ static T apply(T v, int32_t bits) {
+ typedef typename mozilla::MakeSigned<T>::Type SignedT;
+ uint32_t maskedBits = uint32_t(bits) % (sizeof(T) * 8);
+ return SignedT(v) >> maskedBits;
+ }
+};
+template<typename T>
+struct ShiftRightLogical {
+ static T apply(T v, int32_t bits) {
+ typedef typename mozilla::MakeUnsigned<T>::Type UnsignedT;
+ uint32_t maskedBits = uint32_t(bits) % (sizeof(T) * 8);
+ return UnsignedT(v) >> maskedBits;
+ }
+};
+
+// Saturating arithmetic is only defined on types smaller than int.
+// Clamp `x` into the range supported by the integral type T.
+template<typename T>
+static T
+Saturate(int x)
+{
+ static_assert(mozilla::IsIntegral<T>::value, "Only integer saturation supported");
+ static_assert(sizeof(T) < sizeof(int), "Saturating int-sized arithmetic is not safe");
+ const T lower = mozilla::MinValue<T>::value;
+ const T upper = mozilla::MaxValue<T>::value;
+ if (x > int(upper))
+ return upper;
+ if (x < int(lower))
+ return lower;
+ return T(x);
+}
+
+// Since signed integer overflow is undefined behavior in C++, it would be
+// wildly irresponsible to attempt something as dangerous as adding two numbers
+// coming from user code. However, in this case we know that T is smaller than
+// int, so there is no way these operations can cause overflow. The
+// static_assert in Saturate() enforces this for us.
+template<typename T>
+struct AddSaturate {
+ static T apply(T l, T r) { return Saturate<T>(l + r); }
+};
+template<typename T>
+struct SubSaturate {
+ static T apply(T l, T r) { return Saturate<T>(l - r); }
+};
+
+} // namespace js
+
+template<typename Out>
+static bool
+StoreResult(JSContext* cx, CallArgs& args, typename Out::Elem* result)
+{
+ RootedObject obj(cx, CreateSimd<Out>(cx, result));
+ if (!obj)
+ return false;
+ args.rval().setObject(*obj);
+ return true;
+}
+
+// StoreResult can GC, and it is commonly used after pulling something out of a
+// TypedObject:
+//
+// Elem result = op(TypedObjectMemory<Elem>(args[0]));
+// StoreResult<Out>(..., result);
+//
+// The pointer extracted from the typed object in args[0] in the above example
+// could be an interior pointer, and therefore be invalidated by GC.
+// TypedObjectMemory() requires an assertion token to be passed in to prove
+// that we won't GC, but the scope of eg an AutoCheckCannotGC RAII object
+// extends to the end of its containing scope -- which would include the call
+// to StoreResult, resulting in a rooting hazard.
+//
+// TypedObjectElemArray fixes this by wrapping the problematic pointer in a
+// type, and the analysis is able to see that it is dead before calling
+// StoreResult. (But if another GC called is made before the pointer is dead,
+// it will correctly report a hazard.)
+//
+template <typename Elem>
+class TypedObjectElemArray {
+ Elem* elements;
+ public:
+ explicit TypedObjectElemArray(HandleValue objVal) {
+ JS::AutoCheckCannotGC nogc;
+ elements = TypedObjectMemory<Elem*>(objVal, nogc);
+ }
+ Elem& operator[](int i) { return elements[i]; }
+} JS_HAZ_GC_POINTER;
+
+// Coerces the inputs of type In to the type Coercion, apply the operator Op
+// and converts the result to the type Out.
+template<typename In, typename Coercion, template<typename C> class Op, typename Out>
+static bool
+CoercedUnaryFunc(JSContext* cx, unsigned argc, Value* vp)
+{
+ typedef typename Coercion::Elem CoercionElem;
+ typedef typename Out::Elem RetElem;
+
+ CallArgs args = CallArgsFromVp(argc, vp);
+ if (args.length() != 1 || !IsVectorObject<In>(args[0]))
+ return ErrorBadArgs(cx);
+
+ CoercionElem result[Coercion::lanes];
+ TypedObjectElemArray<CoercionElem> val(args[0]);
+ for (unsigned i = 0; i < Coercion::lanes; i++)
+ result[i] = Op<CoercionElem>::apply(val[i]);
+ return StoreResult<Out>(cx, args, (RetElem*) result);
+}
+
+// Coerces the inputs of type In to the type Coercion, apply the operator Op
+// and converts the result to the type Out.
+template<typename In, typename Coercion, template<typename C> class Op, typename Out>
+static bool
+CoercedBinaryFunc(JSContext* cx, unsigned argc, Value* vp)
+{
+ typedef typename Coercion::Elem CoercionElem;
+ typedef typename Out::Elem RetElem;
+
+ CallArgs args = CallArgsFromVp(argc, vp);
+ if (args.length() != 2 || !IsVectorObject<In>(args[0]) || !IsVectorObject<In>(args[1]))
+ return ErrorBadArgs(cx);
+
+ CoercionElem result[Coercion::lanes];
+ TypedObjectElemArray<CoercionElem> left(args[0]);
+ TypedObjectElemArray<CoercionElem> right(args[1]);
+ for (unsigned i = 0; i < Coercion::lanes; i++)
+ result[i] = Op<CoercionElem>::apply(left[i], right[i]);
+ return StoreResult<Out>(cx, args, (RetElem*) result);
+}
+
+// Same as above, with no coercion, i.e. Coercion == In.
+template<typename In, template<typename C> class Op, typename Out>
+static bool
+UnaryFunc(JSContext* cx, unsigned argc, Value* vp)
+{
+ return CoercedUnaryFunc<In, Out, Op, Out>(cx, argc, vp);
+}
+
+template<typename In, template<typename C> class Op, typename Out>
+static bool
+BinaryFunc(JSContext* cx, unsigned argc, Value* vp)
+{
+ return CoercedBinaryFunc<In, Out, Op, Out>(cx, argc, vp);
+}
+
+template<typename V>
+static bool
+ExtractLane(JSContext* cx, unsigned argc, Value* vp)
+{
+ typedef typename V::Elem Elem;
+
+ CallArgs args = CallArgsFromVp(argc, vp);
+ if (args.length() < 2 || !IsVectorObject<V>(args[0]))
+ return ErrorBadArgs(cx);
+
+ unsigned lane;
+ if (!ArgumentToLaneIndex(cx, args[1], V::lanes, &lane))
+ return false;
+
+ JS::AutoCheckCannotGC nogc(cx);
+ Elem* vec = TypedObjectMemory<Elem*>(args[0], nogc);
+ Elem val = vec[lane];
+ args.rval().set(V::ToValue(val));
+ return true;
+}
+
+template<typename V>
+static bool
+AllTrue(JSContext* cx, unsigned argc, Value* vp)
+{
+ typedef typename V::Elem Elem;
+
+ CallArgs args = CallArgsFromVp(argc, vp);
+ if (args.length() < 1 || !IsVectorObject<V>(args[0]))
+ return ErrorBadArgs(cx);
+
+ JS::AutoCheckCannotGC nogc(cx);
+ Elem* vec = TypedObjectMemory<Elem*>(args[0], nogc);
+ bool allTrue = true;
+ for (unsigned i = 0; allTrue && i < V::lanes; i++)
+ allTrue = vec[i];
+
+ args.rval().setBoolean(allTrue);
+ return true;
+}
+
+template<typename V>
+static bool
+AnyTrue(JSContext* cx, unsigned argc, Value* vp)
+{
+ typedef typename V::Elem Elem;
+
+ CallArgs args = CallArgsFromVp(argc, vp);
+ if (args.length() < 1 || !IsVectorObject<V>(args[0]))
+ return ErrorBadArgs(cx);
+
+ JS::AutoCheckCannotGC nogc(cx);
+ Elem* vec = TypedObjectMemory<Elem*>(args[0], nogc);
+ bool anyTrue = false;
+ for (unsigned i = 0; !anyTrue && i < V::lanes; i++)
+ anyTrue = vec[i];
+
+ args.rval().setBoolean(anyTrue);
+ return true;
+}
+
+template<typename V>
+static bool
+ReplaceLane(JSContext* cx, unsigned argc, Value* vp)
+{
+ typedef typename V::Elem Elem;
+
+ CallArgs args = CallArgsFromVp(argc, vp);
+ // Only the first and second arguments are mandatory
+ if (args.length() < 2 || !IsVectorObject<V>(args[0]))
+ return ErrorBadArgs(cx);
+
+ unsigned lane;
+ if (!ArgumentToLaneIndex(cx, args[1], V::lanes, &lane))
+ return false;
+
+ Elem value;
+ if (!V::Cast(cx, args.get(2), &value))
+ return false;
+
+ TypedObjectElemArray<Elem> vec(args[0]);
+ Elem result[V::lanes];
+ for (unsigned i = 0; i < V::lanes; i++)
+ result[i] = i == lane ? value : vec[i];
+
+ return StoreResult<V>(cx, args, result);
+}
+
+template<typename V>
+static bool
+Swizzle(JSContext* cx, unsigned argc, Value* vp)
+{
+ typedef typename V::Elem Elem;
+
+ CallArgs args = CallArgsFromVp(argc, vp);
+ if (args.length() != (V::lanes + 1) || !IsVectorObject<V>(args[0]))
+ return ErrorBadArgs(cx);
+
+ unsigned lanes[V::lanes];
+ for (unsigned i = 0; i < V::lanes; i++) {
+ if (!ArgumentToLaneIndex(cx, args[i + 1], V::lanes, &lanes[i]))
+ return false;
+ }
+
+ TypedObjectElemArray<Elem> val(args[0]);
+ Elem result[V::lanes];
+ for (unsigned i = 0; i < V::lanes; i++)
+ result[i] = val[lanes[i]];
+
+ return StoreResult<V>(cx, args, result);
+}
+
+template<typename V>
+static bool
+Shuffle(JSContext* cx, unsigned argc, Value* vp)
+{
+ typedef typename V::Elem Elem;
+
+ CallArgs args = CallArgsFromVp(argc, vp);
+ if (args.length() != (V::lanes + 2) || !IsVectorObject<V>(args[0]) || !IsVectorObject<V>(args[1]))
+ return ErrorBadArgs(cx);
+
+ unsigned lanes[V::lanes];
+ for (unsigned i = 0; i < V::lanes; i++) {
+ if (!ArgumentToLaneIndex(cx, args[i + 2], 2 * V::lanes, &lanes[i]))
+ return false;
+ }
+
+ Elem result[V::lanes];
+ {
+ JS::AutoCheckCannotGC nogc(cx);
+ Elem* lhs = TypedObjectMemory<Elem*>(args[0], nogc);
+ Elem* rhs = TypedObjectMemory<Elem*>(args[1], nogc);
+
+ for (unsigned i = 0; i < V::lanes; i++) {
+ Elem* selectedInput = lanes[i] < V::lanes ? lhs : rhs;
+ result[i] = selectedInput[lanes[i] % V::lanes];
+ }
+ }
+
+ return StoreResult<V>(cx, args, result);
+}
+
+template<typename V, template<typename T> class Op>
+static bool
+BinaryScalar(JSContext* cx, unsigned argc, Value* vp)
+{
+ typedef typename V::Elem Elem;
+
+ CallArgs args = CallArgsFromVp(argc, vp);
+ if (args.length() != 2)
+ return ErrorBadArgs(cx);
+
+ if (!IsVectorObject<V>(args[0]))
+ return ErrorBadArgs(cx);
+
+ int32_t bits;
+ if (!ToInt32(cx, args[1], &bits))
+ return false;
+
+ TypedObjectElemArray<Elem> val(args[0]);
+ Elem result[V::lanes];
+ for (unsigned i = 0; i < V::lanes; i++)
+ result[i] = Op<Elem>::apply(val[i], bits);
+
+ return StoreResult<V>(cx, args, result);
+}
+
+template<typename In, template<typename C> class Op, typename Out>
+static bool
+CompareFunc(JSContext* cx, unsigned argc, Value* vp)
+{
+ typedef typename In::Elem InElem;
+ typedef typename Out::Elem OutElem;
+
+ CallArgs args = CallArgsFromVp(argc, vp);
+ if (args.length() != 2 || !IsVectorObject<In>(args[0]) || !IsVectorObject<In>(args[1]))
+ return ErrorBadArgs(cx);
+
+ OutElem result[Out::lanes];
+ TypedObjectElemArray<InElem> left(args[0]);
+ TypedObjectElemArray<InElem> right(args[1]);
+ for (unsigned i = 0; i < Out::lanes; i++) {
+ unsigned j = (i * In::lanes) / Out::lanes;
+ result[i] = Op<InElem>::apply(left[j], right[j]) ? -1 : 0;
+ }
+
+ return StoreResult<Out>(cx, args, result);
+}
+
+// This struct defines whether we should throw during a conversion attempt,
+// when trying to convert a value of type from From to the type To. This
+// happens whenever a C++ conversion would have undefined behavior (and perhaps
+// be platform-dependent).
+template<typename From, typename To>
+struct ThrowOnConvert;
+
+struct NeverThrow
+{
+ static bool value(int32_t v) {
+ return false;
+ }
+};
+
+// While int32 to float conversions can be lossy, these conversions have
+// defined behavior in C++, so we don't need to care about them here. In practice,
+// this means round to nearest, tie with even (zero bit in significand).
+template<>
+struct ThrowOnConvert<int32_t, float> : public NeverThrow {};
+
+template<>
+struct ThrowOnConvert<uint32_t, float> : public NeverThrow {};
+
+// All int32 can be safely converted to doubles.
+template<>
+struct ThrowOnConvert<int32_t, double> : public NeverThrow {};
+
+template<>
+struct ThrowOnConvert<uint32_t, double> : public NeverThrow {};
+
+// All floats can be safely converted to doubles.
+template<>
+struct ThrowOnConvert<float, double> : public NeverThrow {};
+
+// Double to float conversion for inputs which aren't in the float range are
+// undefined behavior in C++, but they're defined in IEEE754.
+template<>
+struct ThrowOnConvert<double, float> : public NeverThrow {};
+
+// Float to integer conversions have undefined behavior if the float value
+// is out of the representable integer range (on x86, will yield the undefined
+// value pattern, namely 0x80000000; on arm, will clamp the input value), so
+// check this here.
+template<typename From, typename IntegerType>
+struct ThrowIfNotInRange
+{
+ static_assert(mozilla::IsIntegral<IntegerType>::value, "bad destination type");
+
+ static bool value(From v) {
+ // Truncate to integer value before the range check.
+ double d = trunc(double(v));
+ // Arrange relations so NaN returns true (i.e., it throws a RangeError).
+ return !(d >= double(mozilla::MinValue<IntegerType>::value) &&
+ d <= double(mozilla::MaxValue<IntegerType>::value));
+ }
+};
+
+template<>
+struct ThrowOnConvert<double, int32_t> : public ThrowIfNotInRange<double, int32_t> {};
+
+template<>
+struct ThrowOnConvert<double, uint32_t> : public ThrowIfNotInRange<double, uint32_t> {};
+
+template<>
+struct ThrowOnConvert<float, int32_t> : public ThrowIfNotInRange<float, int32_t> {};
+
+template<>
+struct ThrowOnConvert<float, uint32_t> : public ThrowIfNotInRange<float, uint32_t> {};
+
+template<typename V, typename Vret>
+static bool
+FuncConvert(JSContext* cx, unsigned argc, Value* vp)
+{
+ typedef typename V::Elem Elem;
+ typedef typename Vret::Elem RetElem;
+
+ static_assert(!mozilla::IsSame<V,Vret>::value, "Can't convert SIMD type to itself");
+ static_assert(V::lanes == Vret::lanes, "Can only convert from same number of lanes");
+ static_assert(!mozilla::IsIntegral<Elem>::value || !mozilla::IsIntegral<RetElem>::value,
+ "Cannot convert between integer SIMD types");
+
+ CallArgs args = CallArgsFromVp(argc, vp);
+ if (args.length() != 1 || !IsVectorObject<V>(args[0]))
+ return ErrorBadArgs(cx);
+
+ TypedObjectElemArray<Elem> val(args[0]);
+ RetElem result[Vret::lanes];
+ for (unsigned i = 0; i < V::lanes; i++) {
+ if (ThrowOnConvert<Elem, RetElem>::value(val[i])) {
+ JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_SIMD_FAILED_CONVERSION);
+ return false;
+ }
+ result[i] = ConvertScalar<RetElem>(val[i]);
+ }
+
+ return StoreResult<Vret>(cx, args, result);
+}
+
+template<typename V, typename Vret>
+static bool
+FuncConvertBits(JSContext* cx, unsigned argc, Value* vp)
+{
+ typedef typename V::Elem Elem;
+ typedef typename Vret::Elem RetElem;
+
+ static_assert(!mozilla::IsSame<V, Vret>::value, "Can't convert SIMD type to itself");
+ static_assert(V::lanes * sizeof(Elem) == Vret::lanes * sizeof(RetElem),
+ "Can only bitcast from the same number of bits");
+
+ CallArgs args = CallArgsFromVp(argc, vp);
+ if (args.length() != 1 || !IsVectorObject<V>(args[0]))
+ return ErrorBadArgs(cx);
+
+ // While we could just pass the typedMem of args[0] as StoreResults' last
+ // argument, a GC could move the pointer to its memory in the meanwhile.
+ // For consistency with other SIMD functions, simply copy the input in a
+ // temporary array.
+ RetElem copy[Vret::lanes];
+ {
+ JS::AutoCheckCannotGC nogc(cx);
+ memcpy(copy, TypedObjectMemory<RetElem*>(args[0], nogc), Vret::lanes * sizeof(RetElem));
+ }
+ return StoreResult<Vret>(cx, args, copy);
+}
+
+template<typename Vret>
+static bool
+FuncSplat(JSContext* cx, unsigned argc, Value* vp)
+{
+ typedef typename Vret::Elem RetElem;
+
+ CallArgs args = CallArgsFromVp(argc, vp);
+ RetElem arg;
+ if (!Vret::Cast(cx, args.get(0), &arg))
+ return false;
+
+ RetElem result[Vret::lanes];
+ for (unsigned i = 0; i < Vret::lanes; i++)
+ result[i] = arg;
+ return StoreResult<Vret>(cx, args, result);
+}
+
+template<typename V>
+static bool
+Bool(JSContext* cx, unsigned argc, Value* vp)
+{
+ typedef typename V::Elem Elem;
+
+ CallArgs args = CallArgsFromVp(argc, vp);
+
+ Elem result[V::lanes];
+ for (unsigned i = 0; i < V::lanes; i++)
+ result[i] = ToBoolean(args.get(i)) ? -1 : 0;
+ return StoreResult<V>(cx, args, result);
+}
+
+template<typename V, typename MaskType>
+static bool
+SelectBits(JSContext* cx, unsigned argc, Value* vp)
+{
+ typedef typename V::Elem Elem;
+ typedef typename MaskType::Elem MaskTypeElem;
+
+ CallArgs args = CallArgsFromVp(argc, vp);
+ if (args.length() != 3 || !IsVectorObject<MaskType>(args[0]) ||
+ !IsVectorObject<V>(args[1]) || !IsVectorObject<V>(args[2]))
+ {
+ return ErrorBadArgs(cx);
+ }
+
+ TypedObjectElemArray<MaskTypeElem> val(args[0]);
+ TypedObjectElemArray<MaskTypeElem> tv(args[1]);
+ TypedObjectElemArray<MaskTypeElem> fv(args[2]);
+
+ MaskTypeElem tr[MaskType::lanes];
+ for (unsigned i = 0; i < MaskType::lanes; i++)
+ tr[i] = And<MaskTypeElem>::apply(val[i], tv[i]);
+
+ MaskTypeElem fr[MaskType::lanes];
+ for (unsigned i = 0; i < MaskType::lanes; i++)
+ fr[i] = And<MaskTypeElem>::apply(Not<MaskTypeElem>::apply(val[i]), fv[i]);
+
+ MaskTypeElem orInt[MaskType::lanes];
+ for (unsigned i = 0; i < MaskType::lanes; i++)
+ orInt[i] = Or<MaskTypeElem>::apply(tr[i], fr[i]);
+
+ Elem* result = reinterpret_cast<Elem*>(orInt);
+ return StoreResult<V>(cx, args, result);
+}
+
+template<typename V, typename MaskType>
+static bool
+Select(JSContext* cx, unsigned argc, Value* vp)
+{
+ typedef typename V::Elem Elem;
+ typedef typename MaskType::Elem MaskTypeElem;
+
+ CallArgs args = CallArgsFromVp(argc, vp);
+ if (args.length() != 3 || !IsVectorObject<MaskType>(args[0]) ||
+ !IsVectorObject<V>(args[1]) || !IsVectorObject<V>(args[2]))
+ {
+ return ErrorBadArgs(cx);
+ }
+
+ TypedObjectElemArray<MaskTypeElem> mask(args[0]);
+ TypedObjectElemArray<Elem> tv(args[1]);
+ TypedObjectElemArray<Elem> fv(args[2]);
+
+ Elem result[V::lanes];
+ for (unsigned i = 0; i < V::lanes; i++)
+ result[i] = mask[i] ? tv[i] : fv[i];
+
+ return StoreResult<V>(cx, args, result);
+}
+
+// Extract an integer lane index from a function argument.
+//
+// Register an exception and return false if the argument is not suitable.
+static bool
+ArgumentToLaneIndex(JSContext* cx, JS::HandleValue v, unsigned limit, unsigned* lane)
+{
+ uint64_t arg;
+ if (!ToIntegerIndex(cx, v, &arg))
+ return false;
+ if (arg >= limit)
+ return ErrorBadIndex(cx);
+
+ *lane = unsigned(arg);
+ return true;
+}
+
+// Look for arguments (ta, idx) where ta is a TypedArray and idx is a
+// non-negative integer.
+// Check that accessBytes can be accessed starting from index idx in the array.
+// Return the array handle in typedArray and idx converted to a byte offset in byteStart.
+static bool
+TypedArrayFromArgs(JSContext* cx, const CallArgs& args, uint32_t accessBytes,
+ MutableHandleObject typedArray, size_t* byteStart)
+{
+ if (!args[0].isObject())
+ return ErrorBadArgs(cx);
+
+ JSObject& argobj = args[0].toObject();
+ if (!argobj.is<TypedArrayObject>())
+ return ErrorBadArgs(cx);
+
+ typedArray.set(&argobj);
+
+ uint64_t index;
+ if (!ToIntegerIndex(cx, args[1], &index))
+ return false;
+
+ // Do the range check in 64 bits even when size_t is 32 bits.
+ // This can't overflow because index <= 2^53.
+ uint64_t bytes = index * typedArray->as<TypedArrayObject>().bytesPerElement();
+ // Keep in sync with AsmJS OnOutOfBounds function.
+ if ((bytes + accessBytes) > typedArray->as<TypedArrayObject>().byteLength())
+ return ErrorBadIndex(cx);
+
+ *byteStart = bytes;
+
+ return true;
+}
+
+template<class V, unsigned NumElem>
+static bool
+Load(JSContext* cx, unsigned argc, Value* vp)
+{
+ typedef typename V::Elem Elem;
+
+ CallArgs args = CallArgsFromVp(argc, vp);
+ if (args.length() != 2)
+ return ErrorBadArgs(cx);
+
+ size_t byteStart;
+ RootedObject typedArray(cx);
+ if (!TypedArrayFromArgs(cx, args, sizeof(Elem) * NumElem, &typedArray, &byteStart))
+ return false;
+
+ Rooted<TypeDescr*> typeDescr(cx, GetTypeDescr<V>(cx));
+ if (!typeDescr)
+ return false;
+
+ Rooted<TypedObject*> result(cx, TypedObject::createZeroed(cx, typeDescr, 0));
+ if (!result)
+ return false;
+
+ JS::AutoCheckCannotGC nogc(cx);
+ SharedMem<Elem*> src =
+ typedArray->as<TypedArrayObject>().viewDataEither().addBytes(byteStart).cast<Elem*>();
+ Elem* dst = reinterpret_cast<Elem*>(result->typedMem(nogc));
+ jit::AtomicOperations::podCopySafeWhenRacy(SharedMem<Elem*>::unshared(dst), src, NumElem);
+
+ args.rval().setObject(*result);
+ return true;
+}
+
+template<class V, unsigned NumElem>
+static bool
+Store(JSContext* cx, unsigned argc, Value* vp)
+{
+ typedef typename V::Elem Elem;
+
+ CallArgs args = CallArgsFromVp(argc, vp);
+ if (args.length() != 3)
+ return ErrorBadArgs(cx);
+
+ size_t byteStart;
+ RootedObject typedArray(cx);
+ if (!TypedArrayFromArgs(cx, args, sizeof(Elem) * NumElem, &typedArray, &byteStart))
+ return false;
+
+ if (!IsVectorObject<V>(args[2]))
+ return ErrorBadArgs(cx);
+
+ JS::AutoCheckCannotGC nogc(cx);
+ Elem* src = TypedObjectMemory<Elem*>(args[2], nogc);
+ SharedMem<Elem*> dst =
+ typedArray->as<TypedArrayObject>().viewDataEither().addBytes(byteStart).cast<Elem*>();
+ js::jit::AtomicOperations::podCopySafeWhenRacy(dst, SharedMem<Elem*>::unshared(src), NumElem);
+
+ args.rval().setObject(args[2].toObject());
+ return true;
+}
+
+#define DEFINE_SIMD_FLOAT32X4_FUNCTION(Name, Func, Operands) \
+bool \
+js::simd_float32x4_##Name(JSContext* cx, unsigned argc, Value* vp) \
+{ \
+ return Func(cx, argc, vp); \
+}
+FLOAT32X4_FUNCTION_LIST(DEFINE_SIMD_FLOAT32X4_FUNCTION)
+#undef DEFINE_SIMD_FLOAT32X4_FUNCTION
+
+#define DEFINE_SIMD_FLOAT64X2_FUNCTION(Name, Func, Operands) \
+bool \
+js::simd_float64x2_##Name(JSContext* cx, unsigned argc, Value* vp) \
+{ \
+ return Func(cx, argc, vp); \
+}
+FLOAT64X2_FUNCTION_LIST(DEFINE_SIMD_FLOAT64X2_FUNCTION)
+#undef DEFINE_SIMD_FLOAT64X2_FUNCTION
+
+#define DEFINE_SIMD_INT8X16_FUNCTION(Name, Func, Operands) \
+bool \
+js::simd_int8x16_##Name(JSContext* cx, unsigned argc, Value* vp) \
+{ \
+ return Func(cx, argc, vp); \
+}
+INT8X16_FUNCTION_LIST(DEFINE_SIMD_INT8X16_FUNCTION)
+#undef DEFINE_SIMD_INT8X16_FUNCTION
+
+#define DEFINE_SIMD_INT16X8_FUNCTION(Name, Func, Operands) \
+bool \
+js::simd_int16x8_##Name(JSContext* cx, unsigned argc, Value* vp) \
+{ \
+ return Func(cx, argc, vp); \
+}
+INT16X8_FUNCTION_LIST(DEFINE_SIMD_INT16X8_FUNCTION)
+#undef DEFINE_SIMD_INT16X8_FUNCTION
+
+#define DEFINE_SIMD_INT32X4_FUNCTION(Name, Func, Operands) \
+bool \
+js::simd_int32x4_##Name(JSContext* cx, unsigned argc, Value* vp) \
+{ \
+ return Func(cx, argc, vp); \
+}
+INT32X4_FUNCTION_LIST(DEFINE_SIMD_INT32X4_FUNCTION)
+#undef DEFINE_SIMD_INT32X4_FUNCTION
+
+#define DEFINE_SIMD_UINT8X16_FUNCTION(Name, Func, Operands) \
+bool \
+js::simd_uint8x16_##Name(JSContext* cx, unsigned argc, Value* vp) \
+{ \
+ return Func(cx, argc, vp); \
+}
+UINT8X16_FUNCTION_LIST(DEFINE_SIMD_UINT8X16_FUNCTION)
+#undef DEFINE_SIMD_UINT8X16_FUNCTION
+
+#define DEFINE_SIMD_UINT16X8_FUNCTION(Name, Func, Operands) \
+bool \
+js::simd_uint16x8_##Name(JSContext* cx, unsigned argc, Value* vp) \
+{ \
+ return Func(cx, argc, vp); \
+}
+UINT16X8_FUNCTION_LIST(DEFINE_SIMD_UINT16X8_FUNCTION)
+#undef DEFINE_SIMD_UINT16X8_FUNCTION
+
+#define DEFINE_SIMD_UINT32X4_FUNCTION(Name, Func, Operands) \
+bool \
+js::simd_uint32x4_##Name(JSContext* cx, unsigned argc, Value* vp) \
+{ \
+ return Func(cx, argc, vp); \
+}
+UINT32X4_FUNCTION_LIST(DEFINE_SIMD_UINT32X4_FUNCTION)
+#undef DEFINE_SIMD_UINT32X4_FUNCTION
+
+#define DEFINE_SIMD_BOOL8X16_FUNCTION(Name, Func, Operands) \
+bool \
+js::simd_bool8x16_##Name(JSContext* cx, unsigned argc, Value* vp) \
+{ \
+ return Func(cx, argc, vp); \
+}
+
+BOOL8X16_FUNCTION_LIST(DEFINE_SIMD_BOOL8X16_FUNCTION)
+#undef DEFINE_SIMD_BOOL8X16_FUNCTION
+
+#define DEFINE_SIMD_BOOL16X8_FUNCTION(Name, Func, Operands) \
+bool \
+js::simd_bool16x8_##Name(JSContext* cx, unsigned argc, Value* vp) \
+{ \
+ return Func(cx, argc, vp); \
+}
+BOOL16X8_FUNCTION_LIST(DEFINE_SIMD_BOOL16X8_FUNCTION)
+#undef DEFINE_SIMD_BOOL16X8_FUNCTION
+
+#define DEFINE_SIMD_BOOL32X4_FUNCTION(Name, Func, Operands) \
+bool \
+js::simd_bool32x4_##Name(JSContext* cx, unsigned argc, Value* vp) \
+{ \
+ return Func(cx, argc, vp); \
+}
+BOOL32X4_FUNCTION_LIST(DEFINE_SIMD_BOOL32X4_FUNCTION)
+#undef DEFINE_SIMD_BOOL32X4_FUNCTION
+
+#define DEFINE_SIMD_BOOL64X2_FUNCTION(Name, Func, Operands) \
+bool \
+js::simd_bool64x2_##Name(JSContext* cx, unsigned argc, Value* vp) \
+{ \
+ return Func(cx, argc, vp); \
+}
+BOOL64X2_FUNCTION_LIST(DEFINE_SIMD_BOOL64X2_FUNCTION)
+#undef DEFINE_SIMD_BOOL64X2_FUNCTION