diff --git a/compiler/CMakeLists.txt b/compiler/CMakeLists.txt index 214677c8214e53e7f8d17c08f3241a711f8cdc4b..dada7ceebdc1b74478e844da581ac6f789039969 100644 --- a/compiler/CMakeLists.txt +++ b/compiler/CMakeLists.txt @@ -33,6 +33,7 @@ set(COMPILER_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/intrinsics_type_resolving_ecmascript.cpp ${CMAKE_CURRENT_SOURCE_DIR}/codegen_intrinsics_ecmascript.cpp ${CMAKE_CURRENT_SOURCE_DIR}/optimizer/ir_builder/ecmascript_inst_builder.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/optimizer/ecma_pipeline.cpp ) target_sources(arkcompiler PRIVATE ${COMPILER_SOURCES}) @@ -42,3 +43,6 @@ target_include_directories(arkcompiler PUBLIC ) add_inst_templates(${CMAKE_CURRENT_SOURCE_DIR}/optimizer/ir_builder/ecmascript_inst_templates.yaml) + +add_merge_plugin(PLUGIN_NAME "create_pipeline.h" INPUT_FILE "${CMAKE_CURRENT_SOURCE_DIR}/plugin_files/plugin_create_pipeline.h") +add_merge_plugin(PLUGIN_NAME "create_pipeline_includes.h" INPUT_FILE "${CMAKE_CURRENT_SOURCE_DIR}/plugin_files/plugin_create_pipeline_includes.h") diff --git a/compiler/optimizer/ecma_pipeline.cpp b/compiler/optimizer/ecma_pipeline.cpp new file mode 100644 index 0000000000000000000000000000000000000000..0601a6ad39221771af0928d83ee5eefd7ba05880 --- /dev/null +++ b/compiler/optimizer/ecma_pipeline.cpp @@ -0,0 +1,116 @@ +/** + * Copyright (c) 2022 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ecma_pipeline.h" +#include "optimizer/ir/graph.h" + +#include "optimizer/analysis/alias_analysis.h" +#include "optimizer/analysis/linear_order.h" +#include "optimizer/analysis/monitor_analysis.h" +#include "optimizer/analysis/rpo.h" +#include "optimizer/optimizations/balance_expressions.h" +#include "optimizer/optimizations/branch_elimination.h" +#include "optimizer/optimizations/checks_elimination.h" +#include "optimizer/optimizations/code_sink.h" +#include "optimizer/optimizations/deoptimize_elimination.h" +#include "optimizer/optimizations/cleanup.h" +#include "optimizer/optimizations/if_conversion.h" +#include "optimizer/optimizations/licm.h" +#include "optimizer/optimizations/licm_conditions.h" +#include "optimizer/optimizations/loop_peeling.h" +#include "optimizer/optimizations/loop_unroll.h" +#include "optimizer/optimizations/lowering.h" +#include "optimizer/optimizations/lse.h" +#include "optimizer/optimizations/memory_barriers.h" +#include "optimizer/optimizations/memory_coalescing.h" +#include "optimizer/optimizations/peepholes.h" +#include "optimizer/optimizations/redundant_loop_elimination.h" +#include "optimizer/optimizations/regalloc/reg_alloc.h" +#include "optimizer/optimizations/scheduler.h" +#include "optimizer/optimizations/try_catch_resolving.h" +#include "optimizer/optimizations/types_resolving.h" +#include "optimizer/optimizations/vn.h" +#include "optimizer/optimizations/cse.h" +#include "optimizer/optimizations/move_constants.h" +#include "optimizer/optimizations/adjust_arefs.h" + +namespace panda::compiler::ecmascript { + +bool EcmaPipeline::RunOptimizations() +{ + auto graph = GetGraph(); + + ASSERT(!graph->IsOsrMode() && "We don't support OSR in JS yet"); + + graph->RunPass(); + if (!graph->RunPass()) { + LOG(WARNING, COMPILER) << "Compiler detected incorrect monitor policy"; + return false; + } + graph->RunPass(); + graph->RunPass(); + graph->RunPass(); + graph->RunPass(false); + if (graph->IsAotMode()) { + graph->RunPass(); + } + graph->RunPass(); + graph->RunPass(false); + graph->RunPass(OPTIONS.GetCompilerLicmHoistLimit()); + graph->RunPass(); + graph->RunPass(); + graph->RunPass(); + graph->RunPass(); + graph->RunPass(); + if (graph->RunPass() && graph->RunPass()) { + graph->RunPass(); + } + graph->RunPass(); + if (graph->IsAotMode()) { + graph->RunPass(); + } + graph->RunPass(); + graph->RunPass(OPTIONS.GetCompilerLoopUnrollInstLimit(), OPTIONS.GetCompilerLoopUnrollFactor()); + graph->RunPass(); + if (graph->RunPass()) { + graph->RunPass(); + } + graph->RunPass(); + if (graph->IsAotMode()) { + graph->RunPass(); + } + if (graph->RunPass()) { + graph->RunPass(); + } + +#ifndef NDEBUG + graph->SetLowLevelInstructionsEnabled(); +#endif // NDEBUG + graph->RunPass(false); + graph->RunPass(); + graph->RunPass(); + graph->RunPass(OPTIONS.IsCompilerMemoryCoalescingAligned()); + graph->RunPass(OPTIONS.GetCompilerIfConversionLimit()); + graph->RunPass(); + // Perform MoveConstants after Scheduler because Scheduler can rearrange constants + // and cause spillfill in reg alloc + graph->RunPass(); + graph->RunPass(); + graph->RunPass(); + + return true; +} + +} // namespace panda::compiler::ecmascript diff --git a/compiler/optimizer/ecma_pipeline.h b/compiler/optimizer/ecma_pipeline.h new file mode 100644 index 0000000000000000000000000000000000000000..a31b1da652a53a51306af8fb0bb8bef7b340f52b --- /dev/null +++ b/compiler/optimizer/ecma_pipeline.h @@ -0,0 +1,36 @@ +/** + * 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. + */ + +#ifndef PANDA_ECMA_PIPELINE_H +#define PANDA_ECMA_PIPELINE_H + +#include "compiler/optimizer/pipeline.h" + +namespace panda::compiler::ecmascript { + +class EcmaPipeline : public Pipeline { +public: + using Pipeline::Pipeline; + ~EcmaPipeline() override = default; + + NO_COPY_SEMANTIC(EcmaPipeline); + NO_MOVE_SEMANTIC(EcmaPipeline); + + bool RunOptimizations() override; +}; + +} // namespace panda::compiler::ecmascript + +#endif // PANDA_ECMA_PIPELINE_H diff --git a/compiler/plugin_files/plugin_create_pipeline.h b/compiler/plugin_files/plugin_create_pipeline.h new file mode 100644 index 0000000000000000000000000000000000000000..80f8d5a1669af6a381888e4ab972078680d5a40d --- /dev/null +++ b/compiler/plugin_files/plugin_create_pipeline.h @@ -0,0 +1,18 @@ + /** + * 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. + */ + +case panda_file::SourceLang::ECMASCRIPT: { + return std::make_unique(graph); +} \ No newline at end of file diff --git a/compiler/plugin_files/plugin_create_pipeline_includes.h b/compiler/plugin_files/plugin_create_pipeline_includes.h new file mode 100644 index 0000000000000000000000000000000000000000..9f62ed4df3a1416ae48dd4abf85b5b5f9f6b7b81 --- /dev/null +++ b/compiler/plugin_files/plugin_create_pipeline_includes.h @@ -0,0 +1 @@ +#include "plugins/ecmascript/compiler/optimizer/ecma_pipeline.h" \ No newline at end of file diff --git a/runtime/class_linker/panda_file_translator.cpp b/runtime/class_linker/panda_file_translator.cpp index 7582a1b1f305b7523fec1be813f8e515446f7065..ad7e5dbc5970ae8ff615c93fbbbe351d87c229dc 100644 --- a/runtime/class_linker/panda_file_translator.cpp +++ b/runtime/class_linker/panda_file_translator.cpp @@ -147,6 +147,7 @@ void PandaFileTranslator::TranslateMethod(const compiler::AotClass &aot_class, c } method->SetCallTypeFromAnnotation(); + method->InitProfileVector(); const uint8_t *insns = codeDataAccessor.GetInstructions(); if (translated_code_.find(insns) == translated_code_.end()) { translated_code_.insert(insns); diff --git a/runtime/compiler/ecmascript_runtime_interface.cpp b/runtime/compiler/ecmascript_runtime_interface.cpp index f840ddf3d94c8d3f9aea0f96ff5ecae1d7fc8296..7e2e913acb25f1597131bbf1ab92567913943218 100644 --- a/runtime/compiler/ecmascript_runtime_interface.cpp +++ b/runtime/compiler/ecmascript_runtime_interface.cpp @@ -13,6 +13,7 @@ * limitations under the License. */ +#include "runtime/profiling/profiling.h" #include "plugins/ecmascript/runtime/compiler/ecmascript_runtime_interface.h" #include "plugins/ecmascript/runtime/ecma_vm.h" #include "plugins/ecmascript/runtime/js_function.h" @@ -61,7 +62,7 @@ uintptr_t EcmaRuntimeInterface::GetGlobalVarAddress(MethodPtr method, size_t id) if (thread == nullptr || !thread->IsThreadAlive()) { return 0; } - auto panda_file = MethodCast(method)->GetPandaFile(); + auto panda_file = JsMethodCast(method)->GetPandaFile(); JSTaggedValue constant_pool(JSTaggedValue::VALUE_HOLE); auto func = [&](Program *p) { constant_pool = p->GetConstantPool(); }; @@ -96,14 +97,22 @@ uintptr_t EcmaRuntimeInterface::GetGlobalVarAddress(MethodPtr method, size_t id) return reinterpret_cast(res.GetHeapObject()); } -EcmaRuntimeInterface::MethodProfile EcmaRuntimeInterface::GetMethodProfile(MethodPtr method) const +EcmaRuntimeInterface::MethodProfile EcmaRuntimeInterface::GetMethodProfile(MethodPtr method, bool from_vector) const { + if (from_vector) { + if (auto prof_vector = JsMethodCast(method)->GetProfilingVector(); prof_vector != nullptr) { + return reinterpret_cast(prof_vector); + } + return profiling::INVALID_PROFILE; + } auto name = GetMethodFullName(method, false); auto prof = profile_.find(std::string(name)); if (prof == profile_.end()) { LOG(DEBUG, COMPILER) << "Profile not found for " << name; } - return (prof != profile_.end()) ? reinterpret_cast(prof->second.data()) : 0; + return (prof != profile_.end()) ? reinterpret_cast( + const_cast(prof->second.data())) + : profiling::INVALID_PROFILE; } EcmaRuntimeInterface::BytecodeProfile EcmaRuntimeInterface::GetBytecodeProfile(MethodProfile prof, @@ -137,4 +146,4 @@ Expected EcmaRuntimeInterface::AddProfile(std::string_view f return true; } -} // namespace panda::ecmascript \ No newline at end of file +} // namespace panda::ecmascript diff --git a/runtime/compiler/ecmascript_runtime_interface.h b/runtime/compiler/ecmascript_runtime_interface.h index 2ac6184c39fe3bf7ce300749aa4dda8cc32eada7..bd9fb65315ecd21227e124b9903c3985a6efe49f 100644 --- a/runtime/compiler/ecmascript_runtime_interface.h +++ b/runtime/compiler/ecmascript_runtime_interface.h @@ -17,6 +17,8 @@ #include "runtime/compiler.h" #include "plugins/ecmascript/runtime/js_method.h" +#include "plugins/ecmascript/runtime/js_object.h" +#include "plugins/ecmascript/runtime/js_tagged_value.h" #include "plugins/ecmascript/runtime/ecma_profiling.h" namespace panda::ecmascript { @@ -25,6 +27,12 @@ class EcmaVM; class EcmaRuntimeInterface : public PandaRuntimeInterface { public: explicit EcmaRuntimeInterface(const EcmaVM *ecma_vm); + + ecmascript::JSMethod *JsMethodCast(RuntimeInterface::MethodPtr method) const + { + return static_cast(method); + } + size_t GetLanguageExtensionSize(Arch arch) const override; std::string GetMethodFullName(MethodPtr method, [[maybe_unused]] bool with_signature) const override; @@ -52,12 +60,17 @@ public: return EntrypointId::GET_GLOBAL_VAR_ADDRESS; } - MethodProfile GetMethodProfile(MethodPtr method) const override; + MethodProfile GetMethodProfile(MethodPtr method, bool from_vector) const override; BytecodeProfile GetBytecodeProfile(MethodProfile prof, const uint8_t *bc_inst, size_t pc) const override; Expected AddProfile(std::string_view fname) override; + std::string GetMethodName([[maybe_unused]] MethodPtr method) const override + { + return utf::Mutf8AsCString(JsMethodCast(method)->GetName().data); + } + private: const EcmaVM *ecma_vm_ {nullptr}; panda::ecmascript::EcmaProfileContainer profile_; diff --git a/runtime/ecma_vm.cpp b/runtime/ecma_vm.cpp index 4364466441a6a4719d9c9922e08793fbbf13d166..8d20bfa02fa991432251eab22932e55a5dd22470 100644 --- a/runtime/ecma_vm.cpp +++ b/runtime/ecma_vm.cpp @@ -368,7 +368,6 @@ void EcmaVM::SaveProfileInfo() auto profile_size = mda.GetProfileSize().value(); // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic) proto[method_name] = std::vector(profile_data, profile_data + profile_size); - std::cerr << "SAVE " << method_name << std::endl; } } diff --git a/runtime/interpreter/ecma-interpreter-inl.h b/runtime/interpreter/ecma-interpreter-inl.h index 053c1ef8672b4e8d1072d4af88c0aa9b96fc2876..10d7895f06ed589c582fbdf2640017e3138548c8 100644 --- a/runtime/interpreter/ecma-interpreter-inl.h +++ b/runtime/interpreter/ecma-interpreter-inl.h @@ -264,6 +264,8 @@ public: } else { // Interpreter EcmascriptEnvironment *prev_env = js_thread->GetEcmascriptEnv(); + method->IncrementHotnessCounter(0, nullptr); + // Call stackless interpreter this->template CallInterpreterStackless, format, true, false, false, false>(method); diff --git a/runtime/js_method.cpp b/runtime/js_method.cpp index 61fbe03bdd28dc8d4949a58c011a71181a2e240d..399d13321a55dd9ed9bf51963246506d3483b5aa 100644 --- a/runtime/js_method.cpp +++ b/runtime/js_method.cpp @@ -52,11 +52,20 @@ void JSMethod::SetCallTypeFromAnnotation() } } }); +} + +void JSMethod::InitProfileVector() +{ + const panda_file::File *pandaFile = GetPandaFile(); + panda_file::File::EntityId fieldId = GetFileId(); + panda_file::MethodDataAccessor mda(*pandaFile, fieldId); auto prof_size = mda.GetProfileSize(); if (prof_size) { profile_size_ = prof_size.value(); + size_t size = RoundUp(prof_size.value(), BITS_PER_INTPTR); // NOLINTNEXTLINE(modernize-avoid-c-arrays) - profiling_data_ = MakePandaUnique(RoundUp(prof_size.value(), BITS_PER_INTPTR)); + profiling_data_ = MakePandaUnique(size); + std::memset(profiling_data_.get(), 0, size); } } diff --git a/runtime/js_method.h b/runtime/js_method.h index 638ba9715b0be5c870a3c2187340a376f5af26c3..dead95f635c0cd21aece9fe5b8a735aea40efa13 100644 --- a/runtime/js_method.h +++ b/runtime/js_method.h @@ -123,6 +123,8 @@ public: std::string GetFullName([[maybe_unused]] bool with_signature = false) const; void SetCallTypeFromAnnotation(); + void InitProfileVector(); + JSTaggedValue GetLength() const; uint8_t *GetProfilingVector() diff --git a/tests/checked/CMakeLists.txt b/tests/checked/CMakeLists.txt index 3ccbcb36ed35ded7e3555eba79dc080bce7bd425..7f09a016de16cdcdc222f6dbc189926281830808 100644 --- a/tests/checked/CMakeLists.txt +++ b/tests/checked/CMakeLists.txt @@ -117,4 +117,7 @@ endfunction() if (NOT PANDA_TARGET_ARM32) panda_add_checked_test_ecma(FILE ${CMAKE_CURRENT_SOURCE_DIR}/type_resolving.js SUPPORT_RELEASE true) panda_add_checked_test_ecma(FILE ${CMAKE_CURRENT_SOURCE_DIR}/global_var.js SUPPORT_RELEASE true) + if (PANDA_ENABLE_BYTECODE_PROFILING) + panda_add_checked_test_ecma(FILE ${CMAKE_CURRENT_SOURCE_DIR}/ecma_profiling.js SUPPORT_RELEASE true) + endif() endif() diff --git a/tests/checked/ecma_profiling.js b/tests/checked/ecma_profiling.js new file mode 100644 index 0000000000000000000000000000000000000000..aba4bd51eed41bf863055c3402ffb46c39b4ce65 --- /dev/null +++ b/tests/checked/ecma_profiling.js @@ -0,0 +1,38 @@ +/* + * 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 Common JIT profiling +//! RUN options: "--no-async-jit --compiler-hotness-threshold=10 --compiler-regex _GLOBAL::test_profile", entry: "_GLOBAL::func_main_0" +//! METHOD "test_profile" +//! PASS_AFTER "IrBuilder" +//! INST /Intrinsic.Add.*/ +//! INST /Intrinsic.Neg.*/ +//! PASS_AFTER "Codegen" +//! INST_NOT /Intrinsic.Add.*/ +//! INST_NOT /Intrinsic.Neg.*/ + +function test_profile(a, b) { + a = a + b; + return -a; +} + +var res = 0; +for (var i = 0; i < 20; i++) { + res += test_profile(i, res); +} + +if (res != -19) { + throw "Wrong result: " + res; +}