michael@0: /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- michael@0: * vim: set ts=8 sts=4 et sw=4 tw=99: michael@0: * This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: /* michael@0: * JS SIMD pseudo-module. michael@0: * Specification matches polyfill: michael@0: * https://github.com/johnmccutchan/ecmascript_simd/blob/master/src/ecmascript_simd.js michael@0: * The objects float32x4 and int32x4 are installed on the SIMD pseudo-module. michael@0: */ michael@0: michael@0: #include "builtin/SIMD.h" michael@0: michael@0: #include "jsapi.h" michael@0: #include "jsfriendapi.h" michael@0: michael@0: #include "builtin/TypedObject.h" michael@0: #include "js/Value.h" michael@0: michael@0: #include "jsobjinlines.h" michael@0: michael@0: using namespace js; michael@0: michael@0: using mozilla::ArrayLength; michael@0: using mozilla::IsFinite; michael@0: using mozilla::IsNaN; michael@0: michael@0: namespace js { michael@0: extern const JSFunctionSpec Float32x4Methods[]; michael@0: extern const JSFunctionSpec Int32x4Methods[]; michael@0: } michael@0: michael@0: /////////////////////////////////////////////////////////////////////////// michael@0: // X4 michael@0: michael@0: static const char *laneNames[] = {"lane 0", "lane 1", "lane 2", "lane3"}; michael@0: michael@0: template michael@0: static bool GetX4Lane(JSContext *cx, unsigned argc, Value *vp) { michael@0: typedef typename Type32x4::Elem Elem; michael@0: michael@0: CallArgs args = CallArgsFromVp(argc, vp); michael@0: if(!args.thisv().isObject() || !args.thisv().toObject().is()) { michael@0: JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_INCOMPATIBLE_PROTO, michael@0: X4TypeDescr::class_.name, laneNames[lane], michael@0: InformalValueTypeName(args.thisv())); michael@0: return false; michael@0: } michael@0: michael@0: TypedObject &typedObj = args.thisv().toObject().as(); michael@0: TypeDescr &descr = typedObj.typeDescr(); michael@0: if (descr.kind() != TypeDescr::X4 || descr.as().type() != Type32x4::type) { michael@0: JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_INCOMPATIBLE_PROTO, michael@0: X4TypeDescr::class_.name, laneNames[lane], michael@0: InformalValueTypeName(args.thisv())); michael@0: return false; michael@0: } michael@0: michael@0: MOZ_ASSERT(!typedObj.owner().isNeutered()); michael@0: Elem *data = reinterpret_cast(typedObj.typedMem()); michael@0: Type32x4::setReturn(args, data[lane]); michael@0: return true; michael@0: } michael@0: michael@0: #define LANE_ACCESSOR(type, lane) \ michael@0: static bool type##Lane##lane(JSContext *cx, unsigned argc, Value *vp) { \ michael@0: return GetX4Lane(cx, argc, vp);\ michael@0: } michael@0: michael@0: #define FOUR_LANES_ACCESSOR(type) \ michael@0: LANE_ACCESSOR(type, 0); \ michael@0: LANE_ACCESSOR(type, 1); \ michael@0: LANE_ACCESSOR(type, 2); \ michael@0: LANE_ACCESSOR(type, 3); michael@0: michael@0: FOUR_LANES_ACCESSOR(Int32x4); michael@0: FOUR_LANES_ACCESSOR(Float32x4); michael@0: #undef FOUR_LANES_ACCESSOR michael@0: #undef LANE_ACCESSOR michael@0: michael@0: template michael@0: static bool SignMask(JSContext *cx, unsigned argc, Value *vp) { michael@0: typedef typename Type32x4::Elem Elem; michael@0: michael@0: CallArgs args = CallArgsFromVp(argc, vp); michael@0: if(!args.thisv().isObject() || !args.thisv().toObject().is()) { michael@0: JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_INCOMPATIBLE_PROTO, michael@0: X4TypeDescr::class_.name, "signMask", michael@0: InformalValueTypeName(args.thisv())); michael@0: return false; michael@0: } michael@0: michael@0: TypedObject &typedObj = args.thisv().toObject().as(); michael@0: TypeDescr &descr = typedObj.typeDescr(); michael@0: if (descr.kind() != TypeDescr::X4 || descr.as().type() != Type32x4::type) { michael@0: JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_INCOMPATIBLE_PROTO, michael@0: X4TypeDescr::class_.name, "signMask", michael@0: InformalValueTypeName(args.thisv())); michael@0: return false; michael@0: } michael@0: michael@0: MOZ_ASSERT(!typedObj.owner().isNeutered()); michael@0: Elem *data = reinterpret_cast(typedObj.typedMem()); michael@0: int32_t mx = data[0] < 0.0 ? 1 : 0; michael@0: int32_t my = data[1] < 0.0 ? 1 : 0; michael@0: int32_t mz = data[2] < 0.0 ? 1 : 0; michael@0: int32_t mw = data[3] < 0.0 ? 1 : 0; michael@0: int32_t result = mx | my << 1 | mz << 2 | mw << 3; michael@0: args.rval().setInt32(result); michael@0: return true; michael@0: } michael@0: michael@0: #define SIGN_MASK(type) \ michael@0: static bool type##SignMask(JSContext *cx, unsigned argc, Value *vp) { \ michael@0: return SignMask(cx, argc, vp); \ michael@0: } michael@0: SIGN_MASK(Float32x4); michael@0: SIGN_MASK(Int32x4); michael@0: #undef SIGN_MASK michael@0: michael@0: const Class X4TypeDescr::class_ = { michael@0: "X4", michael@0: JSCLASS_HAS_RESERVED_SLOTS(JS_DESCR_SLOTS), michael@0: JS_PropertyStub, /* addProperty */ michael@0: JS_DeletePropertyStub, /* delProperty */ michael@0: JS_PropertyStub, /* getProperty */ michael@0: JS_StrictPropertyStub, /* setProperty */ michael@0: JS_EnumerateStub, michael@0: JS_ResolveStub, michael@0: JS_ConvertStub, michael@0: nullptr, /* finalize */ michael@0: call, /* call */ michael@0: nullptr, /* hasInstance */ michael@0: nullptr, /* construct */ michael@0: nullptr michael@0: }; michael@0: michael@0: // These classes just exist to group together various properties and so on. michael@0: namespace js { michael@0: class Int32x4Defn { michael@0: public: michael@0: static const X4TypeDescr::Type type = X4TypeDescr::TYPE_INT32; michael@0: static const JSFunctionSpec TypeDescriptorMethods[]; michael@0: static const JSPropertySpec TypedObjectProperties[]; michael@0: static const JSFunctionSpec TypedObjectMethods[]; michael@0: }; michael@0: class Float32x4Defn { michael@0: public: michael@0: static const X4TypeDescr::Type type = X4TypeDescr::TYPE_FLOAT32; michael@0: static const JSFunctionSpec TypeDescriptorMethods[]; michael@0: static const JSPropertySpec TypedObjectProperties[]; michael@0: static const JSFunctionSpec TypedObjectMethods[]; michael@0: }; michael@0: } // namespace js michael@0: michael@0: const JSFunctionSpec js::Float32x4Defn::TypeDescriptorMethods[] = { michael@0: JS_SELF_HOSTED_FN("toSource", "DescrToSource", 0, 0), michael@0: JS_SELF_HOSTED_FN("array", "ArrayShorthand", 1, 0), michael@0: JS_SELF_HOSTED_FN("equivalent", "TypeDescrEquivalent", 1, 0), michael@0: JS_FS_END michael@0: }; michael@0: michael@0: const JSPropertySpec js::Float32x4Defn::TypedObjectProperties[] = { michael@0: JS_PSG("x", Float32x4Lane0, JSPROP_PERMANENT), michael@0: JS_PSG("y", Float32x4Lane1, JSPROP_PERMANENT), michael@0: JS_PSG("z", Float32x4Lane2, JSPROP_PERMANENT), michael@0: JS_PSG("w", Float32x4Lane3, JSPROP_PERMANENT), michael@0: JS_PSG("signMask", Float32x4SignMask, JSPROP_PERMANENT), michael@0: JS_PS_END michael@0: }; michael@0: michael@0: const JSFunctionSpec js::Float32x4Defn::TypedObjectMethods[] = { michael@0: JS_SELF_HOSTED_FN("toSource", "X4ToSource", 0, 0), michael@0: JS_FS_END michael@0: }; michael@0: michael@0: const JSFunctionSpec js::Int32x4Defn::TypeDescriptorMethods[] = { michael@0: JS_SELF_HOSTED_FN("toSource", "DescrToSource", 0, 0), michael@0: JS_SELF_HOSTED_FN("array", "ArrayShorthand", 1, 0), michael@0: JS_SELF_HOSTED_FN("equivalent", "TypeDescrEquivalent", 1, 0), michael@0: JS_FS_END, michael@0: }; michael@0: michael@0: const JSPropertySpec js::Int32x4Defn::TypedObjectProperties[] = { michael@0: JS_PSG("x", Int32x4Lane0, JSPROP_PERMANENT), michael@0: JS_PSG("y", Int32x4Lane1, JSPROP_PERMANENT), michael@0: JS_PSG("z", Int32x4Lane2, JSPROP_PERMANENT), michael@0: JS_PSG("w", Int32x4Lane3, JSPROP_PERMANENT), michael@0: JS_PSG("signMask", Int32x4SignMask, JSPROP_PERMANENT), michael@0: JS_PS_END michael@0: }; michael@0: michael@0: const JSFunctionSpec js::Int32x4Defn::TypedObjectMethods[] = { michael@0: JS_SELF_HOSTED_FN("toSource", "X4ToSource", 0, 0), michael@0: JS_FS_END michael@0: }; michael@0: michael@0: template michael@0: static JSObject * michael@0: CreateX4Class(JSContext *cx, michael@0: Handle global, michael@0: HandlePropertyName stringRepr) michael@0: { michael@0: const X4TypeDescr::Type type = T::type; michael@0: michael@0: RootedObject funcProto(cx, global->getOrCreateFunctionPrototype(cx)); michael@0: if (!funcProto) michael@0: return nullptr; michael@0: michael@0: // Create type constructor itself and initialize its reserved slots. michael@0: michael@0: Rooted x4(cx); michael@0: x4 = NewObjectWithProto(cx, funcProto, global, TenuredObject); michael@0: if (!x4) michael@0: return nullptr; michael@0: michael@0: x4->initReservedSlot(JS_DESCR_SLOT_KIND, Int32Value(TypeDescr::X4)); michael@0: x4->initReservedSlot(JS_DESCR_SLOT_STRING_REPR, StringValue(stringRepr)); michael@0: x4->initReservedSlot(JS_DESCR_SLOT_ALIGNMENT, Int32Value(X4TypeDescr::size(type))); michael@0: x4->initReservedSlot(JS_DESCR_SLOT_SIZE, Int32Value(X4TypeDescr::alignment(type))); michael@0: x4->initReservedSlot(JS_DESCR_SLOT_OPAQUE, BooleanValue(false)); michael@0: x4->initReservedSlot(JS_DESCR_SLOT_TYPE, Int32Value(T::type)); michael@0: michael@0: if (!CreateUserSizeAndAlignmentProperties(cx, x4)) michael@0: return nullptr; michael@0: michael@0: // Create prototype property, which inherits from Object.prototype. michael@0: michael@0: RootedObject objProto(cx, global->getOrCreateObjectPrototype(cx)); michael@0: if (!objProto) michael@0: return nullptr; michael@0: Rooted proto(cx); michael@0: proto = NewObjectWithProto(cx, objProto, nullptr, TenuredObject); michael@0: if (!proto) michael@0: return nullptr; michael@0: proto->initTypeDescrSlot(*x4); michael@0: x4->initReservedSlot(JS_DESCR_SLOT_TYPROTO, ObjectValue(*proto)); michael@0: michael@0: // Link constructor to prototype and install properties. michael@0: michael@0: if (!JS_DefineFunctions(cx, x4, T::TypeDescriptorMethods)) michael@0: return nullptr; michael@0: michael@0: if (!LinkConstructorAndPrototype(cx, x4, proto) || michael@0: !DefinePropertiesAndBrand(cx, proto, T::TypedObjectProperties, michael@0: T::TypedObjectMethods)) michael@0: { michael@0: return nullptr; michael@0: } michael@0: michael@0: return x4; michael@0: } michael@0: michael@0: bool michael@0: X4TypeDescr::call(JSContext *cx, unsigned argc, Value *vp) michael@0: { michael@0: CallArgs args = CallArgsFromVp(argc, vp); michael@0: const uint32_t LANES = 4; michael@0: michael@0: if (args.length() < LANES) { michael@0: JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_MORE_ARGS_NEEDED, michael@0: args.callee().getClass()->name, "3", "s"); michael@0: return false; michael@0: } michael@0: michael@0: double values[LANES]; michael@0: for (uint32_t i = 0; i < LANES; i++) { michael@0: if (!ToNumber(cx, args[i], &values[i])) michael@0: return false; michael@0: } michael@0: michael@0: Rooted descr(cx, &args.callee().as()); michael@0: Rooted result(cx, TypedObject::createZeroed(cx, descr, 0)); michael@0: if (!result) michael@0: return false; michael@0: michael@0: MOZ_ASSERT(!result->owner().isNeutered()); michael@0: switch (descr->type()) { michael@0: #define STORE_LANES(_constant, _type, _name) \ michael@0: case _constant: \ michael@0: { \ michael@0: _type *mem = reinterpret_cast<_type*>(result->typedMem()); \ michael@0: for (uint32_t i = 0; i < LANES; i++) { \ michael@0: mem[i] = ConvertScalar<_type>(values[i]); \ michael@0: } \ michael@0: break; \ michael@0: } michael@0: JS_FOR_EACH_X4_TYPE_REPR(STORE_LANES) michael@0: #undef STORE_LANES michael@0: } michael@0: args.rval().setObject(*result); michael@0: return true; michael@0: } michael@0: michael@0: /////////////////////////////////////////////////////////////////////////// michael@0: // SIMD class michael@0: michael@0: const Class SIMDObject::class_ = { michael@0: "SIMD", michael@0: JSCLASS_HAS_CACHED_PROTO(JSProto_SIMD), michael@0: JS_PropertyStub, /* addProperty */ michael@0: JS_DeletePropertyStub, /* delProperty */ michael@0: JS_PropertyStub, /* getProperty */ michael@0: JS_StrictPropertyStub, /* setProperty */ michael@0: JS_EnumerateStub, michael@0: JS_ResolveStub, michael@0: JS_ConvertStub, michael@0: nullptr, /* finalize */ michael@0: nullptr, /* call */ michael@0: nullptr, /* hasInstance */ michael@0: nullptr, /* construct */ michael@0: nullptr michael@0: }; michael@0: michael@0: JSObject * michael@0: SIMDObject::initClass(JSContext *cx, Handle global) michael@0: { michael@0: // SIMD relies on having the TypedObject module initialized. michael@0: // In particular, the self-hosted code for array() wants michael@0: // to be able to call GetTypedObjectModule(). It is NOT necessary michael@0: // to install the TypedObjectModule global, but at the moment michael@0: // those two things are not separable. michael@0: if (!global->getOrCreateTypedObjectModule(cx)) michael@0: return nullptr; michael@0: michael@0: // Create SIMD Object. michael@0: RootedObject objProto(cx, global->getOrCreateObjectPrototype(cx)); michael@0: if(!objProto) michael@0: return nullptr; michael@0: RootedObject SIMD(cx, NewObjectWithGivenProto(cx, &SIMDObject::class_, objProto, michael@0: global, SingletonObject)); michael@0: if (!SIMD) michael@0: return nullptr; michael@0: michael@0: // float32x4 michael@0: michael@0: RootedObject float32x4Object(cx); michael@0: float32x4Object = CreateX4Class(cx, global, michael@0: cx->names().float32x4); michael@0: if (!float32x4Object) michael@0: return nullptr; michael@0: michael@0: RootedValue float32x4Value(cx, ObjectValue(*float32x4Object)); michael@0: if (!JS_DefineFunctions(cx, float32x4Object, Float32x4Methods) || michael@0: !JSObject::defineProperty(cx, SIMD, cx->names().float32x4, michael@0: float32x4Value, nullptr, nullptr, michael@0: JSPROP_READONLY | JSPROP_PERMANENT)) michael@0: { michael@0: return nullptr; michael@0: } michael@0: michael@0: // int32x4 michael@0: michael@0: RootedObject int32x4Object(cx); michael@0: int32x4Object = CreateX4Class(cx, global, michael@0: cx->names().int32x4); michael@0: if (!int32x4Object) michael@0: return nullptr; michael@0: michael@0: RootedValue int32x4Value(cx, ObjectValue(*int32x4Object)); michael@0: if (!JS_DefineFunctions(cx, int32x4Object, Int32x4Methods) || michael@0: !JSObject::defineProperty(cx, SIMD, cx->names().int32x4, michael@0: int32x4Value, nullptr, nullptr, michael@0: JSPROP_READONLY | JSPROP_PERMANENT)) michael@0: { michael@0: return nullptr; michael@0: } michael@0: michael@0: RootedValue SIMDValue(cx, ObjectValue(*SIMD)); michael@0: michael@0: // Everything is set up, install SIMD on the global object. michael@0: if (!JSObject::defineProperty(cx, global, cx->names().SIMD, SIMDValue, nullptr, nullptr, 0)) michael@0: return nullptr; michael@0: michael@0: global->setConstructor(JSProto_SIMD, SIMDValue); michael@0: michael@0: // Define float32x4 functions and install as a property of the SIMD object. michael@0: global->setFloat32x4TypeDescr(*float32x4Object); michael@0: michael@0: // Define int32x4 functions and install as a property of the SIMD object. michael@0: global->setInt32x4TypeDescr(*int32x4Object); michael@0: michael@0: return SIMD; michael@0: } michael@0: michael@0: JSObject * michael@0: js_InitSIMDClass(JSContext *cx, HandleObject obj) michael@0: { michael@0: JS_ASSERT(obj->is()); michael@0: Rooted global(cx, &obj->as()); michael@0: return SIMDObject::initClass(cx, global); michael@0: } michael@0: michael@0: template michael@0: static bool michael@0: IsVectorObject(HandleValue v) michael@0: { michael@0: if (!v.isObject()) michael@0: return false; michael@0: michael@0: JSObject &obj = v.toObject(); michael@0: if (!obj.is()) michael@0: return false; michael@0: michael@0: TypeDescr &typeRepr = obj.as().typeDescr(); michael@0: if (typeRepr.kind() != TypeDescr::X4) michael@0: return false; michael@0: michael@0: return typeRepr.as().type() == V::type; michael@0: } michael@0: michael@0: template michael@0: static Elem michael@0: TypedObjectMemory(HandleValue v) michael@0: { michael@0: TypedObject &obj = v.toObject().as(); michael@0: MOZ_ASSERT(!obj.owner().isNeutered()); michael@0: return reinterpret_cast(obj.typedMem()); michael@0: } michael@0: michael@0: template michael@0: JSObject * michael@0: js::Create(JSContext *cx, typename V::Elem *data) michael@0: { michael@0: typedef typename V::Elem Elem; michael@0: Rooted typeDescr(cx, &V::GetTypeDescr(*cx->global())); michael@0: JS_ASSERT(typeDescr); michael@0: michael@0: Rooted result(cx, TypedObject::createZeroed(cx, typeDescr, 0)); michael@0: if (!result) michael@0: return nullptr; michael@0: michael@0: MOZ_ASSERT(!result->owner().isNeutered()); michael@0: Elem *resultMem = reinterpret_cast(result->typedMem()); michael@0: memcpy(resultMem, data, sizeof(Elem) * V::lanes); michael@0: return result; michael@0: } michael@0: michael@0: template JSObject *js::Create(JSContext *cx, Float32x4::Elem *data); michael@0: template JSObject *js::Create(JSContext *cx, Int32x4::Elem *data); michael@0: michael@0: namespace js { michael@0: template michael@0: struct Abs { michael@0: static inline T apply(T x, T zero) { return V::toType(x < 0 ? -1 * x : x); } michael@0: }; michael@0: template michael@0: struct Neg { michael@0: static inline T apply(T x, T zero) { return V::toType(-1 * x); } michael@0: }; michael@0: template michael@0: struct Not { michael@0: static inline T apply(T x, T zero) { return V::toType(~x); } michael@0: }; michael@0: template michael@0: struct Rec { michael@0: static inline T apply(T x, T zero) { return V::toType(1 / x); } michael@0: }; michael@0: template michael@0: struct RecSqrt { michael@0: static inline T apply(T x, T zero) { return V::toType(1 / sqrt(x)); } michael@0: }; michael@0: template michael@0: struct Sqrt { michael@0: static inline T apply(T x, T zero) { return V::toType(sqrt(x)); } michael@0: }; michael@0: template michael@0: struct Add { michael@0: static inline T apply(T l, T r) { return V::toType(l + r); } michael@0: }; michael@0: template michael@0: struct Sub { michael@0: static inline T apply(T l, T r) { return V::toType(l - r); } michael@0: }; michael@0: template michael@0: struct Div { michael@0: static inline T apply(T l, T r) { return V::toType(l / r); } michael@0: }; michael@0: template michael@0: struct Mul { michael@0: static inline T apply(T l, T r) { return V::toType(l * r); } michael@0: }; michael@0: template michael@0: struct Minimum { michael@0: static inline T apply(T l, T r) { return V::toType(l < r ? l : r); } michael@0: }; michael@0: template michael@0: struct Maximum { michael@0: static inline T apply(T l, T r) { return V::toType(l > r ? l : r); } michael@0: }; michael@0: template michael@0: struct LessThan { michael@0: static inline T apply(T l, T r) { return V::toType(l < r ? 0xFFFFFFFF : 0x0); } michael@0: }; michael@0: template michael@0: struct LessThanOrEqual { michael@0: static inline T apply(T l, T r) { return V::toType(l <= r ? 0xFFFFFFFF : 0x0); } michael@0: }; michael@0: template michael@0: struct GreaterThan { michael@0: static inline T apply(T l, T r) { return V::toType(l > r ? 0xFFFFFFFF : 0x0); } michael@0: }; michael@0: template michael@0: struct GreaterThanOrEqual { michael@0: static inline T apply(T l, T r) { return V::toType(l >= r ? 0xFFFFFFFF : 0x0); } michael@0: }; michael@0: template michael@0: struct Equal { michael@0: static inline T apply(T l, T r) { return V::toType(l == r ? 0xFFFFFFFF : 0x0); } michael@0: }; michael@0: template michael@0: struct NotEqual { michael@0: static inline T apply(T l, T r) { return V::toType(l != r ? 0xFFFFFFFF : 0x0); } michael@0: }; michael@0: template michael@0: struct Xor { michael@0: static inline T apply(T l, T r) { return V::toType(l ^ r); } michael@0: }; michael@0: template michael@0: struct And { michael@0: static inline T apply(T l, T r) { return V::toType(l & r); } michael@0: }; michael@0: template michael@0: struct Or { michael@0: static inline T apply(T l, T r) { return V::toType(l | r); } michael@0: }; michael@0: template michael@0: struct Scale { michael@0: static inline T apply(int32_t lane, T scalar, T x) { return V::toType(scalar * x); } michael@0: }; michael@0: template michael@0: struct WithX { michael@0: static inline T apply(int32_t lane, T scalar, T x) { return V::toType(lane == 0 ? scalar : x); } michael@0: }; michael@0: template michael@0: struct WithY { michael@0: static inline T apply(int32_t lane, T scalar, T x) { return V::toType(lane == 1 ? scalar : x); } michael@0: }; michael@0: template michael@0: struct WithZ { michael@0: static inline T apply(int32_t lane, T scalar, T x) { return V::toType(lane == 2 ? scalar : x); } michael@0: }; michael@0: template michael@0: struct WithW { michael@0: static inline T apply(int32_t lane, T scalar, T x) { return V::toType(lane == 3 ? scalar : x); } michael@0: }; michael@0: template michael@0: struct WithFlagX { michael@0: static inline T apply(T l, T f, T x) { return V::toType(l == 0 ? (f ? 0xFFFFFFFF : 0x0) : x); } michael@0: }; michael@0: template michael@0: struct WithFlagY { michael@0: static inline T apply(T l, T f, T x) { return V::toType(l == 1 ? (f ? 0xFFFFFFFF : 0x0) : x); } michael@0: }; michael@0: template michael@0: struct WithFlagZ { michael@0: static inline T apply(T l, T f, T x) { return V::toType(l == 2 ? (f ? 0xFFFFFFFF : 0x0) : x); } michael@0: }; michael@0: template michael@0: struct WithFlagW { michael@0: static inline T apply(T l, T f, T x) { return V::toType(l == 3 ? (f ? 0xFFFFFFFF : 0x0) : x); } michael@0: }; michael@0: template michael@0: struct Shuffle { michael@0: static inline int32_t apply(int32_t l, int32_t mask) { return V::toType((mask >> l) & 0x3); } michael@0: }; michael@0: } michael@0: michael@0: template michael@0: static bool michael@0: Func(JSContext *cx, unsigned argc, Value *vp) michael@0: { michael@0: typedef typename V::Elem Elem; michael@0: typedef typename Vret::Elem RetElem; michael@0: michael@0: CallArgs args = CallArgsFromVp(argc, vp); michael@0: if (args.length() != 1 && args.length() != 2) { michael@0: JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_BAD_ARGS); michael@0: return false; michael@0: } michael@0: michael@0: RetElem result[Vret::lanes]; michael@0: if (args.length() == 1) { michael@0: if (!IsVectorObject(args[0])) { michael@0: JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_BAD_ARGS); michael@0: return false; michael@0: } michael@0: michael@0: Elem *val = TypedObjectMemory(args[0]); michael@0: for (int32_t i = 0; i < Vret::lanes; i++) michael@0: result[i] = Op::apply(val[i], 0); michael@0: } else { michael@0: JS_ASSERT(args.length() == 2); michael@0: if(!IsVectorObject(args[0]) || !IsVectorObject(args[1])) michael@0: { michael@0: JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_BAD_ARGS); michael@0: return false; michael@0: } michael@0: michael@0: Elem *left = TypedObjectMemory(args[0]); michael@0: Elem *right = TypedObjectMemory(args[1]); michael@0: for (int32_t i = 0; i < Vret::lanes; i++) michael@0: result[i] = Op::apply(left[i], right[i]); michael@0: } michael@0: michael@0: RootedObject obj(cx, Create(cx, result)); michael@0: if (!obj) michael@0: return false; michael@0: michael@0: args.rval().setObject(*obj); michael@0: return true; michael@0: } michael@0: michael@0: template michael@0: static bool michael@0: FuncWith(JSContext *cx, unsigned argc, Value *vp) michael@0: { michael@0: typedef typename V::Elem Elem; michael@0: typedef typename Vret::Elem RetElem; michael@0: michael@0: CallArgs args = CallArgsFromVp(argc, vp); michael@0: if (args.length() != 2 || !IsVectorObject(args[0]) || michael@0: (!args[1].isNumber() && !args[1].isBoolean())) michael@0: { michael@0: JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_BAD_ARGS); michael@0: return false; michael@0: } michael@0: michael@0: Elem *val = TypedObjectMemory(args[0]); michael@0: RetElem result[Vret::lanes]; michael@0: michael@0: if (args[1].isNumber()) { michael@0: Elem withAsNumber; michael@0: if (!Vret::toType(cx, args[1], &withAsNumber)) michael@0: return false; michael@0: for (int32_t i = 0; i < Vret::lanes; i++) michael@0: result[i] = OpWith::apply(i, withAsNumber, val[i]); michael@0: } else if (args[1].isBoolean()) { michael@0: bool withAsBool = args[1].toBoolean(); michael@0: for (int32_t i = 0; i < Vret::lanes; i++) michael@0: result[i] = OpWith::apply(i, withAsBool, val[i]); michael@0: } michael@0: michael@0: RootedObject obj(cx, Create(cx, result)); michael@0: if (!obj) michael@0: return false; michael@0: michael@0: args.rval().setObject(*obj); michael@0: return true; michael@0: } michael@0: michael@0: template michael@0: static bool michael@0: FuncShuffle(JSContext *cx, unsigned argc, Value *vp) michael@0: { michael@0: typedef typename V::Elem Elem; michael@0: typedef typename Vret::Elem RetElem; michael@0: michael@0: CallArgs args = CallArgsFromVp(argc, vp); michael@0: if (args.length() != 2 && args.length() != 3) { michael@0: JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_BAD_ARGS); michael@0: return false; michael@0: } michael@0: michael@0: RetElem result[Vret::lanes]; michael@0: if (args.length() == 2) { michael@0: if (!IsVectorObject(args[0]) || !args[1].isNumber()) michael@0: { michael@0: JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_BAD_ARGS); michael@0: return false; michael@0: } michael@0: michael@0: Elem *val = TypedObjectMemory(args[0]);; michael@0: Elem arg1; michael@0: if (!Vret::toType(cx, args[1], &arg1)) michael@0: return false; michael@0: michael@0: for (int32_t i = 0; i < Vret::lanes; i++) michael@0: result[i] = val[OpShuffle::apply(i * 2, arg1)]; michael@0: } else { michael@0: JS_ASSERT(args.length() == 3); michael@0: if (!IsVectorObject(args[0]) || !IsVectorObject(args[1]) || !args[2].isNumber()) michael@0: { michael@0: JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_BAD_ARGS); michael@0: return false; michael@0: } michael@0: michael@0: Elem *val1 = TypedObjectMemory(args[0]); michael@0: Elem *val2 = TypedObjectMemory(args[1]); michael@0: Elem arg2; michael@0: if (!Vret::toType(cx, args[2], &arg2)) michael@0: return false; michael@0: michael@0: for (int32_t i = 0; i < Vret::lanes; i++) { michael@0: if (i < Vret::lanes / 2) michael@0: result[i] = val1[OpShuffle::apply(i * 2, arg2)]; michael@0: else michael@0: result[i] = val2[OpShuffle::apply(i * 2, arg2)]; michael@0: } michael@0: } michael@0: michael@0: RootedObject obj(cx, Create(cx, result)); michael@0: if (!obj) michael@0: return false; michael@0: michael@0: args.rval().setObject(*obj); michael@0: return true; michael@0: } michael@0: michael@0: template michael@0: static bool michael@0: FuncConvert(JSContext *cx, unsigned argc, Value *vp) michael@0: { michael@0: typedef typename V::Elem Elem; michael@0: typedef typename Vret::Elem RetElem; michael@0: michael@0: CallArgs args = CallArgsFromVp(argc, vp); michael@0: if (args.length() != 1 || !IsVectorObject(args[0])) michael@0: { michael@0: JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_BAD_ARGS); michael@0: return false; michael@0: } michael@0: michael@0: Elem *val = TypedObjectMemory(args[0]); michael@0: RetElem result[Vret::lanes]; michael@0: for (int32_t i = 0; i < Vret::lanes; i++) michael@0: result[i] = RetElem(val[i]); michael@0: michael@0: RootedObject obj(cx, Create(cx, result)); michael@0: if (!obj) michael@0: return false; michael@0: michael@0: args.rval().setObject(*obj); michael@0: return true; michael@0: } michael@0: michael@0: template michael@0: static bool michael@0: FuncConvertBits(JSContext *cx, unsigned argc, Value *vp) michael@0: { michael@0: typedef typename Vret::Elem RetElem; michael@0: michael@0: CallArgs args = CallArgsFromVp(argc, vp); michael@0: if (args.length() != 1 || !IsVectorObject(args[0])) michael@0: { michael@0: JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_BAD_ARGS); michael@0: return false; michael@0: } michael@0: michael@0: RetElem *val = TypedObjectMemory(args[0]); michael@0: RootedObject obj(cx, Create(cx, val)); michael@0: if (!obj) michael@0: return false; michael@0: michael@0: args.rval().setObject(*obj); michael@0: return true; michael@0: } michael@0: michael@0: template michael@0: static bool michael@0: FuncZero(JSContext *cx, unsigned argc, Value *vp) michael@0: { michael@0: typedef typename Vret::Elem RetElem; michael@0: michael@0: CallArgs args = CallArgsFromVp(argc, vp); michael@0: if (args.length() != 0) { michael@0: JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_BAD_ARGS); michael@0: return false; michael@0: } michael@0: michael@0: RetElem result[Vret::lanes]; michael@0: for (int32_t i = 0; i < Vret::lanes; i++) michael@0: result[i] = RetElem(0); michael@0: michael@0: RootedObject obj(cx, Create(cx, result)); michael@0: if (!obj) michael@0: return false; michael@0: michael@0: args.rval().setObject(*obj); michael@0: return true; michael@0: } michael@0: michael@0: template michael@0: static bool michael@0: FuncSplat(JSContext *cx, unsigned argc, Value *vp) michael@0: { michael@0: typedef typename Vret::Elem RetElem; michael@0: michael@0: CallArgs args = CallArgsFromVp(argc, vp); michael@0: if (args.length() != 1 || !args[0].isNumber()) { michael@0: JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_BAD_ARGS); michael@0: return false; michael@0: } michael@0: michael@0: RetElem arg; michael@0: if (!Vret::toType(cx, args[0], &arg)) michael@0: return false; michael@0: michael@0: RetElem result[Vret::lanes]; michael@0: for (int32_t i = 0; i < Vret::lanes; i++) michael@0: result[i] = arg; michael@0: michael@0: RootedObject obj(cx, Create(cx, result)); michael@0: if (!obj) michael@0: return false; michael@0: michael@0: args.rval().setObject(*obj); michael@0: return true; michael@0: } michael@0: michael@0: static bool michael@0: Int32x4Bool(JSContext *cx, unsigned argc, Value *vp) michael@0: { michael@0: CallArgs args = CallArgsFromVp(argc, vp); michael@0: if (args.length() != 4 || michael@0: !args[0].isBoolean() || !args[1].isBoolean() || michael@0: !args[2].isBoolean() || !args[3].isBoolean()) michael@0: { michael@0: JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_BAD_ARGS); michael@0: return false; michael@0: } michael@0: michael@0: int32_t result[Int32x4::lanes]; michael@0: for (int32_t i = 0; i < Int32x4::lanes; i++) michael@0: result[i] = args[i].toBoolean() ? 0xFFFFFFFF : 0x0; michael@0: michael@0: RootedObject obj(cx, Create(cx, result)); michael@0: if (!obj) michael@0: return false; michael@0: michael@0: args.rval().setObject(*obj); michael@0: return true; michael@0: } michael@0: michael@0: static bool michael@0: Float32x4Clamp(JSContext *cx, unsigned argc, Value *vp) michael@0: { michael@0: CallArgs args = CallArgsFromVp(argc, vp); michael@0: if (args.length() != 3 || !IsVectorObject(args[0]) || michael@0: !IsVectorObject(args[1]) || !IsVectorObject(args[2])) michael@0: { michael@0: JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_BAD_ARGS); michael@0: return false; michael@0: } michael@0: michael@0: float *val = TypedObjectMemory(args[0]); michael@0: float *lowerLimit = TypedObjectMemory(args[1]); michael@0: float *upperLimit = TypedObjectMemory(args[2]); michael@0: michael@0: float result[Float32x4::lanes]; michael@0: for (int32_t i = 0; i < Float32x4::lanes; i++) { michael@0: result[i] = val[i] < lowerLimit[i] ? lowerLimit[i] : val[i]; michael@0: result[i] = result[i] > upperLimit[i] ? upperLimit[i] : result[i]; michael@0: } michael@0: michael@0: RootedObject obj(cx, Create(cx, result)); michael@0: if (!obj) michael@0: return false; michael@0: michael@0: args.rval().setObject(*obj); michael@0: return true; michael@0: } michael@0: michael@0: static bool michael@0: Int32x4Select(JSContext *cx, unsigned argc, Value *vp) michael@0: { michael@0: CallArgs args = CallArgsFromVp(argc, vp); michael@0: if (args.length() != 3 || !IsVectorObject(args[0]) || michael@0: !IsVectorObject(args[1]) || !IsVectorObject(args[2])) michael@0: { michael@0: JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_BAD_ARGS); michael@0: return false; michael@0: } michael@0: michael@0: int32_t *val = TypedObjectMemory(args[0]); michael@0: int32_t *tv = TypedObjectMemory(args[1]); michael@0: int32_t *fv = TypedObjectMemory(args[2]); michael@0: michael@0: int32_t tr[Int32x4::lanes]; michael@0: for (int32_t i = 0; i < Int32x4::lanes; i++) michael@0: tr[i] = And::apply(val[i], tv[i]); michael@0: michael@0: int32_t fr[Int32x4::lanes]; michael@0: for (int32_t i = 0; i < Int32x4::lanes; i++) michael@0: fr[i] = And::apply(Not::apply(val[i], 0), fv[i]); michael@0: michael@0: int32_t orInt[Int32x4::lanes]; michael@0: for (int32_t i = 0; i < Int32x4::lanes; i++) michael@0: orInt[i] = Or::apply(tr[i], fr[i]); michael@0: michael@0: float *result = reinterpret_cast(orInt); michael@0: RootedObject obj(cx, Create(cx, result)); michael@0: if (!obj) michael@0: return false; michael@0: michael@0: args.rval().setObject(*obj); michael@0: return true; michael@0: } michael@0: michael@0: #define DEFINE_SIMD_FLOAT32X4_FUNCTION(Name, Func, Operands, Flags, MIRId) \ michael@0: bool \ michael@0: js::simd_float32x4_##Name(JSContext *cx, unsigned argc, Value *vp) \ michael@0: { \ michael@0: return Func(cx, argc, vp); \ michael@0: } michael@0: FLOAT32X4_FUNCTION_LIST(DEFINE_SIMD_FLOAT32X4_FUNCTION) michael@0: #undef DEFINE_SIMD_FLOAT32x4_FUNCTION michael@0: michael@0: #define DEFINE_SIMD_INT32X4_FUNCTION(Name, Func, Operands, Flags, MIRId) \ michael@0: bool \ michael@0: js::simd_int32x4_##Name(JSContext *cx, unsigned argc, Value *vp) \ michael@0: { \ michael@0: return Func(cx, argc, vp); \ michael@0: } michael@0: INT32X4_FUNCTION_LIST(DEFINE_SIMD_INT32X4_FUNCTION) michael@0: #undef DEFINE_SIMD_INT32X4_FUNCTION michael@0: michael@0: const JSFunctionSpec js::Float32x4Methods[] = { michael@0: #define SIMD_FLOAT32X4_FUNCTION_ITEM(Name, Func, Operands, Flags, MIRId) \ michael@0: JS_FN(#Name, js::simd_float32x4_##Name, Operands, Flags), michael@0: FLOAT32X4_FUNCTION_LIST(SIMD_FLOAT32X4_FUNCTION_ITEM) michael@0: #undef SIMD_FLOAT32x4_FUNCTION_ITEM michael@0: JS_FS_END michael@0: }; michael@0: michael@0: const JSFunctionSpec js::Int32x4Methods[] = { michael@0: #define SIMD_INT32X4_FUNCTION_ITEM(Name, Func, Operands, Flags, MIRId) \ michael@0: JS_FN(#Name, js::simd_int32x4_##Name, Operands, Flags), michael@0: INT32X4_FUNCTION_LIST(SIMD_INT32X4_FUNCTION_ITEM) michael@0: #undef SIMD_INT32X4_FUNCTION_ITEM michael@0: JS_FS_END michael@0: };