From 8889ec5d1c555b8e3dbf2ca4b34c147bd8ce58b8 Mon Sep 17 00:00:00 2001 From: Konstantin Baladurin Date: Sun, 7 Aug 2022 17:21:25 +0100 Subject: [PATCH] Support Load/StoreObjectDynamic for ecmascript Change-Id: I1e12d67db95d7af060914a4e1785e8abb780e810 Signed-off-by: Konstantin Baladurin --- HostTools.cmake | 1 + compiler/codegen_intrinsics_ecmascript.cpp | 31 +- .../ecmascript_codegen_extensions.cpp | 59 ++ .../ecmascript_codegen_extensions.h | 5 +- .../code_generator/compiler_base_types.cpp | 8 + compiler/optimizer/ir/dyn_datatype.h | 4 + ecmascript_plugin_options.yaml | 3 + ecmastdlib/ecmastdlib.pa | 8 +- irtoc_scripts/common.irt | 96 +++ irtoc_scripts/interpreter_handlers.irt | 235 +++---- irtoc_scripts/irtoc_plugins.gn | 17 + irtoc_scripts/irtoc_scripts.gn | 3 +- irtoc_scripts/object.irt | 660 ++++++++++++++++++ irtoc_scripts/object_helpers.irt | 184 +++++ irtoc_scripts/string_helpers.irt | 170 +++++ runtime/CMakeLists.txt | 31 +- runtime/asm_defines/asm_defines.def | 43 ++ runtime/asm_defines/defines.h | 1 + runtime/ecma_entrypoints.yaml | 119 +++- runtime/ecma_runtime.yaml | 56 +- runtime/ic/properties_cache-inl.h | 2 +- runtime/ic/properties_cache.h | 34 +- runtime/intrinsics-inl.h | 36 +- runtime/js_hclass.h | 5 + runtime/layout_info.h | 18 +- subproject_sources.gn | 3 +- 26 files changed, 1624 insertions(+), 208 deletions(-) create mode 100644 irtoc_scripts/irtoc_plugins.gn create mode 100644 irtoc_scripts/object.irt create mode 100644 irtoc_scripts/object_helpers.irt create mode 100644 irtoc_scripts/string_helpers.irt diff --git a/HostTools.cmake b/HostTools.cmake index ecc0d766f..f8b9aff8c 100644 --- a/HostTools.cmake +++ b/HostTools.cmake @@ -1 +1,2 @@ list(APPEND HOST_TOOLS_TARGETS es2panda) +list(APPEND HOST_TOOLS_TARGETS irtoc_ecmascript_fastpath) diff --git a/compiler/codegen_intrinsics_ecmascript.cpp b/compiler/codegen_intrinsics_ecmascript.cpp index 52582262b..17a26581e 100644 --- a/compiler/codegen_intrinsics_ecmascript.cpp +++ b/compiler/codegen_intrinsics_ecmascript.cpp @@ -252,8 +252,10 @@ void Codegen::LdLexVarDyn([[maybe_unused]] IntrinsicInst *inst, [[maybe_unused]] void Codegen::GetObjectClassTypeIntrinsic([[maybe_unused]] IntrinsicInst *inst, [[maybe_unused]] Reg dst, SRCREGS src) { ScopedTmpReg tmp_reg(GetEncoder(), ConvertDataType(DataType::UINT64, GetArch())); - GetEncoder()->EncodeLdr(tmp_reg, false, MemRef(src[0], GetRuntime()->GetObjClassOffset(GetArch()))); - GetEncoder()->EncodeLdr(tmp_reg, false, MemRef(tmp_reg, cross_values::GetHclassDataOffset(GetArch()))); + Reg tmp_reg_ref = tmp_reg.GetReg().As(ConvertDataType(DataType::REFERENCE, GetArch())); + + GetEncoder()->EncodeLdr(tmp_reg_ref, false, MemRef(src[0], GetRuntime()->GetObjClassOffset(GetArch()))); + GetEncoder()->EncodeLdr(tmp_reg, false, MemRef(tmp_reg_ref, cross_values::GetJshclassBitfieldOffset(GetArch()))); GetEncoder()->EncodeAnd(tmp_reg, tmp_reg, Imm(static_cast(cross_values::GetJshclassBitfieldTypeMask(GetArch())))); auto type_start_bit = cross_values::GetJshclassBitfieldTypeStartBit(GetArch()); @@ -332,4 +334,29 @@ void Codegen::CreateDynCallCheck([[maybe_unused]] IntrinsicInst *inst, [[maybe_u cross_values::GetJshclassBitfieldClassConstructorStartBit(GetArch()), true); } +void Codegen::CreateDynClassNumberOfProps([[maybe_unused]] IntrinsicInst *inst, Reg dst, SRCREGS src) +{ + ScopedTmpReg tmp_reg(GetEncoder(), ConvertDataType(DataType::UINT64, GetArch())); + GetEncoder()->EncodeLdr(tmp_reg, false, MemRef(src[0], cross_values::GetJshclassBitfield1Offset(GetArch()))); + GetEncoder()->EncodeShr(tmp_reg, tmp_reg, + Imm(cross_values::GetJshclassBitfield1NumberOfPropsBitsStartBit(GetArch()))); + GetEncoder()->EncodeAnd(dst, tmp_reg, Imm(cross_values::GetJshclassBitfield1NumberOfPropsBitsMask(GetArch()))); +} + +void Codegen::CreateDynClassGetHash([[maybe_unused]] IntrinsicInst *inst, Reg dst, SRCREGS src) +{ + GetEncoder()->EncodeMov(dst, src[0].As(TypeInfo(TypeInfo::TypeId::INT32))); + GetEncoder()->EncodeShr(dst, dst, Imm(3U)); +} + +void Codegen::CreateLdObjDynByName([[maybe_unused]] IntrinsicInst *inst, Reg dst, SRCREGS src) +{ + CallRuntime(inst, EntrypointId::LOAD_OBJECT_DYNAMIC_BY_NAME, dst, {}, src[0], src[1U], src[2U]); +} + +void Codegen::CreateStObjDynByName([[maybe_unused]] IntrinsicInst *inst, Reg dst, SRCREGS src) +{ + CallRuntime(inst, EntrypointId::STORE_OBJECT_DYNAMIC_BY_NAME, dst, {}, src[0], src[1U], src[2U], src[3U]); +} + } // namespace panda::compiler diff --git a/compiler/ecmascript_extensions/ecmascript_codegen_extensions.cpp b/compiler/ecmascript_extensions/ecmascript_codegen_extensions.cpp index b5e634248..cbaffc85f 100644 --- a/compiler/ecmascript_extensions/ecmascript_codegen_extensions.cpp +++ b/compiler/ecmascript_extensions/ecmascript_codegen_extensions.cpp @@ -73,4 +73,63 @@ void Codegen::GenerateEcmascriptEnvInEpilogue() env_reg.GetReg().GetSize()); } +bool Codegen::GenerateLoadObjectDynamic(Inst *inst) +{ + auto dst_reg = ConvertRegister(inst->GetDstReg(), inst->GetType()); + auto obj_reg = ConvertRegister(inst->GetSrcReg(0), inst->GetInputType(0)); + auto key_reg = ConvertRegister(inst->GetSrcReg(1U), inst->GetInputType(1U)); + + auto type = inst->CastToLoadObjectDynamic()->GetAccessType(); + auto mode = inst->CastToLoadObjectDynamic()->GetAccessMode(); + auto use_ic = type == DynObjectAccessType::BY_NAME; + + EntrypointId id = EntrypointId::LOAD_OBJECT_DYNAMIC; + + if (type == DynObjectAccessType::BY_INDEX) { + if (mode == DynObjectAccessMode::DICTIONARY) { + id = EntrypointId::LOAD_OBJECT_DYNAMIC_BY_INDEX_DICTIONARY; + } + } else if (type == DynObjectAccessType::BY_NAME) { + id = EntrypointId::LOAD_OBJECT_DYNAMIC_BY_NAME; + } + + if (use_ic) { + CallRuntime(inst, id, dst_reg, {}, obj_reg, key_reg, TypedImm(inst->GetPc())); + } else { + CallRuntime(inst, id, dst_reg, {}, obj_reg, key_reg); + } + + return true; +} + +bool Codegen::GenerateStoreObjectDynamic(Inst *inst) +{ + auto dst_reg = ConvertRegister(inst->GetDstReg(), inst->GetType()); + auto obj_reg = ConvertRegister(inst->GetSrcReg(0), inst->GetInputType(0)); + auto key_reg = ConvertRegister(inst->GetSrcReg(1U), inst->GetInputType(1U)); + auto val_reg = ConvertRegister(inst->GetSrcReg(2U), inst->GetInputType(2U)); + + auto type = inst->CastToStoreObjectDynamic()->GetAccessType(); + auto mode = inst->CastToStoreObjectDynamic()->GetAccessMode(); + auto use_ic = type == DynObjectAccessType::BY_NAME; + + EntrypointId id = EntrypointId::STORE_OBJECT_DYNAMIC; + + if (type == DynObjectAccessType::BY_INDEX) { + if (mode == DynObjectAccessMode::DICTIONARY) { + id = EntrypointId::STORE_OBJECT_DYNAMIC_BY_INDEX_DICTIONARY; + } + } else if (type == DynObjectAccessType::BY_NAME) { + id = EntrypointId::STORE_OBJECT_DYNAMIC_BY_NAME; + } + + if (use_ic) { + CallRuntime(inst, id, dst_reg, {}, obj_reg, key_reg, val_reg, TypedImm(inst->GetPc())); + } else { + CallRuntime(inst, id, dst_reg, {}, obj_reg, key_reg, val_reg); + } + + return true; +} + } // namespace panda::compiler diff --git a/compiler/ecmascript_extensions/ecmascript_codegen_extensions.h b/compiler/ecmascript_extensions/ecmascript_codegen_extensions.h index 4b068f44c..ea7520e98 100644 --- a/compiler/ecmascript_extensions/ecmascript_codegen_extensions.h +++ b/compiler/ecmascript_extensions/ecmascript_codegen_extensions.h @@ -19,4 +19,7 @@ void GenerateEcmascriptEnvInPrologue(); void GenerateEcmascriptEnvInEpilogue(); -#endif // PANDA_COMPILER_ECMASCRIPT_CODEGEN_EXTENSIONS_H \ No newline at end of file +bool GenerateLoadObjectDynamic(Inst *inst); +bool GenerateStoreObjectDynamic(Inst *inst); + +#endif // PANDA_COMPILER_ECMASCRIPT_CODEGEN_EXTENSIONS_H diff --git a/compiler/optimizer/code_generator/compiler_base_types.cpp b/compiler/optimizer/code_generator/compiler_base_types.cpp index 2b12bc10b..a286ea54a 100644 --- a/compiler/optimizer/code_generator/compiler_base_types.cpp +++ b/compiler/optimizer/code_generator/compiler_base_types.cpp @@ -189,6 +189,9 @@ bool ecmascript::CompareAnyTypeGen(const CompareAnyTypeInst *cati, EncodeVisitor case AnyBaseType::ECMASCRIPT_STRING_TYPE: CompareAnyTypeGenObjectType(codegen, enc, dst, src, cross_values::GetJstypeString(codegen->GetArch())); return true; + case AnyBaseType::ECMASCRIPT_SYMBOL_TYPE: + CompareAnyTypeGenObjectType(codegen, enc, dst, src, cross_values::GetJstypeSymbol(codegen->GetArch())); + return true; case AnyBaseType::ECMASCRIPT_ARRAY_TYPE: CompareAnyTypeGenObjectType(codegen, enc, dst, src, cross_values::GetJstypeJsArray(codegen->GetArch())); return true; @@ -277,6 +280,7 @@ bool ecmascript::CastAnyTypeValueGen(const CastAnyTypeValueInst *cati, EncodeVis case AnyBaseType::ECMASCRIPT_OBJECT_TYPE: case AnyBaseType::ECMASCRIPT_HEAP_OBJECT_TYPE: case AnyBaseType::ECMASCRIPT_STRING_TYPE: + case AnyBaseType::ECMASCRIPT_SYMBOL_TYPE: case AnyBaseType::ECMASCRIPT_ARRAY_TYPE: case AnyBaseType::ECMASCRIPT_TRANSITION_HANDLER_TYPE: case AnyBaseType::ECMASCRIPT_PROTOTYPE_HANDLER_TYPE: @@ -323,6 +327,7 @@ bool ecmascript::CastValueToAnyTypeGen(const CastValueToAnyTypeInst *cvai, Encod enc->EncodeAdd(dst, dst, Imm(panda::coretypes::TaggedValue::DOUBLE_ENCODE_OFFSET)); return true; case AnyBaseType::ECMASCRIPT_STRING_TYPE: + case AnyBaseType::ECMASCRIPT_SYMBOL_TYPE: case AnyBaseType::ECMASCRIPT_OBJECT_TYPE: case AnyBaseType::ECMASCRIPT_HEAP_OBJECT_TYPE: case AnyBaseType::ECMASCRIPT_ARRAY_TYPE: @@ -375,6 +380,9 @@ bool ecmascript::AnyTypeCheckGen(const AnyTypeCheckInst *check_inst, EncodeVisit case AnyBaseType::ECMASCRIPT_STRING_TYPE: CheckAnyTypeGenObjectType(codegen, enc, src, cross_values::GetJstypeString(codegen->GetArch()), id); return true; + case AnyBaseType::ECMASCRIPT_SYMBOL_TYPE: + CompareAnyTypeGenObjectType(codegen, enc, tmp_reg, src, cross_values::GetJstypeSymbol(codegen->GetArch())); + return true; case AnyBaseType::ECMASCRIPT_ARRAY_TYPE: CheckAnyTypeGenObjectType(codegen, enc, src, cross_values::GetJstypeJsArray(codegen->GetArch()), id); return true; diff --git a/compiler/optimizer/ir/dyn_datatype.h b/compiler/optimizer/ir/dyn_datatype.h index 0f9de45e0..0c40665cb 100644 --- a/compiler/optimizer/ir/dyn_datatype.h +++ b/compiler/optimizer/ir/dyn_datatype.h @@ -63,6 +63,7 @@ static inline std::optional IsAnyTypeCanBeSubtypeOf(panda::compiler::AnyBa switch (type) { case panda::compiler::AnyBaseType::ECMASCRIPT_HEAP_OBJECT_TYPE: case panda::compiler::AnyBaseType::ECMASCRIPT_STRING_TYPE: + case panda::compiler::AnyBaseType::ECMASCRIPT_SYMBOL_TYPE: case panda::compiler::AnyBaseType::ECMASCRIPT_ARRAY_TYPE: case panda::compiler::AnyBaseType::ECMASCRIPT_TRANSITION_HANDLER_TYPE: case panda::compiler::AnyBaseType::ECMASCRIPT_PROTOTYPE_HANDLER_TYPE: @@ -75,6 +76,7 @@ static inline std::optional IsAnyTypeCanBeSubtypeOf(panda::compiler::AnyBa case panda::compiler::AnyBaseType::ECMASCRIPT_HEAP_OBJECT_TYPE: switch (type) { case panda::compiler::AnyBaseType::ECMASCRIPT_STRING_TYPE: + case panda::compiler::AnyBaseType::ECMASCRIPT_SYMBOL_TYPE: case panda::compiler::AnyBaseType::ECMASCRIPT_ARRAY_TYPE: case panda::compiler::AnyBaseType::ECMASCRIPT_TRANSITION_HANDLER_TYPE: case panda::compiler::AnyBaseType::ECMASCRIPT_PROTOTYPE_HANDLER_TYPE: @@ -85,6 +87,7 @@ static inline std::optional IsAnyTypeCanBeSubtypeOf(panda::compiler::AnyBa } break; case panda::compiler::AnyBaseType::ECMASCRIPT_STRING_TYPE: + case panda::compiler::AnyBaseType::ECMASCRIPT_SYMBOL_TYPE: case panda::compiler::AnyBaseType::ECMASCRIPT_ARRAY_TYPE: case panda::compiler::AnyBaseType::ECMASCRIPT_TRANSITION_HANDLER_TYPE: case panda::compiler::AnyBaseType::ECMASCRIPT_PROTOTYPE_HANDLER_TYPE: @@ -102,6 +105,7 @@ static inline std::optional IsAnyTypeCanBeSubtypeOf(panda::compiler::AnyBa case panda::compiler::AnyBaseType::ECMASCRIPT_HEAP_OBJECT_TYPE: return std::nullopt; case panda::compiler::AnyBaseType::ECMASCRIPT_STRING_TYPE: + case panda::compiler::AnyBaseType::ECMASCRIPT_SYMBOL_TYPE: case panda::compiler::AnyBaseType::ECMASCRIPT_TRANSITION_HANDLER_TYPE: case panda::compiler::AnyBaseType::ECMASCRIPT_PROTOTYPE_HANDLER_TYPE: return true; diff --git a/ecmascript_plugin_options.yaml b/ecmascript_plugin_options.yaml index 063f289d4..af576bf15 100644 --- a/ecmascript_plugin_options.yaml +++ b/ecmascript_plugin_options.yaml @@ -33,6 +33,8 @@ header_path_compiler_inst_builder_extension: plugins/ecmascript/compiler/optimizer/ir_builder/ecmascript_inst_builder.h function_codegen_prologue: GenerateEcmascriptEnvInPrologue function_codegen_epilogue: GenerateEcmascriptEnvInEpilogue + load_object_dynamic_codegen: GenerateLoadObjectDynamic + store_object_dynamic_codegen: GenerateStoreObjectDynamic compiler_base_types: header_path_implementation_codegen: plugins/ecmascript/compiler/optimizer/code_generator/compiler_base_types.h func_compare_implementation_codegen: panda::compiler::ecmascript::CompareAnyTypeGen @@ -51,6 +53,7 @@ OBJECT_TYPE: panda::compiler::DataType::Type::REFERENCE HEAP_OBJECT_TYPE: panda::compiler::DataType::Type::REFERENCE STRING_TYPE: panda::compiler::DataType::Type::REFERENCE + SYMBOL_TYPE: panda::compiler::DataType::Type::REFERENCE ARRAY_TYPE: panda::compiler::DataType::Type::REFERENCE TRANSITION_HANDLER_TYPE: panda::compiler::DataType::Type::REFERENCE PROTOTYPE_HANDLER_TYPE: panda::compiler::DataType::Type::REFERENCE diff --git a/ecmastdlib/ecmastdlib.pa b/ecmastdlib/ecmastdlib.pa index e6fcb318d..e2111886d 100644 --- a/ecmastdlib/ecmastdlib.pa +++ b/ecmastdlib/ecmastdlib.pa @@ -175,7 +175,7 @@ .function void Ecmascript.Intrinsics.debugger() .function void Ecmascript.Intrinsics.NativeMethodWrapper(any a0) -.function void Ecmascript.Intrinsics.GetObjectClassType(any a0) +.function u8 Ecmascript.Intrinsics.GetObjectClassType(any a0) .function any Ecmascript.Intrinsics.IsNan(f64 a0) .function any Ecmascript.Intrinsics.GetEcmaConstantPool() .function any Ecmascript.Intrinsics.GetEcmaThisFunc() @@ -185,3 +185,9 @@ .function u1 Ecmascript.Intrinsics.DynClassIsExtensible(any a0) .function any Ecmascript.Intrinsics.DynObjectGetClass(any a0) .function void Ecmascript.Intrinsics.DynObjectSetClass(any a0, any a1) + +.function u32 Ecmascript.Intrinsics.DynClassNumberOfProps(any a0) +.function u32 Ecmascript.Intrinsics.DynClassGetHash(any a0) + +.function any Ecmascript.Intrinsics.LdObjDynByName(any a0, any a1, u16 a2) +.function any Ecmascript.Intrinsics.StObjDynByName(any a0, any a1, any a2, u16 a3) \ No newline at end of file diff --git a/irtoc_scripts/common.irt b/irtoc_scripts/common.irt index 642dbf5f9..2dec337cb 100644 --- a/irtoc_scripts/common.irt +++ b/irtoc_scripts/common.irt @@ -34,6 +34,8 @@ module Constants ECMASTRING_JSTYPE_STRING = "JSTYPE_STRING" ECMASTRING_MIX_LENGTH_FIELD = "runtime->GetFieldByOffset(cross_values::GetEcmastringMixLengthOffset(graph->GetArch()))" + ECMASTRING_STRING_COMPRESSED_MASK = "cross_values::GetEcmastringStringCompressedMask(graph->GetArch())" + ECMASTRING_DATA_OFFSET = "cross_values::GetEcmastringDataOffset(graph->GetArch())" ECMASCRIPT_JSFORINITERATOR_FAST_REMAINING_INDEX_OFFSET = "JSFORINITERATOR_FAST_REMAINING_INDEX_OFFSET" ECMASCRIPT_JSFORINITERATOR_REMAINING_KEYS_OFFSET = "JSFORINITERATOR_REMAINING_KEYS_OFFSET" @@ -50,6 +52,7 @@ module Constants JS_OBJECT_PROPERTIES_FIELD = "runtime->GetFieldByOffset(cross_values::GetJsobjectPropertiesOffset(graph->GetArch()))" JS_OBJECT_ELEMENTS_FIELD = "runtime->GetFieldByOffset(cross_values::GetJsobjectElementsOffset(graph->GetArch()))" + JS_OBJECT_MAX_ELEMENT_INDEX = "cross_values::GetJsobjectMaxElementIndex(graph->GetArch())" JS_ARRAY_LENGTH_FIELD = "runtime->GetFieldByOffset(cross_values::GetJsarrayLengthOffset(graph->GetArch()))" JS_FUNCTION_METHOD_FIELD = "runtime->GetFieldByOffset(cross_values::GetJsfunctionMethodOffset(graph->GetArch()))" JS_FUNCTION_PROFILE_TYPE_INFO_FIELD = "runtime->GetFieldByOffset(cross_values::GetJsfunctionProfileTypeInfoOffset(graph->GetArch()))" @@ -67,5 +70,98 @@ module Constants DYN_INT_TYPE = "AnyBaseType::ECMASCRIPT_INT_TYPE" DYN_DOUBLE_TYPE = "AnyBaseType::ECMASCRIPT_DOUBLE_TYPE" + JS_HCLASS_LAYOUT_FIELD = "runtime->GetFieldByOffset(cross_values::GetJshclassLayoutOffset(graph->GetArch()))" + JS_HCLASS_BITFIELD1_FIELD = "runtime->GetFieldByOffset(cross_values::GetJshclassBitfield1Offset(graph->GetArch()))" + JS_HCLASS_OBJECT_SIZE_FIELD = "runtime->GetFieldByOffset(cross_values::GetJshclassObjectSizeOffset(graph->GetArch()))" + + JS_STRING_HASHCODE_FIELD = "runtime->GetFieldByOffset(cross_values::GetJsStringHashcodeOffset(graph->GetArch()))" + JS_SYMBOL_HASHFIELD_FIELD = "runtime->GetFieldByOffset(cross_values::GetJsSymbolHashfieldOffset(graph->GetArch()))" + + PROPERTIES_CACHE_LENGTH = "cross_values::GetPropertiesCacheLength(graph->GetArch())" + PROPERTIES_CACHE_LENGTH_MASK = "cross_values::GetPropertiesCacheLengthMask(graph->GetArch())" + PROPERTIES_CACHE_ENTRY_SIZE = "cross_values::GetPropertiesCacheEntrySize(graph->GetArch())" + PROPERTIES_CACHE_ENTRY_HCLASS_OFFSET = "cross_values::GetPropertiesCacheEntryHclassOffset(graph->GetArch())" + PROPERTIES_CACHE_ENTRY_KEY_OFFSET = "cross_values::GetPropertiesCacheEntryKeyOffset(graph->GetArch())" + PROPERTIES_CACHE_ENTRY_RESULTS_OFFSET = "cross_values::GetPropertiesCacheEntryResultsOffset(graph->GetArch())" + + PROPERTY_ATTR_OFFSET_BIT = "cross_values::GetPropertyAttributesOffsetFieldStartBit(graph->GetArch())" + PROPERTY_ATTR_OFFSET_MASK = "cross_values::GetPropertyAttributesOffsetFieldMask(graph->GetArch())" + PROPERTY_ATTR_INLINE_BIT = "cross_values::GetPropertyAttributesInlineFieldStartBit(graph->GetArch())" + PROPERTY_ATTR_INLINE_MASK = "cross_values::GetPropertyAttributesInlineFieldMask(graph->GetArch())" + PROPERTY_ATTR_ACCESSOR_BIT = "cross_values::GetPropertyAttributesAccessorFieldStartBit(graph->GetArch())" + PROPERTY_ATTR_ACCESSOR_MASK = "cross_values::GetPropertyAttributesAccessorFieldMask(graph->GetArch())" + PROPERTY_ATTR_WRITABLE_BIT = "cross_values::GetPropertyAttributesWritableFieldStartBit(graph->GetArch())" + PROPERTY_ATTR_WRITABLE_MASK = "cross_values::GetPropertyAttributesWritableFieldMask(graph->GetArch())" + PROPERTY_ATTR_SORTED_INDEX_BIT = "cross_values::GetPropertyAttributesSortedIndexFieldStartBit(graph->GetArch())" + PROPERTY_ATTR_SORTED_INDEX_MASK = "cross_values::GetPropertyAttributesSortedIndexFieldMask(graph->GetArch())" + + HCLASS_LAYOUT_MAX_ELEMENTS_LINEAR_SEARCH = "9" + HCLASS_LAYOUT_PROPERTIES_OFFSET = "cross_values::GetJshclassLayoutPropertiesOffset(graph->GetArch())" + HCLASS_LAYOUT_PROPERTIES_SIZE = "cross_values::GetJshclassLayoutPropertiesSize(graph->GetArch())" + HCLASS_LAYOUT_PROPERTIES_KEY_OFFSET = "cross_values::GetJshclassLayoutPropertiesKeyOffset(graph->GetArch())" + HCLASS_LAYOUT_PROPERTIES_ATTR_OFFSET = "cross_values::GetJshclassLayoutPropertiesAttrOffset(graph->GetArch())" + + HCLASS_LAYOUT_INFO_NUMBER_OF_PROPERTIES_INDEX = "cross_values::GetJshclassLayoutInfoNumberOfPropertiesIndex(graph->GetArch())" + + THREAD_PROPERTIES_CACHE_OFFSET = "cross_values::GetThreadPropertiesCacheOffset(graph->GetArch())" + + TAGGED_HASH_TABLE_SIZE_INDEX = "cross_values::GetTaggedHashTableSizeIndex(graph->GetArch())" + ORDER_TAGGED_HASH_TABLE_HEADER_SIZE = "cross_values::GetOrderTaggedHashTableHeaderSize(graph->GetArch())" + + NAME_DICTIONARY_ENTRY_SIZE = "cross_values::GetNameDictionaryEntrySize(graph->GetArch())" + NAME_DICTIONARY_ENTRY_KEY_INDEX = "cross_values::GetNameDictionaryEntryKeyIndex(graph->GetArch())" + NAME_DICTIONARY_ENTRY_VALUE_INDEX = "cross_values::GetNameDictionaryEntryValueIndex(graph->GetArch())" + NAME_DICTIONARY_ENTRY_DETAILS_INDEX = "cross_values::GetNameDictionaryEntryDetailsIndex(graph->GetArch())" + + NUMBER_DICTIONARY_ENTRY_SIZE = "cross_values::GetNumberDictionaryEntrySize(graph->GetArch())" + NUMBER_DICTIONARY_ENTRY_KEY_INDEX = "cross_values::GetNumberDictionaryEntryKeyIndex(graph->GetArch())" + NUMBER_DICTIONARY_ENTRY_VALUE_INDEX = "cross_values::GetNumberDictionaryEntryValueIndex(graph->GetArch())" + NUMBER_DICTIONARY_ENTRY_DETAILS_INDEX = "cross_values::GetNumberDictionaryEntryDetailsIndex(graph->GetArch())" + + MURMURHASH_SEED = 0x12345678 + MURMURHASH_C1 = 0xCC9E2D51 + MURMURHASH_C2 = 0x1B873593 + MURMURHASH_MAIN_FIRST_SHIFT = 15 + MURMURHASH_MAIN_SECOND_SHIFT = 13 + MURMURHASH_MAIN_CONSTANT = 0xE6546B64 + MURMURHASH_MAIN_MULTIPLICATOR = 5 + MURMURHASH_KEY_LEN = 4 + + MURMURHASH_FINALIZE_FIRST_SHIFT = 16 + MURMURHASH_FINALIZE_SECOND_SHIFT = 13 + MURMURHASH_FINALIZE_THIRD_SHIFT = 16 + MURMURHASH_FINALIZE_FIRST_MULTIPLICATOR = 0x85EBCA6B + MURMURHASH_FINALIZE_SECOND_MULTIPLICATOR = 0xC2BAE35 + $VERBOSE = verbose end + +macro(:i32toany) do |arg| + CastValueToAnyType(arg).AnyType("AnyBaseType::ECMASCRIPT_INT_TYPE").any +end + +macro(:u32toany) do |arg| + CastValueToAnyType(u32toi32(arg)).AnyType("AnyBaseType::ECMASCRIPT_INT_TYPE").any +end + +macro(:f64toany) do |arg| + CastValueToAnyType(arg).AnyType("AnyBaseType::ECMASCRIPT_DOUBLE_TYPE").any +end + +macro(:get_this_func) do || + Intrinsic(:GET_ECMA_THIS_FUNC).ref +end + +macro(:get_ic) do || + LoadObject(get_this_func()).ObjField(Constants::JS_FUNCTION_PROFILE_TYPE_INFO_FIELD).any +end + +macro(:map_ic_slot) do |ic_slot| + method_ptr := LoadObject(get_this_func()).ObjField(Constants::JS_FUNCTION_METHOD_FIELD).ptr + mapping := Load(method_ptr, "JSMETHOD_IC_MAPPING_OFFSET").ptr + Load(mapping, ic_slot).u8 +end + +macro(:cmpanysymbol) do |arg| + CompareAnyType(arg).AnyType("AnyBaseType::ECMASCRIPT_SYMBOL_TYPE").b +end \ No newline at end of file diff --git a/irtoc_scripts/interpreter_handlers.irt b/irtoc_scripts/interpreter_handlers.irt index f41b44822..f6e3d50be 100644 --- a/irtoc_scripts/interpreter_handlers.irt +++ b/irtoc_scripts/interpreter_handlers.irt @@ -13,6 +13,8 @@ # limitations under the License. include_relative '../../plugins/ecmascript/irtoc_scripts/common.irt' +include_relative '../../plugins/ecmascript/irtoc_scripts/string_helpers.irt' +include_relative '../../plugins/ecmascript/irtoc_scripts/object_helpers.irt' ################## ecma fastpath ##################### @@ -61,18 +63,6 @@ macro(:booltoany) do |arg| CastValueToAnyType(arg).AnyType("AnyBaseType::ECMASCRIPT_BOOLEAN_TYPE").any end -macro(:u32toany) do |arg| - CastValueToAnyType(u32toi32(arg)).AnyType("AnyBaseType::ECMASCRIPT_INT_TYPE").any -end - -macro(:i32toany) do |arg| - CastValueToAnyType(arg).AnyType(Constants::DYN_INT_TYPE).any -end - -macro(:f64toany) do |arg| - CastValueToAnyType(arg).AnyType(Constants::DYN_DOUBLE_TYPE).any -end - macro(:anytoboolean) do |arg| CastAnyTypeValue(arg).AnyType("AnyBaseType::ECMASCRIPT_BOOLEAN_TYPE").b end @@ -109,10 +99,6 @@ macro(:cmpanyhole) do |arg| CompareAnyType(arg).AnyType("AnyBaseType::ECMASCRIPT_HOLE_TYPE").b end -macro(:cmpanyf64) do |arg| - CompareAnyType(arg).AnyType("AnyBaseType::ECMASCRIPT_DOUBLE_TYPE").b -end - macro(:cmpanyobj) do |arg| CompareAnyType(arg).AnyType("AnyBaseType::ECMASCRIPT_OBJECT_TYPE").b end @@ -1121,10 +1107,6 @@ macro(:cmpanyprotohandler) do |arg| CompareAnyType(arg).AnyType("AnyBaseType::ECMASCRIPT_PROTOTYPE_HANDLER_TYPE").b end -macro(:cmpanyspecialindexedobj) do |arg| - CompareAnyType(arg).AnyType("AnyBaseType::ECMASCRIPT_SPECIAL_INDEXED_TYPE").b -end - macro(:get_class) do |arg| Intrinsic(:DYN_OBJECT_GET_CLASS, arg).ref end @@ -1133,24 +1115,6 @@ macro(:set_class) do |obj, cls| Intrinsic(:DYN_OBJECT_SET_CLASS, obj, cls).void end -macro(:get_bits) do |data, offset, mask| - AndI(ShrI(data).Imm(offset).i32).Imm(mask).i32 -end - -macro(:get_this_func) do || - Intrinsic(:GET_ECMA_THIS_FUNC).ref -end - -macro(:get_ic) do || - LoadObject(get_this_func()).ObjField(Constants::JS_FUNCTION_PROFILE_TYPE_INFO_FIELD).any -end - -macro(:map_ic_slot) do |ic_slot| - method_ptr := LoadObject(get_this_func()).ObjField(Constants::JS_FUNCTION_METHOD_FIELD).ptr - mapping := Load(method_ptr, "JSMETHOD_IC_MAPPING_OFFSET").ptr - Load(mapping, ic_slot).u8 -end - macro(:access_global_var_ic) do |key, value, ic_slot, access| ic := get_ic() IfImm(cmpanyundefined(ic)).Imm(0).CC(:CC_EQ).b { @@ -1270,14 +1234,6 @@ macro(:store_obj_dyn) do |obj, index, value| NOP() end -macro(:store_element) do |elements, index, value| - IfImm(cmpanyheapobj(value)).Imm(0).CC(:CC_EQ).b { - StoreArray(elements, index, value).any - } Else { - StoreArray(elements, index, value).SetNeedBarrier(true).any - } -end - macro(:store_obj_ic_with_transition) do |obj, handler, value| handler_info := LoadObject(handler).ObjField(Constants::JS_TRANSITION_HANDLER_HANDLER_INFO_FEILD).any res := access_obj_ic_with_handler(obj, anytoi32(handler_info), value, @@ -1365,13 +1321,13 @@ Label(:Exit) Phi(res, miss_res).any end -macro(:try_load_obj_ic) do |obj, ic_slot| +scoped_macro(:try_load_obj_ic) do |obj, ic_slot| access_obj_ic(obj, nil, ic_slot, lambda do |obj, handler, _| load_obj_ic_with_handler(obj, handler) end) end -macro(:try_store_obj_ic) do |obj, value, ic_slot| +scoped_macro(:try_store_obj_ic) do |obj, value, ic_slot| access_obj_ic(obj, value, ic_slot, lambda do |obj, handler, value| store_obj_ic_with_handler(obj, handler, value) end) @@ -1394,128 +1350,99 @@ macro(:ic_handler_get_offset) do |handler| get_bits(handler, Constants::IC_HANDLER_OFFSET_BIT, Constants::IC_HANDLER_OFFSET_MASK) end -macro(:is_dictionary_element) do |arg| - cls := Intrinsic(:DYN_OBJECT_GET_CLASS, arg).ref - Intrinsic(:DYN_CLASS_IS_DICTIONARY_ELEMENT, cls).b -end - -macro(:is_extensible) do |arg| - cls := Intrinsic(:DYN_OBJECT_GET_CLASS, arg).ref - Intrinsic(:DYN_CLASS_IS_EXTENSIBLE, cls).b -end - -macro(:handle_access_object_dynamic_by_index) do |obj, index, value, access| - IfImm(Or(is_dictionary_element(obj), cmpanyspecialindexedobj(obj)).b).Imm(0).CC(:CC_EQ).b { - elements := LoadObject(anytoheapobj(obj)).ObjField(Constants::JS_OBJECT_ELEMENTS_FIELD).ref - length := i32tou32(LenArray(elements).i32) - If(index, length).CC(:CC_B).b { - res = access.call(elements, index, value) - access_res := res - } Else { - oob_res := Constants::TAGGED_HOLE - } - array_res := Phi(access_res, oob_res).any - } Else { - dict_res := Constants::TAGGED_HOLE - } - Phi(array_res, dict_res).any +macro(:load_object_dynamic) do |obj, key, ic_slot, props| + if props.type == :by_name && (!ic_slot.is_a? Integer) + Intrinsic(:LD_OBJ_DYN_BY_NAME, obj, key, ic_slot).any + else + LoadObjectDynamic(obj, key) + .set_access_type(props.type) + .set_access_mode(props.mode) + .any + end end -macro(:handle_access_object_dynamic) do |obj, key, value, array_access, obj_access| - IfImm(cmpanyi32(key)).Imm(0).CC(:CC_NE) { - byindex_res := handle_access_object_dynamic_by_index(obj, anytou32(key), value, array_access) - } Else { - res = obj_access.call(obj, key, value) - byvalue_res := res - } - Phi(byindex_res, byvalue_res).any +macro(:store_object_dynamic) do |obj, key, value, ic_slot, props| + if props.type == :by_name && (!ic_slot.is_a? Integer) + Intrinsic(:ST_OBJ_DYN_BY_NAME, obj, key, value, ic_slot).any + else + StoreObjectDynamic(obj, key, value) + .set_access_type(props.type) + .set_access_mode(props.mode) + .any + end end -macro(:handle_load_object_dynamic) do |obj, key, ic_slot| +macro(:handle_load_object_dynamic) do |obj, key, ic_slot, props| handle_access_object_dynamic(obj, key, nil, - lambda do |elements, index, _| - LoadArray(elements, index).any + lambda do |obj, index, _| + handle_load_object_dynamic_by_index(obj, index, props) end, lambda do |obj, key, _| unless ic_slot.nil? - try_load_obj_ic(obj, ic_slot) + r0 := try_load_obj_ic(obj, ic_slot) + IfImm(cmpanyhole(r0)).Imm(0).CC(:CC_NE).b { + r1 := load_object_dynamic(obj, key, ic_slot, props) + } + Phi(r0, r1).any else - Constants::TAGGED_HOLE + load_object_dynamic(obj, key, ic_slot, props) end - end) + end, + props) end -macro(:handle_load_object_dynamic_by_index) do |obj, key| +macro(:handle_load_object_dynamic_by_index) do |obj, key, props| handle_access_object_dynamic_by_index(obj, key, nil, lambda do |elements, index, _| LoadArray(elements, index).any - end) -end - -macro(:get_array_length) do |obj| - len := LoadObject(anytoheapobj(obj)).ObjField(Constants::JS_ARRAY_LENGTH_FIELD).any - IfImm(cmpanyi32(len)).Imm(0).CC(:CC_NE) { - res1 := anytou32(len) - } Else { - res2 := f64tou32(anytof64(len)) - } - Phi(res1, res2).u32 -end - -macro(:set_array_length) do |obj, length| - StoreObject(anytoheapobj(obj), i32toany(length)).ObjField(Constants::JS_ARRAY_LENGTH_FIELD).any -end - -macro(:store_value_to_elements) do |obj, elements, index, value| - element := LoadArray(elements, index).any - IfImm(cmpanyhole(element)).Imm(0).CC(:CC_EQ).b { - store_element(elements, index, value) - store_res := Constants::TAGGED_UNDEFINED - NOP() - } Else { - IfImm(is_extensible(obj)).Imm(0).CC(:CC_NE).b { - IfImm(cmpanyarr(obj)).Imm(0).CC(:CC_NE).b { - len := get_array_length(obj) - If(index, len).CC(:CC_AE).b { - set_array_length(obj, AddI(index).Imm(1).i32) - } - } - store_element(elements, index, value) - hole_res1 := Constants::TAGGED_UNDEFINED - NOP() - } Else { - hole_res2 := Constants::TAGGED_HOLE - } - hole_res := Phi(hole_res1, hole_res2).any - NOP() - } - Phi(store_res, hole_res).any + end, + lambda do |obj, index, _| + LoadObjectDynamic(obj, i32toanychecked(u32toi32(key))) + .set_access_type(:by_index) + .set_access_mode(:dictionary) + .any + end, + props) end -macro(:handle_store_object_dynamic) do |obj, key, value, ic_slot| +macro(:handle_store_object_dynamic) do |obj, key, value, ic_slot, props| handle_access_object_dynamic(obj, key, value, - lambda do |elements, index, value| - store_value_to_elements(obj, elements, index, value) + lambda do |obj, index, value| + handle_store_object_dynamic_by_index(obj, index, value, props) end, lambda do |obj, _, value| unless ic_slot.nil? - try_store_obj_ic(obj, value, ic_slot) + r0 := try_store_obj_ic(obj, value, ic_slot) + IfImm(cmpanyhole(r0)).Imm(0).CC(:CC_NE).b { + r1 := store_object_dynamic(obj, key, value, ic_slot, props) + } + Phi(r0, r1).any else - Constants::TAGGED_HOLE + store_object_dynamic(obj, key, value, ic_slot, props) end - end) + end, + props) end -macro(:handle_store_object_dynamic_by_index) do |obj, key, value| +macro(:handle_store_object_dynamic_by_index) do |obj, key, value, props| handle_access_object_dynamic_by_index(obj, key, value, lambda do |elements, index, value| store_value_to_elements(obj, elements, index, value) - end) + end, + lambda do |obj, index, value| + Constants::TAGGED_HOLE + StoreObjectDynamic(obj, i32toanychecked(u32toi32(key)), value) + .set_access_type(:by_index) + .set_access_mode(:dictionary) + .any + end, + props) end macro(:handle_ecma_ldobjbyindex) do |index, obj| + props = ObjectAccessProperties.new(:by_index, :unknown, false) IfImm(cmpanyheapobj(obj)).Imm(0).CC(:CC_NE).b { - load_res := handle_load_object_dynamic_by_index(obj, index) + load_res := handle_load_object_dynamic_by_index(obj, index, props) IfImm(cmpanyhole(load_res)).Imm(0).CC(:CC_NE) { slow_res := ecma_intrinsic_invoke("LdObjByIndexSlow", index, obj).any } @@ -1531,8 +1458,9 @@ function(:EcmaLdobjbyindex, params: {'index'=>'i32', 'obj'=>'any'}, mode: [:Inte end macro(:handle_ecma_stobjbyindex) do |index, obj, value| + props = ObjectAccessProperties.new(:by_index, :unknown, false) IfImm(cmpanyheapobj(obj)).Imm(0).CC(:CC_NE).b { - store_res := handle_store_object_dynamic_by_index(obj, index, value) + store_res := handle_store_object_dynamic_by_index(obj, index, value, props) IfImm(cmpanyhole(store_res)).Imm(0).CC(:CC_NE) { ecma_intrinsic_invoke("StObjByIndexSlow", index, obj, value).any } @@ -1547,8 +1475,9 @@ function(:EcmaStobjbyindex, params: {'index'=>'i32', 'obj'=>'any', 'value'=>'any end macro(:handle_ecma_stownbyindex) do |index, obj, value| + props = ObjectAccessProperties.new(:by_index, :unknown, false) IfImm(cmpanyheapobj(obj)).Imm(0).CC(:CC_NE).b { - store_res := handle_store_object_dynamic_by_index(obj, index, value) + store_res := handle_store_object_dynamic_by_index(obj, index, value, props) IfImm(cmpanyhole(store_res)).Imm(0).CC(:CC_NE) { ecma_intrinsic_invoke("StOwnByIndexSlow", index, obj, value).any } @@ -1566,8 +1495,9 @@ end # LD_OBJECT_BY_VALUE # macro(:handle_ecma_ldobjbyvalue) do |obj, key, ic_slot| + props = ObjectAccessProperties.new(:unknown, :unknown, false) IfImm(cmpanyheapobj(obj)).Imm(0).CC(:CC_NE).b { - load_res := handle_load_object_dynamic(obj, key, ic_slot) + load_res := handle_load_object_dynamic(obj, key, ic_slot, props) IfImm(cmpanyhole(load_res)).Imm(0).CC(:CC_NE) { slow_res := ecma_intrinsic_invoke("LdObjByValueSlow", obj, key, ic_slot).any } @@ -1593,8 +1523,9 @@ function(:FastPathLdObjByValue, next end + props = ObjectAccessProperties.new(:unknown, :unknown, false) IfImm(cmpanyheapobj(obj)).Imm(0).NE.b { - load_res := handle_load_object_dynamic(obj, key, ic_slot).any + load_res := handle_load_object_dynamic(obj, key, ic_slot, props).any IfImm(cmpanyhole(load_res)).Imm(0).NE { Intrinsic(:SLOW_PATH_ENTRY, obj, key, ic_slot).Method("LdObjByValueBridge", :AddImm).Relocate.Terminator.any } @@ -1608,8 +1539,9 @@ end # ST_OBJECT_BY_VALUE # macro(:handle_ecma_stobjbyvalue) do |obj, key, value, ic_slot| + props = ObjectAccessProperties.new(:unknown, :unknown, false) IfImm(cmpanyheapobj(obj)).Imm(0).CC(:CC_NE).b { - store_res := handle_store_object_dynamic(obj, key, value, ic_slot) + store_res := handle_store_object_dynamic(obj, key, value, ic_slot, props) IfImm(cmpanyhole(store_res)).Imm(0).CC(:CC_NE) { ecma_intrinsic_invoke("StObjByValueSlow", obj, key, value, ic_slot).any } @@ -1628,15 +1560,16 @@ function(:FastPathStObjByValue, mode: [:FastPath, :DynamicMethod, :DynamicStub], regmap: $full_regmap, regalloc_set: RegMask.new($full_regmap, :arg0, :arg1) + $panda_callees_mask - $panda_regmap, - validate: { spills_count_max: { default: 4, arm32: 9999 } } ) do + validate: { spills_count_max: { default: 6, arm32: 9999 } } ) do # Arm32 is not supported if Options.arch == :arm32 Intrinsic(:UNREACHABLE).Terminator.void next end + props = ObjectAccessProperties.new(:unknown, :unknown, false) IfImm(cmpanyheapobj(obj)).Imm(0).CC(:CC_NE).b { - store_res := handle_store_object_dynamic(obj, key, value, ic_slot) + store_res := handle_store_object_dynamic(obj, key, value, ic_slot, props) IfImm(cmpanyhole(store_res)).Imm(0).CC(:CC_NE) { Intrinsic(:SLOW_PATH_ENTRY, obj, key, value, ic_slot).Method("StObjByValueBridge", :AddImm).Relocate.Terminator.void } @@ -1656,9 +1589,10 @@ macro(:resolve_id) do |id| end macro(:handle_ecma_ldobjbyname) do |obj, id, ic_slot| + props = ObjectAccessProperties.new(:by_name, :unknown, false) key := resolve_id(id) IfImm(cmpanyheapobj(obj)).Imm(0).CC(:CC_NE).b { - load_res := handle_load_object_dynamic(obj, key, ic_slot) + load_res := handle_load_object_dynamic(obj, key, ic_slot, props) IfImm(cmpanyhole(load_res)).Imm(0).CC(:CC_NE).b { slow_res := ecma_intrinsic_invoke("LdObjByNameSlow", id, obj, ic_slot).any } @@ -1684,9 +1618,10 @@ function(:FastPathLdObjByName, next end + props = ObjectAccessProperties.new(:by_name, :unknown, false) key := resolve_id(id) IfImm(cmpanyheapobj(obj)).Imm(0).CC(:CC_NE).b { - load_res := handle_load_object_dynamic(obj, key, ic_slot) + load_res := handle_load_object_dynamic(obj, key, ic_slot, props) IfImm(cmpanyhole(load_res)).Imm(0).CC(:CC_NE).b { Intrinsic(:SLOW_PATH_ENTRY, id, obj, ic_slot).Method("LdObjByNameBridge", :AddImm).Relocate.Terminator.ptr } @@ -1700,9 +1635,10 @@ end # ST_OBJECT_BY_NAME # macro(:handle_ecma_stobjbyname) do |obj, id, value, ic_slot| + props = ObjectAccessProperties.new(:by_name, :unknown, false) key := resolve_id(id) IfImm(cmpanyheapobj(obj)).Imm(0).CC(:CC_NE).b { - store_res := handle_store_object_dynamic(obj, key, value, ic_slot) + store_res := handle_store_object_dynamic(obj, key, value, ic_slot, props) IfImm(cmpanyhole(store_res)).Imm(0).CC(:CC_NE).b { ecma_intrinsic_invoke("StObjByNameSlow", id, obj, value, ic_slot).any } @@ -1728,9 +1664,10 @@ function(:FastPathStObjByName, next end + props = ObjectAccessProperties.new(:by_name, :unknown, false) key := resolve_id(id) IfImm(cmpanyheapobj(obj)).Imm(0).NE.b { - res := handle_store_object_dynamic(obj, key, value, ic_slot) + res := handle_store_object_dynamic(obj, key, value, ic_slot, props) IfImm(cmpanyhole(res)).Imm(0).NE.b { Intrinsic(:SLOW_PATH_ENTRY, id, obj, value, ic_slot).Method("StObjByNameBridge", :AddImm).Relocate.Terminator.ptr } diff --git a/irtoc_scripts/irtoc_plugins.gn b/irtoc_scripts/irtoc_plugins.gn new file mode 100644 index 000000000..11c5be3b3 --- /dev/null +++ b/irtoc_scripts/irtoc_plugins.gn @@ -0,0 +1,17 @@ +# 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. + +srcs = [ + "interpreter_handlers.irt", + "interpreter_main_loop.irt", +] diff --git a/irtoc_scripts/irtoc_scripts.gn b/irtoc_scripts/irtoc_scripts.gn index 11c5be3b3..c85c7d53b 100644 --- a/irtoc_scripts/irtoc_scripts.gn +++ b/irtoc_scripts/irtoc_scripts.gn @@ -12,6 +12,5 @@ # limitations under the License. srcs = [ - "interpreter_handlers.irt", - "interpreter_main_loop.irt", + "object.irt", ] diff --git a/irtoc_scripts/object.irt b/irtoc_scripts/object.irt new file mode 100644 index 000000000..48ec6cced --- /dev/null +++ b/irtoc_scripts/object.irt @@ -0,0 +1,660 @@ +# 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_relative 'common.irt' +include_relative '../../../irtoc/scripts/common.irt' +include_relative 'string_helpers.irt' +include_relative 'object_helpers.irt' + +scoped_macro(:number_of_props) do |arg| + Intrinsic(:DYN_CLASS_NUMBER_OF_PROPS, arg).i32 +end + +scoped_macro(:layout_get_property) do |layout, index| + offset := AddI(Mul(index, Constants::SIZEOF_PTR)).Imm(Constants::HCLASS_LAYOUT_PROPERTIES_OFFSET).u32 + Load(layout, offset).ptr +end + +scoped_macro(:get_properties_cache) do || + LoadI(%tr).Imm(Constants::THREAD_PROPERTIES_CACHE_OFFSET).ptr +end + +scoped_macro(:get_key_hash) do |key| + IfImm(cmpanystring(key)).Imm(0).CC(:CC_NE).b { + string_hash := ecma_string_get_hashcode(anytoheapobj(key)) + } Else { + symbol_hash := LoadObject(anytoheapobj(key)).ObjField(Constants::JS_SYMBOL_HASHFIELD_FIELD).u32 + } + key_hash := Phi(string_hash, symbol_hash).u32 +end + +scoped_macro(:hash) do |cls, key| + cls_hash := Intrinsic(:DYN_CLASS_GET_HASH, cls).u32 + key_hash := get_key_hash(key) + AndI(Xor(cls_hash, key_hash).u32).Imm(Constants::PROPERTIES_CACHE_LENGTH_MASK).u32 +end + +scoped_macro(:get_prop_offset) do |idx| + AddI(Mul(idx, Constants::HCLASS_LAYOUT_PROPERTIES_SIZE).u32).Imm(Constants::HCLASS_LAYOUT_PROPERTIES_OFFSET).u32 +end + +scoped_macro(:get_prop_attr) do |layout, idx| + offset := get_prop_offset(idx) + Load(layout, AddI(offset).Imm(Constants::HCLASS_LAYOUT_PROPERTIES_ATTR_OFFSET).u32).any +end + +scoped_macro(:get_prop_key) do |layout, idx| + offset := get_prop_offset(idx) + Load(layout, AddI(offset).Imm(Constants::HCLASS_LAYOUT_PROPERTIES_KEY_OFFSET).u32).any +end + +scoped_macro(:prop_is_accessor) do |attributes| + get_bits(attributes, Constants::PROPERTY_ATTR_ACCESSOR_BIT, Constants::PROPERTY_ATTR_ACCESSOR_MASK) +end + +scoped_macro(:prop_is_writable) do |attributes| + get_bits(attributes, Constants::PROPERTY_ATTR_WRITABLE_BIT, Constants::PROPERTY_ATTR_WRITABLE_MASK) +end + +scoped_macro(:prop_get_sorted_index) do |attributes| + get_bits(attributes, Constants::PROPERTY_ATTR_SORTED_INDEX_BIT, Constants::PROPERTY_ATTR_SORTED_INDEX_MASK) +end + +scoped_macro(:layout_info_get_sorted_index) do |layout, index| + attributes := get_prop_attr(layout, index) + prop_get_sorted_index(attributes) +end + +scoped_macro(:layout_info_get_sorted_key) do |layout, index| + fixed_idx := layout_info_get_sorted_index(layout, index) + get_prop_key(layout, fixed_idx) +end + +scoped_macro(:layout_info_number_of_elements) do |layout| + anytoi32(LoadArray(layout, Constants::HCLASS_LAYOUT_INFO_NUMBER_OF_PROPERTIES_INDEX).any) +end + +scoped_macro(:layout_binary_search_result) do |index, props_num| + If(index, props_num).CC(:CC_LT) { + r0 := index + } Else { + r1 := -1 + } + Phi(r0, r1).i32 +end + +scoped_macro(:layout_binary_search) do |layout, key, props_num| + low0 := 0 + elements := layout_info_number_of_elements(layout) + high0 := SubI(elements).Imm(1).i32 + key_hash := get_key_hash(key) + r0 := -1 + +Label(:Loop) + low := Phi(low0, low1, low2).i32 + high := Phi(high0, high1, high2).i32 + + If(low, high).CC(:CC_GT).b { + Goto(:Exit) + } + + mid := Add(low, Div(Sub(high, low).i32, 2).i32).i32 + mid_key := layout_info_get_sorted_key(layout, mid) + mid_hash := get_key_hash(mid_key) + + If(mid_hash, key_hash).CC(:CC_GT).b { + low1 := low + high1 := Sub(mid, 1).i32 + Goto(:Loop) + } + + If(mid_hash, key_hash).CC(:CC_LT).b { + low2 := Add(mid, 1).i32 + high2 := high + Goto(:Loop) + } + + sorted_idx := layout_info_get_sorted_index(layout, mid) + current_key := get_prop_key(layout, sorted_idx) + + If(current_key, key).CC(:CC_EQ).b { + r1 := layout_binary_search_result(sorted_idx, props_num) + Goto(:Exit) + } + + mid_left0 := mid +Label(:SearchLeftLoop) + mid_left := Phi(mid_left0, mid_left1).i32 + IfImm(mid_left).Imm(0).CC(:CC_EQ).b { + Goto(:SearchLeftLoopExit) + } + + mid_left1 := Sub(mid_left, 1).i32 + + sorted_idx_left:= layout_info_get_sorted_index(layout, mid_left1) + current_key_left := get_prop_key(layout, sorted_idx_left) + + If(get_key_hash(current_key_left), key_hash).CC(:CC_EQ).b { + If(current_key_left, key).CC(:CC_EQ).b { + r2 := layout_binary_search_result(sorted_idx_left, props_num) + Goto(:Exit) + } + } Else { + r3 := -1 + Goto(:SearchLeftLoopExit) + } + Goto(:SearchLeftLoop) + +Label(:SearchLeftLoopExit) + mid_right0 := mid +Label(:SearchRightLoop) + mid_right := Phi(mid_right0, mid_right1).i32 + + If(mid_right, Sub(elements, 1).i32).CC(:CC_GE).b { + Goto(:Exit) + } + + mid_right1 := Add(mid_right, 1).i32 + + sorted_idx_right:= layout_info_get_sorted_index(layout, mid_right1) + current_key_right := get_prop_key(layout, sorted_idx_right) + + If(get_key_hash(current_key_right), key_hash).CC(:CC_EQ).b { + If(current_key_right, key).CC(:CC_EQ).b { + r4 := layout_binary_search_result(sorted_idx_right, props_num) + Goto(:Exit) + } + } Else { + r5 := -1 + Goto(:Exit) + } + Goto(:SearchRightLoop) + +Label(:Exit) + Phi(r0, r1, r2, r3, r4, r5).i32 +end + +scoped_macro(:layout_find_element) do |layout, obj, key, props_num| + r0 := -1 + i0 := 0 + If(props_num, Constants::HCLASS_LAYOUT_MAX_ELEMENTS_LINEAR_SEARCH).CC(:CC_LE).b { + Label(:LinearSearchLoop) + i := Phi(i0, i1).i32 + If(i, props_num).CC(:CC_EQ).b { + Goto(:LinearSearchLoopExit) + } + + prop_key := get_prop_key(layout, i) + + If(prop_key, key).CC(:CC_EQ).b { + r := i + Goto(:LinearSearchLoopExit) + } + i1 := AddI(i).Imm(1).i32 + Goto(:LinearSearchLoop) + Label(:LinearSearchLoopExit) + linear_res := Phi(r0, r).i32 + } Else { + cache := get_properties_cache() + hclass := get_class(obj) + h := hash(hclass, key) + offset := Mul(h, Constants::PROPERTIES_CACHE_ENTRY_SIZE).u32 + + If(h, Constants::PROPERTIES_CACHE_LENGTH).CC(:CC_EQ).b { + Goto(:Slow) + } + + entry_hclass := Load(cache, AddI(offset).Imm(Constants::PROPERTIES_CACHE_ENTRY_HCLASS_OFFSET).u32).ref + If(entry_hclass, hclass).CC(:CC_NE).b { + Goto(:Slow) + } + + entry_key := Load(cache, AddI(offset).Imm(Constants::PROPERTIES_CACHE_ENTRY_KEY_OFFSET).u32).any + If(entry_key, key).CC(:CC_NE).b { + Goto(:Slow) + } + + entry_res := Load(cache, AddI(offset).Imm(Constants::PROPERTIES_CACHE_ENTRY_RESULTS_OFFSET).u32).i32 + Goto(:Exit) + Label(:Slow) + slow_res := layout_binary_search(layout, key, props_num) + If(slow_res, -1).CC(:CC_NE).b { + Store(cache, AddI(offset).Imm(Constants::PROPERTIES_CACHE_ENTRY_HCLASS_OFFSET).u32, heapobjtoany(hclass)).any + Store(cache, AddI(offset).Imm(Constants::PROPERTIES_CACHE_ENTRY_KEY_OFFSET).u32, key).any + Store(cache, AddI(offset).Imm(Constants::PROPERTIES_CACHE_ENTRY_RESULTS_OFFSET).u32, slow_res).i32 + } + NOP() + Label(:Exit) + res := Phi(entry_res, slow_res).i32 + } + Phi(linear_res, res).i32 +end + +scoped_macro(:dict_get_size) do |dict| + anytoi32(LoadArray(dict, Constants::TAGGED_HASH_TABLE_SIZE_INDEX).any) +end + +scoped_macro(:dict_first_pos) do |hash, size| + mask := SubI(size).Imm(1).u32 + And(hash, mask).i32 +end + +scoped_macro(:dict_next_pos) do |last, number, size| + mask := SubI(size).Imm(1).u32 + # (last + (number * (number + 1)) / 2) & (size - 1); // 2 : half + And(Add(last, Div(Mul(number, AddI(number).Imm(1).u32).u32, 2).u32).u32, mask).i32 +end + +scoped_macro(:dict_get_entry_elem_index) do |entry, entry_size, elem_idx| + AddI(AddI(Mul(entry, entry_size).i32).Imm(Constants::ORDER_TAGGED_HASH_TABLE_HEADER_SIZE).i32).Imm(elem_idx).i32 +end + +scoped_macro(:name_dict_get_value) do |dict, entry| + idx := dict_get_entry_elem_index(entry, + Constants::NAME_DICTIONARY_ENTRY_SIZE, + Constants::NAME_DICTIONARY_ENTRY_VALUE_INDEX) + LoadArray(dict, idx).any +end + +scoped_macro(:name_dict_set_value) do |dict, entry, value| + idx := dict_get_entry_elem_index(entry, + Constants::NAME_DICTIONARY_ENTRY_SIZE, + Constants::NAME_DICTIONARY_ENTRY_VALUE_INDEX) + store_element(dict, idx, value) +end + +scoped_macro(:name_dict_get_attributes) do |dict, entry| + idx := dict_get_entry_elem_index(entry, + Constants::NAME_DICTIONARY_ENTRY_SIZE, + Constants::NAME_DICTIONARY_ENTRY_DETAILS_INDEX) + anytoi32(LoadArray(dict, idx).any) +end + +scoped_macro(:dict_get_key) do |dict, entry, get_key_index| + idx = get_key_index.call(entry) + If(idx, LenArray(dict).i32).CC(:CC_LT).b { + r0 := LoadArray(dict, idx).any + } Else { + r1 := Constants::TAGGED_UNDEFINED + } + Phi(r0, r1).any +end + +scoped_macro(:dict_find_entry) do |dict, key, get_hash, is_match, get_key_index| + size := dict_get_size(dict) + hash = get_hash.call(key) + r0 := -1 + + entry0 := dict_first_pos(hash, size) + count0 := 1 + +Label(:Begin) + entry := Phi(entry0, entry1).i32 + count := Phi(count0, count1).i32 + key_value := dict_get_key(dict, entry, get_key_index) + + IfImm(cmpanyundefined(key_value)).Imm(0).CC(:CC_NE).b { + Goto(:Exit) + } + + IfImm(cmpanyhole(key_value)).Imm(0).CC(:CC_EQ).b { + r = is_match.call(key, key_value) + IfImm(r).Imm(0).CC(:CC_NE).b { + r1 := entry + Goto(:Exit) + } + } + + entry1 := dict_next_pos(entry, count, size) + count1 := AddI(count).Imm(1).i32 + Goto(:Begin) + +Label(:Exit) + Phi(r0, r1).i32 +end + +scoped_macro(:name_dict_find_entry) do |dict, key| + dict_find_entry(dict, key, + lambda do |key| + get_key_hash(key) + end, + lambda do |key, other| + Compare(key, other).CC(:CC_EQ).b + end, + lambda do |entry| + dict_get_entry_elem_index(entry, + Constants::NAME_DICTIONARY_ENTRY_SIZE, + Constants::NAME_DICTIONARY_ENTRY_KEY_INDEX) + end) +end + +scoped_macro(:number_dict_get_value) do |dict, entry| + idx := dict_get_entry_elem_index(entry, + Constants::NUMBER_DICTIONARY_ENTRY_SIZE, + Constants::NUMBER_DICTIONARY_ENTRY_VALUE_INDEX) + LoadArray(dict, idx).any +end + +scoped_macro(:number_dict_set_value) do |dict, entry, value| + idx := dict_get_entry_elem_index(entry, + Constants::NUMBER_DICTIONARY_ENTRY_SIZE, + Constants::NUMBER_DICTIONARY_ENTRY_VALUE_INDEX) + store_element(dict, idx, value).any +end + +scoped_macro(:number_dict_get_attributes) do |dict, entry| + idx := dict_get_entry_elem_index(entry, + Constants::NUMBER_DICTIONARY_ENTRY_SIZE, + Constants::NUMBER_DICTIONARY_ENTRY_DETAILS_INDEX) + anytoi32(LoadArray(dict, idx).any) +end + +scoped_macro(:rotl) do |word, shift| + Or(Shl(word, shift).u32, Shr(word, Sub(32, shift).u32).u32).u32 +end + +scoped_macro(:hash_finilaze) do |h| + h0 := Xor(h, Shr(h, Constants::MURMURHASH_FINALIZE_FIRST_SHIFT).u32).u32 + h1 := Mul(h0, Constants::MURMURHASH_FINALIZE_FIRST_MULTIPLICATOR).u32 + h2 := Xor(h1, Shr(h1, Constants::MURMURHASH_FINALIZE_SECOND_SHIFT).u32).u32 + h3 := Mul(h2, Constants::MURMURHASH_FINALIZE_SECOND_MULTIPLICATOR).u32 + Xor(h3, Shr(h3, Constants::MURMURHASH_FINALIZE_THIRD_SHIFT).u32).u32 +end + +scoped_macro(:get_index_hash) do |index| + k0 := Mul(index, Constants::MURMURHASH_C1).u32 + k1 := rotl(k0, Constants::MURMURHASH_MAIN_FIRST_SHIFT) + k2 := Mul(k1, Constants::MURMURHASH_C2).u32 + + hash0 := Xor(Constants::MURMURHASH_SEED, k2).u32 + hash1 := rotl(hash0, Constants::MURMURHASH_MAIN_SECOND_SHIFT).u32 + hash2 := AddI(Mul(hash1, Constants::MURMURHASH_MAIN_MULTIPLICATOR).u32).Imm(Constants::MURMURHASH_MAIN_CONSTANT).u32 + hash3 := Xor(hash2, Constants::MURMURHASH_KEY_LEN).u32 + hash_finilaze(hash3) +end + +scoped_macro(:number_dict_find_entry) do |dict, index| + dict_find_entry(dict, index, + lambda do |key| + get_index_hash(anytou32(key)) + end, + lambda do |key, other| + IfImm(And(cmpanyi32(key), cmpanyi32(other)).u32).Imm(0).CC(:CC_NE).b { + cmp_r0 := Compare(anytoi32(key), anytoi32(other)).CC(:CC_EQ).b + } Else { + cmp_r1 := 0 + } + Phi(cmp_r0, cmp_r1).b + end, + lambda do |entry| + dict_get_entry_elem_index(entry, + Constants::NUMBER_DICTIONARY_ENTRY_SIZE, + Constants::NUMBER_DICTIONARY_ENTRY_KEY_INDEX) + end) +end + +scoped_macro(:handle_access_object_dynamic_by_key) do |obj, key, value, access, dict_access, access_props| + throw "Incorrect access type #{access_props.type}" unless access_props.type == :by_name || access_props.type == :unknown + + IfImm(is_dictionary_element(obj, access_props)).Imm(0).CC(:CC_EQ).SrcType("DataType::BOOL").b { + cls := get_class(obj) + layout := LoadObject(cls).ObjField(Constants::JS_HCLASS_LAYOUT_FIELD).ref + props_num := number_of_props(cls) + idx := layout_find_element(layout, obj, key, props_num) + If(idx, -1).CC(:CC_NE).b { + attributes := anytoi32(get_prop_attr(layout, idx)) + IfImm(prop_is_accessor(attributes)).Imm(0).CC(:CC_EQ).b { + res = access.call(obj, attributes, value) + access_res := res + } Else { + accessor_res := Constants::TAGGED_HOLE + } + found_res := Phi(access_res, accessor_res).any + } Else { + not_found_res := Constants::TAGGED_HOLE + } + byvalue_inl_res := Phi(found_res, not_found_res).any + } Else { + dict := anytoheapobj(LoadObject(obj).ObjField(Constants::JS_OBJECT_PROPERTIES_FIELD).any) + dict_entry := name_dict_find_entry(dict, key) + + If(dict_entry, -1).CC(:CC_NE).b { + dict_attr := name_dict_get_attributes(dict, dict_entry) + IfImm(prop_is_accessor(dict_attr)).Imm(0).CC(:CC_EQ).b { + dict_res = dict_access.call(dict, dict_entry, dict_attr, value) + dict_access_res := dict_res + } Else { + dict_accessor_res := Constants::TAGGED_HOLE + } + dict_found_res := Phi(dict_access_res, dict_accessor_res).any + } Else { + dict_not_found_res := Constants::TAGGED_HOLE + } + byvalue_dict_res := Phi(dict_found_res, dict_not_found_res).any + } + byvalue_res := Phi(byvalue_inl_res, byvalue_dict_res).any +end + +scoped_macro(:handle_access_object_dynamic_by_index_wrapper) do |obj, index, value, access, dict_access, access_props| + handle_access_object_dynamic_by_index(obj, index, value, access, + lambda do |obj, index, value| + dict := anytoheapobj(LoadObject(obj).ObjField(Constants::JS_OBJECT_PROPERTIES_FIELD).any) + dict_entry := number_dict_find_entry(dict, u32toany(index)) + If(dict_entry, -1).CC(:CC_NE).b { + dict_attr := number_dict_get_attributes(dict, dict_entry) + IfImm(prop_is_accessor(dict_attr)).Imm(0).CC(:CC_EQ).b { + dict_res = dict_access.call(dict, dict_entry, dict_attr, value) + dict_access_res := dict_res + NOP() + } Else { + dict_accessor_res := Constants::TAGGED_HOLE + } + dict_found_res := Phi(dict_access_res, dict_accessor_res).any + } Else { + dict_not_found_res := Constants::TAGGED_HOLE + } + Phi(dict_found_res, dict_not_found_res).any + end, + access_props) +end + +scoped_macro(:get_inlined_props_start_index) do |cls| + bitfield1 := LoadObject(cls).ObjField(Constants::JS_HCLASS_BITFIELD1_FIELD).i32 + get_bits(bitfield1, Constants::JS_HCLASS_BITFIELD1_INLINED_PROPS_START_BIT, Constants::JS_HCLASS_BITFIELD1_INLINED_PROPS_START_MASK) +end + +scoped_macro(:get_inlined_props_offset) do |cls, index| + start_index := get_inlined_props_start_index(cls) + Mul(Add(start_index, index).i32, Constants::TAGGED_TYPE_SIZE).i32 +end + +scoped_macro(:prop_is_inlined) do |attributes| + get_bits(attributes, Constants::PROPERTY_ATTR_INLINE_BIT, Constants::PROPERTY_ATTR_INLINE_MASK) +end + +scoped_macro(:prop_offset) do |attributes| + get_bits(attributes, Constants::PROPERTY_ATTR_OFFSET_BIT, Constants::PROPERTY_ATTR_OFFSET_MASK) +end + +scoped_macro(:get_obj_size) do |cls| + LoadObject(cls).ObjField(Constants::JS_HCLASS_OBJECT_SIZE_FIELD).i32 +end + +scoped_macro(:get_inlined_properties) do |cls| + type := Intrinsic(:GET_OBJECT_CLASS_TYPE, cls).u8 + cmp0 := Compare(type, Constants::JSTYPE_JS_OBJECT_BEGIN).CC(:CC_GE).b + cmp1 := Compare(type, Constants::JSTYPE_JS_OBJECT_END).CC(:CC_LE).b + IfImm(And(cmp0, cmp1).b).Imm(0).CC(:CC_NE).b { + inlined_start_idx := get_inlined_props_start_index(cls) + res0 := Sub(Div(get_obj_size(cls), Constants::TAGGED_TYPE_SIZE).i32, inlined_start_idx).i32 + } Else { + res1 := 0 + } + Phi(res0, res1).i32 +end + +scoped_macro(:obj_get_property) do |obj, attributes, update_ic| + attr_index := prop_offset(attributes) + cls := get_class(obj) + IfImm(prop_is_inlined(attributes)).Imm(0).CC(:CC_NE).b { + offset := get_inlined_props_offset(cls, attr_index) + update_ic.call(cls, Div(offset, Constants::TAGGED_TYPE_SIZE).i32, 1) + r0 := Load(obj, offset).any + } Else { + index := Sub(attr_index, get_inlined_properties(cls)).i32 + update_ic.call(cls, index, 0) + props := LoadObject(obj).ObjField(Constants::JS_OBJECT_PROPERTIES_FIELD).any + r1 := LoadArray(anytoheapobj(props), index).any + } + Phi(r0, r1).any +end + +scoped_macro(:obj_set_property) do |obj, attributes, value, update_ic| + attr_index := prop_offset(attributes) + cls := get_class(obj) + IfImm(prop_is_inlined(attributes)).Imm(0).CC(:CC_NE).b { + offset := get_inlined_props_offset(cls, attr_index) + update_ic.call(cls, Div(offset, Constants::TAGGED_TYPE_SIZE).i32, 1) + Store(obj, offset, value).any + } Else { + index := Sub(attr_index, get_inlined_properties(cls)).i32 + update_ic.call(cls, index, 0) + props := LoadObject(obj).ObjField(Constants::JS_OBJECT_PROPERTIES_FIELD).any + store_element(props, index, value) + } +end + +scoped_macro(:update_ic) do |hclass, offset, is_inlined, ic_slot| + d0 := 0 + d1 := set_bits(d0, Constants::IC_HANDLER_KIND_BIT, Constants::IC_HANDLER_KIND_MASK, Constants::IC_HANDLER_KIND_FIELD) + d2 := set_bits(d1, Constants::IC_HANDLER_OFFSET_BIT, Constants::IC_HANDLER_OFFSET_MASK, offset) + handler := set_bits(d2, Constants::IC_HANDLER_INLINE_BIT, Constants::IC_HANDLER_INLINE_MASK, is_inlined) + + ic := get_ic() + idx := map_ic_slot(ic_slot) + IfImm(cmpanyundefined(ic)).Imm(0).CC(:CC_EQ).b { + StoreArray(anytoheapobj(ic), idx, heapobjtoany(hclass)).SetNeedBarrier(true).any + StoreArray(anytoheapobj(ic), AddI(idx).Imm(1).i32, i32toany(handler)).any + } +end + +[[:unknown, :unknown], [:by_name, :unknown], [:by_index, :dictionary]].each do |access_type, access_mode| + to_name = lambda { |s| s.to_s.split('_').collect(&:capitalize).join } + postfix = "" + postfix = "#{postfix}#{to_name.call(access_type)}" unless access_type == :unknown + postfix = "#{postfix}#{to_name.call(access_mode)}" unless access_mode == :unknown + + extra_params = {} + if access_type == :by_name + extra_params.update({ic_slot: 'u16'}) + end + + function("LoadObjectDynamic#{postfix}", + params: {obj: 'ref', key: 'any'}.merge(extra_params), + regmap: $full_regmap, + mode: [:FastPath, :DynamicMethod, :DynamicStub], + regalloc_set: $panda_mask, + lang: 'ECMASCRIPT') { + if Options.arch == :arm32 + Return(Constants::TAGGED_HOLE).any + next + end + + access_props = ObjectAccessProperties.new(access_type, access_mode, true) + Return(handle_access_object_dynamic(heapobjtoany(obj), key, nil, + lambda do |obj, index, _| + handle_access_object_dynamic_by_index_wrapper(obj, index, nil, + lambda do |elements, index, _| + LoadArray(elements, index).any + end, + lambda do |dict, entry, attributes, _| + number_dict_get_value(dict, entry) + end, + access_props) + end, + lambda do |obj, key, value| + handle_access_object_dynamic_by_key(anytoheapobj(obj), key, nil, + lambda do |obj, attributes, _| + obj_get_property(obj, attributes, + lambda do |hclass, offset, is_inlined| + if access_props.type == :by_name + global :ic_slot + update_ic(hclass, offset, is_inlined, ic_slot) + end + end) + end, + lambda do |dict, entry, attributes, _| + name_dict_get_value(dict, entry) + end, + access_props) + end, + access_props)).any + } + + function("StoreObjectDynamic#{postfix}", + params: {obj: 'ref', key: 'any', value: 'any'}.merge(extra_params), + regmap: $full_regmap, + mode: [:FastPath, :DynamicMethod, :DynamicStub], + regalloc_set: $panda_mask, + lang: 'ECMASCRIPT') { + if Options.arch == :arm32 + Return(Constants::TAGGED_HOLE).any + next + end + + access_props = ObjectAccessProperties.new(access_type, access_mode, true) + Return(handle_access_object_dynamic(heapobjtoany(obj), key, value, + lambda do |obj, index, value| + handle_access_object_dynamic_by_index_wrapper(obj, index, value, + lambda do |elements, index, value| + store_value_to_elements(obj, elements, index, value) + end, + lambda do |dict, entry, attributes, value| + number_dict_set_value(dict, entry, value) + Constants::TAGGED_UNDEFINED + end, + access_props) + end, + lambda do |obj, key, value| + handle_access_object_dynamic_by_key(anytoheapobj(obj), key, value, + lambda do |obj, attributes, value| + IfImm(prop_is_writable(attributes)).Imm(0).CC(:CC_NE).b { + obj_set_property(obj, attributes, value, + lambda do |hclass, offset, is_inlined| + if access_props.type == :by_name + global :ic_slot + update_ic(hclass, offset, is_inlined, ic_slot) + end + end) + NOP() + r0 := Constants::TAGGED_UNDEFINED + } Else { + r1 := Constants::TAGGED_HOLE + } + Phi(r0, r1).any + end, + lambda do |dict, entry, attributes, value| + IfImm(prop_is_writable(attributes)).Imm(0).CC(:CC_NE).b { + name_dict_set_value(dict, entry, value) + NOP() + r0 := Constants::TAGGED_UNDEFINED + } Else { + r1 := Constants::TAGGED_HOLE + } + Phi(r0, r1).any + end, + access_props) + end, + access_props)).any + } +end \ No newline at end of file diff --git a/irtoc_scripts/object_helpers.irt b/irtoc_scripts/object_helpers.irt new file mode 100644 index 000000000..388361c31 --- /dev/null +++ b/irtoc_scripts/object_helpers.irt @@ -0,0 +1,184 @@ +# 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. + +class ObjectAccessProperties + attr_reader :type, :mode, :try_convert_key_to_index + + def initialize(type, mode, try_convert_key_to_index) + @type = type + @mode = mode + @try_convert_key_to_index = try_convert_key_to_index + end +end + +scoped_macro(:get_array_length) do |obj| + len := LoadObject(anytoheapobj(obj)).ObjField(Constants::JS_ARRAY_LENGTH_FIELD).any + IfImm(cmpanyi32(len)).Imm(0).CC(:CC_NE) { + res1 := anytou32(len) + } Else { + res2 := f64tou32(anytof64(len)) + } + Phi(res1, res2).u32 +end + +scoped_macro(:set_array_length) do |obj, length| + StoreObject(anytoheapobj(obj), i32toanychecked(length)).ObjField(Constants::JS_ARRAY_LENGTH_FIELD).any +end + +scoped_macro(:is_dictionary_element) do |arg, props| + if props.mode == :unknown + cls := Intrinsic(:DYN_OBJECT_GET_CLASS, arg).ref + Intrinsic(:DYN_CLASS_IS_DICTIONARY_ELEMENT, cls).b + elsif props.mode == :dictionary + r := 1 + else + r := 0 + end +end + +scoped_macro(:is_extensible) do |arg| + cls := Intrinsic(:DYN_OBJECT_GET_CLASS, arg).ref + Intrinsic(:DYN_CLASS_IS_EXTENSIBLE, cls).b +end + +scoped_macro(:store_element) do |elements, index, value| + IfImm(cmpanyheapobj(value)).Imm(0).CC(:CC_EQ).b { + StoreArray(elements, index, value).any + } Else { + StoreArray(elements, index, value).SetNeedBarrier(true).any + } +end + +scoped_macro(:store_value_to_elements) do |obj, elements, index, value| + element := LoadArray(elements, index).any + IfImm(cmpanyhole(element)).Imm(0).CC(:CC_EQ).b { + store_element(elements, index, value) + store_res := Constants::TAGGED_UNDEFINED + NOP() + } Else { + IfImm(is_extensible(obj)).Imm(0).CC(:CC_NE).b { + IfImm(cmpanyarr(obj)).Imm(0).CC(:CC_NE).b { + len := get_array_length(obj) + If(index, len).CC(:CC_AE).b { + set_array_length(obj, AddI(index).Imm(1).i32) + } + } + store_element(elements, index, value) + hole_res1 := Constants::TAGGED_UNDEFINED + NOP() + } Else { + hole_res2 := Constants::TAGGED_HOLE + } + hole_res := Phi(hole_res1, hole_res2).any + NOP() + } + Phi(store_res, hole_res).any +end + +scoped_macro(:handle_access_object_dynamic_by_index) do |obj, index, value, access, dict_access, props| + throw "Incorrect access type #{props.type}" unless props.type == :by_index || props.type == :unknown + + IfImm(cmpanyspecialindexedobj(obj)).Imm(0).CC(:CC_NE).b { + specialindexedobj_res := Constants::TAGGED_HOLE + Goto(:Exit) + } + + IfImm(is_dictionary_element(obj, props)).Imm(0).CC(:CC_EQ).SrcType("DataType::BOOL").b { + elements := LoadObject(anytoheapobj(obj)).ObjField(Constants::JS_OBJECT_ELEMENTS_FIELD).ref + length := i32tou32(LenArray(elements).i32) + If(index, length).CC(:CC_B).b { + res = access.call(elements, index, value) + access_res := res + } Else { + oob_res := Constants::TAGGED_HOLE + } + array_res := Phi(access_res, oob_res).any + } Else { + res = dict_access.call(obj, index, value) + dict_res := res + } +Label(:Exit) + Phi(specialindexedobj_res, array_res, dict_res).any +end + +scoped_macro(:handle_access_object_dynamic) do |obj, key, value, array_access, obj_access, props| + IfImm(cmpanyspecialindexedobj(obj)).Imm(0).CC(:CC_NE).b { + specialindexedobj_res := Constants::TAGGED_HOLE + Goto(:Exit) + } + + if props.type == :by_index || props.type == :unknown + IfImm(cmpanyi32(key)).Imm(0).CC(:CC_NE).b { + idx0 := anytou32(key) + Goto(:ByIndex) + } + + IfImm(cmpanyf64(key)).Imm(0).CC(:CC_NE).b { + fkey = anytof64(key) + idx1 := f64toi64(fkey).u32 + If(u32tof64(idx1), fkey).CC(:CC_EQ).b { + Goto(:ByIndex) + } Else { + fkey_slow_res := Constants::TAGGED_HOLE + Goto(:Exit) + } + } + + if props.try_convert_key_to_index + IfImm(cmpanystring(key)).Imm(0).CC(:CC_NE).b { + idx2 := ecma_string_to_element_index(anytoheapobj(key)) + If(idx2, Constants::JS_OBJECT_MAX_ELEMENT_INDEX).CC(:CC_NE).b { + Goto(:ByIndex) + } + } + end + + if props.type == :by_index + Intrinsic(:UNREACHABLE).Terminator.void + end + end + + if props.type == :by_name || props.type == :unknown + IfImm(Or(cmpanystring(key), cmpanysymbol(key)).b).Imm(0).CC(:CC_EQ) { + byname_slow_res := Constants::TAGGED_HOLE + Goto(:Exit) + } + + res = obj_access.call(obj, key, value) + byname_res := res + Goto(:Exit) + end + + if props.type == :by_index || props.type == :unknown + Label(:ByIndex) + if props.try_convert_key_to_index + idx := Phi(idx0, idx1, idx2).u32 + else + idx := Phi(idx0, idx1).u32 + end + + res = array_access.call(obj, idx, value) + byindex_res := res + end + +Label(:Exit) + if props.type == :unknown + Phi(specialindexedobj_res, fkey_slow_res, byname_slow_res, byname_res, byindex_res).any + elsif props.type == :by_name + Phi(specialindexedobj_res, byname_slow_res, byname_res).any + elsif props.type == :by_index + Phi(specialindexedobj_res, fkey_slow_res, byindex_res).any + else + throw "Unsupported access type: #{props.type}" + end +end diff --git a/irtoc_scripts/string_helpers.irt b/irtoc_scripts/string_helpers.irt new file mode 100644 index 000000000..1a7891c48 --- /dev/null +++ b/irtoc_scripts/string_helpers.irt @@ -0,0 +1,170 @@ +# 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. + +scoped_macro(:ecma_string_is_utf16) do |s| + mix_length := LoadObject(s).ObjField(Constants::ECMASTRING_MIX_LENGTH_FIELD).u32 + And(mix_length, Constants::ECMASTRING_STRING_COMPRESSED_MASK).b +end + +scoped_macro(:ecma_string_length) do |s| + mix_length := LoadObject(s).ObjField(Constants::ECMASTRING_MIX_LENGTH_FIELD).u32 + ShrI(mix_length).Imm(2).u32 +end + +scoped_macro(:ecma_string_get_char_utf8) do |s, i| + Load(s, AddI(i).Imm(Constants::ECMASTRING_DATA_OFFSET).u32).u8 +end + +scoped_macro(:ecma_string_get_char_utf16) do |s, i| + Load(s, AddI(Mul(i, 2).u32).Imm(Constants::ECMASTRING_DATA_OFFSET).u32).u16 +end + +scoped_macro(:ecma_string_compute_hashcode_utf8) do |s| + length := ecma_string_length(s) + i0 := 0 + h0 := 0 +Label(:Loop) + i := Phi(i0, i1).u32 + h := Phi(h0, h1).u32 + + If(i, length).CC(:CC_GE).b { + Goto(:Exit) + } + + c := ecma_string_get_char_utf8(s, i) + h1 := Add(Sub(ShlI(h).Imm(5).u32, h).u32, c).u32 + i1 := AddI(i).Imm(1).u32 + + Goto(:Loop) + +Label(:Exit) + h +end + +scoped_macro(:ecma_string_compute_hashcode_utf16) do |s| + length := ecma_string_length(s) + i0 := 0 + h0 := 0 +Label(:Loop) + i := Phi(i0, i1).u32 + h := Phi(h0, h1).u32 + + If(i, length).CC(:CC_GE).b { + Goto(:Exit) + } + + c := ecma_string_get_char_utf16(s, i) + h1 := Add(Sub(ShlI(h).Imm(5).u32, h).u32, c).u32 + i1 := AddI(i).Imm(1).u32 + + Goto(:Loop) + +Label(:Exit) + h +end + +scoped_macro(:ecma_string_compute_hashcode) do |s| + IfImm(ecma_string_is_utf16(s)).Imm(0).CC(:CC_NE).b { + r0 := ecma_string_compute_hashcode_utf16(s) + } Else { + r1 := ecma_string_compute_hashcode_utf8(s) + } + Phi(r0, r1).u32 +end + +scoped_macro(:ecma_string_get_hashcode) do |s| + h := LoadObject(s).ObjField(Constants::JS_STRING_HASHCODE_FIELD).u32 + IfImm(h).Imm(0).CC(:CC_EQ).b { + h0 := ecma_string_compute_hashcode(s) + StoreObject(s, h0).ObjField(Constants::JS_STRING_HASHCODE_FIELD).u32 + } Else { + h1 := h + } + Phi(h0, h1).u32 +end + +scoped_macro(:ecma_string_to_element_index) do |s| + IfImm(ecma_string_is_utf16(s)).Imm(0).CC(:CC_NE).b { + r0 := Constants::JS_OBJECT_MAX_ELEMENT_INDEX + Goto(:Exit) + } + + length := ecma_string_length(s) + IfImm(length).Imm(0).CC(:CC_EQ).b { + r1 := Constants::JS_OBJECT_MAX_ELEMENT_INDEX + Goto(:Exit) + } + + c0 := Load(s, Constants::ECMASTRING_DATA_OFFSET).u8 + If(c0, 0x30).CC(:CC_EQ).b { + If(length, 1).CC(:CC_NE).b { + r2 := Constants::JS_OBJECT_MAX_ELEMENT_INDEX + } Else { + r3 := 0 + } + z_r := Phi(r2, r3).u32 + Goto(:Exit) + } + + If(c0, 0x30).CC(:CC_LT).b { + r4 := Constants::JS_OBJECT_MAX_ELEMENT_INDEX + Goto(:Exit) + } + + If(c0, 0x39).CC(:CC_GT).b { + r5 := Constants::JS_OBJECT_MAX_ELEMENT_INDEX + Goto(:Exit) + } + + n0 := SubI(u32tou64(u8tou32(c0))).Imm(0x30).u64 + i0 := 1 + +Label(:Head) + i := Phi(i0, i1).u32 + n := Phi(n0, n1).u64 + If(i, length).CC(:CC_EQ).b { + Goto(:LoopCastAndExit) + } + + c := Load(s, AddI(i).Imm(Constants::ECMASTRING_DATA_OFFSET).u32).u8 + + If(c, 0x30).CC(:CC_LT).b { + r6 := Constants::JS_OBJECT_MAX_ELEMENT_INDEX + Goto(:LoopExit) + } + + If(c, 0x39).CC(:CC_GT).b { + r7 := Constants::JS_OBJECT_MAX_ELEMENT_INDEX + Goto(:LoopExit) + } + + n1 := Add(Mul(n, 10).u64, SubI(u32tou64(u8tou32(c))).Imm(0x30).u64).u64 + + If(n1, Constants::JS_OBJECT_MAX_ELEMENT_INDEX).CC(:CC_GE).b { + r8 := Constants::JS_OBJECT_MAX_ELEMENT_INDEX + Goto(:LoopExit) + } + + i1 := AddI(i).Imm(1).u32 + + Goto(:Head) + +Label(:LoopCastAndExit) + r9 := u64tou32(n) + +Label(:LoopExit) + l_r := Phi(r6, r7, r8, r9).u32 + +Label(:Exit) + Phi(r0, r1, z_r, r4, r5, l_r).u32 +end diff --git a/runtime/CMakeLists.txt b/runtime/CMakeLists.txt index 5f4cff34d..25c2b108e 100644 --- a/runtime/CMakeLists.txt +++ b/runtime/CMakeLists.txt @@ -177,7 +177,36 @@ set(ECMASCRIPT_SOURCES ) add_dependencies(arkruntime_static ecmastdlib_inline_h) -target_sources(arkruntime_static PRIVATE ${ECMASCRIPT_SOURCES}) + +set(IRTOC_ECMASCRIPT_SCRIPTS ${PANDA_ROOT}/plugins/ecmascript/irtoc_scripts) +set(IRTOC_ECMASCRIPT_SCRIPTS ${IRTOC_ECMASCRIPT_SCRIPTS} PARENT_SCOPE) +if(NOT (CMAKE_CROSSCOMPILING OR PANDA_TARGET_OHOS)) + irtoc_compile(TARGET_NAME irtoc_ecmascript_fastpath + INPUT_FILES ${IRTOC_ECMASCRIPT_SCRIPTS}/object.irt + TARGET_VARIABLE IRTOC_ECMASCRIPT_FASTPATH_OBJ) + + execute_process(COMMAND touch ${IRTOC_ECMASCRIPT_FASTPATH_OBJ}) + set(irtoc_ecmascript_fastpath_target irtoc_ecmascript_fastpath) +else() + ExternalProject_Get_Property(panda_host_tools binary_dir) + set(IRTOC_ECMASCRIPT_FASTPATH_OBJ "${binary_dir}/irtoc/irtoc_ecmascript_fastpath/irtoc_ecmascript_fastpath.o") + + SET_SOURCE_FILES_PROPERTIES(${IRTOC_ECMASCRIPT_FASTPATH_OBJ} PROPERTIES + EXTERNAL_OBJECT true + GENERATED true + ) + + # Due to cmake complain about absence of the irtoc file, we just fake it until it will be generated + execute_process(COMMAND mkdir -p ${binary_dir}/irtoc/irtoc_ecmascript_fastpath) + execute_process(COMMAND touch ${IRTOC_ECMASCRIPT_FASTPATH_OBJ}) + set(irtoc_ecmascript_fastpath_target build_host_tools) +endif() + +add_dependencies(arkruntime_static + ${irtoc_ecmascript_fastpath_target} +) + +target_sources(arkruntime_static PRIVATE ${ECMASCRIPT_SOURCES} ${IRTOC_ECMASCRIPT_FASTPATH_OBJ}) target_include_directories(arkruntime_static PUBLIC ${PANDA_ECMASCRIPT_PLUGIN_SOURCE}/runtime ${PANDA_BINARY_ROOT}/compiler/generated diff --git a/runtime/asm_defines/asm_defines.def b/runtime/asm_defines/asm_defines.def index fa8cb1aa9..f51225178 100644 --- a/runtime/asm_defines/asm_defines.def +++ b/runtime/asm_defines/asm_defines.def @@ -10,11 +10,14 @@ DEFINE_VALUE(ECMASCRIPT_ENVIRONMENT_THIS_FUNC_OFFSET, panda::ecmascript::Ecmascr DEFINE_VALUE(JSTYPE_JS_OBJECT_BEGIN, static_cast(panda::ecmascript::JSType::JS_OBJECT_BEGIN)) DEFINE_VALUE(JSTYPE_JS_OBJECT_END, static_cast(panda::ecmascript::JSType::JS_OBJECT_END)) DEFINE_VALUE(JSTYPE_STRING, static_cast(panda::ecmascript::JSType::STRING)) +DEFINE_VALUE(JSTYPE_SYMBOL, static_cast(panda::ecmascript::JSType::SYMBOL)) DEFINE_VALUE(JSTYPE_JS_ARRAY, static_cast(panda::ecmascript::JSType::JS_ARRAY)) DEFINE_VALUE(JSTYPE_PROTOTYPE_HANDLER, static_cast(panda::ecmascript::JSType::PROTOTYPE_HANDLER)) DEFINE_VALUE(JSTYPE_TRANSITION_HANDLER, static_cast(panda::ecmascript::JSType::TRANSITION_HANDLER)) DEFINE_VALUE(JSTYPE_JS_FUNCTION, static_cast(panda::ecmascript::JSType::JS_FUNCTION)) DEFINE_VALUE(ECMASTRING_MIX_LENGTH_OFFSET, panda::ecmascript::EcmaString::MIX_LENGTH_OFFSET) +DEFINE_VALUE(ECMASTRING_DATA_OFFSET, panda::ecmascript::EcmaString::DATA_OFFSET) +DEFINE_VALUE(ECMASTRING_STRING_COMPRESSED_MASK, panda::ecmascript::EcmaString::GetStringCompressionMask()) DEFINE_VALUE(JSFORINITERATOR_FAST_REMAINING_INDEX_OFFSET, panda::ecmascript::JSForInIterator::FAST_REMAINING_INDEX_OFFSET) DEFINE_VALUE(JSFORINITERATOR_REMAINING_KEYS_OFFSET, panda::ecmascript::JSForInIterator::REMAINING_KEYS_OFFSET) DEFINE_VALUE(TAGGEDARRAY_DATA_OFFSET, panda::ecmascript::TaggedArray::DATA_OFFSET) @@ -30,8 +33,19 @@ DEFINE_VALUE(JSHCLASS_BITFIELD_EXTENSIBLE_START_BIT, panda::ecmascript::JSHClass DEFINE_VALUE(JSHCLASS_BITFIELD_INLINED_PROPS_START_BITS_START_BIT, panda::ecmascript::JSHClass::InlinedPropsStartBits::START_BIT) DEFINE_VALUE(JSHCLASS_BITFIELD_INLINED_PROPS_START_BITS_MASK, panda::ecmascript::JSHClass::InlinedPropsStartBits::MaxValue()) DEFINE_VALUE(JSHCLASS_HCLASS_OFFSET, panda::ecmascript::JSHClass::GetHClassOffset()) +DEFINE_VALUE(JSHCLASS_LAYOUT_OFFSET, panda::ecmascript::JSHClass::LAYOUT_OFFSET) +DEFINE_VALUE(JSHCLASS_BITFIELD1_OFFSET, panda::ecmascript::JSHClass::BIT_FIELD1_OFFSET) +DEFINE_VALUE(JSHCLASS_BITFIELD1_NUMBER_OF_PROPS_BITS_START_BIT, panda::ecmascript::JSHClass::NumberOfPropsBits::START_BIT) +DEFINE_VALUE(JSHCLASS_BITFIELD1_NUMBER_OF_PROPS_BITS_MASK, panda::ecmascript::JSHClass::NumberOfPropsBits::MaxValue()) +DEFINE_VALUE(JSHCLASS_OBJECT_SIZE_OFFSET, panda::ecmascript::JSHClass::OBJECT_SIZE_OFFSET) +DEFINE_VALUE(JSHCLASS_LAYOUT_PROPERTIES_OFFSET, panda::ecmascript::LayoutInfo::GetPropertiesOffset()) +DEFINE_VALUE(JSHCLASS_LAYOUT_PROPERTIES_SIZE, sizeof(panda::ecmascript::Properties)) +DEFINE_VALUE(JSHCLASS_LAYOUT_PROPERTIES_KEY_OFFSET, panda::ecmascript::Properties::GetKeyOffset()) +DEFINE_VALUE(JSHCLASS_LAYOUT_PROPERTIES_ATTR_OFFSET, panda::ecmascript::Properties::GetAttrOffset()) +DEFINE_VALUE(JSHCLASS_LAYOUT_INFO_NUMBER_OF_PROPERTIES_INDEX, panda::ecmascript::LayoutInfo::NUMBER_OF_PROPERTIES_INDEX) DEFINE_VALUE(JSOBJECT_PROPERTIES_OFFSET, panda::ecmascript::JSObject::PROPERTIES_OFFSET) DEFINE_VALUE(JSOBJECT_ELEMENTS_OFFSET, panda::ecmascript::JSObject::ELEMENTS_OFFSET) +DEFINE_VALUE(JSOBJECT_MAX_ELEMENT_INDEX, static_cast(panda::ecmascript::JSObject::MAX_ELEMENT_INDEX)) DEFINE_VALUE(JSARRAY_LENGTH_OFFSET, panda::ecmascript::JSArray::LENGTH_OFFSET) DEFINE_VALUE(JSFUNCTION_METHOD_OFFSET, panda::ecmascript::JSFunction::METHOD_OFFSET) DEFINE_VALUE(JSFUNCTION_PROFILE_TYPE_INFO_OFFSET, panda::ecmascript::JSFunction::PROFILE_TYPE_INFO_OFFSET) @@ -46,6 +60,8 @@ DEFINE_VALUE(JSPROTOTYPE_HANDLER_HANDLER_INFO_OFFSET, panda::ecmascript::Prototy DEFINE_VALUE(JSPROTOTYPE_HANDLER_PROTO_CELL_OFFSET, panda::ecmascript::PrototypeHandler::PROTO_CELL_OFFSET) DEFINE_VALUE(JSPROTOTYPE_HANDLER_HOLDER_OFFSET, panda::ecmascript::PrototypeHandler::HOLDER_OFFSET) DEFINE_VALUE(JSPROTO_CHANGE_MARKER_HAS_CHANGED_OFFSET, panda::ecmascript::ProtoChangeMarker::HAS_CHANGED_OFFSET) +DEFINE_VALUE(JS_STRING_HASHCODE_OFFSET, panda::ecmascript::EcmaString::HASHCODE_OFFSET) +DEFINE_VALUE(JS_SYMBOL_HASHFIELD_OFFSET, panda::ecmascript::JSSymbol::HASHFIELD_OFFSET) DEFINE_VALUE(IC_HANDLER_KIND_BIT_START_BIT, panda::ecmascript::HandlerBase::KindBit::START_BIT) DEFINE_VALUE(IC_HANDLER_KIND_BIT_MASK, panda::ecmascript::HandlerBase::KindBit::MaxValue()) DEFINE_VALUE(IC_HANDLER_OFFSET_BIT_START_BIT, panda::ecmascript::HandlerBase::OffsetBit::START_BIT) @@ -56,3 +72,30 @@ DEFINE_VALUE(IC_HANDLER_HANDLER_KIND_FIELD, static_cast(panda::ecmascr DEFINE_VALUE(HCLASS_DATA_OFFSET, HClass::GetDataOffset()) DEFINE_VALUE(JSMETHOD_IC_MAPPING_OFFSET, panda::ecmascript::JSMethod::GetICMappingOffset()) DEFINE_VALUE(JSTHREAD_GLOBAL_OBJECT_OFFSET, panda::ecmascript::JSThread::GetGlobalObjectOffset()) +DEFINE_VALUE(THREAD_PROPERTIES_CACHE_OFFSET, panda::ecmascript::JSThread::GetPropertiesCacheOffset()) +DEFINE_VALUE(PROPERTIES_CACHE_LENGTH, panda::ecmascript::PropertiesCache::CACHE_LENGTH) +DEFINE_VALUE(PROPERTIES_CACHE_LENGTH_MASK, panda::ecmascript::PropertiesCache::CACHE_LENGTH_MASK) +DEFINE_VALUE(PROPERTIES_CACHE_ENTRY_SIZE, sizeof(panda::ecmascript::PropertiesCache::PropertyKey)) +DEFINE_VALUE(PROPERTIES_CACHE_ENTRY_HCLASS_OFFSET, panda::ecmascript::PropertiesCache::PropertyKey::GetHClassOffset()) +DEFINE_VALUE(PROPERTIES_CACHE_ENTRY_KEY_OFFSET, panda::ecmascript::PropertiesCache::PropertyKey::GetKeyOffset()) +DEFINE_VALUE(PROPERTIES_CACHE_ENTRY_RESULTS_OFFSET, panda::ecmascript::PropertiesCache::PropertyKey::GetResultsOffset()) +DEFINE_VALUE(PROPERTY_ATTRIBUTES_OFFSET_FIELD_START_BIT, panda::ecmascript::PropertyAttributes::OffsetField::START_BIT) +DEFINE_VALUE(PROPERTY_ATTRIBUTES_OFFSET_FIELD_MASK, panda::ecmascript::PropertyAttributes::OffsetField::MaxValue()) +DEFINE_VALUE(PROPERTY_ATTRIBUTES_INLINE_FIELD_START_BIT, panda::ecmascript::PropertyAttributes::IsInlinedPropsField::START_BIT) +DEFINE_VALUE(PROPERTY_ATTRIBUTES_INLINE_FIELD_MASK, panda::ecmascript::PropertyAttributes::IsInlinedPropsField::MaxValue()) +DEFINE_VALUE(PROPERTY_ATTRIBUTES_ACCESSOR_FIELD_START_BIT, panda::ecmascript::PropertyAttributes::IsAccessorField::START_BIT) +DEFINE_VALUE(PROPERTY_ATTRIBUTES_ACCESSOR_FIELD_MASK, panda::ecmascript::PropertyAttributes::IsAccessorField::MaxValue()) +DEFINE_VALUE(PROPERTY_ATTRIBUTES_WRITABLE_FIELD_START_BIT, panda::ecmascript::PropertyAttributes::WritableField::START_BIT) +DEFINE_VALUE(PROPERTY_ATTRIBUTES_WRITABLE_FIELD_MASK, panda::ecmascript::PropertyAttributes::WritableField::MaxValue()) +DEFINE_VALUE(PROPERTY_ATTRIBUTES_SORTED_INDEX_FIELD_START_BIT, panda::ecmascript::PropertyAttributes::SortedIndexField::START_BIT) +DEFINE_VALUE(PROPERTY_ATTRIBUTES_SORTED_INDEX_FIELD_MASK, panda::ecmascript::PropertyAttributes::SortedIndexField::MaxValue()) +DEFINE_VALUE(TAGGED_HASH_TABLE_SIZE_INDEX, panda::ecmascript::NameDictionary::SIZE_INDEX) +DEFINE_VALUE(ORDER_TAGGED_HASH_TABLE_HEADER_SIZE, panda::ecmascript::NameDictionary::TABLE_HEADER_SIZE) +DEFINE_VALUE(NAME_DICTIONARY_ENTRY_SIZE, panda::ecmascript::NameDictionary::ENTRY_SIZE) +DEFINE_VALUE(NAME_DICTIONARY_ENTRY_KEY_INDEX, panda::ecmascript::NameDictionary::ENTRY_KEY_INDEX) +DEFINE_VALUE(NAME_DICTIONARY_ENTRY_VALUE_INDEX, panda::ecmascript::NameDictionary::ENTRY_VALUE_INDEX) +DEFINE_VALUE(NAME_DICTIONARY_ENTRY_DETAILS_INDEX, panda::ecmascript::NameDictionary::ENTRY_DETAILS_INDEX) +DEFINE_VALUE(NUMBER_DICTIONARY_ENTRY_SIZE, panda::ecmascript::NumberDictionary::ENTRY_SIZE) +DEFINE_VALUE(NUMBER_DICTIONARY_ENTRY_KEY_INDEX, panda::ecmascript::NumberDictionary::ENTRY_KEY_INDEX) +DEFINE_VALUE(NUMBER_DICTIONARY_ENTRY_VALUE_INDEX, panda::ecmascript::NumberDictionary::ENTRY_VALUE_INDEX) +DEFINE_VALUE(NUMBER_DICTIONARY_ENTRY_DETAILS_INDEX, panda::ecmascript::NumberDictionary::ENTRY_DETAILS_INDEX) diff --git a/runtime/asm_defines/defines.h b/runtime/asm_defines/defines.h index 73cfdd6b0..3a098269e 100644 --- a/runtime/asm_defines/defines.h +++ b/runtime/asm_defines/defines.h @@ -27,6 +27,7 @@ #include "plugins/ecmascript/runtime/ecma_string.h" #include "plugins/ecmascript/runtime/js_for_in_iterator.h" #include "plugins/ecmascript/runtime/tagged_array.h" +#include "plugins/ecmascript/runtime/tagged_dictionary.h" #include "plugins/ecmascript/runtime/tagged_queue.h" #include "plugins/ecmascript/runtime/js_method.h" diff --git a/runtime/ecma_entrypoints.yaml b/runtime/ecma_entrypoints.yaml index 6941cf0e7..760ecad65 100644 --- a/runtime/ecma_entrypoints.yaml +++ b/runtime/ecma_entrypoints.yaml @@ -1,30 +1,89 @@ -# 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. -# -# This file describes copmiler-to-runtime entrypoints. -# Fields: -# * properties: -# - no_return: entrypoint doesn't jump back to the caller. -# - external: don't generate entrypoint and bridge declarations, initialize table's element by nullptr. -# - irtoc: entrypoint is generated by irtoc tool -# - intrinsic: this is call of intrinsic wrapped in entrypoint bridge -# * signature: signature of the entrypoint, the first element is the return value, the rest are arguments. -# * entrypoint: entrypoint function name. - -- name: GetGlobalVarAddress - entrypoint: JSGetGlobalVarAddress - bridge: entrypoint - properties: [] - signature: - - uintptr_t - - uint32_t +# 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. +# +# This file describes copmiler-to-runtime entrypoints. +# Fields: +# * properties: +# - no_return: entrypoint doesn't jump back to the caller. +# - external: don't generate entrypoint and bridge declarations, initialize table's element by nullptr. +# - irtoc: entrypoint is generated by irtoc tool +# - intrinsic: this is call of intrinsic wrapped in entrypoint bridge +# * signature: signature of the entrypoint, the first element is the return value, the rest are arguments. +# * entrypoint: entrypoint function name. + +- name: GetGlobalVarAddress + entrypoint: JSGetGlobalVarAddress + bridge: entrypoint + properties: [] + signature: + - uintptr_t + - uint32_t + +- name: StoreObjectDynamic + entrypoint: StoreObjectDynamic + bridge: none + properties: [irtoc] + signature: + - uint64_t + - panda::ObjectHeader* + - uint64_t + - uint64_t + +- name: LoadObjectDynamic + entrypoint: LoadObjectDynamic + bridge: none + properties: [irtoc] + signature: + - uint64_t + - panda::ObjectHeader* + - uint64_t + +- name: StoreObjectDynamicByName + entrypoint: StoreObjectDynamicByName + bridge: none + properties: [irtoc] + signature: + - uint64_t + - panda::ObjectHeader* + - uint64_t + - uint64_t + - uint16_t + +- name: LoadObjectDynamicByName + entrypoint: LoadObjectDynamicByName + bridge: none + properties: [irtoc] + signature: + - uint64_t + - panda::ObjectHeader* + - uint64_t + - uint16_t + +- name: StoreObjectDynamicByIndexDictionary + entrypoint: StoreObjectDynamicByIndexDictionary + bridge: none + properties: [irtoc] + signature: + - uint64_t + - panda::ObjectHeader* + - uint64_t + - uint64_t + +- name: LoadObjectDynamicByIndexDictionary + entrypoint: LoadObjectDynamicByIndexDictionary + bridge: none + properties: [irtoc] + signature: + - uint64_t + - panda::ObjectHeader* + - uint64_t diff --git a/runtime/ecma_runtime.yaml b/runtime/ecma_runtime.yaml index 6a2b4fab1..255ea9f89 100644 --- a/runtime/ecma_runtime.yaml +++ b/runtime/ecma_runtime.yaml @@ -1896,7 +1896,7 @@ intrinsics: static: true codegen_func: GetObjectClassTypeIntrinsic signature: - ret: void + ret: u8 args: [any] impl: panda::ecmascript::intrinsics::GetObjectClassType use_thread: false @@ -2013,3 +2013,57 @@ intrinsics: codegen_func: CreateDynCallCheck use_thread: false clear_flags: [] + +- name: DynClassNumberOfProps + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: DynClassNumberOfProps + static: true + signature: + ret: u32 + args: [any] + impl: panda::ecmascript::intrinsics::DynClassNumberOfProps + codegen_func: CreateDynClassNumberOfProps + use_thread: false + clear_flags: [no_dce, barrier, require_state, runtime_call] + +- name: DynClassGetHash + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: DynClassGetHash + static: true + signature: + ret: u32 + args: [any] + impl: panda::ecmascript::intrinsics::DynClassGetHash + codegen_func: CreateDynClassGetHash + use_thread: false + clear_flags: [no_dce, barrier, require_state, runtime_call] + +- name: LdObjDynByName + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: LdObjDynByName + static: true + signature: + ret: any + args: [any, any, u16] + impl: panda::ecmascript::intrinsics::LdObjDynByName + codegen_func: CreateLdObjDynByName + use_thread: false + is_fastpath: true + clear_flags: [barrier, require_state] + +- name: StObjDynByName + space: ecmascript + class_name: Ecmascript.Intrinsics + method_name: StObjDynByName + static: true + signature: + ret: any + args: [any, any, any, u16] + impl: panda::ecmascript::intrinsics::StObjDynByName + codegen_func: CreateStObjDynByName + use_thread: false + is_fastpath: true + clear_flags: [barrier, require_state] diff --git a/runtime/ic/properties_cache-inl.h b/runtime/ic/properties_cache-inl.h index ec5060607..534b31e0c 100644 --- a/runtime/ic/properties_cache-inl.h +++ b/runtime/ic/properties_cache-inl.h @@ -48,7 +48,7 @@ void PropertiesCache::Clear() int PropertiesCache::Hash(JSHClass *cls, JSTaggedValue key) { - uint32_t clsHash = static_cast(reinterpret_cast(cls)) >> 3U; // skip 8bytes + uint32_t clsHash = JSHClass::Hash(cls); uint32_t keyHash = key.GetKeyHashCode(); return static_cast((clsHash ^ keyHash) & CACHE_LENGTH_MASK); } diff --git a/runtime/ic/properties_cache.h b/runtime/ic/properties_cache.h index 37be36b31..1c43e738f 100644 --- a/runtime/ic/properties_cache.h +++ b/runtime/ic/properties_cache.h @@ -31,6 +31,30 @@ public: inline void Clear(); static const int NOT_FOUND = -1; + static const uint32_t CACHE_LENGTH_BIT = 10; + static const uint32_t CACHE_LENGTH = (1U << CACHE_LENGTH_BIT); + static const uint32_t CACHE_LENGTH_MASK = CACHE_LENGTH - 1; + + struct PropertyKey { + JSHClass *hclass_ {nullptr}; + JSTaggedValue key_ {JSTaggedValue::Hole()}; + int results_ {NOT_FOUND}; + + static constexpr uint32_t GetHClassOffset() + { + return MEMBER_OFFSET(PropertyKey, hclass_); + } + + static constexpr uint32_t GetKeyOffset() + { + return MEMBER_OFFSET(PropertyKey, key_); + } + + static constexpr uint32_t GetResultsOffset() + { + return MEMBER_OFFSET(PropertyKey, results_); + } + }; DEFAULT_MOVE_SEMANTIC(PropertiesCache); DEFAULT_COPY_SEMANTIC(PropertiesCache); @@ -46,18 +70,8 @@ private: } ~PropertiesCache() = default; - struct PropertyKey { - JSHClass *hclass_ {nullptr}; - JSTaggedValue key_ {JSTaggedValue::Hole()}; - int results_ {NOT_FOUND}; - }; - static inline int Hash(JSHClass *cls, JSTaggedValue key); - static const uint32_t CACHE_LENGTH_BIT = 10; - static const uint32_t CACHE_LENGTH = (1U << CACHE_LENGTH_BIT); - static const uint32_t CACHE_LENGTH_MASK = CACHE_LENGTH - 1; - std::array keys_ {}; friend class JSThread; diff --git a/runtime/intrinsics-inl.h b/runtime/intrinsics-inl.h index 2d9dc1454..4b392ee27 100644 --- a/runtime/intrinsics-inl.h +++ b/runtime/intrinsics-inl.h @@ -1932,7 +1932,7 @@ INLINE_ECMA_INTRINSICS void NativeMethodWrapper([[maybe_unused]] uint64_t arg) } // NOLINTNEXTLINE(misc-definitions-in-headers) -INLINE_ECMA_INTRINSICS void GetObjectClassType([[maybe_unused]] uint64_t arg) +INLINE_ECMA_INTRINSICS uint8_t GetObjectClassType([[maybe_unused]] uint64_t arg) { UNREACHABLE(); } @@ -1943,12 +1943,6 @@ INLINE_ECMA_INTRINSICS uint64_t IsNan([[maybe_unused]] double arg) return static_cast(std::isnan(arg)); } -// NOLINTNEXTLINE(misc-definitions-in-headers) -INLINE_ECMA_INTRINSICS uint32_t GetStringHashcode(uint64_t arg) -{ - return EcmaString::Cast(JSTaggedValue(arg).GetHeapObject())->GetHashcode(); -} - // NOLINTNEXTLINE(misc-definitions-in-headers) INLINE_ECMA_INTRINSICS uint64_t GetEcmaConstantPool(JSThread *thread) { @@ -1995,6 +1989,34 @@ INLINE_ECMA_INTRINSICS void DynObjectSetClass(uint64_t obj, uint64_t hclass) JSTaggedValue(obj).GetTaggedObject()->SetClass(cls); } +// NOLINTNEXTLINE(misc-definitions-in-headers) +INLINE_ECMA_INTRINSICS uint32_t DynClassNumberOfProps(uint64_t obj) +{ + auto *cls = JSHClass::Cast(JSTaggedValue(obj).GetTaggedObject()); + return cls->NumberOfProps(); +} + +// NOLINTNEXTLINE(misc-definitions-in-headers) +INLINE_ECMA_INTRINSICS uint32_t DynClassGetHash(uint64_t obj) +{ + auto *cls = JSHClass::Cast(JSTaggedValue(obj).GetTaggedObject()); + return JSHClass::Hash(cls); +} + +// NOLINTNEXTLINE(misc-definitions-in-headers) +INLINE_ECMA_INTRINSICS uint64_t LdObjDynByName([[maybe_unused]] uint64_t obj, [[maybe_unused]] uint64_t key, + [[maybe_unused]] uint16_t ic_slot) +{ + return JSTaggedValue::Hole().GetRawData(); +} + +// NOLINTNEXTLINE(misc-definitions-in-headers) +INLINE_ECMA_INTRINSICS uint64_t StObjDynByName([[maybe_unused]] uint64_t obj, [[maybe_unused]] uint64_t key, + [[maybe_unused]] uint64_t value, [[maybe_unused]] uint16_t ic_slot) +{ + return JSTaggedValue::Hole().GetRawData(); +} + } // namespace panda::ecmascript::intrinsics #endif // PLUGINS_ECMASCRIPT_RUNTIME_INTRINSICS_INL_H diff --git a/runtime/js_hclass.h b/runtime/js_hclass.h index bead4887c..dfa8e46e7 100644 --- a/runtime/js_hclass.h +++ b/runtime/js_hclass.h @@ -236,6 +236,11 @@ public: OFFSET_MAX_OBJECT_SIZE_IN_WORDS_WITHOUT_INLINED>; // 15 using ObjectSizeInWordsBits = InlinedPropsStartBits::NextField; // 30 + static uint32_t Hash(JSHClass *cls) + { + return static_cast(reinterpret_cast(cls)) >> 3U; + } + static JSHClass *Cast(const TaggedObject *object); inline bool HasReferenceField(); diff --git a/runtime/layout_info.h b/runtime/layout_info.h index 847701739..8ff6e34a7 100644 --- a/runtime/layout_info.h +++ b/runtime/layout_info.h @@ -24,6 +24,16 @@ namespace panda::ecmascript { struct Properties { JSTaggedValue key_; JSTaggedValue attr_; + + static constexpr uint32_t GetKeyOffset() + { + return MEMBER_OFFSET(Properties, key_); + } + + static constexpr uint32_t GetAttrOffset() + { + return MEMBER_OFFSET(Properties, attr_); + } }; class LayoutInfo : private TaggedArray { @@ -62,8 +72,7 @@ public: inline Properties *GetProperties() const { - return reinterpret_cast(reinterpret_cast(this) + TaggedArray::DATA_OFFSET + - ELEMENTS_START_INDEX * JSTaggedValue::TaggedTypeSize()); + return reinterpret_cast(reinterpret_cast(this) + GetPropertiesOffset()); } static inline uint32_t ComputeArrayLength(uint32_t properties_number) @@ -77,6 +86,11 @@ public: return new_capacity > MAX_PROPERTIES_LENGTH ? MAX_PROPERTIES_LENGTH : new_capacity; } + static inline constexpr uint32_t GetPropertiesOffset() + { + return TaggedArray::DATA_OFFSET + ELEMENTS_START_INDEX * JSTaggedValue::TaggedTypeSize(); + } + int32_t FindElementWithCache(JSThread *thread, JSHClass *cls, JSTaggedValue key, int32_t propertiesNumber); int32_t FindElement(JSTaggedValue key, int32_t propertiesNumber); int32_t BinarySearch(JSTaggedValue key, int32_t propertiesNumber); diff --git a/subproject_sources.gn b/subproject_sources.gn index 8ee17cffd..d35c75fc8 100644 --- a/subproject_sources.gn +++ b/subproject_sources.gn @@ -20,7 +20,8 @@ option_yaml_path = "ecmascript_plugin_options.yaml" inst_templates_yaml_path = "compiler/optimizer/ir_builder/ecmascript_inst_templates.yaml" runtime_option_yaml_path = "runtime_options.yaml" entrypoints_yaml_path = "runtime/ecma_entrypoints.yaml" -irtoc_plugins = "irtoc_scripts/irtoc_scripts.gn" +irtoc_scripts = "irtoc_scripts/irtoc_scripts.gn" +irtoc_plugins = "irtoc_scripts/irtoc_plugins.gn" arkruntime_header_sub_deps = [ ":ecma_intrinsics_gen_arkruntime", ":ecmastblib_inline_h", -- Gitee