From 5c79c9cf2d6a6a3db3f884dbdf50cfff18ff9924 Mon Sep 17 00:00:00 2001 From: Ishin Pavel Date: Mon, 3 Apr 2023 18:05:19 +0300 Subject: [PATCH] Support TypeOf in compiler Signed-off-by: Ishin Pavel --- .../ecmascript_compiler_interface.h | 5 + .../ecmascript_irtoc_interface.h | 60 ++++++++ .../ecmascript_irtoc_interface_includes.h | 22 +++ compiler/intrinsics_inline_ecmascript.inl | 4 + .../intrinsics_type_resolving_ecmascript.cpp | 17 +++ ...intrinsics_type_resolving_ecmascript.inl.h | 1 + .../code_generator/compiler_base_types.cpp | 75 +++++++++- .../code_generator/compiler_base_types.h | 2 + compiler/optimizer/ir/dyn_datatype.h | 16 ++ ecmascript_plugin_options.yaml | 4 + irtoc_scripts/common.irt | 51 +++++++ irtoc_scripts/interpreter_handlers.irt | 68 +++++++++ isa/isa.yaml | 7 +- runtime/asm_defines/asm_defines.def | 4 +- .../compiler/ecmascript_runtime_interface.cpp | 76 +++++++++- .../compiler/ecmascript_runtime_interface.h | 6 + runtime/ecma_profiling.h | 130 +++++++++++++++- runtime/ecma_runtime.yaml | 1 + runtime/global_env_constants.h | 10 ++ runtime/interpreter/ecma-interpreter-inl.h | 31 +++- tests/checked/CMakeLists.txt | 1 + tests/checked/typeof.js | 140 ++++++++++++++++++ 22 files changed, 717 insertions(+), 14 deletions(-) create mode 100644 compiler/ecmascript_extensions/ecmascript_irtoc_interface.h create mode 100644 compiler/ecmascript_extensions/ecmascript_irtoc_interface_includes.h create mode 100644 tests/checked/typeof.js diff --git a/compiler/ecmascript_extensions/ecmascript_compiler_interface.h b/compiler/ecmascript_extensions/ecmascript_compiler_interface.h index 7455e2571..a16e51510 100644 --- a/compiler/ecmascript_extensions/ecmascript_compiler_interface.h +++ b/compiler/ecmascript_extensions/ecmascript_compiler_interface.h @@ -88,4 +88,9 @@ virtual GlobalVarInfo GetGlobalVarInfo([[maybe_unused]] MethodPtr method, [[mayb return {GlobalVarInfo::Type::DEFAULT, 0}; } +virtual uintptr_t GetGlobalConstStringOffsetForAnyType([[maybe_unused]] compiler::AnyBaseType type, + [[maybe_unused]] Arch arch) const +{ + return 0; +} #endif // PANDA_COMPILER_ECMASCRIPT_COMPILER_INTERFACE_H \ No newline at end of file diff --git a/compiler/ecmascript_extensions/ecmascript_irtoc_interface.h b/compiler/ecmascript_extensions/ecmascript_irtoc_interface.h new file mode 100644 index 000000000..bfe30ebde --- /dev/null +++ b/compiler/ecmascript_extensions/ecmascript_irtoc_interface.h @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2021-2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_COMPILER_ECMASCRIPT_IRTOC_INTERFACE_H +#define PANDA_COMPILER_ECMASCRIPT_IRTOC_INTERFACE_H + +size_t GetGlobalConstStringOffsetForAnyType(compiler::AnyBaseType type, Arch arch) const override +{ + auto global_const_array_offset = + cross_values::GetJsthreadGlobalConstantsOffset(arch) + cross_values::GetGlobalConstConstantsOffset(arch); + switch (type) { + case compiler::AnyBaseType::ECMASCRIPT_UNDEFINED_TYPE: + return global_const_array_offset + panda::ecmascript::GlobalEnvConstants::GetGlobalConstantOffset( + panda::ecmascript::ConstantIndex::UNDEFINED_STRING_INDEX); + case compiler::AnyBaseType::ECMASCRIPT_STRING_TYPE: + return global_const_array_offset + panda::ecmascript::GlobalEnvConstants::GetGlobalConstantOffset( + panda::ecmascript::ConstantIndex::STRING_STRING_INDEX); + case compiler::AnyBaseType::ECMASCRIPT_INT_TYPE: + return global_const_array_offset + panda::ecmascript::GlobalEnvConstants::GetGlobalConstantOffset( + panda::ecmascript::ConstantIndex::NUMBER_STRING_INDEX); + case compiler::AnyBaseType::ECMASCRIPT_DOUBLE_TYPE: + return global_const_array_offset + panda::ecmascript::GlobalEnvConstants::GetGlobalConstantOffset( + panda::ecmascript::ConstantIndex::NUMBER_STRING_INDEX); + case compiler::AnyBaseType::ECMASCRIPT_SYMBOL_TYPE: + return global_const_array_offset + panda::ecmascript::GlobalEnvConstants::GetGlobalConstantOffset( + panda::ecmascript::ConstantIndex::SYMBOL_STRING_INDEX); + case compiler::AnyBaseType::ECMASCRIPT_CALLABLE_TYPE: + return global_const_array_offset + panda::ecmascript::GlobalEnvConstants::GetGlobalConstantOffset( + panda::ecmascript::ConstantIndex::FUNCTION_STRING_INDEX); + case compiler::AnyBaseType::ECMASCRIPT_BOOLEAN_TYPE: + return global_const_array_offset + panda::ecmascript::GlobalEnvConstants::GetGlobalConstantOffset( + panda::ecmascript::ConstantIndex::BOOLEAN_STRING_INDEX); + case compiler::AnyBaseType::ECMASCRIPT_NULL_TYPE: + return global_const_array_offset + panda::ecmascript::GlobalEnvConstants::GetGlobalConstantOffset( + panda::ecmascript::ConstantIndex::OBJECT_STRING_INDEX); + case compiler::AnyBaseType::ECMASCRIPT_BIGINT_TYPE: + return global_const_array_offset + panda::ecmascript::GlobalEnvConstants::GetGlobalConstantOffset( + panda::ecmascript::ConstantIndex::BIGINT_STRING_INDEX); + case compiler::AnyBaseType::ECMASCRIPT_HEAP_OBJECT_TYPE: + return global_const_array_offset + panda::ecmascript::GlobalEnvConstants::GetGlobalConstantOffset( + panda::ecmascript::ConstantIndex::OBJECT_STRING_INDEX); + default: + UNREACHABLE(); + } + return 0; +} + +#endif // PANDA_COMPILER_ECMASCRIPT_IRTOC_INTERFACE_H \ No newline at end of file diff --git a/compiler/ecmascript_extensions/ecmascript_irtoc_interface_includes.h b/compiler/ecmascript_extensions/ecmascript_irtoc_interface_includes.h new file mode 100644 index 000000000..e28a2ce93 --- /dev/null +++ b/compiler/ecmascript_extensions/ecmascript_irtoc_interface_includes.h @@ -0,0 +1,22 @@ +/* + * Copyright (c) 2021-2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef PANDA_COMPILER_ECMASCRIPT_IRTOC_INTERFACE_INCLUDES_H +#define PANDA_COMPILER_ECMASCRIPT_IRTOC_INTERFACE_INCLUDES_H + +#include "plugins/ecmascript/runtime/global_env_constants.h" +#include "plugins/ecmascript/runtime/js_thread.h" + +#endif // PANDA_COMPILER_ECMASCRIPT_IRTOC_INTERFACE_INCLUDES_H \ No newline at end of file diff --git a/compiler/intrinsics_inline_ecmascript.inl b/compiler/intrinsics_inline_ecmascript.inl index 07f009200..97e6c1df7 100644 --- a/compiler/intrinsics_inline_ecmascript.inl +++ b/compiler/intrinsics_inline_ecmascript.inl @@ -152,4 +152,8 @@ case RuntimeInterface::IntrinsicId::INTRINSIC_ST_OBJ_BY_NAME: { } case RuntimeInterface::IntrinsicId::INTRINSIC_RESOLVE_ALLOC_RESULT: { return InlineResolveAllocResult(intrinsic); +} +case RuntimeInterface::IntrinsicId::INTRINSIC_TYPEOF_DYN: { + ASSERT(types_.size() == 1); + return InlineTypeOf(intrinsic); } \ No newline at end of file diff --git a/compiler/intrinsics_type_resolving_ecmascript.cpp b/compiler/intrinsics_type_resolving_ecmascript.cpp index d10331f15..6443e5acf 100644 --- a/compiler/intrinsics_type_resolving_ecmascript.cpp +++ b/compiler/intrinsics_type_resolving_ecmascript.cpp @@ -99,6 +99,23 @@ bool TypesResolving::InlineLdNan(IntrinsicInst *intrinsic) return InlineLdConstant(intrinsic, AnyBaseType::ECMASCRIPT_DOUBLE_TYPE, TaggedValue::VALUE_NAN); } +bool TypesResolving::InlineTypeOf(IntrinsicInst *intrinsic) +{ + auto any_type = intrinsic->GetInput(0).GetInst(); + if (any_type->GetOpcode() != Opcode::AnyTypeCheck) { + return false; + } + auto type = any_type->CastToAnyTypeCheck()->GetAnyType(); + + if (type == AnyBaseType::UNDEFINED_TYPE) { + return false; + } + auto any_name = GetGraph()->CreateInstGetAnyTypeName(DataType::ANY, intrinsic->GetPc()); + any_name->SetAnyType(type); + intrinsic->InsertBefore(any_name); + return true; +} + void TypesResolving::CreateCompareClass(uint32_t pc, Inst *get_cls_inst, RuntimeInterface::ClassPtr receiver, BasicBlock *load_bb) { diff --git a/compiler/intrinsics_type_resolving_ecmascript.inl.h b/compiler/intrinsics_type_resolving_ecmascript.inl.h index 8505d54b5..ae5ec59de 100644 --- a/compiler/intrinsics_type_resolving_ecmascript.inl.h +++ b/compiler/intrinsics_type_resolving_ecmascript.inl.h @@ -27,6 +27,7 @@ bool InlineLdNan(IntrinsicInst *intrinsic); bool InlineLdObjByName(IntrinsicInst *intrinsic); bool InlineStObjByName(IntrinsicInst *intrinsic); bool InlineResolveAllocResult(IntrinsicInst *intrinsic); +bool InlineTypeOf(IntrinsicInst *intrinsic); private: template diff --git a/compiler/optimizer/code_generator/compiler_base_types.cpp b/compiler/optimizer/code_generator/compiler_base_types.cpp index ace74e71c..937a7a689 100644 --- a/compiler/optimizer/code_generator/compiler_base_types.cpp +++ b/compiler/optimizer/code_generator/compiler_base_types.cpp @@ -156,15 +156,73 @@ static void CompareAnyTypeGenObject(Encoder *enc, const Reg &dst, const Reg &src enc->BindLabel(label_false); } +static void CompareOrCheckAnyTypeCallable(Codegen *codegen, Encoder *enc, const Reg &dst, const Reg &src, + LabelHolder::LabelId id = LabelHolder::INVALID_LABEL) +{ + auto label_false = enc->CreateLabel(); + auto arch = codegen->GetArch(); + Reg res_reg = dst; + bool need_temp = (dst.GetId() == src.GetId()); + ScopedTmpRegLazy tmp_dst(enc); + if (need_temp) { + tmp_dst.AcquireIfInvalid(); + res_reg = tmp_dst.GetReg().As(codegen->ConvertDataType(DataType::ANY, arch)); + } + + if (id == LabelHolder::INVALID_LABEL) { + enc->EncodeMov(res_reg, Imm(0)); + } + LabelHolder::LabelId label = (id == LabelHolder::INVALID_LABEL) ? label_false : id; + CheckAnyTypeGenObject(enc, src, label); + ScopedTmpReg tmp_reg(enc, codegen->ConvertDataType(DataType::REFERENCE, arch)); + + enc->EncodeLdr(tmp_reg, false, MemRef(src, codegen->GetRuntime()->GetObjClassOffset(arch))); + Reg tmp32_reg = tmp_reg.GetReg().As(TypeInfo(TypeInfo::TypeId::INT32)); + enc->EncodeLdr(tmp32_reg, false, MemRef(tmp_reg, codegen->GetRuntime()->GetBaseClassFlagsOffset(arch))); + auto is_callable_mask = codegen->GetRuntime()->GetCallableMask(); + enc->EncodeJumpTest(label, tmp32_reg, Imm(is_callable_mask), Condition::TST_EQ); + if (id == LabelHolder::INVALID_LABEL) { + enc->EncodeMov(res_reg, Imm(1U)); + enc->BindLabel(label_false); + if (need_temp) { + enc->EncodeMov(dst, res_reg); + } + } +} + // dst = (IsHeapObject() and ! object.type, type) static void CompareAnyTypeGenObjectType(Codegen *codegen, Encoder *enc, const Reg &dst, const Reg &src, uint32_t type, Condition fail_cc = Condition::NE) { + Reg res_reg = dst; + auto arch = codegen->GetArch(); + bool need_temp = (dst.GetId() == src.GetId()); + ScopedTmpRegLazy tmp_reg(enc); + if (need_temp) { + tmp_reg.AcquireIfInvalid(); + res_reg = tmp_reg.GetReg().As(codegen->ConvertDataType(DataType::ANY, arch)); + } auto label_false = enc->CreateLabel(); - enc->EncodeMov(dst, Imm(0)); + enc->EncodeMov(res_reg, Imm(0)); CheckAnyTypeGenObjectType(codegen, enc, src, type, label_false, fail_cc); - enc->EncodeMov(dst, Imm(1U)); + enc->EncodeMov(res_reg, Imm(1U)); enc->BindLabel(label_false); + if (need_temp) { + enc->EncodeMov(dst, res_reg); + } +} + +bool ecmascript::GetAnyTypeNameGen(const GetAnyTypeNameInst *inst, EncodeVisitor *enc_v) +{ + Encoder *enc = enc_v->GetEncoder(); + Codegen *codegen = enc_v->GetCodegen(); + auto dst = codegen->ConvertRegister(inst->GetDstReg(), DataType::Type::ANY); + + auto graph = codegen->GetGraph(); + auto offset = graph->GetRuntime()->GetGlobalConstStringOffsetForAnyType(inst->GetAnyType(), graph->GetArch()); + enc->EncodeLdr(dst, false, MemRef(codegen->ThreadReg(), offset)); + + return true; } bool ecmascript::CompareAnyTypeGen(const CompareAnyTypeInst *cati, EncodeVisitor *enc_v) @@ -216,6 +274,12 @@ bool ecmascript::CompareAnyTypeGen(const CompareAnyTypeInst *cati, EncodeVisitor CompareAnyTypeGenObjectType(codegen, enc, dst, src, cross_values::GetJstypeJsArray(codegen->GetArch()), Condition::LE); return true; + case AnyBaseType::ECMASCRIPT_FUNCTION_TYPE: + CompareAnyTypeGenObjectType(codegen, enc, dst, src, cross_values::GetJstypeJsFunction(codegen->GetArch())); + return true; + case AnyBaseType::ECMASCRIPT_CALLABLE_TYPE: + CompareOrCheckAnyTypeCallable(codegen, enc, dst, src); + return true; case AnyBaseType::ECMASCRIPT_BOOLEAN_TYPE: { CompareAnyTypeGenBool(enc, dst, src); return true; @@ -294,6 +358,8 @@ bool ecmascript::CastAnyTypeValueGen(const CastAnyTypeValueInst *cati, EncodeVis case AnyBaseType::ECMASCRIPT_ARRAY_TYPE: case AnyBaseType::ECMASCRIPT_TRANSITION_HANDLER_TYPE: case AnyBaseType::ECMASCRIPT_PROTOTYPE_HANDLER_TYPE: + case AnyBaseType::ECMASCRIPT_FUNCTION_TYPE: + case AnyBaseType::ECMASCRIPT_CALLABLE_TYPE: case AnyBaseType::ECMASCRIPT_SPECIAL_INDEXED_TYPE: { enc->EncodeMov(dst, src); return true; @@ -344,6 +410,8 @@ bool ecmascript::CastValueToAnyTypeGen(const CastValueToAnyTypeInst *cvai, Encod case AnyBaseType::ECMASCRIPT_ARRAY_TYPE: case AnyBaseType::ECMASCRIPT_TRANSITION_HANDLER_TYPE: case AnyBaseType::ECMASCRIPT_PROTOTYPE_HANDLER_TYPE: + case AnyBaseType::ECMASCRIPT_FUNCTION_TYPE: + case AnyBaseType::ECMASCRIPT_CALLABLE_TYPE: case AnyBaseType::ECMASCRIPT_SPECIAL_INDEXED_TYPE: // With current boxing scheme, we must guarantee that boxed objects have upper 16 bits set to 0. // Boxed values are no less than 64 bits wide (to hold double-precision floating point numbers). @@ -489,6 +557,9 @@ bool ecmascript::AnyTypeCheckGen(AnyTypeCheckInst *check_inst, EncodeVisitor *en case AnyBaseType::ECMASCRIPT_FUNCTION_TYPE: CheckAnyTypeGenObjectType(codegen, enc, src, cross_values::GetJstypeJsFunction(codegen->GetArch()), id); return true; + case AnyBaseType::ECMASCRIPT_CALLABLE_TYPE: + CompareOrCheckAnyTypeCallable(codegen, enc, INVALID_REGISTER, src, id); + return true; default: return false; } diff --git a/compiler/optimizer/code_generator/compiler_base_types.h b/compiler/optimizer/code_generator/compiler_base_types.h index 114ac3c2e..4c79d9d43 100644 --- a/compiler/optimizer/code_generator/compiler_base_types.h +++ b/compiler/optimizer/code_generator/compiler_base_types.h @@ -24,6 +24,7 @@ class CompareAnyTypeInst; class CastAnyTypeValueInst; class CastValueToAnyTypeInst; class AnyTypeCheckInst; +class GetAnyTypeNameInst; class FixedInputsInst2; class EncodeVisitor; @@ -38,6 +39,7 @@ bool DynamicCallCheckGen(FixedInputsInst2 *check_inst, EncodeVisitor *enc_v); bool LoadConstantPoolGen(const Inst *inst, EncodeVisitor *enc_v); bool LoadLexicalEnvGen(const Inst *inst, EncodeVisitor *enc_v); bool LoadStringDynamicGen(Inst *inst, EncodeVisitor *enc_v); +bool GetAnyTypeNameGen(const GetAnyTypeNameInst *inst, EncodeVisitor *enc_v); } // namespace ecmascript } // namespace panda::compiler diff --git a/compiler/optimizer/ir/dyn_datatype.h b/compiler/optimizer/ir/dyn_datatype.h index 498e730a8..292d31498 100644 --- a/compiler/optimizer/ir/dyn_datatype.h +++ b/compiler/optimizer/ir/dyn_datatype.h @@ -74,6 +74,8 @@ static inline std::optional IsAnyTypeCanBeSubtypeOf(panda::compiler::AnyBa case panda::compiler::AnyBaseType::ECMASCRIPT_TRANSITION_HANDLER_TYPE: case panda::compiler::AnyBaseType::ECMASCRIPT_PROTOTYPE_HANDLER_TYPE: case panda::compiler::AnyBaseType::ECMASCRIPT_SPECIAL_INDEXED_TYPE: + case panda::compiler::AnyBaseType::ECMASCRIPT_FUNCTION_TYPE: + case panda::compiler::AnyBaseType::ECMASCRIPT_CALLABLE_TYPE: return true; default: break; @@ -88,6 +90,8 @@ static inline std::optional IsAnyTypeCanBeSubtypeOf(panda::compiler::AnyBa case panda::compiler::AnyBaseType::ECMASCRIPT_TRANSITION_HANDLER_TYPE: case panda::compiler::AnyBaseType::ECMASCRIPT_PROTOTYPE_HANDLER_TYPE: case panda::compiler::AnyBaseType::ECMASCRIPT_SPECIAL_INDEXED_TYPE: + case panda::compiler::AnyBaseType::ECMASCRIPT_FUNCTION_TYPE: + case panda::compiler::AnyBaseType::ECMASCRIPT_CALLABLE_TYPE: return true; default: break; @@ -122,6 +126,18 @@ static inline std::optional IsAnyTypeCanBeSubtypeOf(panda::compiler::AnyBa break; } break; + case panda::compiler::AnyBaseType::ECMASCRIPT_FUNCTION_TYPE: + case panda::compiler::AnyBaseType::ECMASCRIPT_CALLABLE_TYPE: + switch (type) { + case panda::compiler::AnyBaseType::ECMASCRIPT_OBJECT_TYPE: + case panda::compiler::AnyBaseType::ECMASCRIPT_HEAP_OBJECT_TYPE: + case panda::compiler::AnyBaseType::ECMASCRIPT_CALLABLE_TYPE: + case panda::compiler::AnyBaseType::ECMASCRIPT_FUNCTION_TYPE: + return std::nullopt; + default: + break; + } + break; default: break; } diff --git a/ecmascript_plugin_options.yaml b/ecmascript_plugin_options.yaml index 104f39aef..63d3aac11 100644 --- a/ecmascript_plugin_options.yaml +++ b/ecmascript_plugin_options.yaml @@ -30,6 +30,8 @@ main_header_path: plugins/ecmascript/compiler/extensions.h header_path_implementation_codegen: plugins/ecmascript/compiler/ecmascript_extensions/ecmascript_codegen_extensions.h header_path_compiler_interface_extension: plugins/ecmascript/compiler/ecmascript_extensions/ecmascript_compiler_interface.h + header_path_irtoc_interface_extension: plugins/ecmascript/compiler/ecmascript_extensions/ecmascript_irtoc_interface.h + header_path_irtoc_interface_extension_includes: plugins/ecmascript/compiler/ecmascript_extensions/ecmascript_irtoc_interface_includes.h header_path_compiler_inst_builder_extension: plugins/ecmascript/compiler/optimizer/ir_builder/ecmascript_inst_builder.h function_codegen_prologue: GenerateEcmascriptEnvInPrologue function_codegen_epilogue: GenerateEcmascriptEnvInEpilogue @@ -38,6 +40,7 @@ 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 + func_get_name_implementation_codegen: panda::compiler::ecmascript::GetAnyTypeNameGen 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 @@ -65,6 +68,7 @@ SPECIAL_INDEXED_TYPE: panda::compiler::DataType::Type::REFERENCE BOOLEAN_TYPE: panda::compiler::DataType::Type::BOOL FUNCTION_TYPE: panda::compiler::DataType::Type::REFERENCE + CALLABLE_TYPE: panda::compiler::DataType::Type::REFERENCE BIGINT_TYPE: panda::compiler::DataType::Type::REFERENCE Intrinsics: intrinsic_inline_inl: plugins/ecmascript/compiler/intrinsics_inline_ecmascript.inl diff --git a/irtoc_scripts/common.irt b/irtoc_scripts/common.irt index 9c02ac2ea..9d5357ee5 100644 --- a/irtoc_scripts/common.irt +++ b/irtoc_scripts/common.irt @@ -215,7 +215,58 @@ scoped_macro(:map_ic_slot) do |this_func, ic_slot| mapping := Load(method_ptr, "JSMETHOD_IC_MAPPING_OFFSET").ptr Load(mapping, ic_slot).u8 end +macro(:cmpanyi32) do |arg| + CompareAnyType(arg).AnyType("AnyBaseType::ECMASCRIPT_INT_TYPE").b +end + +macro(:cmpanyf64) do |arg| + CompareAnyType(arg).AnyType("AnyBaseType::ECMASCRIPT_DOUBLE_TYPE").b +end + +macro(:cmpanyheapobj) do |arg| + CompareAnyType(arg).AnyType("AnyBaseType::ECMASCRIPT_HEAP_OBJECT_TYPE").b +end + +macro(:cmpanyarr) do |arg| + CompareAnyType(arg).AnyType("AnyBaseType::ECMASCRIPT_ARRAY_TYPE").b +end + +macro(:cmpanyhole) do |arg| + CompareAnyType(arg).AnyType("AnyBaseType::ECMASCRIPT_HOLE_TYPE").b +end + +macro(:cmpanyundefined) do |arg| + CompareAnyType(arg).AnyType("AnyBaseType::ECMASCRIPT_UNDEFINED_TYPE").b +end + +macro(:cmpanystring) do |arg| + CompareAnyType(arg).AnyType("AnyBaseType::ECMASCRIPT_STRING_TYPE").b +end + +macro(:cmpanyspecialindexedobj) do |arg| + CompareAnyType(arg).AnyType("AnyBaseType::ECMASCRIPT_SPECIAL_INDEXED_TYPE").b +end macro(:cmpanysymbol) do |arg| CompareAnyType(arg).AnyType("AnyBaseType::ECMASCRIPT_SYMBOL_TYPE").b +end + +macro(:cmpanynull) do |arg| + CompareAnyType(arg).AnyType("AnyBaseType::ECMASCRIPT_NULL_TYPE").b +end + +macro(:cmpanybool) do |arg| + CompareAnyType(arg).AnyType("AnyBaseType::ECMASCRIPT_BOOLEAN_TYPE").b +end + +macro(:cmpanybigint) do |arg| + CompareAnyType(arg).AnyType("AnyBaseType::ECMASCRIPT_BIGINT_TYPE").b +end + +macro(:cmpanyfunction) do |arg| + CompareAnyType(arg).AnyType("AnyBaseType::ECMASCRIPT_FUNCTION_TYPE").b +end + +macro(:cmpanycallable) do |arg| + CompareAnyType(arg).AnyType("AnyBaseType::ECMASCRIPT_CALLABLE_TYPE").b end \ No newline at end of file diff --git a/irtoc_scripts/interpreter_handlers.irt b/irtoc_scripts/interpreter_handlers.irt index 7653d023b..053519dd6 100644 --- a/irtoc_scripts/interpreter_handlers.irt +++ b/irtoc_scripts/interpreter_handlers.irt @@ -1875,3 +1875,71 @@ function(:FastPathStGlobalVar, end ###################################################### +# TYPE_OF +# +macro(:handle_ecma_typeof) do |value| + IfImm(cmpanyf64(value).b).Imm(0).NE.b { + double := GetAnyTypeName().AnyType("AnyBaseType::ECMASCRIPT_DOUBLE_TYPE").any + Goto(:Exit) + } + IfImm(cmpanyi32(value).b).Imm(0).NE.b { + int := GetAnyTypeName().AnyType("AnyBaseType::ECMASCRIPT_INT_TYPE").any + Goto(:Exit) + } + IfImm(cmpanyundefined(value)).Imm(0).CC(:CC_NE).b { + undefined := GetAnyTypeName().AnyType("AnyBaseType::ECMASCRIPT_UNDEFINED_TYPE").any + Goto(:Exit) + } + IfImm(cmpanybool(value)).Imm(0).CC(:CC_NE).b { + boolean := GetAnyTypeName().AnyType("AnyBaseType::ECMASCRIPT_BOOLEAN_TYPE").any + Goto(:Exit) + } + IfImm(cmpanynull(value)).Imm(0).CC(:CC_NE).b { + null := GetAnyTypeName().AnyType("AnyBaseType::ECMASCRIPT_NULL_TYPE").any + Goto(:Exit) + } + IfImm(cmpanyheapobj(value)).Imm(0).CC(:CC_NE).b { + IfImm(cmpanystring(value)).Imm(0).CC(:CC_NE).b { + string := GetAnyTypeName().AnyType("AnyBaseType::ECMASCRIPT_STRING_TYPE").any + Goto(:Exit) + } + IfImm(cmpanysymbol(value)).Imm(0).CC(:CC_NE).b { + symbol := GetAnyTypeName().AnyType("AnyBaseType::ECMASCRIPT_SYMBOL_TYPE").any + Goto(:Exit) + } + IfImm(cmpanybigint(value)).Imm(0).CC(:CC_NE).b { + big_int := GetAnyTypeName().AnyType("AnyBaseType::ECMASCRIPT_BIGINT_TYPE").any + Goto(:Exit) + } + IfImm(cmpanycallable(value)).Imm(0).CC(:CC_NE).b { + function := GetAnyTypeName().AnyType("AnyBaseType::ECMASCRIPT_CALLABLE_TYPE").any + Goto(:Exit) + } + object := GetAnyTypeName().AnyType("AnyBaseType::ECMASCRIPT_HEAP_OBJECT_TYPE").any + Goto(:Exit) + } +undefined_string := GetAnyTypeName().AnyType("AnyBaseType::ECMASCRIPT_UNDEFINED_TYPE").any +Label(:Exit) + Phi(double, int, undefined, boolean, null, string, symbol, big_int, function, object, undefined_string).any +end + +function(:EcmaTypeofdyn, params: { 'value'=>'any'}, mode: [:Interpreter, :DynamicMethod, :DynamicStub], lang: 'ECMASCRIPT', regmap: $panda_regmap, enable_builder: true) do + res := handle_ecma_typeof(value) + Return(res).any +end + +function(:FastTypeOfDyn, + params: {value: 'any'}, + mode: [:FastPath, :DynamicMethod, :DynamicStub], + regmap: $full_regmap, + lang: 'ECMASCRIPT', + regalloc_set: RegMask.new($full_regmap, :arg0, :arg1) + $panda_callees_mask - $panda_regmap) do + # Arm32 is not supported + if Options.arch == :arm32 + Intrinsic(:UNREACHABLE).Terminator.void + next + end + + res := handle_ecma_typeof(value) + Return(res).any +end \ No newline at end of file diff --git a/isa/isa.yaml b/isa/isa.yaml index 86d9712f5..69e7a9443 100644 --- a/isa/isa.yaml +++ b/isa/isa.yaml @@ -55,6 +55,9 @@ profiles: - name: UnaryArith size: 2 properties: [operand_types_1] + - name: TypeOf + size: 2 + properties: [operand_types_1] - name: ObjByIndex size: 1 properties: [] @@ -158,8 +161,10 @@ groups: - sig: ecma.typeofdyn acc: inout:top prefix: ecma - format: [pref_op_none] + format: [pref_op_none_prof_16] + properties: [inlinable] intrinsic_name: INTRINSIC_TYPEOF_DYN + profile: TypeOf - sig: ecma.ldlexenvdyn acc: out:top diff --git a/runtime/asm_defines/asm_defines.def b/runtime/asm_defines/asm_defines.def index 72588841c..18eca2de3 100644 --- a/runtime/asm_defines/asm_defines.def +++ b/runtime/asm_defines/asm_defines.def @@ -96,6 +96,7 @@ 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(HCLASS_IS_BUILTINS_CTOR_MASK, HClass::IS_BUILTINS_CTOR) +DEFINE_VALUE(JSTHREAD_GLOBAL_CONSTANTS_OFFSET, panda::ecmascript::JSThread::GetGlobalConstantsOffset()) 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) @@ -131,4 +132,5 @@ DEFINE_VALUE(FUNCTION_KIND_DERIVED_CTOR, panda::ecmascript::FunctionKind::DERIVE DEFINE_VALUE(TAGGED_TYPE_SIZE, panda::ecmascript::JSTaggedValue::TaggedTypeSize()) DEFINE_VALUE(ECMA_VM_GLOBAL_ENV_OFFSET, panda::ecmascript::EcmaVM::GetGlobalEnvOffset()) DEFINE_VALUE(GLOBAL_ENV_HEADER_SIZE, panda::ecmascript::GlobalEnv::HEADER_SIZE) -DEFINE_VALUE(GLOBAL_ENV_EMPTY_ARRAY_INDEX, panda::ecmascript::GlobalEnv::EMPTY_ARRAY_OBJECT_INDEX) \ No newline at end of file +DEFINE_VALUE(GLOBAL_ENV_EMPTY_ARRAY_INDEX, panda::ecmascript::GlobalEnv::EMPTY_ARRAY_OBJECT_INDEX) +DEFINE_VALUE(GLOBAL_CONST_CONSTANTS_OFFSET, panda::ecmascript::GlobalEnvConstants::GetConstantsOffset()) \ No newline at end of file diff --git a/runtime/compiler/ecmascript_runtime_interface.cpp b/runtime/compiler/ecmascript_runtime_interface.cpp index cfa8189fa..bae954248 100644 --- a/runtime/compiler/ecmascript_runtime_interface.cpp +++ b/runtime/compiler/ecmascript_runtime_interface.cpp @@ -165,6 +165,37 @@ Expected EcmaRuntimeInterface::AddProfile(std::string_view f return true; } +inline compiler::AnyBaseType GetTypeOfType(panda::ecmascript::ProfilingTypeOfBits::Type type, bool *is_integer_seen, + bool *is_type_profiled) +{ + if (type == panda::ecmascript::ProfilingTypeOfBits::NONE) { + return compiler::AnyBaseType::UNDEFINED_TYPE; + } + + *is_type_profiled = true; + switch (type) { + case panda::ecmascript::ProfilingTypeOfBits::NUMBER: { + *is_integer_seen = true; + return compiler::AnyBaseType::ECMASCRIPT_DOUBLE_TYPE; + } + case panda::ecmascript::ProfilingTypeOfBits::SYMBOL: + return compiler::AnyBaseType::ECMASCRIPT_SYMBOL_TYPE; + case panda::ecmascript::ProfilingTypeOfBits::BOOLEAN: + return compiler::AnyBaseType::ECMASCRIPT_BOOLEAN_TYPE; + case panda::ecmascript::ProfilingTypeOfBits::STRING: + return compiler::AnyBaseType::ECMASCRIPT_STRING_TYPE; + case panda::ecmascript::ProfilingTypeOfBits::FUNCTION: + return compiler::AnyBaseType::ECMASCRIPT_CALLABLE_TYPE; + case panda::ecmascript::ProfilingTypeOfBits::UNDEFINED: + return compiler::AnyBaseType::ECMASCRIPT_UNDEFINED_TYPE; + case panda::ecmascript::ProfilingTypeOfBits::BIGINT: + return compiler::AnyBaseType::ECMASCRIPT_BIGINT_TYPE; + default: + return compiler::AnyBaseType::UNDEFINED_TYPE; + } + return compiler::AnyBaseType::UNDEFINED_TYPE; +} + compiler::AnyBaseType EcmaRuntimeInterface::GetProfilingAnyType(RuntimeInterface::BytecodeProfile profile, const BytecodeInstruction *bc_inst, unsigned index, bool *is_integer_seen, bool *is_type_profiled) @@ -173,6 +204,10 @@ compiler::AnyBaseType EcmaRuntimeInterface::GetProfilingAnyType(RuntimeInterface auto ecma_prof = reinterpret_cast(profile); panda::ecmascript::ProfilingTypeBits::Type type = panda::ecmascript::ProfilingTypeBits::NONE; switch (kind) { + case profiling::ProfilingKind::TYPE_OF: { + panda::ecmascript::TypeOfOperationProfile p(ecma_prof); + return GetTypeOfType(p.GetOperandType(index).GetType(), is_integer_seen, is_type_profiled); + } case profiling::ProfilingKind::UNARY_ARITH: { panda::ecmascript::UnaryOperationProfile p(ecma_prof); type = p.GetOperandType(index).GetType(); @@ -193,7 +228,6 @@ compiler::AnyBaseType EcmaRuntimeInterface::GetProfilingAnyType(RuntimeInterface default: LOG(FATAL, COMMON) << "Unknown profile"; } - if (type == panda::ecmascript::ProfilingTypeBits::NONE) { return compiler::AnyBaseType::UNDEFINED_TYPE; } @@ -244,6 +278,46 @@ compiler::AnyBaseType EcmaRuntimeInterface::ResolveSpecialAnyTypeByConstant(core return compiler::AnyBaseType::UNDEFINED_TYPE; } +size_t EcmaRuntimeInterface::GetGlobalConstStringOffsetForAnyType(compiler::AnyBaseType type, Arch arch) const +{ + auto global_const_array_offset = + cross_values::GetJsthreadGlobalConstantsOffset(arch) + cross_values::GetGlobalConstConstantsOffset(arch); + switch (type) { + case compiler::AnyBaseType::ECMASCRIPT_UNDEFINED_TYPE: + return global_const_array_offset + panda::ecmascript::GlobalEnvConstants::GetGlobalConstantOffset( + panda::ecmascript::ConstantIndex::UNDEFINED_STRING_INDEX); + case compiler::AnyBaseType::ECMASCRIPT_STRING_TYPE: + return global_const_array_offset + panda::ecmascript::GlobalEnvConstants::GetGlobalConstantOffset( + panda::ecmascript::ConstantIndex::STRING_STRING_INDEX); + case compiler::AnyBaseType::ECMASCRIPT_INT_TYPE: + return global_const_array_offset + panda::ecmascript::GlobalEnvConstants::GetGlobalConstantOffset( + panda::ecmascript::ConstantIndex::NUMBER_STRING_INDEX); + case compiler::AnyBaseType::ECMASCRIPT_DOUBLE_TYPE: + return global_const_array_offset + panda::ecmascript::GlobalEnvConstants::GetGlobalConstantOffset( + panda::ecmascript::ConstantIndex::NUMBER_STRING_INDEX); + case compiler::AnyBaseType::ECMASCRIPT_SYMBOL_TYPE: + return global_const_array_offset + panda::ecmascript::GlobalEnvConstants::GetGlobalConstantOffset( + panda::ecmascript::ConstantIndex::SYMBOL_STRING_INDEX); + case compiler::AnyBaseType::ECMASCRIPT_CALLABLE_TYPE: + return global_const_array_offset + panda::ecmascript::GlobalEnvConstants::GetGlobalConstantOffset( + panda::ecmascript::ConstantIndex::FUNCTION_STRING_INDEX); + case compiler::AnyBaseType::ECMASCRIPT_BOOLEAN_TYPE: + return global_const_array_offset + panda::ecmascript::GlobalEnvConstants::GetGlobalConstantOffset( + panda::ecmascript::ConstantIndex::BOOLEAN_STRING_INDEX); + case compiler::AnyBaseType::ECMASCRIPT_NULL_TYPE: + return global_const_array_offset + panda::ecmascript::GlobalEnvConstants::GetGlobalConstantOffset( + panda::ecmascript::ConstantIndex::OBJECT_STRING_INDEX); + case compiler::AnyBaseType::ECMASCRIPT_BIGINT_TYPE: + return global_const_array_offset + panda::ecmascript::GlobalEnvConstants::GetGlobalConstantOffset( + panda::ecmascript::ConstantIndex::BIGINT_STRING_INDEX); + case compiler::AnyBaseType::ECMASCRIPT_HEAP_OBJECT_TYPE: + return global_const_array_offset + panda::ecmascript::GlobalEnvConstants::GetGlobalConstantOffset( + panda::ecmascript::ConstantIndex::OBJECT_STRING_INDEX); + default: + UNREACHABLE(); + } + return 0; +} RuntimeInterface::NewObjDynInfo EcmaRuntimeInterface::GetNewObjDynInfo(uintptr_t ctor) const { static constexpr NewObjDynInfo SLOW_PATH = {NewObjDynInfo::AllocatorType::SLOW_PATH, diff --git a/runtime/compiler/ecmascript_runtime_interface.h b/runtime/compiler/ecmascript_runtime_interface.h index f81fbbeb5..4c04ed62a 100644 --- a/runtime/compiler/ecmascript_runtime_interface.h +++ b/runtime/compiler/ecmascript_runtime_interface.h @@ -146,6 +146,7 @@ public: bool *is_type_profiled) override; compiler::AnyBaseType ResolveSpecialAnyTypeByConstant(coretypes::TaggedValue any_const) override; + size_t GetGlobalConstStringOffsetForAnyType(compiler::AnyBaseType type, Arch arch) const override; NewObjDynInfo GetNewObjDynInfo(uintptr_t ctor) const override; bool GetProfileDataForNamedAccess(PandaRuntimeInterface::MethodPtr m, uintptr_t slot_id, @@ -166,6 +167,11 @@ public: CleanObjectHandles(MethodCast(method)); } + uint32_t GetCallableMask() const override + { + return HClass::GetCallableMask(); + } + private: void CleanObjectHandles(Method *method); void AddObjectHandle(Method *method, ObjectHeader *obj); diff --git a/runtime/ecma_profiling.h b/runtime/ecma_profiling.h index ee8c095aa..617b4a510 100644 --- a/runtime/ecma_profiling.h +++ b/runtime/ecma_profiling.h @@ -45,11 +45,11 @@ public: stream << '['; const char *sep = ""; if (type & INTEGER) { - stream << sep << "Integer or objct array access"; + stream << sep << "Integer"; sep = "|"; } if (type & DOUBLE) { - stream << sep << "Double or not object"; + stream << sep << "Double"; sep = "|"; } if (type & BOOLEAN) { @@ -68,6 +68,65 @@ public: } }; +class ProfilingTypeOfBits { +public: + enum Type : uint8_t { + NONE = 0, + NUMBER = (1U << 0U), + SYMBOL = (1U << 1U), + BOOLEAN = (1U << 2U), + STRING = (1U << 3U), + FUNCTION = (1U << 4U), + UNDEFINED = (1U << 5U), + BIGINT = (1U << 6U), + OBJECT = (1U << 7U), + LAST = OBJECT, + }; + template + static void Dump(S &stream, Type type) + { + if (type == NONE) { + stream << "[None]"; + return; + } + stream << '['; + const char *sep = ""; + if (type & NUMBER) { + stream << sep << "Number"; + sep = "|"; + } + if (type & SYMBOL) { + stream << sep << "Symbol"; + sep = "|"; + } + if (type & BOOLEAN) { + stream << sep << "Bool"; + sep = "|"; + } + if (type & STRING) { + stream << sep << "String"; + sep = "|"; + } + if (type & OBJECT) { + stream << sep << "Object"; + sep = "|"; + } + if (type & FUNCTION) { + stream << sep << "Function"; + sep = "|"; + } + if (type & UNDEFINED) { + stream << sep << "Undefined"; + sep = "|"; + } + if (type & BIGINT) { + stream << sep << "BigInt"; + sep = "|"; + } + stream << ']'; + } +}; + class ProfilingIndexedAccessBits { public: enum Type : uint8_t { @@ -150,6 +209,7 @@ private: }; using ProfilingType = ProfileInfo; +using ProfilingTypeOf = ProfileInfo; using ProfilingIndexedAccess = ProfileInfo; inline ProfilingTypeBits::Type GetTypeFromValue(JSTaggedValue value) @@ -164,9 +224,41 @@ inline ProfilingTypeBits::Type GetTypeFromValue(JSTaggedValue value) if (value.IsBoolean()) { return ProfilingTypeBits::BOOLEAN; } + if (value.IsString()) { + return ProfilingTypeBits::STRING; + } return ProfilingTypeBits::OBJECT; } +inline ProfilingTypeOfBits::Type GetTypeOfFromValue(JSTaggedValue value) +{ + if (value.IsInt()) { + return ProfilingTypeOfBits::NUMBER; + } + // IsDouble() is encoded as `!IsInt() && !IsObject()`, IsInt() we already tested, so check only it is not an object + if (!value.IsObject()) { + return ProfilingTypeOfBits::NUMBER; + } + if (value.IsBoolean()) { + return ProfilingTypeOfBits::BOOLEAN; + } + if (value.IsString()) { + return ProfilingTypeOfBits::STRING; + } + if (value.IsBigInt()) { + return ProfilingTypeOfBits::BIGINT; + } + if (value.IsCallable()) { + return ProfilingTypeOfBits::FUNCTION; + } + if (value.IsSymbol()) { + return ProfilingTypeOfBits::SYMBOL; + } + if (value.IsUndefined()) { + return ProfilingTypeOfBits::UNDEFINED; + } + return ProfilingTypeOfBits::OBJECT; +} ProfilingIndexedAccessBits::Type GetObjectTypeFromValue(JSTaggedValue value); // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) @@ -261,6 +353,33 @@ protected: SET_LAST_FIELD_FINAL(OperandType); }; +class TypeOfOperationProfile : public ResultProfile { +public: + using Base = ResultProfile; + using Base::Base; + + ProfilingTypeOf GetOperandType() const + { + return ProfilingTypeOf(GetField()); + } + + ProfilingTypeOf GetOperandType([[maybe_unused]] size_t index) const + { + CHECK_LT(index, 1U); + return ProfilingTypeOf(GetOperandType()); + } + + static void Update(Base::ValueType *data, JSTaggedValue value) + { + *data |= OperandType::Encode(GetTypeOfFromValue(value)); + } + +protected: + using OperandType = + Base::LastField::NextField; + SET_LAST_FIELD_FINAL(OperandType); +}; + class UnaryOperationProfile : public ResultProfile { public: using Base = ResultProfile; @@ -291,9 +410,9 @@ protected: SET_LAST_FIELD_FINAL(OperandType); }; -class BinaryOperationProfile : public ResultProfile { +class BinaryOperationProfile : public ValueProfileBase { public: - using Base = ResultProfile; + using Base = ValueProfileBase; using Base::Base; ProfilingType GetOperandType(size_t index) const @@ -324,8 +443,7 @@ public: } protected: - using LeftOperandType = - Base::LastField::NextField; + using LeftOperandType = BitField; using RightOperandType = LeftOperandType::NextField; SET_LAST_FIELD_FINAL(RightOperandType); diff --git a/runtime/ecma_runtime.yaml b/runtime/ecma_runtime.yaml index 8e7d4fd49..1d9a7e0c3 100644 --- a/runtime/ecma_runtime.yaml +++ b/runtime/ecma_runtime.yaml @@ -651,6 +651,7 @@ intrinsics: args: [acc] impl: panda::ecmascript::intrinsics::TypeofDyn clear_flags: [no_dce] + fast_path: FastTypeOfDyn - name: Callruntimerange space: ecmascript diff --git a/runtime/global_env_constants.h b/runtime/global_env_constants.h index df6b20572..ae7860fa7 100644 --- a/runtime/global_env_constants.h +++ b/runtime/global_env_constants.h @@ -381,6 +381,16 @@ public: GLOBAL_ENV_CONSTANT_ACCESSOR(DECL_GET) #undef DECL_GET + static constexpr uint32_t GetConstantsOffset() + { + return MEMBER_OFFSET(GlobalEnvConstants, constants_); + } + + static constexpr uint32_t GetGlobalConstantOffset(ConstantIndex index) + { + return static_cast(index) << 3U; + } + void VisitRangeSlot(const RootRangeVisitor &visitor) { visitor(ecmascript::Root::ROOT_VM, ObjectSlot(ToUintPtr(BeginSlot())), ObjectSlot(ToUintPtr(EndSlot()))); diff --git a/runtime/interpreter/ecma-interpreter-inl.h b/runtime/interpreter/ecma-interpreter-inl.h index 97522c710..4da0b2361 100644 --- a/runtime/interpreter/ecma-interpreter-inl.h +++ b/runtime/interpreter/ecma-interpreter-inl.h @@ -95,6 +95,14 @@ namespace panda::ecmascript { UNUSED_VAR(rhs); \ } +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define UPDATE_TYPE_OF_PROFILE(lhs) \ + if constexpr (IS_PROFILE_ENABLED) { \ + UpdateTypeOfProfile(lhs); \ + } else { \ + UNUSED_VAR(lhs); \ + } + // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) #define UPDATE_CALL_PROFILE(func) \ if constexpr (IS_PROFILE_ENABLED) { \ @@ -976,6 +984,7 @@ public: LOG_INST() << "typeofdyn"; uint64_t acc = GetAccAsTaggedValue().GetRawData(); + UPDATE_TYPE_OF_PROFILE(acc); INTRINSIC_CALL_SETACC(intrinsics::TypeofDyn(this->GetJSThread(), acc)); this->template MoveToNextInst(); } @@ -2871,13 +2880,29 @@ public: auto prof_data = method->GetProfilingVector(); auto prof_id = this->GetInst().GetProfileId(); ASSERT(prof_id >= 0); + ASSERT(method->GetProfileSize() > helpers::ToUnsigned(prof_id)); // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) auto prof_value = reinterpret_cast(&prof_data[prof_id]); - ASSERT(method->GetProfileSize() > helpers::ToUnsigned(prof_id)); return prof_value; } + void UpdateTypeOfProfile(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); + ASSERT(method->GetProfileSize() > helpers::ToUnsigned(prof_id)); + // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) + auto prof_value = reinterpret_cast(&prof_data[prof_id]); + TypeOfOperationProfile::Update(prof_value, JSTaggedValue(value)); + } + void UpdateUnaryArithProfile(coretypes::TaggedType value) { auto method = static_cast(this->GetFrame()->GetMethod()); @@ -2888,9 +2913,9 @@ public: auto prof_data = method->GetProfilingVector(); auto prof_id = this->GetInst().GetProfileId(); ASSERT(prof_id >= 0); + ASSERT(method->GetProfileSize() > helpers::ToUnsigned(prof_id)); // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) auto prof_value = reinterpret_cast(&prof_data[prof_id]); - ASSERT(method->GetProfileSize() > helpers::ToUnsigned(prof_id)); UnaryOperationProfile::Update(prof_value, JSTaggedValue(value)); } @@ -2906,9 +2931,9 @@ public: auto prof_data = method->GetProfilingVector(); auto prof_id = this->GetInst().GetProfileId(); ASSERT(prof_id >= 0); + ASSERT(method->GetProfileSize() > helpers::ToUnsigned(prof_id)); // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) auto prof_value = reinterpret_cast(&prof_data[prof_id]); - ASSERT(method->GetProfileSize() > helpers::ToUnsigned(prof_id)); BinaryOperationProfile::Update(prof_value, left, right); } diff --git a/tests/checked/CMakeLists.txt b/tests/checked/CMakeLists.txt index 54c018493..114f2e974 100644 --- a/tests/checked/CMakeLists.txt +++ b/tests/checked/CMakeLists.txt @@ -109,6 +109,7 @@ if (NOT PANDA_TARGET_ARM32) panda_add_checked_test_ecma(FILE ${CMAKE_CURRENT_SOURCE_DIR}/ecma_call_profile_clear.js SUPPORT_RELEASE true) panda_add_checked_test_ecma(FILE ${CMAKE_CURRENT_SOURCE_DIR}/peephole_negoverflowandzerocheck.js SUPPORT_RELEASE true) panda_add_checked_test_ecma(FILE ${CMAKE_CURRENT_SOURCE_DIR}/ldlex.js SUPPORT_RELEASE true) + panda_add_checked_test_ecma(FILE ${CMAKE_CURRENT_SOURCE_DIR}/typeof.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/typeof.js b/tests/checked/typeof.js new file mode 100644 index 000000000..e6de10509 --- /dev/null +++ b/tests/checked/typeof.js @@ -0,0 +1,140 @@ +/* + * 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 TypeOfDyn +//! RUN options: "--no-async-jit=true --compiler-hotness-threshold=10 --compiler-regex _GLOBAL::test_type.*", entry: "_GLOBAL::func_main_0" +//! METHOD "test_type_undefined" +//! PASS_AFTER "TypesResolving" +//! INST "AnyTypeCheck ECMASCRIPT_UNDEFINED_TYPE" +//! INST_NEXT "GetAnyTypeName ECMASCRIPT_UNDEFINED_TYPE" +//! METHOD "test_type_string" +//! PASS_AFTER "TypesResolving" +//! INST "AnyTypeCheck ECMASCRIPT_STRING_TYPE" +//! INST_NEXT "GetAnyTypeName ECMASCRIPT_STRING_TYPE" +//! METHOD "test_type_boolean" +//! PASS_AFTER "TypesResolving" +//! INST "AnyTypeCheck ECMASCRIPT_BOOLEAN_TYPE" +//! INST_NEXT "GetAnyTypeName ECMASCRIPT_BOOLEAN_TYPE" +//! METHOD "test_type_int" +//! PASS_AFTER "TypesResolving" +//! INST "AnyTypeCheck ECMASCRIPT_DOUBLE_TYPE i" +//! INST_NEXT "GetAnyTypeName ECMASCRIPT_DOUBLE_TYPE" +//! METHOD "test_type_double" +//! PASS_AFTER "TypesResolving" +//! INST "AnyTypeCheck ECMASCRIPT_DOUBLE_TYPE i" +//! INST_NEXT "GetAnyTypeName ECMASCRIPT_DOUBLE_TYPE" +//! METHOD "test_type_bigint" +//! PASS_AFTER "TypesResolving" +//! INST "AnyTypeCheck ECMASCRIPT_BIGINT_TYPE" +//! INST_NEXT "GetAnyTypeName ECMASCRIPT_BIGINT_TYPE" +//! METHOD "test_type_symbol" +//! PASS_AFTER "TypesResolving" +//! INST "AnyTypeCheck ECMASCRIPT_SYMBOL_TYPE" +//! INST_NEXT "GetAnyTypeName ECMASCRIPT_SYMBOL_TYPE" +//! METHOD "test_type_function" +//! PASS_AFTER "TypesResolving" +//! INST "AnyTypeCheck ECMASCRIPT_CALLABLE_TYPE" +//! INST_NEXT "GetAnyTypeName ECMASCRIPT_CALLABLE_TYPE" +//! METHOD "test_type_object" +//! PASS_AFTER "TypesResolving" +//! INST_NOT "AnyTypeCheck ECMASCRIPT_HEAP_OBJECT_TYPE" +//! INST_NOT "GetAnyTypeName" + +//! CHECKER Test check AOT TypeOfDyn +//! RUN_PAOC options: "--compiler-regex _GLOBAL::test_type.*" +//! RUN options: "--compiler-enable-jit=false", entry: "_GLOBAL::func_main_0" + +function test_type_undefined(a) +{ + return (typeof a === "undefined"); +} + +function test_type_string(a) +{ + return (typeof a === "string"); +} + +function test_type_boolean(a) +{ + return (typeof a === "boolean"); +} + +function test_type_int(a) +{ + return (typeof a === "number"); +} + +function test_type_double(a) +{ + return (typeof a === "number"); +} + +function test_type_bigint(a) +{ + return (typeof a === "bigint"); +} + +function test_type_symbol(a) +{ + return (typeof a === "symbol"); +} + +function test_type_function(a) +{ + return (typeof a === "function"); +} + +function test_type_object(a) +{ + return (typeof a === "object"); +} +function foo () {} + +for (let i = 0 ; i < 30; i++) { + if (!test_type_undefined(undefined)) { + throw "test_type_undefined is failed "; + } + if (!test_type_string("foo")) { + throw "test_type_string is failed "; + } + if (!test_type_boolean(false)) { + throw "test_type_boolean false is failed "; + } + if (!test_type_boolean(true)) { + throw "test_type_boolean true is failed "; + } + if (!test_type_int(10)) { + throw "test_type_int is failed "; + } + if (!test_type_double(1.1)) { + throw "test_type_double is failed "; + } + if (!test_type_bigint(1n)) { + throw "test_type_bigint is failed "; + } + if (!test_type_symbol(Symbol())) { + throw "test_type_symbol is failed "; + } + if (!test_type_function(foo)) { + throw "test_type_function is failed "; + } + var obj = {}; + if (!test_type_object(obj)) { + throw "test_type_object is failed "; + } + if (!test_type_object(null)) { + throw "test_type_object null is failed "; + } +} -- Gitee