blob: 3e5f502c3d3b7200f5a793a4a9b78eb517d8d376 [file]
//-------------------------------------------------------------------------------------------------------
// Copyright (C) Microsoft Corporation and contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
//-------------------------------------------------------------------------------------------------------
#include "RuntimeLanguagePch.h"
namespace Js
{
bool SIMDUtils::IsSimdType(const Var aVar) //Needs SIMD Type Id's to be contiguous.
{
Assert(aVar);
TypeId tid = JavascriptOperators::GetTypeId(aVar);
return (TypeIds_SIMDFloat32x4 <= tid && tid <= TypeIds_SIMDBool8x16) ? true : false;
}
uint32 SIMDUtils::GetSIMDLaneCount(const Var aVar)
{
Assert(IsSimdType(aVar));
TypeId tid = JavascriptOperators::GetTypeId(aVar);
switch(tid)
{
case TypeIds_SIMDFloat32x4:
case TypeIds_SIMDInt32x4:
case TypeIds_SIMDUint32x4:
case TypeIds_SIMDBool32x4:
return 4;
case TypeIds_SIMDInt16x8:
case TypeIds_SIMDUint16x8:
case TypeIds_SIMDBool16x8:
return 8;
case TypeIds_SIMDInt8x16:
case TypeIds_SIMDUint8x16:
case TypeIds_SIMDBool8x16:
return 16;
default:
Assert(UNREACHED);
}
return 0;
}
uint32 SIMDUtils::SIMDCheckTypedArrayIndex(ScriptContext* scriptContext, const Var index)
{
uint32 uint32Value;
Assert(index != NULL);
uint32Value = SIMDCheckUint32Number<true>(scriptContext, index);
return uint32Value;
}
uint32 SIMDUtils::SIMDCheckLaneIndex(ScriptContext* scriptContext, const Var lane, const uint32 range)
{
Assert(lane != NULL);
uint32 uint32Value = SIMDCheckUint32Number(scriptContext, lane);
if (uint32Value >= range)
{
JavascriptError::ThrowRangeError(scriptContext, JSERR_SimdLaneRangeError);
}
return uint32Value;
}
// Is Number with uint32 value.
template<bool acceptNegZero>
uint32 SIMDUtils::SIMDCheckUint32Number(ScriptContext* scriptContext, const Var value)
{
int32 int32Value;
if (JavascriptNumber::Is(value))
{
if (!JavascriptNumber::TryGetInt32Value<acceptNegZero>(JavascriptNumber::GetValue(value), &int32Value))
{
JavascriptError::ThrowRangeError(scriptContext, JSERR_ArgumentOutOfRange);
}
}
else if (TaggedInt::Is(value))
{
int32Value = TaggedInt::ToInt32(value);
}
else
{
if (!JavascriptNumber::TryGetInt32Value<acceptNegZero>(JavascriptConversion::ToNumber(value, scriptContext), &int32Value))
{
JavascriptError::ThrowRangeError(scriptContext, JSERR_ArgumentOutOfRange);
}
}
return int32Value;
}
#if _M_IX86 || _M_AMD64
SIMDValue SIMDUtils::FromSimdBits(const SIMDValue value)
{
X86SIMDValue x86Result;
X86SIMDValue v = X86SIMDValue::ToX86SIMDValue(value);
_mm_store_ps(x86Result.f32, v.m128_value);
return X86SIMDValue::ToSIMDValue(x86Result);
}
#else
SIMDValue SIMDUtils::FromSimdBits(const SIMDValue value)
{
SIMDValue result;
result.i32[SIMD_X] = value.i32[SIMD_X];
result.i32[SIMD_Y] = value.i32[SIMD_Y];
result.i32[SIMD_Z] = value.i32[SIMD_Z];
result.i32[SIMD_W] = value.i32[SIMD_W];
return result;
}
#endif
SIMDValue SIMDUtils::SIMD128InnerShuffle(const SIMDValue src1, const SIMDValue src2, uint32 laneCount, const uint32* lanes)
{
SIMDValue result = { 0 };
Assert(laneCount == 16 || laneCount == 8 || laneCount == 4);
Assert(lanes != nullptr);
switch (laneCount)
{
case 4:
for (uint i = 0; i < laneCount; ++i)
{
result.i32[i] = lanes[i] < laneCount ? src1.i32[lanes[i]] : src2.i32[lanes[i] - laneCount];
}
break;
case 8:
for (uint i = 0; i < laneCount; ++i)
{
result.i16[i] = lanes[i] < laneCount ? src1.i16[lanes[i]] : src2.i16[lanes[i] - laneCount];
}
break;
case 16:
for (uint i = 0; i < laneCount; ++i)
{
result.i8[i] = lanes[i] < laneCount ? src1.i8[lanes[i]] : src2.i8[lanes[i] - laneCount];
}
break;
default:
Assert(UNREACHED);
}
return result;
}
template <class SIMDType>
Var SIMDUtils::SIMD128SlowShuffle(Var src1, Var src2, Var* lanes, const uint32 laneCount, const uint32 range, ScriptContext* scriptContext)
{
SIMDType *a = SIMDType::FromVar(src1);
SIMDType *b = SIMDType::FromVar(src2);
Assert(a);
Assert(b);
SIMDValue src1Value = a->GetValue();
SIMDValue src2Value = b->GetValue();
SIMDValue result;
uint32 laneValue[16] = { 0 };
Assert(laneCount == 16 || laneCount == 8 || laneCount == 4);
for (uint i = 0; i < laneCount; ++i)
{
laneValue[i] = SIMDUtils::SIMDCheckLaneIndex(scriptContext, lanes[i], range);
}
result = SIMD128InnerShuffle(src1Value, src2Value, laneCount, laneValue);
return SIMDType::New(&result, scriptContext);
}
template Var SIMDUtils::SIMD128SlowShuffle<JavascriptSIMDInt32x4 >(Var src1, Var src2, Var *lanes, const uint32 laneCount, const uint32 range, ScriptContext* scriptContext);
template Var SIMDUtils::SIMD128SlowShuffle<JavascriptSIMDFloat32x4>(Var src1, Var src2, Var *lanes, const uint32 laneCount, const uint32 range, ScriptContext* scriptContext);
template Var SIMDUtils::SIMD128SlowShuffle<JavascriptSIMDFloat64x2>(Var src1, Var src2, Var *lanes, const uint32 laneCount, const uint32 range, ScriptContext* scriptContext);
template Var SIMDUtils::SIMD128SlowShuffle<JavascriptSIMDUint32x4 >(Var src1, Var src2, Var *lanes, const uint32 laneCount, const uint32 range, ScriptContext* scriptContext);
template Var SIMDUtils::SIMD128SlowShuffle<JavascriptSIMDInt8x16 >(Var src1, Var src2, Var *lanes, const uint32 laneCount, const uint32 range, ScriptContext* scriptContext);
template Var SIMDUtils::SIMD128SlowShuffle<JavascriptSIMDInt16x8 >(Var src1, Var src2, Var *lanes, const uint32 laneCount, const uint32 range, ScriptContext* scriptContext);
template Var SIMDUtils::SIMD128SlowShuffle<JavascriptSIMDUint8x16 >(Var src1, Var src2, Var *lanes, const uint32 laneCount, const uint32 range, ScriptContext* scriptContext);
template Var SIMDUtils::SIMD128SlowShuffle<JavascriptSIMDUint16x8 >(Var src1, Var src2, Var *lanes, const uint32 laneCount, const uint32 range, ScriptContext* scriptContext);
bool SIMDUtils::SIMDIsSupportedTypedArray(Var value)
{
return JavascriptOperators::GetTypeId(value) >= TypeIds_Int8Array && JavascriptOperators::GetTypeId(value) <= TypeIds_Float64Array;
}
/*
Checks if:
1. Array is supported typed array
2. Lane index is a Number/TaggedInt and int32 value
3. Lane index is within array bounds
*/
SIMDValue* SIMDUtils::SIMDCheckTypedArrayAccess(Var arg1, Var arg2, TypedArrayBase **tarray, int32 *index, uint32 dataWidth, ScriptContext *scriptContext)
{
if (!SIMDIsSupportedTypedArray(arg1))
{
JavascriptError::ThrowTypeError(scriptContext, JSERR_SimdInvalidArgType, _u("Simd typed array access"));
}
*index = SIMDCheckUint32Number<true>(scriptContext, arg2);
// bound check
*tarray = TypedArrayBase::FromVar(arg1);
uint32 bpe = (*tarray)->GetBytesPerElement();
int32 offset = (*index) * bpe;
if (offset < 0 || ((int64)offset + dataWidth) >(*tarray)->GetByteLength())
{
JavascriptError::ThrowRangeError(scriptContext, JSERR_ArgumentOutOfRange, _u("Simd typed array access"));
}
return (SIMDValue*)((*tarray)->GetByteBuffer() + offset);
}
SIMDValue SIMDUtils::SIMDLdData(const SIMDValue *data, uint8 dataWidth)
{
SIMDValue result = { 0, 0, 0, 0 };
// bitwise copy. Always use integer fields to avoid wrong copy of NaNs.
switch (dataWidth)
{
case 16:
result.i32[SIMD_W] = data->i32[SIMD_W];
// fall through
case 12:
result.i32[SIMD_Z] = data->i32[SIMD_Z];
// fall through
case 8:
result.i32[SIMD_Y] = data->i32[SIMD_Y];
// fall through
case 4:
result.i32[SIMD_X] = data->i32[SIMD_X];
break;
default:
Assert(UNREACHED);
}
return result;
}
void SIMDUtils::SIMDStData(SIMDValue *data, const SIMDValue simdValue, uint8 dataWidth)
{
// bitwise copy. Always use integer fields to avoid wrong copy of NaNs.
switch (dataWidth)
{
case 16:
data->i32[SIMD_W] = simdValue.i32[SIMD_W];
// fall through
case 12:
data->i32[SIMD_Z] = simdValue.i32[SIMD_Z];
// fall through
case 8:
data->i32[SIMD_Y] = simdValue.i32[SIMD_Y];
// fall through
case 4:
data->i32[SIMD_X] = simdValue.i32[SIMD_X];
break;
default:
Assert(UNREACHED);
}
}
#if ENABLE_NATIVE_CODEGEN
// Maps Simd opcodes which are non-contiguous to a zero-based linear space. Used to index a table using a Simd opcode.
uint32 SIMDUtils::SimdOpcodeAsIndex(Js::OpCode op)
{
if (op <= Js::OpCode::Simd128_End)
{
return (uint32)((Js::OpCode)op - Js::OpCode::Simd128_Start);
}
else
{
Assert(op >= Js::OpCode::Simd128_Start_Extend && op <= Js::OpCode::Simd128_End_Extend);
return (uint32)((Js::OpCode)op - Js::OpCode::Simd128_Start_Extend) + (uint32)(Js::OpCode::Simd128_End - Js::OpCode::Simd128_Start) + 1;
}
}
#endif
}