• 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_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 
SubWithTSType(JSThread * thread,JSTaggedValue left,JSTaggedValue right,JSTaggedValue argType)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     }
MulWithTSType(JSThread * thread,JSTaggedValue left,JSTaggedValue right,JSTaggedValue argType)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 
DivWithTSType(JSThread * thread,JSTaggedValue left,JSTaggedValue right,JSTaggedValue argType)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 
ModWithTSType(JSThread * thread,JSTaggedValue left,JSTaggedValue right,JSTaggedValue argType)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 
GetBitOPDate(JSThread * thread,JSTaggedValue left,JSTaggedValue right,int32_t & opNumber0,int32_t & opNumber1,BinaryType opType)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 
ShlWithTSType(JSThread * thread,JSTaggedValue left,JSTaggedValue right,JSTaggedValue argType)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 
ShrWithTSType(JSThread * thread,JSTaggedValue left,JSTaggedValue right,JSTaggedValue argType)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 
AshrWithTSType(JSThread * thread,JSTaggedValue left,JSTaggedValue right,JSTaggedValue argType)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 
AndWithTSType(JSThread * thread,JSTaggedValue left,JSTaggedValue right,JSTaggedValue argType)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 
OrWithTSType(JSThread * thread,JSTaggedValue left,JSTaggedValue right,JSTaggedValue argType)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 
XorWithTSType(JSThread * thread,JSTaggedValue left,JSTaggedValue right,JSTaggedValue argType)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