diff options
author | Matt A. Tobin <mattatobin@localhost.localdomain> | 2018-02-02 04:16:08 -0500 |
---|---|---|
committer | Matt A. Tobin <mattatobin@localhost.localdomain> | 2018-02-02 04:16:08 -0500 |
commit | 5f8de423f190bbb79a62f804151bc24824fa32d8 (patch) | |
tree | 10027f336435511475e392454359edea8e25895d /js/src/builtin/SIMD.cpp | |
parent | 49ee0794b5d912db1f95dce6eb52d781dc210db5 (diff) | |
download | uxp-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.gz |
Add m-esr52 at 52.6.0
Diffstat (limited to 'js/src/builtin/SIMD.cpp')
-rw-r--r-- | js/src/builtin/SIMD.cpp | 1552 |
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 0000000000..2383922db8 --- /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 |