Introduction
This proposal adds SIMD types and operations to Javascript, focusing on the value semantics. The proposal adds new primitive types Float32x4, etc, together with wrappers and a definition of their behavior in the language. The current proposal does not include all SIMD functions, but it defines almost all types and some core operations on them. More definitions for more functions that operate on them can be found at the SIMD.js reference documentation.
One problem that this spec aims to solve is to define equality for SIMD values. Existing implementations use object identity-based equality. However, maintaining object identity puts a big burden on compilers to maintain this identity through operations, where they would rather be able to duplicate and de-duplicate SIMD values arbitrarily based on algebraic identities. By making SIMD values into primitive types with structural equality, compilers are given more freedom.
Ideally, SIMD values will fit into a larger value types proposal. Such a proposal would be a bit more involved, but good work has already been done in that direction. This document describes SIMD without a larger value type system, but it aims to be consistent with how value types might work, and once someone steps forward to describe value types in more detail, it will be great to simplify this text by just explaining SIMD in terms of value types. On the other hand, this proposal gives a vehicle to work out some of the issues in value types and can be used as a guide for future value type designs.
This document is organized in terms of where changes would be made to the ES6 spec. Although ecmarkup generates numbering at the beginning of headers, these won't correspond to the numbering within the existing ECMA spec, so I've included a matching numbering in parentheses afterwards, referring to the ES6 spec.
In this text, SIMD is used to refer to the various SIMD types: Float32x4, Float64x2, Int32x4, Int16x8, and Int8x16. Similarly to Number, SIMD is used to refer to both the type and the wrapper constructor object. This looks a bit confusing, but it provides the most regularity, as an aim of this specification is to make SIMD types primitives that operate analogously to the existing primitives, rather than a new, exotic sort of thing. To reduce ambiguity, the wrapper constructor is usually referred to as SIMDConstructor, and the type is referred to as SIMDType. SIMD types are associated with a descriptor spec object, called SIMDDescriptor.
Please file any issues here!
Because this document is in spec order, rather than written for direct readability, the logical starting point is actually halfway down.
Related links:
Changelog:
- v0.1: Initial proposal based on SIMD values held in Data Blocks
- v0.2:
- SIMD values are explained as Lists of Numbers, and serialized/deserialized only on loads and stores to TypedArrays, and casts.
- SIMD values point to a type descriptor, not to the wrapper constructor, which makes cross-realm access more straightforward.
- Add all SIMD types, more SIMD operations and all DataView operations.
- v0.3: Add more operations, fix some errors and clarify wording.
TODO:
- Logical and bitwise operations, like lessThan, bool, xor, a large group of functions.
- Logical casts between types, like SIMD.float32x4.fromInt32x4
- Int64x2 type (requires care because not directly constructable or readable)
5SIMD objects
The SIMD global object has several constructor properties, one for each SIMD type.
Each SIMD value is specified as a record with the following internal attributes:
- [[SIMDTypeDescriptor]], which refers to the wrapper constructor for the type.
- [[SIMDElements]], which is a List of Numbers representing the SIMD contents. The [[SIMDElements]] List is modified in internal algorithms when building a SIMD value, but never modified after that. As a rule, the elements list may be modified before putting it in the record, but may not be modified once it is in a record for a SIMD value.
SIMD type descriptors have the following internal slots:
- [[SIMDLength]]: The number of elements present in a SIMD value of the type
- [[SIMDElementSize]]: Size in bytes of each element
- [[SIMDCastNumber]]: An internal algorithm for down-casting a Number to the precision representable in the SIMD type
- [[SIMDSerializeElement]]: An internal algorithm for writing a Number as [[SIMDElementSize]] bytes
- [[SIMDDeserializeElement]]: An internal algorithm for converting [[SIMDElementSize]] bytes into a Number
NoteRather than constructors having internal slots, this could be specified by a table analogous to Table 49 in the ES6 description for TypedArrays. However, the author finds it easier to express this purely in terms of records. TypedArrays could be specified like this too, and share some infrastructure with SIMD, but in their current form, it seems difficult to generalize them to SIMD directly.
NoteSIMDCastNumber converts elements to Numbers; however, for most SIMD types, it will convert them to a restricted range of Number. In a real implementation, the numbers would likely be represented in a more compact form. However, the internal representation is not observable.
5.1Internal algorithms on SIMD types
5.1.1SIMDCreate( descriptor, fields...)
This internal algorithm creates a new value of the type corresponding to the specified wrapper with the following procedure:
NoteThe following algorithm uses splat arguments in the spec and indexes them as an array, but there isn't proper machinery in the spec to describe this behavior
- If Length(fields) == descriptor.[[SIMDLength]], then throw a TypeError.
- Let list be a new List of length descriptor.[[SIMDLength]].
- For i from 0 to descriptor.[[SIMDLength]],
- Let n be descriptor.[[SIMDCastNumber]](fields[i]).
- ReturnIfAbrupt(n).
- Set list[i] to n.
- Return the record { [[SIMDTypeDescriptor]]: descriptor, [[SIMDElements]]: list }.
5.1.3SIMDReplaceLane( value, field, replacement )
- Assert: Type(value) is a SIMDType.
- Let descriptor be a.[[SIMDTypeDescriptor]].
- If Type(field) is not Number, throw a TypeError
- If field != ToInt32(field) or field < 0 or field >= descriptor.[[SIMDLength]], throw a RangeError.
- Let list be a copy of value.[[SIMDElements]].
- Set list[field] to replacement.
- Return SIMDCreate(descriptor, ...list).
NoteWhile this single definition doesn't explicitly refer to the SIMD type in indexing the list, an implementation may use different representations for the Number elements on different types and generate different code for the accesses.
5.1.4SIMDBinaryOp( a, b, op )
- Assert: a.[[SIMDTypeDescriptor]] is b.[[SIMDTypeDescriptor]]
- Let descriptor be a.[[SIMDTypeDescriptor]].
- Let list be a new List of length descriptor.[[SIMDLength]].
- For i from 0 to descriptor.[[SIMDLength]],
- Let ax = SIMDExtractLane(a, i).
- Let bx = SIMDExtractLane(b, i).
- Let res = op(ax, bx).
- ReturnIfAbrupt(res).
- Set list[i] to res.
- Return SIMDCreate(descriptor, ...list).
5.1.5SIMDUnaryOp( a, op )
- Let descriptor be a.[[SIMDTypeDescriptor]].
- Let list be a new List of length descriptor.[[SIMDLength]].
- ReturnIfAbrupt(block).
- For i from 0 to descriptor.[[SIMDLength]],
- Let ax = SIMDExtractLane(a, i).
- Let res = op(ax).
- ReturnIfAbrupt(res).
- Set list[i] to res.
- Return SIMDCreate(descriptor, ...list).
5.1.6SIMDLoad( dataBlock, descriptor, byteOffset [, length] )
- Assert: dataBlock is a Data Block, descriptor is a SIMD type descriptor
- If length is not provided, let length be descriptor.[[SIMDLength]]. Otherwise, assert length <= descriptor.[[SIMDLength]].
- Assert: byteOffset is an integer greater than or equal to zero, and less than or equal to the size of dataBlock - descriptor.[[SIMDElementSize]] * length.
- Let list be a List of length descriptor.[[SIMDLength]], initialized to all 0.
- For i from 0 to length - 1,
- Set list[i] to descriptor.[[SIMDDeserializeElement]](dataBlock, byteOffset).
- Return the record { [[SIMDTypeDescriptor]]: descriptor, [[SIMDElements]]: list }.
5.1.7SIMDStore( dataBlock, descriptor, byteOffset, n [, length] )
- Assert: dataBlock is a Data Block, descriptor is a SIMD type descriptor
- If length is not provided, let length be descriptor.[[SIMDLength]]. Otherwise, assert length <= descriptor.[[SIMDLength]].
- Assert: byteOffset is an integer greater than or equal to zero, and less than or equal to the size of dataBlock - descriptor.[[SIMDElementSize]] * length.
- For i from 0 to length - 1,
- descriptor.[[SIMDSerializeElement]](dataBlock, byteOffset + i * descriptor.[[SIMDElementSize]], n.[[SIMDElements]][i]).
5.1.8SIMDReinterpretCast( value, newDescriptor )
NoteThis is used to define operations like SIMD.Float32x4.fromInt8x16Bits.
- Assert: value.[[SIMDTypeDescriptor]].[[SIMDLength]] * value.[[SIMDTypeDescriptor]].[[SIMDElementSize]] == newDescriptor.[[SIMDLength]] * newDescriptor.[[SIMDElementSize]].
- Let bytes be newDescriptor.[[SIMDLength]] * newDescriptor.[[SIMDElementSize]].
- Let block be the result of CreateByteDataBlock(bytes).
- ReturnIfAbrupt(block).
- SIMDStore(block, value, 0).
- Return SIMDLoad(block, newDescriptor, 0).
5.2SIMDConstructor
Each SIMDConstructor, namely Float32x4, Float64x2, Int32x4, Int16x8, and Int8x16, is associated with a SIMDType and SIMDDescriptor. This section describes the constructors and properties on them. Most properties are identical, existing separately defined on each constructor, with most differences being in the SIMDDescriptor. Certain functions are defined only on a subset of SIMDConstructors, however, and this is noted above their algorithm definition.
The definitions of the constructor and properties of the constructor to follow constitute different identities of functions and objects for each of the copies; in a real implementation, they may call out to completely different pieces of code, even if their implementation in the spec is the same.
Note
As with Boolean, String, etc, SIMDConstructor is a constructor for the wrapper when invoked with new, and returns a primitive when called as a function.
5.2.1SIMDConstructor( value )
This description applies if the constructor is called with exactly one argument.
- If NewTarget is undefined, throw a ReferenceError (NB: TypeError?).
- If value is not of the type SIMDType, throw a TypeError.
- Let O be OrdinaryCreateFromConstructor(NewTarget,
"%_SIMD_Prototype%"
, «[[SIMDWrapperData]]» ).
- ReturnIfAbrupt(O).
- Set the value of O’s [[SIMDWrapperData]] internal slot to value.
- Return O.
5.2.2SIMDConstructor( fields... )
This description applies if the constructor is called with more than one argument.
- If SIMDDescriptor.[[SIMDElementsLength]] does not equal Length(fields), throw a TypeError.
- Return SIMDCreate(SIMDDescriptor, fields...).
5.2.3SIMDConstructor.check(a)
- If a.[[SIMDTypeDescriptor]] is not SIMDDescriptor or b.[[SIMDTypeDescriptor]] is not SIMDDescriptor, throw a TypeError.
- Return a.
5.2.4SIMDConstructor.splat(n)
- Let list be a new List of length SIMDDescriptor.[[SIMDLength]], with all entries filled with n.
- Return SIMDCreate(SIMDDescriptor, ...list).
5.2.5SIMDConstructor.add(a, b)
NoteThis definition uses the normal definition of + on Javascript numbers. Is this appropriate for all SIMD types?
- If a.[[SIMDTypeDescriptor]] is not SIMDDescriptor or b.[[SIMDTypeDescriptor]] is not SIMDDescriptor, throw a TypeError.
- Return SIMDBinaryOp(a, b, +).
5.2.6SIMDConstructor.sub(a, b)
NoteThis definition uses the normal definition of - on Javascript numbers. Is this appropriate for all SIMD types?
- If a.[[SIMDTypeDescriptor]] is not SIMDDescriptor or b.[[SIMDTypeDescriptor]] is not SIMDDescriptor, throw a TypeError.
- Return SIMDBinaryOp(a, b, -).
5.2.7SIMDConstructor.mul(a, b)
NoteThis definition uses the normal definition of * on Javascript numbers. Is this appropriate for all SIMD types?
- If a.[[SIMDTypeDescriptor]] is not SIMDDescriptor or b.[[SIMDTypeDescriptor]] is not SIMDDescriptor, throw a TypeError.
- Return SIMDBinaryOp(a, b, *).
5.2.8SIMDConstructor.div(a, b)
NoteThis definition uses the normal definition of / on Javascript numbers. Is this appropriate for all SIMD types?
- If a.[[SIMDTypeDescriptor]] is not SIMDDescriptor or b.[[SIMDTypeDescriptor]] is not SIMDDescriptor, throw a TypeError.
- Return SIMDBinaryOp(a, b, /).
This property is defined only on SIMD types with floating point values.
NoteIn this specification, that includes only Float32x4 and Float64x2
5.2.9SIMDConstructor.max(a, b)
- If a.[[SIMDTypeDescriptor]] is not SIMDDescriptor or b.[[SIMDTypeDescriptor]] is not SIMDDescriptor, throw a TypeError.
- Return SIMDBinaryOp(a, b,
Math.max
), for the initial value of Math.max
.
This property is defined only on SIMD types with floating point values.
NoteIn this specification, that includes only Float32x4 and Float64x2
5.2.10SIMDConstructor.min(a, b)
- If a.[[SIMDTypeDescriptor]] is not SIMDDescriptor or b.[[SIMDTypeDescriptor]] is not SIMDDescriptor, throw a TypeError.
- Return SIMDBinaryOp(a, b,
Math.min
), for the initial value of Math.min
.
5.2.11MaxNum(n, m)
- Assert Type(n) is Number and Type(m) is Number.
- If n is NaN, return m.
- If m is NaN, return n.
- Return
Math.max
(n, m) for the initial value of Math.max
.
5.2.12SIMDConstructor.maxNum(a, b)
This property is defined only on SIMD types with floating point values.
NoteIn this specification, that includes only Float32x4 and Float64x2
- If a.[[SIMDTypeDescriptor]] is not SIMDDescriptor or b.[[SIMDTypeDescriptor]] is not SIMDDescriptor, throw a TypeError.
- Return SIMDBinaryOp(a, b, MaxNum).
5.2.13MinNum(n, m)
- Assert Type(n) is Number and Type(m) is Number.
- If n is NaN, return m.
- If m is NaN, return n.
- Return
Math.min
(n, m) for the initial value of Math.min
.
5.2.14SIMDConstructor.minNum(a, b)
This property is defined only on SIMD types with floating point values.
NoteIn this specification, that includes only Float32x4 and Float64x2
- If a.[[SIMDTypeDescriptor]] is not SIMDDescriptor or b.[[SIMDTypeDescriptor]] is not SIMDDescriptor, throw a TypeError.
- Return SIMDBinaryOp(a, b, MinNum).
5.2.15SIMDConstructor.neg(a, b)
- If a.[[SIMDTypeDescriptor]] is not SIMDDescriptor, throw a TypeError.
- Return SIMDUnaryOp(a, b, -).
5.2.16SIMDConstructor.sqrt(a)
This property is defined only on SIMD types with floating point values.
NoteIn this specification, that includes only Float32x4 and Float64x2
- If a.[[SIMDTypeDescriptor]] is not SIMDDescriptor, throw a TypeError.
- Return SIMDUnaryOp(a,
Math.sqrt
), for the initial value of Math.sqrt
.
5.2.17ReciprocalApproximation(n)
NoteThe definition here calculates the actual reciprocal. The definition should be reformulated in terms of the boundary case requirements used for function properties of the math object in 20.2.2
- Assert: Type(n) is Number.
- Return ToNumber(1 / n).
5.2.18SIMDConstructor.reciprocalApproximation(a, b)
This property is defined only on SIMD types with floating point values.
NoteIn this specification, that includes only Float32x4 and Float64x2
- If a.[[SIMDTypeDescriptor]] is not SIMDDescriptor, throw a TypeError.
- Return SIMDUnaryOp(a, b, ReciprocalApproximation).
5.2.19ReciprocalApproximation(n)
NoteThe definition here calculates the actual reciprocal. The definition should be reformulated in terms of the boundary case requirements used for function properties of the math object in 20.2.2
- Assert: Type(n) is Number.
- Return ToNumber(1 /
Math.sqrt
(n)), for the initial value of Math.sqrt
.
5.2.20SIMDConstructor.reciprocalSqrtApproximation(a)
This property is defined only on SIMD types with floating point values.
NoteIn this specification, that includes only Float32x4 and Float64x2
- If a.[[SIMDTypeDescriptor]] is not SIMDDescriptor, throw a TypeError.
- Return SIMDUnaryOp(a, ReciprocalSqrtApproximation).sqrt`.
5.2.21SIMDConstructor.abs(a)
- If a.[[SIMDTypeDescriptor]] is not SIMDDescriptor, throw a TypeError.
- Return SIMDUnaryOp(a,
Math.abs
), for the initial value of Math.abs
.
5.2.23SIMDConstructor.replaceLane(simd, field)
- If a.[[SIMDTypeDescriptor]] is not SIMDDescriptor, throw a TypeError.
- Return SIMDReplaceLane(simd, field).
5.2.24SIMDConstructor.store(tarray, index, simd)
NoteThe first seven steps of the following store
/load
functions are very similar. A refactoring could reduce the spec size.
- If simd.[[SIMDTypeDescriptor]] is not SIMDDescriptor, throw a TypeError.
- If tarray does not have a [[ViewedArrayBuffer]] internal slot, throw a TypeError.
- Let block be tarray.[[ViewedArrayBuffer]].[[ArrayBufferData]]
- If index != ToInt32(index), throw a TypeError.
- Let elementLength be tarray.[[ByteLength]] / _tarray.[[ArrayLength]].
- Let byteIndex be index * elementLength.
- If byteIndex + SIMDDescriptor.[[SIMDElementSize]] * SIMDDescriptor.[[SIMDLength]] > tarray.[[ByteLength]] or byteIndex < 0, throw a RangeError.
- SIMDStore(block, SIMDDescriptor, byteIndex, simd).
5.2.25SIMDConstructor.store1(tarray, index, simd)
This function is defined only on SIMD types where SIMDDescriptor.[[SIMDLength]] <= 4.
NoteIn this specification, that set consists of Float32x4, Int32x4, and Float64x2.
- If simd.[[SIMDTypeDescriptor]] is not SIMDDescriptor, throw a TypeError.
- If tarray does not have a [[ViewedArrayBuffer]] internal slot, throw a TypeError.
- Let block be tarray.[[ViewedArrayBuffer]].[[ArrayBufferData]]
- If index != ToInt32(index), throw a TypeError.
- Let elementLength be tarray.[[ByteLength]] / _tarray.[[ArrayLength]].
- Let byteIndex be index * elementLength.
- If byteIndex + SIMDDescriptor.[[SIMDElementSize]] > tarray.[[ByteLength]] or byteIndex < 0, throw a RangeError.
- SIMDStore(block, SIMDDescriptor, byteIndex, simd, 1).
5.2.26SIMDConstructor.store2(tarray, index, simd)
This function is defined only on SIMD types where SIMDDescriptor.[[SIMDLength]] == 4.
NoteIn this specification, that set consists of Float32x4 and Int32x4.
- If simd.[[SIMDTypeDescriptor]] is not SIMDDescriptor, throw a TypeError.
- If tarray does not have a [[ViewedArrayBuffer]] internal slot, throw a TypeError.
- Let block be tarray.[[ViewedArrayBuffer]].[[ArrayBufferData]]
- If index != ToInt32(index), throw a TypeError.
- Let elementLength be tarray.[[ByteLength]] / _tarray.[[ArrayLength]].
- Let byteIndex be index * elementLength.
- If byteIndex + SIMDDescriptor.[[SIMDElementSize]] * 2 > tarray.[[ByteLength]] or byteIndex < 0, throw a RangeError.
- SIMDStore(block, SIMDDescriptor, byteIndex, simd, 2).
5.2.27SIMDConstructor.store3(tarray, index)
This function is defined only on SIMD types where SIMDDescriptor.[[SIMDLength]] == 4.
NoteIn this specification, that set consists of Float32x4 and Int32x4.
- If simd.[[SIMDTypeDescriptor]] is not SIMDDescriptor, throw a TypeError.
- If tarray does not have a [[ViewedArrayBuffer]] internal slot, throw a TypeError.
- Let block be tarray.[[ViewedArrayBuffer]].[[ArrayBufferData]]
- If index != ToInt32(index), throw a TypeError.
- Let elementLength be tarray.[[ByteLength]] / _tarray.[[ArrayLength]].
- Let byteIndex be index * elementLength.
- If byteIndex + SIMDDescriptor.[[SIMDElementSize]] * 3 > tarray.[[ByteLength]] or byteIndex < 0, throw a RangeError.
- Return SIMDStore(block, SIMDDescriptor, byteIndex, 3).
5.2.28SIMDConstructor.load(tarray, index)
- If tarray does not have a [[ViewedArrayBuffer]] internal slot, throw a TypeError.
- Let block be tarray.[[ViewedArrayBuffer]].[[ArrayBufferData]]
- If index != ToInt32(index), throw a TypeError.
- Let elementLength be tarray.[[ByteLength]] / _tarray.[[ArrayLength]].
- Let byteIndex be index * elementLength.
- If byteIndex + SIMDDescriptor.[[SIMDElementSize]] * SIMDDescriptor.[[SIMDLength]] > tarray.[[ByteLength]] or byteIndex < 0, throw a RangeError.
- Return SIMDLoad(block, SIMDDescriptor, byteIndex).
5.2.29SIMDConstructor.load1(tarray, index)
This function is defined only on SIMD types where SIMDDescriptor.[[SIMDLength]] <= 4.
NoteIn this specification, that set consists of Float32x4, Int32x4, and Float64x2.
- If tarray does not have a [[ViewedArrayBuffer]] internal slot, throw a TypeError.
- Let block be tarray.[[ViewedArrayBuffer]].[[ArrayBufferData]]
- If index != ToInt32(index), throw a TypeError.
- Let elementLength be tarray.[[ByteLength]] / _tarray.[[ArrayLength]].
- Let byteIndex be index * elementLength.
- If byteIndex + SIMDDescriptor.[[SIMDElementSize]] > tarray.[[ByteLength]] or byteIndex < 0, throw a RangeError.
- Return SIMDLoad(block, SIMDDescriptor, byteIndex, 1).
5.2.30SIMDConstructor.load2(tarray, index)
This function is defined only on SIMD types where SIMDDescriptor.[[SIMDLength]] == 4.
NoteIn this specification, that set consists of Float32x4 and Int32x4.
- If tarray does not have a [[ViewedArrayBuffer]] internal slot, throw a TypeError.
- Let block be tarray.[[ViewedArrayBuffer]].[[ArrayBufferData]]
- If index != ToInt32(index), throw a TypeError.
- Let elementLength be tarray.[[ByteLength]] / _tarray.[[ArrayLength]].
- Let byteIndex be index * elementLength.
- If byteIndex + SIMDDescriptor.[[SIMDElementSize]] * 2 > tarray.[[ByteLength]] or byteIndex < 0, throw a RangeError.
- Return SIMDLoad(block, SIMDDescriptor, byteIndex, 2).
5.2.31SIMDConstructor.load3(tarray, index)
This function is defined only on SIMD types where SIMDDescriptor.[[SIMDLength]] == 4.
NoteIn this specification, that set consists of Float32x4 and Int32x4.
- If tarray does not have a [[ViewedArrayBuffer]] internal slot, throw a TypeError.
- Let block be tarray.[[ViewedArrayBuffer]].[[ArrayBufferData]]
- If index != ToInt32(index), throw a TypeError.
- Let elementLength be tarray.[[ByteLength]] / _tarray.[[ArrayLength]].
- Let byteIndex be index * elementLength.
- If byteIndex + SIMDDescriptor.[[SIMDElementSize]] * 3 > tarray.[[ByteLength]] or byteIndex < 0, throw a RangeError.
- Return SIMDLoad(block, SIMDDescriptor, byteIndex, 3).
5.2.32SIMDConstructor.fromTIMDBits( value )
In this definition, TIMD ranges over all SIMD types for which SIMDDescriptor.[[SIMDElementSize]] * SIMDDescriptor.[[SIMDLength]] == TIMDDescriptor.[[SIMDElementSize]] * TIMDDescriptor.[[SIMDLength]].
NoteAll of the SIMD types described in this spec are 16 bytes, so all pairs are included.
- If value.[[SIMDTypeDescriptor]] is not TIMDDescriptor, throw a TypeError.
- Return SIMDReinterpretCast(value, SIMDDescriptor).
5.2.33SIMD.swizzle( a, lanes... )
- If a.[[SIMDTypeDescriptor]] is not SIMDDescriptor, throw a TypeError.
- If the length of lanes does not equal SIMDDescriptor.[[SIMDLength]], throw a TypeError.
- For each lane in lanes:
- If lane is not a Number, or lane != ToInt32(lane), throw a TypeError.
- If lane < 0 or lane >= SIMDDescriptor.[[SIMDLength]], throw a RangeError.
- Let list be a new List of length SIMDDescriptor.[[SIMDLength]].
- For i in from 0 to SIMDDescriptor.[[SIMDLength]] - 1,
- Set list[i] to SIMDExtractLane(a, lanes[i])
- Return SIMDCreate(SIMDDescriptor, ...list).
5.2.34SIMD.shuffle( a, b, lanes... )
- If a.[[SIMDTypeDescriptor]] is not SIMDDescriptor, or if b.[[SIMDTypeDescriptor]] is not SIMDDescriptor, throw a TypeError.
- If the length of lanes does not equal SIMDDescriptor.[[SIMDLength]] * 2, throw a TypeError.
- For each lane in lanes:
- If lane is not a Number, or lane != ToInt32(lane), throw a TypeError.
- If lane < 0 or lane >= SIMDDescriptor.[[SIMDLength]] * 2, throw a RangeError.
- Let list be a new List of length SIMDDescriptor.[[SIMDLength]].
- For i in from 0 to SIMDDescriptor.[[SIMDLength]] - 1,
- Let idx be lanes[i].
- If idx >= SIMDDescriptor.[[SIMDLength]],
- Set list[i] to SIMDExtractLane(b, lanes[i] - SIMDDescriptor.[[SIMDLength]])
- Otherwise,
- Set list[i] to SIMDExtractLane(a, lanes[i])
- Return SIMDCreate(SIMDDescriptor, ...list).
5.3The SIMDConstructor.prototype
Note
Previously, if accessors like x were included, these would be spec'd as getters (with null setter) on the SIMDConstructor.prototype . However, they have been removed in favor of SIMDConstructor.extractLane. Are there any non-trivial properties? Users could add methods like .add() here if they want to use SIMD in an object-oriented way. It would also be easy to define properties on the prototype that would make SIMD values and wrapper objects array-like.
5.3.1SIMDConstructor.prototype.constructor
The initial value of SIMDConstructor.prototype.constructor is the intrinsic object %SIMDConstructor%
5.3.2SIMDConstructor.prototype.valueOf()
- If this does not have a [[SIMDWrapperData]] internal slot, throw a TypeError.
- Return this.[[SIMDWrapperData]].
5.3.3SIMDConstructor.prototype.toString()
- If this does not have a [[SIMDWrapperData]] internal slot, throw a TypeError.
- Return ToString(this.[[SIMDWrapperData]]).
NoteThis definition depends on the primitive SIMDType's behavior under ToString. Alternatively, SIMDType could have ToString defined by calling ToObject and then reaching this method (or whatever the user overrides it with), in which case the current definition in ToString would be brought down here.
5.4SIMD type descriptors
In the internal algorithms in this section, preceding the first step, if isLittleEndian is not present, set isLittleEndian to either true or false. The choice is implementation dependent and should be the alternative that is most efficient for the implementation. An implementation must use the same value each time one of the following algorithms is executed, and it must be consistent across all algorithms.
NoteA refactoring might be able to reduce the size of this section somewhat.
5.4.1Float32x4Descriptor type descriptor
The Float32x4Descriptor SIMD type descriptor has the following internal slots:
- [[SIMDLength]]: 4
- [[SIMDElementSize]]: 4
- [[SIMDCastNumber]]: the initial value of Math.fround
- [[SIMDSerializeElement]]: SerializeFloat32
- [[SIMDDeserializeElement]]: DeserializeFloat32
5.4.1.1SerializeFloat32( block, offset, n, isLittleEndian )
NoteDerived from part of SetValueInBuffer. Note that this specification does not require a particular bit pattern for NaN, and that it does not need to be the same across calls, but it cannot be signalling because the serialized value should be safe for other code to read, if a number is placed in an external ArrayBuffer which is read by external code.
- Assert: block is a Data Block.
- Assert: offset is a number.
- Assert: n is a number.
- Assert: offset + 4 is less than or equal to the size of block.
- Set rawBytes to a List containing the 4 bytes that are the result of converting value to IEEE 754-2008 binary32 format using “Round to nearest, ties to even” rounding mode. If isLittleEndian is false, the bytes are arranged in big endian order. Otherwise, the bytes are arranged in little endian order. If value is NaN, rawValue may be set to any implementation chosen non-signaling NaN encoding. An implementation must always choose the same non-signaling NaN encoding for a distinct Not-a-Number value.
- Store the individual bytes of rawBytes into block, in order, starting at block[offset].
5.4.1.2DeserializeFloat32( block, offset, isLittleEndian )
NoteDerived from part of GetValueFromBuffer. Note that while this says to return "the NaN value", the binary representation is not observable and canonicalization is not required.
- Assert: block is a Data Block.
- Assert: offset is a number.
- Assert: offset + 4 is less than or equal to the size of block.
- Let rawValue be a List of 4 containing, in order, the sequence of 4 bytes starting with block[offset].
- If isLittleEndian is false, reverse the order of the elements of rawValue.
- Let value be the byte elements of rawValue concatenated and interpreted as a little-endian bit string encoding of an IEEE 754-2008 binary32 value.
- If value is an IEEE 754-2008 binary32 NaN value, return the NaN Number value.
- Return the Number value that corresponds to value.
5.4.2Float64x2Descriptor type descriptor
The Float64x2Descriptor SIMD type descriptor has the following internal slots:
- [[SIMDLength]]: 2
- [[SIMDElementSize]]: 8
- [[SIMDCastNumber]]: ToNumber
- [[SIMDSerializeElement]]: SerializeFloat64
- [[SIMDDeserializeElement]]: DeserializeFloat64
5.4.2.1SerializeFloat64( block, offset, n, isLittleEndian )
NoteDerived from part of SetValueInBuffer. Note that this specification does not require a particular bit pattern for NaN, and that it does not need to be the same across calls, but it cannot be signalling because the serialized value should be safe for other code to read, if a number is placed in an external ArrayBuffer which is read by external code.
- Assert: block is a Data Block.
- Assert: offset is a number.
- Assert: n is a number.
- Assert: offset + 8 is less than or equal to the size of block.
- Set rawBytes to a List containing the 8 bytes that are the result of converting value to IEEE 754-2008 binary64 format using “Round to nearest, ties to even” rounding mode. If isLittleEndian is false, the bytes are arranged in big endian order. Otherwise, the bytes are arranged in little endian order. If value is NaN, rawValue may be set to any implementation chosen non-signaling NaN encoding. An implementation must always choose the same non-signaling NaN encoding for a distinct Not-a-Number value.
- Store the individual bytes of rawBytes into block, in order, starting at block[offset].
5.4.2.2DeserializeFloat64( block, offset, isLittleEndian )
NoteDerived from part of GetValueFromBuffer. Note that while this says to return "the NaN value", the binary representation is not observable and canonicalization is not required.
- Assert: block is a Data Block.
- Assert: offset is a number.
- Assert: offset + 8 is less than or equal to the size of block.
- Let rawValue be a List of 8 containing, in order, the sequence of 8 bytes starting with block[offset].
- If isLittleEndian is false, reverse the order of the elements of rawValue.
- Let value be the byte elements of rawValue concatenated and interpreted as a little-endian bit string encoding of an IEEE 754-2008 binary64 value.
- If value is an IEEE 754-2008 binary64 NaN value, return the NaN Number value.
- Return the Number value that corresponds to value.
5.4.3Int32x4Descriptor type descriptor
The Int32x4Descriptor SIMD type descriptor has the following internal slots:
- [[SIMDLength]]: 4
- [[SIMDElementSize]]: 4
- [[SIMDCastNumber]]: ToInt32
- [[SIMDSerializeElement]]: SerializeInt32
- [[SIMDDeserializeElement]]: DeserializeInt32
5.4.3.1SerializeInt32( block, offset, n, isLittleEndian )
NoteDerived from part of SetValueInBuffer.
- Assert: block is a Data Block.
- Assert: offset is a number.
- Assert: n is a number.
- Assert: n == ToInt32(n).
- Assert: offset + 4 is less than or equal to the size of block.
- Let rawBytes be a List containing the 4-byte binary 2’s complement encoding of n. If isLittleEndian is false, the bytes are ordered in big endian order. Otherwise, the bytes are ordered in little endian order.
- Store the individual bytes of rawBytes into block, in order, starting at block[offset].
5.4.3.2DeserializeInt32( block, offset, isLittleEndian )
NoteDerived from part of GetValueFromBuffer.
- Assert: block is a Data Block.
- Assert: offset is a number.
- Assert: offset + 4 is less than or equal to the size of block.
- Let rawValue be a List of 4 containing, in order, the sequence of 4 bytes starting with block[offset].
- If isLittleEndian is false, reverse the order of the elements of rawValue.
- Let intValue be the byte elements of rawValue concatenated and interpreted as a bit string encoding of a binary little-endian 2’s complement number of bit length 32.
- Return the Number value that corresponds to intValue.
5.4.4Int16x8Descriptor type descriptor
The Int16x8Descriptor SIMD type descriptor has the following internal slots:
- [[SIMDLength]]: 8
- [[SIMDElementSize]]: 2
- [[SIMDCastNumber]]: ToInt16
- [[SIMDSerializeElement]]: SerializeInt16
- [[SIMDDeserializeElement]]: DeserializeInt16
5.4.4.1SerializeInt16( block, offset, n, isLittleEndian )
NoteDerived from part of SetValueInBuffer.
- Assert: block is a Data Block.
- Assert: offset is a number.
- Assert: n is a number.
- Assert: n == ToInt16(n).
- Assert: offset + 2 is less than or equal to the size of block.
- Let rawBytes be a List containing the 2-byte binary 2’s complement encoding of n. If isLittleEndian is false, the bytes are ordered in big endian order. Otherwise, the bytes are ordered in little endian order.
- Store the individual bytes of rawBytes into block, in order, starting at block[offset].
5.4.4.2DeserializeInt16( block, offset, isLittleEndian )
NoteDerived from part of GetValueFromBuffer.
- Assert: block is a Data Block.
- Assert: offset is a number.
- Assert: offset + 4 is less than or equal to the size of block.
- Let rawValue be a List of 4 containing, in order, the sequence of 2 bytes starting with block[offset].
- If isLittleEndian is false, reverse the order of the elements of rawValue.
- Let intValue be the byte elements of rawValue concatenated and interpreted as a bit string encoding of a binary little-endian 2’s complement number of bit length 16.
- Return the Number value that corresponds to intValue.
5.4.5Int8x16Descriptor type descriptor
The Int8x16Descriptor SIMD type descriptor has the following internal slots:
- [[SIMDLength]]: 16
- [[SIMDElementSize]]: 1
- [[SIMDCastNumber]]: ToInt8
- [[SIMDSerializeElement]]: SerializeInt8
- [[SIMDDeserializeElement]]: DeserializeInt8
5.4.5.1SerializeInt8( block, offset, n, isLittleEndian )
NoteDerived from part of SetValueInBuffer.
- Assert: block is a Data Block.
- Assert: offset is a number.
- Assert: n is a number.
- Assert: n == ToInt8(n).
- Assert: offset + 1 is less than or equal to the size of block.
- Let rawBytes be a List containing the 1-byte binary 2’s complement encoding of n.
- Store the individual bytes of rawBytes into block, in order, starting at block[offset].
5.4.5.2DeserializeInt8( block, offset, isLittleEndian )
NoteDerived from part of GetValueFromBuffer.
- Assert: block is a Data Block.
- Assert: offset is a number.
- Assert: offset + 4 is less than or equal to the size of block.
- Let rawValue be a List of one element containing, the singleton byte at block[offset].
- Let intValue be the byte elements of rawValue concatenated and interpreted as a bit string encoding of a binary little-endian 2’s complement number of bit length 8.
- Return the Number value that corresponds to intValue.