• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2021-2024 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 "function.h"
17 #include "compiler/codegen_boundary.h"
18 #include "compiler/codegen_fastpath.h"
19 #include "compiler/codegen_interpreter.h"
20 #include "compiler/optimizer_run.h"
21 #include "compiler/dangling_pointers_checker.h"
22 #include "compiler/optimizer/code_generator/target_info.h"
23 #include "compiler/optimizer/optimizations/balance_expressions.h"
24 #include "compiler/optimizer/optimizations/branch_elimination.h"
25 #include "compiler/optimizer/optimizations/checks_elimination.h"
26 #include "compiler/optimizer/optimizations/code_sink.h"
27 #include "compiler/optimizer/optimizations/cse.h"
28 #include "compiler/optimizer/optimizations/deoptimize_elimination.h"
29 #include "compiler/optimizer/optimizations/if_conversion.h"
30 #include "compiler/optimizer/optimizations/if_merging.h"
31 #include "compiler/optimizer/optimizations/licm.h"
32 #include "compiler/optimizer/optimizations/loop_peeling.h"
33 #include "compiler/optimizer/optimizations/loop_unroll.h"
34 #include "compiler/optimizer/optimizations/lowering.h"
35 #include "compiler/optimizer/optimizations/lse.h"
36 #include "compiler/optimizer/optimizations/memory_barriers.h"
37 #include "compiler/optimizer/optimizations/memory_coalescing.h"
38 #include "compiler/optimizer/optimizations/move_constants.h"
39 #include "compiler/optimizer/optimizations/peepholes.h"
40 #include "compiler/optimizer/optimizations/redundant_loop_elimination.h"
41 #include "compiler/optimizer/optimizations/regalloc/reg_alloc.h"
42 #include "compiler/optimizer/optimizations/scheduler.h"
43 #include "compiler/optimizer/optimizations/try_catch_resolving.h"
44 #include "compiler/optimizer/optimizations/vn.h"
45 #include "elfio/elfio.hpp"
46 #include "irtoc_runtime.h"
47 
48 namespace ark::irtoc {
49 
50 using compiler::Graph;
51 
52 static bool RunIrtocOptimizations(Graph *graph);
53 static bool RunIrtocInterpreterOptimizations(Graph *graph);
54 
Compile(Arch arch,ArenaAllocator * allocator,ArenaAllocator * localAllocator)55 Function::Result Function::Compile(Arch arch, ArenaAllocator *allocator, ArenaAllocator *localAllocator)
56 {
57     IrtocRuntimeInterface runtime;
58     graph_ = allocator->New<Graph>(Graph::GraphArgs {allocator, localAllocator, arch, this, &runtime}, false);
59     builder_ = std::make_unique<compiler::IrConstructor>();
60 
61     MakeGraphImpl();
62 
63     if (GetGraph()->GetMode().IsNative()) {
64         compiler::InPlaceCompilerTaskRunner taskRunner;
65         taskRunner.GetContext().SetGraph(GetGraph());
66         bool success = true;
67         taskRunner.AddCallbackOnFail(
68             [&success]([[maybe_unused]] compiler::InPlaceCompilerContext &compilerCtx) { success = false; });
69         compiler::RunOptimizations<compiler::INPLACE_MODE>(std::move(taskRunner));
70         if (!success) {
71             return Unexpected("RunOptimizations failed!");
72         }
73         compilationResult_ = CompilationResult::ARK;
74     } else {
75         auto result = RunOptimizations();
76         if (!result) {
77             return result;
78         }
79         compilationResult_ = result.Value();
80     }
81     builder_.reset(nullptr);
82 
83     auto code = GetGraph()->GetCode();
84     SetCode(code);
85 
86     ASSERT(compilationResult_ != CompilationResult::INVALID);
87     return compilationResult_;
88 }
89 
RunOptimizations()90 Function::Result Function::RunOptimizations()
91 {
92     auto result = CompilationResult::INVALID;
93     bool interpreter = GetGraph()->GetMode().IsInterpreter() || GetGraph()->GetMode().IsInterpreterEntry();
94     bool shouldTryLlvm = false;
95 #ifdef PANDA_LLVM_IRTOC
96     bool withLlvmSuffix = std::string_view {GetName()}.find(LLVM_SUFFIX) != std::string_view::npos;
97     if (withLlvmSuffix) {
98 #ifdef PANDA_LLVM_INTERPRETER
99         shouldTryLlvm = true;
100         // Functions with "_LLVM" suffix must be interpreter handlers from the irtoc_code_llvm.cpp
101         LOG_IF(!interpreter, FATAL, IRTOC)
102             << "'" << GetName() << "' must not contain '" << LLVM_SUFFIX << "', only interpreter handlers can";
103 #else
104         LOG(FATAL, IRTOC) << "'" << GetName() << "' handler can not exist with PANDA_LLVM_INTERPRETER disabled";
105 #endif
106     }
107 #ifdef PANDA_LLVM_FASTPATH
108     shouldTryLlvm |= !interpreter;
109 #endif
110 #endif  // PANDA_LLVM_IRTOC
111     if (shouldTryLlvm) {
112         result = CompileByLLVM();
113         ASSERT(result == CompilationResult::LLVM || result == CompilationResult::ARK_BECAUSE_SKIP ||
114                result == CompilationResult::ARK_BECAUSE_FALLBACK);
115     } else {
116         result = CompilationResult::ARK;
117     }
118     if (result == CompilationResult::LLVM) {
119         return result;
120     }
121     ASSERT(GetGraph()->GetCode().Empty());
122     if (interpreter) {
123         if (!RunIrtocInterpreterOptimizations(GetGraph())) {
124             return Unexpected {"RunIrtocInterpreterOptimizations failed"};
125         }
126     } else {
127         if (!RunIrtocOptimizations(GetGraph())) {
128             return Unexpected {"RunIrtocOptimizations failed"};
129         }
130     }
131     return result;
132 }
133 
SetCode(Span<uint8_t> code)134 void Function::SetCode(Span<uint8_t> code)
135 {
136     std::copy(code.begin(), code.end(), std::back_inserter(code_));
137 }
138 
CompileByLLVM()139 CompilationResult Function::CompileByLLVM()
140 {
141 #ifndef PANDA_LLVM_IRTOC
142     UNREACHABLE();
143 #else
144     if (SkippedByLLVM()) {
145         return CompilationResult::ARK_BECAUSE_SKIP;
146     }
147     ASSERT(llvmCompiler_ != nullptr);
148     auto result = llvmCompiler_->TryAddGraph(GetGraph());
149     if (!result.HasValue()) {
150         LOG(FATAL, IRTOC) << "LLVM IRTOC compilation failed for function '" << GetName() << "', error: '"
151                           << result.Error() << "'";
152     }
153     return result.Value() ? CompilationResult::LLVM : CompilationResult::ARK_BECAUSE_FALLBACK;
154 #endif  // ifndef PANDA_LLVM_IRTOC
155 }
156 
157 #ifdef PANDA_LLVM_IRTOC
ReportCompilationStatistic(std::ostream * out)158 void Function::ReportCompilationStatistic(std::ostream *out)
159 {
160     ASSERT(out != nullptr);
161     ASSERT(!g_options.Validate());
162     std::string statsLevel = g_options.GetIrtocLlvmStats();
163     if (statsLevel == "none") {
164         return;
165     }
166     if (statsLevel != "full" && GetGraph()->IsDynamicMethod()) {
167         return;
168     }
169     if (statsLevel == "short") {
170         if (compilationResult_ == CompilationResult::LLVM || compilationResult_ == CompilationResult::ARK) {
171             return;
172         }
173     }
174 
175     static constexpr auto HANDLER_WIDTH = 96;
176     static constexpr auto RESULT_WIDTH = 8;
177 
178     *out << "LLVM " << GraphModeToString() << std::setw(HANDLER_WIDTH) << GetName() << " --- "
179          << std::setw(RESULT_WIDTH) << LLVMCompilationResultToString() << "(size " << GetCodeSize() << ")" << std::endl;
180 }
181 
LLVMCompilationResultToString() const182 std::string_view Function::LLVMCompilationResultToString() const
183 {
184     if (compilationResult_ == CompilationResult::ARK) {
185         return "ark";
186     }
187     if (compilationResult_ == CompilationResult::LLVM) {
188         return "ok";
189     }
190     if (compilationResult_ == CompilationResult::ARK_BECAUSE_FALLBACK) {
191         return "fallback";
192     }
193     if (compilationResult_ == CompilationResult::ARK_BECAUSE_SKIP) {
194         return "skip";
195     }
196     UNREACHABLE();
197 }
198 
GraphModeToString()199 std::string_view Function::GraphModeToString()
200 {
201     if (GetGraph()->GetMode().IsFastPath()) {
202         return "fastpath";
203     }
204     if (GetGraph()->GetMode().IsBoundary()) {
205         return "boundary";
206     }
207     if (GetGraph()->GetMode().IsInterpreter() || GetGraph()->GetMode().IsInterpreterEntry()) {
208         return "interp";
209     }
210     if (GetGraph()->GetMode().IsNative()) {
211         return "native";
212     }
213     UNREACHABLE();
214 }
215 #endif
216 
AddRelocation(const compiler::RelocationInfo & info)217 void Function::AddRelocation(const compiler::RelocationInfo &info)
218 {
219     relocationEntries_.emplace_back(info);
220 }
221 
222 #ifdef PANDA_LLVM_IRTOC
SkippedByLLVM()223 bool Function::SkippedByLLVM()
224 {
225     auto name = std::string(GetName());
226 #ifdef PANDA_LLVM_INTERPRETER
227     if (GetGraph()->GetMode().IsInterpreterEntry() || GetGraph()->GetMode().IsInterpreter()) {
228         ASSERT(name.find(LLVM_SUFFIX) == name.size() - LLVM_SUFFIX.size());
229         name = name.substr(0, name.size() - LLVM_SUFFIX.size());
230         return std::find(SKIPPED_HANDLERS.begin(), SKIPPED_HANDLERS.end(), name) != SKIPPED_HANDLERS.end();
231     }
232 #endif
233 
234 #ifdef PANDA_LLVM_FASTPATH
235     if (GetGraph()->GetMode().IsFastPath()) {
236         ASSERT(GetArch() == Arch::AARCH64);
237         return std::find(SKIPPED_FASTPATHS.begin(), SKIPPED_FASTPATHS.end(), name) != SKIPPED_FASTPATHS.end();
238     }
239 #endif
240     return true;
241 }
242 #endif
243 
RunIrtocInterpreterOptimizations(Graph * graph)244 static bool RunIrtocInterpreterOptimizations(Graph *graph)
245 {
246     compiler::g_options.SetCompilerChecksElimination(false);
247     // aantipina: re-enable Lse
248     compiler::g_options.SetCompilerLse(false);
249 #ifdef PANDA_COMPILER_TARGET_AARCH64
250     compiler::g_options.SetCompilerMemoryCoalescing(false);
251 #endif
252     if (!compiler::g_options.IsCompilerNonOptimizing()) {
253         graph->RunPass<compiler::Peepholes>();
254         graph->RunPass<compiler::BranchElimination>();
255         graph->RunPass<compiler::ValNum>();
256         graph->RunPass<compiler::IfMerging>();
257         graph->RunPass<compiler::Cleanup>();
258         graph->RunPass<compiler::Cse>();
259         graph->RunPass<compiler::Licm>(compiler::g_options.GetCompilerLicmHoistLimit());
260         graph->RunPass<compiler::RedundantLoopElimination>();
261         graph->RunPass<compiler::LoopPeeling>();
262         graph->RunPass<compiler::Lse>();
263         graph->RunPass<compiler::ValNum>();
264         if (graph->RunPass<compiler::Peepholes>() && graph->RunPass<compiler::BranchElimination>()) {
265             graph->RunPass<compiler::Peepholes>();
266         }
267         graph->RunPass<compiler::Cleanup>();
268         graph->RunPass<compiler::Cse>();
269         graph->RunPass<compiler::ChecksElimination>();
270         graph->RunPass<compiler::LoopUnroll>(compiler::g_options.GetCompilerLoopUnrollInstLimit(),
271                                              compiler::g_options.GetCompilerLoopUnrollFactor());
272         graph->RunPass<compiler::BalanceExpressions>();
273         if (graph->RunPass<compiler::Peepholes>()) {
274             graph->RunPass<compiler::BranchElimination>();
275         }
276         graph->RunPass<compiler::ValNum>();
277         graph->RunPass<compiler::Cse>();
278 
279 #ifndef NDEBUG
280         graph->SetLowLevelInstructionsEnabled();
281 #endif  // NDEBUG
282         graph->RunPass<compiler::Cleanup>();
283         graph->RunPass<compiler::Lowering>();
284         graph->RunPass<compiler::CodeSink>();
285         graph->RunPass<compiler::MemoryCoalescing>(compiler::g_options.IsCompilerMemoryCoalescingAligned());
286         graph->RunPass<compiler::IfConversion>(compiler::g_options.GetCompilerIfConversionLimit());
287         graph->RunPass<compiler::MoveConstants>();
288     }
289 
290     graph->RunPass<compiler::Cleanup>();
291     if (!compiler::RegAlloc(graph) || !graph->RunPass<compiler::DanglingPointersChecker>()) {
292         return false;
293     }
294 
295     return graph->RunPass<compiler::CodegenInterpreter>();
296 }
297 
RunIrtocOptimizations(Graph * graph)298 static bool RunIrtocOptimizations(Graph *graph)
299 {
300     if (!compiler::g_options.IsCompilerNonOptimizing()) {
301         graph->RunPass<compiler::Peepholes>();
302         graph->RunPass<compiler::ValNum>();
303         graph->RunPass<compiler::Cse>();
304 #ifndef NDEBUG
305         graph->SetLowLevelInstructionsEnabled();
306 #endif  // NDEBUG
307         graph->RunPass<compiler::Cleanup>();
308         graph->RunPass<compiler::Lowering>();
309         graph->RunPass<compiler::CodeSink>();
310         graph->RunPass<compiler::MemoryCoalescing>(compiler::g_options.IsCompilerMemoryCoalescingAligned());
311         graph->RunPass<compiler::IfConversion>(compiler::g_options.GetCompilerIfConversionLimit());
312         graph->RunPass<compiler::Cleanup>();
313         graph->RunPass<compiler::Scheduler>();
314         // Perform MoveConstants after Scheduler because Scheduler can rearrange constants
315         // and cause spillfill in reg alloc
316         graph->RunPass<compiler::MoveConstants>();
317     }
318 
319     graph->RunPass<compiler::Cleanup>();
320     if (!compiler::RegAlloc(graph)) {
321         LOG(FATAL, IRTOC) << "RunOptimizations failed: register allocation error";
322         return false;
323     }
324 
325     if (graph->GetMode().IsFastPath()) {
326         if (!graph->RunPass<compiler::CodegenFastPath>()) {
327             LOG(FATAL, IRTOC) << "RunOptimizations failed: code generation error";
328             return false;
329         }
330     } else if (graph->GetMode().IsBoundary()) {
331         if (!graph->RunPass<compiler::CodegenBoundary>()) {
332             LOG(FATAL, IRTOC) << "RunOptimizations failed: code generation error";
333             return false;
334         }
335     } else {
336         UNREACHABLE();
337     }
338 
339     return true;
340 }
341 
342 }  // namespace ark::irtoc
343