1 // Copyright 2015 the V8 project authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "src/runtime/runtime-utils.h"
6
7 #include "src/arguments.h"
8 #include "src/base/macros.h"
9 #include "src/conversions.h"
10 #include "src/factory.h"
11 #include "src/objects-inl.h"
12
13 // Implement Single Instruction Multiple Data (SIMD) operations as defined in
14 // the SIMD.js draft spec:
15 // http://littledan.github.io/simd.html
16
17 namespace v8 {
18 namespace internal {
19
20 namespace {
21
22 // Functions to convert Numbers to SIMD component types.
23
24 template <typename T, typename F>
CanCast(F from)25 static bool CanCast(F from) {
26 // A float can't represent 2^31 - 1 or 2^32 - 1 exactly, so promote the limits
27 // to double. Otherwise, the limit is truncated and numbers like 2^31 or 2^32
28 // get through, causing any static_cast to be undefined.
29 from = trunc(from);
30 return from >= static_cast<double>(std::numeric_limits<T>::min()) &&
31 from <= static_cast<double>(std::numeric_limits<T>::max());
32 }
33
34
35 // Explicitly specialize for conversions to float, which always succeed.
36 template <>
CanCast(int32_t from)37 bool CanCast<float>(int32_t from) {
38 return true;
39 }
40
41
42 template <>
CanCast(uint32_t from)43 bool CanCast<float>(uint32_t from) {
44 return true;
45 }
46
47
48 template <typename T>
49 static T ConvertNumber(double number);
50
51
52 template <>
ConvertNumber(double number)53 float ConvertNumber<float>(double number) {
54 return DoubleToFloat32(number);
55 }
56
57
58 template <>
ConvertNumber(double number)59 int32_t ConvertNumber<int32_t>(double number) {
60 return DoubleToInt32(number);
61 }
62
63
64 template <>
ConvertNumber(double number)65 uint32_t ConvertNumber<uint32_t>(double number) {
66 return DoubleToUint32(number);
67 }
68
69
70 template <>
ConvertNumber(double number)71 int16_t ConvertNumber<int16_t>(double number) {
72 return static_cast<int16_t>(DoubleToInt32(number));
73 }
74
75
76 template <>
ConvertNumber(double number)77 uint16_t ConvertNumber<uint16_t>(double number) {
78 return static_cast<uint16_t>(DoubleToUint32(number));
79 }
80
81
82 template <>
ConvertNumber(double number)83 int8_t ConvertNumber<int8_t>(double number) {
84 return static_cast<int8_t>(DoubleToInt32(number));
85 }
86
87
88 template <>
ConvertNumber(double number)89 uint8_t ConvertNumber<uint8_t>(double number) {
90 return static_cast<uint8_t>(DoubleToUint32(number));
91 }
92
93
94 // TODO(bbudge): Make this consistent with SIMD instruction results.
RecipApprox(float a)95 inline float RecipApprox(float a) { return 1.0f / a; }
96
97
98 // TODO(bbudge): Make this consistent with SIMD instruction results.
RecipSqrtApprox(float a)99 inline float RecipSqrtApprox(float a) { return 1.0f / std::sqrt(a); }
100
101
102 // Saturating addition for int16_t and int8_t.
103 template <typename T>
AddSaturate(T a,T b)104 inline T AddSaturate(T a, T b) {
105 const T max = std::numeric_limits<T>::max();
106 const T min = std::numeric_limits<T>::min();
107 int32_t result = a + b;
108 if (result > max) return max;
109 if (result < min) return min;
110 return result;
111 }
112
113
114 // Saturating subtraction for int16_t and int8_t.
115 template <typename T>
SubSaturate(T a,T b)116 inline T SubSaturate(T a, T b) {
117 const T max = std::numeric_limits<T>::max();
118 const T min = std::numeric_limits<T>::min();
119 int32_t result = a - b;
120 if (result > max) return max;
121 if (result < min) return min;
122 return result;
123 }
124
125
Min(float a,float b)126 inline float Min(float a, float b) {
127 if (a < b) return a;
128 if (a > b) return b;
129 if (a == b) return std::signbit(a) ? a : b;
130 return std::numeric_limits<float>::quiet_NaN();
131 }
132
133
Max(float a,float b)134 inline float Max(float a, float b) {
135 if (a > b) return a;
136 if (a < b) return b;
137 if (a == b) return std::signbit(b) ? a : b;
138 return std::numeric_limits<float>::quiet_NaN();
139 }
140
141
MinNumber(float a,float b)142 inline float MinNumber(float a, float b) {
143 if (std::isnan(a)) return b;
144 if (std::isnan(b)) return a;
145 return Min(a, b);
146 }
147
148
MaxNumber(float a,float b)149 inline float MaxNumber(float a, float b) {
150 if (std::isnan(a)) return b;
151 if (std::isnan(b)) return a;
152 return Max(a, b);
153 }
154
155 } // namespace
156
157 //-------------------------------------------------------------------
158
159 // SIMD helper functions.
160
RUNTIME_FUNCTION(Runtime_IsSimdValue)161 RUNTIME_FUNCTION(Runtime_IsSimdValue) {
162 HandleScope scope(isolate);
163 DCHECK(args.length() == 1);
164 return isolate->heap()->ToBoolean(args[0]->IsSimd128Value());
165 }
166
167
168 //-------------------------------------------------------------------
169
170 // Utility macros.
171
172 // TODO(gdeepti): Fix to use ToNumber conversion once polyfill is updated.
173 #define CONVERT_SIMD_LANE_ARG_CHECKED(name, index, lanes) \
174 Handle<Object> name_object = args.at<Object>(index); \
175 if (!name_object->IsNumber()) { \
176 THROW_NEW_ERROR_RETURN_FAILURE( \
177 isolate, NewTypeError(MessageTemplate::kInvalidSimdIndex)); \
178 } \
179 double number = name_object->Number(); \
180 if (number < 0 || number >= lanes || !IsInt32Double(number)) { \
181 THROW_NEW_ERROR_RETURN_FAILURE( \
182 isolate, NewRangeError(MessageTemplate::kInvalidSimdIndex)); \
183 } \
184 uint32_t name = static_cast<uint32_t>(number);
185
186 #define CONVERT_SIMD_ARG_HANDLE_THROW(Type, name, index) \
187 Handle<Type> name; \
188 if (args[index]->Is##Type()) { \
189 name = args.at<Type>(index); \
190 } else { \
191 THROW_NEW_ERROR_RETURN_FAILURE( \
192 isolate, NewTypeError(MessageTemplate::kInvalidSimdOperation)); \
193 }
194
195 #define SIMD_UNARY_OP(type, lane_type, lane_count, op, result) \
196 static const int kLaneCount = lane_count; \
197 DCHECK(args.length() == 1); \
198 CONVERT_SIMD_ARG_HANDLE_THROW(type, a, 0); \
199 lane_type lanes[kLaneCount]; \
200 for (int i = 0; i < kLaneCount; i++) { \
201 lanes[i] = op(a->get_lane(i)); \
202 } \
203 Handle<type> result = isolate->factory()->New##type(lanes);
204
205 #define SIMD_BINARY_OP(type, lane_type, lane_count, op, result) \
206 static const int kLaneCount = lane_count; \
207 DCHECK(args.length() == 2); \
208 CONVERT_SIMD_ARG_HANDLE_THROW(type, a, 0); \
209 CONVERT_SIMD_ARG_HANDLE_THROW(type, b, 1); \
210 lane_type lanes[kLaneCount]; \
211 for (int i = 0; i < kLaneCount; i++) { \
212 lanes[i] = op(a->get_lane(i), b->get_lane(i)); \
213 } \
214 Handle<type> result = isolate->factory()->New##type(lanes);
215
216 #define SIMD_RELATIONAL_OP(type, bool_type, lane_count, a, b, op, result) \
217 static const int kLaneCount = lane_count; \
218 DCHECK(args.length() == 2); \
219 CONVERT_SIMD_ARG_HANDLE_THROW(type, a, 0); \
220 CONVERT_SIMD_ARG_HANDLE_THROW(type, b, 1); \
221 bool lanes[kLaneCount]; \
222 for (int i = 0; i < kLaneCount; i++) { \
223 lanes[i] = a->get_lane(i) op b->get_lane(i); \
224 } \
225 Handle<bool_type> result = isolate->factory()->New##bool_type(lanes);
226
227 //-------------------------------------------------------------------
228
229 // Common functions.
230
231 #define GET_NUMERIC_ARG(lane_type, name, index) \
232 Handle<Object> a; \
233 ASSIGN_RETURN_FAILURE_ON_EXCEPTION( \
234 isolate, a, Object::ToNumber(args.at<Object>(index))); \
235 name = ConvertNumber<lane_type>(a->Number());
236
237 #define GET_BOOLEAN_ARG(lane_type, name, index) \
238 name = args[index]->BooleanValue();
239
240 #define SIMD_ALL_TYPES(FUNCTION) \
241 FUNCTION(Float32x4, float, 4, NewNumber, GET_NUMERIC_ARG) \
242 FUNCTION(Int32x4, int32_t, 4, NewNumber, GET_NUMERIC_ARG) \
243 FUNCTION(Uint32x4, uint32_t, 4, NewNumber, GET_NUMERIC_ARG) \
244 FUNCTION(Bool32x4, bool, 4, ToBoolean, GET_BOOLEAN_ARG) \
245 FUNCTION(Int16x8, int16_t, 8, NewNumber, GET_NUMERIC_ARG) \
246 FUNCTION(Uint16x8, uint16_t, 8, NewNumber, GET_NUMERIC_ARG) \
247 FUNCTION(Bool16x8, bool, 8, ToBoolean, GET_BOOLEAN_ARG) \
248 FUNCTION(Int8x16, int8_t, 16, NewNumber, GET_NUMERIC_ARG) \
249 FUNCTION(Uint8x16, uint8_t, 16, NewNumber, GET_NUMERIC_ARG) \
250 FUNCTION(Bool8x16, bool, 16, ToBoolean, GET_BOOLEAN_ARG)
251
252 #define SIMD_CREATE_FUNCTION(type, lane_type, lane_count, extract, replace) \
253 RUNTIME_FUNCTION(Runtime_Create##type) { \
254 static const int kLaneCount = lane_count; \
255 HandleScope scope(isolate); \
256 DCHECK(args.length() == kLaneCount); \
257 lane_type lanes[kLaneCount]; \
258 for (int i = 0; i < kLaneCount; i++) { \
259 replace(lane_type, lanes[i], i) \
260 } \
261 return *isolate->factory()->New##type(lanes); \
262 }
263
264 #define SIMD_EXTRACT_FUNCTION(type, lane_type, lane_count, extract, replace) \
265 RUNTIME_FUNCTION(Runtime_##type##ExtractLane) { \
266 HandleScope scope(isolate); \
267 DCHECK(args.length() == 2); \
268 CONVERT_SIMD_ARG_HANDLE_THROW(type, a, 0); \
269 CONVERT_SIMD_LANE_ARG_CHECKED(lane, 1, lane_count); \
270 return *isolate->factory()->extract(a->get_lane(lane)); \
271 }
272
273 #define SIMD_REPLACE_FUNCTION(type, lane_type, lane_count, extract, replace) \
274 RUNTIME_FUNCTION(Runtime_##type##ReplaceLane) { \
275 static const int kLaneCount = lane_count; \
276 HandleScope scope(isolate); \
277 DCHECK(args.length() == 3); \
278 CONVERT_SIMD_ARG_HANDLE_THROW(type, simd, 0); \
279 CONVERT_SIMD_LANE_ARG_CHECKED(lane, 1, kLaneCount); \
280 lane_type lanes[kLaneCount]; \
281 for (int i = 0; i < kLaneCount; i++) { \
282 lanes[i] = simd->get_lane(i); \
283 } \
284 replace(lane_type, lanes[lane], 2); \
285 Handle<type> result = isolate->factory()->New##type(lanes); \
286 return *result; \
287 }
288
289 #define SIMD_CHECK_FUNCTION(type, lane_type, lane_count, extract, replace) \
290 RUNTIME_FUNCTION(Runtime_##type##Check) { \
291 HandleScope scope(isolate); \
292 CONVERT_SIMD_ARG_HANDLE_THROW(type, a, 0); \
293 return *a; \
294 }
295
296 #define SIMD_SWIZZLE_FUNCTION(type, lane_type, lane_count, extract, replace) \
297 RUNTIME_FUNCTION(Runtime_##type##Swizzle) { \
298 static const int kLaneCount = lane_count; \
299 HandleScope scope(isolate); \
300 DCHECK(args.length() == 1 + kLaneCount); \
301 CONVERT_SIMD_ARG_HANDLE_THROW(type, a, 0); \
302 lane_type lanes[kLaneCount]; \
303 for (int i = 0; i < kLaneCount; i++) { \
304 CONVERT_SIMD_LANE_ARG_CHECKED(index, i + 1, kLaneCount); \
305 lanes[i] = a->get_lane(index); \
306 } \
307 Handle<type> result = isolate->factory()->New##type(lanes); \
308 return *result; \
309 }
310
311 #define SIMD_SHUFFLE_FUNCTION(type, lane_type, lane_count, extract, replace) \
312 RUNTIME_FUNCTION(Runtime_##type##Shuffle) { \
313 static const int kLaneCount = lane_count; \
314 HandleScope scope(isolate); \
315 DCHECK(args.length() == 2 + kLaneCount); \
316 CONVERT_SIMD_ARG_HANDLE_THROW(type, a, 0); \
317 CONVERT_SIMD_ARG_HANDLE_THROW(type, b, 1); \
318 lane_type lanes[kLaneCount]; \
319 for (int i = 0; i < kLaneCount; i++) { \
320 CONVERT_SIMD_LANE_ARG_CHECKED(index, i + 2, kLaneCount * 2); \
321 lanes[i] = index < kLaneCount ? a->get_lane(index) \
322 : b->get_lane(index - kLaneCount); \
323 } \
324 Handle<type> result = isolate->factory()->New##type(lanes); \
325 return *result; \
326 }
327
328 SIMD_ALL_TYPES(SIMD_CREATE_FUNCTION)
329 SIMD_ALL_TYPES(SIMD_EXTRACT_FUNCTION)
330 SIMD_ALL_TYPES(SIMD_REPLACE_FUNCTION)
331 SIMD_ALL_TYPES(SIMD_CHECK_FUNCTION)
332 SIMD_ALL_TYPES(SIMD_SWIZZLE_FUNCTION)
333 SIMD_ALL_TYPES(SIMD_SHUFFLE_FUNCTION)
334
335 //-------------------------------------------------------------------
336
337 // Float-only functions.
338
339 #define SIMD_ABS_FUNCTION(type, lane_type, lane_count) \
340 RUNTIME_FUNCTION(Runtime_##type##Abs) { \
341 HandleScope scope(isolate); \
342 SIMD_UNARY_OP(type, lane_type, lane_count, std::abs, result); \
343 return *result; \
344 }
345
346 #define SIMD_SQRT_FUNCTION(type, lane_type, lane_count) \
347 RUNTIME_FUNCTION(Runtime_##type##Sqrt) { \
348 HandleScope scope(isolate); \
349 SIMD_UNARY_OP(type, lane_type, lane_count, std::sqrt, result); \
350 return *result; \
351 }
352
353 #define SIMD_RECIP_APPROX_FUNCTION(type, lane_type, lane_count) \
354 RUNTIME_FUNCTION(Runtime_##type##RecipApprox) { \
355 HandleScope scope(isolate); \
356 SIMD_UNARY_OP(type, lane_type, lane_count, RecipApprox, result); \
357 return *result; \
358 }
359
360 #define SIMD_RECIP_SQRT_APPROX_FUNCTION(type, lane_type, lane_count) \
361 RUNTIME_FUNCTION(Runtime_##type##RecipSqrtApprox) { \
362 HandleScope scope(isolate); \
363 SIMD_UNARY_OP(type, lane_type, lane_count, RecipSqrtApprox, result); \
364 return *result; \
365 }
366
367 #define BINARY_DIV(a, b) (a) / (b)
368 #define SIMD_DIV_FUNCTION(type, lane_type, lane_count) \
369 RUNTIME_FUNCTION(Runtime_##type##Div) { \
370 HandleScope scope(isolate); \
371 SIMD_BINARY_OP(type, lane_type, lane_count, BINARY_DIV, result); \
372 return *result; \
373 }
374
375 #define SIMD_MINNUM_FUNCTION(type, lane_type, lane_count) \
376 RUNTIME_FUNCTION(Runtime_##type##MinNum) { \
377 HandleScope scope(isolate); \
378 SIMD_BINARY_OP(type, lane_type, lane_count, MinNumber, result); \
379 return *result; \
380 }
381
382 #define SIMD_MAXNUM_FUNCTION(type, lane_type, lane_count) \
383 RUNTIME_FUNCTION(Runtime_##type##MaxNum) { \
384 HandleScope scope(isolate); \
385 SIMD_BINARY_OP(type, lane_type, lane_count, MaxNumber, result); \
386 return *result; \
387 }
388
389 SIMD_ABS_FUNCTION(Float32x4, float, 4)
390 SIMD_SQRT_FUNCTION(Float32x4, float, 4)
391 SIMD_RECIP_APPROX_FUNCTION(Float32x4, float, 4)
392 SIMD_RECIP_SQRT_APPROX_FUNCTION(Float32x4, float, 4)
393 SIMD_DIV_FUNCTION(Float32x4, float, 4)
394 SIMD_MINNUM_FUNCTION(Float32x4, float, 4)
395 SIMD_MAXNUM_FUNCTION(Float32x4, float, 4)
396
397 //-------------------------------------------------------------------
398
399 // Int-only functions.
400
401 #define SIMD_INT_TYPES(FUNCTION) \
402 FUNCTION(Int32x4, int32_t, 32, 4) \
403 FUNCTION(Int16x8, int16_t, 16, 8) \
404 FUNCTION(Int8x16, int8_t, 8, 16)
405
406 #define SIMD_UINT_TYPES(FUNCTION) \
407 FUNCTION(Uint32x4, uint32_t, 32, 4) \
408 FUNCTION(Uint16x8, uint16_t, 16, 8) \
409 FUNCTION(Uint8x16, uint8_t, 8, 16)
410
411 #define CONVERT_SHIFT_ARG_CHECKED(name, index) \
412 Handle<Object> name_object = args.at<Object>(index); \
413 if (!name_object->IsNumber()) { \
414 THROW_NEW_ERROR_RETURN_FAILURE( \
415 isolate, NewTypeError(MessageTemplate::kInvalidSimdOperation)); \
416 } \
417 int32_t signed_shift = 0; \
418 args[index]->ToInt32(&signed_shift); \
419 uint32_t name = bit_cast<uint32_t>(signed_shift);
420
421 #define SIMD_LSL_FUNCTION(type, lane_type, lane_bits, lane_count) \
422 RUNTIME_FUNCTION(Runtime_##type##ShiftLeftByScalar) { \
423 static const int kLaneCount = lane_count; \
424 HandleScope scope(isolate); \
425 DCHECK(args.length() == 2); \
426 CONVERT_SIMD_ARG_HANDLE_THROW(type, a, 0); \
427 CONVERT_SHIFT_ARG_CHECKED(shift, 1); \
428 lane_type lanes[kLaneCount] = {0}; \
429 shift &= lane_bits - 1; \
430 for (int i = 0; i < kLaneCount; i++) { \
431 lanes[i] = a->get_lane(i) << shift; \
432 } \
433 Handle<type> result = isolate->factory()->New##type(lanes); \
434 return *result; \
435 }
436
437 #define SIMD_LSR_FUNCTION(type, lane_type, lane_bits, lane_count) \
438 RUNTIME_FUNCTION(Runtime_##type##ShiftRightByScalar) { \
439 static const int kLaneCount = lane_count; \
440 HandleScope scope(isolate); \
441 DCHECK(args.length() == 2); \
442 CONVERT_SIMD_ARG_HANDLE_THROW(type, a, 0); \
443 CONVERT_SHIFT_ARG_CHECKED(shift, 1); \
444 lane_type lanes[kLaneCount] = {0}; \
445 shift &= lane_bits - 1; \
446 for (int i = 0; i < kLaneCount; i++) { \
447 lanes[i] = static_cast<lane_type>(bit_cast<lane_type>(a->get_lane(i)) >> \
448 shift); \
449 } \
450 Handle<type> result = isolate->factory()->New##type(lanes); \
451 return *result; \
452 }
453
454 #define SIMD_ASR_FUNCTION(type, lane_type, lane_bits, lane_count) \
455 RUNTIME_FUNCTION(Runtime_##type##ShiftRightByScalar) { \
456 static const int kLaneCount = lane_count; \
457 HandleScope scope(isolate); \
458 DCHECK(args.length() == 2); \
459 CONVERT_SIMD_ARG_HANDLE_THROW(type, a, 0); \
460 CONVERT_SHIFT_ARG_CHECKED(shift, 1); \
461 shift &= lane_bits - 1; \
462 lane_type lanes[kLaneCount]; \
463 for (int i = 0; i < kLaneCount; i++) { \
464 int64_t shifted = static_cast<int64_t>(a->get_lane(i)) >> shift; \
465 lanes[i] = static_cast<lane_type>(shifted); \
466 } \
467 Handle<type> result = isolate->factory()->New##type(lanes); \
468 return *result; \
469 }
470
471 SIMD_INT_TYPES(SIMD_LSL_FUNCTION)
472 SIMD_UINT_TYPES(SIMD_LSL_FUNCTION)
473 SIMD_INT_TYPES(SIMD_ASR_FUNCTION)
474 SIMD_UINT_TYPES(SIMD_LSR_FUNCTION)
475
476 //-------------------------------------------------------------------
477
478 // Bool-only functions.
479
480 #define SIMD_BOOL_TYPES(FUNCTION) \
481 FUNCTION(Bool32x4, 4) \
482 FUNCTION(Bool16x8, 8) \
483 FUNCTION(Bool8x16, 16)
484
485 #define SIMD_ANY_FUNCTION(type, lane_count) \
486 RUNTIME_FUNCTION(Runtime_##type##AnyTrue) { \
487 HandleScope scope(isolate); \
488 DCHECK(args.length() == 1); \
489 CONVERT_SIMD_ARG_HANDLE_THROW(type, a, 0); \
490 bool result = false; \
491 for (int i = 0; i < lane_count; i++) { \
492 if (a->get_lane(i)) { \
493 result = true; \
494 break; \
495 } \
496 } \
497 return isolate->heap()->ToBoolean(result); \
498 }
499
500 #define SIMD_ALL_FUNCTION(type, lane_count) \
501 RUNTIME_FUNCTION(Runtime_##type##AllTrue) { \
502 HandleScope scope(isolate); \
503 DCHECK(args.length() == 1); \
504 CONVERT_SIMD_ARG_HANDLE_THROW(type, a, 0); \
505 bool result = true; \
506 for (int i = 0; i < lane_count; i++) { \
507 if (!a->get_lane(i)) { \
508 result = false; \
509 break; \
510 } \
511 } \
512 return isolate->heap()->ToBoolean(result); \
513 }
514
515 SIMD_BOOL_TYPES(SIMD_ANY_FUNCTION)
516 SIMD_BOOL_TYPES(SIMD_ALL_FUNCTION)
517
518 //-------------------------------------------------------------------
519
520 // Small Int-only functions.
521
522 #define SIMD_SMALL_INT_TYPES(FUNCTION) \
523 FUNCTION(Int16x8, int16_t, 8) \
524 FUNCTION(Uint16x8, uint16_t, 8) \
525 FUNCTION(Int8x16, int8_t, 16) \
526 FUNCTION(Uint8x16, uint8_t, 16)
527
528 #define SIMD_ADD_SATURATE_FUNCTION(type, lane_type, lane_count) \
529 RUNTIME_FUNCTION(Runtime_##type##AddSaturate) { \
530 HandleScope scope(isolate); \
531 SIMD_BINARY_OP(type, lane_type, lane_count, AddSaturate, result); \
532 return *result; \
533 }
534
535 #define BINARY_SUB(a, b) (a) - (b)
536 #define SIMD_SUB_SATURATE_FUNCTION(type, lane_type, lane_count) \
537 RUNTIME_FUNCTION(Runtime_##type##SubSaturate) { \
538 HandleScope scope(isolate); \
539 SIMD_BINARY_OP(type, lane_type, lane_count, SubSaturate, result); \
540 return *result; \
541 }
542
543 SIMD_SMALL_INT_TYPES(SIMD_ADD_SATURATE_FUNCTION)
544 SIMD_SMALL_INT_TYPES(SIMD_SUB_SATURATE_FUNCTION)
545
546 //-------------------------------------------------------------------
547
548 // Numeric functions.
549
550 #define SIMD_NUMERIC_TYPES(FUNCTION) \
551 FUNCTION(Float32x4, float, 4) \
552 FUNCTION(Int32x4, int32_t, 4) \
553 FUNCTION(Uint32x4, uint32_t, 4) \
554 FUNCTION(Int16x8, int16_t, 8) \
555 FUNCTION(Uint16x8, uint16_t, 8) \
556 FUNCTION(Int8x16, int8_t, 16) \
557 FUNCTION(Uint8x16, uint8_t, 16)
558
559 #define BINARY_ADD(a, b) (a) + (b)
560 #define SIMD_ADD_FUNCTION(type, lane_type, lane_count) \
561 RUNTIME_FUNCTION(Runtime_##type##Add) { \
562 HandleScope scope(isolate); \
563 SIMD_BINARY_OP(type, lane_type, lane_count, BINARY_ADD, result); \
564 return *result; \
565 }
566
567 #define BINARY_SUB(a, b) (a) - (b)
568 #define SIMD_SUB_FUNCTION(type, lane_type, lane_count) \
569 RUNTIME_FUNCTION(Runtime_##type##Sub) { \
570 HandleScope scope(isolate); \
571 SIMD_BINARY_OP(type, lane_type, lane_count, BINARY_SUB, result); \
572 return *result; \
573 }
574
575 #define BINARY_MUL(a, b) (a) * (b)
576 #define SIMD_MUL_FUNCTION(type, lane_type, lane_count) \
577 RUNTIME_FUNCTION(Runtime_##type##Mul) { \
578 HandleScope scope(isolate); \
579 SIMD_BINARY_OP(type, lane_type, lane_count, BINARY_MUL, result); \
580 return *result; \
581 }
582
583 #define SIMD_MIN_FUNCTION(type, lane_type, lane_count) \
584 RUNTIME_FUNCTION(Runtime_##type##Min) { \
585 HandleScope scope(isolate); \
586 SIMD_BINARY_OP(type, lane_type, lane_count, Min, result); \
587 return *result; \
588 }
589
590 #define SIMD_MAX_FUNCTION(type, lane_type, lane_count) \
591 RUNTIME_FUNCTION(Runtime_##type##Max) { \
592 HandleScope scope(isolate); \
593 SIMD_BINARY_OP(type, lane_type, lane_count, Max, result); \
594 return *result; \
595 }
596
597 SIMD_NUMERIC_TYPES(SIMD_ADD_FUNCTION)
598 SIMD_NUMERIC_TYPES(SIMD_SUB_FUNCTION)
599 SIMD_NUMERIC_TYPES(SIMD_MUL_FUNCTION)
600 SIMD_NUMERIC_TYPES(SIMD_MIN_FUNCTION)
601 SIMD_NUMERIC_TYPES(SIMD_MAX_FUNCTION)
602
603 //-------------------------------------------------------------------
604
605 // Relational functions.
606
607 #define SIMD_RELATIONAL_TYPES(FUNCTION) \
608 FUNCTION(Float32x4, Bool32x4, 4) \
609 FUNCTION(Int32x4, Bool32x4, 4) \
610 FUNCTION(Uint32x4, Bool32x4, 4) \
611 FUNCTION(Int16x8, Bool16x8, 8) \
612 FUNCTION(Uint16x8, Bool16x8, 8) \
613 FUNCTION(Int8x16, Bool8x16, 16) \
614 FUNCTION(Uint8x16, Bool8x16, 16)
615
616 #define SIMD_EQUALITY_TYPES(FUNCTION) \
617 SIMD_RELATIONAL_TYPES(FUNCTION) \
618 FUNCTION(Bool32x4, Bool32x4, 4) \
619 FUNCTION(Bool16x8, Bool16x8, 8) \
620 FUNCTION(Bool8x16, Bool8x16, 16)
621
622 #define SIMD_EQUAL_FUNCTION(type, bool_type, lane_count) \
623 RUNTIME_FUNCTION(Runtime_##type##Equal) { \
624 HandleScope scope(isolate); \
625 SIMD_RELATIONAL_OP(type, bool_type, lane_count, a, b, ==, result); \
626 return *result; \
627 }
628
629 #define SIMD_NOT_EQUAL_FUNCTION(type, bool_type, lane_count) \
630 RUNTIME_FUNCTION(Runtime_##type##NotEqual) { \
631 HandleScope scope(isolate); \
632 SIMD_RELATIONAL_OP(type, bool_type, lane_count, a, b, !=, result); \
633 return *result; \
634 }
635
636 SIMD_EQUALITY_TYPES(SIMD_EQUAL_FUNCTION)
637 SIMD_EQUALITY_TYPES(SIMD_NOT_EQUAL_FUNCTION)
638
639 #define SIMD_LESS_THAN_FUNCTION(type, bool_type, lane_count) \
640 RUNTIME_FUNCTION(Runtime_##type##LessThan) { \
641 HandleScope scope(isolate); \
642 SIMD_RELATIONAL_OP(type, bool_type, lane_count, a, b, <, result); \
643 return *result; \
644 }
645
646 #define SIMD_LESS_THAN_OR_EQUAL_FUNCTION(type, bool_type, lane_count) \
647 RUNTIME_FUNCTION(Runtime_##type##LessThanOrEqual) { \
648 HandleScope scope(isolate); \
649 SIMD_RELATIONAL_OP(type, bool_type, lane_count, a, b, <=, result); \
650 return *result; \
651 }
652
653 #define SIMD_GREATER_THAN_FUNCTION(type, bool_type, lane_count) \
654 RUNTIME_FUNCTION(Runtime_##type##GreaterThan) { \
655 HandleScope scope(isolate); \
656 SIMD_RELATIONAL_OP(type, bool_type, lane_count, a, b, >, result); \
657 return *result; \
658 }
659
660 #define SIMD_GREATER_THAN_OR_EQUAL_FUNCTION(type, bool_type, lane_count) \
661 RUNTIME_FUNCTION(Runtime_##type##GreaterThanOrEqual) { \
662 HandleScope scope(isolate); \
663 SIMD_RELATIONAL_OP(type, bool_type, lane_count, a, b, >=, result); \
664 return *result; \
665 }
666
667 SIMD_RELATIONAL_TYPES(SIMD_LESS_THAN_FUNCTION)
668 SIMD_RELATIONAL_TYPES(SIMD_LESS_THAN_OR_EQUAL_FUNCTION)
669 SIMD_RELATIONAL_TYPES(SIMD_GREATER_THAN_FUNCTION)
670 SIMD_RELATIONAL_TYPES(SIMD_GREATER_THAN_OR_EQUAL_FUNCTION)
671
672 //-------------------------------------------------------------------
673
674 // Logical functions.
675
676 #define SIMD_LOGICAL_TYPES(FUNCTION) \
677 FUNCTION(Int32x4, int32_t, 4, _INT) \
678 FUNCTION(Uint32x4, uint32_t, 4, _INT) \
679 FUNCTION(Int16x8, int16_t, 8, _INT) \
680 FUNCTION(Uint16x8, uint16_t, 8, _INT) \
681 FUNCTION(Int8x16, int8_t, 16, _INT) \
682 FUNCTION(Uint8x16, uint8_t, 16, _INT) \
683 FUNCTION(Bool32x4, bool, 4, _BOOL) \
684 FUNCTION(Bool16x8, bool, 8, _BOOL) \
685 FUNCTION(Bool8x16, bool, 16, _BOOL)
686
687 #define BINARY_AND_INT(a, b) (a) & (b)
688 #define BINARY_AND_BOOL(a, b) (a) && (b)
689 #define SIMD_AND_FUNCTION(type, lane_type, lane_count, op) \
690 RUNTIME_FUNCTION(Runtime_##type##And) { \
691 HandleScope scope(isolate); \
692 SIMD_BINARY_OP(type, lane_type, lane_count, BINARY_AND##op, result); \
693 return *result; \
694 }
695
696 #define BINARY_OR_INT(a, b) (a) | (b)
697 #define BINARY_OR_BOOL(a, b) (a) || (b)
698 #define SIMD_OR_FUNCTION(type, lane_type, lane_count, op) \
699 RUNTIME_FUNCTION(Runtime_##type##Or) { \
700 HandleScope scope(isolate); \
701 SIMD_BINARY_OP(type, lane_type, lane_count, BINARY_OR##op, result); \
702 return *result; \
703 }
704
705 #define BINARY_XOR_INT(a, b) (a) ^ (b)
706 #define BINARY_XOR_BOOL(a, b) (a) != (b)
707 #define SIMD_XOR_FUNCTION(type, lane_type, lane_count, op) \
708 RUNTIME_FUNCTION(Runtime_##type##Xor) { \
709 HandleScope scope(isolate); \
710 SIMD_BINARY_OP(type, lane_type, lane_count, BINARY_XOR##op, result); \
711 return *result; \
712 }
713
714 #define UNARY_NOT_INT ~
715 #define UNARY_NOT_BOOL !
716 #define SIMD_NOT_FUNCTION(type, lane_type, lane_count, op) \
717 RUNTIME_FUNCTION(Runtime_##type##Not) { \
718 HandleScope scope(isolate); \
719 SIMD_UNARY_OP(type, lane_type, lane_count, UNARY_NOT##op, result); \
720 return *result; \
721 }
722
723 SIMD_LOGICAL_TYPES(SIMD_AND_FUNCTION)
724 SIMD_LOGICAL_TYPES(SIMD_OR_FUNCTION)
725 SIMD_LOGICAL_TYPES(SIMD_XOR_FUNCTION)
726 SIMD_LOGICAL_TYPES(SIMD_NOT_FUNCTION)
727
728 //-------------------------------------------------------------------
729
730 // Select functions.
731
732 #define SIMD_SELECT_TYPES(FUNCTION) \
733 FUNCTION(Float32x4, float, Bool32x4, 4) \
734 FUNCTION(Int32x4, int32_t, Bool32x4, 4) \
735 FUNCTION(Uint32x4, uint32_t, Bool32x4, 4) \
736 FUNCTION(Int16x8, int16_t, Bool16x8, 8) \
737 FUNCTION(Uint16x8, uint16_t, Bool16x8, 8) \
738 FUNCTION(Int8x16, int8_t, Bool8x16, 16) \
739 FUNCTION(Uint8x16, uint8_t, Bool8x16, 16)
740
741 #define SIMD_SELECT_FUNCTION(type, lane_type, bool_type, lane_count) \
742 RUNTIME_FUNCTION(Runtime_##type##Select) { \
743 static const int kLaneCount = lane_count; \
744 HandleScope scope(isolate); \
745 DCHECK(args.length() == 3); \
746 CONVERT_SIMD_ARG_HANDLE_THROW(bool_type, mask, 0); \
747 CONVERT_SIMD_ARG_HANDLE_THROW(type, a, 1); \
748 CONVERT_SIMD_ARG_HANDLE_THROW(type, b, 2); \
749 lane_type lanes[kLaneCount]; \
750 for (int i = 0; i < kLaneCount; i++) { \
751 lanes[i] = mask->get_lane(i) ? a->get_lane(i) : b->get_lane(i); \
752 } \
753 Handle<type> result = isolate->factory()->New##type(lanes); \
754 return *result; \
755 }
756
757 SIMD_SELECT_TYPES(SIMD_SELECT_FUNCTION)
758
759 //-------------------------------------------------------------------
760
761 // Signed / unsigned functions.
762
763 #define SIMD_SIGNED_TYPES(FUNCTION) \
764 FUNCTION(Float32x4, float, 4) \
765 FUNCTION(Int32x4, int32_t, 4) \
766 FUNCTION(Int16x8, int16_t, 8) \
767 FUNCTION(Int8x16, int8_t, 16)
768
769 #define SIMD_NEG_FUNCTION(type, lane_type, lane_count) \
770 RUNTIME_FUNCTION(Runtime_##type##Neg) { \
771 HandleScope scope(isolate); \
772 SIMD_UNARY_OP(type, lane_type, lane_count, -, result); \
773 return *result; \
774 }
775
776 SIMD_SIGNED_TYPES(SIMD_NEG_FUNCTION)
777
778 //-------------------------------------------------------------------
779
780 // Casting functions.
781
782 #define SIMD_FROM_TYPES(FUNCTION) \
783 FUNCTION(Float32x4, float, 4, Int32x4, int32_t) \
784 FUNCTION(Float32x4, float, 4, Uint32x4, uint32_t) \
785 FUNCTION(Int32x4, int32_t, 4, Float32x4, float) \
786 FUNCTION(Int32x4, int32_t, 4, Uint32x4, uint32_t) \
787 FUNCTION(Uint32x4, uint32_t, 4, Float32x4, float) \
788 FUNCTION(Uint32x4, uint32_t, 4, Int32x4, int32_t) \
789 FUNCTION(Int16x8, int16_t, 8, Uint16x8, uint16_t) \
790 FUNCTION(Uint16x8, uint16_t, 8, Int16x8, int16_t) \
791 FUNCTION(Int8x16, int8_t, 16, Uint8x16, uint8_t) \
792 FUNCTION(Uint8x16, uint8_t, 16, Int8x16, int8_t)
793
794 #define SIMD_FROM_FUNCTION(type, lane_type, lane_count, from_type, from_ctype) \
795 RUNTIME_FUNCTION(Runtime_##type##From##from_type) { \
796 static const int kLaneCount = lane_count; \
797 HandleScope scope(isolate); \
798 DCHECK(args.length() == 1); \
799 CONVERT_SIMD_ARG_HANDLE_THROW(from_type, a, 0); \
800 lane_type lanes[kLaneCount]; \
801 for (int i = 0; i < kLaneCount; i++) { \
802 from_ctype a_value = a->get_lane(i); \
803 if (a_value != a_value || !CanCast<lane_type>(a_value)) { \
804 THROW_NEW_ERROR_RETURN_FAILURE( \
805 isolate, NewRangeError(MessageTemplate::kInvalidSimdLaneValue)); \
806 } \
807 lanes[i] = static_cast<lane_type>(a_value); \
808 } \
809 Handle<type> result = isolate->factory()->New##type(lanes); \
810 return *result; \
811 }
812
813 SIMD_FROM_TYPES(SIMD_FROM_FUNCTION)
814
815 #define SIMD_FROM_BITS_TYPES(FUNCTION) \
816 FUNCTION(Float32x4, float, 4, Int32x4) \
817 FUNCTION(Float32x4, float, 4, Uint32x4) \
818 FUNCTION(Float32x4, float, 4, Int16x8) \
819 FUNCTION(Float32x4, float, 4, Uint16x8) \
820 FUNCTION(Float32x4, float, 4, Int8x16) \
821 FUNCTION(Float32x4, float, 4, Uint8x16) \
822 FUNCTION(Int32x4, int32_t, 4, Float32x4) \
823 FUNCTION(Int32x4, int32_t, 4, Uint32x4) \
824 FUNCTION(Int32x4, int32_t, 4, Int16x8) \
825 FUNCTION(Int32x4, int32_t, 4, Uint16x8) \
826 FUNCTION(Int32x4, int32_t, 4, Int8x16) \
827 FUNCTION(Int32x4, int32_t, 4, Uint8x16) \
828 FUNCTION(Uint32x4, uint32_t, 4, Float32x4) \
829 FUNCTION(Uint32x4, uint32_t, 4, Int32x4) \
830 FUNCTION(Uint32x4, uint32_t, 4, Int16x8) \
831 FUNCTION(Uint32x4, uint32_t, 4, Uint16x8) \
832 FUNCTION(Uint32x4, uint32_t, 4, Int8x16) \
833 FUNCTION(Uint32x4, uint32_t, 4, Uint8x16) \
834 FUNCTION(Int16x8, int16_t, 8, Float32x4) \
835 FUNCTION(Int16x8, int16_t, 8, Int32x4) \
836 FUNCTION(Int16x8, int16_t, 8, Uint32x4) \
837 FUNCTION(Int16x8, int16_t, 8, Uint16x8) \
838 FUNCTION(Int16x8, int16_t, 8, Int8x16) \
839 FUNCTION(Int16x8, int16_t, 8, Uint8x16) \
840 FUNCTION(Uint16x8, uint16_t, 8, Float32x4) \
841 FUNCTION(Uint16x8, uint16_t, 8, Int32x4) \
842 FUNCTION(Uint16x8, uint16_t, 8, Uint32x4) \
843 FUNCTION(Uint16x8, uint16_t, 8, Int16x8) \
844 FUNCTION(Uint16x8, uint16_t, 8, Int8x16) \
845 FUNCTION(Uint16x8, uint16_t, 8, Uint8x16) \
846 FUNCTION(Int8x16, int8_t, 16, Float32x4) \
847 FUNCTION(Int8x16, int8_t, 16, Int32x4) \
848 FUNCTION(Int8x16, int8_t, 16, Uint32x4) \
849 FUNCTION(Int8x16, int8_t, 16, Int16x8) \
850 FUNCTION(Int8x16, int8_t, 16, Uint16x8) \
851 FUNCTION(Int8x16, int8_t, 16, Uint8x16) \
852 FUNCTION(Uint8x16, uint8_t, 16, Float32x4) \
853 FUNCTION(Uint8x16, uint8_t, 16, Int32x4) \
854 FUNCTION(Uint8x16, uint8_t, 16, Uint32x4) \
855 FUNCTION(Uint8x16, uint8_t, 16, Int16x8) \
856 FUNCTION(Uint8x16, uint8_t, 16, Uint16x8) \
857 FUNCTION(Uint8x16, uint8_t, 16, Int8x16)
858
859 #define SIMD_FROM_BITS_FUNCTION(type, lane_type, lane_count, from_type) \
860 RUNTIME_FUNCTION(Runtime_##type##From##from_type##Bits) { \
861 static const int kLaneCount = lane_count; \
862 HandleScope scope(isolate); \
863 DCHECK(args.length() == 1); \
864 CONVERT_SIMD_ARG_HANDLE_THROW(from_type, a, 0); \
865 lane_type lanes[kLaneCount]; \
866 a->CopyBits(lanes); \
867 Handle<type> result = isolate->factory()->New##type(lanes); \
868 return *result; \
869 }
870
871 SIMD_FROM_BITS_TYPES(SIMD_FROM_BITS_FUNCTION)
872
873
874 //-------------------------------------------------------------------
875
876 // Load and Store functions.
877
878 #define SIMD_LOADN_STOREN_TYPES(FUNCTION) \
879 FUNCTION(Float32x4, float, 4) \
880 FUNCTION(Int32x4, int32_t, 4) \
881 FUNCTION(Uint32x4, uint32_t, 4)
882
883 #define SIMD_COERCE_INDEX(name, i) \
884 Handle<Object> length_object, number_object; \
885 ASSIGN_RETURN_FAILURE_ON_EXCEPTION( \
886 isolate, length_object, Object::ToLength(isolate, args.at<Object>(i))); \
887 ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, number_object, \
888 Object::ToNumber(args.at<Object>(i))); \
889 if (number_object->Number() != length_object->Number()) { \
890 THROW_NEW_ERROR_RETURN_FAILURE( \
891 isolate, NewTypeError(MessageTemplate::kInvalidSimdIndex)); \
892 } \
893 int32_t name = number_object->Number();
894
895 // Common Load and Store Functions
896
897 #define SIMD_LOAD(type, lane_type, lane_count, count, result) \
898 static const int kLaneCount = lane_count; \
899 DCHECK(args.length() == 2); \
900 CONVERT_SIMD_ARG_HANDLE_THROW(JSTypedArray, tarray, 0); \
901 SIMD_COERCE_INDEX(index, 1); \
902 size_t bpe = tarray->element_size(); \
903 uint32_t bytes = count * sizeof(lane_type); \
904 size_t byte_length = NumberToSize(isolate, tarray->byte_length()); \
905 if (index < 0 || index * bpe + bytes > byte_length) { \
906 THROW_NEW_ERROR_RETURN_FAILURE( \
907 isolate, NewRangeError(MessageTemplate::kInvalidSimdIndex)); \
908 } \
909 size_t tarray_offset = NumberToSize(isolate, tarray->byte_offset()); \
910 uint8_t* tarray_base = \
911 static_cast<uint8_t*>(tarray->GetBuffer()->backing_store()) + \
912 tarray_offset; \
913 lane_type lanes[kLaneCount] = {0}; \
914 memcpy(lanes, tarray_base + index * bpe, bytes); \
915 Handle<type> result = isolate->factory()->New##type(lanes);
916
917 #define SIMD_STORE(type, lane_type, lane_count, count, a) \
918 static const int kLaneCount = lane_count; \
919 DCHECK(args.length() == 3); \
920 CONVERT_SIMD_ARG_HANDLE_THROW(JSTypedArray, tarray, 0); \
921 CONVERT_SIMD_ARG_HANDLE_THROW(type, a, 2); \
922 SIMD_COERCE_INDEX(index, 1); \
923 size_t bpe = tarray->element_size(); \
924 uint32_t bytes = count * sizeof(lane_type); \
925 size_t byte_length = NumberToSize(isolate, tarray->byte_length()); \
926 if (index < 0 || byte_length < index * bpe + bytes) { \
927 THROW_NEW_ERROR_RETURN_FAILURE( \
928 isolate, NewRangeError(MessageTemplate::kInvalidSimdIndex)); \
929 } \
930 size_t tarray_offset = NumberToSize(isolate, tarray->byte_offset()); \
931 uint8_t* tarray_base = \
932 static_cast<uint8_t*>(tarray->GetBuffer()->backing_store()) + \
933 tarray_offset; \
934 lane_type lanes[kLaneCount]; \
935 for (int i = 0; i < kLaneCount; i++) { \
936 lanes[i] = a->get_lane(i); \
937 } \
938 memcpy(tarray_base + index * bpe, lanes, bytes);
939
940 #define SIMD_LOAD_FUNCTION(type, lane_type, lane_count) \
941 RUNTIME_FUNCTION(Runtime_##type##Load) { \
942 HandleScope scope(isolate); \
943 SIMD_LOAD(type, lane_type, lane_count, lane_count, result); \
944 return *result; \
945 }
946
947
948 #define SIMD_LOAD1_FUNCTION(type, lane_type, lane_count) \
949 RUNTIME_FUNCTION(Runtime_##type##Load1) { \
950 HandleScope scope(isolate); \
951 SIMD_LOAD(type, lane_type, lane_count, 1, result); \
952 return *result; \
953 }
954
955
956 #define SIMD_LOAD2_FUNCTION(type, lane_type, lane_count) \
957 RUNTIME_FUNCTION(Runtime_##type##Load2) { \
958 HandleScope scope(isolate); \
959 SIMD_LOAD(type, lane_type, lane_count, 2, result); \
960 return *result; \
961 }
962
963
964 #define SIMD_LOAD3_FUNCTION(type, lane_type, lane_count) \
965 RUNTIME_FUNCTION(Runtime_##type##Load3) { \
966 HandleScope scope(isolate); \
967 SIMD_LOAD(type, lane_type, lane_count, 3, result); \
968 return *result; \
969 }
970
971
972 #define SIMD_STORE_FUNCTION(type, lane_type, lane_count) \
973 RUNTIME_FUNCTION(Runtime_##type##Store) { \
974 HandleScope scope(isolate); \
975 SIMD_STORE(type, lane_type, lane_count, lane_count, a); \
976 return *a; \
977 }
978
979
980 #define SIMD_STORE1_FUNCTION(type, lane_type, lane_count) \
981 RUNTIME_FUNCTION(Runtime_##type##Store1) { \
982 HandleScope scope(isolate); \
983 SIMD_STORE(type, lane_type, lane_count, 1, a); \
984 return *a; \
985 }
986
987
988 #define SIMD_STORE2_FUNCTION(type, lane_type, lane_count) \
989 RUNTIME_FUNCTION(Runtime_##type##Store2) { \
990 HandleScope scope(isolate); \
991 SIMD_STORE(type, lane_type, lane_count, 2, a); \
992 return *a; \
993 }
994
995
996 #define SIMD_STORE3_FUNCTION(type, lane_type, lane_count) \
997 RUNTIME_FUNCTION(Runtime_##type##Store3) { \
998 HandleScope scope(isolate); \
999 SIMD_STORE(type, lane_type, lane_count, 3, a); \
1000 return *a; \
1001 }
1002
1003
1004 SIMD_NUMERIC_TYPES(SIMD_LOAD_FUNCTION)
1005 SIMD_LOADN_STOREN_TYPES(SIMD_LOAD1_FUNCTION)
1006 SIMD_LOADN_STOREN_TYPES(SIMD_LOAD2_FUNCTION)
1007 SIMD_LOADN_STOREN_TYPES(SIMD_LOAD3_FUNCTION)
1008 SIMD_NUMERIC_TYPES(SIMD_STORE_FUNCTION)
1009 SIMD_LOADN_STOREN_TYPES(SIMD_STORE1_FUNCTION)
1010 SIMD_LOADN_STOREN_TYPES(SIMD_STORE2_FUNCTION)
1011 SIMD_LOADN_STOREN_TYPES(SIMD_STORE3_FUNCTION)
1012
1013 //-------------------------------------------------------------------
1014
1015 } // namespace internal
1016 } // namespace v8
1017