diff --git a/compiler/ecmascript_extensions/ecmascript_compiler_interface.h b/compiler/ecmascript_extensions/ecmascript_compiler_interface.h index a16e515108fcb0e90523e94abe027f3b995b71de..fa353f77b7b221585064f0fa9528c62564950978 100644 --- a/compiler/ecmascript_extensions/ecmascript_compiler_interface.h +++ b/compiler/ecmascript_extensions/ecmascript_compiler_interface.h @@ -53,6 +53,20 @@ virtual bool GetProfileDataForNamedAccess( return false; } +virtual bool GetProfileDataForValueAccess( + [[maybe_unused]] RuntimeInterface::MethodPtr m, [[maybe_unused]] uintptr_t slot_id, + [[maybe_unused]] ArenaVector *profile) +{ + return false; +} + +virtual bool GetProfileDataForValueAccess( + [[maybe_unused]] RuntimeInterface::MethodPtr m, [[maybe_unused]] uintptr_t func, [[maybe_unused]] uintptr_t slot_id, + [[maybe_unused]] ArenaVector *profile) +{ + return false; +} + virtual size_t GetLexicalEnvParentEnvIndex() const { return 0; diff --git a/compiler/intrinsics_inline_ecmascript.inl b/compiler/intrinsics_inline_ecmascript.inl index 97e6c1df73418f08a8211c4a877e36e702f7ecb1..89930bcbc4ea04515092a22cd3ff3715293fe04f 100644 --- a/compiler/intrinsics_inline_ecmascript.inl +++ b/compiler/intrinsics_inline_ecmascript.inl @@ -150,6 +150,12 @@ case RuntimeInterface::IntrinsicId::INTRINSIC_LD_OBJ_BY_NAME: { case RuntimeInterface::IntrinsicId::INTRINSIC_ST_OBJ_BY_NAME: { return InlineStObjByName(intrinsic); } +case RuntimeInterface::IntrinsicId::INTRINSIC_LD_OBJ_BY_VALUE: { + return InlineLdObjByValue(intrinsic); +} +case RuntimeInterface::IntrinsicId::INTRINSIC_ST_OBJ_BY_VALUE: { + return InlineStObjByValue(intrinsic); +} case RuntimeInterface::IntrinsicId::INTRINSIC_RESOLVE_ALLOC_RESULT: { return InlineResolveAllocResult(intrinsic); } diff --git a/compiler/intrinsics_type_resolving_ecmascript.cpp b/compiler/intrinsics_type_resolving_ecmascript.cpp index 30e353c87c0add765f610e48edb8823ff376559f..65b63c9807a4f5ac4576e0395cfde166888013c2 100644 --- a/compiler/intrinsics_type_resolving_ecmascript.cpp +++ b/compiler/intrinsics_type_resolving_ecmascript.cpp @@ -152,26 +152,38 @@ Inst *TypesResolving::CreateCompareClassWithDeopt(uint32_t pc, Inst *get_cls_ins return deopt_inst; } -template +Inst *InsertLoadObject(Inst *input, uint32_t type_id, RuntimeInterface::MethodPtr method, uint32_t pc, + RuntimeInterface::FieldPtr field, ObjectType obj_type, Inst *insert_after, + DataType::Type data_type) +{ + auto load_object = input->GetBasicBlock()->GetGraph()->CreateInstLoadObject(data_type, pc); + load_object->SetInput(0, input); + load_object->SetTypeId(type_id); + load_object->SetMethod(method); + load_object->SetObjField(field); + load_object->SetObjectType(obj_type); + insert_after->InsertAfter(load_object); + return load_object; +} + +template 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 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_DYN_INLINED); - insert_after->InsertAfter(load_object); - + auto load_object = InsertLoadObject(obj_inst, offset, save_state->GetMethod(), pc, nullptr, + ObjectType::MEM_DYN_INLINED, insert_after, DataType::ANY); 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()); + if constexpr (BY_NAME) { + store_object->SetInput(1, intrinsic->GetInput(1).GetInst()); + } else { + store_object->SetInput(1, intrinsic->GetInput(2).GetInst()); + } store_object->SetTypeId(offset); store_object->SetMethod(save_state->GetMethod()); store_object->SetObjField(nullptr); @@ -183,34 +195,36 @@ Inst *InsertMemFromFieldInlined(IntrinsicInst *intrinsic, Inst *obj_inst, Inst * } } -template +Inst *InsertBoundsCheck(Inst *array, Inst *index, Inst *save_state, uint32_t pc, Inst *insert_after) +{ + auto graph = array->GetBasicBlock()->GetGraph(); + auto array_length = graph->CreateInstLenArray(DataType::INT32, pc); + array_length->SetInput(0, array); + + // 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); + + return bounds_check; +} + +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); + auto load_object = InsertLoadObject(obj_inst, TypeIdMixin::MEM_DYN_PROPS_ID, save_state->GetMethod(), pc, nullptr, + ObjectType::MEM_DYN_PROPS, insert_after, DataType::REFERENCE); 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); + auto bounds_check = InsertBoundsCheck(load_object, index, save_state, pc, insert_after); insert_after = bounds_check; index = bounds_check; @@ -226,7 +240,11 @@ Inst *InsertMemFromField(IntrinsicInst *intrinsic, Inst *obj_inst, Inst *insert_ 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()); + if constexpr (BY_NAME) { + store_array->SetInput(2, intrinsic->GetInput(1).GetInst()); + } else { + store_array->SetInput(2, intrinsic->GetInput(2).GetInst()); + } store_array->SetNeedBarrier(true); array = store_array; } @@ -264,31 +282,17 @@ Inst *InsertPrototypeCheckInst(IntrinsicInst *intrinsic, uintptr_t proto_mem, In (*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); + auto prototype = InsertLoadObject(proto_handler, TypeIdMixin::MEM_DYN_PROTO_HOLDER_ID, save_state->GetMethod(), pc, + nullptr, ObjectType::MEM_DYN_PROTO_HOLDER, proto_handler, DataType::REFERENCE); // 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); + auto prototype_marker = + InsertLoadObject(proto_handler, TypeIdMixin::MEM_DYN_PROTO_CELL_ID, save_state->GetMethod(), pc, nullptr, + ObjectType::MEM_DYN_PROTO_CELL, prototype, DataType::REFERENCE); // 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 is_change = InsertLoadObject(prototype_marker, TypeIdMixin::MEM_DYN_CHANGE_FIELD_ID, save_state->GetMethod(), + pc, nullptr, ObjectType::MEM_DYN_CHANGE_FIELD, prototype_marker, DataType::BOOL); auto deopt_inst = graph->CreateInstDeoptimizeIf(DataType::NO_TYPE, pc); deopt_inst->SetDeoptimizeType(DeoptimizeType::INLINE_IC); @@ -301,35 +305,35 @@ Inst *InsertPrototypeCheckInst(IntrinsicInst *intrinsic, uintptr_t proto_mem, In return prototype; } -template -Inst *InsertAccessByNameInst(IntrinsicInst *intrinsic, Inst *obj_inst, Inst *insert_after, - RuntimeInterface::NamedAccessProfileData profile) +template +Inst *InsertAccessInst(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); + return InsertMemFromFieldInlined(intrinsic, obj_inst, insert_after, offset); case RuntimeInterface::NamedAccessProfileType::FIELD: - return InsertMemFromField(intrinsic, obj_inst, insert_after, offset); + 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); + return InsertMemFromFieldInlined(intrinsic, obj_inst, store_class, offset); } case RuntimeInterface::NamedAccessProfileType::TRANSITION: { ASSERT(!IS_LOAD); - auto store = InsertMemFromField(intrinsic, obj_inst, insert_after, offset); + 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); + 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); + return InsertMemFromField(intrinsic, load_proto, insert_after, offset); } default: UNREACHABLE(); @@ -372,7 +376,17 @@ bool TypesResolving::InlineStObjByName(IntrinsicInst *intrinsic) return InlineObjByName(intrinsic); } -bool TypesResolving::GetICForMemAccess(IntrinsicInst *intrinsic) +bool TypesResolving::InlineLdObjByValue(IntrinsicInst *intrinsic) +{ + return InlineObjByValue(intrinsic); +} + +bool TypesResolving::InlineStObjByValue(IntrinsicInst *intrinsic) +{ + return InlineObjByValue(intrinsic); +} + +bool TypesResolving::GetICForMemNamedAccess(IntrinsicInst *intrinsic) { auto pc = intrinsic->GetPc(); auto runtime = GetGraph()->GetRuntime(); @@ -392,6 +406,26 @@ bool TypesResolving::GetICForMemAccess(IntrinsicInst *intrinsic) return true; } +bool TypesResolving::GetICForMemValueAccess(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->GetProfileDataForValueAccess(GetGraph()->GetMethod(), func, pc, &named_access_profile_)) { + return false; + } + } else if (!runtime->GetProfileDataForValueAccess(GetGraph()->GetMethod(), pc, &named_access_profile_)) { + return false; + } + return true; +} + Inst *TypesResolving::InsertCheckAndCastInstructions(IntrinsicInst *intrinsic) { auto pc = intrinsic->GetPc(); @@ -410,6 +444,7 @@ Inst *TypesResolving::InsertCheckAndCastInstructions(IntrinsicInst *intrinsic) cast_value->SetAnyType(AnyBaseType::ECMASCRIPT_HEAP_OBJECT_TYPE); cast_value->SetFlag(inst_flags::NO_HOIST); any_check->InsertAfter(cast_value); + return cast_value; } @@ -425,7 +460,7 @@ void TypesResolving::InlineObjByNameMonomorphic(IntrinsicInst *intrinsic, Inst * prof.type == RuntimeInterface::NamedAccessProfileType::PROTOTYPE_INLINED) { obj = get_cls_inst; } - [[maybe_unused]] auto load_inst = InsertAccessByNameInst(intrinsic, obj, deopt, prof); + [[maybe_unused]] auto load_inst = InsertAccessInst(intrinsic, obj, deopt, prof); if constexpr (IS_LOAD) { intrinsic->ReplaceUsers(load_inst); @@ -474,7 +509,7 @@ void TypesResolving::InlineObjByNamePolymorphic(IntrinsicInst *intrinsic, Inst * obj = get_cls_inst; } - [[maybe_unused]] auto load = InsertAccessByNameInst(intrinsic, obj, last_ss, prof); + [[maybe_unused]] auto load = InsertAccessInst(intrinsic, obj, last_ss, prof); if constexpr (IS_LOAD) { phi_inst->AppendInput(load); @@ -493,6 +528,171 @@ void TypesResolving::InlineObjByNamePolymorphic(IntrinsicInst *intrinsic, Inst * intrinsic->GetBasicBlock()->RemoveInst(intrinsic); } +void InsertCheckKeyInstructions(IntrinsicInst *intrinsic, uintptr_t key_mem) +{ + auto graph = intrinsic->GetBasicBlock()->GetGraph(); + auto pc = intrinsic->GetPc(); + auto save_state = intrinsic->GetSaveState(); + auto key_ic = graph->CreateInstLoadObjFromConst(DataType::ANY, pc, key_mem); + auto cmp_inst = graph->CreateInstCompare(DataType::BOOL, pc, ConditionCode::CC_NE); + cmp_inst->SetInput(0, key_ic); + cmp_inst->SetInput(1, intrinsic->GetInput(1).GetInst()); + cmp_inst->SetOperandsType(DataType::ANY); + auto deopt_inst = graph->CreateInstDeoptimizeIf(DataType::NO_TYPE, pc); + deopt_inst->SetDeoptimizeType(DeoptimizeType::INLINE_IC); + deopt_inst->SetInput(0, cmp_inst); + deopt_inst->SetSaveState(save_state); + intrinsic->InsertBefore(key_ic); + intrinsic->InsertBefore(cmp_inst); + intrinsic->InsertBefore(deopt_inst); +} + +template +void TypesResolving::InlineObjByValueWithKey(IntrinsicInst *intrinsic) +{ + auto pc = intrinsic->GetPc(); + auto save_state = intrinsic->GetSaveState(); + auto prof = named_access_profile_[0]; + InsertCheckKeyInstructions(intrinsic, prof.key); + auto cast_value = InsertCheckAndCastInstructions(intrinsic); + auto get_cls_inst = + InsertLoadObject(cast_value, TypeIdMixin::MEM_DYN_CLASS_ID, intrinsic->GetSaveState()->GetMethod(), pc, nullptr, + ObjectType::MEM_DYN_CLASS, cast_value, DataType::REFERENCE); + + auto deopt = CreateCompareClassWithDeopt(pc, get_cls_inst, prof.klass, save_state); + Inst *obj = cast_value; + if (prof.type == RuntimeInterface::NamedAccessProfileType::PROTOTYPE || + prof.type == RuntimeInterface::NamedAccessProfileType::PROTOTYPE_INLINED) { + obj = get_cls_inst; + } + [[maybe_unused]] auto load_inst = InsertAccessInst(intrinsic, obj, deopt, prof); + + if constexpr (IS_LOAD) { + intrinsic->ReplaceUsers(load_inst); + } + + intrinsic->SetInlined(false); + intrinsic->GetBasicBlock()->RemoveInst(intrinsic); +} + +template +void TypesResolving::InlineObjByValueFromElements(IntrinsicInst *intrinsic) +{ + auto pc = intrinsic->GetPc(); + auto save_state = intrinsic->GetSaveState(); + + // Check that input is heap object + auto cast_value = InsertCheckAndCastInstructions(intrinsic); + + // Insert Load class for the object + auto get_cls_inst = InsertLoadObject(cast_value, TypeIdMixin::MEM_DYN_CLASS_ID, save_state->GetMethod(), pc, + nullptr, ObjectType::MEM_DYN_CLASS, cast_value, DataType::REFERENCE); + + // check objects classes with first class from IC + auto load_cls_inst = GetGraph()->CreateInstClassImmediate(DataType::REFERENCE, pc, named_access_profile_[0].klass); + 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); + get_cls_inst->InsertAfter(load_cls_inst); + load_cls_inst->InsertAfter(cmp_inst); + Inst *deopt_input = cmp_inst; + if (named_access_profile_.size() == 2) { + // check objects classes with second class from IC + auto load_cls_inst_1 = + GetGraph()->CreateInstClassImmediate(DataType::REFERENCE, pc, named_access_profile_[1].klass); + auto cmp_inst_1 = GetGraph()->CreateInstCompare(DataType::BOOL, pc, ConditionCode::CC_NE); + cmp_inst_1->SetInput(0, get_cls_inst); + cmp_inst_1->SetInput(1, load_cls_inst_1); + cmp_inst_1->SetOperandsType(DataType::REFERENCE); + auto and_inst = GetGraph()->CreateInstAnd(DataType::BOOL, pc); + and_inst->SetInput(0, cmp_inst); + and_inst->SetInput(1, cmp_inst_1); + deopt_input = and_inst; + cmp_inst->InsertAfter(load_cls_inst_1); + load_cls_inst_1->InsertAfter(cmp_inst_1); + cmp_inst_1->InsertAfter(and_inst); + } + // Insert deoptimize + auto deopt_inst = GetGraph()->CreateInstDeoptimizeIf(DataType::NO_TYPE, pc); + deopt_inst->SetDeoptimizeType(DeoptimizeType::INLINE_IC); + deopt_inst->SetInput(0, deopt_input); + deopt_inst->SetSaveState(save_state); + deopt_input->InsertAfter(deopt_inst); + + auto key = intrinsic->GetInput(1).GetInst(); + auto any_check = GetGraph()->CreateInstAnyTypeCheck(DataType::ANY, pc); + any_check->SetInput(0, key); + any_check->SetInput(1, save_state); + any_check->SetAnyType(AnyBaseType::ECMASCRIPT_INT_TYPE); + intrinsic->InsertBefore(any_check); + + auto cast_value_int = GetGraph()->CreateInstCastAnyTypeValue(DataType::INT32, pc); + cast_value_int->SetInput(0, any_check); + // NOLINTNEXTLINE(readability-magic-numbers) + cast_value_int->SetAnyType(AnyBaseType::ECMASCRIPT_INT_TYPE); + cast_value_int->SetFlag(inst_flags::NO_HOIST); + any_check->InsertAfter(cast_value_int); + + // Insert Load Object for Elements array + auto load_object = InsertLoadObject(cast_value, TypeIdMixin::MEM_DYN_ELEMENTS_ID, save_state->GetMethod(), pc, + nullptr, ObjectType::MEM_DYN_ELEMENTS, cast_value_int, DataType::REFERENCE); + + // Insert LenArray and BoundChecks + auto bounds_check = InsertBoundsCheck(load_object, cast_value_int, save_state, pc, load_object); + + if constexpr (IS_LOAD) { + auto load_array = GetGraph()->CreateInstLoadArray(DataType::ANY, pc); + + load_array->SetInput(0, load_object); + load_array->SetInput(1, bounds_check); + intrinsic->ReplaceUsers(load_array); + bounds_check->InsertAfter(load_array); + auto cmp_hole_inst = GetGraph()->CreateInstCompare(DataType::BOOL, pc, ConditionCode::CC_EQ); + cmp_hole_inst->SetInput(0, load_array); + cmp_hole_inst->SetInput( + 1, GetGraph()->FindOrCreateConstant(DataType::Any(panda::coretypes::TaggedValue::VALUE_HOLE))); + cmp_hole_inst->SetOperandsType(DataType::ANY); + load_array->InsertAfter(cmp_hole_inst); + + auto deopt_hole_inst = GetGraph()->CreateInstDeoptimizeIf(DataType::NO_TYPE, pc); + deopt_hole_inst->SetDeoptimizeType(DeoptimizeType::HOLE); + deopt_hole_inst->SetInput(0, cmp_hole_inst); + deopt_hole_inst->SetSaveState(save_state); + cmp_hole_inst->InsertAfter(deopt_hole_inst); + } else { + auto store_array = GetGraph()->CreateInstStoreArray(DataType::ANY, pc); + + store_array->SetInput(0, load_object); + store_array->SetInput(1, bounds_check); + store_array->SetInput(2, intrinsic->GetInput(2).GetInst()); + store_array->SetNeedBarrier(true); + bounds_check->InsertAfter(store_array); + } + intrinsic->SetInlined(false); + intrinsic->GetBasicBlock()->RemoveInst(intrinsic); +} + +template +bool TypesResolving::InlineObjByValue(IntrinsicInst *intrinsic) +{ + if (GetGraph()->IsAotMode()) { + return false; + } + ASSERT(intrinsic->GetSaveState() != nullptr); + if (!GetICForMemValueAccess(intrinsic)) { + return false; + } + ASSERT(!named_access_profile_.empty()); + if (named_access_profile_[0].key == 0) { + InlineObjByValueFromElements(intrinsic); + return true; + } + + InlineObjByValueWithKey(intrinsic); + return false; +} + template bool TypesResolving::InlineObjByName(IntrinsicInst *intrinsic) { @@ -501,20 +701,16 @@ bool TypesResolving::InlineObjByName(IntrinsicInst *intrinsic) } auto pc = intrinsic->GetPc(); ASSERT(intrinsic->GetSaveState() != nullptr); - if (!GetICForMemAccess(intrinsic)) { + if (!GetICForMemNamedAccess(intrinsic)) { return false; } ASSERT(!named_access_profile_.empty()); auto cast_value = InsertCheckAndCastInstructions(intrinsic); + auto get_cls_inst = + InsertLoadObject(cast_value, TypeIdMixin::MEM_DYN_CLASS_ID, intrinsic->GetSaveState()->GetMethod(), pc, nullptr, + ObjectType::MEM_DYN_CLASS, cast_value, DataType::REFERENCE); - 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) { InlineObjByNameMonomorphic(intrinsic, get_cls_inst, cast_value); return true; diff --git a/compiler/intrinsics_type_resolving_ecmascript.inl.h b/compiler/intrinsics_type_resolving_ecmascript.inl.h index ae5ec59de539394ffa9f83b3773074868f705147..4ba537c9c6aead78f6d820b786b5e666cf7b059c 100644 --- a/compiler/intrinsics_type_resolving_ecmascript.inl.h +++ b/compiler/intrinsics_type_resolving_ecmascript.inl.h @@ -26,19 +26,29 @@ bool InlineLdInfinity(IntrinsicInst *intrinsic); bool InlineLdNan(IntrinsicInst *intrinsic); bool InlineLdObjByName(IntrinsicInst *intrinsic); bool InlineStObjByName(IntrinsicInst *intrinsic); +bool InlineLdObjByValue(IntrinsicInst *intrinsic); +bool InlineStObjByValue(IntrinsicInst *intrinsic); bool InlineResolveAllocResult(IntrinsicInst *intrinsic); bool InlineTypeOf(IntrinsicInst *intrinsic); private: template bool InlineObjByName(IntrinsicInst *intrinsic); +template +bool InlineObjByValue(IntrinsicInst *intrinsic); + +template +void InlineObjByValueWithKey(IntrinsicInst *intrinsic); +template +void InlineObjByValueFromElements(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); +bool GetICForMemNamedAccess(IntrinsicInst *intrinsic); +bool GetICForMemValueAccess(IntrinsicInst *intrinsic); Inst *InsertCheckAndCastInstructions(IntrinsicInst *intrinsic); void CreateCompareClass(uint32_t pc, Inst *get_cls_inst, RuntimeInterface::ClassPtr receiver, BasicBlock *load_bb); diff --git a/compiler/optimizer/ecma_pipeline.cpp b/compiler/optimizer/ecma_pipeline.cpp index 76dddcf6966829ae399ca8efc1116b0eb013a912..85ade812308fa03b1687024264a15d638d3aed57 100644 --- a/compiler/optimizer/ecma_pipeline.cpp +++ b/compiler/optimizer/ecma_pipeline.cpp @@ -87,6 +87,7 @@ bool EcmaPipeline::RunOptimizations() graph->RunPass(); if (graph->RunPass() && graph->RunPass()) { graph->RunPass(); + graph->RunPass(); } graph->RunPass(); if (graph->IsAotMode()) { diff --git a/runtime/class_linker/panda_file_translator.cpp b/runtime/class_linker/panda_file_translator.cpp index 669faa6fb477f80465d2d64066bffe88db61d275..6ffae90a5e64fa8ed067725ddf6dc5cb0eaecb4e 100644 --- a/runtime/class_linker/panda_file_translator.cpp +++ b/runtime/class_linker/panda_file_translator.cpp @@ -149,6 +149,7 @@ void PandaFileTranslator::TranslateMethod(const compiler::AotClass &aot_class, c method->SetCallTypeFromAnnotation(); method->InitProfileVector(); const uint8_t *insns = code_data_accessor.GetInstructions(); + if (translated_code_.find(insns) == translated_code_.end()) { translated_code_.insert(insns); if (pf.GetHeader()->quickened_flag != 0U) { diff --git a/runtime/compiler/ecmascript_runtime_interface.cpp b/runtime/compiler/ecmascript_runtime_interface.cpp index 0bcce53121c64f2064a9ad52437fe229f2e430b6..d1860b45b655f9d28f794e7d3b0057a8c8dd9f88 100644 --- a/runtime/compiler/ecmascript_runtime_interface.cpp +++ b/runtime/compiler/ecmascript_runtime_interface.cpp @@ -378,7 +378,7 @@ RuntimeInterface::NewObjDynInfo EcmaRuntimeInterface::GetNewObjDynInfo(uintptr_t bool EcmaRuntimeInterface::AddProfileInfo(PandaRuntimeInterface::MethodPtr m, ArenaVector *profile, - ProfileTypeInfo *profile_type_info, uint8_t slot) + ProfileTypeInfo *profile_type_info, uint8_t slot, uintptr_t key) { JSTaggedValue hclass_value = profile_type_info->Get(slot); if (!hclass_value.IsHeapObject()) { @@ -403,7 +403,7 @@ bool EcmaRuntimeInterface::AddProfileInfo(PandaRuntimeInterface::MethodPtr m, auto offset = is_inlined ? static_cast(index * JSTaggedValue::TaggedTypeSize()) : static_cast(index); AddObjectHandle(method, hclass_value.GetHeapObject()); - profile->push_back({klass, 0, offset, type}); + profile->push_back({klass, 0, key, offset, type}); return true; } return false; @@ -424,7 +424,7 @@ bool EcmaRuntimeInterface::AddProfileInfo(PandaRuntimeInterface::MethodPtr m, 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}); + profile->push_back({klass, reinterpret_cast(new_h_class->GetHClass()), key, offset, type}); return true; } if (handler.IsPrototypeHandler()) { @@ -454,34 +454,117 @@ bool EcmaRuntimeInterface::AddProfileInfo(PandaRuntimeInterface::MethodPtr m, return false; } AddObjectHandle(method, hclass_value.GetHeapObject()); - profile->push_back({klass, ref_addr, offset, type}); + profile->push_back({klass, ref_addr, key, offset, type}); return true; } return false; } -bool EcmaRuntimeInterface::GetProfileDataForNamedAccess(PandaRuntimeInterface::MethodPtr m, uintptr_t slot_id, - ArenaVector *profile) +bool EcmaRuntimeInterface::AddElementInfo(ArenaVector *profile, + ProfileTypeInfo *profile_type_info, uint8_t slot) { - if (profile == nullptr) { + JSTaggedValue hclass_value = profile_type_info->Get(slot); + if (!hclass_value.IsHeapObject()) { return false; } - profile->clear(); - ScopedMutatorLock lock; + ASSERT(hclass_value.IsJSHClass()); + auto klass = static_cast(hclass_value.GetHeapObject())->GetHClass(); + JSTaggedValue handler = profile_type_info->Get(slot + 1); + if (UNLIKELY(handler.IsUndefined())) { + return false; + } + ASSERT(handler.IsInt()); + auto handler_info = static_cast(handler.GetInt()); + // TODO(pishin) support for double + if (UNLIKELY(!HandlerBase::IsKeyInt(handler_info))) { + return false; + } + if (HandlerBase::IsArrayOverflowed(handler_info)) { + return false; + } + + profile->push_back({klass, 0, 0, 0, NamedAccessProfileType::ELEMENT}); + return true; +} + +bool EcmaRuntimeInterface::AddProfileValueInfo(PandaRuntimeInterface::MethodPtr m, + ArenaVector *profile, + ProfileTypeInfo *profile_type_info, uint8_t slot) +{ + JSTaggedValue first_value = profile_type_info->Get(slot); + if (!first_value.IsHeapObject()) { + return false; + } + if (first_value.IsJSHClass()) { + if (AddElementInfo(profile, profile_type_info, slot)) { + AddElementInfo(profile, profile_type_info, slot + 2); + return true; + } + return false; + } + auto ref_addr = AddFixedObjectHandle(MethodCast(m), first_value.GetHeapObject()); + if (ref_addr == 0) { + return false; + } + return AddProfileInfo(m, profile, profile_type_info, slot + 1, ref_addr); +} + +inline ProfileTypeInfo *EcmaRuntimeInterface::GetProfileTypeInfo(PandaRuntimeInterface::MethodPtr m, uintptr_t slot_id, + uint8_t *slot) +{ os::memory::LockHolder lock_m(mutex_); uint8_t *mapping = JsMethodCast(m)->GetICMapping(); if (mapping == nullptr) { - return false; + return nullptr; } // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) - auto slot = mapping[slot_id]; + *slot = mapping[slot_id]; auto it = js_function_table_.find(MethodCast(m)); if (it == js_function_table_.end()) { - return false; + return nullptr; } auto func = JSFunction::Cast(ecma_vm_->GetGlobalObjectStorage()->Get(it->second)); - ProfileTypeInfo *profile_type_info = ProfileTypeInfo::Cast(func->GetProfileTypeInfo().GetTaggedObject()); + return ProfileTypeInfo::Cast(func->GetProfileTypeInfo().GetTaggedObject()); +} + +inline ProfileTypeInfo *EcmaRuntimeInterface::GetProfileTypeInfo(uintptr_t func_address, uintptr_t slot_id, + uint8_t *slot) +{ + 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 nullptr; + } + auto func = JSFunction::Cast(js_func.GetHeapObject()); + uint8_t *mapping = func->GetMethod()->GetICMapping(); + if (mapping == nullptr) { + return nullptr; + } + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + *slot = mapping[slot_id]; + auto profile_info_obj = func->GetProfileTypeInfo(); + if (profile_info_obj.IsUndefined()) { + return nullptr; + } + + return ProfileTypeInfo::Cast(profile_info_obj.GetTaggedObject()); +} + +bool EcmaRuntimeInterface::GetProfileDataForNamedAccess(PandaRuntimeInterface::MethodPtr m, uintptr_t slot_id, + ArenaVector *profile) +{ + if (profile == nullptr) { + return false; + } + profile->clear(); + ScopedMutatorLock lock; + uint8_t slot; + ProfileTypeInfo *profile_type_info = GetProfileTypeInfo(m, slot_id, &slot); + if (profile_type_info == nullptr) { + return false; + } if (AddProfileInfo(m, profile, profile_type_info, slot)) { AddProfileInfo(m, profile, profile_type_info, slot + 2); return true; @@ -498,26 +581,12 @@ bool EcmaRuntimeInterface::GetProfileDataForNamedAccess(PandaRuntimeInterface::M 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()) { + uint8_t slot; + ProfileTypeInfo *profile_type_info = GetProfileTypeInfo(func_address, slot_id, &slot); + if (profile_type_info == nullptr) { return false; } - - ProfileTypeInfo *profile_type_info = ProfileTypeInfo::Cast(profile_info_obj.GetTaggedObject()); if (AddProfileInfo(m, profile, profile_type_info, slot)) { AddProfileInfo(m, profile, profile_type_info, slot + 2); return true; @@ -525,6 +594,41 @@ bool EcmaRuntimeInterface::GetProfileDataForNamedAccess(PandaRuntimeInterface::M return false; } +bool EcmaRuntimeInterface::GetProfileDataForValueAccess(PandaRuntimeInterface::MethodPtr m, uintptr_t slot_id, + ArenaVector *profile) +{ + if (profile == nullptr) { + return false; + } + profile->clear(); + ScopedMutatorLock lock; + uint8_t slot; + ProfileTypeInfo *profile_type_info = GetProfileTypeInfo(m, slot_id, &slot); + if (profile_type_info == nullptr) { + return false; + } + + return AddProfileValueInfo(m, profile, profile_type_info, slot); +} + +bool EcmaRuntimeInterface::GetProfileDataForValueAccess(PandaRuntimeInterface::MethodPtr m, uintptr_t func_address, + uintptr_t slot_id, ArenaVector *profile) +{ + if (profile == nullptr) { + return false; + } + profile->clear(); + ASSERT(func_address != 0); + ScopedMutatorLock lock; + + uint8_t slot; + ProfileTypeInfo *profile_type_info = GetProfileTypeInfo(func_address, slot_id, &slot); + if (profile_type_info == nullptr) { + return false; + } + + return AddProfileValueInfo(m, profile, profile_type_info, slot); +} void EcmaRuntimeInterface::CleanFunction(Method *method) { os::memory::LockHolder lock(mutex_); diff --git a/runtime/compiler/ecmascript_runtime_interface.h b/runtime/compiler/ecmascript_runtime_interface.h index 205d06449b610f84463ca72b4ce4db55a18197e5..865730649164c3f66338e9596418d7978e53960f 100644 --- a/runtime/compiler/ecmascript_runtime_interface.h +++ b/runtime/compiler/ecmascript_runtime_interface.h @@ -154,6 +154,10 @@ public: ArenaVector *profile) override; bool GetProfileDataForNamedAccess(PandaRuntimeInterface::MethodPtr m, uintptr_t func_address, uintptr_t slot_id, ArenaVector *profile) override; + bool GetProfileDataForValueAccess(PandaRuntimeInterface::MethodPtr m, uintptr_t slot_id, + ArenaVector *profile) override; + bool GetProfileDataForValueAccess(PandaRuntimeInterface::MethodPtr m, uintptr_t func_address, uintptr_t slot_id, + ArenaVector *profile) override; void CleanFunction(Method *method); @@ -178,7 +182,12 @@ private: 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); + ProfileTypeInfo *profile_type_info, uint8_t slot, uintptr_t key = 0); + bool AddProfileValueInfo(PandaRuntimeInterface::MethodPtr m, ArenaVector *profile, + ProfileTypeInfo *profile_type_info, uint8_t slot); + bool AddElementInfo(ArenaVector *profile, ProfileTypeInfo *profile_type_info, uint8_t slot); + ProfileTypeInfo *GetProfileTypeInfo(PandaRuntimeInterface::MethodPtr m, uintptr_t slot_id, uint8_t *slot); + ProfileTypeInfo *GetProfileTypeInfo(uintptr_t func_address, uintptr_t slot_id, uint8_t *slot); size_t GetLexicalEnvParentEnvIndex() const override; diff --git a/runtime/ic/ic_handler-inl.h b/runtime/ic/ic_handler-inl.h index 6c49e992ae05c2b4098629c53469461f9c1200ab..ad87126cad406088487105b2689c1b25e7106fe0 100644 --- a/runtime/ic/ic_handler-inl.h +++ b/runtime/ic/ic_handler-inl.h @@ -22,10 +22,11 @@ #include "ic_handler.h" namespace panda::ecmascript { -JSHandle LoadHandler::LoadElement(const JSThread *thread) +JSHandle LoadHandler::LoadElement(const JSThread *thread, bool is_int) { uint32_t handler = 0; KindBit::Set(HandlerKind::ELEMENT, &handler); + IsKeyIntBit::Set(is_int, &handler); return JSHandle(thread, JSTaggedValue(handler)); } @@ -91,10 +92,12 @@ JSHandle PrototypeHandler::StorePrototype(const JSThread *thread, return JSHandle::Cast(handler); } -JSHandle StoreHandler::StoreElement(const JSThread *thread, JSHandle receiver) +JSHandle StoreHandler::StoreElement(const JSThread *thread, JSHandle receiver, + bool is_int) { uint32_t handler = 0; KindBit::Set(HandlerKind::ELEMENT, &handler); + IsKeyIntBit::Set(is_int, &handler); if (receiver->IsJSArray()) { IsJSArrayBit::Set(true, &handler); diff --git a/runtime/ic/ic_handler.h b/runtime/ic/ic_handler.h index aa090573eba07d1a6f85a5478773fe545e61bb40..bbbcbfaa58d058e04c2e11c1efa6f1c6c33acc0d 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 ObservedOverflowArrayBit = IsJSArrayBit::NextFlag; + using IsKeyIntBit = IsJSArrayBit::NextFlag; + using ObservedOverflowArrayBit = IsKeyIntBit::NextFlag; using OffsetBit = ObservedOverflowArrayBit::NextField; HandlerBase() = default; @@ -101,6 +102,16 @@ public: ObservedOverflowArrayBit::Set(true, &handler); } + static inline bool IsKeyInt(uint32_t handler) + { + return IsKeyIntBit::Get(handler); + } + + static inline void SetKeyNotInt(uint32_t &handler) + { + IsKeyIntBit::Set(false, &handler); + } + static inline int GetOffset(uint32_t handler) { return OffsetBit::Get(handler); @@ -110,13 +121,14 @@ public: class LoadHandler final : public HandlerBase { public: static inline JSHandle LoadProperty(const JSThread *thread, const ObjectOperator &op); - static inline JSHandle LoadElement(const JSThread *thread); + static inline JSHandle LoadElement(const JSThread *thread, bool is_int); }; class StoreHandler final : public HandlerBase { public: static inline JSHandle StoreProperty(const JSThread *thread, const ObjectOperator &op); - static inline JSHandle StoreElement(const JSThread *thread, JSHandle receiver); + static inline JSHandle StoreElement(const JSThread *thread, JSHandle receiver, + bool is_int); }; class TransitionHandler : public TaggedObject { diff --git a/runtime/ic/ic_runtime.cpp b/runtime/ic/ic_runtime.cpp index 01498ae85bb9405ad638d7ccc9436b050ab32fdc..08cc45913caa612ae3a07b159e61220f77be4914 100644 --- a/runtime/ic/ic_runtime.cpp +++ b/runtime/ic/ic_runtime.cpp @@ -42,7 +42,7 @@ void ICRuntime::UpdateLoadHandler(const ObjectOperator &op, JSHandleIsDictionaryElement()) { return; } - handler_value = LoadHandler::LoadElement(thread_); + handler_value = LoadHandler::LoadElement(thread_, key.IsEmpty() ? false : key->IsInt()); } else { if (!op.IsFound()) { handler_value = PrototypeHandler::LoadPrototype(thread_, op, hclass); @@ -78,7 +78,7 @@ void ICRuntime::UpdateStoreHandler(const ObjectOperator &op, JSHandle handler_value; if (op.IsElement()) { - handler_value = StoreHandler::StoreElement(thread_, receiver); + handler_value = StoreHandler::StoreElement(thread_, receiver, key.IsEmpty() ? false : key->IsInt()); } else { ASSERT(op.IsFound()); if (op.IsOnPrototype()) { diff --git a/runtime/ic/ic_runtime_stub-inl.h b/runtime/ic/ic_runtime_stub-inl.h index 9f5b0ab4c0ea4946b940a5286857e5e51482d724..c124b677ab51ecaa9f0dc3295bb0eb1381cb7ff4 100644 --- a/runtime/ic/ic_runtime_stub-inl.h +++ b/runtime/ic/ic_runtime_stub-inl.h @@ -187,6 +187,12 @@ JSTaggedValue ICRuntimeStub::LoadICByValue(JSThread *thread, JSFunction *func, J // Check element if (first_value.GetTaggedObject() == hclass) { ASSERT(HandlerBase::IsElement(profile_type_info->Get(slot_id + 1).GetInt())); + if (!key.IsInt()) { + JSTaggedValue handler = profile_type_info->Get(slot_id + 1); + auto handler_info = static_cast(handler.GetInt()); + HandlerBase::SetKeyNotInt(handler_info); + profile_type_info->Set(thread, slot_id + 1, JSTaggedValue(handler_info)); + } return LoadElement(JSObject::Cast(receiver.GetHeapObject()), key); } size_t index = slot_id + 2; @@ -194,6 +200,12 @@ JSTaggedValue ICRuntimeStub::LoadICByValue(JSThread *thread, JSFunction *func, J first_value = profile_type_info->Get(index); if (first_value.GetRawData() == ToUintPtr(hclass)) { ASSERT(HandlerBase::IsElement(profile_type_info->Get(index + 1).GetInt())); + if (!key.IsInt()) { + JSTaggedValue handler = profile_type_info->Get(index + 1); + auto handler_info = static_cast(handler.GetInt()); + HandlerBase::SetKeyNotInt(handler_info); + profile_type_info->Set(thread, index + 1, JSTaggedValue(handler_info)); + } return LoadElement(JSObject::Cast(receiver.GetHeapObject()), key); } index += 2; @@ -234,7 +246,18 @@ JSTaggedValue ICRuntimeStub::StoreICByValue(JSThread *thread, JSFunction *func, if (first_value.GetTaggedObject() == hclass) { JSTaggedValue handler = profile_type_info->Get(slot_id + 1); auto handler_info = static_cast(handler.GetInt()); - return StoreElement(thread, JSObject::Cast(receiver.GetHeapObject()), key, value, handler_info); + bool was_overflowed = false; + auto res = StoreElement(thread, JSObject::Cast(receiver.GetHeapObject()), key, value, handler_info, + &was_overflowed); + if (!key.IsInt()) { + HandlerBase::SetKeyNotInt(handler_info); + profile_type_info->Set(thread, slot_id + 1, JSTaggedValue(handler_info)); + } + if (was_overflowed) { + HandlerBase::SetArrayOverflowed(handler_info); + profile_type_info->Set(thread, slot_id + 1, JSTaggedValue(handler_info)); + } + return res; } size_t index = slot_id + 2; while (index < SlotSizeForICByValue()) { @@ -242,7 +265,18 @@ JSTaggedValue ICRuntimeStub::StoreICByValue(JSThread *thread, JSFunction *func, if (first_value.GetRawData() == ToUintPtr(hclass)) { JSTaggedValue handler = profile_type_info->Get(index + 1); auto handler_info = static_cast(handler.GetInt()); - return StoreElement(thread, JSObject::Cast(receiver.GetHeapObject()), key, value, handler_info); + bool was_overflowed = false; + auto res = StoreElement(thread, JSObject::Cast(receiver.GetHeapObject()), key, value, handler_info, + &was_overflowed); + if (!key.IsInt()) { + HandlerBase::SetKeyNotInt(handler_info); + profile_type_info->Set(thread, index + 1, JSTaggedValue(handler_info)); + } + if (was_overflowed) { + HandlerBase::SetArrayOverflowed(handler_info); + profile_type_info->Set(thread, index + 1, JSTaggedValue(handler_info)); + } + return res; } index += 2; } @@ -473,7 +507,7 @@ JSTaggedValue ICRuntimeStub::LoadElement(JSObject *receiver, JSTaggedValue key) } JSTaggedValue ICRuntimeStub::StoreElement(JSThread *thread, JSObject *receiver, JSTaggedValue key, JSTaggedValue value, - uint32_t handler_info) + uint32_t handler_info, bool *was_overflowed) { INTERPRETER_TRACE(thread, StoreElement); ASSERT(HandlerBase::IsElement(handler_info)); @@ -492,6 +526,7 @@ JSTaggedValue ICRuntimeStub::StoreElement(JSThread *thread, JSObject *receiver, TaggedArray *elements = TaggedArray::Cast(receiver->GetElements().GetHeapObject()); uint32_t capacity = elements->GetLength(); if (element_index >= capacity) { + *was_overflowed = true; if (JSObject::ShouldTransToDict(capacity, element_index)) { return JSTaggedValue::Hole(); } diff --git a/runtime/ic/ic_runtime_stub.h b/runtime/ic/ic_runtime_stub.h index bb77bde5dc0a8cd51d1fa136dc56cd93609037a4..94bdfae0e5991aba330263fb0c8b7443040d64e9 100644 --- a/runtime/ic/ic_runtime_stub.h +++ b/runtime/ic/ic_runtime_stub.h @@ -77,7 +77,8 @@ public: JSTaggedValue key, JSTaggedValue value, uint32_t slot_id); ARK_INLINE static inline JSTaggedValue LoadElement(JSObject *receiver, JSTaggedValue key); ARK_INLINE static inline JSTaggedValue StoreElement(JSThread *thread, JSObject *receiver, JSTaggedValue key, - JSTaggedValue value, uint32_t handler_info); + JSTaggedValue value, uint32_t handler_info, + bool *was_overflowed); ARK_INLINE static inline uint32_t TryToElementsIndex(JSTaggedValue key); private: diff --git a/tests/checked/CMakeLists.txt b/tests/checked/CMakeLists.txt index b2ca166ff5e27150b90e8a60c27c13f4a83fdc46..d3bf71611b3d2e1f73fe36119ba526f427841a9a 100644 --- a/tests/checked/CMakeLists.txt +++ b/tests/checked/CMakeLists.txt @@ -105,6 +105,7 @@ if (NOT PANDA_TARGET_ARM32) 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}/obj_by_value.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/ecma_profiling.js b/tests/checked/ecma_profiling.js index d8214757e2529c2f45da8c69fb6d4806607cbbf2..30ca63a1ac0c63b11b3e8192955677d448ea716f 100644 --- a/tests/checked/ecma_profiling.js +++ b/tests/checked/ecma_profiling.js @@ -90,6 +90,13 @@ function test_update(idx, val) { arr[idx] |= val; } +// Initializing the array +test_update(4, 0) +test_update(3, 0) +test_update(2, 0) +test_update(1, 0) +test_update(0, 0) + for (var i = 0; i < 20; i++) { test_update(i >> 2, 1 << i); } diff --git a/tests/checked/obj_by_index.js b/tests/checked/obj_by_index.js index 6d725a5fbbb9c71b9be6418a4099ef3b9f957de9..0237729904a9d9518efdaf6484d9f3a604603ef8 100644 --- a/tests/checked/obj_by_index.js +++ b/tests/checked/obj_by_index.js @@ -13,7 +13,7 @@ * limitations under the License. */ -//! CHECKER Test check sore and load by index +//! CHECKER Test check store and load by index //! RUN options: "--no-async-jit --compiler-hotness-threshold=10 --compiler-regex _GLOBAL::test_load_store_by_index", entry: "_GLOBAL::func_main_0" //! METHOD "test_load_store_by_index" //! PASS_AFTER "IrBuilder" diff --git a/tests/checked/obj_by_name.js b/tests/checked/obj_by_name.js index 66763035013ceb4b295a0d1131bd305e6c167463..e9913df18c3f6665fc75c0c3d2959d637ac310c2 100644 --- a/tests/checked/obj_by_name.js +++ b/tests/checked/obj_by_name.js @@ -13,7 +13,7 @@ * limitations under the License. */ -//! CHECKER Test check store and load by index +//! CHECKER Test check store and load by name //! 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_field_inlined" //! PASS_AFTER "TypesResolving" diff --git a/tests/checked/obj_by_value.js b/tests/checked/obj_by_value.js new file mode 100644 index 0000000000000000000000000000000000000000..b5e387040e26f80aed3bdb0caf9e9f89de936576 --- /dev/null +++ b/tests/checked/obj_by_value.js @@ -0,0 +1,240 @@ +/* + * 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 value +//! RUN options: "--no-async-jit --compiler-hotness-threshold=10 --compiler-regex _GLOBAL::test_value.*", entry: "_GLOBAL::func_main_0" +//! METHOD "test_value_store_single_class" +//! PASS_BEFORE "Scheduler" +//! INST "AnyTypeCheck ECMASCRIPT_HEAP_OBJECT_TYPE" +//! INST_NEXT "CastAnyTypeValue" +//! INST_NEXT /LoadObject.*Class/ +//! INST_NEXT "ClassImmediate" +//! INST_NEXT "Compare" +//! INST_NEXT "DeoptimizeIf" +//! INST_NEXT "AnyTypeCheck ECMASCRIPT_INT_TYPE" +//! INST_NEXT /LoadObject.*Elements/ +//! INST_NEXT "LenArray" +//! INST_NEXT "BoundsCheck" +//! INST_NEXT "StoreArray" +//! INST_NOT "Intrinsic.StObjByValue" +//! METHOD "test_value_load_single_class" +//! PASS_BEFORE "Scheduler" +//! INST "AnyTypeCheck ECMASCRIPT_HEAP_OBJECT_TYPE" +//! INST_NEXT "CastAnyTypeValue" +//! INST_NEXT /LoadObject.*Class/ +//! INST_NEXT "ClassImmediate" +//! INST_NEXT "Compare" +//! INST_NEXT "DeoptimizeIf" +//! INST_NEXT "AnyTypeCheck ECMASCRIPT_INT_TYPE" +//! INST_NEXT /LoadObject.*Elements/ +//! INST_NEXT "LenArray" +//! INST_NEXT "BoundsCheck" +//! INST_NEXT "LoadArray" +//! INST_NEXT "DeoptimizeIf" +//! INST_NOT "Intrinsic.LdObjByValue" +//! METHOD "test_value_store_two_classes" +//! PASS_BEFORE "Scheduler" +//! INST "AnyTypeCheck ECMASCRIPT_HEAP_OBJECT_TYPE" +//! INST_NEXT "CastAnyTypeValue" +//! INST_NEXT /LoadObject.*Class/ +//! INST_NEXT "ClassImmediate" +//! INST_NEXT "Compare" +//! INST_NEXT "ClassImmediate" +//! INST_NEXT "Compare" +//! INST_NEXT "And" +//! INST_NEXT "DeoptimizeIf" +//! INST_NEXT "AnyTypeCheck ECMASCRIPT_INT_TYPE" +//! INST_NEXT /LoadObject.*Elements/ +//! INST_NEXT "LenArray" +//! INST_NEXT "BoundsCheck" +//! INST_NEXT "StoreArray" +//! INST_NOT "Intrinsic.StObjByValue" +//! METHOD "test_value_load_two_classes" +//! PASS_BEFORE "Scheduler" +//! INST "AnyTypeCheck ECMASCRIPT_HEAP_OBJECT_TYPE" +//! INST_NEXT "CastAnyTypeValue" +//! INST_NEXT /LoadObject.*Class/ +//! INST_NEXT "ClassImmediate" +//! INST_NEXT "Compare" +//! INST_NEXT "ClassImmediate" +//! INST_NEXT "Compare" +//! INST_NEXT "And" +//! INST_NEXT "DeoptimizeIf" +//! INST_NEXT "AnyTypeCheck ECMASCRIPT_INT_TYPE" +//! INST_NEXT /LoadObject.*Elements/ +//! INST_NEXT "LenArray" +//! INST_NEXT "BoundsCheck" +//! INST_NEXT "LoadArray" +//! INST_NEXT "DeoptimizeIf" +//! INST_NOT "Intrinsic.LdObjByValue" +//! METHOD "test_value_store_not_int" +//! PASS_BEFORE "Scheduler" +//! INST_NOT "AnyTypeCheck ECMASCRIPT_HEAP_OBJECT_TYPE" +//! INST "Intrinsic.StObjByValue" +//! METHOD "test_value_load_not_int" +//! PASS_BEFORE "Scheduler" +//! INST_NOT "AnyTypeCheck ECMASCRIPT_HEAP_OBJECT_TYPE" +//! INST "Intrinsic.LdObjByValue" +//! METHOD "test_value_store_key" +//! PASS_BEFORE "Scheduler" +//! INST "LoadObjFromConst" +//! INST_NEXT "Compare" +//! INST_NEXT "DeoptimizeIf" +//! INST_NEXT "AnyTypeCheck ECMASCRIPT_HEAP_OBJECT_TYPE" +//! INST_NEXT "CastAnyTypeValue" +//! INST_NEXT /LoadObject.*Class/ +//! INST_NEXT "ClassImmediate" +//! INST_NEXT "Compare" +//! INST_NEXT "DeoptimizeIf" +//! INST_NOT "Intrinsic.StObjByValue" +//! METHOD "test_value_load_key" +//! PASS_BEFORE "Scheduler" +//! INST "LoadObjFromConst" +//! INST_NEXT "Compare" +//! INST_NEXT "DeoptimizeIf" +//! INST_NEXT "AnyTypeCheck ECMASCRIPT_HEAP_OBJECT_TYPE" +//! INST_NEXT "CastAnyTypeValue" +//! INST_NEXT /LoadObject.*Class/ +//! INST_NEXT "ClassImmediate" +//! INST_NEXT "Compare" +//! INST_NEXT "DeoptimizeIf" +//! INST_NOT "Intrinsic.LdObjByValue" +//! EVENT /DeoptimizationReason,.*test_value_store_single_class.*ANY_TYPE_CHECK/ +//! EVENT_NEXT /DeoptimizationReason,.*test_value_load_single_class.*INLINE_IC/ +//! EVENT_NEXT /DeoptimizationReason,.*test_value_store_two_classes.*ANY_TYPE_CHECK/ + +function test_value_store_single_class(a, b) { + a[b] = 2 +} + +function test_value_load_single_class(a, b) { + return a[b] + 1 +} + +function test_value_store_two_classes(a, b) { + a[b] = 5 +} + +function test_value_load_two_classes(a, b) { + return a[b] +} + +function test_value_store_not_int(a, b) { + a[b] = 6 +} + +function test_value_load_not_int(a, b) { + return a[b] + 2 +} + +function test_value_store_key(a, b) { + a[b] = 7 +} + +function test_value_load_key(a, b) { + return a[b] + 3 +} + +let a = {} +let c = {} +let b1 = 1 +let b2 = 2 +let b3 = 3 +c.field1 = 5 +a[b1] = 30 +a[b2] = 30 +a[b3] = 30 +c[b1] = 30 +c[b2] = 30 +c[b3] = 30 +let d1 = Math.floor(1.2) +let d = {} +d[b1] = 30 + +let k = {} +let s = "key" + +for (let i = 0 ; i < 50; i++) { + test_value_store_single_class(a, b1) + let res = test_value_load_single_class(a, b1) + if ( res != 3) { + throw "test_value_load_single_class is failed for b1 - incorrect return value: " + res; + } + test_value_store_single_class(a, b2) + res = test_value_load_single_class(a, b2) + if (res != 3) { + throw "test_value_load_single_class is failed for b2 - incorrect return value: " + res; + } + test_value_store_single_class(a, b3) + res = test_value_load_single_class(a, b3) + if (res != 3) { + throw "test_value_load_single_class is failed for b3 - incorrect return value: " + res; + } + test_value_store_two_classes(a, b1) + res = test_value_load_two_classes(a, b1) + if (res != 5) { + throw "test_value_load_two_classes is failed for a and b1 - incorrect return value" + res; + } + test_value_store_two_classes(a, b2) + res = test_value_load_two_classes(a, b2) + if (res != 5) { + throw "test_value_load_two_classes is failed for a and b2 - incorrect return value" + res; + } + test_value_store_two_classes(a, b3) + res = test_value_load_two_classes(a, b3) + if (res != 5) { + throw "test_value_load_two_classes is failed for a and b3 - incorrect return value" + res; + } + test_value_store_two_classes(c, b1) + res = test_value_load_two_classes(c, b1) + if (res != 5) { + throw "test_value_load_two_classes is failed for c and b1 - incorrect return value" + res; + } + test_value_store_two_classes(c, b2) + res = test_value_load_two_classes(c, b2) + if (res != 5) { + throw "test_value_load_two_classes is failed for c and b2 - incorrect return value" + res; + } + test_value_store_two_classes(c, b3) + res = test_value_load_two_classes(c, b3) + if (res != 5) { + throw "test_value_load_two_classes is failed for c and b3 - incorrect return value" + res; + } + test_value_store_not_int(a, d1) + res = test_value_load_not_int(a, d1) + if (res != 8) { + throw "test_value_store_not_int is failed for - incorrect return value: " + res; + } + test_value_store_key(k, s) + res = test_value_load_key(k, s) + if (res != 10) { + throw "test_value_store_key is failed for - incorrect return value: " + res; + } +} + +test_value_store_single_class(a, d1) +test_value_load_single_class(c, b1) + +var get_type_error = false; +try { + test_value_store_two_classes(10, b1) +} catch (e) { + if (e instanceof TypeError) { + get_type_error = true; + } +} +if (!get_type_error) { + throw "don't catch type error"; +} \ No newline at end of file diff --git a/tests/compiler/peepholes_ecma_test.cpp b/tests/compiler/peepholes_ecma_test.cpp index c580e094ba74ff5c086325e24ac9968bcab75d6a..9c9594f90df8c31ab998f4ed4089c586c7d30fc8 100644 --- a/tests/compiler/peepholes_ecma_test.cpp +++ b/tests/compiler/peepholes_ecma_test.cpp @@ -208,7 +208,7 @@ TEST_F(PeepholesTest, CastIntToAnyToDouble) BASIC_BLOCK(2, -1) { INST(1, Opcode::CastValueToAnyType).any().AnyType(AnyBaseType::ECMASCRIPT_INT_TYPE).Inputs(0); - INST(5, Opcode::SaveState).Inputs(1).SrcVregs({0}); + INST(5, Opcode::SaveState).Inputs(0).SrcVregs({0}); INST(2, Opcode::AnyTypeCheck).any().AnyType(AnyBaseType::ECMASCRIPT_DOUBLE_TYPE).Inputs(1, 5); INST(6, Opcode::Cast).f64().SrcType(DataType::INT32).Inputs(0); INST(4, Opcode::Return).f64().Inputs(6);