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_