* Copyright (c) 2024-2025 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "ecmascript/compiler/builtins/builtins_dataview_stub_builder.h"
#include "ecmascript/builtins/builtins_arraybuffer.h"
#include "ecmascript/compiler/builtins/builtins_arraybuffer_stub_builder.h"
#include "ecmascript/compiler/builtins/builtins_typedarray_stub_builder.h"
namespace panda::ecmascript::kungfu {
#define DEFINE_DATAVIEW_SET_METHOD(name, type) \
void BuiltinsDataViewStubBuilder::Set##name(GateRef glue, GateRef thisValue, \
GateRef numArgs, Variable* res, Label *exit, Label *slowPath) \
{ \
SetTypedValue<DataViewType::type>(glue, thisValue, numArgs, res, exit, slowPath); \
}
DATAVIEW_SET_TYPES(DEFINE_DATAVIEW_SET_METHOD)
#undef DEFINE_DATAVIEW_SET_METHOD
template <DataViewType type>
void BuiltinsDataViewStubBuilder::SetTypedValue(GateRef glue, GateRef thisValue,
GateRef numArgs, [[maybe_unused]] Variable* res, Label *exit, Label *slowPath)
{
auto env = GetEnvironment();
Label thisIsEcmaObject(env);
Label thisIsDataView(env);
Label indexIsInt(env);
BRANCH(IsEcmaObject(glue, thisValue), &thisIsEcmaObject, slowPath);
Bind(&thisIsEcmaObject);
BRANCH(IsDataView(glue, thisValue), &thisIsDataView, slowPath);
Bind(&thisIsDataView);
GateRef indexTagged = GetCallArg0(numArgs);
GateRef value = GetCallArg1(numArgs);
BRANCH(TaggedIsInt(indexTagged), &indexIsInt, slowPath);
Bind(&indexIsInt);
{
DEFVARIABLE(isLittleEndian, VariableType::JS_ANY(), TaggedFalse());
Label indexIsValid(env);
Label valueIsValid(env);
Label checkOffset(env);
Label getArrayBuffer(env);
Label setValue(env);
Label toBool(env);
GateRef index = GetInt32OfTInt(indexTagged);
BRANCH(Int32LessThan(index, Int32(0)), slowPath, &indexIsValid);
Bind(&indexIsValid);
{
BRANCH(TaggedIsNumber(value), &valueIsValid, slowPath);
Bind(&valueIsValid);
GateRef littleEndianHandle;
if constexpr (type == DataViewType::UINT8 || type == DataViewType::INT8) {
littleEndianHandle = TaggedTrue();
} else {
littleEndianHandle = GetCallArg2(numArgs);
}
BRANCH(TaggedIsUndefined(littleEndianHandle), &getArrayBuffer, &toBool);
Bind(&toBool);
{
isLittleEndian = FastToBoolean(glue, littleEndianHandle, 1);
Jump(&getArrayBuffer);
}
Bind(&getArrayBuffer);
{
GateRef buffer = GetViewedArrayBuffer(glue, thisValue);
BRANCH(IsDetachedBuffer(glue, buffer), slowPath, &checkOffset);
Bind(&checkOffset);
{
GateRef size = ZExtInt32ToInt64(GetByteLength(thisValue));
GateRef elementSize = GetElementSize(type);
GateRef indexInt64 = ZExtInt32ToInt64(index);
BRANCH(Int64UnsignedGreaterThan(Int64Add(indexInt64, elementSize), size), slowPath, &setValue);
Bind(&setValue);
{
Label isNaN(env);
Label next(env);
GateRef offset = ZExtInt32ToInt64(GetByteOffset(thisValue));
GateRef bufferIndex = TruncInt64ToInt32(Int64Add(indexInt64, offset));
BuiltinsArrayBufferStubBuilder builder(this, GetCurrentGlobalEnv());
GateRef pointer = builder.GetDataPointFromBuffer(glue, buffer);
DEFVARIABLE(doubleValue, VariableType::FLOAT64(), TaggedGetNumber(value));
GateRef doubleNanValue = Double(base::NAN_VALUE);
BRANCH(DoubleIsNAN(*doubleValue), &isNaN, &next);
Bind(&isNaN);
{
doubleValue = doubleNanValue;
Jump(&next);
}
Bind(&next);
if constexpr (type == DataViewType::INT32 || type == DataViewType::UINT32) {
SetValueInBufferForInt32(glue, pointer, bufferIndex,
DoubleToInt(glue, *doubleValue), *isLittleEndian);
} else if constexpr (type == DataViewType::FLOAT32) {
GateRef flaotValue = TruncDoubleToFloat32(*doubleValue);
SetValueInBufferForInt32(glue, pointer, bufferIndex,
CastFloat32ToInt32(flaotValue), *isLittleEndian);
} else if constexpr (type == DataViewType::FLOAT64) {
GateRef int64Value = CastDoubleToInt64(*doubleValue);
SetValueInBufferForInt64(glue, pointer, bufferIndex,
int64Value, *isLittleEndian);
}
Jump(exit);
}
}
}
}
}
}
void BuiltinsDataViewStubBuilder::SetValueInBufferForInt32(GateRef glue, GateRef pointer, GateRef offset,
GateRef value, GateRef littleEndianHandle)
{
auto env = GetEnvironment();
Label subentry(env);
env->SubCfgEntry(&subentry);
Label exit(env);
Label littleEnd(env);
Label notLittleEnd(env);
GateRef b0 = Int32And(value, Int32(0xFF));
GateRef b1 = Int32And(Int32LSR(value, Int32(builtins::BITS_EIGHT)), Int32(0xFF));
GateRef b2 = Int32And(Int32LSR(value, Int32(2 * builtins::BITS_EIGHT)), Int32(0xFF));
GateRef b3 = Int32LSR(value, Int32(builtins::BITS_TWENTY_FOUR));
BRANCH(TaggedIsTrue(littleEndianHandle), &littleEnd, ¬LittleEnd);
Bind(&littleEnd);
{
Store(VariableType::INT8(), glue, pointer, offset, TruncInt32ToInt8(b0));
Store(VariableType::INT8(), glue, pointer, Int32Add(offset, Int32(1)), TruncInt32ToInt8(b1));
Store(VariableType::INT8(), glue, pointer, Int32Add(offset, Int32(OffsetIndex::TWO)), TruncInt32ToInt8(b2));
Store(VariableType::INT8(), glue, pointer, Int32Add(offset, Int32(OffsetIndex::THREE)), TruncInt32ToInt8(b3));
Jump(&exit);
}
Bind(¬LittleEnd);
{
Store(VariableType::INT8(), glue, pointer, offset, TruncInt32ToInt8(b3));
Store(VariableType::INT8(), glue, pointer, Int32Add(offset, Int32(1)), TruncInt32ToInt8(b2));
Store(VariableType::INT8(), glue, pointer, Int32Add(offset, Int32(OffsetIndex::TWO)), TruncInt32ToInt8(b1));
Store(VariableType::INT8(), glue, pointer, Int32Add(offset, Int32(OffsetIndex::THREE)), TruncInt32ToInt8(b0));
Jump(&exit);
}
Bind(&exit);
env->SubCfgExit();
}
void BuiltinsDataViewStubBuilder::SetValueInBufferForInt64(GateRef glue, GateRef pointer, GateRef offset,
GateRef value, GateRef littleEndianHandle)
{
auto env = GetEnvironment();
Label subentry(env);
env->SubCfgEntry(&subentry);
Label exit(env);
Label littleEnd(env);
Label notLittleEnd(env);
GateRef lowerInt32 = TruncInt64ToInt32(Int64And(value, Int64(0xFFFFFFFF)));
GateRef highInt32 = TruncInt64ToInt32(Int64LSR(Int64And(value, Int64(0xFFFFFFFF00000000)), Int64(32)));
GateRef b0 = Int32And(lowerInt32, Int32(builtins::BITS_MASK_FF));
GateRef b1 = Int32And(Int32LSR(lowerInt32, Int32(builtins::BITS_EIGHT)), Int32(builtins::BITS_MASK_FF));
GateRef b2 = Int32And(Int32LSR(lowerInt32, Int32(2 * builtins::BITS_EIGHT)), Int32(builtins::BITS_MASK_FF));
GateRef b3 = Int32LSR(lowerInt32, Int32(builtins::BITS_TWENTY_FOUR));
GateRef b4 = Int32And(highInt32, Int32(builtins::BITS_MASK_FF));
GateRef b5 = Int32And(Int32LSR(highInt32, Int32(builtins::BITS_EIGHT)), Int32(builtins::BITS_MASK_FF));
GateRef b6 = Int32And(Int32LSR(highInt32, Int32(2 * builtins::BITS_EIGHT)), Int32(builtins::BITS_MASK_FF));
GateRef b7 = Int32LSR(highInt32, Int32(builtins::BITS_TWENTY_FOUR));
BRANCH(TaggedIsTrue(littleEndianHandle), &littleEnd, ¬LittleEnd);
Bind(&littleEnd);
{
Store(VariableType::INT8(), glue, pointer, offset, TruncInt32ToInt8(b0));
Store(VariableType::INT8(), glue, pointer, Int32Add(offset, Int32(1)), TruncInt32ToInt8(b1));
Store(VariableType::INT8(), glue, pointer, Int32Add(offset, Int32(OffsetIndex::TWO)), TruncInt32ToInt8(b2));
Store(VariableType::INT8(), glue, pointer, Int32Add(offset, Int32(OffsetIndex::THREE)), TruncInt32ToInt8(b3));
Store(VariableType::INT8(), glue, pointer, Int32Add(offset, Int32(OffsetIndex::FOUR)), TruncInt32ToInt8(b4));
Store(VariableType::INT8(), glue, pointer, Int32Add(offset, Int32(OffsetIndex::FIVE)), TruncInt32ToInt8(b5));
Store(VariableType::INT8(), glue, pointer, Int32Add(offset, Int32(OffsetIndex::SIX)), TruncInt32ToInt8(b6));
Store(VariableType::INT8(), glue, pointer, Int32Add(offset, Int32(OffsetIndex::SEVEN)), TruncInt32ToInt8(b7));
Jump(&exit);
}
Bind(¬LittleEnd);
{
Store(VariableType::INT8(), glue, pointer, offset, TruncInt32ToInt8(b7));
Store(VariableType::INT8(), glue, pointer, Int32Add(offset, Int32(1)), TruncInt32ToInt8(b6));
Store(VariableType::INT8(), glue, pointer, Int32Add(offset, Int32(OffsetIndex::TWO)), TruncInt32ToInt8(b5));
Store(VariableType::INT8(), glue, pointer, Int32Add(offset, Int32(OffsetIndex::THREE)), TruncInt32ToInt8(b4));
Store(VariableType::INT8(), glue, pointer, Int32Add(offset, Int32(OffsetIndex::FOUR)), TruncInt32ToInt8(b3));
Store(VariableType::INT8(), glue, pointer, Int32Add(offset, Int32(OffsetIndex::FIVE)), TruncInt32ToInt8(b2));
Store(VariableType::INT8(), glue, pointer, Int32Add(offset, Int32(OffsetIndex::SIX)), TruncInt32ToInt8(b1));
Store(VariableType::INT8(), glue, pointer, Int32Add(offset, Int32(OffsetIndex::SEVEN)), TruncInt32ToInt8(b0));
Jump(&exit);
}
Bind(&exit);
env->SubCfgExit();
}
GateRef BuiltinsDataViewStubBuilder::GetElementSize(DataViewType type)
{
GateRef size;
switch (type) {
case DataViewType::INT8:
case DataViewType::UINT8:
case DataViewType::UINT8_CLAMPED:
size = Int64(1);
break;
case DataViewType::INT16:
case DataViewType::UINT16:
size = Int64(2);
break;
case DataViewType::INT32:
case DataViewType::UINT32:
case DataViewType::FLOAT32:
size = Int64(4);
break;
case DataViewType::FLOAT64:
case DataViewType::BIGINT64:
case DataViewType::BIGUINT64:
size = Int64(8);
break;
default:
LOG_ECMA(FATAL) << "this branch is unreachable";
UNREACHABLE();
}
return size;
}
#if ENABLE_LATEST_OPTIMIZATION
#define DEFINE_DATAVIEW_GET_METHOD(name, type) \
void BuiltinsDataViewStubBuilder::Get##name(GateRef glue, GateRef thisValue, \
GateRef numArgs, Variable* res, Label *exit, Label *slowPath) \
{ \
GetTypedValue<DataViewType::type>(glue, thisValue, numArgs, res, exit, slowPath); \
}
#else
#define DEFINE_DATAVIEW_GET_METHOD(name, type) \
void BuiltinsDataViewStubBuilder::Get##name([[maybe_unused]] GateRef glue, [[maybe_unused]] GateRef thisValue, \
[[maybe_unused]] GateRef numArgs, [[maybe_unused]] Variable* res, [[maybe_unused]] Label *exit, Label *slowPath) \
{ \
Jump(slowPath); \
}
#endif
DATAVIEW_GET_TYPES(DEFINE_DATAVIEW_GET_METHOD)
#undef DEFINE_DATAVIEW_GET_METHOD
template <DataViewType type>
void BuiltinsDataViewStubBuilder::GetTypedValue(GateRef glue, GateRef thisValue,
GateRef numArgs, Variable* res, Label *exit, Label *slowPath)
{
auto env = GetEnvironment();
Label thisIsEcmaObject(env);
Label thisIsDataView(env);
Label indexIsInt(env);
DEFVARIABLE(index, VariableType::INT64(), Int64(0));
BRANCH(IsEcmaObject(glue, thisValue), &thisIsEcmaObject, slowPath);
Bind(&thisIsEcmaObject);
BRANCH(IsDataView(glue, thisValue), &thisIsDataView, slowPath);
Bind(&thisIsDataView);
DEFVARIABLE(indexTagged, VariableType::JS_ANY(), GetCallArg0(numArgs));
BRANCH(TaggedIsInt(*indexTagged), &indexIsInt, slowPath);
Bind(&indexIsInt);
{
Label indexIsValid(env);
Label checkOffset(env);
Label getArrayBuffer(env);
Label getValue(env);
Label toBool(env);
index = GetInt64OfTInt(*indexTagged);
BRANCH(Int64LessThan(*index, Int64(0)), slowPath, &indexIsValid);
Bind(&indexIsValid);
{
DEFVARIABLE(littleEndianHandle, VariableType::JS_ANY(), TaggedFalse());
if constexpr (type == DataViewType::UINT8 || type == DataViewType::INT8) {
littleEndianHandle = TaggedTrue();
} else {
littleEndianHandle = GetCallArg1(numArgs);
}
BRANCH(TaggedIsUndefined(*littleEndianHandle), &getArrayBuffer, &toBool);
Bind(&toBool);
{
littleEndianHandle = FastToBoolean(glue, *littleEndianHandle, 1);
Jump(&getArrayBuffer);
}
Bind(&getArrayBuffer);
{
GateRef buffer = GetViewedArrayBuffer(glue, thisValue);
BRANCH(IsDetachedBuffer(glue, buffer), slowPath, &checkOffset);
Bind(&checkOffset);
{
GateRef size = ZExtInt32ToInt64(GetByteLength(thisValue));
GateRef elementSize = GetElementSize(type);
BRANCH(Int64UnsignedGreaterThan(Int64Add(*index, elementSize), size), slowPath, &getValue);
Bind(&getValue);
{
GateRef offset = ZExtInt32ToInt64(GetByteOffset(thisValue));
GateRef bufferIndex = TruncInt64ToInt32(Int64Add(*index, offset));
BuiltinsArrayBufferStubBuilder arrayBufferBuilder(this, GetCurrentGlobalEnv());
GateRef pointer = arrayBufferBuilder.GetDataPointFromBuffer(glue, buffer);
GateRef result = arrayBufferBuilder.GetValueFromBuffer(
glue, pointer, bufferIndex,
*littleEndianHandle, type);
res->WriteVariable(result);
Jump(exit);
}
}
}
}
}
}
}