diff --git a/compiler/background_task_runner.h b/compiler/background_task_runner.h new file mode 100644 index 0000000000000000000000000000000000000000..17bab598dc03960087117a68275e07585bfa12c3 --- /dev/null +++ b/compiler/background_task_runner.h @@ -0,0 +1,220 @@ +/** + * 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. + */ + +#ifndef COMPILER_BACKGROUND_TASK_RUNNER_H_ +#define COMPILER_BACKGROUND_TASK_RUNNER_H_ + +#include + +#include "libpandabase/task_runner.h" +#include "libpandabase/taskmanager/task.h" +#include "libpandabase/taskmanager/task_queue.h" +#include "compiler/optimizer/pipeline.h" +#include "runtime/include/compiler_interface.h" + +namespace panda { + +template +class ArenaAllocatorT; +class CompilerTask; +class Thread; +class Method; +class PandaVM; + +} // namespace panda + +namespace panda::compiler { + +class Graph; + +class BackgroundCompilerContext { +public: + using ArenaAllocator = panda::ArenaAllocatorT; + + void SetCompilerTask(std::unique_ptr> compiler_task) + { + compiler_task_ = std::move(compiler_task); + } + + void SetCompilerThread(std::unique_ptr> compiler_thread) + + { + compiler_thread_ = std::move(compiler_thread); + } + + void SetAllocator(std::unique_ptr allocator) + { + allocator_ = std::move(allocator); + } + + void SetLocalAllocator(std::unique_ptr local_allocator) + { + local_allocator_ = std::move(local_allocator); + } + + void SetMethodName(std::string method_name) + { + method_name_ = std::move(method_name); + } + + void SetGraph(Graph *graph) + { + graph_ = graph; + } + + void SetPipeline(std::unique_ptr pipeline) + { + pipeline_ = std::move(pipeline); + } + + Method *GetMethod() + { + return compiler_task_->GetMethod(); + } + + bool IsOsr() + { + return compiler_task_->IsOsr(); + } + + PandaVM *GetVM() + { + return compiler_task_->GetVM(); + } + + ArenaAllocator *GetAllocator() + { + return allocator_.get(); + } + + ArenaAllocator *GetLocalAllocator() + { + return local_allocator_.get(); + } + + std::string &GetMethodName() + { + return method_name_; + } + + Graph *GetGraph() + { + return graph_; + } + + Pipeline *GetPipeline() + { + return pipeline_.get(); + } + +private: + std::unique_ptr> compiler_task_; + std::unique_ptr> compiler_thread_; + std::unique_ptr allocator_; + std::unique_ptr local_allocator_; + std::string method_name_; + Graph *graph_ {nullptr}; + std::unique_ptr pipeline_; +}; + +namespace copy_hooks { +class FailOnCopy { +public: + FailOnCopy() = default; + FailOnCopy(const FailOnCopy &other) + { + (void)other; + UNREACHABLE(); + } + FailOnCopy &operator=(const FailOnCopy &other) + { + (void)other; + UNREACHABLE(); + } + DEFAULT_MOVE_SEMANTIC(FailOnCopy); + ~FailOnCopy() = default; +}; + +template +class FakeCopyable : public FailOnCopy { +public: + explicit FakeCopyable(T &&t) : target_(std::forward(t)) {} + FakeCopyable(const FakeCopyable &other) + : FailOnCopy(other), // this will fail + target_(std::move(const_cast(other.target_))) // never reached + { + } + FakeCopyable &operator=(const FakeCopyable &other) = default; + DEFAULT_MOVE_SEMANTIC(FakeCopyable); + ~FakeCopyable() = default; + + template + auto operator()(Args &&...args) + { + return target_(std::forward(args)...); + } + +private: + T target_; +}; + +template +FakeCopyable MakeFakeCopyable(T &&t) +{ + return FakeCopyable(std::forward(t)); +} + +} // namespace copy_hooks + +class BackgroundCompilerTaskRunner : public panda::TaskRunner { +public: + BackgroundCompilerTaskRunner(taskmanager::TaskQueue *compiler_queue, Thread *compiler_thread, + std::function set_current_thread) + : compiler_queue_(compiler_queue), + compiler_thread_(compiler_thread), + set_current_thread_(std::move(set_current_thread)) + { + } + + BackgroundCompilerContext &GetContext() override + { + return task_ctx_; + } + + void StartTask(TaskRunner::NextTaskRunner next_task) && override + { + auto *compiler_queue = compiler_queue_; + auto task_runner = [next_task = std::move(next_task), next_runner = std::move(*this)]() mutable { + auto set_current_thread = next_runner.set_current_thread_; + set_current_thread(next_runner.compiler_thread_); + next_task(std::move(next_runner)); + set_current_thread(nullptr); + }; + taskmanager::TaskProperties properties(taskmanager::TaskType::JIT, taskmanager::VMType::STATIC_VM, + taskmanager::TaskExecutionMode::BACKGROUND); + auto task = taskmanager::Task::Create(properties, copy_hooks::MakeFakeCopyable(std::move(task_runner))); + compiler_queue->AddTask(std::move(task)); + } + +private: + taskmanager::TaskQueue *compiler_queue_ {nullptr}; + Thread *compiler_thread_ {nullptr}; + std::function set_current_thread_; + BackgroundCompilerContext task_ctx_; +}; + +} // namespace panda::compiler + +#endif // COMPILER_BACKGROUND_TASK_RUNNER_H_ diff --git a/compiler/compile_method.cpp b/compiler/compile_method.cpp index cd031da260c7b8fef781d83c1504b0b157a86eef..5ce4cb1dc714744a930901e34c62f22c98737acc 100644 --- a/compiler/compile_method.cpp +++ b/compiler/compile_method.cpp @@ -82,7 +82,7 @@ static void EndCompilation(const std::string &method_name, bool is_osr, size_t b } } -static Arch ChooseArch(Arch arch) +Arch ChooseArch(Arch arch) { if (arch != Arch::NONE) { return arch; @@ -165,123 +165,176 @@ static uint8_t *GetEntryPoint(Graph *graph, [[maybe_unused]] Method *method, con return entry_point; } -bool JITCompileMethod(RuntimeInterface *runtime, Method *method, bool is_osr, CodeAllocator *code_allocator, - ArenaAllocator *allocator, ArenaAllocator *local_allocator, - ArenaAllocator *gdb_debug_info_allocator, JITStats *jit_stats) +void SetCompilationStatus(Method *method, bool is_osr, bool success_compiled) { - std::string method_name = runtime->GetMethodFullName(method, false); + if (success_compiled) { + // Check that method was not deoptimized + method->AtomicSetCompilationStatus(Method::COMPILATION, Method::COMPILED); + return; + } + // If deoptimization occurred during OSR compilation, the compilation returns false. + // For the case we need reset compiation status + if (is_osr) { + method->SetCompilationStatus(Method::NOT_COMPILED); + return; + } + // Failure during compilation, should we retry later? + method->SetCompilationStatus(Method::FAILED); +} + +template +void JITCompileMethod(RuntimeInterface *runtime, CodeAllocator *code_allocator, + ArenaAllocator *gdb_debug_info_allocator, JITStats *jit_stats, + CompilerTaskRunner task_runner) +{ + auto &task_ctx = task_runner.GetContext(); + auto *task_method = task_ctx.GetMethod(); + task_ctx.SetMethodName(runtime->GetMethodFullName(task_method, false)); + auto &method_name = task_ctx.GetMethodName(); + SCOPED_TRACE_STREAM << "JIT compiling " << method_name; if (!OPTIONS.MatchesRegex(method_name)) { LOG(DEBUG, COMPILER) << "Skip the method due to regexp mismatch: " << method_name; - return false; + SetCompilationStatus(task_method, task_ctx.IsOsr(), false); + return task_runner.RunCallbackOnFail(); } if (jit_stats != nullptr) { jit_stats->SetCompilationStart(); } - Graph *graph {nullptr}; - auto finalizer = [graph, jit_stats]([[maybe_unused]] void *ptr) { + task_runner.AddFinalize([jit_stats](CompilerContext &compiler_ctx) { if (jit_stats != nullptr) { // Reset compilation start time in all cases for consistency jit_stats->ResetCompilationStart(); } + auto *graph = compiler_ctx.GetGraph(); if (graph != nullptr) { graph->~Graph(); } - }; - std::unique_ptr fin(&finalizer, finalizer); - - auto arch {Arch::NONE}; - bool is_dynamic = panda::panda_file::IsDynamicLanguage(method->GetClass()->GetSourceLang()); - - if (!CompileInGraph(runtime, method, is_osr, allocator, local_allocator, is_dynamic, &arch, method_name, &graph, - jit_stats)) { - return false; - } - ASSERT(graph != nullptr && graph->GetCode().data() != nullptr); - - if (!is_dynamic && !CheckSingleImplementation(graph)) { - EndCompilation(method_name, is_osr, method->GetCodeSize(), 0, 0, 0, - events::CompilationStatus::FAILED_SINGLE_IMPL, jit_stats); - return false; - } - - // Drop non-native code in any case - if (arch != RUNTIME_ARCH) { - EndCompilation(method_name, is_osr, method->GetCodeSize(), 0, 0, 0, events::CompilationStatus::DROPPED, - jit_stats); - return false; - } - - auto entry_point = - GetEntryPoint(graph, method, method_name, is_osr, code_allocator, gdb_debug_info_allocator, jit_stats); - if (entry_point == nullptr) { - return false; - } - if (is_osr) { - if (!runtime->TrySetOsrCode(method, entry_point)) { - // Compiled code has been deoptimized, so we shouldn't install osr code. - // TODO(compiler): release compiled code memory, when CodeAllocator supports freeing the memory. - return false; - } - } else { - runtime->SetCompiledEntryPoint(method, entry_point); - } - ASSERT(graph != nullptr); - return true; + }); + + auto arch = ChooseArch(Arch::NONE); + bool is_dynamic = panda::panda_file::IsDynamicLanguage(task_method->GetClass()->GetSourceLang()); + + task_runner.AddCallbackOnSuccess([is_dynamic, arch, runtime, code_allocator, gdb_debug_info_allocator, + jit_stats](CompilerContext &compiler_ctx) { + auto is_osr = compiler_ctx.IsOsr(); + auto *method = compiler_ctx.GetMethod(); + bool success_compiled = [&] { + auto *graph = compiler_ctx.GetGraph(); + ASSERT(graph != nullptr && graph->GetCode().data() != nullptr); + + auto &name = compiler_ctx.GetMethodName(); + + if (!is_dynamic && !CheckSingleImplementation(graph)) { + EndCompilation(name, is_osr, method->GetCodeSize(), 0, 0, 0, + events::CompilationStatus::FAILED_SINGLE_IMPL, jit_stats); + return false; + } + + // Drop non-native code in any case + if (arch != RUNTIME_ARCH) { + EndCompilation(name, is_osr, method->GetCodeSize(), 0, 0, 0, events::CompilationStatus::DROPPED, + jit_stats); + return false; + } + + auto entry_point = + GetEntryPoint(graph, method, name, is_osr, code_allocator, gdb_debug_info_allocator, jit_stats); + if (entry_point == nullptr) { + return false; + } + if (is_osr) { + if (!runtime->TrySetOsrCode(method, entry_point)) { + // Compiled code has been deoptimized, so we shouldn't install osr code. + // TODO(compiler): release compiled code memory, when CodeAllocator supports freeing the memory. + return false; + } + } else { + runtime->SetCompiledEntryPoint(method, entry_point); + } + return true; + }(); + SetCompilationStatus(method, is_osr, success_compiled); + }); + task_runner.AddCallbackOnFail([](CompilerContext &compiler_ctx) { + SetCompilationStatus(compiler_ctx.GetMethod(), compiler_ctx.IsOsr(), false); + }); + CompileInGraph(runtime, is_dynamic, arch, std::move(task_runner), jit_stats); } -bool CompileInGraph(RuntimeInterface *runtime, Method *method, bool is_osr, ArenaAllocator *allocator, - ArenaAllocator *local_allocator, bool is_dynamic, Arch *arch, const std::string &method_name, - Graph **graph, JITStats *jit_stats) +template +void CompileInGraph(RuntimeInterface *runtime, bool is_dynamic, Arch arch, CompilerTaskRunner task_runner, + JITStats *jit_stats) { + auto &task_ctx = task_runner.GetContext(); + auto is_osr = task_ctx.IsOsr(); + auto *method = task_ctx.GetMethod(); + auto &method_name = task_ctx.GetMethodName(); + LOG(INFO, COMPILER) << "Compile method" << (is_osr ? "(OSR)" : "") << ": " << method_name << " (" << runtime->GetFileName(method) << ')'; - *arch = ChooseArch(*arch); - if (*arch == Arch::NONE || !BackendSupport(*arch)) { + + if (arch == Arch::NONE || !BackendSupport(arch)) { LOG(DEBUG, COMPILER) << "Compilation unsupported for this platform!"; - return false; + return task_runner.RunCallbackOnFail(); } - ASSERT(*graph == nullptr); - *graph = allocator->New(allocator, local_allocator, *arch, method, runtime, is_osr, nullptr, is_dynamic); - if (*graph == nullptr) { + auto *allocator = task_ctx.GetAllocator(); + auto *local_allocator = task_ctx.GetLocalAllocator(); + task_ctx.SetGraph( + allocator->template New(allocator, local_allocator, arch, method, runtime, is_osr, nullptr, is_dynamic)); + if (task_ctx.GetGraph() == nullptr) { LOG(ERROR, COMPILER) << "Creating graph failed!"; EndCompilation(method_name, is_osr, method->GetCodeSize(), 0, 0, 0, events::CompilationStatus::FAILED, jit_stats); - return false; + return task_runner.RunCallbackOnFail(); } - if (!(*graph)->RunPass()) { + task_runner.SetTaskOnSuccess([jit_stats](CompilerTaskRunner &&next_runner) { + auto &next_ctx = next_runner.GetContext(); + next_ctx.GetGraph()->SetLanguage(next_ctx.GetMethod()->GetClass()->GetSourceLang()); + + next_runner.AddCallbackOnSuccess([]([[maybe_unused]] CompilerContext &compiler_ctx) { + LOG(DEBUG, COMPILER) << "The method is compiled"; + }); + next_runner.AddCallbackOnFail([jit_stats](CompilerContext &compiler_ctx) { + if (!compiler::OPTIONS.IsCompilerIgnoreFailures()) { + LOG(FATAL, COMPILER) << "RunOptimizations failed!"; + } + LOG(WARNING, COMPILER) << "RunOptimizations failed!"; + EndCompilation(compiler_ctx.GetMethodName(), compiler_ctx.IsOsr(), compiler_ctx.GetMethod()->GetCodeSize(), + 0, 0, 0, events::CompilationStatus::FAILED, jit_stats); + }); + + // Run compiler optimizations over created graph + RunOptimizations(std::move(next_runner)); + }); + task_runner.AddCallbackOnFail([jit_stats](CompilerContext &compiler_ctx) { if (!compiler::OPTIONS.IsCompilerIgnoreFailures()) { LOG(FATAL, COMPILER) << "IrBuilder failed!"; } LOG(WARNING, COMPILER) << "IrBuilder failed!"; - EndCompilation(method_name, is_osr, method->GetCodeSize(), 0, 0, 0, events::CompilationStatus::FAILED, - jit_stats); - return false; - } + EndCompilation(compiler_ctx.GetMethodName(), compiler_ctx.IsOsr(), compiler_ctx.GetMethod()->GetCodeSize(), 0, + 0, 0, events::CompilationStatus::FAILED, jit_stats); + }); - (*graph)->SetLanguage(method->GetClass()->GetSourceLang()); - - // Run compiler optimizations over created graph - bool res = RunOptimizations(*graph); - if (!res) { - if (!compiler::OPTIONS.IsCompilerIgnoreFailures()) { - LOG(FATAL, COMPILER) << "RunOptimizations failed!"; - } - LOG(WARNING, COMPILER) << "RunOptimizations failed!"; - EndCompilation(method_name, is_osr, method->GetCodeSize(), 0, 0, 0, events::CompilationStatus::FAILED, - jit_stats); - return false; - } + std::move(task_runner).EndTask([](CompilerContext &compiler_ctx) { + return compiler_ctx.GetGraph()->template RunPass(); + }); +} - LOG(DEBUG, COMPILER) << "The method is compiled"; +template void JITCompileMethod(RuntimeInterface *, CodeAllocator *, ArenaAllocator *, JITStats *, + CompilerTaskRunner); +template void JITCompileMethod(RuntimeInterface *, CodeAllocator *, ArenaAllocator *, JITStats *, + CompilerTaskRunner); +template void CompileInGraph(RuntimeInterface *, bool, Arch, CompilerTaskRunner, + JITStats *); +template void CompileInGraph(RuntimeInterface *, bool, Arch, CompilerTaskRunner, + JITStats *); - return true; -} } // namespace panda::compiler #ifdef PANDA_COMPILER_DEBUG_INFO diff --git a/compiler/compile_method.h b/compiler/compile_method.h index 6ebadb265826df90f6ec191650dcff117af251bd..db21d1ee3600f784f14a0f242f591b7cb4edf8b0 100644 --- a/compiler/compile_method.h +++ b/compiler/compile_method.h @@ -21,6 +21,7 @@ #include "mem/code_allocator.h" #include "include/method.h" #include "utils/arch.h" +#include "compiler_task_runner.h" namespace panda::compiler { class Graph; @@ -58,12 +59,18 @@ private: std::vector> stats_list_; }; -bool JITCompileMethod(RuntimeInterface *runtime, Method *method, bool is_osr, CodeAllocator *code_allocator, - ArenaAllocator *allocator, ArenaAllocator *local_allocator, - ArenaAllocator *gdb_debug_info_allocator, JITStats *jit_stats); -bool CompileInGraph(RuntimeInterface *runtime, Method *method, bool is_osr, ArenaAllocator *allocator, - ArenaAllocator *local_allocator, bool is_dynamic, Arch *arch, const std::string &method_name, - Graph **graph, JITStats *jit_stats = nullptr); +Arch ChooseArch(Arch arch); + +// Parameter RUNNER_MODE=BACKGROUND_MODE means that compilation of method +// is divided into tasks for TaskManager and occurs in its threads. +// Otherwise compilation occurs in-place. +template +void JITCompileMethod(RuntimeInterface *runtime, CodeAllocator *code_allocator, + ArenaAllocator *gdb_debug_info_allocator, JITStats *jit_stats, + CompilerTaskRunner task_runner); +template +void CompileInGraph(RuntimeInterface *runtime, bool is_dynamic, Arch arch, CompilerTaskRunner task_runner, + JITStats *jit_stats = nullptr); bool CheckMethodInLists(const std::string &method_name); } // namespace panda::compiler diff --git a/compiler/compiler_task_runner.h b/compiler/compiler_task_runner.h new file mode 100644 index 0000000000000000000000000000000000000000..e86f1ecadb4ddfb8384d335484b29635be220501 --- /dev/null +++ b/compiler/compiler_task_runner.h @@ -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. + */ + +#ifndef COMPILER_COMPILER_TASK_RUNNER_H_ +#define COMPILER_COMPILER_TASK_RUNNER_H_ + +#include + +namespace panda::compiler { +class InPlaceCompilerContext; +class BackgroundCompilerContext; +class InPlaceCompilerTaskRunner; +class BackgroundCompilerTaskRunner; + +enum TaskRunnerMode { INPLACE_MODE, BACKGROUND_MODE }; + +template +using CompilerContext = + std::conditional_t; +template +using CompilerTaskRunner = + std::conditional_t; + +} // namespace panda::compiler + +#endif // COMPILER_COMPILER_TASK_RUNNER_H_ diff --git a/compiler/inplace_task_runner.h b/compiler/inplace_task_runner.h new file mode 100644 index 0000000000000000000000000000000000000000..cd976f44b33b5627b30179bb3095ddfb4495555a --- /dev/null +++ b/compiler/inplace_task_runner.h @@ -0,0 +1,149 @@ +/** + * 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. + */ + +#ifndef COMPILER_FOREGROUND_TASK_RUNNER_H_ +#define COMPILER_FOREGROUND_TASK_RUNNER_H_ + +#include + +#include "libpandabase/task_runner.h" + +namespace panda { + +template +class ArenaAllocatorT; +class Method; +class PandaVM; +} // namespace panda + +namespace panda::compiler { + +class Pipeline; +class Graph; + +class InPlaceCompilerContext { +public: + using ArenaAllocator = panda::ArenaAllocatorT; + + void SetMethod(Method *method) + { + method_ = method; + } + + void SetOsr(bool is_osr) + { + is_osr_ = is_osr; + } + + void SetVM(PandaVM *panda_vm) + { + panda_vm_ = panda_vm; + } + + void SetAllocator(ArenaAllocator *allocator) + { + allocator_ = allocator; + } + + void SetLocalAllocator(ArenaAllocator *local_allocator) + { + local_allocator_ = local_allocator; + } + + void SetMethodName(std::string method_name) + { + method_name_ = std::move(method_name); + } + + void SetGraph(Graph *graph) + { + graph_ = graph; + } + + void SetPipeline(Pipeline *pipeline) + { + pipeline_ = pipeline; + } + + Method *GetMethod() + { + return method_; + } + + bool IsOsr() + { + return is_osr_; + } + + PandaVM *GetVM() + { + return panda_vm_; + } + + ArenaAllocator *GetAllocator() + { + return allocator_; + } + + ArenaAllocator *GetLocalAllocator() + { + return local_allocator_; + } + + std::string &GetMethodName() + { + return method_name_; + } + + Graph *GetGraph() + { + return graph_; + } + + Pipeline *GetPipeline() + { + return pipeline_; + } + +private: + Method *method_ {nullptr}; + bool is_osr_ {false}; + PandaVM *panda_vm_ {nullptr}; + ArenaAllocator *allocator_ {nullptr}; + ArenaAllocator *local_allocator_ {nullptr}; + std::string method_name_; + Graph *graph_ {nullptr}; + Pipeline *pipeline_ {nullptr}; +}; + +class InPlaceCompilerTaskRunner : public panda::TaskRunner { +public: + InPlaceCompilerContext &GetContext() override + { + return task_ctx_; + } + + void StartTask(TaskRunner::NextTaskRunner next_task) && override + { + next_task(std::move(*this)); + } + +private: + InPlaceCompilerContext task_ctx_; +}; + +} // namespace panda::compiler + +#endif // COMPILER_FOREGROUND_TASK_RUNNER_H_ diff --git a/compiler/optimizer/pipeline.cpp b/compiler/optimizer/pipeline.cpp index 6e8e2372d1513934c314070b48fb76bd1f0a2f26..dbdfff35b919e8c8bba08fb90d37c167ca5d96a2 100644 --- a/compiler/optimizer/pipeline.cpp +++ b/compiler/optimizer/pipeline.cpp @@ -15,6 +15,9 @@ #include "pipeline.h" #include "compiler_options.h" +#include "inplace_task_runner.h" +#include "background_task_runner.h" +#include "compiler_task_runner.h" #include "optimizer/code_generator/codegen.h" #include "optimizer/code_generator/codegen_native.h" @@ -78,16 +81,19 @@ static inline bool RunCodegenPass(Graph *graph) return graph->RunPass(); } -bool Pipeline::Run() +/* static */ +template +void Pipeline::Run(CompilerTaskRunner task_runner) { - auto graph = GetGraph(); + auto *graph = task_runner.GetContext().GetPipeline()->GetGraph(); #if !defined(NDEBUG) && !defined(PANDA_TARGET_MOBILE) if (OPTIONS.IsCompilerVisualizerDump()) { graph->GetPassManager()->InitialDumpVisualizerGraph(); } #endif // NDEBUG && PANDA_TARGET_MOBILE - auto finalizer = [graph](void * /* unused */) { graph->GetPassManager()->Finalize(); }; - std::unique_ptr pp(&finalizer, finalizer); + + task_runner.AddFinalize( + [](CompilerContext &compiler_ctx) { compiler_ctx.GetGraph()->GetPassManager()->Finalize(); }); if (OPTIONS.WasSetCompilerRegallocRegMask()) { COMPILER_LOG(DEBUG, REGALLOC) << "Regalloc mask force set to " << std::hex @@ -96,20 +102,30 @@ bool Pipeline::Run() } if (!OPTIONS.IsCompilerNonOptimizing()) { - if (!RunOptimizations()) { - return false; - } + task_runner.SetTaskOnSuccess([](CompilerTaskRunner &&next_runner) { + Pipeline::RunRegAllocAndCodeGenPass(std::move(next_runner)); + }); + std::move(task_runner).EndTask([](CompilerContext &compiler_ctx) { + return compiler_ctx.GetPipeline()->RunOptimizations(); + }); } else { // TryCatchResolving is needed in the non-optimizing mode since it removes unreachable for compiler // catch-handlers; After supporting catch-handlers' compilation, this pass can be run in the optimizing mode // only. - graph->RunPass(); - if (!graph->RunPass()) { + graph->template RunPass(); + if (!graph->template RunPass()) { LOG(WARNING, COMPILER) << "Compiler detected incorrect monitor policy"; - return false; + return task_runner.RunCallbackOnFail(); } + Pipeline::RunRegAllocAndCodeGenPass(std::move(task_runner)); } +} +/* static */ +template +void Pipeline::RunRegAllocAndCodeGenPass(CompilerTaskRunner task_runner) +{ + auto *graph = task_runner.GetContext().GetPipeline()->GetGraph(); bool fatal_on_err = !OPTIONS.IsCompilerAllowBackendFailures(); // Do not try to encode too large graph auto inst_size = graph->GetCurrentInstructionId(); @@ -119,24 +135,28 @@ bool Pipeline::Run() if (fatal_on_err) { LOG(FATAL, COMPILER) << "RunOptimizations failed: code predicted size too big"; } - return false; - } - graph->RunPass(); - if (!RegAlloc(graph)) { - if (fatal_on_err) { - LOG(FATAL, COMPILER) << "RunOptimizations failed: register allocation error"; - } - return false; + return task_runner.RunCallbackOnFail(); } + graph->template RunPass(); - if (!RunCodegenPass(graph)) { + task_runner.SetTaskOnSuccess([fatal_on_err](CompilerTaskRunner &&next_runner) { + next_runner.AddCallbackOnFail([fatal_on_err]([[maybe_unused]] CompilerContext &compiler_ctx) { + if (fatal_on_err) { + LOG(FATAL, COMPILER) << "RunOptimizations failed: code generation error"; + } + }); + std::move(next_runner).EndTask([](CompilerContext &compiler_ctx) { + return RunCodegenPass(compiler_ctx.GetPipeline()->GetGraph()); + }); + }); + task_runner.AddCallbackOnFail([fatal_on_err]([[maybe_unused]] CompilerContext &compiler_ctx) { if (fatal_on_err) { - LOG(FATAL, COMPILER) << "RunOptimizations failed: code generation error"; + LOG(FATAL, COMPILER) << "RunOptimizations failed: register allocation error"; } - return false; - } - - return true; + }); + std::move(task_runner).EndTask([](CompilerContext &compiler_ctx) { + return RegAlloc(compiler_ctx.GetPipeline()->GetGraph()); + }); } bool Pipeline::RunOptimizations() @@ -241,4 +261,9 @@ bool Pipeline::RunOptimizations() return true; } +template void Pipeline::Run(CompilerTaskRunner); +template void Pipeline::Run(CompilerTaskRunner); +template void Pipeline::RunRegAllocAndCodeGenPass(CompilerTaskRunner); +template void Pipeline::RunRegAllocAndCodeGenPass(CompilerTaskRunner); + } // namespace panda::compiler diff --git a/compiler/optimizer/pipeline.h b/compiler/optimizer/pipeline.h index b93c0fe28443e1496cba5433f2fb76fecd787df2..f907b040edbdb3cfaa10df8ae2bf3eb87fa44ff4 100644 --- a/compiler/optimizer/pipeline.h +++ b/compiler/optimizer/pipeline.h @@ -16,9 +16,11 @@ #ifndef PANDA_PIPELINE_H #define PANDA_PIPELINE_H -#include "macros.h" #include +#include "macros.h" +#include "compiler/compiler_task_runner.h" + namespace panda::compiler { class Graph; @@ -34,7 +36,6 @@ public: NO_COPY_SEMANTIC(Pipeline); NO_MOVE_SEMANTIC(Pipeline); - virtual bool Run(); virtual bool RunOptimizations(); Graph *GetGraph() @@ -42,9 +43,15 @@ public: return graph_; } + template + static void Run(CompilerTaskRunner task_runner); + static std::unique_ptr Create(Graph *graph); private: + template + static void RunRegAllocAndCodeGenPass(CompilerTaskRunner task_runner); + Graph *graph_ {nullptr}; }; diff --git a/compiler/optimizer_run.h b/compiler/optimizer_run.h index 59de55b7ab7163fe65918623ef875f2b0ed78a1e..668e8cde1b31fc9428fdfafd59248860725f62d8 100644 --- a/compiler/optimizer_run.h +++ b/compiler/optimizer_run.h @@ -17,13 +17,24 @@ #define COMPILER_COMPILER_RUN_H_ #include "optimizer/pipeline.h" +#include "inplace_task_runner.h" +#include "background_task_runner.h" +#include "compiler_task_runner.h" namespace panda::compiler { class Graph; -inline bool RunOptimizations(Graph *graph) +template +inline void RunOptimizations(CompilerTaskRunner task_runner) { - return Pipeline::Create(graph)->Run(); + auto &task_ctx = task_runner.GetContext(); + auto pipeline = Pipeline::Create(task_ctx.GetGraph()); + if constexpr (RUNNER_MODE == BACKGROUND_MODE) { + task_ctx.SetPipeline(std::move(pipeline)); + } else { + task_ctx.SetPipeline(pipeline.get()); + } + Pipeline::Run(std::move(task_runner)); } } // namespace panda::compiler diff --git a/compiler/tests/codegen_test.cpp b/compiler/tests/codegen_test.cpp index 613727fdc14d77002f3d2c63ce0db53916799d1d..4f4e9b1aa97645c42e24380cedc8410ebcae28f4 100644 --- a/compiler/tests/codegen_test.cpp +++ b/compiler/tests/codegen_test.cpp @@ -23,6 +23,8 @@ #include "optimizer/optimizations/regalloc/reg_alloc.h" #include "optimizer/optimizations/regalloc/reg_alloc_linear_scan.h" #include "optimizer_run.h" +#include "compiler/inplace_task_runner.h" +#include "compiler/compiler_task_runner.h" #include "libpandabase/macros.h" #include "gtest/gtest.h" @@ -1082,7 +1084,14 @@ TEST_F(CodegenTest, AddOverflow) SetNumVirtRegs(0); SetNumArgs(2); - EXPECT_TRUE(RunOptimizations(graph)); + InPlaceCompilerTaskRunner task_runner; + task_runner.GetContext().SetGraph(graph); + bool success = true; + task_runner.AddCallbackOnFail( + [&success]([[maybe_unused]] InPlaceCompilerContext &compiler_ctx) { success = false; }); + RunOptimizations(std::move(task_runner)); + EXPECT_TRUE(success); + auto code_entry = reinterpret_cast(graph->GetCode().Data()); auto code_exit = code_entry + graph->GetCode().Size(); ASSERT(code_entry != nullptr && code_exit != nullptr); @@ -1143,7 +1152,14 @@ TEST_F(CodegenTest, SubOverflow) SetNumVirtRegs(0); SetNumArgs(2); - EXPECT_TRUE(RunOptimizations(graph)); + InPlaceCompilerTaskRunner task_runner; + task_runner.GetContext().SetGraph(graph); + bool success = true; + task_runner.AddCallbackOnFail( + [&success]([[maybe_unused]] InPlaceCompilerContext &compiler_ctx) { success = false; }); + RunOptimizations(std::move(task_runner)); + EXPECT_TRUE(success); + auto code_entry = reinterpret_cast(graph->GetCode().Data()); auto code_exit = code_entry + graph->GetCode().Size(); ASSERT(code_entry != nullptr && code_exit != nullptr); diff --git a/compiler/tools/paoc/paoc.cpp b/compiler/tools/paoc/paoc.cpp index d198d85bc86aa07f7f75d38fd8ebaaa6b7edd7cb..473cb39e415c91ad311aa670ca90c785cf243ef9 100644 --- a/compiler/tools/paoc/paoc.cpp +++ b/compiler/tools/paoc/paoc.cpp @@ -667,6 +667,27 @@ bool Paoc::Compile(Method *method, size_t method_index) return ctx.compilation_status; } +bool Paoc::CompileInGraph(CompilingContext *ctx, std::string method_name, bool is_osr) +{ + compiler::InPlaceCompilerTaskRunner task_runner; + auto &task_ctx = task_runner.GetContext(); + task_ctx.SetMethod(ctx->method); + task_ctx.SetOsr(is_osr); + task_ctx.SetAllocator(&ctx->allocator); + task_ctx.SetLocalAllocator(&ctx->graph_local_allocator); + task_ctx.SetMethodName(std::move(method_name)); + task_runner.AddFinalize( + [&graph = ctx->graph](InPlaceCompilerContext &compiler_ctx) { graph = compiler_ctx.GetGraph(); }); + + bool success = true; + task_runner.AddCallbackOnFail( + [&success]([[maybe_unused]] InPlaceCompilerContext &compiler_ctx) { success = false; }); + auto arch = ChooseArch(Arch::NONE); + bool is_dynamic = panda::panda_file::IsDynamicLanguage(ctx->method->GetClass()->GetSourceLang()); + compiler::CompileInGraph(runtime_, is_dynamic, arch, std::move(task_runner)); + return success; +} + /** * Compiles a method in JIT mode (i.e. no code generated). * @return `false` on error. @@ -676,10 +697,7 @@ bool Paoc::CompileJit(CompilingContext *ctx) ASSERT(ctx != nullptr); ASSERT(mode_ == PaocMode::JIT); auto name = runtime_->GetMethodFullName(ctx->method, false); - auto arch {Arch::NONE}; - bool is_dynamic = panda::panda_file::IsDynamicLanguage(ctx->method->GetClass()->GetSourceLang()); - if (!compiler::CompileInGraph(runtime_, ctx->method, false, &ctx->allocator, &ctx->graph_local_allocator, - is_dynamic, &arch, name, &ctx->graph)) { + if (!CompileInGraph(ctx, name, false)) { std::string err_msg = "Failed to JIT-compile method: " + name; PrintError(err_msg); return false; @@ -696,12 +714,9 @@ bool Paoc::CompileOsr(CompilingContext *ctx) { ASSERT(ctx != nullptr); ASSERT(mode_ == PaocMode::OSR); - std::string name; - auto arch {Arch::NONE}; - bool is_dynamic = panda::panda_file::IsDynamicLanguage(ctx->method->GetClass()->GetSourceLang()); - if (!compiler::CompileInGraph(runtime_, ctx->method, true, &ctx->allocator, &ctx->graph_local_allocator, is_dynamic, - &arch, name, &ctx->graph)) { - std::string err_msg = "Failed to OSR-compile method: " + runtime_->GetMethodFullName(ctx->method, false); + auto name = runtime_->GetMethodFullName(ctx->method, false); + if (!CompileInGraph(ctx, name, true)) { + std::string err_msg = "Failed to OSR-compile method: " + name; PrintError(err_msg); return false; } @@ -773,7 +788,14 @@ bool Paoc::CompileAot(CompilingContext *ctx) LOG_PAOC(INFO) << "LLVM fallback to ARK AOT on method: " << runtime_->GetMethodFullName(ctx->method, false); } - if (!RunOptimizations(ctx->graph)) { + compiler::InPlaceCompilerTaskRunner task_runner; + task_runner.GetContext().SetGraph(ctx->graph); + bool success = true; + task_runner.AddCallbackOnFail( + [&success]([[maybe_unused]] InPlaceCompilerContext &compiler_ctx) { success = false; }); + RunOptimizations(std::move(task_runner)); + + if (!success) { PrintError("RunOptimizations failed!"); return false; } diff --git a/compiler/tools/paoc/paoc.h b/compiler/tools/paoc/paoc.h index dcae1e20008c34a2526048150c71b588dcf00ddb..aeaeab527d3a80ee99fbe9d28f1112459808a14f 100644 --- a/compiler/tools/paoc/paoc.h +++ b/compiler/tools/paoc/paoc.h @@ -78,6 +78,7 @@ private: bool Compile(Class *klass, const panda_file::File &pfile_ref); bool Compile(Method *method, size_t method_index); + bool CompileInGraph(CompilingContext *ctx, std::string method_name, bool is_osr); bool CompileJit(CompilingContext *ctx); bool CompileOsr(CompilingContext *ctx); bool CompileAot(CompilingContext *ctx); diff --git a/irtoc/backend/function.cpp b/irtoc/backend/function.cpp index 2923df0230d0f543087137e67cbd45cd596c3ec2..013adcc389e117ec4b61b5a16f5bd775566b8076 100644 --- a/irtoc/backend/function.cpp +++ b/irtoc/backend/function.cpp @@ -82,7 +82,13 @@ Function::Result Function::Compile(Arch arch, ArenaAllocator *allocator, ArenaAl bool has_llvm_suffix = std::string_view(GetName()).find(LLVM_SUFFIX) != std::string_view::npos; if (GetGraph()->GetMode().IsNative()) { - if (!RunOptimizations(GetGraph())) { + compiler::InPlaceCompilerTaskRunner task_runner; + task_runner.GetContext().SetGraph(GetGraph()); + bool success = true; + task_runner.AddCallbackOnFail( + [&success]([[maybe_unused]] compiler::InPlaceCompilerContext &compiler_ctx) { success = false; }); + compiler::RunOptimizations(std::move(task_runner)); + if (!success) { return Unexpected("RunOptimizations failed!"); } llvm_compilation_result_ = LLVMCompilationResult::USE_ARK_AS_NO_SUFFIX; diff --git a/libpandabase/mem/arena_allocator.h b/libpandabase/mem/arena_allocator.h index e5f75c3944cbc28e3f979922c7f071251824fa8d..abbc0eeea2147ab26958df47a244a609b3df63fa 100644 --- a/libpandabase/mem/arena_allocator.h +++ b/libpandabase/mem/arena_allocator.h @@ -65,10 +65,8 @@ public: bool limit_alloc_size_by_pool = false); PANDA_PUBLIC_API ~ArenaAllocatorT(); - ArenaAllocatorT(const ArenaAllocatorT &) = delete; - ArenaAllocatorT(ArenaAllocatorT &&) = default; - ArenaAllocatorT &operator=(const ArenaAllocatorT &) = delete; - ArenaAllocatorT &operator=(ArenaAllocatorT &&) = default; + NO_COPY_SEMANTIC(ArenaAllocatorT); + NO_MOVE_SEMANTIC(ArenaAllocatorT); [[nodiscard]] PANDA_PUBLIC_API void *Alloc(size_t size, Alignment align = DEFAULT_ARENA_ALIGNMENT); diff --git a/libpandabase/task_runner.h b/libpandabase/task_runner.h new file mode 100644 index 0000000000000000000000000000000000000000..b20f003597c8217d122ad6ef1824129c4d998cdd --- /dev/null +++ b/libpandabase/task_runner.h @@ -0,0 +1,302 @@ +/** + * 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. + */ + +#ifndef LIBPANDABASE_PANDA_TASK_RUNNER_H_ +#define LIBPANDABASE_PANDA_TASK_RUNNER_H_ + +#include "libpandabase/macros.h" + +#include +#include + +// clang-format off +/** +* panda::TaskRunner task_runner; +* +* ------------------------------------------------------------------------- +* || | +* \/ | +* task_runner.AddCallbackOnSuccess(foo1); >--------------- | +* | | +* ----------------------------------------- | | +* | || | | +* | \/ | | +* | task_runner.AddCallbackOnFail(foo2); >-------------- | | +* | | | | +* | | | | +* | ------------------------------------------------------------------ | +* | || | | +* | \/ | | +* | task_runner.SetTaskOnSuccess(next _task); | | +* | | | +* | ----------------------------------------------------------------|----------|-------- +* | || | | | +* | \/ | | | +* | task_runner.AddCallbackOnFail(foo3); | | | +* | || | | | +* | \/ | | | +* ---------------------------------------- | | | +* | | | +* task_runner.EndTask(task); -----------------> if task() ----------- | | | +* | | | | | +* | | | | | +* | | | | | +* | if has_task_on_success ------ | | +* | | | | +* | | | | +* | | | | +* | else ------------- X ------------------- | +* | | +* | | +* | | +* | | +* else ----------- if has_task_on_fail ------- X | +* | | +* | | +* | | +* | | +* | | +* else ---------------------------------------------- +*/ +// clang-format on + +namespace panda { + +template +class TaskRunner { +public: + TaskRunner() = default; + NO_COPY_SEMANTIC(TaskRunner); + DEFAULT_MOVE_SEMANTIC(TaskRunner); + ~TaskRunner() = default; + + // We must use && to avoid slicing + using NextTaskRunner = std::function; + using Callback = std::function; + + virtual ContextT &GetContext() = 0; + virtual void StartTask(NextTaskRunner next_task) && = 0; + + class TaskCallback { + public: + TaskCallback() = default; + NO_COPY_SEMANTIC(TaskCallback); + NO_MOVE_OPERATOR(TaskCallback); + TaskCallback(TaskCallback &&task_cb) + : callback_on_success_(std::move(task_cb.callback_on_success_)), + callback_on_fail_(std::move(task_cb.callback_on_fail_)) + { + task_cb.SetNeedMakeCall(false); + } + // NOLINTNEXTLINE(modernize-use-equals-default) + ~TaskCallback() + { +#ifndef NDEBUG + ASSERT(!need_make_call_); +#endif + } + + void RunOnSuccess(ContextT &task_ctx) + { + if (callback_on_success_) { + callback_on_success_(task_ctx); + } + SetNeedMakeCall(false); + } + + void RunOnFail(ContextT &task_ctx) + { + if (callback_on_fail_) { + callback_on_fail_(task_ctx); + } + SetNeedMakeCall(false); + } + + template + void AddOnSuccess(Foo foo) + { + if (callback_on_success_) { + callback_on_success_ = NextCallback(std::move(callback_on_success_), std::move(foo)); + } else { + callback_on_success_ = std::move(foo); + } + SetNeedMakeCall(true); + } + + template + void AddOnFail(Foo foo) + { + if (callback_on_fail_) { + callback_on_fail_ = NextCallback(std::move(callback_on_fail_), std::move(foo)); + } else { + callback_on_fail_ = std::move(foo); + } + SetNeedMakeCall(true); + } + + void SetNeedMakeCall([[maybe_unused]] bool need_make_call) + { +#ifndef NDEBUG + need_make_call_ = need_make_call; +#endif + } + + private: + template + Callback NextCallback(Callback &&cb, Foo foo) + { + return [cb = std::move(cb), foo = std::move(foo)](ContextT &task_ctx) mutable { + foo(task_ctx); + cb(task_ctx); + }; + } + + Callback callback_on_success_; + Callback callback_on_fail_; +#ifndef NDEBUG + bool need_make_call_ {false}; +#endif + }; + + class NextTask { + public: + NextTask() = default; + NO_COPY_SEMANTIC(NextTask); + NO_MOVE_OPERATOR(NextTask); + NextTask(NextTask &&next_task) + : task_runner_(std::move(next_task.task_runner_)), has_runner_(next_task.has_runner_) + { + next_task.has_runner_ = false; + } + ~NextTask() = default; + + explicit operator bool() const + { + return has_runner_; + } + + template + void SetTaskRunner(Foo foo) + { + task_runner_ = std::move(foo); + has_runner_ = true; + } + + NextTaskRunner GetTaskRunner() + { + has_runner_ = false; + return std::move(task_runner_); + } + + private: + NextTaskRunner task_runner_; + bool has_runner_ {false}; + }; + + void RunCallbackOnSuccess() + { + auto &context = GetContext(); + current_task_cb_.RunOnSuccess(context); + task_cb_.RunOnSuccess(context); + } + + void RunCallbackOnFail() + { + auto &context = GetContext(); + current_task_cb_.RunOnFail(context); + task_cb_.RunOnFail(context); + } + + template + void AddCallbackOnSuccess(Foo foo) + { + ASSERT(!task_on_success_); + if (task_on_fail_) { + current_task_cb_.AddOnSuccess(std::move(foo)); + } else { + task_cb_.AddOnSuccess(std::move(foo)); + } + } + + template + void AddCallbackOnFail(Foo foo) + { + ASSERT(!task_on_fail_); + if (task_on_success_) { + current_task_cb_.AddOnFail(std::move(foo)); + } else { + task_cb_.AddOnFail(std::move(foo)); + } + } + + template + void AddFinalize(Foo foo) + { + AddCallbackOnSuccess(foo); + AddCallbackOnFail(std::move(foo)); + } + + template + void SetTaskOnSuccess(Foo foo) + { + ASSERT(!task_on_success_); + task_on_success_.SetTaskRunner(std::move(foo)); + } + + template + void SetTaskOnFail(Foo foo) + { + ASSERT(!task_on_success_); + task_on_fail_.SetTaskRunner(std::move(foo)); + } + + template + void EndTask(Foo foo) && + { + auto task_on_success = std::move(task_on_success_); + auto task_on_fail = std::move(task_on_fail_); + auto current_task_cb = std::move(current_task_cb_); + ASSERT(!task_on_success_ && !task_on_fail_); + ContextT &task_ctx = GetContext(); + bool success = foo(task_ctx); + if (success) { + if (task_on_success) { + current_task_cb.SetNeedMakeCall(false); + std::move(*this).StartTask(task_on_success.GetTaskRunner()); + } else { + current_task_cb.RunOnSuccess(task_ctx); + task_cb_.RunOnSuccess(task_ctx); + } + } else { + if (task_on_fail) { + current_task_cb.SetNeedMakeCall(false); + std::move(*this).StartTask(task_on_fail.GetTaskRunner()); + } else { + current_task_cb.RunOnFail(task_ctx); + task_cb_.RunOnFail(task_ctx); + } + } + } + +private: + TaskCallback task_cb_; + TaskCallback current_task_cb_; + NextTask task_on_success_; + NextTask task_on_fail_; +}; + +} // namespace panda + +#endif // LIBPANDABASE_PANDA_TASK_RUNNER_H_ diff --git a/plugins/ets/tests/napi/sampler/CMakeLists.txt b/plugins/ets/tests/napi/sampler/CMakeLists.txt index 63cc6cc9c06a3db55809e5021d0e68df913aa1fa..1206c92dfad0c63b3d16e43077d5325cd1ba8b81 100644 --- a/plugins/ets/tests/napi/sampler/CMakeLists.txt +++ b/plugins/ets/tests/napi/sampler/CMakeLists.txt @@ -69,6 +69,10 @@ add_dependencies(sampler_etsnapi_es2panda sampler_etsnapi_lib) set(ETS_ENTRY_POINT "main") if (CMAKE_BUILD_TYPE STREQUAL "Release" OR CMAKE_BUILD_TYPE STREQUAL "FastVerify") set(ETS_ENTRY_POINT "release_main") + set(RUNTIME_ARGUMENTS + ${RUNTIME_ARGUMENTS} + --ets.workers-type=threadpool + ) endif() diff --git a/plugins/ets/tests/runtime/tooling/sampler/CMakeLists.txt b/plugins/ets/tests/runtime/tooling/sampler/CMakeLists.txt index 53ca0e74d809e4c4dcd89344f4f019b47001d5ee..17fdc73d8b5234d6f0dc98b71419ec03e88f7044 100644 --- a/plugins/ets/tests/runtime/tooling/sampler/CMakeLists.txt +++ b/plugins/ets/tests/runtime/tooling/sampler/CMakeLists.txt @@ -58,6 +58,10 @@ add_custom_target(sampler_es2panda set(ETS_ENTRY_POINT "main") if (CMAKE_BUILD_TYPE STREQUAL "Release") set(ETS_ENTRY_POINT "release_main") + set(RUNTIME_ARGUMENTS + ${RUNTIME_ARGUMENTS} + --ets.workers-type=threadpool + ) endif() foreach(c_arg ${COMPILER_ARGUMENTS_LIST}) diff --git a/runtime/compiler.cpp b/runtime/compiler.cpp index 29c26661ffad101468bfc925011d9fdcb47f5a70..fb04473baf850b310eef8db61f44107e08c246aa 100644 --- a/runtime/compiler.cpp +++ b/runtime/compiler.cpp @@ -27,6 +27,8 @@ #include "runtime/include/thread.h" #include "runtime/include/coretypes/native_pointer.h" #include "runtime/mem/heap_manager.h" +#include "compiler/inplace_task_runner.h" +#include "compiler/background_task_runner.h" namespace panda { @@ -42,10 +44,9 @@ class ErrorHandler : public ClassLinkerErrorHandler { void OnError([[maybe_unused]] ClassLinker::Error error, [[maybe_unused]] const PandaString &message) override {} }; -bool Compiler::IsCompilationExpired(const CompilerTask &ctx) +bool Compiler::IsCompilationExpired(Method *method, bool is_osr) { - return (ctx.IsOsr() && GetOsrCode(ctx.GetMethod()) != nullptr) || - (!ctx.IsOsr() && ctx.GetMethod()->HasCompiledCode()); + return (is_osr && GetOsrCode(method) != nullptr) || (!is_osr && method->HasCompiledCode()); } /// Intrinsics fast paths are supported only for G1 GC. @@ -775,48 +776,42 @@ bool Compiler::CompileMethod(Method *method, uintptr_t bytecode_offset, bool osr return false; } -void Compiler::CompileMethodLocked(const CompilerTask &&ctx) +template +void Compiler::CompileMethodLocked(compiler::CompilerTaskRunner task_runner) { os::memory::LockHolder lock(compilation_lock_); - StartCompileMethod(std::move(ctx)); + StartCompileMethod(std::move(task_runner)); } -void Compiler::StartCompileMethod(const CompilerTask &&ctx) +template +void Compiler::StartCompileMethod(compiler::CompilerTaskRunner task_runner) { ASSERT(runtime_iface_ != nullptr); - auto method = ctx.GetMethod(); + auto &task_ctx = task_runner.GetContext(); + auto *method = task_ctx.GetMethod(); method->ResetHotnessCounter(); - if (IsCompilationExpired(ctx)) { + if (IsCompilationExpired(method, task_ctx.IsOsr())) { ASSERT(!no_async_jit_); - return; - } - - PandaVM *vm = ctx.GetVM(); - - // Set current thread to have access to vm during compilation - Thread compiler_thread(vm, Thread::ThreadType::THREAD_TYPE_COMPILER); - ScopedCurrentThread sct(&compiler_thread); - - auto is_osr = ctx.IsOsr(); - mem::MemStatsType *mem_stats = vm->GetMemStats(); - panda::ArenaAllocator allocator(panda::SpaceType::SPACE_TYPE_COMPILER, mem_stats); - panda::ArenaAllocator graph_local_allocator(panda::SpaceType::SPACE_TYPE_COMPILER, mem_stats, true); - if (!compiler::JITCompileMethod(runtime_iface_, method, ctx.IsOsr(), code_allocator_, &allocator, - &graph_local_allocator, &gdb_debug_info_allocator_, jit_stats_)) { - // If deoptimization occurred during OSR compilation, the compilation returns false. - // For the case we need reset compiation status - if (is_osr) { - method->SetCompilationStatus(Method::NOT_COMPILED); - return; - } - // Failure during compilation, should we retry later? - method->SetCompilationStatus(Method::FAILED); - return; + return task_runner.RunCallbackOnFail(); + } + + mem::MemStatsType *mem_stats = task_ctx.GetVM()->GetMemStats(); + + auto allocator = std::make_unique(panda::SpaceType::SPACE_TYPE_COMPILER, mem_stats); + auto local_allocator = + std::make_unique(panda::SpaceType::SPACE_TYPE_COMPILER, mem_stats, true); + + if constexpr (RUNNER_MODE == compiler::BACKGROUND_MODE) { + task_ctx.SetAllocator(std::move(allocator)); + task_ctx.SetLocalAllocator(std::move(local_allocator)); + } else { + task_ctx.SetAllocator(allocator.get()); + task_ctx.SetLocalAllocator(local_allocator.get()); } - // Check that method was not deoptimized - method->AtomicSetCompilationStatus(Method::COMPILATION, Method::COMPILED); + compiler::JITCompileMethod(runtime_iface_, code_allocator_, &gdb_debug_info_allocator_, jit_stats_, + std::move(task_runner)); } void Compiler::JoinWorker() @@ -890,4 +885,13 @@ uint8_t CompileMethodImpl(coretypes::String *full_method_name, panda_file::Sourc } #endif // PANDA_PRODUCT_BUILD +template void Compiler::CompileMethodLocked( + compiler::CompilerTaskRunner); +template void Compiler::CompileMethodLocked( + compiler::CompilerTaskRunner); +template void Compiler::StartCompileMethod( + compiler::CompilerTaskRunner); +template void Compiler::StartCompileMethod( + compiler::CompilerTaskRunner); + } // namespace panda diff --git a/runtime/compiler.h b/runtime/compiler.h index 2b16398e7ccd82bdc21d50f9ec052daae83b8976..388b2be76577f27eb67dabe5fcd1948e26c85275 100644 --- a/runtime/compiler.h +++ b/runtime/compiler.h @@ -16,6 +16,7 @@ #define PANDA_RUNTIME_COMPILER_H_ #include "compiler/compile_method.h" +#include "compiler/compiler_task_runner.h" #include "compiler/optimizer/ir/runtime_interface.h" #include "libpandabase/mem/code_allocator.h" #include "libpandabase/os/mutex.h" @@ -658,7 +659,7 @@ public: void JoinWorker() override; - bool IsCompilationExpired(const CompilerTask &ctx); + bool IsCompilationExpired(Method *method, bool is_osr); ~Compiler() override { @@ -678,10 +679,12 @@ public: compiler_worker_->AddTask(std::move(ctx)); } - void CompileMethodLocked(const CompilerTask &&ctx); + template + void CompileMethodLocked(compiler::CompilerTaskRunner task_runner); /// Basic method, which starts compilation. Do not use. - void StartCompileMethod(const CompilerTask &&ctx); + template + void StartCompileMethod(compiler::CompilerTaskRunner task_runner); void ScaleThreadPool(size_t number_of_threads) { diff --git a/runtime/compiler_task_manager_worker.cpp b/runtime/compiler_task_manager_worker.cpp index aa3561edc73b23b77447ddb4e849193d24f4fadc..ec4af441a87b030cc727a5ac8f2f181196765f17 100644 --- a/runtime/compiler_task_manager_worker.cpp +++ b/runtime/compiler_task_manager_worker.cpp @@ -15,6 +15,8 @@ #include "runtime/compiler.h" #include "runtime/compiler_task_manager_worker.h" +#include "compiler/background_task_runner.h" +#include "compiler/compiler_task_runner.h" namespace panda { @@ -38,57 +40,73 @@ void CompilerTaskManagerWorker::JoinWorker() void CompilerTaskManagerWorker::AddTask(CompilerTask &&task) { - bool start_compile = false; { os::memory::LockHolder lock(task_queue_lock_); if (compiler_worker_joined_) { return; } - start_compile = compiler_task_deque_.empty(); - if (start_compile) { + if (compiler_task_deque_.empty()) { CompilerTask empty_task; compiler_task_deque_.emplace_back(std::move(empty_task)); } else { compiler_task_deque_.emplace_back(std::move(task)); + return; } } // This means that this is first task in queue, so we can compile it in-place. - if (start_compile) { - // NOLINTNEXTLINE(bugprone-use-after-move,clang-analyzer-cplusplus.Move) - AddTaskInTaskManager(std::move(task)); - } + BackgroundCompileMethod(std::move(task)); } -void CompilerTaskManagerWorker::AddTaskInTaskManager(CompilerTask &&ctx) +void CompilerTaskManagerWorker::BackgroundCompileMethod(CompilerTask &&ctx) { - auto *ctx_ptr = internal_allocator_->New(std::move(ctx)); - auto task_runner = [this, ctx_ptr] { - if (ctx_ptr->GetMethod()->AtomicSetCompilationStatus(Method::WAITING, Method::COMPILATION)) { - compiler_->StartCompileMethod(std::move(*ctx_ptr)); - } - internal_allocator_->Delete(ctx_ptr); + auto thread_deleter = [this](Thread *thread) { internal_allocator_->Delete(thread); }; + std::unique_ptr compiler_thread( + internal_allocator_->New(ctx.GetVM(), Thread::ThreadType::THREAD_TYPE_COMPILER), + std::move(thread_deleter)); + + auto set_current_thread = [](Thread *thread) { + ASSERT(Thread::GetCurrent() != thread); + Thread::SetCurrent(thread); + }; + + compiler::BackgroundCompilerTaskRunner task_runner(compiler_task_manager_queue_, compiler_thread.get(), + std::move(set_current_thread)); + auto &compiler_ctx = task_runner.GetContext(); + compiler_ctx.SetCompilerThread(std::move(compiler_thread)); + + auto task_deleter = [this](CompilerTask *task) { internal_allocator_->Delete(task); }; + std::unique_ptr compiler_task( + internal_allocator_->New(std::move(ctx)), std::move(task_deleter)); + + compiler_ctx.SetCompilerTask(std::move(compiler_task)); + + // Callback to compile next method from compiler_task_deque_ + task_runner.AddFinalize([this]([[maybe_unused]] compiler::BackgroundCompilerContext &task_context) { + CompilerTask next_task; { os::memory::LockHolder lock(task_queue_lock_); ASSERT(!compiler_task_deque_.empty()); // compilation of current task is complete compiler_task_deque_.pop_front(); - CompileNextMethod(); + if (compiler_task_deque_.empty()) { + if (compiler_worker_joined_) { + compiler_tasks_processed_.Signal(); + } + return; + } + // now queue has empty task which will be popped at the end of compilation + next_task = std::move(compiler_task_deque_.front()); } - }; - compiler_task_manager_queue_->AddTask(taskmanager::Task::Create(JIT_TASK_PROPERTIES, std::move(task_runner))); -} + BackgroundCompileMethod(std::move(next_task)); + }); -void CompilerTaskManagerWorker::CompileNextMethod() -{ - if (!compiler_task_deque_.empty()) { - // now queue has empty task which will be popped at the end of compilation - auto next_task = std::move(compiler_task_deque_.front()); - AddTaskInTaskManager(std::move(next_task)); - return; - } - if (compiler_worker_joined_) { - compiler_tasks_processed_.Signal(); - } + std::move(task_runner).StartTask([this](compiler::BackgroundCompilerTaskRunner &&runner) { + if (runner.GetContext().GetMethod()->AtomicSetCompilationStatus(Method::WAITING, Method::COMPILATION)) { + compiler_->StartCompileMethod(std::move(runner)); + } else { + runner.RunCallbackOnFail(); + } + }); } } // namespace panda diff --git a/runtime/compiler_task_manager_worker.h b/runtime/compiler_task_manager_worker.h index add06e5eddbaa9bc2b46d8ae6f1a6f42301a20fa..07b09766fda826d483727a63512d372c3410f7e3 100644 --- a/runtime/compiler_task_manager_worker.h +++ b/runtime/compiler_task_manager_worker.h @@ -64,8 +64,7 @@ public: } private: - void AddTaskInTaskManager(CompilerTask &&ctx); - void CompileNextMethod() REQUIRES(task_queue_lock_); + void BackgroundCompileMethod(CompilerTask &&ctx); taskmanager::TaskQueue *compiler_task_manager_queue_ {nullptr}; os::memory::Mutex task_queue_lock_; diff --git a/runtime/compiler_thread_pool_worker.cpp b/runtime/compiler_thread_pool_worker.cpp index e3cda32d9303d58151ea44779ca3ba198e67e047..fbc4b23ea3125e6cc82fd749dfa30c6134542131 100644 --- a/runtime/compiler_thread_pool_worker.cpp +++ b/runtime/compiler_thread_pool_worker.cpp @@ -17,6 +17,7 @@ #include "runtime/compiler_thread_pool_worker.h" #include "runtime/compiler_queue_simple.h" #include "runtime/compiler_queue_aged_counter_priority.h" +#include "compiler/inplace_task_runner.h" namespace panda { @@ -61,17 +62,27 @@ CompilerQueueInterface *CompilerThreadPoolWorker::CreateJITTaskQueue(const std:: return nullptr; } -CompilerProcessor::CompilerProcessor(Compiler *compiler) +bool CompilerProcessor::Process(CompilerTask &&task) { - compiler_ = compiler; + InPlaceCompileMethod(std::move(task)); + return true; } -bool CompilerProcessor::Process(CompilerTask &&task) +void CompilerProcessor::InPlaceCompileMethod(CompilerTask &&ctx) { - if (task.GetMethod()->AtomicSetCompilationStatus(Method::WAITING, Method::COMPILATION)) { - compiler_->CompileMethodLocked(std::move(task)); + compiler::InPlaceCompilerTaskRunner task_runner; + auto &compiler_ctx = task_runner.GetContext(); + compiler_ctx.SetMethod(ctx.GetMethod()); + compiler_ctx.SetOsr(ctx.IsOsr()); + compiler_ctx.SetVM(ctx.GetVM()); + + // Set current thread to have access to vm during compilation + Thread compiler_thread(ctx.GetVM(), Thread::ThreadType::THREAD_TYPE_COMPILER); + ScopedCurrentThread sct(&compiler_thread); + + if (compiler_ctx.GetMethod()->AtomicSetCompilationStatus(Method::WAITING, Method::COMPILATION)) { + compiler_->CompileMethodLocked(std::move(task_runner)); } - return true; } } // namespace panda diff --git a/runtime/compiler_thread_pool_worker.h b/runtime/compiler_thread_pool_worker.h index fbcb4a5a1ec68488981c95d094ab6582284720ec..7e7bf0e3cdc3ec7ad6d0a517008b6fb02c8a1f62 100644 --- a/runtime/compiler_thread_pool_worker.h +++ b/runtime/compiler_thread_pool_worker.h @@ -23,10 +23,11 @@ namespace panda { class CompilerProcessor : public ProcessorInterface { public: - explicit CompilerProcessor(Compiler *compiler); + explicit CompilerProcessor(Compiler *compiler) : compiler_(compiler) {} bool Process(CompilerTask &&task) override; private: + void InPlaceCompileMethod(CompilerTask &&ctx); Compiler *compiler_; };