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