1/*
2 * Copyright (c) 2024 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 *     http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16#include "ecmascript/compiler/builtins/builtins_dataview_stub_builder.h"
17
18#include "ecmascript/builtins/builtins_arraybuffer.h"
19#include "ecmascript/compiler/builtins/builtins_typedarray_stub_builder.h"
20#include "ecmascript/compiler/new_object_stub_builder.h"
21#include "ecmascript/compiler/stub_builder-inl.h"
22
23namespace panda::ecmascript::kungfu {
24template <DataViewType type>
25void BuiltinsDataViewStubBuilder::SetTypedValue(GateRef glue, GateRef thisValue,
26    GateRef numArgs, [[maybe_unused]] Variable* res, Label *exit, Label *slowPath)
27{
28    auto env = GetEnvironment();
29    Label thisIsHeapObject(env);
30    Label thisIsDataView(env);
31    Label indexIsInt(env);
32    BRANCH(TaggedIsHeapObject(thisValue), &thisIsHeapObject, slowPath);
33    Bind(&thisIsHeapObject);
34    BRANCH(IsDataView(thisValue), &thisIsDataView, slowPath);
35    Bind(&thisIsDataView);
36    GateRef indexTagged = GetCallArg0(numArgs);
37    GateRef value = GetCallArg1(numArgs);
38    BRANCH(TaggedIsInt(indexTagged), &indexIsInt, slowPath);
39    Bind(&indexIsInt);
40    {
41        DEFVARIABLE(isLittleEndian, VariableType::JS_ANY(), TaggedFalse());
42        Label indexIsValid(env);
43        Label valueIsValid(env);
44        Label checkOffset(env);
45        Label getArrayBuffer(env);
46        Label setValue(env);
47        Label toBool(env);
48        GateRef index = GetInt32OfTInt(indexTagged);
49        BRANCH(Int32LessThan(index, Int32(0)), slowPath, &indexIsValid);
50        Bind(&indexIsValid);
51        {
52            BRANCH(TaggedIsNumber(value), &valueIsValid, slowPath);
53            Bind(&valueIsValid);
54            GateRef littleEndianHandle; // need to init
55            if constexpr (type == DataViewType::UINT8 || type == DataViewType::INT8) {
56                littleEndianHandle = TaggedTrue();
57            } else {
58                littleEndianHandle = GetCallArg2(numArgs);
59            }
60            BRANCH(TaggedIsUndefined(littleEndianHandle), &getArrayBuffer, &toBool);
61            Bind(&toBool);
62            {
63                isLittleEndian = FastToBoolean(littleEndianHandle, 1);
64                Jump(&getArrayBuffer);
65            }
66            Bind(&getArrayBuffer);
67            {
68                GateRef buffer = GetViewedArrayBuffer(thisValue);
69                BRANCH(IsDetachedBuffer(buffer), slowPath, &checkOffset);
70                Bind(&checkOffset);
71                {
72                    GateRef offset = GetByteOffset(thisValue);
73                    GateRef size = GetByteLength(thisValue);
74                    GateRef elementSize = GetElementSize(type);
75                    BRANCH(Int32GreaterThan(Int32Add(index, elementSize), size), slowPath, &setValue);
76                    Bind(&setValue);
77                    {
78                        GateRef bufferIndex = Int32Add(index, offset);
79                        BuiltinsTypedArrayStubBuilder builder(this);
80                        GateRef pointer = builder.GetDataPointFromBuffer(buffer);
81                        GateRef doubleValue = TaggedGetNumber(value);
82                        if constexpr (type == DataViewType::INT32 || type == DataViewType::UINT32) {
83                            SetValueInBufferForInt32(glue, pointer, bufferIndex,
84                                                     DoubleToInt(glue, doubleValue), *isLittleEndian);
85                        } else if constexpr (type == DataViewType::FLOAT32) {
86                            GateRef flaotValue = TruncDoubleToFloat32(doubleValue);
87                            SetValueInBufferForInt32(glue, pointer, bufferIndex,
88                                                     CastFloat32ToInt32(flaotValue), *isLittleEndian);
89                        } else if constexpr (type == DataViewType::FLOAT64) {
90                            GateRef int64Value = CastDoubleToInt64(doubleValue);
91                            SetValueInBufferForInt64(glue, pointer, bufferIndex,
92                                                     int64Value, *isLittleEndian);
93                        }
94                        Jump(exit);
95                    }
96                }
97            }
98        }
99    }
100}
101
102template void BuiltinsDataViewStubBuilder::SetTypedValue<DataViewType::INT32>(GateRef glue, GateRef thisValue,
103    GateRef numArgs, Variable* res, Label *exit, Label *slowPath);
104template void BuiltinsDataViewStubBuilder::SetTypedValue<DataViewType::FLOAT32>(GateRef glue, GateRef thisValue,
105    GateRef numArgs, Variable* res, Label *exit, Label *slowPath);
106template void BuiltinsDataViewStubBuilder::SetTypedValue<DataViewType::FLOAT64>(GateRef glue, GateRef thisValue,
107    GateRef numArgs, Variable* res, Label *exit, Label *slowPath);
108
109void BuiltinsDataViewStubBuilder::SetValueInBufferForInt32(GateRef glue, GateRef pointer, GateRef offset,
110    GateRef value, GateRef littleEndianHandle)
111{
112    auto env = GetEnvironment();
113    Label subentry(env);
114    env->SubCfgEntry(&subentry);
115    Label exit(env);
116    Label littleEnd(env);
117    Label notLittleEnd(env);
118    GateRef b0 = Int32And(value, Int32(0xFF));
119    GateRef b1 = Int32And(Int32LSR(value, Int32(builtins::BITS_EIGHT)), Int32(0xFF));
120    GateRef b2 = Int32And(Int32LSR(value, Int32(2 * builtins::BITS_EIGHT)), Int32(0xFF));
121    GateRef b3 = Int32LSR(value, Int32(builtins::BITS_TWENTY_FOUR));
122
123    BRANCH(TaggedIsTrue(littleEndianHandle), &littleEnd, &notLittleEnd);
124    Bind(&littleEnd);
125    {
126        Store(VariableType::INT8(), glue, pointer, offset, TruncInt32ToInt8(b0));
127        Store(VariableType::INT8(), glue, pointer, Int32Add(offset, Int32(1)), TruncInt32ToInt8(b1));
128        Store(VariableType::INT8(), glue, pointer, Int32Add(offset, Int32(OffsetIndex::TWO)), TruncInt32ToInt8(b2));
129        Store(VariableType::INT8(), glue, pointer, Int32Add(offset, Int32(OffsetIndex::THREE)), TruncInt32ToInt8(b3));
130        Jump(&exit);
131    }
132    Bind(&notLittleEnd);
133    {
134        Store(VariableType::INT8(), glue, pointer, offset, TruncInt32ToInt8(b3));
135        Store(VariableType::INT8(), glue, pointer, Int32Add(offset, Int32(1)), TruncInt32ToInt8(b2));
136        Store(VariableType::INT8(), glue, pointer, Int32Add(offset, Int32(OffsetIndex::TWO)), TruncInt32ToInt8(b1));
137        Store(VariableType::INT8(), glue, pointer, Int32Add(offset, Int32(OffsetIndex::THREE)), TruncInt32ToInt8(b0));
138        Jump(&exit);
139    }
140    Bind(&exit);
141    env->SubCfgExit();
142}
143
144void BuiltinsDataViewStubBuilder::SetValueInBufferForInt64(GateRef glue, GateRef pointer, GateRef offset,
145    GateRef value, GateRef littleEndianHandle)
146{
147    auto env = GetEnvironment();
148    Label subentry(env);
149    env->SubCfgEntry(&subentry);
150    Label exit(env);
151    Label littleEnd(env);
152    Label notLittleEnd(env);
153    GateRef lowerInt32 = TruncInt64ToInt32(Int64And(value, Int64(0xFFFFFFFF))); // NOLINT
154    GateRef highInt32 = TruncInt64ToInt32(Int64LSR(Int64And(value, Int64(0xFFFFFFFF00000000)), Int64(32))); // NOLINT
155
156    GateRef b0 = Int32And(lowerInt32, Int32(builtins::BITS_MASK_FF));
157    GateRef b1 = Int32And(Int32LSR(lowerInt32, Int32(builtins::BITS_EIGHT)), Int32(builtins::BITS_MASK_FF));
158    // 2: 2 * 8 bits
159    GateRef b2 = Int32And(Int32LSR(lowerInt32, Int32(2 * builtins::BITS_EIGHT)), Int32(builtins::BITS_MASK_FF));
160    GateRef b3 = Int32LSR(lowerInt32, Int32(builtins::BITS_TWENTY_FOUR));
161    GateRef b4 = Int32And(highInt32, Int32(builtins::BITS_MASK_FF));
162    GateRef b5 = Int32And(Int32LSR(highInt32, Int32(builtins::BITS_EIGHT)), Int32(builtins::BITS_MASK_FF));
163    // 2: 2 * 8 bits
164    GateRef b6 = Int32And(Int32LSR(highInt32, Int32(2 * builtins::BITS_EIGHT)), Int32(builtins::BITS_MASK_FF));
165    GateRef b7 = Int32LSR(highInt32, Int32(builtins::BITS_TWENTY_FOUR));
166
167    BRANCH(TaggedIsTrue(littleEndianHandle), &littleEnd, &notLittleEnd);
168    Bind(&littleEnd);
169    {
170        Store(VariableType::INT8(), glue, pointer, offset, TruncInt32ToInt8(b0));
171        Store(VariableType::INT8(), glue, pointer, Int32Add(offset, Int32(1)), TruncInt32ToInt8(b1));
172        Store(VariableType::INT8(), glue, pointer, Int32Add(offset, Int32(OffsetIndex::TWO)), TruncInt32ToInt8(b2));
173        Store(VariableType::INT8(), glue, pointer, Int32Add(offset, Int32(OffsetIndex::THREE)), TruncInt32ToInt8(b3));
174        Store(VariableType::INT8(), glue, pointer, Int32Add(offset, Int32(OffsetIndex::FOUR)), TruncInt32ToInt8(b4));
175        Store(VariableType::INT8(), glue, pointer, Int32Add(offset, Int32(OffsetIndex::FIVE)), TruncInt32ToInt8(b5));
176        Store(VariableType::INT8(), glue, pointer, Int32Add(offset, Int32(OffsetIndex::SIX)), TruncInt32ToInt8(b6));
177        Store(VariableType::INT8(), glue, pointer, Int32Add(offset, Int32(OffsetIndex::SEVEN)), TruncInt32ToInt8(b7));
178        Jump(&exit);
179    }
180    Bind(&notLittleEnd);
181    {
182        Store(VariableType::INT8(), glue, pointer, offset, TruncInt32ToInt8(b7));
183        Store(VariableType::INT8(), glue, pointer, Int32Add(offset, Int32(1)), TruncInt32ToInt8(b6));
184        Store(VariableType::INT8(), glue, pointer, Int32Add(offset, Int32(OffsetIndex::TWO)), TruncInt32ToInt8(b5));
185        Store(VariableType::INT8(), glue, pointer, Int32Add(offset, Int32(OffsetIndex::THREE)), TruncInt32ToInt8(b4));
186        Store(VariableType::INT8(), glue, pointer, Int32Add(offset, Int32(OffsetIndex::FOUR)), TruncInt32ToInt8(b3));
187        Store(VariableType::INT8(), glue, pointer, Int32Add(offset, Int32(OffsetIndex::FIVE)), TruncInt32ToInt8(b2));
188        Store(VariableType::INT8(), glue, pointer, Int32Add(offset, Int32(OffsetIndex::SIX)), TruncInt32ToInt8(b1));
189        Store(VariableType::INT8(), glue, pointer, Int32Add(offset, Int32(OffsetIndex::SEVEN)), TruncInt32ToInt8(b0));
190        Jump(&exit);
191    }
192    Bind(&exit);
193    env->SubCfgExit();
194}
195
196GateRef BuiltinsDataViewStubBuilder::GetElementSize(DataViewType type)
197{
198    GateRef size;
199    switch (type) {
200        case DataViewType::INT8:
201        case DataViewType::UINT8:
202        case DataViewType::UINT8_CLAMPED:
203            size = Int32(1);
204            break;
205        case DataViewType::INT16:
206        case DataViewType::UINT16:
207            size = Int32(2);  // 2 means the length
208            break;
209        case DataViewType::INT32:
210        case DataViewType::UINT32:
211        case DataViewType::FLOAT32:
212            size = Int32(4);  // 4 means the length
213            break;
214        case DataViewType::FLOAT64:
215        case DataViewType::BIGINT64:
216        case DataViewType::BIGUINT64:
217            size = Int32(8);  // 8 means the length
218            break;
219        default:
220            LOG_ECMA(FATAL) << "this branch is unreachable";
221            UNREACHABLE();
222    }
223    return size;
224}
225}  // namespace panda::ecmascript::kungfu
226