From 9e883c29cfde3a59cfa2f05d3875d7b8f7a08001 Mon Sep 17 00:00:00 2001 From: Ishin Pavel Date: Tue, 21 Feb 2023 19:10:54 +0300 Subject: [PATCH] Support new cases for Ld/StObjByName Signed-off-by: Ishin Pavel --- .../ecmascript_compiler_interface.h | 2 +- .../intrinsics_type_resolving_ecmascript.cpp | 252 ++++++++++++++---- .../ir_builder/ecmascript_inst_builder.cpp | 12 +- runtime/asm_defines/asm_defines.def | 2 + .../class_linker/panda_file_translator.cpp | 16 +- .../compiler/ecmascript_runtime_interface.cpp | 159 ++++++++++- .../compiler/ecmascript_runtime_interface.h | 46 +++- runtime/ecma_vm.cpp | 2 +- runtime/ic/ic_handler.h | 19 +- runtime/ic/ic_runtime_stub-inl.h | 1 + runtime/ic/profile_type_info.h | 2 +- runtime/interpreter/slow_runtime_stub.cpp | 5 +- tests/checked/obj_by_name.js | 173 ++++++++++-- 13 files changed, 573 insertions(+), 118 deletions(-) diff --git a/compiler/ecmascript_extensions/ecmascript_compiler_interface.h b/compiler/ecmascript_extensions/ecmascript_compiler_interface.h index 443dc48cc..3056f919a 100644 --- a/compiler/ecmascript_extensions/ecmascript_compiler_interface.h +++ b/compiler/ecmascript_extensions/ecmascript_compiler_interface.h @@ -47,7 +47,7 @@ virtual bool GetProfileDataForNamedAccess( } virtual bool GetProfileDataForNamedAccess( - [[maybe_unused]] uintptr_t func, [[maybe_unused]] uintptr_t slot_id, + [[maybe_unused]] RuntimeInterface::MethodPtr m, [[maybe_unused]] uintptr_t func, [[maybe_unused]] uintptr_t slot_id, [[maybe_unused]] ArenaVector *profile) { return false; diff --git a/compiler/intrinsics_type_resolving_ecmascript.cpp b/compiler/intrinsics_type_resolving_ecmascript.cpp index f32a74fe6..d10331f15 100644 --- a/compiler/intrinsics_type_resolving_ecmascript.cpp +++ b/compiler/intrinsics_type_resolving_ecmascript.cpp @@ -110,7 +110,8 @@ void TypesResolving::CreateCompareClass(uint32_t pc, Inst *get_cls_inst, Runtime cmp_inst->SetOperandsType(DataType::REFERENCE); if_inst->SetInput(0, cmp_inst); if_inst->SetOperandsType(DataType::BOOL); - load_bb->AppendInst(load_cls_inst); + // We insert ClassImmediate in Dominate blockm because VN can be applied + get_cls_inst->InsertAfter(load_cls_inst); load_bb->AppendInst(cmp_inst); load_bb->AppendInst(if_inst); } @@ -134,62 +135,186 @@ Inst *TypesResolving::CreateCompareClassWithDeopt(uint32_t pc, Inst *get_cls_ins } template -Inst *InsertMemByNameInst(IntrinsicInst *intrinsic, Inst *obj_inst, Inst *insert_after, uint32_t offset, - bool is_inlined) +Inst *InsertMemFromFieldInlined(IntrinsicInst *intrinsic, Inst *obj_inst, Inst *insert_after, uint32_t offset) { auto graph = obj_inst->GetBasicBlock()->GetGraph(); auto save_state = intrinsic->GetSaveState(); auto pc = obj_inst->GetPc(); - if (is_inlined) { - if constexpr (IS_LOAD) { - auto load_object = graph->CreateInstLoadObject(DataType::ANY, pc); - load_object->SetInput(0, obj_inst); - load_object->SetTypeId(offset); - load_object->SetMethod(save_state->GetMethod()); - load_object->SetObjField(nullptr); - load_object->SetObjectType(ObjectType::MEM_DYNAMIC); - insert_after->InsertAfter(load_object); - - return load_object; - } else { - auto store_object = graph->CreateInstStoreObject(DataType::ANY, pc); - store_object->SetInput(0, obj_inst); - store_object->SetInput(1, intrinsic->GetInput(1).GetInst()); - store_object->SetTypeId(offset); - store_object->SetMethod(save_state->GetMethod()); - store_object->SetObjField(nullptr); - store_object->SetObjectType(ObjectType::MEM_DYNAMIC); - store_object->SetNeedBarrier(true); - insert_after->InsertAfter(store_object); - - return store_object; - } - } else { - auto load_object = graph->CreateInstLoadObject(DataType::REFERENCE, pc); + if constexpr (IS_LOAD) { + auto load_object = graph->CreateInstLoadObject(DataType::ANY, pc); + load_object->SetInput(0, obj_inst); + load_object->SetTypeId(offset); load_object->SetMethod(save_state->GetMethod()); load_object->SetObjField(nullptr); - load_object->SetTypeId(0); - load_object->SetObjectType(ObjectType::MEM_PROPERTIES); - load_object->SetInput(0, obj_inst); + load_object->SetObjectType(ObjectType::MEM_DYN_INLINED); insert_after->InsertAfter(load_object); - Inst *array = nullptr; - if constexpr (IS_LOAD) { - auto load_array = graph->CreateInstLoadArray(DataType::ANY, pc); - load_array->SetInput(0, load_object); - load_array->SetInput(1, graph->FindOrCreateConstant(offset)); - array = load_array; - } else { - auto store_array = graph->CreateInstStoreArray(DataType::ANY, pc); - store_array->SetInput(0, load_object); - store_array->SetInput(1, graph->FindOrCreateConstant(offset)); - store_array->SetInput(2, intrinsic->GetInput(1).GetInst()); - store_array->SetNeedBarrier(true); - array = store_array; - } - load_object->InsertAfter(array); + return load_object; + } else { + auto store_object = graph->CreateInstStoreObject(DataType::ANY, pc); + store_object->SetInput(0, obj_inst); + store_object->SetInput(1, intrinsic->GetInput(1).GetInst()); + store_object->SetTypeId(offset); + store_object->SetMethod(save_state->GetMethod()); + store_object->SetObjField(nullptr); + store_object->SetObjectType(ObjectType::MEM_DYN_INLINED); + store_object->SetNeedBarrier(true); + insert_after->InsertAfter(store_object); + + return store_object; + } +} + +template +Inst *InsertMemFromField(IntrinsicInst *intrinsic, Inst *obj_inst, Inst *insert_after, uint32_t offset) +{ + auto graph = obj_inst->GetBasicBlock()->GetGraph(); + auto save_state = intrinsic->GetSaveState(); + auto pc = obj_inst->GetPc(); + auto load_object = graph->CreateInstLoadObject(DataType::REFERENCE, pc); + load_object->SetMethod(save_state->GetMethod()); + load_object->SetObjField(nullptr); + load_object->SetTypeId(TypeIdMixin::MEM_DYN_PROPS_ID); + load_object->SetObjectType(ObjectType::MEM_DYN_PROPS); + load_object->SetInput(0, obj_inst); + insert_after->InsertAfter(load_object); + insert_after = load_object; + Inst *index = graph->FindOrCreateConstant(offset); + if constexpr (INSERT_BOUNDS_CHECK) { + // Create LenArray instruction + auto array_length = graph->CreateInstLenArray(DataType::INT32, pc); + array_length->SetInput(0, load_object); + + // Create BoundCheck instruction + auto bounds_check = graph->CreateInstBoundsCheck(DataType::INT32, pc); + bounds_check->SetInput(0, array_length); + bounds_check->SetInput(2U, save_state); + bounds_check->SetInput(1, index); + bounds_check->SetFlag(inst_flags::CAN_DEOPTIMIZE); + insert_after->InsertAfter(bounds_check); + insert_after->InsertAfter(array_length); + insert_after = bounds_check; + + index = bounds_check; + } + + Inst *array = nullptr; + if constexpr (IS_LOAD) { + auto load_array = graph->CreateInstLoadArray(DataType::ANY, pc); + load_array->SetInput(0, load_object); + load_array->SetInput(1, index); + array = load_array; + } else { + auto store_array = graph->CreateInstStoreArray(DataType::ANY, pc); + store_array->SetInput(0, load_object); + store_array->SetInput(1, index); + store_array->SetInput(2, intrinsic->GetInput(1).GetInst()); + store_array->SetNeedBarrier(true); + array = store_array; + } + insert_after->InsertAfter(array); + + return array; +} - return array; +Inst *InsertChangeClassInst(IntrinsicInst *intrinsic, Inst *obj_inst, Inst *insert_after, uintptr_t klass_addr) +{ + auto graph = obj_inst->GetBasicBlock()->GetGraph(); + auto pc = obj_inst->GetPc(); + auto klass = reinterpret_cast(klass_addr); + auto load_cls_inst = graph->CreateInstClassImmediate(DataType::REFERENCE, pc, klass); + auto store_class = graph->CreateInstStoreObject(DataType::REFERENCE, pc); + store_class->SetMethod(intrinsic->GetSaveState()->GetMethod()); + store_class->SetObjField(nullptr); + store_class->SetTypeId(TypeIdMixin::MEM_DYN_CLASS_ID); + store_class->SetObjectType(ObjectType::MEM_DYN_CLASS); + store_class->SetInput(0, obj_inst); + store_class->SetInput(1, load_cls_inst); + store_class->SetNeedBarrier(true); + insert_after->InsertAfter(store_class); + insert_after->InsertAfter(load_cls_inst); + return store_class; +} + +Inst *InsertPrototypeCheckInst(IntrinsicInst *intrinsic, uintptr_t proto_mem, Inst **insert_after) +{ + auto graph = intrinsic->GetBasicBlock()->GetGraph(); + auto save_state = intrinsic->GetSaveState(); + auto pc = intrinsic->GetPc(); + // Get Prototype handler + auto proto_handler = graph->CreateInstLoadObjFromConst(DataType::REFERENCE, pc, proto_mem); + (*insert_after)->InsertAfter(proto_handler); + + // Get Prototype object + auto prototype = graph->CreateInstLoadObject(DataType::REFERENCE, pc); + prototype->SetMethod(save_state->GetMethod()); + prototype->SetObjField(nullptr); + prototype->SetTypeId(TypeIdMixin::MEM_DYN_PROTO_HOLDER_ID); + prototype->SetObjectType(ObjectType::MEM_DYN_PROTO_HOLDER); + prototype->SetInput(0, proto_handler); + proto_handler->InsertAfter(prototype); + + // Get Prototype change marker + auto prototype_marker = graph->CreateInstLoadObject(DataType::REFERENCE, pc); + prototype_marker->SetMethod(save_state->GetMethod()); + prototype_marker->SetObjField(nullptr); + prototype_marker->SetTypeId(TypeIdMixin::MEM_DYN_PROTO_CELL_ID); + prototype_marker->SetObjectType(ObjectType::MEM_DYN_PROTO_CELL); + prototype_marker->SetInput(0, proto_handler); + prototype->InsertAfter(prototype_marker); + + // Get change field from the marker + auto is_change = graph->CreateInstLoadObject(DataType::BOOL, pc); + is_change->SetMethod(save_state->GetMethod()); + is_change->SetObjField(nullptr); + is_change->SetTypeId(TypeIdMixin::MEM_DYN_CHANGE_FIELD_ID); + is_change->SetObjectType(ObjectType::MEM_DYN_CHANGE_FIELD); + is_change->SetInput(0, prototype_marker); + prototype_marker->InsertAfter(is_change); + + auto deopt_inst = graph->CreateInstDeoptimizeIf(DataType::NO_TYPE, pc); + deopt_inst->SetDeoptimizeType(DeoptimizeType::INLINE_IC); + deopt_inst->SetInput(0, is_change); + deopt_inst->SetSaveState(save_state); + + is_change->InsertAfter(deopt_inst); + + *insert_after = deopt_inst; + return prototype; +} + +template +Inst *InsertAccessByNameInst(IntrinsicInst *intrinsic, Inst *obj_inst, Inst *insert_after, + RuntimeInterface::NamedAccessProfileData profile) +{ + uint32_t offset = profile.offset; + RuntimeInterface::NamedAccessProfileType type = profile.type; + switch (type) { + case RuntimeInterface::NamedAccessProfileType::FIELD_INLINED: + return InsertMemFromFieldInlined(intrinsic, obj_inst, insert_after, offset); + case RuntimeInterface::NamedAccessProfileType::FIELD: + return InsertMemFromField(intrinsic, obj_inst, insert_after, offset); + case RuntimeInterface::NamedAccessProfileType::TRANSITION_INLINED: { + ASSERT(!IS_LOAD); + auto store_class = InsertChangeClassInst(intrinsic, obj_inst, insert_after, profile.cached_value); + return InsertMemFromFieldInlined(intrinsic, obj_inst, store_class, offset); + } + case RuntimeInterface::NamedAccessProfileType::TRANSITION: { + ASSERT(!IS_LOAD); + auto store = InsertMemFromField(intrinsic, obj_inst, insert_after, offset); + InsertChangeClassInst(intrinsic, obj_inst, store->GetPrev(), profile.cached_value); + return store; + } + case RuntimeInterface::NamedAccessProfileType::PROTOTYPE_INLINED: { + auto load_proto = InsertPrototypeCheckInst(intrinsic, profile.cached_value, &insert_after); + return InsertMemFromFieldInlined(intrinsic, load_proto, insert_after, offset); + } + case RuntimeInterface::NamedAccessProfileType::PROTOTYPE: { + auto load_proto = InsertPrototypeCheckInst(intrinsic, profile.cached_value, &insert_after); + return InsertMemFromField(intrinsic, load_proto, insert_after, offset); + } + default: + UNREACHABLE(); } } @@ -240,7 +365,7 @@ bool TypesResolving::GetICForMemAccess(IntrinsicInst *intrinsic) ASSERT(caller_inst->IsInlined()); auto func = caller_inst->GetFunctionObject(); ASSERT(func != 0); - if (!runtime->GetProfileDataForNamedAccess(func, pc, &named_access_profile_)) { + if (!runtime->GetProfileDataForNamedAccess(GetGraph()->GetMethod(), func, pc, &named_access_profile_)) { return false; } } else if (!runtime->GetProfileDataForNamedAccess(GetGraph()->GetMethod(), pc, &named_access_profile_)) { @@ -258,13 +383,13 @@ Inst *TypesResolving::InsertCheckAndCastInstructions(IntrinsicInst *intrinsic) auto any_check = GetGraph()->CreateInstAnyTypeCheck(DataType::ANY, pc); any_check->SetInput(0, obj_inst); any_check->SetInput(1, save_state); - any_check->SetAnyType(AnyBaseType::ECMASCRIPT_OBJECT_TYPE); + any_check->SetAnyType(AnyBaseType::ECMASCRIPT_HEAP_OBJECT_TYPE); intrinsic->InsertBefore(any_check); auto cast_value = GetGraph()->CreateInstCastAnyTypeValue(DataType::REFERENCE, pc); cast_value->SetInput(0, any_check); // NOLINTNEXTLINE(readability-magic-numbers) - cast_value->SetAnyType(AnyBaseType::ECMASCRIPT_OBJECT_TYPE); + cast_value->SetAnyType(AnyBaseType::ECMASCRIPT_HEAP_OBJECT_TYPE); cast_value->SetFlag(inst_flags::NO_HOIST); any_check->InsertAfter(cast_value); return cast_value; @@ -275,9 +400,14 @@ void TypesResolving::InlineObjByNameMonomorphic(IntrinsicInst *intrinsic, Inst * { auto pc = intrinsic->GetPc(); auto save_state = intrinsic->GetSaveState(); - auto deopt = CreateCompareClassWithDeopt(pc, get_cls_inst, named_access_profile_[0].klass, save_state); - [[maybe_unused]] auto load_inst = InsertMemByNameInst( - intrinsic, obj_inst, deopt, named_access_profile_[0].offset, named_access_profile_[0].is_inlined); + auto prof = named_access_profile_[0]; + auto deopt = CreateCompareClassWithDeopt(pc, get_cls_inst, prof.klass, save_state); + Inst *obj = obj_inst; + if (prof.type == RuntimeInterface::NamedAccessProfileType::PROTOTYPE || + prof.type == RuntimeInterface::NamedAccessProfileType::PROTOTYPE_INLINED) { + obj = get_cls_inst; + } + [[maybe_unused]] auto load_inst = InsertAccessByNameInst(intrinsic, obj, deopt, prof); if constexpr (IS_LOAD) { intrinsic->ReplaceUsers(load_inst); @@ -320,9 +450,13 @@ void TypesResolving::InlineObjByNamePolymorphic(IntrinsicInst *intrinsic, Inst * load_bb->AddSucc(load_inserting_block); auto last_ss = CopySaveState(GetGraph(), save_state); load_inserting_block->PrependInst(last_ss); + Inst *obj = obj_inst; + if (prof.type == RuntimeInterface::NamedAccessProfileType::PROTOTYPE || + prof.type == RuntimeInterface::NamedAccessProfileType::PROTOTYPE_INLINED) { + obj = get_cls_inst; + } - [[maybe_unused]] auto load = - InsertMemByNameInst(intrinsic, obj_inst, last_ss, prof.offset, prof.is_inlined); + [[maybe_unused]] auto load = InsertAccessByNameInst(intrinsic, obj, last_ss, prof); if constexpr (IS_LOAD) { phi_inst->AppendInput(load); @@ -356,7 +490,11 @@ bool TypesResolving::InlineObjByName(IntrinsicInst *intrinsic) auto cast_value = InsertCheckAndCastInstructions(intrinsic); - auto get_cls_inst = GetGraph()->CreateInstGetInstanceClass(DataType::REFERENCE, pc); + auto get_cls_inst = GetGraph()->CreateInstLoadObject(DataType::REFERENCE, pc); + get_cls_inst->SetMethod(intrinsic->GetSaveState()->GetMethod()); + get_cls_inst->SetObjField(nullptr); + get_cls_inst->SetTypeId(TypeIdMixin::MEM_DYN_CLASS_ID); + get_cls_inst->SetObjectType(ObjectType::MEM_DYN_CLASS); get_cls_inst->SetInput(0, cast_value); cast_value->InsertAfter(get_cls_inst); if (named_access_profile_.size() == 1) { diff --git a/compiler/optimizer/ir_builder/ecmascript_inst_builder.cpp b/compiler/optimizer/ir_builder/ecmascript_inst_builder.cpp index 4d84a656d..4920f1970 100644 --- a/compiler/optimizer/ir_builder/ecmascript_inst_builder.cpp +++ b/compiler/optimizer/ir_builder/ecmascript_inst_builder.cpp @@ -172,11 +172,11 @@ void InstBuilder::BuildStGlobalVar(const BytecodeInstruction *bc_inst, size_t ty get_address->SetInput(0, save_state); auto store_object = graph_->CreateInstStoreObject(DataType::ANY, pc); - store_object->SetTypeId(type_id); + store_object->SetTypeId(TypeIdMixin::MEM_DYN_GLOBAL_ID); store_object->SetMethod(GetGraph()->GetMethod()); store_object->SetObjField(nullptr); store_object->SetNeedBarrier(true); - store_object->SetObjectType(ObjectType::MEM_GLOBAL); + store_object->SetObjectType(ObjectType::MEM_DYN_GLOBAL); store_object->SetInput(0, get_address); Inst *store_val = nullptr; if constexpr (IS_ACC_READ) { @@ -203,10 +203,10 @@ void InstBuilder::BuildLdGlobalVar(const BytecodeInstruction *bc_inst, size_t ty get_address->SetInput(0, save_state); auto load_object = graph_->CreateInstLoadObject(DataType::ANY, pc); - load_object->SetTypeId(type_id); + load_object->SetTypeId(TypeIdMixin::MEM_DYN_GLOBAL_ID); load_object->SetMethod(GetGraph()->GetMethod()); load_object->SetObjField(nullptr); - load_object->SetObjectType(ObjectType::MEM_GLOBAL); + load_object->SetObjectType(ObjectType::MEM_DYN_GLOBAL); load_object->SetInput(0, get_address); AddInstruction(save_state); AddInstruction(get_address); @@ -257,8 +257,8 @@ void InstBuilder::BuildLdStObjByIndex(const BytecodeInstruction *bc_inst, Inst * auto load_object = graph_->CreateInstLoadObject(DataType::REFERENCE, pc); load_object->SetMethod(GetGraph()->GetMethod()); load_object->SetObjField(nullptr); - load_object->SetTypeId(0); - load_object->SetObjectType(ObjectType::MEM_ELEMENTS); + load_object->SetTypeId(TypeIdMixin::MEM_DYN_ELEMENTS_ID); + load_object->SetObjectType(ObjectType::MEM_DYN_ELEMENTS); load_object->SetInput(0, check_obj); Inst *save_state = nullptr; diff --git a/runtime/asm_defines/asm_defines.def b/runtime/asm_defines/asm_defines.def index 838c5dde3..c1093baf8 100644 --- a/runtime/asm_defines/asm_defines.def +++ b/runtime/asm_defines/asm_defines.def @@ -44,6 +44,8 @@ DEFINE_VALUE(JSHCLASS_BITFIELD_INLINED_PROPS_START_BITS_MASK, panda::ecmascript: DEFINE_VALUE(JSHCLASS_OBJECT_SIZE_OFFSET, panda::ecmascript::JSHClass::OBJECT_SIZE_OFFSET) DEFINE_VALUE(JSHCLASS_HCLASS_OFFSET, panda::ecmascript::JSHClass::GetHClassOffset()) DEFINE_VALUE(JSHCLASS_LAYOUT_OFFSET, panda::ecmascript::JSHClass::GetLayoutOffset()) +DEFINE_VALUE(JSHCLASS_PROTO_OFFSET, panda::ecmascript::JSHClass::GetProtoOffset()) +DEFINE_VALUE(JSHCLASS_PROTO_CHANGE_MARKER_OFFSET, panda::ecmascript::JSHClass::GetProtoChangeMarkerOffset()) DEFINE_VALUE(JSHCLASS_BITFIELD1_OFFSET, panda::ecmascript::JSHClass::BIT_FIELD1_OFFSET) DEFINE_VALUE(JSHCLASS_BITFIELD1_NUMBER_OF_PROPS_BITS_START_BIT, panda::ecmascript::JSHClass::NumberOfPropsBits::START_BIT) DEFINE_VALUE(JSHCLASS_BITFIELD1_NUMBER_OF_PROPS_BITS_MASK, panda::ecmascript::JSHClass::NumberOfPropsBits::MaxValue()) diff --git a/runtime/class_linker/panda_file_translator.cpp b/runtime/class_linker/panda_file_translator.cpp index 549adbc91..669faa6fb 100644 --- a/runtime/class_linker/panda_file_translator.cpp +++ b/runtime/class_linker/panda_file_translator.cpp @@ -518,7 +518,18 @@ void PandaFileTranslator::UpdateICOffset(JSMethod *method, uint32_t ins_sz, cons auto opcode = inst.GetOpcode(); uint32_t bc_offset = inst.GetAddress() - method->GetInstructions(); uint32_t slot_size = 0; + using ICMappingType = JSMethod::ICMappingType; + static_assert(std::is_pointer::value); + using ICMappingElementType = std::remove_pointer::type; + ICMappingType ic_mapping = method->GetICMapping(); + if (method->GetSlotSize() > std::numeric_limits::max()) { + if (ic_mapping != nullptr) { + mem::InternalAllocator<>::GetInternalAllocatorFromRuntime()->Free(ic_mapping); + method->SetICMapping(nullptr); + } + return; + } switch (opcode) { case R::template Get(): case R::template Get(): @@ -539,10 +550,7 @@ void PandaFileTranslator::UpdateICOffset(JSMethod *method, uint32_t ins_sz, cons default: return; } - using ICMappingType = JSMethod::ICMappingType; - static_assert(std::is_pointer::value); - using ICMappingElementType = std::remove_pointer::type; - ICMappingType ic_mapping = method->GetICMapping(); + if (ic_mapping == nullptr && ins_sz < ProfileTypeInfo::MAX_FUNCTION_SIZE) { ic_mapping = mem::InternalAllocator<>::GetInternalAllocatorFromRuntime()->AllocArray(ins_sz); diff --git a/runtime/compiler/ecmascript_runtime_interface.cpp b/runtime/compiler/ecmascript_runtime_interface.cpp index d29dc8053..1c370bd6e 100644 --- a/runtime/compiler/ecmascript_runtime_interface.cpp +++ b/runtime/compiler/ecmascript_runtime_interface.cpp @@ -26,11 +26,22 @@ #include "plugins/ecmascript/runtime/js_hclass-inl.h" #include "plugins/ecmascript/runtime/ic/ic_handler.h" #include "plugins/ecmascript/runtime/ic/profile_type_info.h" +#include "plugins/ecmascript/runtime/ic/proto_change_details.h" #include "runtime/mem/refstorage/global_object_storage.h" namespace panda::ecmascript { -EcmaRuntimeInterface::EcmaRuntimeInterface(const EcmaVM *ecma_vm) : ecma_vm_(ecma_vm) {} +EcmaRuntimeInterface::EcmaRuntimeInterface(const EcmaVM *ecma_vm, mem::InternalAllocatorPtr internal_allocator) + : ecma_vm_(ecma_vm), internal_allocator_(internal_allocator) +{ +} +EcmaRuntimeInterface::~EcmaRuntimeInterface() +{ + os::memory::LockHolder lock(mutex_h_); + for (auto it : handles_by_method_table_) { + internal_allocator_->template Delete(it.second); + } +} size_t EcmaRuntimeInterface::GetLanguageExtensionSize([[maybe_unused]] Arch arch) const { @@ -268,30 +279,87 @@ RuntimeInterface::NewObjDynInfo EcmaRuntimeInterface::GetNewObjDynInfo(uintptr_t return {NewObjDynInfo::AllocatorType::ALLOC_OBJ, NewObjDynInfo::ResolverType::RESOLVE}; } -bool EcmaRuntimeInterface::AddProfileInfo(ArenaVector *profile, +bool EcmaRuntimeInterface::AddProfileInfo(PandaRuntimeInterface::MethodPtr m, + ArenaVector *profile, ProfileTypeInfo *profile_type_info, uint8_t slot) { JSTaggedValue hclass_value = profile_type_info->Get(slot); if (!hclass_value.IsHeapObject()) { return false; } + auto klass = static_cast(hclass_value.GetHeapObject())->GetHClass(); JSTaggedValue handler = profile_type_info->Get(slot + 1); if (UNLIKELY(handler.IsUndefined())) { return false; } + auto method = MethodCast(m); if (LIKELY(handler.IsInt())) { auto handler_info = static_cast(handler.GetInt()); if (LIKELY(HandlerBase::IsField(handler_info))) { + if (HandlerBase::IsArrayOverflowed(handler_info)) { + return false; + } int index = HandlerBase::GetOffset(handler_info); bool is_inlined = HandlerBase::IsInlinedProps(handler_info); + NamedAccessProfileType type = + is_inlined ? NamedAccessProfileType::FIELD_INLINED : NamedAccessProfileType::FIELD; auto offset = is_inlined ? static_cast(index * JSTaggedValue::TaggedTypeSize()) : static_cast(index); - auto klass = static_cast(hclass_value.GetHeapObject())->GetHClass(); - profile->push_back({klass, offset, is_inlined}); + AddObjectHandle(method, hclass_value.GetHeapObject()); + profile->push_back({klass, 0, offset, type}); return true; } return false; } + if (handler.IsTransitionHandler()) { + TransitionHandler *transition_handler = TransitionHandler::Cast(handler.GetTaggedObject()); + JSHClass *new_h_class = JSHClass::Cast(transition_handler->GetTransitionHClass().GetTaggedObject()); + uint32_t handler_info = transition_handler->GetHandlerInfo().GetInt(); + ASSERT(HandlerBase::IsField(handler_info)); + if (HandlerBase::IsArrayOverflowed(handler_info)) { + return false; + } + int index = HandlerBase::GetOffset(handler_info); + bool is_inlined = HandlerBase::IsInlinedProps(handler_info); + NamedAccessProfileType type = + is_inlined ? NamedAccessProfileType::TRANSITION_INLINED : NamedAccessProfileType::TRANSITION; + auto offset = + is_inlined ? static_cast(index * JSTaggedValue::TaggedTypeSize()) : static_cast(index); + AddObjectHandle(method, hclass_value.GetHeapObject()); + AddObjectHandle(method, new_h_class); + profile->push_back({klass, reinterpret_cast(new_h_class->GetHClass()), offset, type}); + return true; + } + if (handler.IsPrototypeHandler()) { + PrototypeHandler *prototype_handler = PrototypeHandler::Cast(handler.GetTaggedObject()); + auto cell_value = prototype_handler->GetProtoCell(); + ASSERT(cell_value.IsProtoChangeMarker()); + ProtoChangeMarker *cell = ProtoChangeMarker::Cast(cell_value.GetHeapObject()); + if (cell->GetHasChanged()) { + return false; + } + JSTaggedValue handler_value = prototype_handler->GetHandlerInfo(); + if (!handler_value.IsInt()) { + return false; + } + uint32_t handler_info = handler_value.GetInt(); + if (!HandlerBase::IsField(handler_info)) { + return false; + } + int index = HandlerBase::GetOffset(handler_info); + bool is_inlined = HandlerBase::IsInlinedProps(handler_info); + NamedAccessProfileType type = + is_inlined ? NamedAccessProfileType::PROTOTYPE_INLINED : NamedAccessProfileType::PROTOTYPE; + auto offset = + is_inlined ? static_cast(index * JSTaggedValue::TaggedTypeSize()) : static_cast(index); + auto ref_addr = AddFixedObjectHandle(method, handler.GetHeapObject()); + if (ref_addr == 0) { + return false; + } + AddObjectHandle(method, hclass_value.GetHeapObject()); + profile->push_back({klass, ref_addr, offset, type}); + return true; + } return false; } @@ -317,15 +385,15 @@ bool EcmaRuntimeInterface::GetProfileDataForNamedAccess(PandaRuntimeInterface::M auto func = JSFunction::Cast(ecma_vm_->GetGlobalObjectStorage()->Get(it->second)); ProfileTypeInfo *profile_type_info = ProfileTypeInfo::Cast(func->GetProfileTypeInfo().GetTaggedObject()); - if (AddProfileInfo(profile, profile_type_info, slot)) { - AddProfileInfo(profile, profile_type_info, slot + 2); + if (AddProfileInfo(m, profile, profile_type_info, slot)) { + AddProfileInfo(m, profile, profile_type_info, slot + 2); return true; } return false; } -bool EcmaRuntimeInterface::GetProfileDataForNamedAccess(uintptr_t func_address, uintptr_t slot_id, - ArenaVector *profile) +bool EcmaRuntimeInterface::GetProfileDataForNamedAccess(PandaRuntimeInterface::MethodPtr m, uintptr_t func_address, + uintptr_t slot_id, ArenaVector *profile) { if (profile == nullptr) { return false; @@ -353,8 +421,8 @@ bool EcmaRuntimeInterface::GetProfileDataForNamedAccess(uintptr_t func_address, } ProfileTypeInfo *profile_type_info = ProfileTypeInfo::Cast(profile_info_obj.GetTaggedObject()); - if (AddProfileInfo(profile, profile_type_info, slot)) { - AddProfileInfo(profile, profile_type_info, slot + 2); + if (AddProfileInfo(m, profile, profile_type_info, slot)) { + AddProfileInfo(m, profile, profile_type_info, slot + 2); return true; } return false; @@ -389,4 +457,75 @@ size_t EcmaRuntimeInterface::GetTaggedArrayElementSize() const return JSTaggedValue::TaggedTypeSize(); } +void EcmaRuntimeInterface::CleanObjectHandles(Method *method) +{ + os::memory::LockHolder lock(mutex_h_); + auto it = handles_by_method_table_.find(method); + auto gos = ecma_vm_->GetGlobalObjectStorage(); + if (it == handles_by_method_table_.end()) { + return; + } + auto vector = it->second; + // Find handles for the method + for (auto *ref : *vector) { + gos->Remove(ref); + } + handles_by_method_table_.erase(it); + internal_allocator_->template Delete(vector); +} + +void EcmaRuntimeInterface::AddObjectHandle(Method *method, ObjectHeader *obj) +{ + os::memory::LockHolder lock(mutex_h_); + auto it = handles_by_method_table_.find(method); + auto gos = ecma_vm_->GetGlobalObjectStorage(); + // Create new Vector for the method + if (it == handles_by_method_table_.end()) { + auto vector = internal_allocator_->template New>(); + auto func_ref = gos->Add(obj, panda::mem::Reference::ObjectType::GLOBAL); + vector->push_back(func_ref); + handles_by_method_table_.insert({method, vector}); + return; + } + auto vector = it->second; + // Find the object in the vector + for (auto *ref : *vector) { + if (obj == gos->Get(ref)) { + return; + } + } + auto func_ref = gos->Add(obj, panda::mem::Reference::ObjectType::GLOBAL); + vector->push_back(func_ref); +} + +uintptr_t EcmaRuntimeInterface::AddFixedObjectHandle(Method *method, ObjectHeader *obj) +{ + os::memory::LockHolder lock(mutex_h_); + auto it = handles_by_method_table_.find(method); + auto gos = ecma_vm_->GetGlobalObjectStorage(); + // Create new Vector for the method + if (it == handles_by_method_table_.end()) { + auto vector = internal_allocator_->template New>(); + auto func_ref = gos->Add(obj, panda::mem::Reference::ObjectType::GLOBAL_FIXED); + if (func_ref == nullptr) { + return 0; + } + vector->push_back(func_ref); + handles_by_method_table_.insert({method, vector}); + return gos->GetAddressForRef(func_ref); + } + auto vector = it->second; + // Find the object in the vector + for (auto *ref : *vector) { + if (obj == gos->Get(ref)) { + return gos->GetAddressForRef(ref); + } + } + auto func_ref = gos->Add(obj, panda::mem::Reference::ObjectType::GLOBAL_FIXED); + if (func_ref == nullptr) { + return 0; + } + vector->push_back(func_ref); + return gos->GetAddressForRef(func_ref); +} } // namespace panda::ecmascript diff --git a/runtime/compiler/ecmascript_runtime_interface.h b/runtime/compiler/ecmascript_runtime_interface.h index 7886a037a..2e9134b44 100644 --- a/runtime/compiler/ecmascript_runtime_interface.h +++ b/runtime/compiler/ecmascript_runtime_interface.h @@ -31,7 +31,10 @@ enum class ObjectFieldType : uint8_t { INVALID = 0, ELEMENTS, COUNT }; class EcmaRuntimeInterface : public PandaRuntimeInterface { public: - explicit EcmaRuntimeInterface(const EcmaVM *ecma_vm); + explicit EcmaRuntimeInterface(const EcmaVM *ecma_vm, mem::InternalAllocatorPtr internal_allocator); + ~EcmaRuntimeInterface() override; + NO_COPY_SEMANTIC(EcmaRuntimeInterface); + NO_MOVE_SEMANTIC(EcmaRuntimeInterface); ecmascript::JSMethod *JsMethodCast(RuntimeInterface::MethodPtr method) const { @@ -70,6 +73,26 @@ public: return panda::cross_values::GetJsobjectPropertiesOffset(arch); } + size_t GetHClassOffset(Arch arch) const override + { + return panda::cross_values::GetJshclassHclassOffset(arch); + } + + size_t GetPrototypeHolderOffset(Arch arch) const override + { + return cross_values::GetJsprototypeHandlerHolderOffset(arch); + } + + size_t GetPrototypeCellOffset(Arch arch) const override + { + return cross_values::GetJsprototypeHandlerProtoCellOffset(arch); + } + + size_t GetIsChangeFieldOffset(Arch arch) const override + { + return panda::cross_values::GetJsprotoChangeMarkerHasChangedOffset(arch); + } + EntrypointId GetGlobalVarEntrypointId() override { return EntrypointId::GET_GLOBAL_VAR_ADDRESS; @@ -88,7 +111,9 @@ public: std::string GetClassName([[maybe_unused]] ClassPtr klass) const override { if (static_cast(klass)->IsDynamicClass()) { - return "JSHClass"; + std::string str = std::to_string(reinterpret_cast(klass)); + + return "JSHClass " + str; } ScopedMutatorLock lock; return ClassCast(klass)->GetName(); @@ -122,7 +147,7 @@ public: NewObjDynInfo GetNewObjDynInfo(uintptr_t ctor) const override; bool GetProfileDataForNamedAccess(PandaRuntimeInterface::MethodPtr m, uintptr_t slot_id, ArenaVector *profile) override; - bool GetProfileDataForNamedAccess(uintptr_t func_address, uintptr_t slot_id, + bool GetProfileDataForNamedAccess(PandaRuntimeInterface::MethodPtr m, uintptr_t func_address, uintptr_t slot_id, ArenaVector *profile) override; void CleanFunction(Method *method); @@ -133,7 +158,17 @@ public: js_function_table_.insert({method, func}); } - bool AddProfileInfo(ArenaVector *profile, ProfileTypeInfo *profile_type_info, uint8_t slot); + void CleanObjectHandles(PandaRuntimeInterface::MethodPtr method) override + { + CleanObjectHandles(MethodCast(method)); + } + +private: + void CleanObjectHandles(Method *method); + void AddObjectHandle(Method *method, ObjectHeader *obj); + uintptr_t AddFixedObjectHandle(Method *method, ObjectHeader *obj); + bool AddProfileInfo(PandaRuntimeInterface::MethodPtr m, ArenaVector *profile, + ProfileTypeInfo *profile_type_info, uint8_t slot); size_t GetLexicalEnvParentEnvIndex() const override; @@ -143,9 +178,12 @@ public: private: const EcmaVM *ecma_vm_ {nullptr}; + mem::InternalAllocatorPtr internal_allocator_; panda::ecmascript::EcmaProfileContainer profile_; PandaUnorderedMap js_function_table_ GUARDED_BY(mutex_); + PandaUnorderedMap *> handles_by_method_table_ GUARDED_BY(mutex_h_); mutable os::memory::Mutex mutex_; + mutable os::memory::Mutex mutex_h_; }; } // namespace panda::ecmascript diff --git a/runtime/ecma_vm.cpp b/runtime/ecma_vm.cpp index 2e496f896..63295d348 100644 --- a/runtime/ecma_vm.cpp +++ b/runtime/ecma_vm.cpp @@ -188,7 +188,7 @@ EcmaVM::EcmaVM(JSRuntimeOptions options) : string_table_(new EcmaStringTable(thi auto heap_manager = mm_->GetHeapManager(); auto internal_allocator = heap_manager->GetInternalAllocator(); - runtime_iface_ = internal_allocator->New(this); + runtime_iface_ = internal_allocator->New(this, internal_allocator); compiler_ = internal_allocator->New(heap_manager->GetCodeAllocator(), internal_allocator, options_, heap_manager->GetMemStats(), runtime_iface_); SetFrameExtSize(ecmascript::JSExtFrame::GetExtSize()); diff --git a/runtime/ic/ic_handler.h b/runtime/ic/ic_handler.h index a8a3d8bd2..aa090573e 100644 --- a/runtime/ic/ic_handler.h +++ b/runtime/ic/ic_handler.h @@ -37,7 +37,8 @@ public: using AccessorBit = InlinedPropsBit::NextFlag; using InternalAccessorBit = AccessorBit::NextFlag; using IsJSArrayBit = InternalAccessorBit::NextFlag; - using OffsetBit = IsJSArrayBit::NextField; + using ObservedOverflowArrayBit = IsJSArrayBit::NextFlag; + using OffsetBit = ObservedOverflowArrayBit::NextField; HandlerBase() = default; virtual ~HandlerBase() = default; @@ -90,6 +91,16 @@ public: return IsJSArrayBit::Get(handler); } + static inline bool IsArrayOverflowed(uint32_t handler) + { + return ObservedOverflowArrayBit::Get(handler); + } + + static inline void SetArrayOverflowed(uint32_t &handler) + { + ObservedOverflowArrayBit::Set(true, &handler); + } + static inline int GetOffset(uint32_t handler) { return OffsetBit::Get(handler); @@ -117,6 +128,12 @@ public: } static inline JSHandle StoreTransition(const JSThread *thread, const ObjectOperator &op); + inline void SetArrayOverflowed(const JSThread *thread) + { + uint32_t info = GetHandlerInfo().GetInt(); + HandlerBase::SetArrayOverflowed(info); + SetHandlerInfo(thread, JSHandle(thread, JSTaggedValue(info))); + } ACCESSORS_START(TaggedObjectSize()) ACCESSORS(0, HandlerInfo) diff --git a/runtime/ic/ic_runtime_stub-inl.h b/runtime/ic/ic_runtime_stub-inl.h index 2078b4b9d..95b35f571 100644 --- a/runtime/ic/ic_runtime_stub-inl.h +++ b/runtime/ic/ic_runtime_stub-inl.h @@ -347,6 +347,7 @@ void ICRuntimeStub::StoreWithTransition(JSThread *thread, JSObject *receiver, JS int index = HandlerBase::GetOffset(handler_info); if (index >= capacity) { [[maybe_unused]] EcmaHandleScope handle_scope(thread); + transition_handler->SetArrayOverflowed(thread); ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); JSHandle properties; JSHandle obj_handle(thread, receiver); diff --git a/runtime/ic/profile_type_info.h b/runtime/ic/profile_type_info.h index 1997f456b..35fd01e24 100644 --- a/runtime/ic/profile_type_info.h +++ b/runtime/ic/profile_type_info.h @@ -87,7 +87,7 @@ class ProfileTypeInfo : public TaggedArray { public: static const uint32_t MAX_FUNC_CACHE_INDEX = std::numeric_limits::max(); // Maximal function's bytecode size in byte to create IC for it. - static constexpr uint32_t MAX_FUNCTION_SIZE = 512; + static constexpr uint32_t MAX_FUNCTION_SIZE = 1024; static ProfileTypeInfo *Cast(TaggedObject *object) { diff --git a/runtime/interpreter/slow_runtime_stub.cpp b/runtime/interpreter/slow_runtime_stub.cpp index 0a9b3bc33..a8713b832 100644 --- a/runtime/interpreter/slow_runtime_stub.cpp +++ b/runtime/interpreter/slow_runtime_stub.cpp @@ -2260,10 +2260,7 @@ JSTaggedValue SlowRuntimeStub::NotifyInlineCache(JSThread *thread, JSFunction *f } uint32_t ic_slot_size = method->GetSlotSize(); static_assert(std::is_pointer::value); - using ICMappingElementType = std::remove_pointer::type; - if (ic_slot_size > std::numeric_limits::max()) { - return JSTaggedValue::Undefined(); - } + ASSERT(ic_slot_size <= std::numeric_limits::type>::max()); ObjectFactory *factory = thread->GetEcmaVM()->GetFactory(); [[maybe_unused]] EcmaHandleScope handle_scope(thread); diff --git a/tests/checked/obj_by_name.js b/tests/checked/obj_by_name.js index 5096f4b63..667630350 100644 --- a/tests/checked/obj_by_name.js +++ b/tests/checked/obj_by_name.js @@ -15,54 +15,106 @@ //! CHECKER Test check store and load by index //! RUN options: "--no-async-jit --compiler-hotness-threshold=10 --compiler-regex _GLOBAL::test_load_store_by_name.*", entry: "_GLOBAL::func_main_0" -//! METHOD "test_load_store_by_name_single_inlined" +//! METHOD "test_load_store_by_name_single_field_inlined" //! PASS_AFTER "TypesResolving" -//! INST "AnyTypeCheck ECMASCRIPT_OBJECT_TYPE" +//! INST "AnyTypeCheck ECMASCRIPT_HEAP_OBJECT_TYPE" //! INST_NEXT "CastAnyTypeValue" -//! INST_NEXT "GetInstanceClass" +//! INST_NEXT /LoadObject.*Class/ //! INST_NEXT "ClassImmediate" //! INST_NEXT "Compare" //! INST_NEXT "DeoptimizeIf" -//! INST_NEXT "StoreObject" -//! INST_NEXT "LoadObject" +//! INST_NEXT /StoreObject.*Dynamic/ +//! INST_NEXT /LoadObject.*Dynamic/ //! INST_NOT "LoadArray" //! INST_NOT "StoreArray" -//! EVENT /Compilation,_GLOBAL::test_load_store_by_name_single_inlined,.*,COMPILED/ -//! METHOD "test_load_store_by_name_single_properties" +//! EVENT /Compilation,_GLOBAL::test_load_store_by_name_single_field_inlined,.*,COMPILED/ +//! METHOD "test_load_store_by_name_single_field_properties" //! PASS_AFTER "TypesResolving" -//! INST "AnyTypeCheck ECMASCRIPT_OBJECT_TYPE" +//! INST "AnyTypeCheck ECMASCRIPT_HEAP_OBJECT_TYPE" //! INST_NEXT "CastAnyTypeValue" -//! INST_NEXT "GetInstanceClass" +//! INST_NEXT /LoadObject.*Class/ //! INST_NEXT "ClassImmediate" //! INST_NEXT "Compare" //! INST_NEXT "DeoptimizeIf" -//! INST_NEXT "LoadObject" +//! INST_NEXT /LoadObject.*Properties/ //! INST_NEXT "StoreArray" //! INST_NEXT "LoadArray" //! INST_NOT "StoreObject" -//! EVENT /Compilation,_GLOBAL::test_load_store_by_name_single_properties,.*,COMPILED/ +//! EVENT /Compilation,_GLOBAL::test_load_store_by_name_single_field_properties,.*,COMPILED/ +//! METHOD "test_load_store_by_name_single_transition_inlined" +//! PASS_AFTER "TypesResolving" +//! INST "AnyTypeCheck ECMASCRIPT_HEAP_OBJECT_TYPE" +//! INST_NEXT "CastAnyTypeValue" +//! INST_NEXT /LoadObject.*Class/ +//! INST_NEXT "ClassImmediate" +//! INST_NEXT "Compare" +//! INST_NEXT "DeoptimizeIf" +//! INST_NEXT "ClassImmediate" +//! INST_NEXT /StoreObject.*Class/ +//! INST_NEXT /StoreObject.*Dynamic/ +//! INST_NEXT /LoadObject.*Dynamic/ +//! INST_NOT "LoadArray" +//! INST_NOT "StoreArray" +//! EVENT /Compilation,_GLOBAL::test_load_store_by_name_single_transition_inlined,.*,COMPILED/ +//! METHOD "test_load_store_by_name_single_transition_properties" +//! PASS_AFTER "TypesResolving" +//! INST "AnyTypeCheck ECMASCRIPT_HEAP_OBJECT_TYPE" +//! INST_NEXT "CastAnyTypeValue" +//! INST_NEXT /LoadObject.*Class/ +//! INST_NEXT "ClassImmediate" +//! INST_NEXT "Compare" +//! INST_NEXT "DeoptimizeIf" +//! INST_NEXT /LoadObject.*Properties/ +//! INST_NEXT "LenArray" +//! INST_NEXT "BoundsCheck" +//! INST_NEXT "ClassImmediate" +//! INST_NEXT /StoreObject.*Class/ +//! INST_NEXT "StoreArray" +//! EVENT /Compilation,_GLOBAL::test_load_store_by_name_single_transition_properties,.*,COMPILED/ +//! METHOD "test_load_store_by_name_single_prototype_inlined" +//! PASS_AFTER "TypesResolving" +//! INST "AnyTypeCheck ECMASCRIPT_HEAP_OBJECT_TYPE" +//! INST_NEXT "CastAnyTypeValue" +//! INST_NEXT /LoadObject.*Class/ +//! INST_NEXT "ClassImmediate" +//! INST_NEXT "Compare" +//! INST_NEXT "DeoptimizeIf" +//! INST_NEXT "LoadObjFromConst" +//! INST_NEXT /LoadObject.*Prototype holder/ +//! INST_NEXT /LoadObject.*Prototype cell/ +//! INST_NEXT /LoadObject.*IsChangeFieald/ +//! INST_NEXT "DeoptimizeIf" +//! EVENT /Compilation,_GLOBAL::test_load_store_by_name_single_prototype_inlined,.*,COMPILED/ //! METHOD "test_load_store_by_name_polymorphic" //! PASS_AFTER "TypesResolving" -//! INST "AnyTypeCheck ECMASCRIPT_OBJECT_TYPE" +//! INST "AnyTypeCheck ECMASCRIPT_HEAP_OBJECT_TYPE" //! INST_NEXT "CastAnyTypeValue" -//! INST_NEXT "GetInstanceClass" +//! INST_NEXT /LoadObject.*Class/ +//! INST_NEXT "ClassImmediate" //! INST_NEXT "ClassImmediate" //! INST_NEXT "Compare" //! INST_NEXT "IfImm" -//! INST_COUNT "LoadObject", 3 +//! INST_COUNT "LoadObject", 5 //! INST_COUNT "StoreArray", 1 //! INST_COUNT "LoadArray", 1 //! INST_COUNT "StoreObject", 1 //! EVENT /Compilation,_GLOBAL::test_load_store_by_name_polymorphic,.*,COMPILED/ -//! EVENT_NEXT /DeoptimizationReason,.*test_load_store_by_name_single_inlined.*INLINE_IC/ -//! EVENT_NEXT /DeoptimizationReason,.*test_load_store_by_name_single_properties.*ANY_TYPE_CHECK/ +//! EVENT_NEXT /DeoptimizationReason,.*test_load_store_by_name_single_field_inlined.*INLINE_IC/ +//! EVENT_NEXT /DeoptimizationReason,.*test_load_store_by_name_single_transition_inlined.*INLINE_IC/ +//! EVENT_NEXT /DeoptimizationReason,.*test_load_store_by_name_single_field_properties.*ANY_TYPE_CHECK/ +//! EVENT_NEXT /DeoptimizationReason,.*test_load_store_by_name_single_prototype_inlined.*ANY_TYPE_CHECK/ -function test_load_store_by_name_single_inlined(a) { + +function Test() {} +Test.prototype.field1 = 5; +Test.prototype.field2 = 4; + +function test_load_store_by_name_single_field_inlined(a) { a.field1 = 2 return a.field2 } -function test_load_store_by_name_single_properties(a) { +function test_load_store_by_name_single_field_properties(a) { a.field1 = 5 return a.field3 } @@ -72,16 +124,30 @@ function test_load_store_by_name_polymorphic(a) { return a.field2 } +function test_load_store_by_name_single_transition_inlined(a) { + a.field2 = 3 + return a.field1 +} + +function test_load_store_by_name_single_transition_properties(a) { + a.field200 = 5 + return a.field1 +} + +function test_load_store_by_name_single_prototype_inlined(a) { + return a.field1 +} + let a = {} a.field1 = 5 a.field2 = 8 -for (let i = 0 ; i < 1000; i++) { - if (test_load_store_by_name_single_inlined(a) != 8) { - throw "test_load_store_by_name_single_inlined is failed for inlined case - incorrect return value"; +for (let i = 0 ; i < 50; i++) { + if (test_load_store_by_name_single_field_inlined(a) != 8) { + throw "test_load_store_by_name_single_field_inlined is failed for inlined case - incorrect return value"; } if (a.field1 != 2) { - throw "test_load_store_by_name_single_inlined is failed for inlined case - incorrect load value"; + throw "test_load_store_by_name_single_field_inlined is failed for inlined case - incorrect load value"; } } let b = {} @@ -89,16 +155,16 @@ for (let i = 100 ; i >=0 ; --i) { b["field" + i] = i } -for (let i = 0 ; i < 1000; i++) { - if (test_load_store_by_name_single_properties(b) != 3) { - throw "test_load_store_by_name_single_properties is failed - incorrect return value"; +for (let i = 0 ; i < 50; i++) { + if (test_load_store_by_name_single_field_properties(b) != 3) { + throw "test_load_store_by_name_single_field_properties is failed - incorrect return value"; } if (b.field1 != 5) { - throw "test_load_store_by_name_single_properties is failed - incorrect load value"; + throw "test_load_store_by_name_single_field_properties is failed - incorrect load value"; } } -for (let i = 0 ; i < 1000; i++) { +for (let i = 0 ; i < 50; i++) { if (test_load_store_by_name_polymorphic(a) != 8) { throw "test_load_store_by_name_polymorphic is failed - incorrect return value for a"; } @@ -113,11 +179,48 @@ for (let i = 0 ; i < 1000; i++) { } } -test_load_store_by_name_single_inlined(b) +for (let i = 0 ; i < 50; i++) { + let c = {}; + c.field1 = i + if (test_load_store_by_name_single_transition_inlined(c) != i) { + throw "test_load_store_by_name_single_transition_inlined is failed for inlined case - incorrect return value"; + } + if (c.field2 != 3) { + throw "test_load_store_by_name_single_transition_inlined is failed for inlined case - incorrect load value"; + } +} + +for (let i = 0 ; i < 50; i++) { + let k = {}; + for (let j = 0 ; j < 98; ++j) { + k["field" + j] = j + } + if (test_load_store_by_name_single_transition_properties(k) != 1) { + throw "test_load_store_by_name_single_transition_properties is failed - incorrect return value"; + } + if (k.field200 != 5) { + throw "test_load_store_by_name_single_transition_properties is failed - incorrect load value"; + } +} + +var prot = new Test() + +for (let i = 0 ; i < 50; i++) { + if (test_load_store_by_name_single_prototype_inlined(prot) != 5) { + throw "test_load_store_by_name_single_prototype_inlined is failed - incorrect return value"; + } +} + +test_load_store_by_name_single_field_inlined(b) + +let d = {}; +d.field1 = 0 +d.field3 = 0 +test_load_store_by_name_single_transition_inlined(d) var get_type_error = false; try { - test_load_store_by_name_single_properties(10) + test_load_store_by_name_single_field_properties(10) } catch (e) { if (e instanceof TypeError) { get_type_error = true; @@ -126,3 +229,15 @@ try { if (!get_type_error) { throw "don't catch type error"; } + +var get_type_error_2 = false; +try { + test_load_store_by_name_single_prototype_inlined(undefined) +} catch (e) { + if (e instanceof TypeError) { + get_type_error_2 = true; + } +} +if (!get_type_error_2) { + throw "don't catch type error 2"; +} \ No newline at end of file -- Gitee