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