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