1 /*
2 * Copyright (c) 2021-2023 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/lowering.h"
47 #include "optimizer/optimizations/lse.h"
48 #include "optimizer/optimizations/memory_barriers.h"
49 #include "optimizer/optimizations/memory_coalescing.h"
50 #include "optimizer/optimizations/peepholes.h"
51 #include "optimizer/optimizations/phi_type_resolving.h"
52 #include "optimizer/optimizations/redundant_loop_elimination.h"
53 #include "optimizer/optimizations/regalloc/reg_alloc.h"
54 #include "optimizer/optimizations/scheduler.h"
55 #include "optimizer/optimizations/simplify_string_builder.h"
56 #include "optimizer/optimizations/try_catch_resolving.h"
57 #include "optimizer/optimizations/inline_intrinsics.h"
58 #include "optimizer/optimizations/vn.h"
59 #include "optimizer/optimizations/cse.h"
60 #include "optimizer/optimizations/move_constants.h"
61 #include "optimizer/optimizations/adjust_arefs.h"
62 #include "optimizer/optimizations/if_merging.h"
63
64 #include "plugins/create_pipeline_includes.h"
65
66 namespace panda::compiler {
67
Create(Graph * graph)68 std::unique_ptr<Pipeline> Pipeline::Create(Graph *graph)
69 {
70 switch (graph->GetLanguage()) {
71 #include "plugins/create_pipeline.h"
72 default:
73 return std::make_unique<Pipeline>(graph);
74 }
75 }
76
RunCodegenPass(Graph * graph)77 static inline bool RunCodegenPass(Graph *graph)
78 {
79 if (graph->GetMethodProperties().GetRequireFrameSetup()) {
80 return graph->RunPass<Codegen>();
81 }
82 return graph->RunPass<CodegenNative>();
83 }
84
85 /* static */
86 template <TaskRunnerMode RUNNER_MODE>
Run(CompilerTaskRunner<RUNNER_MODE> taskRunner)87 void Pipeline::Run(CompilerTaskRunner<RUNNER_MODE> taskRunner)
88 {
89 auto pipeline = taskRunner.GetContext().GetPipeline();
90 auto *graph = pipeline->GetGraph();
91 #if !defined(NDEBUG) && !defined(PANDA_TARGET_MOBILE)
92 if (g_options.IsCompilerVisualizerDump()) {
93 graph->GetPassManager()->InitialDumpVisualizerGraph();
94 }
95 #endif // NDEBUG && PANDA_TARGET_MOBILE
96
97 taskRunner.AddFinalize(
98 [](CompilerContext<RUNNER_MODE> &compilerCtx) { compilerCtx.GetGraph()->GetPassManager()->Finalize(); });
99
100 if (g_options.WasSetCompilerRegallocRegMask()) {
101 COMPILER_LOG(DEBUG, REGALLOC) << "Regalloc mask force set to " << std::hex
102 << g_options.GetCompilerRegallocRegMask() << "\n";
103 graph->SetArchUsedRegs(g_options.GetCompilerRegallocRegMask());
104 }
105
106 if (!g_options.IsCompilerNonOptimizing()) {
107 taskRunner.SetTaskOnSuccess([](CompilerTaskRunner<RUNNER_MODE> nextRunner) {
108 Pipeline::RunRegAllocAndCodeGenPass<RUNNER_MODE>(std::move(nextRunner));
109 });
110 bool success = pipeline->RunOptimizations();
111 CompilerTaskRunner<RUNNER_MODE>::EndTask(std::move(taskRunner), success);
112 return;
113 }
114 // TryCatchResolving is needed in the non-optimizing mode since it removes unreachable for compiler
115 // catch-handlers; After supporting catch-handlers' compilation, this pass can be run in the optimizing mode
116 // only.
117 graph->template RunPass<TryCatchResolving>();
118 if (!graph->template RunPass<MonitorAnalysis>()) {
119 LOG(WARNING, COMPILER) << "Compiler detected incorrect monitor policy";
120 CompilerTaskRunner<RUNNER_MODE>::EndTask(std::move(taskRunner), false);
121 return;
122 }
123 Pipeline::RunRegAllocAndCodeGenPass<RUNNER_MODE>(std::move(taskRunner));
124 }
125
126 /* static */
127 template <TaskRunnerMode RUNNER_MODE>
RunRegAllocAndCodeGenPass(CompilerTaskRunner<RUNNER_MODE> taskRunner)128 void Pipeline::RunRegAllocAndCodeGenPass(CompilerTaskRunner<RUNNER_MODE> taskRunner)
129 {
130 auto *graph = taskRunner.GetContext().GetPipeline()->GetGraph();
131 bool fatalOnErr = !g_options.IsCompilerAllowBackendFailures();
132 // Do not try to encode too large graph
133 auto instSize = graph->GetCurrentInstructionId();
134 auto instsPerByte = graph->GetEncoder()->MaxArchInstPerEncoded();
135 auto maxBitsInInst = GetInstructionSizeBits(graph->GetArch());
136 if ((instSize * instsPerByte * maxBitsInInst) > g_options.GetCompilerMaxGenCodeSize()) {
137 if (fatalOnErr) {
138 LOG(FATAL, COMPILER) << "RunOptimizations failed: code predicted size too big";
139 }
140 CompilerTaskRunner<RUNNER_MODE>::EndTask(std::move(taskRunner), false);
141 return;
142 }
143 graph->template RunPass<Cleanup>();
144
145 taskRunner.SetTaskOnSuccess([fatalOnErr](CompilerTaskRunner<RUNNER_MODE> nextRunner) {
146 nextRunner.AddCallbackOnFail([fatalOnErr]([[maybe_unused]] CompilerContext<RUNNER_MODE> &compilerCtx) {
147 if (fatalOnErr) {
148 LOG(FATAL, COMPILER) << "RunOptimizations failed: code generation error";
149 }
150 });
151 bool success = RunCodegenPass(nextRunner.GetContext().GetPipeline()->GetGraph());
152 CompilerTaskRunner<RUNNER_MODE>::EndTask(std::move(nextRunner), success);
153 });
154 bool success = RegAlloc(graph);
155 if (!success && fatalOnErr) {
156 LOG(FATAL, COMPILER) << "RunOptimizations failed: register allocation error";
157 }
158 CompilerTaskRunner<RUNNER_MODE>::EndTask(std::move(taskRunner), success);
159 }
160
RunOptimizations()161 bool Pipeline::RunOptimizations()
162 {
163 auto graph = GetGraph();
164
165 /* peepholer and branch elimination have some parts that have
166 * to be delayed up until loop unrolling is done, however, if
167 * loop unrolling is not going to be run we don't have to delay */
168 if (!g_options.IsCompilerLoopUnroll()) {
169 graph->SetUnrollComplete();
170 }
171 graph->RunPass<Peepholes>();
172 graph->RunPass<BranchElimination>();
173 graph->RunPass<SimplifyStringBuilder>();
174 graph->RunPass<Cleanup>();
175
176 // The problem with inlining in OSR mode can be found in `bitops-nsieve-bits` benchmark and it is in the
177 // following: we inline the method that has user X within a loop, then peepholes optimize datflow and def of
178 // the X become another instruction within inlined method, but SaveStateOsr didn't take it into account, thus,
179 // we don't restore value of this new definition.
180 // NOTE(msherstennikov): find way to inline in OSR mode
181 if (!graph->IsOsrMode()) {
182 graph->RunPass<Inlining>();
183 }
184 graph->RunPass<CatchInputs>();
185 graph->RunPass<TryCatchResolving>();
186 if (!graph->RunPass<MonitorAnalysis>()) {
187 LOG(WARNING, COMPILER) << "Compiler detected incorrect monitor policy";
188 return false;
189 }
190 graph->RunPass<Peepholes>();
191 graph->RunPass<BranchElimination>();
192 graph->RunPass<ValNum>();
193 graph->RunPass<IfMerging>();
194 graph->RunPass<Cleanup>(false);
195 graph->RunPass<Peepholes>();
196 if (graph->IsAotMode()) {
197 graph->RunPass<Cse>();
198 }
199 if (graph->IsDynamicMethod()) {
200 graph->RunPass<InlineIntrinsics>();
201 graph->RunPass<PhiTypeResolving>();
202 graph->RunPass<Peepholes>();
203 graph->RunPass<BranchElimination>();
204 graph->RunPass<ValNum>();
205 graph->RunPass<Cleanup>(false);
206 }
207 graph->RunPass<ChecksElimination>();
208 graph->RunPass<Licm>(g_options.GetCompilerLicmHoistLimit());
209 graph->RunPass<LicmConditions>();
210 graph->RunPass<RedundantLoopElimination>();
211 graph->RunPass<LoopPeeling>();
212 graph->RunPass<LoopUnswitch>(g_options.GetCompilerLoopUnswitchMaxLevel(),
213 g_options.GetCompilerLoopUnswitchMaxInsts());
214 graph->RunPass<Lse>();
215 graph->RunPass<ValNum>();
216 if (graph->RunPass<Peepholes>() && graph->RunPass<BranchElimination>()) {
217 graph->RunPass<Peepholes>();
218 graph->RunPass<ValNum>();
219 }
220 graph->RunPass<Cleanup>();
221 if (graph->IsAotMode()) {
222 graph->RunPass<Cse>();
223 }
224 graph->RunPass<EscapeAnalysis>();
225 graph->RunPass<LoopIdioms>();
226 graph->RunPass<ChecksElimination>();
227 graph->RunPass<LoopUnroll>(g_options.GetCompilerLoopUnrollInstLimit(), g_options.GetCompilerLoopUnrollFactor());
228 #include <plugins/optimizations_after_unroll.h>
229
230 /* to be removed once generic loop unrolling is implemented */
231 ASSERT(graph->IsUnrollComplete());
232
233 graph->RunPass<Peepholes>();
234 graph->RunPass<BranchElimination>();
235 graph->RunPass<BalanceExpressions>();
236 graph->RunPass<ValNum>();
237 if (graph->IsAotMode()) {
238 graph->RunPass<Cse>();
239 }
240 if (graph->RunPass<DeoptimizeElimination>()) {
241 graph->RunPass<Peepholes>();
242 }
243
244 #ifndef NDEBUG
245 graph->SetLowLevelInstructionsEnabled();
246 #endif // NDEBUG
247 graph->RunPass<Cleanup>(false);
248 graph->RunPass<Lowering>();
249 graph->RunPass<Cleanup>(false);
250 graph->RunPass<CodeSink>();
251 graph->RunPass<MemoryCoalescing>(g_options.IsCompilerMemoryCoalescingAligned());
252 graph->RunPass<IfConversion>(g_options.GetCompilerIfConversionLimit());
253 graph->RunPass<Scheduler>();
254 // Perform MoveConstants after Scheduler because Scheduler can rearrange constants
255 // and cause spillfill in reg alloc
256 graph->RunPass<MoveConstants>();
257 if (graph->RunPass<AdjustRefs>()) {
258 graph->RunPass<ValNum>();
259 graph->RunPass<Cleanup>(false);
260 }
261 graph->RunPass<OptimizeMemoryBarriers>();
262
263 return true;
264 }
265
266 template void Pipeline::Run<BACKGROUND_MODE>(CompilerTaskRunner<BACKGROUND_MODE>);
267 template void Pipeline::Run<INPLACE_MODE>(CompilerTaskRunner<INPLACE_MODE>);
268 template void Pipeline::RunRegAllocAndCodeGenPass<BACKGROUND_MODE>(CompilerTaskRunner<BACKGROUND_MODE>);
269 template void Pipeline::RunRegAllocAndCodeGenPass<INPLACE_MODE>(CompilerTaskRunner<INPLACE_MODE>);
270
271 } // namespace panda::compiler
272