diff --git a/compiler/optimizer/code_generator/compiler_base_types.cpp b/compiler/optimizer/code_generator/compiler_base_types.cpp index 7cf1ea031f1289f0d12bbc7ac46d637f4ae6e822..7cb21fb5c76dd0618bae3b589dffd767fe776a4a 100644 --- a/compiler/optimizer/code_generator/compiler_base_types.cpp +++ b/compiler/optimizer/code_generator/compiler_base_types.cpp @@ -119,9 +119,6 @@ static void CheckAnyTypeGenObject(Encoder *enc, const Reg &src, LabelHolder::Lab enc->EncodeAnd(tmp_reg, src, Imm(panda::coretypes::TaggedValue::TAG_MASK)); enc->EncodeJump(label, tmp_reg, Imm(panda::coretypes::TaggedValue::TAG_OBJECT), Condition::NE); - // !IsHole() - enc->EncodeJump(label, src, Imm(panda::coretypes::TaggedValue::VALUE_HOLE), Condition::EQ); - // !IsSpecial // It's enough to check that `src` > `TAG_SPECIAL_MASK`, as it's guaranteed that special values // are not in object address space @@ -139,12 +136,13 @@ static void CheckAnyTypeGenObjectType(Codegen *codegen, Encoder *enc, const Reg ScopedTmpReg tmp_reg(enc, codegen->ConvertDataType(DataType::REFERENCE, arch)); enc->EncodeLdr(tmp_reg, false, MemRef(src, codegen->GetRuntime()->GetObjClassOffset(arch))); - enc->EncodeLdr(tmp_reg, false, MemRef(tmp_reg, cross_values::GetHclassDataOffset(arch))); - enc->EncodeAnd(tmp_reg, tmp_reg, Imm(static_cast(cross_values::GetJshclassBitfieldTypeMask(arch)))); auto type_start_bit = cross_values::GetJshclassBitfieldTypeStartBit(arch); + auto bit_mask = static_cast(cross_values::GetJshclassBitfieldTypeMask(arch)); + enc->EncodeLdr(tmp_reg, false, MemRef(tmp_reg, cross_values::GetHclassDataOffset(arch))); if (type_start_bit != 0) { enc->EncodeShr(tmp_reg, tmp_reg, Imm(type_start_bit)); } + enc->EncodeAnd(tmp_reg, tmp_reg, Imm(bit_mask)); enc->EncodeJump(label, tmp_reg, Imm(type), fail_cc); } @@ -366,6 +364,33 @@ bool ecmascript::CastValueToAnyTypeGen(const CastValueToAnyTypeInst *cvai, Encod return false; } +bool ecmascript::ObjByIndexCheckGen(const FixedInputsInst2 *check_inst, EncodeVisitor *enc_v, LabelHolder::LabelId id) +{ + auto src = enc_v->GetCodegen()->ConvertRegister(check_inst->GetSrcReg(0), DataType::Type::INT64); + Encoder *enc = enc_v->GetEncoder(); + Codegen *codegen = enc_v->GetCodegen(); + // Check that the value is Heap Object + CheckAnyTypeGenObject(enc, src, id); + + auto arch = codegen->GetArch(); + ScopedTmpReg property_reg(enc, codegen->ConvertDataType(DataType::INT64, arch)); + ScopedTmpReg tmp_reg(enc, codegen->ConvertDataType(DataType::REFERENCE, arch)); + + enc->EncodeLdr(tmp_reg, false, MemRef(src, codegen->GetRuntime()->GetObjClassOffset(arch))); + enc->EncodeLdr(property_reg, false, MemRef(tmp_reg, cross_values::GetHclassDataOffset(arch))); + auto type_start_bit = cross_values::GetJshclassBitfieldTypeStartBit(arch); + if (type_start_bit != 0) { + enc->EncodeShr(property_reg, property_reg, Imm(type_start_bit)); + } + // Check that te object isn't special + enc->EncodeAnd(tmp_reg, property_reg, Imm(static_cast(cross_values::GetJshclassBitfieldTypeMask(arch)))); + enc->EncodeJump(id, tmp_reg, Imm(cross_values::GetJstypeJsArray(codegen->GetArch())), Condition::GT); + auto bit_offset = static_cast(cross_values::GetJshclassBitfieldIsDictionaryStartBit(codegen->GetArch())); + // Check that the object isn't dictionary + enc->EncodeJumpTest(id, tmp_reg, Imm(1U << bit_offset), Condition::TST_NE); + return true; +} + static bool AnyTypeCheckGenCustomDeoptimization(Encoder *enc, Codegen *codegen, AnyTypeCheckInst *check_inst, Reg src) { ScopedTmpReg tmp_reg(enc, TypeInfo(TypeInfo::TypeId::INT64)); diff --git a/compiler/optimizer/code_generator/compiler_base_types.h b/compiler/optimizer/code_generator/compiler_base_types.h index 23106021a2eba2ea1e2cfef89442b485b10a2d0d..c76c24376bcd9a1eff78b0970c501ce3a7b1d1c5 100644 --- a/compiler/optimizer/code_generator/compiler_base_types.h +++ b/compiler/optimizer/code_generator/compiler_base_types.h @@ -23,6 +23,7 @@ class CompareAnyTypeInst; class CastAnyTypeValueInst; class CastValueToAnyTypeInst; class AnyTypeCheckInst; +class FixedInputsInst2; class EncodeVisitor; namespace ecmascript { @@ -31,6 +32,7 @@ bool CompareAnyTypeGen(const CompareAnyTypeInst *cati, EncodeVisitor *enc_v); bool CastAnyTypeValueGen(const CastAnyTypeValueInst *cati, EncodeVisitor *enc_v); bool CastValueToAnyTypeGen(const CastValueToAnyTypeInst *cvai, EncodeVisitor *enc_v); bool AnyTypeCheckGen(AnyTypeCheckInst *check_inst, EncodeVisitor *enc_v); +bool ObjByIndexCheckGen(const FixedInputsInst2 *check_inst, EncodeVisitor *enc_v, LabelHolder::LabelId id); } // namespace ecmascript } // namespace panda::compiler diff --git a/compiler/optimizer/ir_builder/ecmascript_inst_builder.cpp b/compiler/optimizer/ir_builder/ecmascript_inst_builder.cpp index e4682c70ae4945cedcfc664ae8073a514b26e269..27adf38ae969b5df2b45fa08b52d4ee809564df5 100644 --- a/compiler/optimizer/ir_builder/ecmascript_inst_builder.cpp +++ b/compiler/optimizer/ir_builder/ecmascript_inst_builder.cpp @@ -22,7 +22,7 @@ #include "bytecode_instruction-inl.h" #include "include/coretypes/tagged_value.h" #include "optimizer/ir_builder/pbc_iterator.h" - +#include "plugins/ecmascript/runtime/ecma_profiling.h" namespace panda::compiler { void InstBuilder::FinalizeEcmaFnCall(SaveStateInst *save_state, CallInst *call_inst) @@ -225,6 +225,88 @@ void InstBuilder::BuildLdGlobalVar(const BytecodeInstruction *bc_inst, size_t ty } } +void InstBuilder::BuildStObjByIndex(const BytecodeInstruction *bc_inst, uint64_t imm) +{ + auto pc = GetPc(bc_inst->GetAddress()); + auto profile = GetRuntime()->GetBytecodeProfile(method_profile_, bc_inst->GetAddress(), pc); + + auto ecma_prof = reinterpret_cast(profile); + panda::ecmascript::ObjByIndexOperationProfile p(ecma_prof); + if (p.GetOperandType(0).GetType() != panda::ecmascript::ProfilingIndexedAccessBits::OBJECT_ARRAY_ACCESS) { + BuildEcma(bc_inst); + return; + } + BuildLdStObjByIndex(bc_inst, GetDefinition(bc_inst->GetVReg(0)), imm, GetDefinitionAcc()); +} + +void InstBuilder::BuildLdObjByIndex(const BytecodeInstruction *bc_inst, uint64_t imm) +{ + auto pc = GetPc(bc_inst->GetAddress()); + auto profile = GetRuntime()->GetBytecodeProfile(method_profile_, bc_inst->GetAddress(), pc); + + auto ecma_prof = reinterpret_cast(profile); + panda::ecmascript::ObjByIndexOperationProfile p(ecma_prof); + if (p.GetOperandType(0).GetType() != panda::ecmascript::ProfilingIndexedAccessBits::OBJECT_ARRAY_ACCESS) { + BuildEcma(bc_inst); + return; + } + BuildLdStObjByIndex(bc_inst, GetDefinitionAcc(), imm, nullptr); +} + +void InstBuilder::BuildLdStObjByIndex(const BytecodeInstruction *bc_inst, Inst *obj_inst, uint64_t imm, Inst *store_def) +{ + auto pc = GetPc(bc_inst->GetAddress()); + auto check_obj = graph_->CreateInstObjByIndexCheck(DataType::ANY, pc); + + check_obj->SetInput(0, obj_inst); + + auto load_object = graph_->CreateInstLoadObject(DataType::REFERENCE, pc); + load_object->SetMethod(GetGraph()->GetMethod()); + load_object->SetObjField(nullptr); + load_object->SetTypeId(0); + load_object->SetObjectType(ObjectType::MEM_ELEMENTS); + load_object->SetInput(0, check_obj); + + Inst *save_state = nullptr; + Inst *null_check = nullptr; + Inst *array_length = nullptr; + Inst *bounds_check = nullptr; + BuildChecksBeforeArray(pc, load_object, &save_state, &null_check, &array_length, &bounds_check, false); + ASSERT(save_state != nullptr && null_check == load_object && array_length != nullptr && bounds_check != nullptr); + + check_obj->SetInput(1, save_state); + + // Create instruction + auto inst = graph_->CreateInstLoadArray(DataType::ANY, pc); + bounds_check->SetInput(1, FindOrCreateConstant(imm)); + bounds_check->SetFlag(inst_flags::CAN_DEOPTIMIZE); + + inst->SetInput(0, null_check); + inst->SetInput(1, bounds_check); + + auto cmp_inst = graph_->CreateInstCompare(DataType::BOOL, pc, ConditionCode::CC_EQ); + cmp_inst->SetInput(0, inst); + cmp_inst->SetInput(1, graph_->FindOrCreateConstant(DataType::Any(panda::coretypes::TaggedValue::VALUE_HOLE))); + cmp_inst->SetOperandsType(DataType::ANY); + auto deopt_inst = graph_->CreateInstDeoptimizeIf(DataType::NO_TYPE, pc); + deopt_inst->SetDeoptimizeType(DeoptimizeType::HOLE); + deopt_inst->SetInput(0, cmp_inst); + deopt_inst->SetSaveState(save_state); + + AddInstruction(save_state, check_obj, load_object, array_length, bounds_check, inst, cmp_inst, deopt_inst); + + if (store_def != nullptr) { + auto store = graph_->CreateInstStoreArray(DataType::ANY, pc); + store->SetInput(0, load_object); + store->SetInput(1, bounds_check); + store->SetInput(2U, store_def); + store->SetNeedBarrier(true); + AddInstruction(store); + } else { + UpdateDefinitionAcc(inst); + } +} + template void InstBuilder::BuildStGlobalVar(const BytecodeInstruction *bc_inst, size_t type_id); template void InstBuilder::BuildStGlobalVar(const BytecodeInstruction *bc_inst, size_t type_id); diff --git a/compiler/optimizer/ir_builder/ecmascript_inst_builder.h b/compiler/optimizer/ir_builder/ecmascript_inst_builder.h index 3c9569016206c0baca6de8971f0daea7f390d903..587d8091e2b321fabbc8f7112a4846fbfcbb4248 100644 --- a/compiler/optimizer/ir_builder/ecmascript_inst_builder.h +++ b/compiler/optimizer/ir_builder/ecmascript_inst_builder.h @@ -25,6 +25,10 @@ void BuildStGlobalVar(const BytecodeInstruction *bc_inst, size_t type_id); template void BuildLdGlobalVar(const BytecodeInstruction *bc_inst, size_t type_id); +void BuildStObjByIndex(const BytecodeInstruction *bc_inst, uint64_t imm); +void BuildLdObjByIndex(const BytecodeInstruction *bc_inst, uint64_t imm); + +void BuildLdStObjByIndex(const BytecodeInstruction *bc_inst, Inst *obj_inst, uint64_t imm, Inst *store_def); void BuildEcmaFnCall(const BytecodeInstruction *bc_inst, bool is_range, bool call_this, uint64_t num_args = 0); void FinalizeEcmaFnCall(SaveStateInst *save_state, CallInst *call_inst); diff --git a/compiler/optimizer/ir_builder/ecmascript_inst_templates.yaml b/compiler/optimizer/ir_builder/ecmascript_inst_templates.yaml index 40a588ef084cc69d68b943afa9429d7ebd56fe6a..cb59a4eb547c789c264b60329e731929f227f534 100644 --- a/compiler/optimizer/ir_builder/ecmascript_inst_templates.yaml +++ b/compiler/optimizer/ir_builder/ecmascript_inst_templates.yaml @@ -104,6 +104,18 @@ } else { BuildLdGlobalVar< <%= inst.acc_write? %> >(instruction, instruction->template GetId<<%= inst.get_format %>>().AsFileId().GetOffset()); } + % when "STOBJBYINDEX" + if (graph_->IsBytecodeOptimizer()) { + BuildEcma(instruction); + } else { + BuildStObjByIndex(instruction, instruction->GetImm<<%=inst.get_format%>, 0>()); + } + % when "LDOBJBYINDEX" + if (graph_->IsBytecodeOptimizer()) { + BuildEcma(instruction); + } else { + BuildLdObjByIndex(instruction, instruction->GetImm<<%=inst.get_format%>, 0>()); + } % when "NEWOBJDYNRANGE" if (graph_->IsBytecodeOptimizer()) { BuildEcma(instruction); diff --git a/ecmascript_plugin_options.yaml b/ecmascript_plugin_options.yaml index 9ef486b7b86626e3a26394a02f3b3332508ce698..95b1cd8a6033ee8b1a1d707f5d5881ab978692b7 100644 --- a/ecmascript_plugin_options.yaml +++ b/ecmascript_plugin_options.yaml @@ -41,6 +41,7 @@ func_cast_implementation_codegen: panda::compiler::ecmascript::CastAnyTypeValueGen func_cast_to_any_implementation_codegen: panda::compiler::ecmascript::CastValueToAnyTypeGen func_any_type_check_implementation_codegen: panda::compiler::ecmascript::AnyTypeCheckGen + func_obj_by_index_check_implementation_codegen: panda::compiler::ecmascript::ObjByIndexCheckGen func_resolve_numeric_type: panda::compiler::ecmascript::NumericDataTypeToAnyType func_resolve_string_type: panda::compiler::ecmascript::GetAnyStringType func_is_any_type_can_be_subtype_of: panda::compiler::ecmascript::IsAnyTypeCanBeSubtypeOf diff --git a/isa/isa.yaml b/isa/isa.yaml index 84642c901caea6374a75affb5199d1cdd4fa7cec..a1a78c19a6713c4b6deff9ffccbcc9d0d69ef56d 100644 --- a/isa/isa.yaml +++ b/isa/isa.yaml @@ -37,7 +37,7 @@ namespaces: profiles_schema: name: string size: int - properties: [operand_types_1, operand_types_2] + properties: [operand_types_1, operand_types_2, operand_types_array] profiles: - name: BinaryArith @@ -46,6 +46,9 @@ profiles: - name: UnaryArith size: 2 properties: [operand_types_1] + - name: ObjByIndex + size: 1 + properties: [operand_types_array] groups: - title: Ecma extension intrunctions @@ -634,44 +637,50 @@ groups: - sig: ecma.ldobjbyindex imm acc: inout:top prefix: ecma - format: [pref_op_imm_8] + format: [pref_op_imm_8_prof_16] properties: [inlinable] intrinsic_name: INTRINSIC_LD_OBJ_BY_INDEX + profile: ObjByIndex - sig: ecma.ldobjbyindex imm acc: inout:top prefix: ecma - format: [pref_op_imm_16] + format: [pref_op_imm_16_prof_16] properties: [inlinable] intrinsic_name: INTRINSIC_LD_OBJ_BY_INDEX + profile: ObjByIndex - sig: ecma.ldobjbyindex imm acc: inout:top prefix: ecma - format: [pref_op_imm_32] + format: [pref_op_imm_32_prof_16] properties: [inlinable] intrinsic_name: INTRINSIC_LD_OBJ_BY_INDEX + profile: ObjByIndex - sig: ecma.stobjbyindex imm, v:in:top acc: in:top prefix: ecma - format: [pref_op_imm_8_v_8] + format: [pref_op_imm_8_v_8_prof_16] properties: [inlinable] intrinsic_name: INTRINSIC_ST_OBJ_BY_INDEX + profile: ObjByIndex - sig: ecma.stobjbyindex imm, v:in:top acc: in:top prefix: ecma - format: [pref_op_imm_16_v_8] + format: [pref_op_imm_16_v_8_prof_16] properties: [inlinable] intrinsic_name: INTRINSIC_ST_OBJ_BY_INDEX + profile: ObjByIndex - sig: ecma.stobjbyindex imm, v:in:top acc: in:top prefix: ecma - format: [pref_op_imm_32_v_8] + format: [pref_op_imm_32_v_8_prof_16] properties: [inlinable] intrinsic_name: INTRINSIC_ST_OBJ_BY_INDEX + profile: ObjByIndex - sig: ecma.stownbyindex imm, v:in:top acc: in:top diff --git a/runtime/CMakeLists.txt b/runtime/CMakeLists.txt index 8542c2b1d6fdb893ef2ee6716661e67684bc911b..d47f54b9aa4021b2804335cda54d3e39646fbd79 100644 --- a/runtime/CMakeLists.txt +++ b/runtime/CMakeLists.txt @@ -107,6 +107,7 @@ set(ECMASCRIPT_SOURCES ${ECMA_SRC_DIR}/ecma_exceptions.cpp ${ECMA_SRC_DIR}/ecma_language_context.cpp ${ECMA_SRC_DIR}/ecma_module.cpp + ${ECMA_SRC_DIR}/ecma_profiling.cpp ${ECMA_SRC_DIR}/ecma_string.cpp ${ECMA_SRC_DIR}/ecma_string_table.cpp ${ECMA_SRC_DIR}/ecma_vm.cpp diff --git a/runtime/compiler/ecmascript_runtime_interface.h b/runtime/compiler/ecmascript_runtime_interface.h index bd9fb65315ecd21227e124b9903c3985a6efe49f..a163c10d48b61fc4eecd51f883df33f3cd36e588 100644 --- a/runtime/compiler/ecmascript_runtime_interface.h +++ b/runtime/compiler/ecmascript_runtime_interface.h @@ -24,6 +24,8 @@ namespace panda::ecmascript { class EcmaVM; +enum class ObjectFieldType : uint8_t { INVALID = 0, ELEMENTS, COUNT }; + class EcmaRuntimeInterface : public PandaRuntimeInterface { public: explicit EcmaRuntimeInterface(const EcmaVM *ecma_vm); @@ -55,6 +57,11 @@ public: return panda::cross_values::GetJspropertyBoxValueOffset(arch); } + size_t GetElementsOffset(Arch arch) const override + { + return panda::cross_values::GetJsobjectElementsOffset(arch); + } + EntrypointId GetGlobalVarEntrypointId() override { return EntrypointId::GET_GLOBAL_VAR_ADDRESS; @@ -75,6 +82,7 @@ private: const EcmaVM *ecma_vm_ {nullptr}; panda::ecmascript::EcmaProfileContainer profile_; }; + } // namespace panda::ecmascript #endif // PANDA_RUNTIME_ECMASCRIPT_RUNTIME_INTERFACE_H diff --git a/runtime/ecma_profiling.cpp b/runtime/ecma_profiling.cpp new file mode 100644 index 0000000000000000000000000000000000000000..5e19a9158c8348583658d5a4c5ea0890082b4467 --- /dev/null +++ b/runtime/ecma_profiling.cpp @@ -0,0 +1,34 @@ +/** + * Copyright (c) 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 "ecma_profiling.h" +#include "plugins/ecmascript/runtime/js_hclass.h" +#include "plugins/ecmascript/runtime/mem/tagged_object-inl.h" + +namespace panda::ecmascript { +ProfilingIndexedAccessBits::Type GetObjectTypeFromValue(JSTaggedValue value) +{ + if (!value.IsHeapObject()) { + return ProfilingIndexedAccessBits::NOT_HEAP_OBJECT_ACCESS; + } + auto *hclass = value.GetTaggedObject()->GetClass(); + JSType js_type = hclass->GetObjectType(); + + if (js_type > JSType::JS_ARRAY || hclass->IsDictionaryElement()) { + return ProfilingIndexedAccessBits::OBJECT_ACCESS; + } + return ProfilingIndexedAccessBits::OBJECT_ARRAY_ACCESS; +} +} // namespace panda::ecmascript \ No newline at end of file diff --git a/runtime/ecma_profiling.h b/runtime/ecma_profiling.h index eaefb2d9952fa8640d36412b4f548b361200e059..02318114814083451639b39ddd8df81d59674353 100644 --- a/runtime/ecma_profiling.h +++ b/runtime/ecma_profiling.h @@ -21,7 +21,7 @@ namespace panda::ecmascript { -class ProfilingType { +class ProfilingTypeBits { public: enum Type : uint8_t { NONE = 0, @@ -31,60 +31,142 @@ public: STRING = (1U << 3U), OBJECT = (1U << 4U), LAST = OBJECT, - DOUBLE_INTEGER = DOUBLE | INTEGER, + DOUBLE_INTEGER = DOUBLE | INTEGER + }; + template + static void Dump(S &stream, Type type) + { + if (type == NONE) { + stream << "[None]"; + return; + } + stream << '['; + const char *sep = ""; + if (type & INTEGER) { + stream << sep << "Integer or objct array access"; + sep = "|"; + } + if (type & DOUBLE) { + stream << sep << "Double or not object"; + sep = "|"; + } + if (type & BOOLEAN) { + stream << sep << "Bool"; + sep = "|"; + } + if (type & STRING) { + stream << sep << "String"; + sep = "|"; + } + if (type & OBJECT) { + stream << sep << "Object"; + sep = "|"; + } + stream << ']'; + } +}; + +class ProfilingIndexedAccessBits { +public: + enum Type : uint8_t { + NONE = 0, + OBJECT_ARRAY_ACCESS = (1U << 0U), + NOT_HEAP_OBJECT_ACCESS = (1U << 1U), + OBJECT_ACCESS = (1U << 2U), + LAST = OBJECT_ACCESS }; - ProfilingType() = default; + template + static void Dump(S &stream, Type type) + { + if (type == NONE) { + stream << "[None]"; + return; + } + stream << '['; + const char *sep = ""; + if (type & OBJECT_ARRAY_ACCESS) { + stream << sep << "objct array access"; + sep = "|"; + } + if (type & NOT_HEAP_OBJECT_ACCESS) { + stream << sep << "not object"; + sep = "|"; + } + if (type & OBJECT_ACCESS) { + stream << sep << "Object"; + sep = "|"; + } + stream << ']'; + } +}; + +template +class ProfileInfo { +public: + using Type = typename T::Type; + ProfileInfo() = default; // NOLINTNEXTLINE(google-explicit-constructor) - ProfilingType(Type type) : type_(type) {} + ProfileInfo(Type type) : type_(type) {} // NOLINTNEXTLINE(google-explicit-constructor) - ProfilingType(std::underlying_type_t type) : type_(static_cast(type)) {} + ProfileInfo(std::underlying_type_t type) : type_(static_cast(type)) {} - template - void Dump(T &stream); + template + void Dump(S &stream) + { + T::Dump(stream, type_); + } Type GetType() const { return type_; } - bool Contains(ProfilingType other) const + bool Contains(Type other) const { return (type_ & other.type_) != 0; } - bool operator==(ProfilingType other) + bool operator==(Type other) { return type_ == other.type_; } // NOLINTNEXTLINE(google-explicit-constructor) - operator Type() const + operator T() const { return type_; } -private: - Type type_ {NONE}; + T operator|(T other) + { + return T(type_ | other.type_); + } - friend ProfilingType operator|(ProfilingType a, ProfilingType b); +private: + Type type_ {T::NONE}; }; -inline ProfilingType GetTypeFromValue(JSTaggedValue value) +using ProfilingType = ProfileInfo; +using ProfilingIndexedAccess = ProfileInfo; + +inline ProfilingTypeBits::Type GetTypeFromValue(JSTaggedValue value) { if (value.IsInt()) { - return ProfilingType::INTEGER; + return ProfilingTypeBits::INTEGER; } // IsDouble() is encoded as `!IsInt() && !IsObject()`, IsInt() we already tested, so check only it is not an object if (!value.IsObject()) { - return ProfilingType::DOUBLE; + return ProfilingTypeBits::DOUBLE; } if (value.IsBoolean()) { - return ProfilingType::BOOLEAN; + return ProfilingTypeBits::BOOLEAN; } - return ProfilingType::OBJECT; + return ProfilingTypeBits::OBJECT; } +ProfilingIndexedAccessBits::Type GetObjectTypeFromValue(JSTaggedValue value); + // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) #define SET_LAST_FIELD(name) \ using LastField = name; \ @@ -128,36 +210,63 @@ private: ValueType value_ {0}; }; -template +template class ResultProfile : public ValueProfileBase { public: using Base = ValueProfileBase; using Base::Base; - ProfilingType GetResultType() const + P GetResultType() const { return this->template GetField(); } protected: - using ResultType = BitField; + using ResultType = BitField; SET_LAST_FIELD(ResultType); }; -class UnaryOperationProfile : public ResultProfile { +class ObjByIndexOperationProfile : public ValueProfileBase { public: - using Base = ResultProfile; + using Base = ValueProfileBase; + using Base::Base; + + ProfilingIndexedAccess GetOperandType() const + { + return ProfilingIndexedAccess(GetField()); + } + + ProfilingIndexedAccess GetOperandType([[maybe_unused]] size_t index) const + { + CHECK_LT(index, 1U); + return ProfilingIndexedAccess(GetOperandType()); + } + + static void Update(Base::ValueType *data, JSTaggedValue value) + { + *data |= OperandType::Encode(GetObjectTypeFromValue(value)); + } + +protected: + using OperandType = + BitField; + SET_LAST_FIELD_FINAL(OperandType); +}; + +class UnaryOperationProfile : public ResultProfile { +public: + using Base = ResultProfile; using Base::Base; ProfilingType GetOperandType() const { - return GetField(); + return ProfilingType(GetField()); } ProfilingType GetOperandType([[maybe_unused]] size_t index) const { CHECK_LT(index, 1U); - return GetOperandType(); + return ProfilingType(GetOperandType()); } static void Update(Base::ValueType *data, JSTaggedValue value) @@ -166,29 +275,30 @@ public: } protected: - using OperandType = Base::LastField::NextField; + using OperandType = + Base::LastField::NextField; SET_LAST_FIELD_FINAL(OperandType); }; -class BinaryOperationProfile : public ResultProfile { +class BinaryOperationProfile : public ResultProfile { public: - using Base = ResultProfile; + using Base = ResultProfile; using Base::Base; ProfilingType GetOperandType(size_t index) const { CHECK_LT(index, 2U); - return index == 0 ? GetField() : GetField(); + return index == 0 ? GetLeftOperandType() : GetRightOperandType(); } ProfilingType GetLeftOperandType() const { - return GetField(); + return ProfilingType(GetField()); } ProfilingType GetRightOperandType() const { - return GetField(); + return ProfilingType(GetField()); } static void Update(Base::ValueType *data, JSTaggedValue lhs, JSTaggedValue rhs) @@ -199,48 +309,13 @@ public: } protected: - using LeftOperandType = Base::LastField::NextField; - using RightOperandType = LeftOperandType::NextField; + using LeftOperandType = + Base::LastField::NextField; + using RightOperandType = + LeftOperandType::NextField; SET_LAST_FIELD_FINAL(RightOperandType); }; -template -void ProfilingType::Dump(T &stream) -{ - if (GetType() == NONE) { - stream << "[None]"; - return; - } - stream << '['; - const char *sep = ""; - if (GetType() & INTEGER) { - stream << sep << "Integer"; - sep = "|"; - } - if (GetType() & DOUBLE) { - stream << sep << "Double"; - sep = "|"; - } - if (GetType() & BOOLEAN) { - stream << sep << "Bool"; - sep = "|"; - } - if (GetType() & STRING) { - stream << sep << "String"; - sep = "|"; - } - if (GetType() & OBJECT) { - stream << sep << "Object"; - sep = "|"; - } - stream << ']'; -} - -inline ProfilingType operator|(ProfilingType a, ProfilingType b) -{ - return ProfilingType(a.type_ | b.type_); -} - /** * The following types are used in profile data serialization. */ diff --git a/runtime/interpreter/ecma-interpreter-inl.h b/runtime/interpreter/ecma-interpreter-inl.h index 12c90b39aef8a8951bca64eea7f7b2ee30d54b80..b56caa07f7e359a1ba781d6b1f68606fee3ebefc 100644 --- a/runtime/interpreter/ecma-interpreter-inl.h +++ b/runtime/interpreter/ecma-interpreter-inl.h @@ -79,6 +79,13 @@ namespace panda::ecmascript { UNUSED_VAR(rhs); \ } +#define UPDATE_OBJ_BY_INDEX_PROFILE(lhs) \ + if constexpr (IS_PROFILE_ENABLED) { \ + UpdateObjByIndexProfile(lhs); \ + } else { \ + UNUSED_VAR(lhs); \ + } + template class JSFrameHelper { public: @@ -2130,6 +2137,7 @@ public: << " imm" << idx; uint64_t obj = GetAccAsTaggedValue().GetRawData(); + UPDATE_OBJ_BY_INDEX_PROFILE(obj); INTRINSIC_CALL_CHECK_SETACC(intrinsics::LdObjByIndex(this->GetJSThread(), idx, obj)); this->template MoveToNextInst(); @@ -2145,6 +2153,7 @@ public: uint64_t obj = GetRegAsTaggedValue(v0).GetRawData(); uint64_t value = GetAccAsTaggedValue().GetRawData(); + UPDATE_OBJ_BY_INDEX_PROFILE(obj); SaveAccToFrame(); INTRINSIC_CALL_CHECK(intrinsics::StObjByIndex(this->GetJSThread(), idx, obj, value)); @@ -2747,6 +2756,21 @@ public: return JSThread::Cast(this->GetThread()); } + void UpdateObjByIndexProfile(coretypes::TaggedType value) + { + auto method = static_cast(this->GetFrame()->GetMethod()); + // Profiling is not initialized + if (method->GetProfileSize() == 0) { + return; + } + auto prof_data = method->GetProfilingVector(); + auto prof_id = this->GetInst().GetProfileId(); + ASSERT(prof_id >= 0); + auto prof_value = reinterpret_cast(&prof_data[prof_id]); + ASSERT(method->GetProfileSize() > helpers::ToUnsigned(prof_id)); + ObjByIndexOperationProfile::Update(prof_value, JSTaggedValue(value)); + } + void UpdateUnaryArithProfile(coretypes::TaggedType value) { auto method = static_cast(this->GetFrame()->GetMethod()); diff --git a/runtime/profiling/plugin_get_profiling_any_type.h b/runtime/profiling/plugin_get_profiling_any_type.h index 8dd3c4b4e5f8b228820f0f2b7d47557f04a35fc7..03e277a6367604a0214ef132b8413f6337ea3873 100644 --- a/runtime/profiling/plugin_get_profiling_any_type.h +++ b/runtime/profiling/plugin_get_profiling_any_type.h @@ -1,20 +1,20 @@ case panda::SourceLanguage::ECMASCRIPT: { auto kind = profiling::GetProfileKind(bc_inst->GetOpcode()); auto ecma_prof = reinterpret_cast(profile); - panda::ecmascript::ProfilingType type; + panda::ecmascript::ProfilingTypeBits::Type type = panda::ecmascript::ProfilingTypeBits::NONE; switch (kind) { case profiling::ProfilingKind::UNARY_ARITH: { panda::ecmascript::UnaryOperationProfile p(ecma_prof); - type = p.GetOperandType(index); + type = p.GetOperandType(index).GetType(); break; } case profiling::ProfilingKind::BINARY_ARITH: { panda::ecmascript::BinaryOperationProfile p(ecma_prof); - type = p.GetOperandType(index); - auto other_operand_type = p.GetOperandType(1 - index); - if ((type & panda::ecmascript::ProfilingType::OBJECT) == panda::ecmascript::ProfilingType::OBJECT && - (other_operand_type & panda::ecmascript::ProfilingType::OBJECT) == - panda::ecmascript::ProfilingType::OBJECT) { + type = p.GetOperandType(index).GetType(); + auto other_operand_type = p.GetOperandType(1 - index).GetType(); + if ((type & panda::ecmascript::ProfilingTypeBits::OBJECT) == panda::ecmascript::ProfilingTypeBits::OBJECT && + (other_operand_type & panda::ecmascript::ProfilingTypeBits::OBJECT) == + panda::ecmascript::ProfilingTypeBits::OBJECT) { *is_type_profiled = true; return compiler::AnyBaseType::UNDEFINED_TYPE; } @@ -24,21 +24,22 @@ case panda::SourceLanguage::ECMASCRIPT: { LOG(FATAL, COMMON) << "Unknown profile"; } - if (type == panda::ecmascript::ProfilingType(panda::ecmascript::ProfilingType::NONE)) { + if (type == panda::ecmascript::ProfilingTypeBits::NONE) { return compiler::AnyBaseType::UNDEFINED_TYPE; } *is_type_profiled = true; - auto is_int = (type & panda::ecmascript::ProfilingType::INTEGER) == panda::ecmascript::ProfilingType::INTEGER; + auto is_int = + (type & panda::ecmascript::ProfilingTypeBits::INTEGER) == panda::ecmascript::ProfilingTypeBits::INTEGER; // ignore non number types to avoid compiled method destruction on deoptimization - if ((type & panda::ecmascript::ProfilingType::DOUBLE) == panda::ecmascript::ProfilingType::DOUBLE) { + if ((type & panda::ecmascript::ProfilingTypeBits::DOUBLE) == panda::ecmascript::ProfilingTypeBits::DOUBLE) { *is_integer_seen = is_int; return compiler::AnyBaseType::ECMASCRIPT_DOUBLE_TYPE; } if (is_int) { auto is_obj_seen = - (type & panda::ecmascript::ProfilingType::OBJECT) == panda::ecmascript::ProfilingType::OBJECT; + (type & panda::ecmascript::ProfilingTypeBits::OBJECT) == panda::ecmascript::ProfilingTypeBits::OBJECT; if (is_obj_seen) { // return number type to avoid compiled method destruction on deoptimization *is_integer_seen = true; @@ -49,7 +50,7 @@ case panda::SourceLanguage::ECMASCRIPT: { } switch (type) { - case panda::ecmascript::ProfilingType::BOOLEAN: + case panda::ecmascript::ProfilingTypeBits::BOOLEAN: return compiler::AnyBaseType::ECMASCRIPT_BOOLEAN_TYPE; default: return compiler::AnyBaseType::UNDEFINED_TYPE; diff --git a/subproject_sources.gn b/subproject_sources.gn index 34d3a8ecbe2dd27b57b7b2149d65f12b547f71c0..d5218efebf22897d3ae3d520f6ec1463e8f6f9ab 100644 --- a/subproject_sources.gn +++ b/subproject_sources.gn @@ -127,6 +127,7 @@ srcs_runtime = [ "runtime/ecma_exceptions.cpp", "runtime/ecma_language_context.cpp", "runtime/ecma_module.cpp", + "runtime/ecma_profiling.cpp", "runtime/ecma_string.cpp", "runtime/ecma_string_table.cpp", "runtime/ecma_vm.cpp", diff --git a/tests/checked/CMakeLists.txt b/tests/checked/CMakeLists.txt index 6485fab88c987ace237f789aec3d1218859f86bf..5b3f2092d046fadf913d35bbd8eb858454d22493 100644 --- a/tests/checked/CMakeLists.txt +++ b/tests/checked/CMakeLists.txt @@ -60,7 +60,7 @@ function(panda_add_checked_test_ecma) set(OPTIONS "--load-runtimes=ecmascript" "--run-gc-in-place") - if (NOT ${CMAKE_BUILD_TYPE} MATCHES "Debug") + if (${CMAKE_BUILD_TYPE} MATCHES "Release") set(RELEASE_OPT "--release") endif() @@ -98,6 +98,7 @@ if (NOT PANDA_TARGET_ARM32) panda_add_checked_test_ecma(FILE ${CMAKE_CURRENT_SOURCE_DIR}/recompile_not_number.js SUPPORT_RELEASE true) 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) # there is flaky bug when turning on TSAN if (NOT PANDA_ENABLE_THREAD_SANITIZER) panda_add_checked_test_ecma(FILE ${CMAKE_CURRENT_SOURCE_DIR}/acc_after_deopt.js SUPPORT_RELEASE true) diff --git a/tests/checked/obj_by_index.js b/tests/checked/obj_by_index.js new file mode 100644 index 0000000000000000000000000000000000000000..6d725a5fbbb9c71b9be6418a4099ef3b9f957de9 --- /dev/null +++ b/tests/checked/obj_by_index.js @@ -0,0 +1,69 @@ +/* + * 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 sore 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" +//! INST_COUNT "LoadObject", 2 +//! INST_COUNT "BoundsCheck", 2 +//! INST_COUNT "ObjByIndexCheck", 2 +//! INST_COUNT "LoadArray", 2 +//! EVENT /Compilation,_GLOBAL::test_load_store_by_index,.*,COMPILED/ +//! EVENT_NEXT /DeoptimizationReason,.*test_load_store_by_index.*BOUNDS_CHECK/ +//! EVENT_NEXT /DeoptimizationReason,.*test_load_store_by_index.*HOLE/ +//! EVENT_NEXT /DeoptimizationReason,.*test_load_store_by_index.*ANY_TYPE_CHECK/ + +function test_load_store_by_index(a) { + a[9] = 6 + return a[8] +} + +var arr = Array(10) +for (let i = 0 ; i < 10; i++) { + arr[i] = 5; +} + +for (let i = 0 ; i < 1000; i++) { + if (test_load_store_by_index(arr) != 5) { + throw "test_load_store_by_index is failed - incorrect return value"; + } + if (arr[9] != 6) { + throw "test_load_store_by_index is failed - incorrect load value"; + } +} +var arr1 = Array(5) +for (let i = 0 ; i < 5; i++) { + arr1[i] = 4; +} +test_load_store_by_index(arr1) + +var arr2 = Array(20) +arr2[20] = 4; +test_load_store_by_index(arr2) +var get_type_error = false; +try { + test_load_store_by_index(4) +} catch (e) { + if (e instanceof TypeError) { + get_type_error = true; + } +} +if (!get_type_error) { + throw "don't catch type error"; +} + + +