|
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- |
|
2 * vim: set ts=8 sts=4 et sw=4 tw=99: |
|
3 * This Source Code Form is subject to the terms of the Mozilla Public |
|
4 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
6 |
|
7 /* |
|
8 * JS SIMD pseudo-module. |
|
9 * Specification matches polyfill: |
|
10 * https://github.com/johnmccutchan/ecmascript_simd/blob/master/src/ecmascript_simd.js |
|
11 * The objects float32x4 and int32x4 are installed on the SIMD pseudo-module. |
|
12 */ |
|
13 |
|
14 #include "builtin/SIMD.h" |
|
15 |
|
16 #include "jsapi.h" |
|
17 #include "jsfriendapi.h" |
|
18 |
|
19 #include "builtin/TypedObject.h" |
|
20 #include "js/Value.h" |
|
21 |
|
22 #include "jsobjinlines.h" |
|
23 |
|
24 using namespace js; |
|
25 |
|
26 using mozilla::ArrayLength; |
|
27 using mozilla::IsFinite; |
|
28 using mozilla::IsNaN; |
|
29 |
|
30 namespace js { |
|
31 extern const JSFunctionSpec Float32x4Methods[]; |
|
32 extern const JSFunctionSpec Int32x4Methods[]; |
|
33 } |
|
34 |
|
35 /////////////////////////////////////////////////////////////////////////// |
|
36 // X4 |
|
37 |
|
38 static const char *laneNames[] = {"lane 0", "lane 1", "lane 2", "lane3"}; |
|
39 |
|
40 template<typename Type32x4, int lane> |
|
41 static bool GetX4Lane(JSContext *cx, unsigned argc, Value *vp) { |
|
42 typedef typename Type32x4::Elem Elem; |
|
43 |
|
44 CallArgs args = CallArgsFromVp(argc, vp); |
|
45 if(!args.thisv().isObject() || !args.thisv().toObject().is<TypedObject>()) { |
|
46 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_INCOMPATIBLE_PROTO, |
|
47 X4TypeDescr::class_.name, laneNames[lane], |
|
48 InformalValueTypeName(args.thisv())); |
|
49 return false; |
|
50 } |
|
51 |
|
52 TypedObject &typedObj = args.thisv().toObject().as<TypedObject>(); |
|
53 TypeDescr &descr = typedObj.typeDescr(); |
|
54 if (descr.kind() != TypeDescr::X4 || descr.as<X4TypeDescr>().type() != Type32x4::type) { |
|
55 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_INCOMPATIBLE_PROTO, |
|
56 X4TypeDescr::class_.name, laneNames[lane], |
|
57 InformalValueTypeName(args.thisv())); |
|
58 return false; |
|
59 } |
|
60 |
|
61 MOZ_ASSERT(!typedObj.owner().isNeutered()); |
|
62 Elem *data = reinterpret_cast<Elem *>(typedObj.typedMem()); |
|
63 Type32x4::setReturn(args, data[lane]); |
|
64 return true; |
|
65 } |
|
66 |
|
67 #define LANE_ACCESSOR(type, lane) \ |
|
68 static bool type##Lane##lane(JSContext *cx, unsigned argc, Value *vp) { \ |
|
69 return GetX4Lane<type, lane>(cx, argc, vp);\ |
|
70 } |
|
71 |
|
72 #define FOUR_LANES_ACCESSOR(type) \ |
|
73 LANE_ACCESSOR(type, 0); \ |
|
74 LANE_ACCESSOR(type, 1); \ |
|
75 LANE_ACCESSOR(type, 2); \ |
|
76 LANE_ACCESSOR(type, 3); |
|
77 |
|
78 FOUR_LANES_ACCESSOR(Int32x4); |
|
79 FOUR_LANES_ACCESSOR(Float32x4); |
|
80 #undef FOUR_LANES_ACCESSOR |
|
81 #undef LANE_ACCESSOR |
|
82 |
|
83 template<typename Type32x4> |
|
84 static bool SignMask(JSContext *cx, unsigned argc, Value *vp) { |
|
85 typedef typename Type32x4::Elem Elem; |
|
86 |
|
87 CallArgs args = CallArgsFromVp(argc, vp); |
|
88 if(!args.thisv().isObject() || !args.thisv().toObject().is<TypedObject>()) { |
|
89 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_INCOMPATIBLE_PROTO, |
|
90 X4TypeDescr::class_.name, "signMask", |
|
91 InformalValueTypeName(args.thisv())); |
|
92 return false; |
|
93 } |
|
94 |
|
95 TypedObject &typedObj = args.thisv().toObject().as<TypedObject>(); |
|
96 TypeDescr &descr = typedObj.typeDescr(); |
|
97 if (descr.kind() != TypeDescr::X4 || descr.as<X4TypeDescr>().type() != Type32x4::type) { |
|
98 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_INCOMPATIBLE_PROTO, |
|
99 X4TypeDescr::class_.name, "signMask", |
|
100 InformalValueTypeName(args.thisv())); |
|
101 return false; |
|
102 } |
|
103 |
|
104 MOZ_ASSERT(!typedObj.owner().isNeutered()); |
|
105 Elem *data = reinterpret_cast<Elem *>(typedObj.typedMem()); |
|
106 int32_t mx = data[0] < 0.0 ? 1 : 0; |
|
107 int32_t my = data[1] < 0.0 ? 1 : 0; |
|
108 int32_t mz = data[2] < 0.0 ? 1 : 0; |
|
109 int32_t mw = data[3] < 0.0 ? 1 : 0; |
|
110 int32_t result = mx | my << 1 | mz << 2 | mw << 3; |
|
111 args.rval().setInt32(result); |
|
112 return true; |
|
113 } |
|
114 |
|
115 #define SIGN_MASK(type) \ |
|
116 static bool type##SignMask(JSContext *cx, unsigned argc, Value *vp) { \ |
|
117 return SignMask<Int32x4>(cx, argc, vp); \ |
|
118 } |
|
119 SIGN_MASK(Float32x4); |
|
120 SIGN_MASK(Int32x4); |
|
121 #undef SIGN_MASK |
|
122 |
|
123 const Class X4TypeDescr::class_ = { |
|
124 "X4", |
|
125 JSCLASS_HAS_RESERVED_SLOTS(JS_DESCR_SLOTS), |
|
126 JS_PropertyStub, /* addProperty */ |
|
127 JS_DeletePropertyStub, /* delProperty */ |
|
128 JS_PropertyStub, /* getProperty */ |
|
129 JS_StrictPropertyStub, /* setProperty */ |
|
130 JS_EnumerateStub, |
|
131 JS_ResolveStub, |
|
132 JS_ConvertStub, |
|
133 nullptr, /* finalize */ |
|
134 call, /* call */ |
|
135 nullptr, /* hasInstance */ |
|
136 nullptr, /* construct */ |
|
137 nullptr |
|
138 }; |
|
139 |
|
140 // These classes just exist to group together various properties and so on. |
|
141 namespace js { |
|
142 class Int32x4Defn { |
|
143 public: |
|
144 static const X4TypeDescr::Type type = X4TypeDescr::TYPE_INT32; |
|
145 static const JSFunctionSpec TypeDescriptorMethods[]; |
|
146 static const JSPropertySpec TypedObjectProperties[]; |
|
147 static const JSFunctionSpec TypedObjectMethods[]; |
|
148 }; |
|
149 class Float32x4Defn { |
|
150 public: |
|
151 static const X4TypeDescr::Type type = X4TypeDescr::TYPE_FLOAT32; |
|
152 static const JSFunctionSpec TypeDescriptorMethods[]; |
|
153 static const JSPropertySpec TypedObjectProperties[]; |
|
154 static const JSFunctionSpec TypedObjectMethods[]; |
|
155 }; |
|
156 } // namespace js |
|
157 |
|
158 const JSFunctionSpec js::Float32x4Defn::TypeDescriptorMethods[] = { |
|
159 JS_SELF_HOSTED_FN("toSource", "DescrToSource", 0, 0), |
|
160 JS_SELF_HOSTED_FN("array", "ArrayShorthand", 1, 0), |
|
161 JS_SELF_HOSTED_FN("equivalent", "TypeDescrEquivalent", 1, 0), |
|
162 JS_FS_END |
|
163 }; |
|
164 |
|
165 const JSPropertySpec js::Float32x4Defn::TypedObjectProperties[] = { |
|
166 JS_PSG("x", Float32x4Lane0, JSPROP_PERMANENT), |
|
167 JS_PSG("y", Float32x4Lane1, JSPROP_PERMANENT), |
|
168 JS_PSG("z", Float32x4Lane2, JSPROP_PERMANENT), |
|
169 JS_PSG("w", Float32x4Lane3, JSPROP_PERMANENT), |
|
170 JS_PSG("signMask", Float32x4SignMask, JSPROP_PERMANENT), |
|
171 JS_PS_END |
|
172 }; |
|
173 |
|
174 const JSFunctionSpec js::Float32x4Defn::TypedObjectMethods[] = { |
|
175 JS_SELF_HOSTED_FN("toSource", "X4ToSource", 0, 0), |
|
176 JS_FS_END |
|
177 }; |
|
178 |
|
179 const JSFunctionSpec js::Int32x4Defn::TypeDescriptorMethods[] = { |
|
180 JS_SELF_HOSTED_FN("toSource", "DescrToSource", 0, 0), |
|
181 JS_SELF_HOSTED_FN("array", "ArrayShorthand", 1, 0), |
|
182 JS_SELF_HOSTED_FN("equivalent", "TypeDescrEquivalent", 1, 0), |
|
183 JS_FS_END, |
|
184 }; |
|
185 |
|
186 const JSPropertySpec js::Int32x4Defn::TypedObjectProperties[] = { |
|
187 JS_PSG("x", Int32x4Lane0, JSPROP_PERMANENT), |
|
188 JS_PSG("y", Int32x4Lane1, JSPROP_PERMANENT), |
|
189 JS_PSG("z", Int32x4Lane2, JSPROP_PERMANENT), |
|
190 JS_PSG("w", Int32x4Lane3, JSPROP_PERMANENT), |
|
191 JS_PSG("signMask", Int32x4SignMask, JSPROP_PERMANENT), |
|
192 JS_PS_END |
|
193 }; |
|
194 |
|
195 const JSFunctionSpec js::Int32x4Defn::TypedObjectMethods[] = { |
|
196 JS_SELF_HOSTED_FN("toSource", "X4ToSource", 0, 0), |
|
197 JS_FS_END |
|
198 }; |
|
199 |
|
200 template<typename T> |
|
201 static JSObject * |
|
202 CreateX4Class(JSContext *cx, |
|
203 Handle<GlobalObject*> global, |
|
204 HandlePropertyName stringRepr) |
|
205 { |
|
206 const X4TypeDescr::Type type = T::type; |
|
207 |
|
208 RootedObject funcProto(cx, global->getOrCreateFunctionPrototype(cx)); |
|
209 if (!funcProto) |
|
210 return nullptr; |
|
211 |
|
212 // Create type constructor itself and initialize its reserved slots. |
|
213 |
|
214 Rooted<X4TypeDescr*> x4(cx); |
|
215 x4 = NewObjectWithProto<X4TypeDescr>(cx, funcProto, global, TenuredObject); |
|
216 if (!x4) |
|
217 return nullptr; |
|
218 |
|
219 x4->initReservedSlot(JS_DESCR_SLOT_KIND, Int32Value(TypeDescr::X4)); |
|
220 x4->initReservedSlot(JS_DESCR_SLOT_STRING_REPR, StringValue(stringRepr)); |
|
221 x4->initReservedSlot(JS_DESCR_SLOT_ALIGNMENT, Int32Value(X4TypeDescr::size(type))); |
|
222 x4->initReservedSlot(JS_DESCR_SLOT_SIZE, Int32Value(X4TypeDescr::alignment(type))); |
|
223 x4->initReservedSlot(JS_DESCR_SLOT_OPAQUE, BooleanValue(false)); |
|
224 x4->initReservedSlot(JS_DESCR_SLOT_TYPE, Int32Value(T::type)); |
|
225 |
|
226 if (!CreateUserSizeAndAlignmentProperties(cx, x4)) |
|
227 return nullptr; |
|
228 |
|
229 // Create prototype property, which inherits from Object.prototype. |
|
230 |
|
231 RootedObject objProto(cx, global->getOrCreateObjectPrototype(cx)); |
|
232 if (!objProto) |
|
233 return nullptr; |
|
234 Rooted<TypedProto*> proto(cx); |
|
235 proto = NewObjectWithProto<TypedProto>(cx, objProto, nullptr, TenuredObject); |
|
236 if (!proto) |
|
237 return nullptr; |
|
238 proto->initTypeDescrSlot(*x4); |
|
239 x4->initReservedSlot(JS_DESCR_SLOT_TYPROTO, ObjectValue(*proto)); |
|
240 |
|
241 // Link constructor to prototype and install properties. |
|
242 |
|
243 if (!JS_DefineFunctions(cx, x4, T::TypeDescriptorMethods)) |
|
244 return nullptr; |
|
245 |
|
246 if (!LinkConstructorAndPrototype(cx, x4, proto) || |
|
247 !DefinePropertiesAndBrand(cx, proto, T::TypedObjectProperties, |
|
248 T::TypedObjectMethods)) |
|
249 { |
|
250 return nullptr; |
|
251 } |
|
252 |
|
253 return x4; |
|
254 } |
|
255 |
|
256 bool |
|
257 X4TypeDescr::call(JSContext *cx, unsigned argc, Value *vp) |
|
258 { |
|
259 CallArgs args = CallArgsFromVp(argc, vp); |
|
260 const uint32_t LANES = 4; |
|
261 |
|
262 if (args.length() < LANES) { |
|
263 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_MORE_ARGS_NEEDED, |
|
264 args.callee().getClass()->name, "3", "s"); |
|
265 return false; |
|
266 } |
|
267 |
|
268 double values[LANES]; |
|
269 for (uint32_t i = 0; i < LANES; i++) { |
|
270 if (!ToNumber(cx, args[i], &values[i])) |
|
271 return false; |
|
272 } |
|
273 |
|
274 Rooted<X4TypeDescr*> descr(cx, &args.callee().as<X4TypeDescr>()); |
|
275 Rooted<TypedObject*> result(cx, TypedObject::createZeroed(cx, descr, 0)); |
|
276 if (!result) |
|
277 return false; |
|
278 |
|
279 MOZ_ASSERT(!result->owner().isNeutered()); |
|
280 switch (descr->type()) { |
|
281 #define STORE_LANES(_constant, _type, _name) \ |
|
282 case _constant: \ |
|
283 { \ |
|
284 _type *mem = reinterpret_cast<_type*>(result->typedMem()); \ |
|
285 for (uint32_t i = 0; i < LANES; i++) { \ |
|
286 mem[i] = ConvertScalar<_type>(values[i]); \ |
|
287 } \ |
|
288 break; \ |
|
289 } |
|
290 JS_FOR_EACH_X4_TYPE_REPR(STORE_LANES) |
|
291 #undef STORE_LANES |
|
292 } |
|
293 args.rval().setObject(*result); |
|
294 return true; |
|
295 } |
|
296 |
|
297 /////////////////////////////////////////////////////////////////////////// |
|
298 // SIMD class |
|
299 |
|
300 const Class SIMDObject::class_ = { |
|
301 "SIMD", |
|
302 JSCLASS_HAS_CACHED_PROTO(JSProto_SIMD), |
|
303 JS_PropertyStub, /* addProperty */ |
|
304 JS_DeletePropertyStub, /* delProperty */ |
|
305 JS_PropertyStub, /* getProperty */ |
|
306 JS_StrictPropertyStub, /* setProperty */ |
|
307 JS_EnumerateStub, |
|
308 JS_ResolveStub, |
|
309 JS_ConvertStub, |
|
310 nullptr, /* finalize */ |
|
311 nullptr, /* call */ |
|
312 nullptr, /* hasInstance */ |
|
313 nullptr, /* construct */ |
|
314 nullptr |
|
315 }; |
|
316 |
|
317 JSObject * |
|
318 SIMDObject::initClass(JSContext *cx, Handle<GlobalObject *> global) |
|
319 { |
|
320 // SIMD relies on having the TypedObject module initialized. |
|
321 // In particular, the self-hosted code for array() wants |
|
322 // to be able to call GetTypedObjectModule(). It is NOT necessary |
|
323 // to install the TypedObjectModule global, but at the moment |
|
324 // those two things are not separable. |
|
325 if (!global->getOrCreateTypedObjectModule(cx)) |
|
326 return nullptr; |
|
327 |
|
328 // Create SIMD Object. |
|
329 RootedObject objProto(cx, global->getOrCreateObjectPrototype(cx)); |
|
330 if(!objProto) |
|
331 return nullptr; |
|
332 RootedObject SIMD(cx, NewObjectWithGivenProto(cx, &SIMDObject::class_, objProto, |
|
333 global, SingletonObject)); |
|
334 if (!SIMD) |
|
335 return nullptr; |
|
336 |
|
337 // float32x4 |
|
338 |
|
339 RootedObject float32x4Object(cx); |
|
340 float32x4Object = CreateX4Class<Float32x4Defn>(cx, global, |
|
341 cx->names().float32x4); |
|
342 if (!float32x4Object) |
|
343 return nullptr; |
|
344 |
|
345 RootedValue float32x4Value(cx, ObjectValue(*float32x4Object)); |
|
346 if (!JS_DefineFunctions(cx, float32x4Object, Float32x4Methods) || |
|
347 !JSObject::defineProperty(cx, SIMD, cx->names().float32x4, |
|
348 float32x4Value, nullptr, nullptr, |
|
349 JSPROP_READONLY | JSPROP_PERMANENT)) |
|
350 { |
|
351 return nullptr; |
|
352 } |
|
353 |
|
354 // int32x4 |
|
355 |
|
356 RootedObject int32x4Object(cx); |
|
357 int32x4Object = CreateX4Class<Int32x4Defn>(cx, global, |
|
358 cx->names().int32x4); |
|
359 if (!int32x4Object) |
|
360 return nullptr; |
|
361 |
|
362 RootedValue int32x4Value(cx, ObjectValue(*int32x4Object)); |
|
363 if (!JS_DefineFunctions(cx, int32x4Object, Int32x4Methods) || |
|
364 !JSObject::defineProperty(cx, SIMD, cx->names().int32x4, |
|
365 int32x4Value, nullptr, nullptr, |
|
366 JSPROP_READONLY | JSPROP_PERMANENT)) |
|
367 { |
|
368 return nullptr; |
|
369 } |
|
370 |
|
371 RootedValue SIMDValue(cx, ObjectValue(*SIMD)); |
|
372 |
|
373 // Everything is set up, install SIMD on the global object. |
|
374 if (!JSObject::defineProperty(cx, global, cx->names().SIMD, SIMDValue, nullptr, nullptr, 0)) |
|
375 return nullptr; |
|
376 |
|
377 global->setConstructor(JSProto_SIMD, SIMDValue); |
|
378 |
|
379 // Define float32x4 functions and install as a property of the SIMD object. |
|
380 global->setFloat32x4TypeDescr(*float32x4Object); |
|
381 |
|
382 // Define int32x4 functions and install as a property of the SIMD object. |
|
383 global->setInt32x4TypeDescr(*int32x4Object); |
|
384 |
|
385 return SIMD; |
|
386 } |
|
387 |
|
388 JSObject * |
|
389 js_InitSIMDClass(JSContext *cx, HandleObject obj) |
|
390 { |
|
391 JS_ASSERT(obj->is<GlobalObject>()); |
|
392 Rooted<GlobalObject *> global(cx, &obj->as<GlobalObject>()); |
|
393 return SIMDObject::initClass(cx, global); |
|
394 } |
|
395 |
|
396 template<typename V> |
|
397 static bool |
|
398 IsVectorObject(HandleValue v) |
|
399 { |
|
400 if (!v.isObject()) |
|
401 return false; |
|
402 |
|
403 JSObject &obj = v.toObject(); |
|
404 if (!obj.is<TypedObject>()) |
|
405 return false; |
|
406 |
|
407 TypeDescr &typeRepr = obj.as<TypedObject>().typeDescr(); |
|
408 if (typeRepr.kind() != TypeDescr::X4) |
|
409 return false; |
|
410 |
|
411 return typeRepr.as<X4TypeDescr>().type() == V::type; |
|
412 } |
|
413 |
|
414 template<typename Elem> |
|
415 static Elem |
|
416 TypedObjectMemory(HandleValue v) |
|
417 { |
|
418 TypedObject &obj = v.toObject().as<TypedObject>(); |
|
419 MOZ_ASSERT(!obj.owner().isNeutered()); |
|
420 return reinterpret_cast<Elem>(obj.typedMem()); |
|
421 } |
|
422 |
|
423 template<typename V> |
|
424 JSObject * |
|
425 js::Create(JSContext *cx, typename V::Elem *data) |
|
426 { |
|
427 typedef typename V::Elem Elem; |
|
428 Rooted<TypeDescr*> typeDescr(cx, &V::GetTypeDescr(*cx->global())); |
|
429 JS_ASSERT(typeDescr); |
|
430 |
|
431 Rooted<TypedObject *> result(cx, TypedObject::createZeroed(cx, typeDescr, 0)); |
|
432 if (!result) |
|
433 return nullptr; |
|
434 |
|
435 MOZ_ASSERT(!result->owner().isNeutered()); |
|
436 Elem *resultMem = reinterpret_cast<Elem *>(result->typedMem()); |
|
437 memcpy(resultMem, data, sizeof(Elem) * V::lanes); |
|
438 return result; |
|
439 } |
|
440 |
|
441 template JSObject *js::Create<Float32x4>(JSContext *cx, Float32x4::Elem *data); |
|
442 template JSObject *js::Create<Int32x4>(JSContext *cx, Int32x4::Elem *data); |
|
443 |
|
444 namespace js { |
|
445 template<typename T, typename V> |
|
446 struct Abs { |
|
447 static inline T apply(T x, T zero) { return V::toType(x < 0 ? -1 * x : x); } |
|
448 }; |
|
449 template<typename T, typename V> |
|
450 struct Neg { |
|
451 static inline T apply(T x, T zero) { return V::toType(-1 * x); } |
|
452 }; |
|
453 template<typename T, typename V> |
|
454 struct Not { |
|
455 static inline T apply(T x, T zero) { return V::toType(~x); } |
|
456 }; |
|
457 template<typename T, typename V> |
|
458 struct Rec { |
|
459 static inline T apply(T x, T zero) { return V::toType(1 / x); } |
|
460 }; |
|
461 template<typename T, typename V> |
|
462 struct RecSqrt { |
|
463 static inline T apply(T x, T zero) { return V::toType(1 / sqrt(x)); } |
|
464 }; |
|
465 template<typename T, typename V> |
|
466 struct Sqrt { |
|
467 static inline T apply(T x, T zero) { return V::toType(sqrt(x)); } |
|
468 }; |
|
469 template<typename T, typename V> |
|
470 struct Add { |
|
471 static inline T apply(T l, T r) { return V::toType(l + r); } |
|
472 }; |
|
473 template<typename T, typename V> |
|
474 struct Sub { |
|
475 static inline T apply(T l, T r) { return V::toType(l - r); } |
|
476 }; |
|
477 template<typename T, typename V> |
|
478 struct Div { |
|
479 static inline T apply(T l, T r) { return V::toType(l / r); } |
|
480 }; |
|
481 template<typename T, typename V> |
|
482 struct Mul { |
|
483 static inline T apply(T l, T r) { return V::toType(l * r); } |
|
484 }; |
|
485 template<typename T, typename V> |
|
486 struct Minimum { |
|
487 static inline T apply(T l, T r) { return V::toType(l < r ? l : r); } |
|
488 }; |
|
489 template<typename T, typename V> |
|
490 struct Maximum { |
|
491 static inline T apply(T l, T r) { return V::toType(l > r ? l : r); } |
|
492 }; |
|
493 template<typename T, typename V> |
|
494 struct LessThan { |
|
495 static inline T apply(T l, T r) { return V::toType(l < r ? 0xFFFFFFFF : 0x0); } |
|
496 }; |
|
497 template<typename T, typename V> |
|
498 struct LessThanOrEqual { |
|
499 static inline T apply(T l, T r) { return V::toType(l <= r ? 0xFFFFFFFF : 0x0); } |
|
500 }; |
|
501 template<typename T, typename V> |
|
502 struct GreaterThan { |
|
503 static inline T apply(T l, T r) { return V::toType(l > r ? 0xFFFFFFFF : 0x0); } |
|
504 }; |
|
505 template<typename T, typename V> |
|
506 struct GreaterThanOrEqual { |
|
507 static inline T apply(T l, T r) { return V::toType(l >= r ? 0xFFFFFFFF : 0x0); } |
|
508 }; |
|
509 template<typename T, typename V> |
|
510 struct Equal { |
|
511 static inline T apply(T l, T r) { return V::toType(l == r ? 0xFFFFFFFF : 0x0); } |
|
512 }; |
|
513 template<typename T, typename V> |
|
514 struct NotEqual { |
|
515 static inline T apply(T l, T r) { return V::toType(l != r ? 0xFFFFFFFF : 0x0); } |
|
516 }; |
|
517 template<typename T, typename V> |
|
518 struct Xor { |
|
519 static inline T apply(T l, T r) { return V::toType(l ^ r); } |
|
520 }; |
|
521 template<typename T, typename V> |
|
522 struct And { |
|
523 static inline T apply(T l, T r) { return V::toType(l & r); } |
|
524 }; |
|
525 template<typename T, typename V> |
|
526 struct Or { |
|
527 static inline T apply(T l, T r) { return V::toType(l | r); } |
|
528 }; |
|
529 template<typename T, typename V> |
|
530 struct Scale { |
|
531 static inline T apply(int32_t lane, T scalar, T x) { return V::toType(scalar * x); } |
|
532 }; |
|
533 template<typename T, typename V> |
|
534 struct WithX { |
|
535 static inline T apply(int32_t lane, T scalar, T x) { return V::toType(lane == 0 ? scalar : x); } |
|
536 }; |
|
537 template<typename T, typename V> |
|
538 struct WithY { |
|
539 static inline T apply(int32_t lane, T scalar, T x) { return V::toType(lane == 1 ? scalar : x); } |
|
540 }; |
|
541 template<typename T, typename V> |
|
542 struct WithZ { |
|
543 static inline T apply(int32_t lane, T scalar, T x) { return V::toType(lane == 2 ? scalar : x); } |
|
544 }; |
|
545 template<typename T, typename V> |
|
546 struct WithW { |
|
547 static inline T apply(int32_t lane, T scalar, T x) { return V::toType(lane == 3 ? scalar : x); } |
|
548 }; |
|
549 template<typename T, typename V> |
|
550 struct WithFlagX { |
|
551 static inline T apply(T l, T f, T x) { return V::toType(l == 0 ? (f ? 0xFFFFFFFF : 0x0) : x); } |
|
552 }; |
|
553 template<typename T, typename V> |
|
554 struct WithFlagY { |
|
555 static inline T apply(T l, T f, T x) { return V::toType(l == 1 ? (f ? 0xFFFFFFFF : 0x0) : x); } |
|
556 }; |
|
557 template<typename T, typename V> |
|
558 struct WithFlagZ { |
|
559 static inline T apply(T l, T f, T x) { return V::toType(l == 2 ? (f ? 0xFFFFFFFF : 0x0) : x); } |
|
560 }; |
|
561 template<typename T, typename V> |
|
562 struct WithFlagW { |
|
563 static inline T apply(T l, T f, T x) { return V::toType(l == 3 ? (f ? 0xFFFFFFFF : 0x0) : x); } |
|
564 }; |
|
565 template<typename T, typename V> |
|
566 struct Shuffle { |
|
567 static inline int32_t apply(int32_t l, int32_t mask) { return V::toType((mask >> l) & 0x3); } |
|
568 }; |
|
569 } |
|
570 |
|
571 template<typename V, typename Op, typename Vret> |
|
572 static bool |
|
573 Func(JSContext *cx, unsigned argc, Value *vp) |
|
574 { |
|
575 typedef typename V::Elem Elem; |
|
576 typedef typename Vret::Elem RetElem; |
|
577 |
|
578 CallArgs args = CallArgsFromVp(argc, vp); |
|
579 if (args.length() != 1 && args.length() != 2) { |
|
580 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_BAD_ARGS); |
|
581 return false; |
|
582 } |
|
583 |
|
584 RetElem result[Vret::lanes]; |
|
585 if (args.length() == 1) { |
|
586 if (!IsVectorObject<V>(args[0])) { |
|
587 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_BAD_ARGS); |
|
588 return false; |
|
589 } |
|
590 |
|
591 Elem *val = TypedObjectMemory<Elem *>(args[0]); |
|
592 for (int32_t i = 0; i < Vret::lanes; i++) |
|
593 result[i] = Op::apply(val[i], 0); |
|
594 } else { |
|
595 JS_ASSERT(args.length() == 2); |
|
596 if(!IsVectorObject<V>(args[0]) || !IsVectorObject<V>(args[1])) |
|
597 { |
|
598 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_BAD_ARGS); |
|
599 return false; |
|
600 } |
|
601 |
|
602 Elem *left = TypedObjectMemory<Elem *>(args[0]); |
|
603 Elem *right = TypedObjectMemory<Elem *>(args[1]); |
|
604 for (int32_t i = 0; i < Vret::lanes; i++) |
|
605 result[i] = Op::apply(left[i], right[i]); |
|
606 } |
|
607 |
|
608 RootedObject obj(cx, Create<Vret>(cx, result)); |
|
609 if (!obj) |
|
610 return false; |
|
611 |
|
612 args.rval().setObject(*obj); |
|
613 return true; |
|
614 } |
|
615 |
|
616 template<typename V, typename OpWith, typename Vret> |
|
617 static bool |
|
618 FuncWith(JSContext *cx, unsigned argc, Value *vp) |
|
619 { |
|
620 typedef typename V::Elem Elem; |
|
621 typedef typename Vret::Elem RetElem; |
|
622 |
|
623 CallArgs args = CallArgsFromVp(argc, vp); |
|
624 if (args.length() != 2 || !IsVectorObject<V>(args[0]) || |
|
625 (!args[1].isNumber() && !args[1].isBoolean())) |
|
626 { |
|
627 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_BAD_ARGS); |
|
628 return false; |
|
629 } |
|
630 |
|
631 Elem *val = TypedObjectMemory<Elem *>(args[0]); |
|
632 RetElem result[Vret::lanes]; |
|
633 |
|
634 if (args[1].isNumber()) { |
|
635 Elem withAsNumber; |
|
636 if (!Vret::toType(cx, args[1], &withAsNumber)) |
|
637 return false; |
|
638 for (int32_t i = 0; i < Vret::lanes; i++) |
|
639 result[i] = OpWith::apply(i, withAsNumber, val[i]); |
|
640 } else if (args[1].isBoolean()) { |
|
641 bool withAsBool = args[1].toBoolean(); |
|
642 for (int32_t i = 0; i < Vret::lanes; i++) |
|
643 result[i] = OpWith::apply(i, withAsBool, val[i]); |
|
644 } |
|
645 |
|
646 RootedObject obj(cx, Create<Vret>(cx, result)); |
|
647 if (!obj) |
|
648 return false; |
|
649 |
|
650 args.rval().setObject(*obj); |
|
651 return true; |
|
652 } |
|
653 |
|
654 template<typename V, typename OpShuffle, typename Vret> |
|
655 static bool |
|
656 FuncShuffle(JSContext *cx, unsigned argc, Value *vp) |
|
657 { |
|
658 typedef typename V::Elem Elem; |
|
659 typedef typename Vret::Elem RetElem; |
|
660 |
|
661 CallArgs args = CallArgsFromVp(argc, vp); |
|
662 if (args.length() != 2 && args.length() != 3) { |
|
663 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_BAD_ARGS); |
|
664 return false; |
|
665 } |
|
666 |
|
667 RetElem result[Vret::lanes]; |
|
668 if (args.length() == 2) { |
|
669 if (!IsVectorObject<V>(args[0]) || !args[1].isNumber()) |
|
670 { |
|
671 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_BAD_ARGS); |
|
672 return false; |
|
673 } |
|
674 |
|
675 Elem *val = TypedObjectMemory<Elem *>(args[0]);; |
|
676 Elem arg1; |
|
677 if (!Vret::toType(cx, args[1], &arg1)) |
|
678 return false; |
|
679 |
|
680 for (int32_t i = 0; i < Vret::lanes; i++) |
|
681 result[i] = val[OpShuffle::apply(i * 2, arg1)]; |
|
682 } else { |
|
683 JS_ASSERT(args.length() == 3); |
|
684 if (!IsVectorObject<V>(args[0]) || !IsVectorObject<V>(args[1]) || !args[2].isNumber()) |
|
685 { |
|
686 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_BAD_ARGS); |
|
687 return false; |
|
688 } |
|
689 |
|
690 Elem *val1 = TypedObjectMemory<Elem *>(args[0]); |
|
691 Elem *val2 = TypedObjectMemory<Elem *>(args[1]); |
|
692 Elem arg2; |
|
693 if (!Vret::toType(cx, args[2], &arg2)) |
|
694 return false; |
|
695 |
|
696 for (int32_t i = 0; i < Vret::lanes; i++) { |
|
697 if (i < Vret::lanes / 2) |
|
698 result[i] = val1[OpShuffle::apply(i * 2, arg2)]; |
|
699 else |
|
700 result[i] = val2[OpShuffle::apply(i * 2, arg2)]; |
|
701 } |
|
702 } |
|
703 |
|
704 RootedObject obj(cx, Create<Vret>(cx, result)); |
|
705 if (!obj) |
|
706 return false; |
|
707 |
|
708 args.rval().setObject(*obj); |
|
709 return true; |
|
710 } |
|
711 |
|
712 template<typename V, typename Vret> |
|
713 static bool |
|
714 FuncConvert(JSContext *cx, unsigned argc, Value *vp) |
|
715 { |
|
716 typedef typename V::Elem Elem; |
|
717 typedef typename Vret::Elem RetElem; |
|
718 |
|
719 CallArgs args = CallArgsFromVp(argc, vp); |
|
720 if (args.length() != 1 || !IsVectorObject<V>(args[0])) |
|
721 { |
|
722 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_BAD_ARGS); |
|
723 return false; |
|
724 } |
|
725 |
|
726 Elem *val = TypedObjectMemory<Elem *>(args[0]); |
|
727 RetElem result[Vret::lanes]; |
|
728 for (int32_t i = 0; i < Vret::lanes; i++) |
|
729 result[i] = RetElem(val[i]); |
|
730 |
|
731 RootedObject obj(cx, Create<Vret>(cx, result)); |
|
732 if (!obj) |
|
733 return false; |
|
734 |
|
735 args.rval().setObject(*obj); |
|
736 return true; |
|
737 } |
|
738 |
|
739 template<typename V, typename Vret> |
|
740 static bool |
|
741 FuncConvertBits(JSContext *cx, unsigned argc, Value *vp) |
|
742 { |
|
743 typedef typename Vret::Elem RetElem; |
|
744 |
|
745 CallArgs args = CallArgsFromVp(argc, vp); |
|
746 if (args.length() != 1 || !IsVectorObject<V>(args[0])) |
|
747 { |
|
748 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_BAD_ARGS); |
|
749 return false; |
|
750 } |
|
751 |
|
752 RetElem *val = TypedObjectMemory<RetElem *>(args[0]); |
|
753 RootedObject obj(cx, Create<Vret>(cx, val)); |
|
754 if (!obj) |
|
755 return false; |
|
756 |
|
757 args.rval().setObject(*obj); |
|
758 return true; |
|
759 } |
|
760 |
|
761 template<typename Vret> |
|
762 static bool |
|
763 FuncZero(JSContext *cx, unsigned argc, Value *vp) |
|
764 { |
|
765 typedef typename Vret::Elem RetElem; |
|
766 |
|
767 CallArgs args = CallArgsFromVp(argc, vp); |
|
768 if (args.length() != 0) { |
|
769 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_BAD_ARGS); |
|
770 return false; |
|
771 } |
|
772 |
|
773 RetElem result[Vret::lanes]; |
|
774 for (int32_t i = 0; i < Vret::lanes; i++) |
|
775 result[i] = RetElem(0); |
|
776 |
|
777 RootedObject obj(cx, Create<Vret>(cx, result)); |
|
778 if (!obj) |
|
779 return false; |
|
780 |
|
781 args.rval().setObject(*obj); |
|
782 return true; |
|
783 } |
|
784 |
|
785 template<typename Vret> |
|
786 static bool |
|
787 FuncSplat(JSContext *cx, unsigned argc, Value *vp) |
|
788 { |
|
789 typedef typename Vret::Elem RetElem; |
|
790 |
|
791 CallArgs args = CallArgsFromVp(argc, vp); |
|
792 if (args.length() != 1 || !args[0].isNumber()) { |
|
793 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_BAD_ARGS); |
|
794 return false; |
|
795 } |
|
796 |
|
797 RetElem arg; |
|
798 if (!Vret::toType(cx, args[0], &arg)) |
|
799 return false; |
|
800 |
|
801 RetElem result[Vret::lanes]; |
|
802 for (int32_t i = 0; i < Vret::lanes; i++) |
|
803 result[i] = arg; |
|
804 |
|
805 RootedObject obj(cx, Create<Vret>(cx, result)); |
|
806 if (!obj) |
|
807 return false; |
|
808 |
|
809 args.rval().setObject(*obj); |
|
810 return true; |
|
811 } |
|
812 |
|
813 static bool |
|
814 Int32x4Bool(JSContext *cx, unsigned argc, Value *vp) |
|
815 { |
|
816 CallArgs args = CallArgsFromVp(argc, vp); |
|
817 if (args.length() != 4 || |
|
818 !args[0].isBoolean() || !args[1].isBoolean() || |
|
819 !args[2].isBoolean() || !args[3].isBoolean()) |
|
820 { |
|
821 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_BAD_ARGS); |
|
822 return false; |
|
823 } |
|
824 |
|
825 int32_t result[Int32x4::lanes]; |
|
826 for (int32_t i = 0; i < Int32x4::lanes; i++) |
|
827 result[i] = args[i].toBoolean() ? 0xFFFFFFFF : 0x0; |
|
828 |
|
829 RootedObject obj(cx, Create<Int32x4>(cx, result)); |
|
830 if (!obj) |
|
831 return false; |
|
832 |
|
833 args.rval().setObject(*obj); |
|
834 return true; |
|
835 } |
|
836 |
|
837 static bool |
|
838 Float32x4Clamp(JSContext *cx, unsigned argc, Value *vp) |
|
839 { |
|
840 CallArgs args = CallArgsFromVp(argc, vp); |
|
841 if (args.length() != 3 || !IsVectorObject<Float32x4>(args[0]) || |
|
842 !IsVectorObject<Float32x4>(args[1]) || !IsVectorObject<Float32x4>(args[2])) |
|
843 { |
|
844 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_BAD_ARGS); |
|
845 return false; |
|
846 } |
|
847 |
|
848 float *val = TypedObjectMemory<float *>(args[0]); |
|
849 float *lowerLimit = TypedObjectMemory<float *>(args[1]); |
|
850 float *upperLimit = TypedObjectMemory<float *>(args[2]); |
|
851 |
|
852 float result[Float32x4::lanes]; |
|
853 for (int32_t i = 0; i < Float32x4::lanes; i++) { |
|
854 result[i] = val[i] < lowerLimit[i] ? lowerLimit[i] : val[i]; |
|
855 result[i] = result[i] > upperLimit[i] ? upperLimit[i] : result[i]; |
|
856 } |
|
857 |
|
858 RootedObject obj(cx, Create<Float32x4>(cx, result)); |
|
859 if (!obj) |
|
860 return false; |
|
861 |
|
862 args.rval().setObject(*obj); |
|
863 return true; |
|
864 } |
|
865 |
|
866 static bool |
|
867 Int32x4Select(JSContext *cx, unsigned argc, Value *vp) |
|
868 { |
|
869 CallArgs args = CallArgsFromVp(argc, vp); |
|
870 if (args.length() != 3 || !IsVectorObject<Int32x4>(args[0]) || |
|
871 !IsVectorObject<Float32x4>(args[1]) || !IsVectorObject<Float32x4>(args[2])) |
|
872 { |
|
873 JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_TYPED_ARRAY_BAD_ARGS); |
|
874 return false; |
|
875 } |
|
876 |
|
877 int32_t *val = TypedObjectMemory<int32_t *>(args[0]); |
|
878 int32_t *tv = TypedObjectMemory<int32_t *>(args[1]); |
|
879 int32_t *fv = TypedObjectMemory<int32_t *>(args[2]); |
|
880 |
|
881 int32_t tr[Int32x4::lanes]; |
|
882 for (int32_t i = 0; i < Int32x4::lanes; i++) |
|
883 tr[i] = And<int32_t, Int32x4>::apply(val[i], tv[i]); |
|
884 |
|
885 int32_t fr[Int32x4::lanes]; |
|
886 for (int32_t i = 0; i < Int32x4::lanes; i++) |
|
887 fr[i] = And<int32_t, Int32x4>::apply(Not<int32_t, Int32x4>::apply(val[i], 0), fv[i]); |
|
888 |
|
889 int32_t orInt[Int32x4::lanes]; |
|
890 for (int32_t i = 0; i < Int32x4::lanes; i++) |
|
891 orInt[i] = Or<int32_t, Int32x4>::apply(tr[i], fr[i]); |
|
892 |
|
893 float *result = reinterpret_cast<float *>(orInt); |
|
894 RootedObject obj(cx, Create<Float32x4>(cx, result)); |
|
895 if (!obj) |
|
896 return false; |
|
897 |
|
898 args.rval().setObject(*obj); |
|
899 return true; |
|
900 } |
|
901 |
|
902 #define DEFINE_SIMD_FLOAT32X4_FUNCTION(Name, Func, Operands, Flags, MIRId) \ |
|
903 bool \ |
|
904 js::simd_float32x4_##Name(JSContext *cx, unsigned argc, Value *vp) \ |
|
905 { \ |
|
906 return Func(cx, argc, vp); \ |
|
907 } |
|
908 FLOAT32X4_FUNCTION_LIST(DEFINE_SIMD_FLOAT32X4_FUNCTION) |
|
909 #undef DEFINE_SIMD_FLOAT32x4_FUNCTION |
|
910 |
|
911 #define DEFINE_SIMD_INT32X4_FUNCTION(Name, Func, Operands, Flags, MIRId) \ |
|
912 bool \ |
|
913 js::simd_int32x4_##Name(JSContext *cx, unsigned argc, Value *vp) \ |
|
914 { \ |
|
915 return Func(cx, argc, vp); \ |
|
916 } |
|
917 INT32X4_FUNCTION_LIST(DEFINE_SIMD_INT32X4_FUNCTION) |
|
918 #undef DEFINE_SIMD_INT32X4_FUNCTION |
|
919 |
|
920 const JSFunctionSpec js::Float32x4Methods[] = { |
|
921 #define SIMD_FLOAT32X4_FUNCTION_ITEM(Name, Func, Operands, Flags, MIRId) \ |
|
922 JS_FN(#Name, js::simd_float32x4_##Name, Operands, Flags), |
|
923 FLOAT32X4_FUNCTION_LIST(SIMD_FLOAT32X4_FUNCTION_ITEM) |
|
924 #undef SIMD_FLOAT32x4_FUNCTION_ITEM |
|
925 JS_FS_END |
|
926 }; |
|
927 |
|
928 const JSFunctionSpec js::Int32x4Methods[] = { |
|
929 #define SIMD_INT32X4_FUNCTION_ITEM(Name, Func, Operands, Flags, MIRId) \ |
|
930 JS_FN(#Name, js::simd_int32x4_##Name, Operands, Flags), |
|
931 INT32X4_FUNCTION_LIST(SIMD_INT32X4_FUNCTION_ITEM) |
|
932 #undef SIMD_INT32X4_FUNCTION_ITEM |
|
933 JS_FS_END |
|
934 }; |