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