diff --git a/BUILD.gn b/BUILD.gn index 0f3ddb637c..efb753c1f1 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -722,6 +722,7 @@ ecma_source = [ "ecmascript/async_generator_helper.cpp", "ecmascript/global_env.cpp", "ecmascript/global_env_constants.cpp", + "ecmascript/ic/ic_handler.cpp", "ecmascript/ic/ic_runtime.cpp", "ecmascript/ic/profile_type_info.cpp", "ecmascript/ic/property_box.cpp", diff --git a/ecmascript/base/config.h b/ecmascript/base/config.h index 9c73f72be7..eae7791494 100644 --- a/ecmascript/base/config.h +++ b/ecmascript/base/config.h @@ -34,6 +34,7 @@ namespace panda::ecmascript { #define ECMASCRIPT_ENABLE_FUNCTION_CALL_TIMER 0 #define ECMASCRIPT_ENABLE_ELEMENTSKIND_ALWAY_GENERIC 0 #define ECMASCRIPT_ENABLE_SCOPE_LOCK_STAT 0 +#define ECMASCRIPT_ENABLE_TRACE_IC 0 #define ENABLE_NEXT_OPTIMIZATION 1 diff --git a/ecmascript/ic/ic_handler.cpp b/ecmascript/ic/ic_handler.cpp new file mode 100644 index 0000000000..bbbd155110 --- /dev/null +++ b/ecmascript/ic/ic_handler.cpp @@ -0,0 +1,333 @@ +/* + * Copyright (c) 2021-2024 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ecmascript/ic/ic_handler.h" +#include "ecmascript/global_env_constants-inl.h" + +namespace panda::ecmascript { + +JSHandle LoadHandler::LoadProperty(const JSThread *thread, const ObjectOperator &op) +{ + uint64_t handler = 0; + ASSERT(!op.IsElement()); + if (!op.IsFound()) { + KindBit::Set(HandlerKind::NON_EXIST, &handler); + return JSHandle(thread, JSTaggedValue::WrapUint64(handler)); + } + ASSERT(op.IsFastMode()); + + JSTaggedValue val = op.GetValue(); + if (val.IsPropertyBox()) { + return JSHandle(thread, val); + } + bool hasAccessor = op.IsAccessorDescriptor(); + AccessorBit::Set(hasAccessor, &handler); + + if (!hasAccessor) { + JSHandle receiver = op.GetReceiver(); + if (receiver->IsString()) { + JSTaggedValue lenKey = thread->GlobalConstants()->GetLengthString(); + JSHandle key = op.GetKey(); + EcmaString *proKey = key->IsString() ? EcmaString::Cast(key->GetTaggedObject()) : nullptr; + if (EcmaStringAccessor::StringsAreEqual(proKey, EcmaString::Cast(lenKey.GetTaggedObject()))) { + KindBit::Set(HandlerKind::STRING_LENGTH, &handler); + } else { + KindBit::Set(HandlerKind::STRING, &handler); + } + } else if (receiver->IsNumber()) { + KindBit::Set(HandlerKind::NUMBER, &handler); + } else { + KindBit::Set(HandlerKind::FIELD, &handler); + } + } + + if (op.IsInlinedProps()) { + InlinedPropsBit::Set(true, &handler); + JSHandle holder = JSHandle::Cast(op.GetHolder()); + auto index = holder->GetJSHClass()->GetInlinedPropertiesIndex(op.GetIndex()); + OffsetBit::Set(index, &handler); + AttrIndexBit::Set(op.GetIndex(), &handler); + RepresentationBit::Set(op.GetRepresentation(), &handler); + return JSHandle(thread, JSTaggedValue::WrapUint64(handler)); + } + if (op.IsFastMode()) { + JSHandle holder = JSHandle::Cast(op.GetHolder()); + uint32_t inlinePropNum = holder->GetJSHClass()->GetInlinedProperties(); + AttrIndexBit::Set(op.GetIndex() + inlinePropNum, &handler); + OffsetBit::Set(op.GetIndex(), &handler); + RepresentationBit::Set(Representation::TAGGED, &handler); + return JSHandle(thread, JSTaggedValue::WrapUint64(handler)); + } + LOG_ECMA(FATAL) << "this branch is unreachable"; + UNREACHABLE(); +} + +JSHandle LoadHandler::LoadElement(const JSThread *thread, const ObjectOperator &op) +{ + uint64_t handler = 0; + KindBit::Set(HandlerKind::ELEMENT, &handler); + // To avoid logical errors and Deopt, temporarily skipping PGO Profiling. + // logical errors: + // When accessing an element of an object, AOT does not have a chain-climbing operation, + // so if the element is on a prototype, it will not be able to get the correct element. + // deopt: + // Currently there is no way to save the type of the key in pgo file, even if the type of the key + // is string, it will be treated as a number type by the AOT, leading to deopt at runtime. + if (op.GetReceiver() != op.GetHolder() || op.KeyFromStringType()) { + NeedSkipInPGODumpBit::Set(true, &handler); + } + if (op.GetReceiver()->IsJSArray()) { + IsJSArrayBit::Set(true, &handler); + } + return JSHandle(thread, JSTaggedValue::WrapUint64(handler)); +} + +JSHandle StoreHandler::StoreProperty(const JSThread *thread, const ObjectOperator &op) +{ + uint64_t handler = 0; + JSHandle receiver = JSHandle::Cast(op.GetReceiver()); + SFieldTypeBitSet(op, receiver, &handler); + if (op.IsElement()) { + SOutOfBoundsBit::Set(op.GetElementOutOfBounds(), &handler); + return StoreElement(thread, op.GetReceiver(), handler); + } + JSTaggedValue val = op.GetValue(); + if (val.IsPropertyBox()) { + return JSHandle(thread, val); + } + bool hasSetter = op.IsAccessorDescriptor(); + AccessorBit::Set(hasSetter, &handler); + if (!hasSetter) { + SKindBit::Set(StoreHandlerKind::S_FIELD, &handler); + } + if (op.IsInlinedProps()) { + InlinedPropsBit::Set(true, &handler); + uint32_t index = 0; + if (!hasSetter) { + index = receiver->GetJSHClass()->GetInlinedPropertiesIndex(op.GetIndex()); + } else { + JSHandle holder = JSHandle::Cast(op.GetHolder()); + index = holder->GetJSHClass()->GetInlinedPropertiesIndex(op.GetIndex()); + } + AttrIndexBit::Set(op.GetIndex(), &handler); + OffsetBit::Set(index, &handler); + RepresentationBit::Set(op.GetRepresentation(), &handler); + return JSHandle(thread, JSTaggedValue::WrapUint64(handler)); + } + ASSERT(op.IsFastMode()); + uint32_t inlinePropNum = receiver->GetJSHClass()->GetInlinedProperties(); + AttrIndexBit::Set(op.GetIndex() + inlinePropNum, &handler); + OffsetBit::Set(op.GetIndex(), &handler); + RepresentationBit::Set(Representation::TAGGED, &handler); + return JSHandle(thread, JSTaggedValue::WrapUint64(handler)); +} + +JSHandle TransitionHandler::StoreTransition(const JSThread *thread, const ObjectOperator &op) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle handler = factory->NewTransitionHandler(); + JSHandle handlerInfo = StoreHandler::StoreProperty(thread, op); + handler->SetHandlerInfo(thread, handlerInfo); + auto hclass = JSObject::Cast(op.GetReceiver()->GetTaggedObject())->GetJSHClass(); + handler->SetTransitionHClass(thread, JSTaggedValue(hclass)); + return JSHandle::Cast(handler); +} + +JSHandle PrototypeHandler::LoadPrototype(const JSThread *thread, const ObjectOperator &op, + const JSHandle &hclass) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle handlerInfo = LoadHandler::LoadProperty(thread, op); + JSHandle handler = factory->NewPrototypeHandler(); + handler->SetHandlerInfo(thread, handlerInfo); + if (op.IsFound()) { + handler->SetHolder(thread, op.GetHolder()); + } + if (op.IsAccessorDescriptor()) { + JSTaggedValue result = op.GetValue(); + if (result.IsPropertyBox()) { + result = PropertyBox::Cast(result.GetTaggedObject())->GetValue(); + } + AccessorData *accessor = AccessorData::Cast(result.GetTaggedObject()); + if (!accessor->IsInternal()) { + JSTaggedValue getter = accessor->GetGetter(); + if (!getter.IsUndefined()) { + JSHandle func(thread, getter); + uint32_t methodOffset = Method::Cast(func->GetMethod())->GetMethodId().GetOffset(); + handler->SetAccessorMethodId(methodOffset); + handler->SetAccessorJSFunction(thread, getter); + } + } + } + // ShareToLocal is prohibited + if (!hclass->IsJSShared()) { + auto result = JSHClass::EnableProtoChangeMarker(thread, hclass); + handler->SetProtoCell(thread, result); + } + return JSHandle::Cast(handler); +} + +JSHandle PrototypeHandler::StorePrototype(const JSThread *thread, const ObjectOperator &op, + const JSHandle &hclass) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle handler = factory->NewPrototypeHandler(); + JSHandle handlerInfo = StoreHandler::StoreProperty(thread, op); + handler->SetHandlerInfo(thread, handlerInfo); + handler->SetHolder(thread, op.GetHolder()); + if (op.IsAccessorDescriptor()) { + JSTaggedValue result = op.GetValue(); + if (result.IsPropertyBox()) { + result = PropertyBox::Cast(result.GetTaggedObject())->GetValue(); + } + AccessorData *accessor = AccessorData::Cast(result.GetTaggedObject()); + if (!accessor->IsInternal() && accessor->HasSetter()) { + JSTaggedValue setter = accessor->GetSetter(); + JSHandle func(thread, setter); + handler->SetAccessorMethodId( + Method::Cast(func->GetMethod())->GetMethodId().GetOffset()); + handler->SetAccessorJSFunction(thread, setter); + } + } + // ShareToLocal is prohibited + if (!hclass->IsJSShared()) { + auto result = JSHClass::EnableProtoChangeMarker(thread, hclass); + handler->SetProtoCell(thread, result); + } + return JSHandle::Cast(handler); +} + +JSHandle TransWithProtoHandler::StoreTransition(const JSThread *thread, const ObjectOperator &op, + const JSHandle &hclass) +{ + ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); + JSHandle handler = factory->NewTransWithProtoHandler(); + JSHandle handlerInfo = StoreHandler::StoreProperty(thread, op); + handler->SetHandlerInfo(thread, handlerInfo); + auto result = JSHClass::EnableProtoChangeMarker(thread, hclass); + handler->SetProtoCell(thread, result); + handler->SetTransitionHClass(thread, hclass.GetTaggedValue()); + + return JSHandle::Cast(handler); +} + +void HandlerBase::PrintLoadHandler([[maybe_unused]] uint64_t handler, [[maybe_unused]] std::ostream& os) +{ +#if ECMASCRIPT_ENABLE_TRACE_IC + HandlerKind kind = GetKind(handler); + os << "kind = "; + switch (kind) { + case HandlerKind::NONE: + os << "NONE" + << ", is InlinedPropsBit = " + << InlinedPropsBit::Get(handler) + << ", is AccessorBit = " + << AccessorBit::Get(handler) + << ", OffsetBit = " + << OffsetBit::Get(handler) + << ", AttrIndexBit = " + << AttrIndexBit::Get(handler); + break; + case HandlerKind::FIELD: + os << "FIELD" + << ", is InlinedPropsBit = " + << InlinedPropsBit::Get(handler) + << ", is AccessorBit = " + << AccessorBit::Get(handler) + << ", OffsetBit = " + << OffsetBit::Get(handler) + << ", AttrIndexBit = " + << AttrIndexBit::Get(handler); + break; + case HandlerKind::ELEMENT: + os << "ELEMENT" + << ", is JSArray = " + << IsJSArrayBit::Get(handler); + break; + case HandlerKind::DICTIONARY: + os << "DICTIONARY"; + break; + case HandlerKind::STRING: + os << "STRING"; + break; + case HandlerKind::STRING_LENGTH: + os << "STRING_LENGTH"; + break; + case HandlerKind::TYPED_ARRAY: + os << "TYPED_ARRAY," + << "is OnHeap = " + << IsOnHeapBit::Get(handler); + break; + case HandlerKind::NUMBER: + os << "NUMBER"; + break; + case HandlerKind::NON_EXIST: + os << "NON_EXIST"; + break; + default: + UNREACHABLE(); + } + os << std::endl; +#endif +} + +void HandlerBase::PrintStoreHandler([[maybe_unused]] uint64_t handler, [[maybe_unused]] std::ostream& os) +{ +#if ECMASCRIPT_ENABLE_TRACE_IC + StoreHandlerKind kind = static_cast(GetKind(handler)); + os << "kind = "; + switch (kind) { + case StoreHandlerKind::S_NONE: + os << "NONE" + << ", is InlinedPropsBit = " + << InlinedPropsBit::Get(handler) + << ", is AccessorBit = " + << AccessorBit::Get(handler) + << ", OffsetBit = " + << OffsetBit::Get(handler) + << ", AttrIndexBit = " + << AttrIndexBit::Get(handler) + << ", SFieldTypeBit = " + << static_cast(SFieldTypeBit::Get(handler)); + break; + case StoreHandlerKind::S_FIELD: + os << "S_FIELD" + << ", is InlinedPropsBit = " + << InlinedPropsBit::Get(handler) + << ", is AccessorBit = " + << AccessorBit::Get(handler) + << ", OffsetBit = " + << OffsetBit::Get(handler) + << ", AttrIndexBit = " + << AttrIndexBit::Get(handler) + << ", SFieldTypeBit = " + << static_cast(SFieldTypeBit::Get(handler)); + break; + case StoreHandlerKind::S_ELEMENT: + os << "S_ELEMENT" + << ", is JSArray = " + << IsJSArrayBit::Get(handler) + << ", SOutOfBoundsBit = " + << SOutOfBoundsBit::Get(handler) + << ", SFieldTypeBit = " + << static_cast(SFieldTypeBit::Get(handler)); + break; + default: + UNREACHABLE(); + } + os << std::endl; +#endif +} +} // namespace panda::ecmascript \ No newline at end of file diff --git a/ecmascript/ic/ic_handler.h b/ecmascript/ic/ic_handler.h index 06e4a4cb53..86909e4fd2 100644 --- a/ecmascript/ic/ic_handler.h +++ b/ecmascript/ic/ic_handler.h @@ -194,85 +194,15 @@ public: { return IsOnHeapBit::Get(handler); } + + static void PrintLoadHandler(uint64_t handler, std::ostream& os); + static void PrintStoreHandler(uint64_t handler, std::ostream& os); }; class LoadHandler final : public HandlerBase { public: - static inline JSHandle LoadProperty(const JSThread *thread, const ObjectOperator &op) - { - uint64_t handler = 0; - ASSERT(!op.IsElement()); - if (!op.IsFound()) { - KindBit::Set(HandlerKind::NON_EXIST, &handler); - return JSHandle(thread, JSTaggedValue::WrapUint64(handler)); - } - ASSERT(op.IsFastMode()); - - JSTaggedValue val = op.GetValue(); - if (val.IsPropertyBox()) { - return JSHandle(thread, val); - } - bool hasAccessor = op.IsAccessorDescriptor(); - AccessorBit::Set(hasAccessor, &handler); - - if (!hasAccessor) { - JSHandle receiver = op.GetReceiver(); - if (receiver->IsString()) { - JSTaggedValue lenKey = thread->GlobalConstants()->GetLengthString(); - JSHandle key = op.GetKey(); - EcmaString *proKey = key->IsString() ? EcmaString::Cast(key->GetTaggedObject()) : nullptr; - if (EcmaStringAccessor::StringsAreEqual(proKey, EcmaString::Cast(lenKey.GetTaggedObject()))) { - KindBit::Set(HandlerKind::STRING_LENGTH, &handler); - } else { - KindBit::Set(HandlerKind::STRING, &handler); - } - } else if (receiver->IsNumber()) { - KindBit::Set(HandlerKind::NUMBER, &handler); - } else { - KindBit::Set(HandlerKind::FIELD, &handler); - } - } - - if (op.IsInlinedProps()) { - InlinedPropsBit::Set(true, &handler); - JSHandle holder = JSHandle::Cast(op.GetHolder()); - auto index = holder->GetJSHClass()->GetInlinedPropertiesIndex(op.GetIndex()); - OffsetBit::Set(index, &handler); - AttrIndexBit::Set(op.GetIndex(), &handler); - RepresentationBit::Set(op.GetRepresentation(), &handler); - return JSHandle(thread, JSTaggedValue::WrapUint64(handler)); - } - if (op.IsFastMode()) { - JSHandle holder = JSHandle::Cast(op.GetHolder()); - uint32_t inlinePropNum = holder->GetJSHClass()->GetInlinedProperties(); - AttrIndexBit::Set(op.GetIndex() + inlinePropNum, &handler); - OffsetBit::Set(op.GetIndex(), &handler); - RepresentationBit::Set(Representation::TAGGED, &handler); - return JSHandle(thread, JSTaggedValue::WrapUint64(handler)); - } - LOG_ECMA(FATAL) << "this branch is unreachable"; - UNREACHABLE(); - } - - static inline JSHandle LoadElement(const JSThread *thread, const ObjectOperator &op) - { - uint64_t handler = 0; - KindBit::Set(HandlerKind::ELEMENT, &handler); - // To avoid logical errors and Deopt, temporarily skipping PGO Profiling. - // logical errors: - // When accessing an element of an object, AOT does not have a chain-climbing operation, - // so if the element is on a prototype, it will not be able to get the correct element. - // deopt: - // Currently there is no way to save the type of the key in pgo file, even if the type of the key - // is string, it will be treated as a number type by the AOT, leading to deopt at runtime. - if (op.GetReceiver() != op.GetHolder() || op.KeyFromStringType()) { - NeedSkipInPGODumpBit::Set(true, &handler); - } - if (op.GetReceiver()->IsJSArray()) { - IsJSArrayBit::Set(true, &handler); - } - return JSHandle(thread, JSTaggedValue::WrapUint64(handler)); - } + static JSHandle LoadProperty(const JSThread *thread, const ObjectOperator &op); + static JSHandle LoadElement(const JSThread *thread, const ObjectOperator &op); static inline JSHandle LoadStringElement(const JSThread *thread) { @@ -293,45 +223,7 @@ public: class StoreHandler final : public HandlerBase { public: - static inline JSHandle StoreProperty(const JSThread *thread, const ObjectOperator &op) - { - uint64_t handler = 0; - JSHandle receiver = JSHandle::Cast(op.GetReceiver()); - SFieldTypeBitSet(op, receiver, &handler); - if (op.IsElement()) { - SOutOfBoundsBit::Set(op.GetElementOutOfBounds(), &handler); - return StoreElement(thread, op.GetReceiver(), handler); - } - JSTaggedValue val = op.GetValue(); - if (val.IsPropertyBox()) { - return JSHandle(thread, val); - } - bool hasSetter = op.IsAccessorDescriptor(); - AccessorBit::Set(hasSetter, &handler); - if (!hasSetter) { - SKindBit::Set(StoreHandlerKind::S_FIELD, &handler); - } - if (op.IsInlinedProps()) { - InlinedPropsBit::Set(true, &handler); - uint32_t index = 0; - if (!hasSetter) { - index = receiver->GetJSHClass()->GetInlinedPropertiesIndex(op.GetIndex()); - } else { - JSHandle holder = JSHandle::Cast(op.GetHolder()); - index = holder->GetJSHClass()->GetInlinedPropertiesIndex(op.GetIndex()); - } - AttrIndexBit::Set(op.GetIndex(), &handler); - OffsetBit::Set(index, &handler); - RepresentationBit::Set(op.GetRepresentation(), &handler); - return JSHandle(thread, JSTaggedValue::WrapUint64(handler)); - } - ASSERT(op.IsFastMode()); - uint32_t inlinePropNum = receiver->GetJSHClass()->GetInlinedProperties(); - AttrIndexBit::Set(op.GetIndex() + inlinePropNum, &handler); - OffsetBit::Set(op.GetIndex(), &handler); - RepresentationBit::Set(Representation::TAGGED, &handler); - return JSHandle(thread, JSTaggedValue::WrapUint64(handler)); - } + static JSHandle StoreProperty(const JSThread *thread, const ObjectOperator &op); static inline JSHandle StoreElement(const JSThread *thread, JSHandle receiver, uint64_t handler) @@ -364,16 +256,7 @@ public: return static_cast(object); } - static inline JSHandle StoreTransition(const JSThread *thread, const ObjectOperator &op) - { - ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); - JSHandle handler = factory->NewTransitionHandler(); - JSHandle handlerInfo = StoreHandler::StoreProperty(thread, op); - handler->SetHandlerInfo(thread, handlerInfo); - auto hclass = JSObject::Cast(op.GetReceiver()->GetTaggedObject())->GetJSHClass(); - handler->SetTransitionHClass(thread, JSTaggedValue(hclass)); - return JSHandle::Cast(handler); - } + static JSHandle StoreTransition(const JSThread *thread, const ObjectOperator &op); static constexpr size_t HANDLER_INFO_OFFSET = TaggedObjectSize(); ACCESSORS(HandlerInfo, HANDLER_INFO_OFFSET, TRANSITION_HCLASS_OFFSET) @@ -391,68 +274,10 @@ public: return static_cast(object); } - static inline JSHandle LoadPrototype(const JSThread *thread, const ObjectOperator &op, - const JSHandle &hclass) - { - ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); - JSHandle handlerInfo = LoadHandler::LoadProperty(thread, op); - JSHandle handler = factory->NewPrototypeHandler(); - handler->SetHandlerInfo(thread, handlerInfo); - if (op.IsFound()) { - handler->SetHolder(thread, op.GetHolder()); - } - if (op.IsAccessorDescriptor()) { - JSTaggedValue result = op.GetValue(); - if (result.IsPropertyBox()) { - result = PropertyBox::Cast(result.GetTaggedObject())->GetValue(); - } - AccessorData *accessor = AccessorData::Cast(result.GetTaggedObject()); - if (!accessor->IsInternal()) { - JSTaggedValue getter = accessor->GetGetter(); - if (!getter.IsUndefined()) { - JSHandle func(thread, getter); - uint32_t methodOffset = Method::Cast(func->GetMethod())->GetMethodId().GetOffset(); - handler->SetAccessorMethodId(methodOffset); - handler->SetAccessorJSFunction(thread, getter); - } - } - } - // ShareToLocal is prohibited - if (!hclass->IsJSShared()) { - auto result = JSHClass::EnableProtoChangeMarker(thread, hclass); - handler->SetProtoCell(thread, result); - } - return JSHandle::Cast(handler); - } - static inline JSHandle StorePrototype(const JSThread *thread, const ObjectOperator &op, - const JSHandle &hclass) - { - ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); - JSHandle handler = factory->NewPrototypeHandler(); - JSHandle handlerInfo = StoreHandler::StoreProperty(thread, op); - handler->SetHandlerInfo(thread, handlerInfo); - handler->SetHolder(thread, op.GetHolder()); - if (op.IsAccessorDescriptor()) { - JSTaggedValue result = op.GetValue(); - if (result.IsPropertyBox()) { - result = PropertyBox::Cast(result.GetTaggedObject())->GetValue(); - } - AccessorData *accessor = AccessorData::Cast(result.GetTaggedObject()); - if (!accessor->IsInternal() && accessor->HasSetter()) { - JSTaggedValue setter = accessor->GetSetter(); - JSHandle func(thread, setter); - handler->SetAccessorMethodId( - Method::Cast(func->GetMethod())->GetMethodId().GetOffset()); - handler->SetAccessorJSFunction(thread, setter); - } - } - // ShareToLocal is prohibited - if (!hclass->IsJSShared()) { - auto result = JSHClass::EnableProtoChangeMarker(thread, hclass); - handler->SetProtoCell(thread, result); - } - return JSHandle::Cast(handler); - } + static JSHandle LoadPrototype(const JSThread *thread, const ObjectOperator &op, + const JSHandle &hclass); + static JSHandle StorePrototype(const JSThread *thread, const ObjectOperator &op, + const JSHandle &hclass); static constexpr size_t HANDLER_INFO_OFFSET = TaggedObjectSize(); ACCESSORS(HandlerInfo, HANDLER_INFO_OFFSET, PROTO_CELL_OFFSET) @@ -474,19 +299,8 @@ public: return static_cast(object); } - static inline JSHandle StoreTransition(const JSThread *thread, const ObjectOperator &op, - const JSHandle &hclass) - { - ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); - JSHandle handler = factory->NewTransWithProtoHandler(); - JSHandle handlerInfo = StoreHandler::StoreProperty(thread, op); - handler->SetHandlerInfo(thread, handlerInfo); - auto result = JSHClass::EnableProtoChangeMarker(thread, hclass); - handler->SetProtoCell(thread, result); - handler->SetTransitionHClass(thread, hclass.GetTaggedValue()); - - return JSHandle::Cast(handler); - } + static JSHandle StoreTransition(const JSThread *thread, const ObjectOperator &op, + const JSHandle &hclass); static constexpr size_t HANDLER_INFO_OFFSET = TaggedObjectSize(); ACCESSORS(HandlerInfo, HANDLER_INFO_OFFSET, TRANSITION_HCLASS_OFFSET) @@ -505,19 +319,6 @@ public: return static_cast(object); } - static inline JSHandle StoreAOT(const JSThread *thread, const ObjectOperator &op, - const JSHandle &hclass) - { - ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); - JSHandle handler = factory->NewStoreAOTHandler(); - JSHandle handlerInfo = StoreHandler::StoreProperty(thread, op); - handler->SetHandlerInfo(thread, handlerInfo); - handler->SetHolder(thread, op.GetHolder()); - auto result = JSHClass::EnableProtoChangeMarker(thread, hclass); - handler->SetProtoCell(thread, result); - return JSHandle::Cast(handler); - } - static constexpr size_t HANDLER_INFO_OFFSET = TaggedObjectSize(); ACCESSORS(HandlerInfo, HANDLER_INFO_OFFSET, PROTO_CELL_OFFSET) ACCESSORS(ProtoCell, PROTO_CELL_OFFSET, HOLDER_OFFSET) diff --git a/ecmascript/ic/ic_runtime.cpp b/ecmascript/ic/ic_runtime.cpp index 553462cf93..1d30b451a4 100644 --- a/ecmascript/ic/ic_runtime.cpp +++ b/ecmascript/ic/ic_runtime.cpp @@ -15,13 +15,13 @@ #include "ecmascript/ic/ic_runtime.h" #include "ecmascript/ic/ic_handler.h" +#include "ecmascript/dfx/stackinfo/js_stackinfo.h" #include "ecmascript/interpreter/interpreter.h" #include "ecmascript/interpreter/slow_runtime_stub.h" #include "ecmascript/js_primitive_ref.h" #include "ecmascript/shared_objects/js_shared_array.h" namespace panda::ecmascript { -#define TRACE_IC 0 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) void ICRuntime::UpdateLoadHandler(const ObjectOperator &op, JSHandle key, JSHandle receiver) @@ -152,25 +152,6 @@ void ICRuntime::UpdateStoreHandler(const ObjectOperator &op, JSHandle receiver, - [[maybe_unused]] JSHandle key) const -{ -#if TRACE_IC - auto kind = ICKindToString(GetICKind()); - auto state = ProfileTypeAccessor::ICStateToString(icAccessor_.GetICState()); - if (key->IsString()) { - auto keyStrHandle = JSHandle::Cast(key); - LOG_ECMA(ERROR) << kind << " miss key is: " << EcmaStringAccessor(keyStrHandle).ToCString() - << ", receiver is " << receiver->GetTaggedObject()->GetClass()->IsDictionaryMode() - << ", state is " << state; - } else { - LOG_ECMA(ERROR) << kind << " miss " << ", state is " - << ", receiver is " << receiver->GetTaggedObject()->GetClass()->IsDictionaryMode() - << state; - } -#endif -} - JSTaggedValue LoadICRuntime::LoadValueMiss(JSHandle receiver, JSHandle key) { JSTaggedValue::RequireObjectCoercible(thread_, receiver, "Cannot load property of null or undefined"); @@ -210,7 +191,7 @@ JSTaggedValue LoadICRuntime::LoadValueMiss(JSHandle receiver, JSH icAccessor_.SetAsMega(); return result.GetTaggedValue(); } - TraceIC(receiver, key); + TraceIC(GetThread(), receiver, key); // do not cache element if (!op.IsFastMode()) { icAccessor_.SetAsMega(); @@ -261,7 +242,7 @@ JSTaggedValue LoadICRuntime::LoadMiss(JSHandle receiver, JSHandle icAccessor_.SetAsMega(); return result.GetTaggedValue(); } - TraceIC(receiver, key); + TraceIC(GetThread(), receiver, key); // do not cache element if (!op.IsFastMode()) { icAccessor_.SetAsMega(); @@ -320,7 +301,7 @@ JSTaggedValue LoadICRuntime::LoadTypedArrayValueMiss(JSHandle rec icAccessor_.SetAsMega(); return result.GetTaggedValue(); } - TraceIC(receiver, key); + TraceIC(GetThread(), receiver, key); // do not cache element if (!op.IsFastMode()) { icAccessor_.SetAsMega(); @@ -408,7 +389,7 @@ JSTaggedValue StoreICRuntime::StoreMiss(JSHandle receiver, JSHand return JSTaggedValue::Undefined(); } - TraceIC(receiver, key); + TraceIC(GetThread(), receiver, key); if (success) { UpdateStoreHandler(op, key, receiver); } @@ -474,7 +455,7 @@ JSTaggedValue StoreICRuntime::StoreTypedArrayValueMiss(JSHandle r return JSTaggedValue::Undefined(); } - TraceIC(receiver, key); + TraceIC(GetThread(), receiver, key); if (success) { UpdateStoreHandler(op, key, receiver); } @@ -494,4 +475,56 @@ JSTaggedValue StoreICRuntime::HandleAccesor(ObjectOperator *op, const JSHandle receiver, + [[maybe_unused]] JSHandle key) const +{ +#if ECMASCRIPT_ENABLE_TRACE_IC + // If BackTrace affects IC, can choose not to execute it. + std::string strTraceIC = "Miss Func BackTrace: "; + std::vector jsStackInfo = JsStackInfo::BuildJsStackInfo(thread, true); + if (jsStackInfo.empty()) { + strTraceIC += "empty"; + } else { + JsFrameInfo jsFrameInfo = jsStackInfo.front(); + size_t pos = jsFrameInfo.pos.find(':', 0); + if (pos != CString::npos) { + int lineNumber = std::stoi(jsFrameInfo.pos.substr(0, pos)); + int columnNumber = std::stoi(jsFrameInfo.pos.substr(pos + 1)); + auto sourceMapcb = thread->GetEcmaVM()->GetSourceMapTranslateCallback(); + if (sourceMapcb != nullptr && !jsFrameInfo.fileName.empty()) { + sourceMapcb(jsFrameInfo.fileName, lineNumber, columnNumber); + } + } + strTraceIC += "funcName: " + jsFrameInfo.functionName + ", url: " + + jsFrameInfo.fileName + ":" + jsFrameInfo.pos; + } + LOG_ECMA(ERROR) << strTraceIC; + + auto kind = ICKindToString(GetICKind()); + bool primitiveIc = false; + if (receiver->IsNumber() || receiver->IsString()) { + primitiveIc = true; + } + auto state = ProfileTypeAccessor::ICStateToString(icAccessor_.GetICState()); + if (key->IsString()) { + auto keyStrHandle = JSHandle::Cast(key); + LOG_ECMA(ERROR) << kind << " miss, key is: " << EcmaStringAccessor(keyStrHandle).ToCString() + << ", icstate is: " << state + << ", slotid is: " << GetSlotId(); + } else { + LOG_ECMA(ERROR) << kind << " miss, " + << ", icstate is " << state + << ", slotid is:" << GetSlotId(); + } + if (primitiveIc) { + LOG_ECMA(ERROR) << "primitiveIc "; + } else { + JSHClass *jshclass = receiver->GetTaggedObject()->GetClass(); + LOG_ECMA(ERROR) << "receiver DictionaryMode is: " << jshclass->IsDictionaryMode() + << ", hclass is: "<< std::hex << jshclass; + } +#endif +} } // namespace panda::ecmascript diff --git a/ecmascript/ic/ic_runtime.h b/ecmascript/ic/ic_runtime.h index a41053e55e..0aa43d197a 100644 --- a/ecmascript/ic/ic_runtime.h +++ b/ecmascript/ic/ic_runtime.h @@ -57,7 +57,12 @@ public: return icAccessor_.GetKind(); } - void TraceIC(JSHandle receiver, JSHandle key) const; + uint32_t GetSlotId() const + { + return icAccessor_.GetSlotId(); + } + + void TraceIC(JSThread *thread, JSHandle receiver, JSHandle key) const; protected: JSThread *thread_; diff --git a/ecmascript/ic/ic_runtime_stub-inl.h b/ecmascript/ic/ic_runtime_stub-inl.h index 14c3bf4a35..79e16ed18e 100644 --- a/ecmascript/ic/ic_runtime_stub-inl.h +++ b/ecmascript/ic/ic_runtime_stub-inl.h @@ -223,6 +223,7 @@ ARK_INLINE JSTaggedValue ICRuntimeStub::StoreICWithHandler(JSThread *thread, JST INTERPRETER_TRACE(thread, StoreICWithHandler); if (handler.IsInt()) { auto handlerInfo = JSTaggedValue::UnwrapToUint64(handler); + HandlerBase::PrintStoreHandler(handlerInfo, std::cout); if (HandlerBase::IsNonSharedStoreField(handlerInfo)) { StoreField(thread, JSObject::Cast(receiver.GetTaggedObject()), value, handlerInfo); return JSTaggedValue::Undefined(); @@ -452,6 +453,7 @@ ARK_INLINE JSTaggedValue ICRuntimeStub::LoadICWithHandler(JSThread *thread, JSTa INTERPRETER_TRACE(thread, LoadICWithHandler); if (LIKELY(handler.IsInt())) { auto handlerInfo = JSTaggedValue::UnwrapToUint64(handler); + HandlerBase::PrintLoadHandler(handlerInfo, std::cout); if (LIKELY(HandlerBase::IsField(handlerInfo))) { return LoadFromField(JSObject::Cast(holder.GetTaggedObject()), handlerInfo); } @@ -481,6 +483,7 @@ ARK_INLINE JSTaggedValue ICRuntimeStub::LoadICWithElementHandler(JSThread *threa { if (LIKELY(handler.IsInt())) { auto handlerInfo = JSTaggedValue::UnwrapToUint64(handler); + HandlerBase::PrintLoadHandler(handlerInfo, std::cout); if (HandlerBase::IsNormalElement(handlerInfo)) { return LoadElement(JSObject::Cast(receiver.GetTaggedObject()), key); } else if (HandlerBase::IsTypedArrayElement(handlerInfo)) { @@ -556,6 +559,7 @@ JSTaggedValue ICRuntimeStub::StoreElement(JSThread *thread, JSObject *receiver, uint32_t elementIndex = static_cast(index); if (handler.IsInt()) { auto handlerInfo = JSTaggedValue::UnwrapToUint64(handler); + HandlerBase::PrintStoreHandler(handlerInfo, std::cout); [[maybe_unused]] EcmaHandleScope handleScope(thread); JSHandle receiverHandle(thread, receiver); JSHandle valueHandle(thread, value); diff --git a/ecmascript/ic/profile_type_info.h b/ecmascript/ic/profile_type_info.h index 8f16976586..f805091c92 100644 --- a/ecmascript/ic/profile_type_info.h +++ b/ecmascript/ic/profile_type_info.h @@ -476,6 +476,11 @@ public: return kind_; } + uint32_t GetSlotId() const + { + return slotId_; + } + private: JSThread* thread_; JSHandle profileTypeInfo_; diff --git a/ecmascript/ic/tests/ic_runtime_test.cpp b/ecmascript/ic/tests/ic_runtime_test.cpp index 5060ccaa41..963e1f4ac9 100644 --- a/ecmascript/ic/tests/ic_runtime_test.cpp +++ b/ecmascript/ic/tests/ic_runtime_test.cpp @@ -155,8 +155,8 @@ HWTEST_F_L0(ICRunTimeTest, TraceIC) JSHandle handleProfileTypeInfo = factory->NewProfileTypeInfo(arrayLength); ICRuntime icRuntime(thread, handleProfileTypeInfo, 4, ICKind::NamedGlobalLoadIC); // 4: means the NamedGlobalLoadIC - icRuntime.TraceIC(handleReceiver, handleKeyWithString); - icRuntime.TraceIC(handleReceiver, handleKeyWithElement); + icRuntime.TraceIC(thread, handleReceiver, handleKeyWithString); + icRuntime.TraceIC(thread, handleReceiver, handleKeyWithElement); } HWTEST_F_L0(ICRunTimeTest, StoreMiss)