diff --git a/compiler/optimizer/code_generator/compiler_base_types.cpp b/compiler/optimizer/code_generator/compiler_base_types.cpp index af1d20051ffefb13eb7833e6cec30b318e65d53d..35d02eec5023c37d42c65b97a016c205aed897e0 100644 --- a/compiler/optimizer/code_generator/compiler_base_types.cpp +++ b/compiler/optimizer/code_generator/compiler_base_types.cpp @@ -629,25 +629,6 @@ bool ecmascript::AnyTypeCheckGen(AnyTypeCheckInst *check_inst, EncodeVisitor *en return false; } -bool ecmascript::DynamicCallCheckGen(FixedInputsInst2 *check_inst, EncodeVisitor *enc_v) -{ - auto enc = enc_v->GetEncoder(); - auto codegen = enc_v->GetCodegen(); - auto arch = codegen->GetArch(); - auto src = codegen->ConvertRegister(check_inst->GetSrcReg(0), DataType::Type::ANY); - auto exit_label = - codegen->CreateSlowPath(check_inst, DeoptimizeType::ANY_TYPE_CHECK)->GetLabel(); - - ScopedTmpReg tmp(enc, codegen->ConvertDataType(DataType::UINT64, arch)); - - codegen->LoadClassFromObject(tmp, src); - enc->EncodeLdr( - tmp, false, - MemRef(tmp, -cross_values::GetJshclassHclassOffset(arch) + cross_values::GetJshclassBitfieldOffset(arch))); - enc->EncodeBitTestAndBranch(exit_label, tmp, cross_values::GetJshclassBitfieldClassConstructorStartBit(arch), true); - return true; -} - bool ecmascript::LoadConstantPoolGen(const Inst *inst, EncodeVisitor *enc_v) { auto *codegen = enc_v->GetCodegen(); @@ -679,4 +660,36 @@ bool ecmascript::LoadStringDynamicGen(Inst *inst, EncodeVisitor *enc_v) RegMask::GetZeroMask(), src, TypedImm(string_type)); return true; } + +bool ecmascript::HclassCheckGen(HclassCheckInst *check_inst, EncodeVisitor *enc_v) +{ + auto enc = enc_v->GetEncoder(); + auto codegen = enc_v->GetCodegen(); + auto arch = codegen->GetArch(); + auto src = codegen->ConvertRegister(check_inst->GetSrcReg(0), DataType::Type::REFERENCE); + auto exit_label = + codegen->CreateSlowPath(check_inst, DeoptimizeType::ANY_TYPE_CHECK)->GetLabel(); + + ScopedTmpReg tmp_reg(enc, codegen->ConvertDataType(DataType::REFERENCE, arch)); + auto bit_mask = codegen->GetRuntime()->GetHClassBitfieldTypeMask(arch); + auto type = codegen->GetRuntime()->GetJstypeJsFunction(arch); + + if (check_inst->GetCheckIsFunction()) { + enc->EncodeAnd(tmp_reg, src, Imm(bit_mask)); + enc->EncodeJump(exit_label, tmp_reg, Imm(type), Condition::NE); + } + + if (check_inst->GetCheckFunctionIsNotClassConstructor()) { + auto constructor_start_bit = codegen->GetRuntime()->GetJshclassBitfieldClassConstructorStartBit(arch); + enc->EncodeBitTestAndBranch(exit_label, src, constructor_start_bit, true); + } + + return true; +} + +bool ecmascript::IsDynHeapObject(AnyBaseType type) +{ + return type == compiler::AnyBaseType::ECMASCRIPT_HEAP_OBJECT_TYPE; +} + } // namespace panda::compiler diff --git a/compiler/optimizer/code_generator/compiler_base_types.h b/compiler/optimizer/code_generator/compiler_base_types.h index 4c79d9d43fc47cd74843b999d8c789c9e730d6a7..aaefed93527a35cf52487a8d23d39f52d928e883 100644 --- a/compiler/optimizer/code_generator/compiler_base_types.h +++ b/compiler/optimizer/code_generator/compiler_base_types.h @@ -16,6 +16,7 @@ #ifndef PLUGINS_ECMASCRIPT_COMPILER_OPTIMIZER_CODE_GENERATOR_COMPILER_BASE_TYPES_H #define PLUGINS_ECMASCRIPT_COMPILER_OPTIMIZER_CODE_GENERATOR_COMPILER_BASE_TYPES_H #include "compiler/optimizer/code_generator/encode.h" +#include "source_languages.h" namespace panda::compiler { @@ -27,6 +28,7 @@ class AnyTypeCheckInst; class GetAnyTypeNameInst; class FixedInputsInst2; class EncodeVisitor; +class HclassCheckInst; namespace ecmascript { @@ -40,6 +42,8 @@ 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); +bool HclassCheckGen(HclassCheckInst *check_inst, EncodeVisitor *enc_v); +bool IsDynHeapObject(AnyBaseType type); } // namespace ecmascript } // namespace panda::compiler diff --git a/compiler/optimizer/ir_builder/ecmascript_inst_builder.cpp b/compiler/optimizer/ir_builder/ecmascript_inst_builder.cpp index 9bef8f03a6de43a217143df92cc85b936f540b08..7839e1c0a828d9fedfbf972e4957a067ec906721 100644 --- a/compiler/optimizer/ir_builder/ecmascript_inst_builder.cpp +++ b/compiler/optimizer/ir_builder/ecmascript_inst_builder.cpp @@ -52,17 +52,34 @@ void InstBuilder::BuildEcmaFnCall(const BytecodeInstruction *bc_inst, bool is_ra } } - { // Check callee is JSFunction - auto callee_func = BuildAnyTypeCheckInst(bc_pc, callee, save_state, AnyBaseType::ECMASCRIPT_FUNCTION_TYPE); - callee = callee_func; - } - - { // Check callee is suitable to call (!IsConstructor) - auto fn_class_check = GetGraph()->CreateInstDynamicCallCheck(DataType::ANY, bc_pc); - fn_class_check->SetInput(0, callee); - fn_class_check->SetInput(1, save_state); - AddInstruction(fn_class_check); - callee = fn_class_check; + // Check callee is heap object + auto type_check = BuildAnyTypeCheckInst(bc_pc, callee, save_state, AnyBaseType::ECMASCRIPT_HEAP_OBJECT_TYPE); + callee = type_check; + + { // Load class (it is movable object in js) + auto load_class = GetGraph()->CreateInstLoadObject(DataType::REFERENCE, bc_pc); + load_class->SetMethod(GetGraph()->GetMethod()); + load_class->SetObjField(nullptr); + load_class->SetTypeId(TypeIdMixin::MEM_DYN_CLASS_ID); + load_class->SetObjectType(ObjectType::MEM_DYN_CLASS); + load_class->SetInput(0, callee); + AddInstruction(load_class); + + // Load Jshclass + auto load_hclass = GetGraph()->CreateInstLoadObject(DataType::REFERENCE, bc_pc); + load_hclass->SetMethod(GetGraph()->GetMethod()); + load_hclass->SetObjField(nullptr); + load_hclass->SetTypeId(TypeIdMixin::MEM_DYN_HCLASS_ID); + load_hclass->SetObjectType(ObjectType::MEM_DYN_HCLASS); + load_hclass->SetInput(0, load_class); + AddInstruction(load_hclass); + + auto hclass_check = GetGraph()->CreateInstHclassCheck(DataType::NO_TYPE, bc_pc); + hclass_check->SetInput(0, load_hclass); + hclass_check->SetInput(1, save_state); + hclass_check->SetCheckIsFunction(true); + hclass_check->SetCheckFunctionIsNotClassConstructor(true); + AddInstruction(hclass_check); } auto call_inst = GetGraph()->CreateInstCallDynamic(DataType::ANY, bc_pc); diff --git a/ecmascript_plugin_options.yaml b/ecmascript_plugin_options.yaml index 72cdf9c8a9747e1bed1c0dfd9672e7a690c0596d..024c5d5d296c1535f7a37dab887fd81257a1ae26 100644 --- a/ecmascript_plugin_options.yaml +++ b/ecmascript_plugin_options.yaml @@ -44,6 +44,7 @@ func_cast_implementation_codegen: panda::compiler::ecmascript::CastAnyTypeValueGen func_cast_to_any_implementation_codegen: panda::compiler::ecmascript::CastValueToAnyTypeGen func_any_type_check_implementation_codegen: panda::compiler::ecmascript::AnyTypeCheckGen + func_hclass_check_implementation_codegen: panda::compiler::ecmascript::HclassCheckGen func_dynamic_call_check_implementation_codegen: panda::compiler::ecmascript::DynamicCallCheckGen func_obj_by_index_check_implementation_codegen: panda::compiler::ecmascript::ObjByIndexCheckGen func_resolve_numeric_type: panda::compiler::ecmascript::NumericDataTypeToAnyType @@ -52,6 +53,7 @@ func_load_constant_pool: panda::compiler::ecmascript::LoadConstantPoolGen func_load_lexical_env: panda::compiler::ecmascript::LoadLexicalEnvGen func_load_string_dyn: panda::compiler::ecmascript::LoadStringDynamicGen + func_is_dyn_heap_object: panda::compiler::ecmascript::IsDynHeapObject list_types: HOLE_TYPE: panda::compiler::DataType::Type::ANY NULL_TYPE: panda::compiler::DataType::Type::ANY diff --git a/runtime/compiler/ecmascript_runtime_interface.h b/runtime/compiler/ecmascript_runtime_interface.h index 61648bb4988aafcca93f8111b00c089c7648fc92..63fa2abfdd6f53fb5e70c585d6b53907f5b22283 100644 --- a/runtime/compiler/ecmascript_runtime_interface.h +++ b/runtime/compiler/ecmascript_runtime_interface.h @@ -77,7 +77,31 @@ public: size_t GetHClassOffset(Arch arch) const override { - return panda::cross_values::GetJshclassHclassOffset(arch); + return -panda::cross_values::GetJshclassHclassOffset(arch) + + panda::cross_values::GetJshclassBitfieldOffset(arch); + } + + size_t GetHClassBitfieldTypeStartBit(Arch arch) const override + { + return panda::cross_values::GetJshclassBitfieldTypeStartBit(arch); + } + + uint64_t GetHClassBitfieldTypeMask(Arch arch) const override + { + return static_cast(panda::cross_values::GetJshclassBitfieldTypeMask(arch)) + << static_cast(panda::cross_values::GetJshclassBitfieldTypeStartBit(arch)); + } + + uint64_t GetJshclassBitfieldClassConstructorStartBit(Arch arch) const override + { + return static_cast(panda::cross_values::GetJshclassBitfieldClassConstructorStartBit(arch)) + << static_cast(panda::cross_values::GetJshclassBitfieldTypeStartBit(arch)); + } + + size_t GetJstypeJsFunction(Arch arch) const override + { + return static_cast(panda::cross_values::GetJstypeJsFunction(arch)) + << static_cast(panda::cross_values::GetJshclassBitfieldTypeStartBit(arch)); } size_t GetPrototypeHolderOffset(Arch arch) const override diff --git a/tests/checked/CMakeLists.txt b/tests/checked/CMakeLists.txt index 1adfa3e4fd300e02c35dd858eb99de73ac51ae3d..cade3c53b0422a058cc322e431addc1b013b8283 100644 --- a/tests/checked/CMakeLists.txt +++ b/tests/checked/CMakeLists.txt @@ -121,6 +121,11 @@ if (NOT PANDA_TARGET_ARM32) panda_add_checked_test_ecma(FILE ${CMAKE_CURRENT_SOURCE_DIR}/string_equals.js SUPPORT_RELEASE true) panda_add_checked_test_ecma(FILE ${CMAKE_CURRENT_SOURCE_DIR}/stlex.js SUPPORT_RELEASE true) panda_add_checked_test_ecma(FILE ${CMAKE_CURRENT_SOURCE_DIR}/poplexenv.js SUPPORT_RELEASE true) + panda_add_checked_test_ecma(FILE ${CMAKE_CURRENT_SOURCE_DIR}/hclass_check_not_elim.js SUPPORT_RELEASE true) + panda_add_checked_test_ecma(FILE ${CMAKE_CURRENT_SOURCE_DIR}/hclass_check_elim.js SUPPORT_RELEASE true) + panda_add_checked_test_ecma(FILE ${CMAKE_CURRENT_SOURCE_DIR}/hclass_check_non_callable.js SUPPORT_RELEASE true) + panda_add_checked_test_ecma(FILE ${CMAKE_CURRENT_SOURCE_DIR}/hclass_check_constructor.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/ecma_inlining_monomorphic.js b/tests/checked/ecma_inlining_monomorphic.js index e4ae0c8873381c291e19bfd45a9a32fd29cb2cc6..0afae69a5cff6488ee5fae1ee6296acc22246504 100644 --- a/tests/checked/ecma_inlining_monomorphic.js +++ b/tests/checked/ecma_inlining_monomorphic.js @@ -19,16 +19,21 @@ //! EVENT /Compilation,_GLOBAL::test,.*,COMPILED/ //! EVENT_NOT /Deoptimization,_GLOBAL::.*test.*,.*,IFRAME/ -//! CHECKER Remove DynamicCallCheck for inlined ecma method +//! CHECKER Remove HclassCheck for inlined ecma method //! RUN options: "--no-async-jit --compiler-hotness-threshold=10 --compiler-regex _GLOBAL::test", entry: "_GLOBAL::func_main_0" +//! EVENT /Inline,_GLOBAL::test,_GLOBAL::one,.*DYNAMIC_MONOMORPHIC,SUCCESS/ //! METHOD "test" //! PASS_AFTER "IrBuilder" -//! INST "DynamicCallCheck" +//! INST "AnyTypeCheck" +//! INST "HclassCheck" //! INST "CallDynamic" +//! //! PASS_AFTER "ChecksElimination" -//! INST_NOT "DynamicCallCheck" +//! INST "AnyTypeCheck" +//! INST "HclassCheck" //! INST_NOT "CallDynamic" + function one() { return 1; } diff --git a/tests/checked/hclass_check_constructor.js b/tests/checked/hclass_check_constructor.js new file mode 100644 index 0000000000000000000000000000000000000000..a1f4bb4d0627d91c09f1d4a9984aaa382946075f --- /dev/null +++ b/tests/checked/hclass_check_constructor.js @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2023 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 hclass check with constructor +//! RUN options: "--compiler-regex='_GLOBAL::main' --compiler-enable-jit=true --no-async-jit=true --compiler-hotness-threshold=0", entry: "_GLOBAL::func_main_0", result: 1 +//! EVENT /Compilation,_GLOBAL::main,.*,COMPILED/ +//! EVENT /DeoptimizationReason,_GLOBAL::func_main_0,ANY_TYPE_CHECK/ + + +class Test {} + +for (let i = 0; i < 10; i++) { + Test(); +} diff --git a/tests/checked/hclass_check_elim.js b/tests/checked/hclass_check_elim.js new file mode 100644 index 0000000000000000000000000000000000000000..d98c09359a659d2a40d5ed73379101a7039fac7f --- /dev/null +++ b/tests/checked/hclass_check_elim.js @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2023 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 HclassCheck eliminated and updated in inlined case +//! RUN options: "--compiler-regex='_GLOBAL::main' --compiler-enable-jit=true --no-async-jit=true --compiler-hotness-threshold=1", entry: "_GLOBAL::func_main_0" +//! EVENT /Compilation,_GLOBAL::main,.*,COMPILED/ +//! METHOD "main" +//! PASS_BEFORE "ChecksElimination" +//! INST_COUNT "AnyTypeCheck ECMASCRIPT_HEAP_OBJECT_TYPE", 3 +//! INST_COUNT "HclassCheck", 3 +//! INST_COUNT "HclassCheck [IsFunc, IsNotClassConstr]", 3 +//! INST_NOT "CallDynamic" +//! +//! PASS_AFTER "ChecksElimination" +//! INST_COUNT "AnyTypeCheck ECMASCRIPT_HEAP_OBJECT_TYPE", 1 +//! INST_COUNT "HclassCheck", 1 +//! INST_COUNT "HclassCheck [IsFunc]", 1 +//! INST_NOT "CallDynamic" + + +//! CHECKER Test HclassCheck eliminated in case with calls +//! RUN options: "--compiler-regex='_GLOBAL::main' --compiler-enable-jit=true --no-async-jit=true --compiler-hotness-threshold=1 --compiler-inlining=false", entry: "_GLOBAL::func_main_0" +//! EVENT /Compilation,_GLOBAL::main,.*,COMPILED/ +//! METHOD "main" +//! PASS_BEFORE "ChecksElimination" +//! INST_COUNT "LoadObject 4294967288 Class", 3 +//! INST_COUNT "LoadObject 4294967287 Hclass", 3 +//! INST_COUNT "HclassCheck", 3 +//! INST_COUNT "HclassCheck [IsFunc, IsNotClassConstr]", 3 +//! INST_COUNT "AnyTypeCheck ECMASCRIPT_HEAP_OBJECT_TYPE", 3 +//! +//! PASS_AFTER "ChecksElimination" +//! INST_COUNT "AnyTypeCheck ECMASCRIPT_HEAP_OBJECT_TYPE", 1 +//! INST_COUNT "HclassCheck", 1 +//! INST_COUNT "HclassCheck [IsFunc, IsNotClassConstr]", 1 +//! +//! PASS_AFTER_NEXT "Cleanup" +//! INST_COUNT "AnyTypeCheck ECMASCRIPT_HEAP_OBJECT_TYPE", 1 +//! INST_COUNT "LoadObject 4294967288 Class", 1 +//! INST_COUNT "LoadObject 4294967287 Hclass", 1 +//! INST_COUNT "HclassCheck", 1 +//! INST_COUNT "HclassCheck [IsFunc, IsNotClassConstr]", 1 + + +function foo() { + return 1; +} + +for (let i = 0; i < 2; i++) { + let call = foo; + call(); + call(); + call(); +} diff --git a/tests/checked/hclass_check_non_callable.js b/tests/checked/hclass_check_non_callable.js new file mode 100644 index 0000000000000000000000000000000000000000..40fa65b9b437a12797a09abb484500428b9db858 --- /dev/null +++ b/tests/checked/hclass_check_non_callable.js @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2023 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 hclass check non callable function +//! RUN options: "--compiler-regex='_GLOBAL::main' --compiler-enable-jit=true --compiler-inlining=true --no-async-jit=true --compiler-hotness-threshold=0", entry: "_GLOBAL::func_main_0", result: 1 +//! EVENT /Compilation,_GLOBAL::main,.*,COMPILED/ +//! EVENT /DeoptimizationReason,_GLOBAL::func_main_0,ANY_TYPE_CHECK/ +//! METHOD "main" +//! PASS_BEFORE "ChecksElimination" +//! INST "AnyTypeCheck ECMASCRIPT_HEAP_OBJECT_TYPE" +//! INST "HclassCheck [IsFunc, IsNotClassConstr]" +//! +//! PASS_AFTER "ChecksElimination" +//! INST "AnyTypeCheck ECMASCRIPT_HEAP_OBJECT_TYPE" +//! INST "HclassCheck [IsFunc, IsNotClassConstr]" + + +function foo() {} + +let a = foo; +for (let i = 0; i < 2; i++) { + a(); + a = 1; + // This is necessary so that at the first iteration of the loop it is known + // that the object was a function, and after that it is not +} diff --git a/tests/checked/hclass_check_not_elim.js b/tests/checked/hclass_check_not_elim.js new file mode 100644 index 0000000000000000000000000000000000000000..c7da937c0806c249a61e736d422ada694b04495c --- /dev/null +++ b/tests/checked/hclass_check_not_elim.js @@ -0,0 +1,39 @@ +/* + * Copyright (c) 2023 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 HclassCheck didn't eliminate and update in inlined case +//! RUN options: "--compiler-regex='_GLOBAL::main' --compiler-enable-jit=true --no-async-jit=true --compiler-hotness-threshold=1", entry: "_GLOBAL::func_main_0" +//! EVENT /Compilation,_GLOBAL::main,.*,COMPILED/ +//! METHOD "main" +//! PASS_BEFORE "ChecksElimination" +//! INST_COUNT "AnyTypeCheck ECMASCRIPT_HEAP_OBJECT_TYPE", 3 +//! INST_COUNT "HclassCheck", 3 +//! INST_COUNT "HclassCheck [IsFunc, IsNotClassConstr]", 3 +//! +//! PASS_AFTER "ChecksElimination" +//! INST_COUNT "AnyTypeCheck ECMASCRIPT_HEAP_OBJECT_TYPE", 3 +//! INST_COUNT "HclassCheck", 3 +//! INST_COUNT "HclassCheck [IsFunc]", 3 + + +function foo() { + return 1; +} + +for (let i = 0; i < 2; i++) { + foo(); + foo(); + foo(); +} diff --git a/tests/compiler/checks_elimination_ecma_test.cpp b/tests/compiler/checks_elimination_ecma_test.cpp index 6ed25ef581c97959a87c5290f28b036825f8df77..07023d4632e6e8451c9dca20bb182e0667f49735 100644 --- a/tests/compiler/checks_elimination_ecma_test.cpp +++ b/tests/compiler/checks_elimination_ecma_test.cpp @@ -642,7 +642,8 @@ TEST_F(CheckEliminationEcmaTest, EliminateAnyTypeCheckWithUndefinedType) EXPECT_TRUE(GraphComparator().Compare(graph, graph_opt)); } -TEST_F(CheckEliminationEcmaTest, EliminateDuplicateDynamicCallCheck) +// TODO(kaskov) Not shue that we can eliminate duplicate LoadObject and HclassCheck +TEST_F(CheckEliminationEcmaTest, EliminateDuplicateHclassCheck) { auto graph = CreateGraphDynWithDefaultRuntime(); GRAPH(graph) @@ -655,13 +656,33 @@ TEST_F(CheckEliminationEcmaTest, EliminateDuplicateDynamicCallCheck) { INST(2, Opcode::SaveState).Inputs(0, 1).SrcVregs({0, 1}); - INST(3, Opcode::AnyTypeCheck).any().AnyType(AnyBaseType::ECMASCRIPT_FUNCTION_TYPE).Inputs(0, 2); - INST(4, Opcode::DynamicCallCheck).any().Inputs(3, 2); - INST(5, Opcode::CallDynamic).any().InputsAutoType(4, 10, 10, 1, 2); - - INST(6, Opcode::AnyTypeCheck).any().AnyType(AnyBaseType::ECMASCRIPT_FUNCTION_TYPE).Inputs(0, 2); - INST(7, Opcode::DynamicCallCheck).any().Inputs(6, 2); - INST(8, Opcode::CallDynamic).any().InputsAutoType(7, 10, 10, 5, 2); + INST(11, Opcode::AnyTypeCheck).any().AnyType(AnyBaseType::ECMASCRIPT_HEAP_OBJECT_TYPE).Inputs(0, 2); + INST(12, Opcode::LoadObject) + .ref() + .TypeId(TypeIdMixin::MEM_DYN_CLASS_ID) + .ObjectType(ObjectType::MEM_DYN_CLASS) + .Inputs(11); + INST(13, Opcode::LoadObject) + .ref() + .TypeId(TypeIdMixin::MEM_DYN_HCLASS_ID) + .ObjectType(ObjectType::MEM_DYN_HCLASS) + .Inputs(12); + INST(14, Opcode::HclassCheck).any().Inputs(13, 2).SetChecks(HclassChecks::ALL_CHECKS); + INST(5, Opcode::CallDynamic).any().InputsAutoType(11, 10, 10, 1, 2); + + INST(15, Opcode::AnyTypeCheck).any().AnyType(AnyBaseType::ECMASCRIPT_HEAP_OBJECT_TYPE).Inputs(0, 2); + INST(16, Opcode::LoadObject) + .ref() + .TypeId(TypeIdMixin::MEM_DYN_CLASS_ID) + .ObjectType(ObjectType::MEM_DYN_CLASS) + .Inputs(15); + INST(17, Opcode::LoadObject) + .ref() + .TypeId(TypeIdMixin::MEM_DYN_HCLASS_ID) + .ObjectType(ObjectType::MEM_DYN_HCLASS) + .Inputs(16); + INST(18, Opcode::HclassCheck).any().Inputs(17, 2).SetChecks(HclassChecks::ALL_CHECKS); + INST(8, Opcode::CallDynamic).any().InputsAutoType(15, 10, 10, 1, 2); INST(9, Opcode::Return).any().Inputs(8); } @@ -683,11 +704,21 @@ TEST_F(CheckEliminationEcmaTest, EliminateDuplicateDynamicCallCheck) { INST(2, Opcode::SaveState).Inputs(0, 1).SrcVregs({0, 1}); - INST(3, Opcode::AnyTypeCheck).any().AnyType(AnyBaseType::ECMASCRIPT_FUNCTION_TYPE).Inputs(0, 2); - INST(4, Opcode::DynamicCallCheck).any().Inputs(3, 2); - INST(5, Opcode::CallDynamic).any().InputsAutoType(4, 10, 10, 1, 2); - - INST(8, Opcode::CallDynamic).any().InputsAutoType(4, 10, 10, 5, 2); + INST(11, Opcode::AnyTypeCheck).any().AnyType(AnyBaseType::ECMASCRIPT_HEAP_OBJECT_TYPE).Inputs(0, 2); + INST(12, Opcode::LoadObject) + .ref() + .TypeId(TypeIdMixin::MEM_DYN_CLASS_ID) + .ObjectType(ObjectType::MEM_DYN_CLASS) + .Inputs(11); + INST(13, Opcode::LoadObject) + .ref() + .TypeId(TypeIdMixin::MEM_DYN_HCLASS_ID) + .ObjectType(ObjectType::MEM_DYN_HCLASS) + .Inputs(12); + INST(14, Opcode::HclassCheck).any().Inputs(13, 2).SetChecks(HclassChecks::ALL_CHECKS); + + INST(5, Opcode::CallDynamic).any().InputsAutoType(11, 10, 10, 1, 2); + INST(8, Opcode::CallDynamic).any().InputsAutoType(11, 10, 10, 1, 2); INST(9, Opcode::Return).any().Inputs(8); } @@ -696,8 +727,9 @@ TEST_F(CheckEliminationEcmaTest, EliminateDuplicateDynamicCallCheck) EXPECT_TRUE(GraphComparator().Compare(graph, graph_opt)); } -TEST_F(CheckEliminationEcmaTest, EliminateDynamicCallCheckInlined) +TEST_F(CheckEliminationEcmaTest, EliminateHclassCheckInlined) { + auto method = reinterpret_cast(1); auto graph = CreateGraphDynWithDefaultRuntime(); GRAPH(graph) { @@ -709,20 +741,40 @@ TEST_F(CheckEliminationEcmaTest, EliminateDynamicCallCheckInlined) { INST(2, Opcode::SaveState).Inputs(0, 1).SrcVregs({0, 1}); - INST(3, Opcode::AnyTypeCheck).any().AnyType(AnyBaseType::ECMASCRIPT_FUNCTION_TYPE).Inputs(0, 2); - INST(4, Opcode::DynamicCallCheck).any().Inputs(3, 2); - INST(5, Opcode::CallDynamic).any().Inlined().InputsAutoType(4, 10, 10, 1, 2); + INST(3, Opcode::AnyTypeCheck).any().AnyType(AnyBaseType::ECMASCRIPT_HEAP_OBJECT_TYPE).Inputs(0, 2); + INST(4, Opcode::LoadObject) + .ref() + .TypeId(TypeIdMixin::MEM_DYN_CLASS_ID) + .ObjectType(ObjectType::MEM_DYN_CLASS) + .Inputs(3); + INST(11, Opcode::LoadObject) + .ref() + .TypeId(TypeIdMixin::MEM_DYN_HCLASS_ID) + .ObjectType(ObjectType::MEM_DYN_HCLASS) + .Inputs(4); + INST(12, Opcode::HclassCheck).any().Inputs(11, 2).SetChecks(HclassChecks::ALL_CHECKS); + INST(13, Opcode::LoadImmediate) + .ptr() + .Class(method) + .ObjectTypeLoadImm(LoadImmediateInst::ObjectType::METHOD); + INST(14, Opcode::LoadObject) + .ptr() + .TypeId(TypeIdMixin::MEM_DYN_METHOD_ID) + .ObjectType(ObjectType::MEM_DYN_METHOD) + .Inputs(3); + INST(15, Opcode::Compare).SrcType(DataType::POINTER).CC(CC_NE).b().Inputs(14, 13); + INST(16, Opcode::DeoptimizeIf).DeoptimizeType(DeoptimizeType::INLINE_DYN).Inputs(15, 2); + + INST(5, Opcode::CallDynamic).any().Inlined().InputsAutoType(3, 10, 10, 1, 2); INST(6, Opcode::SaveState).Inputs(0, 1).SrcVregs({0, 1}).Caller(5); // do something to prevent cleanup of call/return inlined INST(7, Opcode::AnyTypeCheck).any().AnyType(AnyBaseType::ECMASCRIPT_INT_TYPE).Inputs(1, 6); INST(8, Opcode::ReturnInlined).Inputs(2); - - INST(9, Opcode::Return).any().Inputs(7); + INST(9, Opcode::Return).any().Inputs(5); } } ASSERT_TRUE(graph->RunPass()); - ASSERT_TRUE(graph->RunPass()); GraphChecker(graph).Check(); auto graph_opt = CreateGraphDynWithDefaultRuntime(); @@ -736,20 +788,45 @@ TEST_F(CheckEliminationEcmaTest, EliminateDynamicCallCheckInlined) BASIC_BLOCK(2, -1) { INST(2, Opcode::SaveState).Inputs(0, 1).SrcVregs({0, 1}); - INST(3, Opcode::AnyTypeCheck).any().AnyType(AnyBaseType::ECMASCRIPT_FUNCTION_TYPE).Inputs(0, 2); + + INST(3, Opcode::AnyTypeCheck).any().AnyType(AnyBaseType::ECMASCRIPT_HEAP_OBJECT_TYPE).Inputs(0, 2); + INST(4, Opcode::LoadObject) + .ref() + .TypeId(TypeIdMixin::MEM_DYN_CLASS_ID) + .ObjectType(ObjectType::MEM_DYN_CLASS) + .Inputs(3); + INST(11, Opcode::LoadObject) + .ref() + .TypeId(TypeIdMixin::MEM_DYN_HCLASS_ID) + .ObjectType(ObjectType::MEM_DYN_HCLASS) + .Inputs(4); + INST(12, Opcode::HclassCheck).any().Inputs(11, 2).SetChecks(HclassChecks::IS_FUNCTION); + INST(13, Opcode::LoadImmediate) + .ptr() + .Class(method) + .ObjectTypeLoadImm(LoadImmediateInst::ObjectType::METHOD); + INST(14, Opcode::LoadObject) + .ptr() + .TypeId(TypeIdMixin::MEM_DYN_METHOD_ID) + .ObjectType(ObjectType::MEM_DYN_METHOD) + .Inputs(3); + INST(15, Opcode::Compare).SrcType(DataType::POINTER).CC(CC_NE).b().Inputs(14, 13); + INST(16, Opcode::DeoptimizeIf).DeoptimizeType(DeoptimizeType::INLINE_DYN).Inputs(15, 2); + INST(5, Opcode::CallDynamic).any().Inlined().InputsAutoType(3, 10, 10, 1, 2); - INST(6, Opcode::SaveState).Inputs(0, 1).SrcVregs({0, 1}); + INST(6, Opcode::SaveState).Inputs(0, 1).SrcVregs({0, 1}).Caller(5); + // do something to prevent cleanup of call/return inlined INST(7, Opcode::AnyTypeCheck).any().AnyType(AnyBaseType::ECMASCRIPT_INT_TYPE).Inputs(1, 6); INST(8, Opcode::ReturnInlined).Inputs(2); - - INST(9, Opcode::Return).any().Inputs(7); + INST(9, Opcode::Return).any().Inputs(5); } } - EXPECT_TRUE(GraphComparator().Compare(graph, graph_opt)); } -TEST_F(CheckEliminationEcmaTest, MoveDynamicCallCheckFromLoop) +// TODO(kaskov) To deal with the correctness of the HclassCheck +// Not shue that we can move LoadObject and HclassCheck from loop +TEST_F(CheckEliminationEcmaTest, MoveHclassCheckFromLoop) { auto graph = CreateGraphDynWithDefaultRuntime(); GRAPH(graph) @@ -777,9 +854,22 @@ TEST_F(CheckEliminationEcmaTest, MoveDynamicCallCheckFromLoop) { INST(8, Opcode::Phi).s32().Inputs(0, 13); INST(9, Opcode::SaveState).Inputs(0, 1, 2, 4, 5).SrcVregs({0, 1, 2, 4, 5}); - INST(10, Opcode::AnyTypeCheck).any().AnyType(AnyBaseType::ECMASCRIPT_FUNCTION_TYPE).Inputs(5, 9); - INST(11, Opcode::DynamicCallCheck).any().Inputs(10, 9); - INST(12, Opcode::CallDynamic).any().InputsAutoType(11, 2, 2, 8, 9); + + INST(10, Opcode::AnyTypeCheck).any().AnyType(AnyBaseType::ECMASCRIPT_HEAP_OBJECT_TYPE).Inputs(5, 9); + + INST(17, Opcode::LoadObject) + .ref() + .TypeId(TypeIdMixin::MEM_DYN_CLASS_ID) + .ObjectType(ObjectType::MEM_DYN_CLASS) + .Inputs(10); + INST(18, Opcode::LoadObject) + .ref() + .TypeId(TypeIdMixin::MEM_DYN_HCLASS_ID) + .ObjectType(ObjectType::MEM_DYN_HCLASS) + .Inputs(17); + INST(11, Opcode::HclassCheck).any().Inputs(18, 9).SetChecks(HclassChecks::ALL_CHECKS); + + INST(12, Opcode::CallDynamic).any().InputsAutoType(10, 2, 2, 8, 9); INST(13, Opcode::Add).s32().Inputs(8, 1); // i++ INST(14, Opcode::Compare).CC(CC_LT).b().Inputs(13, 2); // i < 10 @@ -793,7 +883,6 @@ TEST_F(CheckEliminationEcmaTest, MoveDynamicCallCheckFromLoop) ASSERT_TRUE(graph->RunPass()); GraphChecker(graph).Check(); - auto graph_opt = CreateGraphDynWithDefaultRuntime(); GRAPH(graph_opt) @@ -814,8 +903,7 @@ TEST_F(CheckEliminationEcmaTest, MoveDynamicCallCheckFromLoop) .IntrinsicId(compiler::RuntimeInterface::IntrinsicId::INTRINSIC_DEFINEFUNC_DYN) .Inputs({{compiler::DataType::ANY, 4}, {compiler::DataType::NO_TYPE, 3}}); INST(20, Opcode::SaveStateDeoptimize).Inputs(0, 1, 2, 4, 5).SrcVregs({0, 1, 2, 4, 5}); - INST(10, Opcode::AnyTypeCheck).any().AnyType(AnyBaseType::ECMASCRIPT_FUNCTION_TYPE).Inputs(5, 20); - INST(11, Opcode::DynamicCallCheck).any().Inputs(10, 20); + INST(10, Opcode::AnyTypeCheck).any().AnyType(AnyBaseType::ECMASCRIPT_HEAP_OBJECT_TYPE).Inputs(5, 20); INST(6, Opcode::Compare).SrcType(DataType::INT32).CC(CC_LT).b().Inputs(0, 2); // 0 < 10 INST(7, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0).Inputs(6); } @@ -823,7 +911,20 @@ TEST_F(CheckEliminationEcmaTest, MoveDynamicCallCheckFromLoop) { INST(8, Opcode::Phi).s32().Inputs(0, 13); INST(9, Opcode::SaveState).Inputs(0, 1, 2, 4, 5).SrcVregs({0, 1, 2, 4, 5}); - INST(12, Opcode::CallDynamic).any().InputsAutoType(11, 2, 2, 8, 9); + + INST(17, Opcode::LoadObject) + .ref() + .TypeId(TypeIdMixin::MEM_DYN_CLASS_ID) + .ObjectType(ObjectType::MEM_DYN_CLASS) + .Inputs(10); + INST(18, Opcode::LoadObject) + .ref() + .TypeId(TypeIdMixin::MEM_DYN_HCLASS_ID) + .ObjectType(ObjectType::MEM_DYN_HCLASS) + .Inputs(17); + INST(11, Opcode::HclassCheck).any().Inputs(18, 9).SetChecks(HclassChecks::ALL_CHECKS); + + INST(12, Opcode::CallDynamic).any().InputsAutoType(10, 2, 2, 8, 9); INST(13, Opcode::Add).s32().Inputs(8, 1); // i++ INST(14, Opcode::Compare).CC(CC_LT).b().Inputs(13, 2); // i < 10