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_HANDLER_H 17 #define ECMASCRIPT_IC_IC_HANDLER_H 18 19 #include "ecmascript/ecma_macros.h" 20 #include "ecmascript/js_tagged_value-inl.h" 21 #include "ecmascript/js_typed_array.h" 22 #include "ecmascript/mem/tagged_object.h" 23 24 namespace panda::ecmascript { 25 class HandlerBase { 26 public: 27 static constexpr uint32_t KIND_BIT_LENGTH = 3; 28 static constexpr uint32_t STORE_KIND_BIT_LENGTH = 2; 29 enum HandlerKind { 30 NONE = 0, 31 FIELD, 32 ELEMENT, 33 DICTIONARY, 34 STRING, 35 STRING_LENGTH, 36 TYPED_ARRAY, 37 NON_EXIST, 38 TOTAL_KINDS, 39 }; 40 41 // Store Handler kind combined with KindBit called SWholeKindBit. Which used to quickly check S_FIELD kind 42 enum StoreHandlerKind { 43 S_NONE = HandlerKind::NONE, 44 S_FIELD, 45 S_ELEMENT, 46 S_TOTAL_KINDS, 47 }; 48 49 // For Load 50 using KindBit = BitField<HandlerKind, 0, KIND_BIT_LENGTH>; 51 using InlinedPropsBit = KindBit::NextFlag; 52 using AccessorBit = InlinedPropsBit::NextFlag; 53 using InternalAccessorBit = AccessorBit::NextFlag; 54 using IsJSArrayBit = InternalAccessorBit::NextFlag; 55 using OffsetBit = IsJSArrayBit::NextField<uint32_t, PropertyAttributes::OFFSET_BITFIELD_NUM>; 56 using RepresentationBit = OffsetBit::NextField<Representation, PropertyAttributes::REPRESENTATION_NUM>; 57 using AttrIndexBit = RepresentationBit::NextField<uint32_t, PropertyAttributes::OFFSET_BITFIELD_NUM>; 58 using IsOnHeapBit = AttrIndexBit::NextFlag; 59 using NeedSkipInPGODumpBit = IsOnHeapBit::NextFlag; 60 static_assert(static_cast<size_t>(HandlerKind::TOTAL_KINDS) <= (1 << KIND_BIT_LENGTH)); 61 62 // For Store 63 using SKindBit = BitField<StoreHandlerKind, 0, STORE_KIND_BIT_LENGTH>; 64 using SSharedBit = SKindBit::NextFlag; 65 using SWholeKindBit = KindBit; 66 static_assert(SKindBit::START_BIT == SWholeKindBit::START_BIT); 67 static_assert(SSharedBit::Mask() || SKindBit::Mask() == KindBit::Mask()); 68 using STrackTypeBit = AttrIndexBit::NextField<uint32_t, PropertyAttributes::TRACK_TYPE_NUM>; 69 static_assert(static_cast<size_t>(StoreHandlerKind::S_TOTAL_KINDS) <= (1 << STORE_KIND_BIT_LENGTH)); 70 71 HandlerBase() = default; 72 virtual ~HandlerBase() = default; 73 IsAccessor(uint32_t handler)74 static inline bool IsAccessor(uint32_t handler) 75 { 76 return AccessorBit::Get(handler); 77 } 78 IsInternalAccessor(uint32_t handler)79 static inline bool IsInternalAccessor(uint32_t handler) 80 { 81 return InternalAccessorBit::Get(handler); 82 } 83 GetTrackType(uint32_t handler)84 static inline TrackType GetTrackType(uint32_t handler) 85 { 86 return static_cast<TrackType>(STrackTypeBit::Get(handler)); 87 } 88 IsNonExist(uint32_t handler)89 static inline bool IsNonExist(uint32_t handler) 90 { 91 return GetKind(handler) == HandlerKind::NON_EXIST; 92 } 93 IsField(uint32_t handler)94 static inline bool IsField(uint32_t handler) 95 { 96 return GetKind(handler) == HandlerKind::FIELD; 97 } 98 IsNonSharedStoreField(uint32_t handler)99 static inline bool IsNonSharedStoreField(uint32_t handler) 100 { 101 return static_cast<StoreHandlerKind>(GetKind(handler)) == StoreHandlerKind::S_FIELD; 102 } 103 IsStoreShared(uint32_t handler)104 static inline bool IsStoreShared(uint32_t handler) 105 { 106 return SSharedBit::Get(handler); 107 } 108 ClearSharedStoreKind(uint32_t & handler)109 static inline void ClearSharedStoreKind(uint32_t &handler) 110 { 111 SSharedBit::Set<uint32_t>(false, &handler); 112 } 113 IsString(uint32_t handler)114 static inline bool IsString(uint32_t handler) 115 { 116 return GetKind(handler) == HandlerKind::STRING; 117 } 118 IsStringLength(uint32_t handler)119 static inline bool IsStringLength(uint32_t handler) 120 { 121 return GetKind(handler) == HandlerKind::STRING_LENGTH; 122 } 123 IsElement(uint32_t handler)124 static inline bool IsElement(uint32_t handler) 125 { 126 return IsNormalElement(handler) || IsStringElement(handler) || IsTypedArrayElement(handler); 127 } 128 IsNormalElement(uint32_t handler)129 static inline bool IsNormalElement(uint32_t handler) 130 { 131 return GetKind(handler) == HandlerKind::ELEMENT; 132 } 133 IsStringElement(uint32_t handler)134 static inline bool IsStringElement(uint32_t handler) 135 { 136 return GetKind(handler) == HandlerKind::STRING; 137 } 138 IsTypedArrayElement(uint32_t handler)139 static inline bool IsTypedArrayElement(uint32_t handler) 140 { 141 return GetKind(handler) == HandlerKind::TYPED_ARRAY; 142 } 143 IsDictionary(uint32_t handler)144 static inline bool IsDictionary(uint32_t handler) 145 { 146 return GetKind(handler) == HandlerKind::DICTIONARY; 147 } 148 IsInlinedProps(uint32_t handler)149 static inline bool IsInlinedProps(uint32_t handler) 150 { 151 return InlinedPropsBit::Get(handler); 152 } 153 GetKind(uint32_t handler)154 static inline HandlerKind GetKind(uint32_t handler) 155 { 156 return KindBit::Get(handler); 157 } 158 IsJSArray(uint32_t handler)159 static inline bool IsJSArray(uint32_t handler) 160 { 161 return IsJSArrayBit::Get(handler); 162 } 163 NeedSkipInPGODump(uint32_t handler)164 static inline bool NeedSkipInPGODump(uint32_t handler) 165 { 166 return NeedSkipInPGODumpBit::Get(handler); 167 } 168 GetOffset(uint32_t handler)169 static inline int GetOffset(uint32_t handler) 170 { 171 return OffsetBit::Get(handler); 172 } 173 IsOnHeap(uint32_t handler)174 static inline bool IsOnHeap(uint32_t handler) 175 { 176 return IsOnHeapBit::Get(handler); 177 } 178 }; 179 180 class LoadHandler final : public HandlerBase { 181 public: LoadProperty(const JSThread * thread,const ObjectOperator & op)182 static inline JSHandle<JSTaggedValue> LoadProperty(const JSThread *thread, const ObjectOperator &op) 183 { 184 uint32_t handler = 0; 185 ASSERT(!op.IsElement()); 186 if (!op.IsFound()) { 187 KindBit::Set<uint32_t>(HandlerKind::NON_EXIST, &handler); 188 return JSHandle<JSTaggedValue>(thread, JSTaggedValue(handler)); 189 } 190 ASSERT(op.IsFastMode()); 191 192 JSTaggedValue val = op.GetValue(); 193 if (val.IsPropertyBox()) { 194 return JSHandle<JSTaggedValue>(thread, val); 195 } 196 bool hasAccessor = op.IsAccessorDescriptor(); 197 AccessorBit::Set<uint32_t>(hasAccessor, &handler); 198 199 if (!hasAccessor) { 200 if (op.GetReceiver()->IsString()) { 201 JSTaggedValue lenKey = thread->GlobalConstants()->GetLengthString(); 202 EcmaString *proKey = nullptr; 203 if (op.GetKey()->IsString()) { 204 proKey = EcmaString::Cast(op.GetKey()->GetTaggedObject()); 205 } 206 if (EcmaStringAccessor::StringsAreEqual(proKey, EcmaString::Cast(lenKey.GetTaggedObject()))) { 207 KindBit::Set<uint32_t>(HandlerKind::STRING_LENGTH, &handler); 208 } else { 209 KindBit::Set<uint32_t>(HandlerKind::STRING, &handler); 210 } 211 } else { 212 KindBit::Set<uint32_t>(HandlerKind::FIELD, &handler); 213 } 214 } 215 216 if (op.IsInlinedProps()) { 217 InlinedPropsBit::Set<uint32_t>(true, &handler); 218 JSHandle<JSObject> holder = JSHandle<JSObject>::Cast(op.GetHolder()); 219 auto index = holder->GetJSHClass()->GetInlinedPropertiesIndex(op.GetIndex()); 220 OffsetBit::Set<uint32_t>(index, &handler); 221 AttrIndexBit::Set<uint32_t>(op.GetIndex(), &handler); 222 RepresentationBit::Set(op.GetRepresentation(), &handler); 223 return JSHandle<JSTaggedValue>(thread, JSTaggedValue(handler)); 224 } 225 if (op.IsFastMode()) { 226 JSHandle<JSObject> holder = JSHandle<JSObject>::Cast(op.GetHolder()); 227 uint32_t inlinePropNum = holder->GetJSHClass()->GetInlinedProperties(); 228 AttrIndexBit::Set<uint32_t>(op.GetIndex() + inlinePropNum, &handler); 229 OffsetBit::Set<uint32_t>(op.GetIndex(), &handler); 230 RepresentationBit::Set(Representation::TAGGED, &handler); 231 return JSHandle<JSTaggedValue>(thread, JSTaggedValue(handler)); 232 } 233 LOG_ECMA(FATAL) << "this branch is unreachable"; 234 UNREACHABLE(); 235 } 236 LoadElement(const JSThread * thread,const ObjectOperator & op)237 static inline JSHandle<JSTaggedValue> LoadElement(const JSThread *thread, const ObjectOperator &op) 238 { 239 uint32_t handler = 0; 240 KindBit::Set<uint32_t>(HandlerKind::ELEMENT, &handler); 241 242 // To avoid logical errors and Deopt, temporarily skipping PGO Profiling. 243 // logical errors: 244 // When accessing an element of an object, AOT does not have a chain-climbing operation, 245 // so if the element is on a prototype, it will not be able to get the correct element. 246 // deopt: 247 // Currently there is no way to save the type of the key in pgo file, even if the type of the key 248 // is string, it will be treated as a number type by the AOT, leading to deopt at runtime. 249 if (op.GetReceiver() != op.GetHolder() || 250 op.KeyFromStringType()) { 251 NeedSkipInPGODumpBit::Set<uint32_t>(true, &handler); 252 } 253 254 if (op.GetReceiver()->IsJSArray()) { 255 IsJSArrayBit::Set<uint32_t>(true, &handler); 256 } 257 return JSHandle<JSTaggedValue>(thread, JSTaggedValue(handler)); 258 } 259 LoadStringElement(const JSThread * thread)260 static inline JSHandle<JSTaggedValue> LoadStringElement(const JSThread *thread) 261 { 262 uint32_t handler = 0; 263 KindBit::Set<uint32_t>(HandlerKind::STRING, &handler); 264 return JSHandle<JSTaggedValue>(thread, JSTaggedValue(handler)); 265 } 266 LoadTypedArrayElement(const JSThread * thread,JSHandle<JSTypedArray> typedArray)267 static inline JSHandle<JSTaggedValue> LoadTypedArrayElement(const JSThread *thread, 268 JSHandle<JSTypedArray> typedArray) 269 { 270 uint32_t handler = 0; 271 KindBit::Set<uint32_t>(HandlerKind::TYPED_ARRAY, &handler); 272 IsOnHeapBit::Set<uint32_t>(JSHandle<TaggedObject>(typedArray)->GetClass()->IsOnHeapFromBitField(), &handler); 273 return JSHandle<JSTaggedValue>(thread, JSTaggedValue(handler)); 274 } 275 }; 276 277 class StoreHandler final : public HandlerBase { 278 public: StoreProperty(const JSThread * thread,const ObjectOperator & op)279 static inline JSHandle<JSTaggedValue> StoreProperty(const JSThread *thread, const ObjectOperator &op) 280 { 281 uint32_t handler = 0; 282 JSHandle<JSObject> receiver = JSHandle<JSObject>::Cast(op.GetReceiver()); 283 SSharedBit::Set<uint32_t>(op.GetReceiver()->IsJSShared(), &handler); 284 TaggedArray *array = TaggedArray::Cast(receiver->GetProperties().GetTaggedObject()); 285 if (!array->IsDictionaryMode()) { 286 STrackTypeBit::Set(static_cast<uint32_t>(op.GetAttr().GetTrackType()), &handler); 287 } else { 288 STrackTypeBit::Set(static_cast<uint32_t>(op.GetAttr().GetDictTrackType()), &handler); 289 } 290 if (op.IsElement()) { 291 return StoreElement(thread, op.GetReceiver(), handler); 292 } 293 JSTaggedValue val = op.GetValue(); 294 if (val.IsPropertyBox()) { 295 return JSHandle<JSTaggedValue>(thread, val); 296 } 297 bool hasSetter = op.IsAccessorDescriptor(); 298 AccessorBit::Set<uint32_t>(hasSetter, &handler); 299 if (!hasSetter) { 300 SKindBit::Set<uint32_t>(StoreHandlerKind::S_FIELD, &handler); 301 } 302 if (op.IsInlinedProps()) { 303 InlinedPropsBit::Set<uint32_t>(true, &handler); 304 uint32_t index = 0; 305 if (!hasSetter) { 306 index = receiver->GetJSHClass()->GetInlinedPropertiesIndex(op.GetIndex()); 307 } else { 308 JSHandle<JSObject> holder = JSHandle<JSObject>::Cast(op.GetHolder()); 309 index = holder->GetJSHClass()->GetInlinedPropertiesIndex(op.GetIndex()); 310 } 311 AttrIndexBit::Set<uint32_t>(op.GetIndex(), &handler); 312 OffsetBit::Set<uint32_t>(index, &handler); 313 RepresentationBit::Set(op.GetRepresentation(), &handler); 314 return JSHandle<JSTaggedValue>(thread, JSTaggedValue(static_cast<int32_t>(handler))); 315 } 316 ASSERT(op.IsFastMode()); 317 uint32_t inlinePropNum = receiver->GetJSHClass()->GetInlinedProperties(); 318 AttrIndexBit::Set<uint32_t>(op.GetIndex() + inlinePropNum, &handler); 319 OffsetBit::Set<uint32_t>(op.GetIndex(), &handler); 320 RepresentationBit::Set(Representation::TAGGED, &handler); 321 return JSHandle<JSTaggedValue>(thread, JSTaggedValue(static_cast<int32_t>(handler))); 322 } 323 StoreElement(const JSThread * thread,JSHandle<JSTaggedValue> receiver,uint32_t handler)324 static inline JSHandle<JSTaggedValue> StoreElement(const JSThread *thread, 325 JSHandle<JSTaggedValue> receiver, uint32_t handler) 326 { 327 SKindBit::Set<uint32_t>(StoreHandlerKind::S_ELEMENT, &handler); 328 329 if (receiver->IsJSArray()) { 330 IsJSArrayBit::Set<uint32_t>(true, &handler); 331 } 332 return JSHandle<JSTaggedValue>(thread, JSTaggedValue(static_cast<int32_t>(handler))); 333 } 334 }; 335 336 class TransitionHandler : public TaggedObject { 337 public: Cast(TaggedObject * object)338 static TransitionHandler *Cast(TaggedObject *object) 339 { 340 ASSERT(JSTaggedValue(object).IsTransitionHandler()); 341 return static_cast<TransitionHandler *>(object); 342 } 343 StoreTransition(const JSThread * thread,const ObjectOperator & op)344 static inline JSHandle<JSTaggedValue> StoreTransition(const JSThread *thread, const ObjectOperator &op) 345 { 346 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); 347 JSHandle<TransitionHandler> handler = factory->NewTransitionHandler(); 348 JSHandle<JSTaggedValue> handlerInfo = StoreHandler::StoreProperty(thread, op); 349 handler->SetHandlerInfo(thread, handlerInfo); 350 auto hclass = JSObject::Cast(op.GetReceiver()->GetTaggedObject())->GetJSHClass(); 351 handler->SetTransitionHClass(thread, JSTaggedValue(hclass)); 352 return JSHandle<JSTaggedValue>::Cast(handler); 353 } 354 355 static constexpr size_t HANDLER_INFO_OFFSET = TaggedObjectSize(); 356 357 ACCESSORS(HandlerInfo, HANDLER_INFO_OFFSET, TRANSITION_HCLASS_OFFSET) 358 359 ACCESSORS(TransitionHClass, TRANSITION_HCLASS_OFFSET, SIZE) 360 361 DECL_VISIT_OBJECT(HANDLER_INFO_OFFSET, SIZE) 362 DECL_DUMP() 363 }; 364 365 class PrototypeHandler : public TaggedObject { 366 public: Cast(TaggedObject * object)367 static PrototypeHandler *Cast(TaggedObject *object) 368 { 369 ASSERT(JSTaggedValue(object).IsPrototypeHandler()); 370 return static_cast<PrototypeHandler *>(object); 371 } 372 LoadPrototype(const JSThread * thread,const ObjectOperator & op,const JSHandle<JSHClass> & hclass)373 static inline JSHandle<JSTaggedValue> LoadPrototype(const JSThread *thread, const ObjectOperator &op, 374 const JSHandle<JSHClass> &hclass) 375 { 376 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); 377 JSHandle<JSTaggedValue> handlerInfo = LoadHandler::LoadProperty(thread, op); 378 JSHandle<PrototypeHandler> handler = factory->NewPrototypeHandler(); 379 handler->SetHandlerInfo(thread, handlerInfo); 380 if (op.IsFound()) { 381 handler->SetHolder(thread, op.GetHolder()); 382 } 383 auto result = JSHClass::EnableProtoChangeMarker(thread, hclass); 384 handler->SetProtoCell(thread, result); 385 return JSHandle<JSTaggedValue>::Cast(handler); 386 } StorePrototype(const JSThread * thread,const ObjectOperator & op,const JSHandle<JSHClass> & hclass)387 static inline JSHandle<JSTaggedValue> StorePrototype(const JSThread *thread, const ObjectOperator &op, 388 const JSHandle<JSHClass> &hclass) 389 { 390 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); 391 JSHandle<PrototypeHandler> handler = factory->NewPrototypeHandler(); 392 JSHandle<JSTaggedValue> handlerInfo = StoreHandler::StoreProperty(thread, op); 393 handler->SetHandlerInfo(thread, handlerInfo); 394 handler->SetHolder(thread, op.GetHolder()); 395 auto result = JSHClass::EnableProtoChangeMarker(thread, hclass); 396 handler->SetProtoCell(thread, result); 397 return JSHandle<JSTaggedValue>::Cast(handler); 398 } 399 400 static constexpr size_t HANDLER_INFO_OFFSET = TaggedObjectSize(); 401 402 ACCESSORS(HandlerInfo, HANDLER_INFO_OFFSET, PROTO_CELL_OFFSET) 403 404 ACCESSORS(ProtoCell, PROTO_CELL_OFFSET, HOLDER_OFFSET) 405 406 ACCESSORS(Holder, HOLDER_OFFSET, SIZE) 407 408 DECL_VISIT_OBJECT(HANDLER_INFO_OFFSET, SIZE) 409 DECL_DUMP() 410 }; 411 412 class TransWithProtoHandler : public TaggedObject { 413 public: Cast(TaggedObject * object)414 static TransWithProtoHandler *Cast(TaggedObject *object) 415 { 416 ASSERT(JSTaggedValue(object).IsTransWithProtoHandler()); 417 return static_cast<TransWithProtoHandler *>(object); 418 } 419 StoreTransition(const JSThread * thread,const ObjectOperator & op,const JSHandle<JSHClass> & hclass)420 static inline JSHandle<JSTaggedValue> StoreTransition(const JSThread *thread, const ObjectOperator &op, 421 const JSHandle<JSHClass> &hclass) 422 { 423 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); 424 JSHandle<TransWithProtoHandler> handler = factory->NewTransWithProtoHandler(); 425 JSHandle<JSTaggedValue> handlerInfo = StoreHandler::StoreProperty(thread, op); 426 handler->SetHandlerInfo(thread, handlerInfo); 427 auto result = JSHClass::EnableProtoChangeMarker(thread, hclass); 428 handler->SetProtoCell(thread, result); 429 handler->SetTransitionHClass(thread, hclass.GetTaggedValue()); 430 431 return JSHandle<JSTaggedValue>::Cast(handler); 432 } 433 434 static constexpr size_t HANDLER_INFO_OFFSET = TaggedObjectSize(); 435 436 ACCESSORS(HandlerInfo, HANDLER_INFO_OFFSET, TRANSITION_HCLASS_OFFSET) 437 438 ACCESSORS(TransitionHClass, TRANSITION_HCLASS_OFFSET, PROTO_CELL_OFFSET) 439 440 ACCESSORS(ProtoCell, PROTO_CELL_OFFSET, SIZE) 441 442 DECL_VISIT_OBJECT(HANDLER_INFO_OFFSET, SIZE) 443 DECL_DUMP() 444 }; 445 446 class StoreTSHandler : public TaggedObject { 447 public: Cast(TaggedObject * object)448 static StoreTSHandler *Cast(TaggedObject *object) 449 { 450 ASSERT(JSTaggedValue(object).IsStoreTSHandler()); 451 return static_cast<StoreTSHandler *>(object); 452 } 453 StoreAOT(const JSThread * thread,const ObjectOperator & op,const JSHandle<JSHClass> & hclass)454 static inline JSHandle<JSTaggedValue> StoreAOT(const JSThread *thread, const ObjectOperator &op, 455 const JSHandle<JSHClass> &hclass) 456 { 457 ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); 458 JSHandle<StoreTSHandler> handler = factory->NewStoreTSHandler(); 459 JSHandle<JSTaggedValue> handlerInfo = StoreHandler::StoreProperty(thread, op); 460 handler->SetHandlerInfo(thread, handlerInfo); 461 handler->SetHolder(thread, op.GetHolder()); 462 auto result = JSHClass::EnableProtoChangeMarker(thread, hclass); 463 handler->SetProtoCell(thread, result); 464 return JSHandle<JSTaggedValue>::Cast(handler); 465 } 466 467 static constexpr size_t HANDLER_INFO_OFFSET = TaggedObjectSize(); 468 469 ACCESSORS(HandlerInfo, HANDLER_INFO_OFFSET, PROTO_CELL_OFFSET) 470 471 ACCESSORS(ProtoCell, PROTO_CELL_OFFSET, HOLDER_OFFSET) 472 473 ACCESSORS(Holder, HOLDER_OFFSET, SIZE) 474 475 DECL_VISIT_OBJECT(HANDLER_INFO_OFFSET, SIZE) 476 DECL_DUMP() 477 }; 478 } // namespace panda::ecmascript 479 #endif // ECMASCRIPT_IC_IC_HANDLER_H 480