• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2021-2025 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  * http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include "pipeline.h"
17 #include "compiler_options.h"
18 #include "inplace_task_runner.h"
19 #include "background_task_runner.h"
20 #include "compiler_task_runner.h"
21 
22 #include "optimizer/code_generator/codegen.h"
23 #include "optimizer/code_generator/codegen_native.h"
24 #include "optimizer/code_generator/method_properties.h"
25 #include "optimizer/ir/graph.h"
26 #include "optimizer/ir/visualizer_printer.h"
27 #include "optimizer/analysis/alias_analysis.h"
28 #include "optimizer/analysis/linear_order.h"
29 #include "optimizer/analysis/monitor_analysis.h"
30 #include "optimizer/analysis/rpo.h"
31 #include "optimizer/optimizations/balance_expressions.h"
32 #include "optimizer/optimizations/branch_elimination.h"
33 #include "optimizer/optimizations/checks_elimination.h"
34 #include "optimizer/optimizations/code_sink.h"
35 #include "optimizer/optimizations/deoptimize_elimination.h"
36 #include "optimizer/optimizations/cleanup.h"
37 #include "optimizer/optimizations/escape.h"
38 #include "optimizer/optimizations/if_conversion.h"
39 #include "optimizer/optimizations/inlining.h"
40 #include "optimizer/optimizations/licm.h"
41 #include "optimizer/optimizations/licm_conditions.h"
42 #include "optimizer/optimizations/loop_idioms.h"
43 #include "optimizer/optimizations/loop_peeling.h"
44 #include "optimizer/optimizations/loop_unswitch.h"
45 #include "optimizer/optimizations/loop_unroll.h"
46 #include "optimizer/optimizations/lower_boxed_boolean.h"
47 #include "optimizer/optimizations/lowering.h"
48 #include "optimizer/optimizations/lse.h"
49 #include "optimizer/optimizations/memory_barriers.h"
50 #include "optimizer/optimizations/memory_coalescing.h"
51 #include "optimizer/optimizations/native_call_optimization.h"
52 #include "optimizer/optimizations/optimize_string_concat.h"
53 #include "optimizer/optimizations/peepholes.h"
54 #include "optimizer/optimizations/phi_type_resolving.h"
55 #include "optimizer/optimizations/redundant_loop_elimination.h"
56 #include "optimizer/optimizations/regalloc/reg_alloc.h"
57 #include "optimizer/optimizations/reserve_string_builder_buffer.h"
58 #include "optimizer/optimizations/savestate_optimization.h"
59 #include "optimizer/optimizations/scheduler.h"
60 #include "optimizer/optimizations/simplify_string_builder.h"
61 #include "optimizer/optimizations/try_catch_resolving.h"
62 #include "optimizer/optimizations/inline_intrinsics.h"
63 #include "optimizer/optimizations/vn.h"
64 #include "optimizer/optimizations/cse.h"
65 #include "optimizer/optimizations/move_constants.h"
66 #include "optimizer/optimizations/adjust_arefs.h"
67 #include "optimizer/optimizations/if_merging.h"
68 
69 #include "compiler/generated/pipeline_includes.h"
70 
71 namespace ark::compiler {
72 
Create(Graph * graph)73 std::unique_ptr<Pipeline> Pipeline::Create(Graph *graph)
74 {
75     // CC-OFFNXT(C_RULE_SWITCH_BRANCH_CHECKER) autogenerated code
76     switch (graph->GetLanguage()) {
77 #include "compiler/generated/create_pipeline.inl"
78         default:
79             return std::make_unique<Pipeline>(graph);
80     }
81 }
82 
RunCodegenPass(Graph * graph)83 static inline bool RunCodegenPass(Graph *graph)
84 {
85     if (graph->GetMethodProperties().GetRequireFrameSetup()) {
86         return graph->RunPass<Codegen>();
87     }
88     return graph->RunPass<CodegenNative>();
89 }
90 
91 /* static */
92 template <TaskRunnerMode RUNNER_MODE>
Run(CompilerTaskRunner<RUNNER_MODE> taskRunner)93 void Pipeline::Run(CompilerTaskRunner<RUNNER_MODE> taskRunner)
94 {
95     auto pipeline = taskRunner.GetContext().GetPipeline();
96     auto *graph = pipeline->GetGraph();
97 #if !defined(NDEBUG) && !defined(PANDA_TARGET_MOBILE)
98     if (g_options.IsCompilerVisualizerDump()) {
99         graph->GetPassManager()->InitialDumpVisualizerGraph();
100     }
101 #endif  // NDEBUG && PANDA_TARGET_MOBILE
102 
103     taskRunner.AddFinalize(
104         [](CompilerContext<RUNNER_MODE> &compilerCtx) { compilerCtx.GetGraph()->GetPassManager()->Finalize(); });
105 
106     if (g_options.WasSetCompilerRegallocRegMask()) {
107         COMPILER_LOG(DEBUG, REGALLOC) << "Regalloc mask force set to " << std::hex
108                                       << g_options.GetCompilerRegallocRegMask() << "\n";
109         graph->SetArchUsedRegs(g_options.GetCompilerRegallocRegMask());
110     }
111 
112     if (!g_options.IsCompilerNonOptimizing()) {
113         taskRunner.SetTaskOnSuccess([](CompilerTaskRunner<RUNNER_MODE> nextRunner) {
114             Pipeline::RunRegAllocAndCodeGenPass<RUNNER_MODE>(std::move(nextRunner));
115         });
116         bool success = pipeline->RunOptimizations();
117         CompilerTaskRunner<RUNNER_MODE>::EndTask(std::move(taskRunner), success);
118         return;
119     }
120     // TryCatchResolving is needed in the non-optimizing mode since it removes unreachable for compiler
121     // catch-handlers; After supporting catch-handlers' compilation, this pass can be run in the optimizing mode
122     // only.
123     graph->template RunPass<TryCatchResolving>();
124     if (!graph->template RunPass<MonitorAnalysis>()) {
125         LOG(WARNING, COMPILER) << "Compiler detected incorrect monitor policy";
126         CompilerTaskRunner<RUNNER_MODE>::EndTask(std::move(taskRunner), false);
127         return;
128     }
129     Pipeline::RunRegAllocAndCodeGenPass<RUNNER_MODE>(std::move(taskRunner));
130 }
131 
132 /* static */
133 template <TaskRunnerMode RUNNER_MODE>
RunRegAllocAndCodeGenPass(CompilerTaskRunner<RUNNER_MODE> taskRunner)134 void Pipeline::RunRegAllocAndCodeGenPass(CompilerTaskRunner<RUNNER_MODE> taskRunner)
135 {
136     auto *graph = taskRunner.GetContext().GetPipeline()->GetGraph();
137     bool fatalOnErr = !g_options.IsCompilerAllowBackendFailures();
138 
139     // Avoid spending too much time in RegAlloc:
140     auto estimatedSize = graph->EstimateCodeSize();
141     if (estimatedSize > g_options.GetCompilerMaxGenCodeSize()) {
142         if (fatalOnErr) {
143             LOG(FATAL, COMPILER) << "RunOptimizations failed: predicted code size is too big (" << estimatedSize << ")";
144         }
145         CompilerTaskRunner<RUNNER_MODE>::EndTask(std::move(taskRunner), false);
146         return;
147     }
148     graph->template RunPass<Cleanup>();
149 
150     taskRunner.SetTaskOnSuccess([fatalOnErr](CompilerTaskRunner<RUNNER_MODE> nextRunner) {
151         nextRunner.AddCallbackOnFail([fatalOnErr]([[maybe_unused]] CompilerContext<RUNNER_MODE> &compilerCtx) {
152             if (fatalOnErr) {
153                 LOG(FATAL, COMPILER) << "RunOptimizations failed: code generation error";
154             }
155         });
156         bool success = RunCodegenPass(nextRunner.GetContext().GetPipeline()->GetGraph());
157         CompilerTaskRunner<RUNNER_MODE>::EndTask(std::move(nextRunner), success);
158     });
159     bool success = RegAlloc(graph);
160     if (!success && fatalOnErr) {
161         LOG(FATAL, COMPILER) << "RunOptimizations failed: register allocation error";
162     }
163     CompilerTaskRunner<RUNNER_MODE>::EndTask(std::move(taskRunner), success);
164 }
165 
166 // CC-OFFNXT(huge_method, G.FUN.01) solid logic
RunOptimizations()167 bool Pipeline::RunOptimizations()
168 {
169     auto graph = GetGraph();
170 
171     /* peepholer and branch elimination have some parts that have
172      * to be delayed up until loop unrolling is done, however, if
173      * loop unrolling is not going to be run we don't have to delay */
174     if (!g_options.IsCompilerLoopUnroll()) {
175         graph->SetUnrollComplete();
176     }
177     graph->RunPass<Peepholes>();
178     graph->RunPass<Cleanup>(false);
179     graph->RunPass<BranchElimination>();
180     graph->RunPass<OptimizeStringConcat>();
181     graph->RunPass<SimplifyStringBuilder>();
182 
183     // The problem with inlining in OSR mode can be found in `bitops-nsieve-bits` benchmark and it is in the
184     // following: we inline the method that has user X within a loop, then peepholes optimize datflow and def of
185     // the X become another instruction within inlined method, but SaveStateOsr didn't take it into account, thus,
186     // we don't restore value of this new definition.
187     // NOTE(msherstennikov): find way to inline in OSR mode
188     if (!graph->IsOsrMode()) {
189         graph->RunPass<Inlining>();
190     }
191     graph->RunPass<CatchInputs>();
192     graph->RunPass<TryCatchResolving>();
193     graph->RunPass<LowerBoxedBoolean>();
194     if (!graph->RunPass<MonitorAnalysis>()) {
195         LOG(WARNING, COMPILER) << "Compiler detected incorrect monitor policy";
196         return false;
197     }
198     graph->RunPass<NativeCallOptimization>();
199     graph->RunPass<Peepholes>();
200     graph->RunPass<BranchElimination>();
201     graph->RunPass<ValNum>();
202     graph->RunPass<IfMerging>();
203     graph->RunPass<Cleanup>(false);
204     graph->RunPass<Peepholes>();
205     if (graph->IsAotMode()) {
206         graph->RunPass<Cse>();
207     }
208     if (graph->IsDynamicMethod()) {
209         graph->RunPass<InlineIntrinsics>();
210         graph->RunPass<PhiTypeResolving>();
211         graph->RunPass<Peepholes>();
212         graph->RunPass<BranchElimination>();
213         graph->RunPass<ValNum>();
214         graph->RunPass<Cleanup>(false);
215     }
216     graph->RunPass<ChecksElimination>();
217     graph->RunPass<Licm>(g_options.GetCompilerLicmHoistLimit());
218     graph->RunPass<LicmConditions>();
219     graph->RunPass<RedundantLoopElimination>();
220     graph->RunPass<LoopPeeling>();
221     graph->RunPass<LoopUnswitch>(g_options.GetCompilerLoopUnswitchMaxLevel(),
222                                  g_options.GetCompilerLoopUnswitchMaxInsts());
223     graph->RunPass<Lse>();
224     graph->RunPass<ValNum>();
225     if (graph->RunPass<Peepholes>() && graph->RunPass<BranchElimination>()) {
226         graph->RunPass<Peepholes>();
227         graph->RunPass<ValNum>();
228     }
229     graph->RunPass<Cleanup>();
230     if (graph->IsAotMode()) {
231         graph->RunPass<Cse>();
232     }
233     graph->RunPass<ChecksElimination>();
234     if (graph->RunPass<DeoptimizeElimination>()) {
235         graph->RunPass<Peepholes>();
236     }
237     graph->RunPass<LoopIdioms>();
238     graph->RunPass<LoopUnroll>(g_options.GetCompilerLoopUnrollInstLimit(), g_options.GetCompilerLoopUnrollFactor());
239     OptimizationsAfterUnroll(graph);
240     graph->RunPass<Peepholes>();
241     graph->RunPass<EscapeAnalysis>();
242     graph->RunPass<ReserveStringBuilderBuffer>();
243 
244     /* to be removed once generic loop unrolling is implemented */
245     ASSERT(graph->IsUnrollComplete());
246 
247     graph->RunPass<Peepholes>();
248     graph->RunPass<BranchElimination>();
249     graph->RunPass<BalanceExpressions>();
250     graph->RunPass<ValNum>();
251     if (graph->IsAotMode()) {
252         graph->RunPass<Cse>();
253     }
254     graph->RunPass<SaveStateOptimization>();
255     graph->RunPass<Peepholes>();
256 #ifndef NDEBUG
257     graph->SetLowLevelInstructionsEnabled();
258 #endif  // NDEBUG
259     graph->RunPass<Cleanup>(false);
260     graph->RunPass<Lowering>();
261     graph->RunPass<Cleanup>(false);
262     graph->RunPass<CodeSink>();
263     graph->RunPass<MemoryCoalescing>(g_options.IsCompilerMemoryCoalescingAligned());
264     graph->RunPass<IfConversion>(g_options.GetCompilerIfConversionLimit());
265     graph->RunPass<Scheduler>();
266     // Perform MoveConstants after Scheduler because Scheduler can rearrange constants
267     // and cause spillfill in reg alloc
268     graph->RunPass<MoveConstants>();
269     if (graph->RunPass<AdjustRefs>()) {
270         graph->RunPass<ValNum>();
271         graph->RunPass<Cleanup>(false);
272     }
273     graph->RunPass<OptimizeMemoryBarriers>();
274 
275     return true;
276 }
277 
278 template void Pipeline::Run<BACKGROUND_MODE>(CompilerTaskRunner<BACKGROUND_MODE>);
279 template void Pipeline::Run<INPLACE_MODE>(CompilerTaskRunner<INPLACE_MODE>);
280 template void Pipeline::RunRegAllocAndCodeGenPass<BACKGROUND_MODE>(CompilerTaskRunner<BACKGROUND_MODE>);
281 template void Pipeline::RunRegAllocAndCodeGenPass<INPLACE_MODE>(CompilerTaskRunner<INPLACE_MODE>);
282 
283 }  // namespace ark::compiler
284