1/*
2 * Copyright (c) 2021 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#ifndef ECMASCRIPT_IC_IC_BINARY_OP_H_
17#define ECMASCRIPT_IC_IC_BINARY_OP_H_
18
19#include "ecmascript/ic/profile_type_info.h"
20#include "ecmascript/interpreter/slow_runtime_stub.h"
21#include "ecmascript/js_tagged_value.h"
22#include "ecmascript/property_attributes.h"
23#include "ecmascript/runtime_call_id.h"
24
25namespace panda::ecmascript {
26enum class BinaryType : uint8_t {
27    NUMBER,
28    NUMBER_GEN,
29    STRING,
30    STRING_GEN,
31    GENERIC,
32};
33
34class ICBinaryOP {
35public:
36    static inline JSTaggedValue AddWithTSType(JSThread *thread, JSTaggedValue left,
37                                              JSTaggedValue right, JSTaggedValue argType)
38    {
39        INTERPRETER_TRACE(thread, AddWithTSType);
40        BinaryType addType = static_cast<BinaryType>(argType.GetInt());
41        switch (addType) {
42            // Support cases, such as: int + double, int + int, double + double
43            case BinaryType::NUMBER: {
44                double a0Double = left.IsInt() ? left.GetInt() : left.GetDouble();
45                double a1Double = right.IsInt() ? right.GetInt() : right.GetDouble();
46                double ret = a0Double + a1Double;
47                return JSTaggedValue(ret);
48            }
49            // Support cases, such as: number + null, undefined + null, boolean + number, etc.
50            case BinaryType::NUMBER_GEN: {
51                JSHandle<JSTaggedValue> leftValue(thread, left);
52                JSHandle<JSTaggedValue> rightValue(thread, right);
53                JSHandle<JSTaggedValue> primitiveA0(thread, JSTaggedValue::ToPrimitive(thread, leftValue));
54                RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
55                JSHandle<JSTaggedValue> primitiveA1(thread, JSTaggedValue::ToPrimitive(thread, rightValue));
56                RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
57
58                JSTaggedNumber taggedValueA0 = JSTaggedValue::ToNumber(thread, primitiveA0);
59                RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
60                JSTaggedNumber taggedValueA1 = JSTaggedValue::ToNumber(thread, primitiveA1);
61                RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
62                double a0Double = taggedValueA0.GetNumber();
63                double a1Double = taggedValueA1.GetNumber();
64                return JSTaggedValue(a0Double + a1Double);
65            }
66            // Support case: string + string.
67            case BinaryType::STRING: {
68                JSHandle<EcmaString> stringA0 = JSHandle<EcmaString>(JSHandle<JSTaggedValue>(thread, left));
69                JSHandle<EcmaString> stringA1 = JSHandle<EcmaString>(JSHandle<JSTaggedValue>(thread, right));
70                EcmaString *ret = EcmaStringAccessor::Concat(thread->GetEcmaVM(), stringA0, stringA1);
71                RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
72                return JSTaggedValue(ret);
73            }
74            // Support cases, such as: string + null, string + object, string + boolean, string + number, etc.
75            case BinaryType::STRING_GEN: {
76                JSHandle<JSTaggedValue> leftValue(thread, left);
77                JSHandle<JSTaggedValue> rightValue(thread, right);
78                if (left.IsString()) {
79                    JSHandle<EcmaString> stringA0 = JSHandle<EcmaString>(leftValue);
80                    JSHandle<EcmaString> stringA1 = JSTaggedValue::ToString(thread, rightValue);
81                    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
82                    EcmaString *ret = EcmaStringAccessor::Concat(thread->GetEcmaVM(), stringA0, stringA1);
83                    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
84                    return JSTaggedValue(ret);
85                } else {
86                    JSHandle<EcmaString> stringA0 = JSTaggedValue::ToString(thread, leftValue);
87                    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
88                    JSHandle<EcmaString> stringA1 = JSHandle<EcmaString>(rightValue);
89                    EcmaString *ret = EcmaStringAccessor::Concat(thread->GetEcmaVM(), stringA0, stringA1);
90                    RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
91                    return JSTaggedValue(ret);
92                }
93            }
94            // Some special cases, such as: object + undefined, object + boolean, etc.
95            case BinaryType::GENERIC: {
96                JSTaggedValue res = SlowRuntimeStub::Add2(thread, left, right);
97                return res;
98            }
99            default: {
100                LOG_ECMA(FATAL) << "this branch is unreachable";
101                UNREACHABLE();
102            }
103        }
104    }
105
106    static inline JSTaggedValue SubWithTSType(JSThread *thread, JSTaggedValue left,
107                                              JSTaggedValue right, JSTaggedValue argType)
108    {
109        INTERPRETER_TRACE(thread, SubWithTSType);
110        BinaryType subType = static_cast<BinaryType>(argType.GetInt());
111        switch (subType) {
112            // Support int or number
113            case BinaryType::NUMBER: {
114                double a0Double = left.IsInt() ? left.GetInt() : left.GetDouble();
115                double a1Double = right.IsInt() ? right.GetInt() : right.GetDouble();
116                double ret = a0Double - a1Double;
117                return JSTaggedValue(ret);
118            }
119            // Support cases, such as: string like '2333', boolean, null
120            case BinaryType::GENERIC: {
121                JSHandle<JSTaggedValue> leftValue(thread, left);
122                JSHandle<JSTaggedValue> rightValue(thread, right);
123                JSTaggedNumber number0 = JSTaggedValue::ToNumber(thread, leftValue);
124                RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
125                JSTaggedNumber number1 = JSTaggedValue::ToNumber(thread, rightValue);
126                RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
127                auto ret = number0 - number1;
128                return JSTaggedValue(ret);
129            }
130            case BinaryType::NUMBER_GEN:
131            case BinaryType::STRING:
132            case BinaryType::STRING_GEN:
133            default: {
134                LOG_ECMA(FATAL) << "this branch is unreachable";
135                UNREACHABLE();
136            }
137        }
138    }
139    static inline JSTaggedValue MulWithTSType(JSThread *thread, JSTaggedValue left,
140                                              JSTaggedValue right, JSTaggedValue argType)
141    {
142        INTERPRETER_TRACE(thread, MulWithTSType);
143        BinaryType mulType = static_cast<BinaryType>(argType.GetInt());
144        switch (mulType) {
145            // Support int or number
146            case BinaryType::NUMBER: {
147                return JSTaggedValue(left.GetNumber() * right.GetNumber());
148            }
149            // Support cases, such as: string like '2333', boolean, null
150            case BinaryType::GENERIC: {
151                JSHandle<JSTaggedValue> leftValue(thread, left);
152                JSHandle<JSTaggedValue> rightValue(thread, right);
153                // 6. Let lnum be ToNumber(leftValue).
154                JSTaggedNumber primitiveA = JSTaggedValue::ToNumber(thread, leftValue);
155                // 7. ReturnIfAbrupt(lnum).
156                RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
157                // 8. Let rnum be ToNumber(rightValue).
158                JSTaggedNumber primitiveB = JSTaggedValue::ToNumber(thread, rightValue);
159                // 9. ReturnIfAbrupt(rnum).
160                RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
161                // 12.6.3.1 Applying the * Operator
162                return primitiveA * primitiveB;
163            }
164            case BinaryType::NUMBER_GEN:
165            case BinaryType::STRING:
166            case BinaryType::STRING_GEN:
167            default: {
168                LOG_ECMA(FATAL) << "this branch is unreachable";
169                UNREACHABLE();
170            }
171        }
172    }
173
174    static inline JSTaggedValue DivWithTSType(JSThread *thread, JSTaggedValue left,
175                                             JSTaggedValue right, JSTaggedValue argType)
176    {
177        INTERPRETER_TRACE(thread, DivWithTSType);
178        BinaryType divType = static_cast<BinaryType>(argType.GetInt());
179        switch (divType) {
180            // Support int or number
181            case BinaryType::NUMBER: {
182                double dLeft = left.IsInt() ? left.GetInt() : left.GetDouble();
183                double dRight = right.IsInt() ? right.GetInt() : right.GetDouble();
184                if (UNLIKELY(dRight == 0.0)) {
185                    if (dLeft == 0.0 || std::isnan(dLeft)) {
186                        return JSTaggedValue(base::NAN_VALUE);
187                    }
188                    uint64_t flagBit = ((base::bit_cast<uint64_t>(dLeft)) ^ (base::bit_cast<uint64_t>(dRight))) &
189                                        base::DOUBLE_SIGN_MASK;
190                    return JSTaggedValue(base::bit_cast<double>(
191                        flagBit ^ (base::bit_cast<uint64_t>(base::POSITIVE_INFINITY))));
192                }
193                return JSTaggedValue(dLeft / dRight);
194            }
195            // Support special cases, such as: string like '2333', boolean, null
196            case BinaryType::GENERIC: {
197                auto res = SlowRuntimeStub::Div2(thread, left, right);
198                return res;
199            }
200            case BinaryType::NUMBER_GEN:
201            case BinaryType::STRING:
202            case BinaryType::STRING_GEN:
203            default: {
204                LOG_ECMA(FATAL) << "this branch is unreachable";
205                UNREACHABLE();
206            }
207        }
208    }
209
210    static inline JSTaggedValue ModWithTSType(JSThread *thread, JSTaggedValue left,
211                                              JSTaggedValue right, JSTaggedValue argType)
212    {
213        INTERPRETER_TRACE(thread, ModWithTSType);
214        BinaryType modType = static_cast<BinaryType>(argType.GetInt());
215        switch (modType) {
216            // Support int or number
217            case BinaryType::NUMBER: {
218                double dLeft = left.IsInt() ? left.GetInt() : left.GetDouble();
219                double dRight = right.IsInt() ? right.GetInt() : right.GetDouble();
220                if (dRight == 0.0 || std::isnan(dRight) || std::isnan(dLeft) || std::isinf(dLeft)) {
221                    return JSTaggedValue(base::NAN_VALUE);
222                }
223                if (dLeft == 0.0 || std::isinf(dRight)) {
224                    return JSTaggedValue(dLeft);
225                }
226                return JSTaggedValue(std::fmod(dLeft, dRight));
227            }
228            // Support special cases, such as: string like '2333', boolean, null
229            case BinaryType::GENERIC: {
230                JSHandle<JSTaggedValue> leftValue(thread, left);
231                JSHandle<JSTaggedValue> rightValue(thread, right);
232                JSTaggedNumber leftNumber = JSTaggedValue::ToNumber(thread, leftValue);
233                RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
234                double dLeft = leftNumber.GetNumber();
235                JSTaggedNumber rightNumber = JSTaggedValue::ToNumber(thread, rightValue);
236                RETURN_EXCEPTION_IF_ABRUPT_COMPLETION(thread);
237                double dRight = rightNumber.GetNumber();
238                // 12.6.3.3 Applying the % Operator
239                if ((dRight == 0.0) || std::isnan(dRight) || std::isnan(dLeft) || std::isinf(dLeft)) {
240                    return JSTaggedValue(base::NAN_VALUE);
241                }
242                if ((dLeft == 0.0) || std::isinf(dRight)) {
243                    return JSTaggedValue(dLeft);
244                }
245                return JSTaggedValue(std::fmod(dLeft, dRight));
246            }
247            case BinaryType::NUMBER_GEN:
248            case BinaryType::STRING:
249            case BinaryType::STRING_GEN:
250            default: {
251                LOG_ECMA(FATAL) << "this branch is unreachable";
252                UNREACHABLE();
253            }
254        }
255    }
256
257    static inline void GetBitOPDate(JSThread *thread, JSTaggedValue left, JSTaggedValue right,
258                                    int32_t &opNumber0, int32_t &opNumber1, BinaryType opType)
259    {
260        INTERPRETER_TRACE(thread, GetBitOPDate);
261        switch (opType) {
262            case BinaryType::NUMBER: {
263                opNumber0 =
264                    left.IsInt() ? left.GetInt() :
265                                   base::NumberHelper::DoubleToInt(left.GetDouble(), base::INT32_BITS);
266                opNumber1 =
267                    right.IsInt() ? right.GetInt() :
268                                    base::NumberHelper::DoubleToInt(right.GetDouble(), base::INT32_BITS);
269                break;
270            }
271            // Support special cases, such as: string like '2333', boolean, null
272            case BinaryType::GENERIC: {
273                JSHandle<JSTaggedValue> leftValue(thread, left);
274                JSHandle<JSTaggedValue> rightValue(thread, right);
275                JSTaggedValue taggedNumber0 = SlowRuntimeStub::ToJSTaggedValueWithInt32(thread,
276                                                                                        leftValue.GetTaggedValue());
277                JSTaggedValue taggedNumber1 = SlowRuntimeStub::ToJSTaggedValueWithUint32(thread,
278                                                                                         rightValue.GetTaggedValue());
279                opNumber0 = taggedNumber0.GetInt();
280                opNumber1 = taggedNumber1.GetInt();
281                break;
282            }
283            case BinaryType::NUMBER_GEN:
284            case BinaryType::STRING:
285            case BinaryType::STRING_GEN:
286            default: {
287                LOG_ECMA(FATAL) << "this branch is unreachable";
288                UNREACHABLE();
289            }
290        }
291        return;
292    }
293
294    static inline JSTaggedValue ShlWithTSType(JSThread *thread, JSTaggedValue left,
295                                              JSTaggedValue right, JSTaggedValue argType)
296    {
297        INTERPRETER_TRACE(thread, ShlWithTSType);
298        BinaryType shlType = static_cast<BinaryType>(argType.GetInt());
299        int32_t opNumber0 = 0;
300        int32_t opNumber1 = 0;
301        GetBitOPDate(thread, left, right, opNumber0, opNumber1, shlType);
302        uint32_t shift =
303                static_cast<uint32_t>(opNumber1) & 0x1f;  // NOLINT(hicpp-signed-bitwise, readability-magic-numbers)
304        using unsigned_type = std::make_unsigned_t<int32_t>;
305        auto ret =
306            static_cast<int32_t>(static_cast<unsigned_type>(opNumber0) << shift);  // NOLINT(hicpp-signed-bitwise)
307        return JSTaggedValue(ret);
308    }
309
310    static inline JSTaggedValue ShrWithTSType(JSThread *thread, JSTaggedValue left,
311                                              JSTaggedValue right, JSTaggedValue argType)
312    {
313        INTERPRETER_TRACE(thread, ShrWithTSType);
314        BinaryType shrType = static_cast<BinaryType>(argType.GetInt());
315        int32_t opNumber0 = 0;
316        int32_t opNumber1 = 0;
317        GetBitOPDate(thread, left, right, opNumber0, opNumber1, shrType);
318        uint32_t shift =
319                static_cast<uint32_t>(opNumber1) & 0x1f;     // NOLINT(hicpp-signed-bitwise, readability-magic-numbers)
320        auto ret = static_cast<int32_t>(opNumber0 >> shift);  // NOLINT(hicpp-signed-bitwise)
321        return JSTaggedValue(ret);
322    }
323
324    static inline JSTaggedValue AshrWithTSType(JSThread *thread, JSTaggedValue left,
325                                               JSTaggedValue right, JSTaggedValue argType)
326    {
327        INTERPRETER_TRACE(thread, AshrWithTSType);
328        BinaryType ashrType = static_cast<BinaryType>(argType.GetInt());
329        int32_t opNumber0 = 0;
330        int32_t opNumber1 = 0;
331        GetBitOPDate(thread, left, right, opNumber0, opNumber1, ashrType);
332        uint32_t shift =
333                static_cast<uint32_t>(opNumber1) & 0x1f;  // NOLINT(hicpp-signed-bitwise, readability-magic-numbers)
334        using unsigned_type = std::make_unsigned_t<uint32_t>;
335        auto ret =
336            static_cast<uint32_t>(static_cast<unsigned_type>(opNumber0) >> shift);  // NOLINT(hicpp-signed-bitwise)
337        return JSTaggedValue(ret);
338    }
339
340    static inline JSTaggedValue AndWithTSType(JSThread *thread, JSTaggedValue left,
341                                              JSTaggedValue right, JSTaggedValue argType)
342    {
343        INTERPRETER_TRACE(thread, AndWithTSType);
344        BinaryType andType = static_cast<BinaryType>(argType.GetInt());
345        int32_t opNumber0 = 0;
346        int32_t opNumber1 = 0;
347        GetBitOPDate(thread, left, right, opNumber0, opNumber1, andType);
348        // NOLINT(hicpp-signed-bitwise)
349        auto ret = static_cast<uint32_t>(opNumber0) & static_cast<uint32_t>(opNumber1);
350        return JSTaggedValue(ret);
351    }
352
353    static inline JSTaggedValue OrWithTSType(JSThread *thread, JSTaggedValue left,
354                                             JSTaggedValue right, JSTaggedValue argType)
355    {
356        INTERPRETER_TRACE(thread, OrWithTSType);
357        BinaryType orType = static_cast<BinaryType>(argType.GetInt());
358        int32_t opNumber0 = 0;
359        int32_t opNumber1 = 0;
360        GetBitOPDate(thread, left, right, opNumber0, opNumber1, orType);
361        // NOLINT(hicpp-signed-bitwise)
362        auto ret = static_cast<uint32_t>(opNumber0) | static_cast<uint32_t>(opNumber1);
363        return JSTaggedValue(ret);
364    }
365
366    static inline JSTaggedValue XorWithTSType(JSThread *thread, JSTaggedValue left,
367                                              JSTaggedValue right, JSTaggedValue argType)
368    {
369        INTERPRETER_TRACE(thread, XorWithTSType);
370        BinaryType xorType = static_cast<BinaryType>(argType.GetInt());
371        int32_t opNumber0 = 0;
372        int32_t opNumber1 = 0;
373        GetBitOPDate(thread, left, right, opNumber0, opNumber1, xorType);
374        // NOLINT(hicpp-signed-bitwise)
375        auto ret = static_cast<uint32_t>(opNumber0) ^ static_cast<uint32_t>(opNumber1);
376        return JSTaggedValue(ret);
377    }
378};
379}  // namespace panda::ecmascript
380
381#endif  // ECMASCRIPT_IC_IC_BINARY_OP_H_
382