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