• 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 "compilation_unit.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/optimizer/code_generator/target_info.h"
22 #include "compiler/optimizer/optimizations/balance_expressions.h"
23 #include "compiler/optimizer/optimizations/branch_elimination.h"
24 #include "compiler/optimizer/optimizations/checks_elimination.h"
25 #include "compiler/optimizer/optimizations/code_sink.h"
26 #include "compiler/optimizer/optimizations/cse.h"
27 #include "compiler/optimizer/optimizations/deoptimize_elimination.h"
28 #include "compiler/optimizer/optimizations/if_conversion.h"
29 #include "compiler/optimizer/optimizations/licm.h"
30 #include "compiler/optimizer/optimizations/loop_peeling.h"
31 #include "compiler/optimizer/optimizations/loop_unroll.h"
32 #include "compiler/optimizer/optimizations/lowering.h"
33 #include "compiler/optimizer/optimizations/lse.h"
34 #include "compiler/optimizer/optimizations/memory_barriers.h"
35 #include "compiler/optimizer/optimizations/memory_coalescing.h"
36 #include "compiler/optimizer/optimizations/move_constants.h"
37 #include "compiler/optimizer/optimizations/peepholes.h"
38 #include "compiler/optimizer/optimizations/redundant_loop_elimination.h"
39 #include "compiler/optimizer/optimizations/regalloc/reg_alloc.h"
40 #include "compiler/optimizer/optimizations/scheduler.h"
41 #include "compiler/optimizer/optimizations/try_catch_resolving.h"
42 #include "compiler/optimizer/optimizations/vn.h"
43 #include "mem/pool_manager.h"
44 #include "irtoc_runtime.h"
45 #include "elfio/elfio.hpp"
46 
47 namespace panda::irtoc {
48 
49 // elfio library missed some elf constants, so lets define it here for a while. We can't include elf.h header because
50 // it conflicts with elfio.
51 static constexpr size_t EF_ARM_EABI_VER5 = 0x05000000;
52 static constexpr size_t EM_AARCH64 = 183;
53 
54 static bool RunIrtocOptimizations(Graph *graph);
55 
Run(std::string_view output)56 Compilation::Result Compilation::Run(std::string_view output)
57 {
58     if (compiler::options.WasSetCompilerRegex()) {
59         methods_regex_ = compiler::options.GetCompilerRegex();
60     }
61     // NOLINTNEXTLINE(readability-magic-numbers)
62     panda::mem::MemConfig::Initialize(32_MB, 64_MB, 600_MB, 32_MB);
63     PoolManager::Initialize();
64 
65     if (RUNTIME_ARCH == Arch::X86_64 && compiler::options.WasSetCompilerCrossArch()) {
66         arch_ = GetArchFromString(compiler::options.GetCompilerCrossArch());
67         if (arch_ == Arch::NONE) {
68             LOG(FATAL, IRTOC) << "FATAL: unknown arch: " << compiler::options.GetCompilerCrossArch();
69         } else if (!compiler::BackendSupport(arch_)) {
70             LOG(FATAL, IRTOC) << "Backend is not supported: " << compiler::options.GetCompilerCrossArch();
71         }
72     }
73 
74     LOG(INFO, IRTOC) << "Start Irtoc compilation for " << GetArchString(arch_) << "...";
75 
76     auto result = Compile();
77 
78     if (result) {
79         LOG(INFO, IRTOC) << "Irtoc compilation success";
80     } else {
81         LOG(ERROR, IRTOC) << "Irtoc compilation failed: " << result.Error();
82     }
83 
84     MakeElf(output);
85 
86     for (auto unit : units_) {
87         delete unit;
88     }
89 
90     PoolManager::Finalize();
91     panda::mem::MemConfig::Finalize();
92 
93     return result;
94 }
95 
Compile()96 Compilation::Result Compilation::Compile()
97 {
98     for (auto unit : units_) {
99         if (compiler::options.WasSetCompilerRegex() && !std::regex_match(unit->GetName(), methods_regex_)) {
100             continue;
101         }
102         LOG(INFO, IRTOC) << "Compile " << unit->GetName();
103         auto result = unit->Compile(arch_);
104         if (!result) {
105             return result;
106         }
107     }
108     return 0;
109 }
110 
RunIrtocInterpreterOptimizations(Graph * graph)111 static bool RunIrtocInterpreterOptimizations(Graph *graph)
112 {
113     compiler::options.SetCompilerChecksElimination(false);
114     // TODO(aantipina): re-enable Lse for #6873
115     compiler::options.SetCompilerLse(false);
116 #ifdef PANDA_COMPILER_TARGET_AARCH64
117     compiler::options.SetCompilerMemoryCoalescing(false);
118 #endif
119     if (!compiler::options.IsCompilerNonOptimizing()) {
120         graph->RunPass<compiler::Peepholes>();
121         graph->RunPass<compiler::BranchElimination>();
122         graph->RunPass<compiler::ValNum>();
123         graph->RunPass<compiler::Cleanup>();
124         graph->RunPass<compiler::Cse>();
125         graph->RunPass<compiler::Licm>(compiler::options.GetCompilerLicmHoistLimit());
126         graph->RunPass<compiler::RedundantLoopElimination>();
127         graph->RunPass<compiler::LoopPeeling>();
128         graph->RunPass<compiler::Lse>();
129         graph->RunPass<compiler::ValNum>();
130         if (graph->RunPass<compiler::Peepholes>() && graph->RunPass<compiler::BranchElimination>()) {
131             graph->RunPass<compiler::Peepholes>();
132         }
133         graph->RunPass<compiler::Cleanup>();
134         graph->RunPass<compiler::Cse>();
135         graph->RunPass<compiler::ChecksElimination>();
136         graph->RunPass<compiler::LoopUnroll>(compiler::options.GetCompilerLoopUnrollInstLimit(),
137                                              compiler::options.GetCompilerLoopUnrollFactor());
138         graph->RunPass<compiler::BalanceExpressions>();
139         if (graph->RunPass<compiler::Peepholes>()) {
140             graph->RunPass<compiler::BranchElimination>();
141         }
142         graph->RunPass<compiler::ValNum>();
143         graph->RunPass<compiler::Cse>();
144 
145 #ifndef NDEBUG
146         graph->SetLowLevelInstructionsEnabled();
147 #endif  // NDEBUG
148         graph->RunPass<compiler::Cleanup>();
149         graph->RunPass<compiler::Lowering>();
150         graph->RunPass<compiler::CodeSink>();
151         graph->RunPass<compiler::MemoryCoalescing>(compiler::options.IsCompilerMemoryCoalescingAligned());
152         graph->RunPass<compiler::IfConversion>(compiler::options.GetCompilerIfConversionLimit());
153         graph->RunPass<compiler::MoveConstants>();
154     }
155 
156     graph->RunPass<compiler::Cleanup>();
157     if (!compiler::RegAlloc(graph)) {
158         return false;
159     }
160 
161     graph->RunPass<compiler::Cleanup>();
162     return graph->RunPass<compiler::CodegenInterpreter>();
163 }
164 
Compile(Arch arch)165 CompilationUnit::Result CompilationUnit::Compile(Arch arch)
166 {
167     IrtocRuntimeInterface runtime;
168 
169     ArenaAllocator allocator_(SpaceType::SPACE_TYPE_COMPILER);
170     ArenaAllocator local_allocator_(SpaceType::SPACE_TYPE_COMPILER);
171 
172     graph_ = allocator_.New<Graph>(&allocator_, &local_allocator_, arch, this, &runtime, false);
173     builder_ = std::make_unique<compiler::IrConstructor>();
174 
175     MakeGraphImpl();
176 
177     if (GetGraph()->GetMode().IsNative()) {
178         if (!RunOptimizations(GetGraph())) {
179             return Unexpected("RunOptimizations failed!");
180         }
181     } else if (GetGraph()->GetMode().IsInterpreter() || GetGraph()->GetMode().IsInterpreterEntry()) {
182         if (!RunIrtocInterpreterOptimizations(GetGraph())) {
183             return Unexpected("RunIrtocInterpreterOptimizations failed!");
184         }
185     } else if (!RunIrtocOptimizations(GetGraph())) {
186         return Unexpected("RunOptimizations failed!");
187     }
188 
189     auto code = GetGraph()->GetData();
190     std::copy(code.begin(), code.end(), std::back_inserter(code_));
191 
192     return 0;
193 }
194 
AddRelocation(const compiler::RelocationInfo & info)195 void CompilationUnit::AddRelocation(const compiler::RelocationInfo &info)
196 {
197     relocation_entries_.emplace_back(info);
198 }
199 
RunIrtocOptimizations(Graph * graph)200 static bool RunIrtocOptimizations(Graph *graph)
201 {
202     if (!compiler::options.IsCompilerNonOptimizing()) {
203         graph->RunPass<compiler::Peepholes>();
204         graph->RunPass<compiler::ValNum>();
205         graph->RunPass<compiler::Cse>();
206         graph->RunPass<compiler::Cleanup>();
207         graph->RunPass<compiler::Lowering>();
208         graph->RunPass<compiler::CodeSink>();
209         graph->RunPass<compiler::MemoryCoalescing>(compiler::options.IsCompilerMemoryCoalescingAligned());
210         graph->RunPass<compiler::IfConversion>(compiler::options.GetCompilerIfConversionLimit());
211         graph->RunPass<compiler::Cleanup>();
212         graph->RunPass<compiler::Scheduler>();
213         // Perform MoveConstants after Scheduler because Scheduler can rearrange constants
214         // and cause spillfill in reg alloc
215         graph->RunPass<compiler::MoveConstants>();
216     }
217 
218     graph->RunPass<compiler::Cleanup>();
219     if (!compiler::RegAlloc(graph)) {
220         LOG(FATAL, IRTOC) << "RunOptimizations failed: register allocation error";
221         return false;
222     }
223 
224     graph->RunPass<compiler::Cleanup>();
225     if (graph->GetMode().IsFastPath()) {
226         if (!graph->RunPass<compiler::CodegenFastPath>()) {
227             LOG(FATAL, IRTOC) << "RunOptimizations failed: code generation error";
228             return false;
229         }
230     } else if (graph->GetMode().IsBoundary()) {
231         if (!graph->RunPass<compiler::CodegenBoundary>()) {
232             LOG(FATAL, IRTOC) << "RunOptimizations failed: code generation error";
233             return false;
234         }
235     } else {
236         UNREACHABLE();
237     }
238 
239     return true;
240 }
241 
GetElfArch(Arch arch)242 static size_t GetElfArch(Arch arch)
243 {
244     switch (arch) {
245         case Arch::AARCH32:
246             return EM_ARM;
247         case Arch::AARCH64:
248             return EM_AARCH64;
249         case Arch::X86:
250             return EM_386;
251         case Arch::X86_64:
252             return EM_X86_64;
253         default:
254             UNREACHABLE();
255     }
256 }
257 
MakeElf(std::string_view output)258 Compilation::Result Compilation::MakeElf(std::string_view output)
259 {
260     ELFIO::elfio elf_writer;
261     elf_writer.create(Is64BitsArch(arch_) ? ELFCLASS64 : ELFCLASS32, ELFDATA2LSB);
262     elf_writer.set_type(ET_REL);
263     if (arch_ == Arch::AARCH32) {
264         elf_writer.set_flags(EF_ARM_EABI_VER5);
265     }
266     elf_writer.set_os_abi(ELFOSABI_NONE);
267     elf_writer.set_machine(GetElfArch(arch_));
268 
269     ELFIO::section *str_sec = elf_writer.sections.add(".strtab");
270     str_sec->set_type(SHT_STRTAB);
271     str_sec->set_addr_align(0x1);
272 
273     ELFIO::string_section_accessor str_writer(str_sec);
274 
275     static constexpr size_t FIRST_GLOBAL_SYMBOL_INDEX = 2;
276     static constexpr size_t SYMTAB_ADDR_ALIGN = 8;
277 
278     ELFIO::section *sym_sec = elf_writer.sections.add(".symtab");
279     sym_sec->set_type(SHT_SYMTAB);
280     sym_sec->set_info(FIRST_GLOBAL_SYMBOL_INDEX);
281     sym_sec->set_link(str_sec->get_index());
282     sym_sec->set_addr_align(SYMTAB_ADDR_ALIGN);
283     sym_sec->set_entry_size(elf_writer.get_default_entry_size(SHT_SYMTAB));
284 
285     ELFIO::symbol_section_accessor symbol_writer(elf_writer, sym_sec);
286 
287     symbol_writer.add_symbol(str_writer, "irtoc.cpp", 0, 0, STB_LOCAL, STT_FILE, 0, SHN_ABS);
288 
289     ELFIO::section *text_sec = elf_writer.sections.add(".text");
290     text_sec->set_type(SHT_PROGBITS);
291     // NOLINTNEXTLINE(hicpp-signed-bitwise)
292     text_sec->set_flags(SHF_ALLOC | SHF_EXECINSTR);
293     text_sec->set_addr_align(GetCodeAlignment(arch_));
294 
295     ELFIO::section *rel_sec = elf_writer.sections.add(".rela.text");
296     rel_sec->set_type(SHT_RELA);
297     rel_sec->set_info(text_sec->get_index());
298     rel_sec->set_link(sym_sec->get_index());
299     const auto ADDR_ALIGN = 4;
300     rel_sec->set_addr_align(ADDR_ALIGN);
301     rel_sec->set_entry_size(elf_writer.get_default_entry_size(SHT_RELA));
302     ELFIO::relocation_section_accessor rel_writer(elf_writer, rel_sec);
303 
304     /* Use symbols map to avoid saving the same symbols multiple times */
305     std::unordered_map<std::string, uint32_t> symbols_map;
306     auto add_symbol = [&](const char *name) {
307         if (auto it = symbols_map.find(name); it != symbols_map.end()) {
308             return it->second;
309         }
310         uint32_t index = symbol_writer.add_symbol(str_writer, name, 0, 0, STB_GLOBAL, STT_NOTYPE, 0, 0);
311         symbols_map.insert({name, index});
312         return index;
313     };
314 
315     size_t offset = 0;
316     for (auto unit : units_) {
317         auto code = unit->GetCode();
318         text_sec->append_data(reinterpret_cast<const char *>(code.data()), code.size());
319         symbol_writer.add_symbol(str_writer, unit->GetName(), offset, code.size(), STB_GLOBAL, STT_FUNC, 0,
320                                  text_sec->get_index());
321         for (auto &rel : unit->GetRelocations()) {
322             size_t rel_offset = offset + rel.offset;
323             auto sindex = add_symbol(unit->GetExternalFunction(rel.data));
324             if (Is64BitsArch(arch_)) {
325                 // NOLINTNEXTLINE(hicpp-signed-bitwise)
326                 rel_writer.add_entry(rel_offset, static_cast<ELFIO::Elf_Xword>(ELF64_R_INFO(sindex, rel.type)),
327                                      rel.addend);
328             } else {
329                 // NOLINTNEXTLINE(hicpp-signed-bitwise)
330                 rel_writer.add_entry(rel_offset, static_cast<ELFIO::Elf_Xword>(ELF32_R_INFO(sindex, rel.type)),
331                                      rel.addend);
332             }
333         }
334         offset += code.size();
335     }
336 
337     elf_writer.save(output.data());
338 
339     return 0;
340 }
341 
342 }  // namespace panda::irtoc
343