/* * Copyright (c) 2021-2024 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 "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" #include "optimizer/code_generator/method_properties.h" #include "optimizer/ir/graph.h" #include "optimizer/ir/visualizer_printer.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/escape.h" #include "optimizer/optimizations/if_conversion.h" #include "optimizer/optimizations/inlining.h" #include "optimizer/optimizations/licm.h" #include "optimizer/optimizations/licm_conditions.h" #include "optimizer/optimizations/loop_idioms.h" #include "optimizer/optimizations/loop_peeling.h" #include "optimizer/optimizations/loop_unswitch.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/optimize_string_concat.h" #include "optimizer/optimizations/peepholes.h" #include "optimizer/optimizations/phi_type_resolving.h" #include "optimizer/optimizations/redundant_loop_elimination.h" #include "optimizer/optimizations/regalloc/reg_alloc.h" #include "optimizer/optimizations/reserve_string_builder_buffer.h" #include "optimizer/optimizations/savestate_optimization.h" #include "optimizer/optimizations/scheduler.h" #include "optimizer/optimizations/simplify_string_builder.h" #include "optimizer/optimizations/try_catch_resolving.h" #include "optimizer/optimizations/inline_intrinsics.h" #include "optimizer/optimizations/vn.h" #include "optimizer/optimizations/cse.h" #include "optimizer/optimizations/move_constants.h" #include "optimizer/optimizations/adjust_arefs.h" #include "optimizer/optimizations/if_merging.h" #include "compiler/generated/pipeline_includes.h" namespace ark::compiler { std::unique_ptr Pipeline::Create(Graph *graph) { // CC-OFFNXT(C_RULE_SWITCH_BRANCH_CHECKER) autogenerated code switch (graph->GetLanguage()) { #include "compiler/generated/create_pipeline.inl" default: return std::make_unique(graph); } } static inline bool RunCodegenPass(Graph *graph) { if (graph->GetMethodProperties().GetRequireFrameSetup()) { return graph->RunPass(); } return graph->RunPass(); } /* static */ template void Pipeline::Run(CompilerTaskRunner taskRunner) { auto pipeline = taskRunner.GetContext().GetPipeline(); auto *graph = pipeline->GetGraph(); #if !defined(NDEBUG) && !defined(PANDA_TARGET_MOBILE) if (g_options.IsCompilerVisualizerDump()) { graph->GetPassManager()->InitialDumpVisualizerGraph(); } #endif // NDEBUG && PANDA_TARGET_MOBILE taskRunner.AddFinalize( [](CompilerContext &compilerCtx) { compilerCtx.GetGraph()->GetPassManager()->Finalize(); }); if (g_options.WasSetCompilerRegallocRegMask()) { COMPILER_LOG(DEBUG, REGALLOC) << "Regalloc mask force set to " << std::hex << g_options.GetCompilerRegallocRegMask() << "\n"; graph->SetArchUsedRegs(g_options.GetCompilerRegallocRegMask()); } if (!g_options.IsCompilerNonOptimizing()) { taskRunner.SetTaskOnSuccess([](CompilerTaskRunner nextRunner) { Pipeline::RunRegAllocAndCodeGenPass(std::move(nextRunner)); }); bool success = pipeline->RunOptimizations(); CompilerTaskRunner::EndTask(std::move(taskRunner), success); return; } // 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->template RunPass(); if (!graph->template RunPass()) { LOG(WARNING, COMPILER) << "Compiler detected incorrect monitor policy"; CompilerTaskRunner::EndTask(std::move(taskRunner), false); return; } Pipeline::RunRegAllocAndCodeGenPass(std::move(taskRunner)); } /* static */ template void Pipeline::RunRegAllocAndCodeGenPass(CompilerTaskRunner taskRunner) { auto *graph = taskRunner.GetContext().GetPipeline()->GetGraph(); bool fatalOnErr = !g_options.IsCompilerAllowBackendFailures(); // Avoid spending too much time in RegAlloc: auto estimatedSize = graph->EstimateCodeSize(); if (estimatedSize > g_options.GetCompilerMaxGenCodeSize()) { if (fatalOnErr) { LOG(FATAL, COMPILER) << "RunOptimizations failed: predicted code size is too big (" << estimatedSize << ")"; } CompilerTaskRunner::EndTask(std::move(taskRunner), false); return; } graph->template RunPass(); taskRunner.SetTaskOnSuccess([fatalOnErr](CompilerTaskRunner nextRunner) { nextRunner.AddCallbackOnFail([fatalOnErr]([[maybe_unused]] CompilerContext &compilerCtx) { if (fatalOnErr) { LOG(FATAL, COMPILER) << "RunOptimizations failed: code generation error"; } }); bool success = RunCodegenPass(nextRunner.GetContext().GetPipeline()->GetGraph()); CompilerTaskRunner::EndTask(std::move(nextRunner), success); }); bool success = RegAlloc(graph); if (!success && fatalOnErr) { LOG(FATAL, COMPILER) << "RunOptimizations failed: register allocation error"; } CompilerTaskRunner::EndTask(std::move(taskRunner), success); } // CC-OFFNXT(huge_method, G.FUN.01) solid logic bool Pipeline::RunOptimizations() { auto graph = GetGraph(); /* peepholer and branch elimination have some parts that have * to be delayed up until loop unrolling is done, however, if * loop unrolling is not going to be run we don't have to delay */ if (!g_options.IsCompilerLoopUnroll()) { graph->SetUnrollComplete(); } graph->RunPass(); graph->RunPass(); graph->RunPass(); graph->RunPass(); // The problem with inlining in OSR mode can be found in `bitops-nsieve-bits` benchmark and it is in the // following: we inline the method that has user X within a loop, then peepholes optimize datflow and def of // the X become another instruction within inlined method, but SaveStateOsr didn't take it into account, thus, // we don't restore value of this new definition. // NOTE(msherstennikov): find way to inline in OSR mode if (!graph->IsOsrMode()) { graph->RunPass(); } graph->RunPass(); graph->RunPass(); if (!graph->RunPass()) { LOG(WARNING, COMPILER) << "Compiler detected incorrect monitor policy"; return false; } graph->RunPass(); graph->RunPass(); graph->RunPass(); graph->RunPass(); graph->RunPass(false); graph->RunPass(); if (graph->IsAotMode()) { graph->RunPass(); } if (graph->IsDynamicMethod()) { graph->RunPass(); graph->RunPass(); graph->RunPass(); graph->RunPass(); graph->RunPass(); graph->RunPass(false); } graph->RunPass(); graph->RunPass(g_options.GetCompilerLicmHoistLimit()); graph->RunPass(); graph->RunPass(); graph->RunPass(); graph->RunPass(g_options.GetCompilerLoopUnswitchMaxLevel(), g_options.GetCompilerLoopUnswitchMaxInsts()); graph->RunPass(); graph->RunPass(); if (graph->RunPass() && graph->RunPass()) { graph->RunPass(); graph->RunPass(); } graph->RunPass(); if (graph->IsAotMode()) { graph->RunPass(); } graph->RunPass(); graph->RunPass(); if (graph->RunPass()) { graph->RunPass(); } graph->RunPass(g_options.GetCompilerLoopUnrollInstLimit(), g_options.GetCompilerLoopUnrollFactor()); OptimizationsAfterUnroll(graph); graph->RunPass(); graph->RunPass(); graph->RunPass(); /* to be removed once generic loop unrolling is implemented */ ASSERT(graph->IsUnrollComplete()); graph->RunPass(); graph->RunPass(); graph->RunPass(); graph->RunPass(); if (graph->IsAotMode()) { graph->RunPass(); } graph->RunPass(); graph->RunPass(); #ifndef NDEBUG graph->SetLowLevelInstructionsEnabled(); #endif // NDEBUG graph->RunPass(false); graph->RunPass(); graph->RunPass(false); graph->RunPass(); graph->RunPass(g_options.IsCompilerMemoryCoalescingAligned()); graph->RunPass(g_options.GetCompilerIfConversionLimit()); graph->RunPass(); // Perform MoveConstants after Scheduler because Scheduler can rearrange constants // and cause spillfill in reg alloc graph->RunPass(); if (graph->RunPass()) { graph->RunPass(); graph->RunPass(false); } graph->RunPass(); return true; } template void Pipeline::Run(CompilerTaskRunner); template void Pipeline::Run(CompilerTaskRunner); template void Pipeline::RunRegAllocAndCodeGenPass(CompilerTaskRunner); template void Pipeline::RunRegAllocAndCodeGenPass(CompilerTaskRunner); } // namespace ark::compiler