diff --git a/compiler/ecmascript_extensions/ecmascript_compiler_interface.h b/compiler/ecmascript_extensions/ecmascript_compiler_interface.h index 4ea1b0e57a75d40672729afef687b2171645d848..b65bc0bad43fe35bf2d49fb16779d5001aeab108 100644 --- a/compiler/ecmascript_extensions/ecmascript_compiler_interface.h +++ b/compiler/ecmascript_extensions/ecmascript_compiler_interface.h @@ -39,4 +39,17 @@ virtual NewObjDynInfo GetNewObjDynInfo([[maybe_unused]] uintptr_t method) const return {NewObjDynInfo::AllocatorType::DEFAULT, NewObjDynInfo::ResolverType::NONE}; } +virtual bool GetProfileDataForNamedAccess( + [[maybe_unused]] RuntimeInterface::MethodPtr m, [[maybe_unused]] uintptr_t slot_id, + [[maybe_unused]] ArenaVector *profile) +{ + return false; +} + +virtual bool GetProfileDataForNamedAccess( + [[maybe_unused]] uintptr_t func, [[maybe_unused]] uintptr_t slot_id, + [[maybe_unused]] ArenaVector *profile) +{ + return false; +} #endif // PANDA_COMPILER_ECMASCRIPT_COMPILER_INTERFACE_H \ No newline at end of file diff --git a/compiler/intrinsics_inline_ecmascript.inl b/compiler/intrinsics_inline_ecmascript.inl index aedac4ab5590b41c3429a5918ff1bdfb871fa7c0..2472f561517bfbf91e163a013c014932735efdb5 100644 --- a/compiler/intrinsics_inline_ecmascript.inl +++ b/compiler/intrinsics_inline_ecmascript.inl @@ -140,4 +140,13 @@ case RuntimeInterface::IntrinsicId::INTRINSIC_LDINFINITY: { case RuntimeInterface::IntrinsicId::INTRINSIC_LDNAN: { ASSERT(types_.size() == 0); return InlineLdNan(intrinsic); +} +case RuntimeInterface::IntrinsicId::INTRINSIC_LD_OBJ_BY_NAME: { + return InlineLdObjByName(intrinsic); +} +case RuntimeInterface::IntrinsicId::INTRINSIC_ST_OBJ_BY_NAME: { + return InlineStObjByName(intrinsic); +} +case RuntimeInterface::IntrinsicId::INTRINSIC_RESOLVE_ALLOC_RESULT: { + return InlineResolveAllocResult(intrinsic); } \ No newline at end of file diff --git a/compiler/intrinsics_type_resolving_ecmascript.cpp b/compiler/intrinsics_type_resolving_ecmascript.cpp index 697f338c46d3efb426a562ada733cd25713a562c..f32a74fe67303719b24b937c0f758554c30c9dc5 100644 --- a/compiler/intrinsics_type_resolving_ecmascript.cpp +++ b/compiler/intrinsics_type_resolving_ecmascript.cpp @@ -14,6 +14,8 @@ */ #include "compiler/optimizer/optimizations/types_resolving.h" +#include "optimizer/analysis/loop_analyzer.h" +#include "compiler/optimizer/ir/analysis.h" #include "runtime/include/coretypes/tagged_value.h" namespace panda::compiler { @@ -96,4 +98,291 @@ bool TypesResolving::InlineLdNan(IntrinsicInst *intrinsic) { return InlineLdConstant(intrinsic, AnyBaseType::ECMASCRIPT_DOUBLE_TYPE, TaggedValue::VALUE_NAN); } + +void TypesResolving::CreateCompareClass(uint32_t pc, Inst *get_cls_inst, RuntimeInterface::ClassPtr receiver, + BasicBlock *load_bb) +{ + auto load_cls_inst = GetGraph()->CreateInstClassImmediate(DataType::REFERENCE, pc, receiver); + auto cmp_inst = GetGraph()->CreateInstCompare(DataType::BOOL, pc, ConditionCode::CC_EQ); + auto if_inst = GetGraph()->CreateInstIfImm(DataType::BOOL, pc, ConditionCode::CC_NE, 0); + cmp_inst->SetInput(0, load_cls_inst); + cmp_inst->SetInput(1, get_cls_inst); + cmp_inst->SetOperandsType(DataType::REFERENCE); + if_inst->SetInput(0, cmp_inst); + if_inst->SetOperandsType(DataType::BOOL); + load_bb->AppendInst(load_cls_inst); + load_bb->AppendInst(cmp_inst); + load_bb->AppendInst(if_inst); +} + +Inst *TypesResolving::CreateCompareClassWithDeopt(uint32_t pc, Inst *get_cls_inst, RuntimeInterface::ClassPtr receiver, + Inst *save_state) +{ + auto load_cls_inst = GetGraph()->CreateInstClassImmediate(DataType::REFERENCE, pc, receiver); + auto cmp_inst = GetGraph()->CreateInstCompare(DataType::BOOL, pc, ConditionCode::CC_NE); + cmp_inst->SetInput(0, get_cls_inst); + cmp_inst->SetInput(1, load_cls_inst); + cmp_inst->SetOperandsType(DataType::REFERENCE); + auto deopt_inst = GetGraph()->CreateInstDeoptimizeIf(DataType::NO_TYPE, pc); + deopt_inst->SetDeoptimizeType(DeoptimizeType::INLINE_IC); + deopt_inst->SetInput(0, cmp_inst); + deopt_inst->SetSaveState(save_state); + get_cls_inst->InsertAfter(deopt_inst); + get_cls_inst->InsertAfter(cmp_inst); + get_cls_inst->InsertAfter(load_cls_inst); + return deopt_inst; +} + +template +Inst *InsertMemByNameInst(IntrinsicInst *intrinsic, Inst *obj_inst, Inst *insert_after, uint32_t offset, + bool is_inlined) +{ + 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); + 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); + 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 array; + } +} + +void InsertDeoptimizeInst(uint32_t pc, SaveStateInst *ss, BasicBlock *load_bb) +{ + ASSERT(load_bb != nullptr); + // NOLINTNEXTLINE(clang-analyzer-core.CallAndMessage) + auto graph = load_bb->GetGraph(); + ASSERT(graph != nullptr); + + // If last class compare returns false we need to deoptimize the method. + // So we construct instruction DeoptimizeIf and insert instead of IfImm inst. + auto if_inst = load_bb->GetLastInst(); + ASSERT(if_inst != nullptr && if_inst->GetOpcode() == Opcode::IfImm); + ASSERT(if_inst->CastToIfImm()->GetImm() == 0 && if_inst->CastToIfImm()->GetCc() == ConditionCode::CC_NE); + + auto compare_inst = if_inst->GetInput(0).GetInst()->CastToCompare(); + ASSERT(compare_inst != nullptr && compare_inst->GetCc() == ConditionCode::CC_EQ); + compare_inst->SetCc(ConditionCode::CC_NE); + + auto deopt_inst = graph->CreateInstDeoptimizeIf(DataType::NO_TYPE, pc); + deopt_inst->SetDeoptimizeType(DeoptimizeType::INLINE_IC); + deopt_inst->SetInput(0, compare_inst); + deopt_inst->SetSaveState(ss); + + load_bb->RemoveInst(if_inst); + load_bb->AppendInst(deopt_inst); +} + +bool TypesResolving::InlineLdObjByName(IntrinsicInst *intrinsic) +{ + return InlineObjByName(intrinsic); +} + +bool TypesResolving::InlineStObjByName(IntrinsicInst *intrinsic) +{ + return InlineObjByName(intrinsic); +} + +bool TypesResolving::GetICForMemAccess(IntrinsicInst *intrinsic) +{ + auto pc = intrinsic->GetPc(); + auto runtime = GetGraph()->GetRuntime(); + auto save_state = intrinsic->GetSaveState(); + ASSERT(save_state != nullptr); + auto caller_inst = save_state->GetCallerInst(); + if (caller_inst != nullptr) { + ASSERT(caller_inst->IsInlined()); + auto func = caller_inst->GetFunctionObject(); + ASSERT(func != 0); + if (!runtime->GetProfileDataForNamedAccess(func, pc, &named_access_profile_)) { + return false; + } + } else if (!runtime->GetProfileDataForNamedAccess(GetGraph()->GetMethod(), pc, &named_access_profile_)) { + return false; + } + return true; +} + +Inst *TypesResolving::InsertCheckAndCastInstructions(IntrinsicInst *intrinsic) +{ + auto pc = intrinsic->GetPc(); + auto save_state = intrinsic->GetSaveState(); + auto obj_inst = intrinsic->GetInput(0).GetInst(); + + 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); + 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->SetFlag(inst_flags::NO_HOIST); + any_check->InsertAfter(cast_value); + return cast_value; +} + +template +void TypesResolving::InlineObjByNameMonomorphic(IntrinsicInst *intrinsic, Inst *get_cls_inst, Inst *obj_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); + + if constexpr (IS_LOAD) { + intrinsic->ReplaceUsers(load_inst); + } + + intrinsic->SetInlined(false); + intrinsic->GetBasicBlock()->RemoveInst(intrinsic); +} + +template +void TypesResolving::InlineObjByNamePolymorphic(IntrinsicInst *intrinsic, Inst *get_cls_inst, Inst *obj_inst) +{ + auto pc = intrinsic->GetPc(); + auto save_state = intrinsic->GetSaveState(); + PhiInst *phi_inst = nullptr; + BasicBlock *load_bb = nullptr; + BasicBlock *load_cont_bb = nullptr; + Inst *load_inst = intrinsic; + for (auto &prof : named_access_profile_) { + if (load_bb == nullptr) { + // Split block by call instruction + load_bb = load_inst->GetBasicBlock(); + load_cont_bb = load_bb->SplitBlockAfterInstruction(load_inst, false); + if constexpr (IS_LOAD) { + phi_inst = GetGraph()->CreateInstPhi(load_inst->GetType(), load_inst->GetPc()); + phi_inst->ReserveInputs(named_access_profile_.size()); + load_cont_bb->AppendPhi(phi_inst); + } + } else { + auto new_load_bb = GetGraph()->CreateEmptyBlock(load_bb); + load_bb->GetLoop()->AppendBlock(new_load_bb); + load_bb->AddSucc(new_load_bb); + load_bb = new_load_bb; + } + + CreateCompareClass(pc, get_cls_inst, prof.klass, load_bb); + // Create load_inserting_block + auto load_inserting_block = GetGraph()->CreateEmptyBlock(load_bb); + load_bb->GetLoop()->AppendBlock(load_inserting_block); + load_bb->AddSucc(load_inserting_block); + auto last_ss = CopySaveState(GetGraph(), save_state); + load_inserting_block->PrependInst(last_ss); + + [[maybe_unused]] auto load = + InsertMemByNameInst(intrinsic, obj_inst, last_ss, prof.offset, prof.is_inlined); + + if constexpr (IS_LOAD) { + phi_inst->AppendInput(load); + } + + load_inserting_block->AddSucc(load_cont_bb); + } + + InsertDeoptimizeInst(pc, save_state, load_bb); + + if constexpr (IS_LOAD) { + intrinsic->ReplaceUsers(phi_inst); + } + + intrinsic->SetInlined(false); + intrinsic->GetBasicBlock()->RemoveInst(intrinsic); +} + +template +bool TypesResolving::InlineObjByName(IntrinsicInst *intrinsic) +{ + if (GetGraph()->IsAotMode()) { + return false; + } + auto pc = intrinsic->GetPc(); + ASSERT(intrinsic->GetSaveState() != nullptr); + if (!GetICForMemAccess(intrinsic)) { + return false; + } + ASSERT(!named_access_profile_.empty()); + + auto cast_value = InsertCheckAndCastInstructions(intrinsic); + + auto get_cls_inst = GetGraph()->CreateInstGetInstanceClass(DataType::REFERENCE, pc); + get_cls_inst->SetInput(0, cast_value); + cast_value->InsertAfter(get_cls_inst); + if (named_access_profile_.size() == 1) { + InlineObjByNameMonomorphic(intrinsic, get_cls_inst, cast_value); + return true; + } + + InlineObjByNamePolymorphic(intrinsic, get_cls_inst, cast_value); + return true; +} + +bool TypesResolving::InlineResolveAllocResult(IntrinsicInst *intrinsic) +{ + auto ctor_res = intrinsic->GetInput(2U).GetInst(); + if (ctor_res->GetOpcode() != Opcode::CastValueToAnyType) { + return false; + } + if (ctor_res->CastToCastValueToAnyType()->GetAnyType() != AnyBaseType::ECMASCRIPT_UNDEFINED_TYPE) { + return false; + } + auto alloc_inst = intrinsic->GetInput(1U).GetInst(); + intrinsic->ReplaceUsers(alloc_inst); + + intrinsic->SetInlined(false); + intrinsic->GetBasicBlock()->RemoveInst(intrinsic); + return true; +} + } // namespace panda::compiler diff --git a/compiler/intrinsics_type_resolving_ecmascript.inl.h b/compiler/intrinsics_type_resolving_ecmascript.inl.h index 7a786080c436e037c3ac9fbf4569af7477350011..8505d54b5bf95e572e945bdb242256bd9bdd5515 100644 --- a/compiler/intrinsics_type_resolving_ecmascript.inl.h +++ b/compiler/intrinsics_type_resolving_ecmascript.inl.h @@ -24,5 +24,24 @@ bool InlineLdNull(IntrinsicInst *intrinsic); bool InlineLdUndefined(IntrinsicInst *intrinsic); bool InlineLdInfinity(IntrinsicInst *intrinsic); bool InlineLdNan(IntrinsicInst *intrinsic); +bool InlineLdObjByName(IntrinsicInst *intrinsic); +bool InlineStObjByName(IntrinsicInst *intrinsic); +bool InlineResolveAllocResult(IntrinsicInst *intrinsic); + +private: +template +bool InlineObjByName(IntrinsicInst *intrinsic); + +template +void InlineObjByNameMonomorphic(IntrinsicInst *intrinsic, Inst *get_cls_inst, Inst *obj_inst); +template +void InlineObjByNamePolymorphic(IntrinsicInst *intrinsic, Inst *get_cls_inst, Inst *obj_inst); + +bool GetICForMemAccess(IntrinsicInst *intrinsic); +Inst *InsertCheckAndCastInstructions(IntrinsicInst *intrinsic); + +void CreateCompareClass(uint32_t pc, Inst *get_cls_inst, RuntimeInterface::ClassPtr receiver, BasicBlock *load_bb); +Inst *CreateCompareClassWithDeopt(uint32_t pc, Inst *get_cls_inst, RuntimeInterface::ClassPtr receiver, + Inst *save_state); #endif // PLUGINS_ECMASCRIPT_COMPILER_INTRINSICS_TYPE_RESOLVING_ECMASCRIPT_INL_H \ No newline at end of file diff --git a/compiler/optimizer/optimizations/ecma_inlining.cpp b/compiler/optimizer/optimizations/ecma_inlining.cpp index e7cb26c0a99e13de89c81023dd60011c58223ec2..ebc8bd72fdd72bb975161492f96449408a2156e7 100644 --- a/compiler/optimizer/optimizations/ecma_inlining.cpp +++ b/compiler/optimizer/optimizations/ecma_inlining.cpp @@ -174,6 +174,10 @@ void EcmaInlining::InsertGraph(CallInst *call_inst, const InlineContext &ctx, Gr call_inst->SetCallMethod(ctx.method); call_inst->SetCallMethodId(GetGraph()->GetRuntime()->GetMethodId(ctx.method)); + + CHECK_EQ(js_functions_.size(), 1U); + ASSERT(js_functions_[0] != 0); + call_inst->SetFunctionObject(js_functions_[0]); } bool EcmaInlining::TryInline(CallInst *call_inst) diff --git a/compiler/optimizer/optimizations/expand_intrinsics.cpp b/compiler/optimizer/optimizations/expand_intrinsics.cpp index ff06a5ea5902956b0f7365d57b36590485bfec5a..64f76c03e9d46c1263eafdff3b4280d9eb949927 100644 --- a/compiler/optimizer/optimizations/expand_intrinsics.cpp +++ b/compiler/optimizer/optimizations/expand_intrinsics.cpp @@ -168,6 +168,7 @@ Inst *ExpandIntrinsics::NewObjResolveCtorResult(InstAppender *appender, Inst *or resolve_result->AddInputType(DataType::ANY); resolve_result->AppendInput(ss_copy_2); resolve_result->AddInputType(DataType::NO_TYPE); + resolve_result->SetInlined(true); appender->Append({ss_copy_2, resolve_result}); diff --git a/compiler/templates/ecmascript_inst_builder_gen.cpp.erb b/compiler/templates/ecmascript_inst_builder_gen.cpp.erb index a6245159692c0280db9558ffbdf6cdc5a23f797e..047ecec1617212843d21686c122a01b8bc1ba468 100644 --- a/compiler/templates/ecmascript_inst_builder_gen.cpp.erb +++ b/compiler/templates/ecmascript_inst_builder_gen.cpp.erb @@ -154,17 +154,15 @@ void InstBuilder::BuildEcmaAsIntrinsics(const BytecodeInstruction* bc_inst) // N % elsif param.reg? { auto input = GetDefinition(bc_inst->GetVReg<<%= format %>, <%= vreg_index %>>()); +% if has_profile && (inst.profile.properties.include?('operand_types_1') || inst.profile.properties.include?('operand_types_2')) // NOLINTNEXTLINE(readability-braces-around-statements, bugprone-suspicious-semicolon) if constexpr (with_speculative) { bool is_integer_seen = false; bool is_type_profiled = false; -% if has_profile && (inst.profile.properties.include?('operand_types_1') || inst.profile.properties.include?('operand_types_2')) auto operand_type = GetRuntime()->GetProfilingAnyType(profile, bc_inst, <%= input_index %>, &is_integer_seen, &is_type_profiled); -% else - auto operand_type = compiler::AnyBaseType::UNDEFINED_TYPE; -% end input = BuildAnyTypeCheckInst(GetPc(bc_inst->GetAddress()), input, inst_save_state, operand_type, is_type_profiled, is_integer_seen); } +% end inst->AppendInput(input); inst->AddInputType(DataType::ANY); } @@ -216,19 +214,18 @@ void InstBuilder::BuildEcmaAsIntrinsics(const BytecodeInstruction* bc_inst) // N % if acc_read { auto input = GetDefinitionAcc(); +% if has_profile && (inst.profile.properties.include?('operand_types_1') || inst.profile.properties.include?('operand_types_2')) + // NOLINTNEXTLINE(readability-braces-around-statements, bugprone-suspicious-semicolon) if constexpr (with_speculative) { bool is_integer_seen = false; bool is_type_profiled = false; -% if has_profile && (inst.profile.properties.include?('operand_types_1') || inst.profile.properties.include?('operand_types_2')) % idx = inst.profile.properties.include?('operand_types_2') ? 1 : 0 auto operand_type = GetRuntime()->GetProfilingAnyType(profile, bc_inst, <%= idx %>, &is_integer_seen, &is_type_profiled); -% else - auto operand_type = compiler::AnyBaseType::UNDEFINED_TYPE; -% end input = BuildAnyTypeCheckInst(GetPc(bc_inst->GetAddress()), input, inst_save_state, operand_type, is_type_profiled, is_integer_seen); } +% end inst->AppendInput(input); inst->AddInputType(DataType::ANY); inst->SetFlag(compiler::inst_flags::ACC_READ); diff --git a/isa/isa.yaml b/isa/isa.yaml index 67f7195db3507a139d9bfeb2eff7fb577850e970..2df643e4877b1b0634e268ad81c9667e028fa3b2 100644 --- a/isa/isa.yaml +++ b/isa/isa.yaml @@ -448,16 +448,18 @@ groups: - sig: ecma.strictnoteqdyn v:in:top acc: inout:top prefix: ecma - format: [pref_op_v_8] + format: [pref_op_v_8_prof_16] properties: [inlinable] intrinsic_name: INTRINSIC_STRICT_NOT_EQ_DYN + profile: BinaryArith - sig: ecma.stricteqdyn v:in:top acc: inout:top prefix: ecma - format: [pref_op_v_8] + format: [pref_op_v_8_prof_16] properties: [inlinable] intrinsic_name: INTRINSIC_STRICT_EQ_DYN + profile: BinaryArith - sig: ecma.resumegenerator v:in:top acc: out:top @@ -826,7 +828,7 @@ groups: acc: out:top prefix: ecma format: [pref_op_imm_16_v_8_prof_16] - properties: [call, not_compilable] + properties: [call, not_compilable, init_obj] intrinsic_name: INTRINSIC_NEWOBJ_DYNRANGE profile: Call diff --git a/runtime/CMakeLists.txt b/runtime/CMakeLists.txt index ab7e86e758aa02434fedb988feea5e0a5edd3c77..0d309fede93397da047d158f663902cb6ab22c81 100644 --- a/runtime/CMakeLists.txt +++ b/runtime/CMakeLists.txt @@ -103,6 +103,7 @@ set(ECMASCRIPT_SOURCES ${ECMA_SRC_DIR}/dump.cpp ${ECMA_SRC_DIR}/ecma_call_profiling_table.cpp ${ECMA_SRC_DIR}/ecma_class_linker_extension.cpp + ${ECMA_SRC_DIR}/ecma_compiler.cpp ${ECMA_SRC_DIR}/ecma_entrypoints.cpp ${ECMA_SRC_DIR}/ecma_exceptions.cpp ${ECMA_SRC_DIR}/ecma_language_context.cpp diff --git a/runtime/compiler/ecmascript_runtime_interface.cpp b/runtime/compiler/ecmascript_runtime_interface.cpp index 992bd30e729573eb912119387f480e90138b0002..3daa2ad0a2bb599e3b40a811d113eb0dbfbb9396 100644 --- a/runtime/compiler/ecmascript_runtime_interface.cpp +++ b/runtime/compiler/ecmascript_runtime_interface.cpp @@ -21,6 +21,12 @@ #include "plugins/ecmascript/compiler/ecmascript_extensions/ecmascript_environment.h" #include "plugins/ecmascript/compiler/ecmascript_extensions/thread_environment_api.h" #include "serializer/serializer.h" +#include "plugins/ecmascript/runtime/js_tagged_value-inl.h" +#include "plugins/ecmascript/runtime/js_array.h" +#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 "runtime/mem/refstorage/global_object_storage.h" namespace panda::ecmascript { @@ -262,4 +268,109 @@ RuntimeInterface::NewObjDynInfo EcmaRuntimeInterface::GetNewObjDynInfo(uintptr_t return {NewObjDynInfo::AllocatorType::ALLOC_OBJ, NewObjDynInfo::ResolverType::RESOLVE}; } +bool EcmaRuntimeInterface::AddProfileInfo(ArenaVector *profile, + ProfileTypeInfo *profile_type_info, uint8_t slot) +{ + JSTaggedValue hclass_value = profile_type_info->Get(slot); + if (!hclass_value.IsHeapObject()) { + return false; + } + JSTaggedValue handler = profile_type_info->Get(slot + 1); + if (UNLIKELY(handler.IsUndefined())) { + return false; + } + if (LIKELY(handler.IsInt())) { + auto handler_info = static_cast(handler.GetInt()); + if (LIKELY(HandlerBase::IsField(handler_info))) { + int index = HandlerBase::GetOffset(handler_info); + bool is_inlined = HandlerBase::IsInlinedProps(handler_info); + 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}); + return true; + } + return false; + } + return false; +} + +bool EcmaRuntimeInterface::GetProfileDataForNamedAccess(PandaRuntimeInterface::MethodPtr m, uintptr_t slot_id, + ArenaVector *profile) +{ + if (profile == nullptr) { + return false; + } + profile->clear(); + ScopedMutatorLock lock; + os::memory::LockHolder lock_m(mutex_); + uint8_t *mapping = JsMethodCast(m)->GetICMapping(); + if (mapping == nullptr) { + return false; + } + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + auto slot = mapping[slot_id]; + auto it = js_function_table_.find(MethodCast(m)); + if (it == js_function_table_.end()) { + return false; + } + + 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); + return true; + } + return false; +} + +bool EcmaRuntimeInterface::GetProfileDataForNamedAccess(uintptr_t func_address, uintptr_t slot_id, + ArenaVector *profile) +{ + if (profile == nullptr) { + return false; + } + profile->clear(); + ASSERT(func_address != 0); + ScopedMutatorLock lock; + os::memory::LockHolder lock_m(mutex_); + + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + auto js_func = TaggedValue(reinterpret_cast(func_address)[0]); + if (!js_func.IsHeapObject()) { + return false; + } + auto func = JSFunction::Cast(js_func.GetHeapObject()); + uint8_t *mapping = func->GetMethod()->GetICMapping(); + if (mapping == nullptr) { + return false; + } + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + auto slot = mapping[slot_id]; + auto profile_info_obj = func->GetProfileTypeInfo(); + if (profile_info_obj.IsUndefined()) { + return false; + } + + ProfileTypeInfo *profile_type_info = ProfileTypeInfo::Cast(profile_info_obj.GetTaggedObject()); + if (AddProfileInfo(profile, profile_type_info, slot)) { + AddProfileInfo(profile, profile_type_info, slot + 2); + return true; + } + return false; +} + +void EcmaRuntimeInterface::CleanFunction(Method *method) +{ + os::memory::LockHolder lock(mutex_); + + auto it = js_function_table_.find(method); + if (it == js_function_table_.end()) { + return; + } + auto func = it->second; + js_function_table_.erase(it); + ASSERT(ecma_vm_ != nullptr); + ecma_vm_->GetGlobalObjectStorage()->Remove(func); +} } // namespace panda::ecmascript diff --git a/runtime/compiler/ecmascript_runtime_interface.h b/runtime/compiler/ecmascript_runtime_interface.h index 196462ade40da698fe9663953ad2b8756c7a56c3..31743db43787e3968524a2c59ca59adc4481fa1c 100644 --- a/runtime/compiler/ecmascript_runtime_interface.h +++ b/runtime/compiler/ecmascript_runtime_interface.h @@ -19,10 +19,13 @@ #include "plugins/ecmascript/runtime/js_method.h" #include "plugins/ecmascript/runtime/js_object.h" #include "plugins/ecmascript/runtime/js_tagged_value.h" +#include "plugins/ecmascript/runtime/js_thread.h" #include "plugins/ecmascript/runtime/ecma_profiling.h" +#include "runtime/mem/refstorage/reference.h" namespace panda::ecmascript { class EcmaVM; +class ProfileTypeInfo; enum class ObjectFieldType : uint8_t { INVALID = 0, ELEMENTS, COUNT }; @@ -62,6 +65,11 @@ public: return panda::cross_values::GetJsobjectElementsOffset(arch); } + size_t GetPropertiesOffset(Arch arch) const override + { + return panda::cross_values::GetJsobjectPropertiesOffset(arch); + } + EntrypointId GetGlobalVarEntrypointId() override { return EntrypointId::GET_GLOBAL_VAR_ADDRESS; @@ -77,6 +85,15 @@ public: return JsMethodCast(method)->GetCodeSize(); } + std::string GetClassName([[maybe_unused]] ClassPtr klass) const override + { + if (static_cast(klass)->IsDynamicClass()) { + return "JSHClass"; + } + ScopedMutatorLock lock; + return ClassCast(klass)->GetName(); + } + MethodProfile GetMethodProfile(MethodPtr method, bool from_vector) const override; BytecodeProfile GetBytecodeProfile(MethodProfile prof, const uint8_t *bc_inst, size_t pc) const override; @@ -103,10 +120,26 @@ public: bool *is_type_profiled) override; 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, + ArenaVector *profile) override; + + void CleanFunction(Method *method); + + void AddFunctionInMap(Method *method, panda::mem::Reference *func) + { + os::memory::LockHolder lock(mutex_); + js_function_table_.insert({method, func}); + } + + bool AddProfileInfo(ArenaVector *profile, ProfileTypeInfo *profile_type_info, uint8_t slot); private: const EcmaVM *ecma_vm_ {nullptr}; panda::ecmascript::EcmaProfileContainer profile_; + PandaUnorderedMap js_function_table_ GUARDED_BY(mutex_); + mutable os::memory::Mutex mutex_; }; } // namespace panda::ecmascript diff --git a/runtime/ecma_call_profiling_table.cpp b/runtime/ecma_call_profiling_table.cpp index 428a161396835d804acf9ad2de5e285931ad3ee8..6482a8eb6e8ce87e6e170306e1030b1994ab2a99 100644 --- a/runtime/ecma_call_profiling_table.cpp +++ b/runtime/ecma_call_profiling_table.cpp @@ -108,4 +108,16 @@ void EcmaCallProfilingTable::UpdateMoved() } } +void EcmaCallProfilingTable::VisitRoots(const GCRootVisitor &visitor) +{ + os::memory::LockHolder holder(table_lock_); + for (size_t i = 1; i < call_profiling_table_.size(); ++i) { + auto *object = call_profiling_table_[i]; + if (object == nullptr) { + continue; + } + visitor(mem::GCRoot(mem::RootType::ROOT_VM, object)); + } +} + } // namespace panda::ecmascript diff --git a/runtime/ecma_call_profiling_table.h b/runtime/ecma_call_profiling_table.h index 697412f4e8b6def6d52f4278f60795f8507d3057..028ae2897e37eea36997a011039440699eaf319e 100644 --- a/runtime/ecma_call_profiling_table.h +++ b/runtime/ecma_call_profiling_table.h @@ -34,6 +34,7 @@ public: uintptr_t GetObjectPtr(uint16_t idx) const; void Sweep(const GCObjectVisitor &visitor); void UpdateMoved(); + void VisitRoots(const GCRootVisitor &visitor); private: NO_COPY_SEMANTIC(EcmaCallProfilingTable); diff --git a/runtime/ecma_compiler.cpp b/runtime/ecma_compiler.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d6785f1632bd77f56a641858ce4aeb52f5a8a6c1 --- /dev/null +++ b/runtime/ecma_compiler.cpp @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2021-2022 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 "plugins/ecmascript/runtime/ecma_compiler.h" +#include "plugins/ecmascript/runtime/compiler/ecmascript_runtime_interface.h" +#include "plugins/ecmascript/runtime/js_function.h" +#include "plugins/ecmascript/compiler/ecmascript_extensions/ecmascript_environment.h" +#include "plugins/ecmascript/runtime/global_handle_collection.h" +#include "plugins/ecmascript/runtime/ecma_global_storage-inl.h" +#include "plugins/ecmascript/compiler/ecmascript_extensions/thread_environment_api.h" +#include "runtime/mem/refstorage/global_object_storage.h" +#include "runtime/mem/refstorage/reference.h" + +namespace panda::ecmascript { +void EcmaCompiler::AddTask(CompilerTask &&task, TaggedValue func) +{ + auto vm = task.GetVM(); + if (func.IsHole()) { + auto thread = static_cast(vm)->GetJSThread(); + auto env = thread->GetEcmascriptEnv(); + if (env != nullptr) { + auto curr_func = JSFunction::Cast(env->GetThisFunc()); + if (curr_func->GetMethod() == task.GetMethod()) { + func = TaggedValue(curr_func); + } + } + } + if (!func.IsHole()) { + auto gos = vm->GetGlobalObjectStorage(); + auto func_ref = gos->Add(func.GetHeapObject(), panda::mem::Reference::ObjectType::GLOBAL); + static_cast(GetRuntimeInterface())->AddFunctionInMap(task.GetMethod(), func_ref); + } + GetThreadPool()->PutTask(std::move(task)); +} + +} // namespace panda::ecmascript diff --git a/runtime/ecma_compiler.h b/runtime/ecma_compiler.h new file mode 100644 index 0000000000000000000000000000000000000000..6dcc64e93f5a771727eeddfb41e24706afd7e534 --- /dev/null +++ b/runtime/ecma_compiler.h @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2021-2022 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. + */ + +#ifndef ECMASCRIPT_ECMA_COMPILER_H +#define ECMASCRIPT_ECMA_COMPILER_H + +#include "runtime/compiler.h" + +namespace panda::ecmascript { + +class GlobalHandleCollection; +class JSThread; + +class EcmaCompiler : public Compiler { +public: + explicit EcmaCompiler(CodeAllocator *code_allocator, mem::InternalAllocatorPtr internal_allocator, + const RuntimeOptions &options, mem::MemStatsType *mem_stats, + compiler::RuntimeInterface *runtime_iface) + : Compiler(code_allocator, internal_allocator, options, mem_stats, runtime_iface) + { + } + + void AddTask(CompilerTask &&task, TaggedValue func) override; +}; +} // namespace panda::ecmascript + +#endif // ECMASCRIPT_ECMA_COMPILER_H \ No newline at end of file diff --git a/runtime/ecma_profiling.cpp b/runtime/ecma_profiling.cpp index 5e19a9158c8348583658d5a4c5ea0890082b4467..df626318d62ebd1bf7574e515cf0b0819a1efc9c 100644 --- a/runtime/ecma_profiling.cpp +++ b/runtime/ecma_profiling.cpp @@ -16,6 +16,7 @@ #include "ecma_profiling.h" #include "plugins/ecmascript/runtime/js_hclass.h" #include "plugins/ecmascript/runtime/mem/tagged_object-inl.h" +#include "plugins/ecmascript/runtime/js_function.h" namespace panda::ecmascript { ProfilingIndexedAccessBits::Type GetObjectTypeFromValue(JSTaggedValue value) diff --git a/runtime/ecma_vm.cpp b/runtime/ecma_vm.cpp index cf6fdf2eb906857b76bd0adaba36e64cc8c38f09..b799cf98d7dc30a3addeb6040ed1d13fbc48e5ba 100644 --- a/runtime/ecma_vm.cpp +++ b/runtime/ecma_vm.cpp @@ -65,6 +65,7 @@ #include "runtime/mem/gc/reference-processor/empty_reference_processor.h" #include "runtime/mem/object_helpers.h" #include "plugins/ecmascript/runtime/compiler/ecmascript_runtime_interface.h" +#include "plugins/ecmascript/runtime/ecma_compiler.h" #include "handle_scope-inl.h" #include "include/coretypes/native_pointer.h" @@ -74,7 +75,6 @@ #include "trace/trace.h" #include "ecmastdlib_inline_gen.h" #include "ir-dyn-base-types.h" - #include "ic/profile_type_info.h" #include "serializer/serializer.h" @@ -189,8 +189,8 @@ 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); - compiler_ = internal_allocator->New(heap_manager->GetCodeAllocator(), internal_allocator, options_, - heap_manager->GetMemStats(), runtime_iface_); + compiler_ = internal_allocator->New(heap_manager->GetCodeAllocator(), internal_allocator, options_, + heap_manager->GetMemStats(), runtime_iface_); SetFrameExtSize(ecmascript::JSExtFrame::GetExtSize()); } @@ -372,6 +372,11 @@ void EcmaVM::UninitializeThreads() thread_->UpdateStatus(ThreadStatus::FINISHED); } +void EcmaVM::CleanUpTask(Method *method) +{ + static_cast(GetCompilerRuntimeInterface())->CleanFunction(method); +} + EcmaVM::~EcmaVM() { vm_initialized_ = false; @@ -1008,6 +1013,9 @@ void EcmaVM::VisitVmRoots(const GCRootVisitor &visitor) } }; rootManager.VisitVMRoots(single_visitor, range_visitor); + if (HasEcmaCallProfileTable()) { + GetEcmaCallProfileTable()->VisitRoots(visitor); + } } void EcmaVM::UpdateVmRefs() diff --git a/runtime/ecma_vm.h b/runtime/ecma_vm.h index 474f2639cee8b1421fce1ba853c4133537dbd452..1a83220cb9e3bb9a5ec949bfd60efa2a554bf134 100644 --- a/runtime/ecma_vm.h +++ b/runtime/ecma_vm.h @@ -150,6 +150,9 @@ public: return PandaVMType::ECMA_VM; } + // Remove global handler for JSFunction corresponding to this method + void CleanUpTask(Method *method) override; + LanguageContext GetLanguageContext() const override { return Runtime::GetCurrent()->GetLanguageContext(panda_file::SourceLang::ECMASCRIPT); @@ -187,7 +190,7 @@ public: panda::mem::GlobalObjectStorage *GetGlobalObjectStorage() const override { - return nullptr; + return mm_->GetGlobalObjectStorage(); } coretypes::String *ResolveString(const panda_file::File &pf, panda_file::File::EntityId id) override diff --git a/runtime/interpreter/ecma-interpreter-inl.h b/runtime/interpreter/ecma-interpreter-inl.h index 47bb2dd2e143ef0b3be60e66c1b90571c5dae44d..9cdaea0699dcb887b8c0a0d8b0f2089165219288 100644 --- a/runtime/interpreter/ecma-interpreter-inl.h +++ b/runtime/interpreter/ecma-interpreter-inl.h @@ -229,7 +229,7 @@ public: auto frame = this->GetFrame(); - if (UNLIKELY(frame->IsInitobj() && frame->IsStackless())) { + if (UNLIKELY(frame->IsInitobj())) { auto acc_in = GetAccAsTaggedValue(); JSFunction *func = JSFunction::Cast(GetCurrentFunction().GetTaggedObject()); if (LIKELY(acc_in.IsECMAObject())) { @@ -256,7 +256,7 @@ public: auto frame = this->GetFrame(); - if (UNLIKELY(frame->IsInitobj() && frame->IsStackless())) { + if (UNLIKELY(frame->IsInitobj())) { auto acc_in = GetAccAsTaggedValue(); JSFunction *func = JSFunction::Cast(GetCurrentFunction().GetTaggedObject()); if (LIKELY(func->IsBase() || acc_in.IsUndefined())) { @@ -361,7 +361,7 @@ public: UPDATE_CALL_PROFILE(this_func); - method->DecrementHotnessCounter(0, nullptr); + method->DecrementHotnessCounter(0, nullptr, false, JSTaggedValue(function)); // Call stackless interpreter this->template CallInterpreterStackless, FORMAT, diff --git a/subproject_sources.gn b/subproject_sources.gn index 440fc1f5f0072cca33e6b241f5c6671cbe8ce81c..26d6585480d3308014a300c4fbf8eb44205522d7 100644 --- a/subproject_sources.gn +++ b/subproject_sources.gn @@ -132,6 +132,7 @@ srcs_runtime = [ "runtime/dump.cpp", "runtime/ecma_call_profiling_table.cpp", "runtime/ecma_class_linker_extension.cpp", + "runtime/ecma_compiler.cpp", "runtime/ecma_entrypoints.cpp", "runtime/ecma_exceptions.cpp", "runtime/ecma_language_context.cpp", diff --git a/tests/checked/CMakeLists.txt b/tests/checked/CMakeLists.txt index b42b303621c974cabb633f3f24306503d0851f59..17f03bce227e67237a91ebee43f8051ac839da70 100644 --- a/tests/checked/CMakeLists.txt +++ b/tests/checked/CMakeLists.txt @@ -101,6 +101,7 @@ if (NOT PANDA_TARGET_ARM32) panda_add_checked_test_ecma(FILE ${CMAKE_CURRENT_SOURCE_DIR}/recompile_undefined.js SUPPORT_RELEASE true) panda_add_checked_test_ecma(FILE ${CMAKE_CURRENT_SOURCE_DIR}/donot_recompile_not_profiled.js SUPPORT_RELEASE true) panda_add_checked_test_ecma(FILE ${CMAKE_CURRENT_SOURCE_DIR}/obj_by_index.js SUPPORT_RELEASE true) + panda_add_checked_test_ecma(FILE ${CMAKE_CURRENT_SOURCE_DIR}/obj_by_name.js SUPPORT_RELEASE true) panda_add_checked_test_ecma(FILE ${CMAKE_CURRENT_SOURCE_DIR}/ecma_inlining.js SUPPORT_RELEASE true) panda_add_checked_test_ecma(FILE ${CMAKE_CURRENT_SOURCE_DIR}/ecma_inlining_megamorphic.js SUPPORT_RELEASE true) panda_add_checked_test_ecma(FILE ${CMAKE_CURRENT_SOURCE_DIR}/ecma_inlining_deoptimize.js SUPPORT_RELEASE true) diff --git a/tests/checked/new_obj_profiling.js b/tests/checked/new_obj_profiling.js index e279d4b154f8ecb1968971cf4276dcd8af5c8efc..ea7e26064df25deb055cc2db6385f00b4f345af8 100644 --- a/tests/checked/new_obj_profiling.js +++ b/tests/checked/new_obj_profiling.js @@ -61,10 +61,14 @@ function monomorphic_call_site_derived_ctor() { //! INST /Intrinsic.ResolveAllocResult.*/ //! INST /Intrinsic.NewobjDynrangeHandled*/ //! METHOD "monomorphic_call_site_simple_ctor" -//! PASS_AFTER "Codegen" +//! PASS_BEFORE "TypesResolving" //! INST /Intrinsic.AllocDynObject.*/ //! INST /Intrinsic.ResolveAllocResult.*/ //! INST_NOT /Intrinsic.NewobjDynrangeHandled*/ +//! PASS_AFTER "TypesResolving" +//! INST /Intrinsic.AllocDynObject.*/ +//! INST_NOT /Intrinsic.ResolveAllocResult.*/ +//! INST_NOT /Intrinsic.NewobjDynrangeHandled*/ //! METHOD "monomorphic_call_site_derived_ctor" //! PASS_AFTER "Codegen" //! INST_NOT /Intrinsic.AllocDynObject.*/ diff --git a/tests/checked/obj_by_name.js b/tests/checked/obj_by_name.js new file mode 100644 index 0000000000000000000000000000000000000000..5096f4b63d7ee9b5f69ac0c26c0996dda6790da3 --- /dev/null +++ b/tests/checked/obj_by_name.js @@ -0,0 +1,128 @@ +/* + * Copyright (c) 2021-2022 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. + */ + +//! 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" +//! PASS_AFTER "TypesResolving" +//! INST "AnyTypeCheck ECMASCRIPT_OBJECT_TYPE" +//! INST_NEXT "CastAnyTypeValue" +//! INST_NEXT "GetInstanceClass" +//! INST_NEXT "ClassImmediate" +//! INST_NEXT "Compare" +//! INST_NEXT "DeoptimizeIf" +//! INST_NEXT "StoreObject" +//! INST_NEXT "LoadObject" +//! 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" +//! PASS_AFTER "TypesResolving" +//! INST "AnyTypeCheck ECMASCRIPT_OBJECT_TYPE" +//! INST_NEXT "CastAnyTypeValue" +//! INST_NEXT "GetInstanceClass" +//! INST_NEXT "ClassImmediate" +//! INST_NEXT "Compare" +//! INST_NEXT "DeoptimizeIf" +//! INST_NEXT "LoadObject" +//! INST_NEXT "StoreArray" +//! INST_NEXT "LoadArray" +//! INST_NOT "StoreObject" +//! EVENT /Compilation,_GLOBAL::test_load_store_by_name_single_properties,.*,COMPILED/ +//! METHOD "test_load_store_by_name_polymorphic" +//! PASS_AFTER "TypesResolving" +//! INST "AnyTypeCheck ECMASCRIPT_OBJECT_TYPE" +//! INST_NEXT "CastAnyTypeValue" +//! INST_NEXT "GetInstanceClass" +//! INST_NEXT "ClassImmediate" +//! INST_NEXT "Compare" +//! INST_NEXT "IfImm" +//! INST_COUNT "LoadObject", 3 +//! 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/ + +function test_load_store_by_name_single_inlined(a) { + a.field1 = 2 + return a.field2 +} + +function test_load_store_by_name_single_properties(a) { + a.field1 = 5 + return a.field3 +} + +function test_load_store_by_name_polymorphic(a) { + a.field1 = 10 + return a.field2 +} + +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"; + } + if (a.field1 != 2) { + throw "test_load_store_by_name_single_inlined is failed for inlined case - incorrect load value"; + } +} +let b = {} +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"; + } + if (b.field1 != 5) { + throw "test_load_store_by_name_single_properties is failed - incorrect load value"; + } +} + +for (let i = 0 ; i < 1000; i++) { + if (test_load_store_by_name_polymorphic(a) != 8) { + throw "test_load_store_by_name_polymorphic is failed - incorrect return value for a"; + } + if (a.field1 != 10) { + throw "test_load_store_by_name_polymorphic is failed - incorrect load value for a"; + } + if (test_load_store_by_name_polymorphic(b) != 2) { + throw "test_load_store_by_name_polymorphic is failed - incorrect return value for b"; + } + if (b.field1 != 10) { + throw "test_load_store_by_name_polymorphic is failed - incorrect load value for b"; + } +} + +test_load_store_by_name_single_inlined(b) + +var get_type_error = false; +try { + test_load_store_by_name_single_properties(10) +} catch (e) { + if (e instanceof TypeError) { + get_type_error = true; + } +} +if (!get_type_error) { + throw "don't catch type error"; +}