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 "aot_builder.h"
17 #include "aot/aot_file.h"
18 #include "elf_builder.h"
19 #include "include/class.h"
20 #include "include/method.h"
21 #include "optimizer/code_generator/encode.h"
22 #include "code_info/code_info.h"
23
24 #include <numeric>
25
26 namespace panda::compiler {
27
28 /**
29 * Fills text section in the ELF builder by the code from the methods in AotBuilder.
30 */
31 class CodeDataProvider : public ElfSectionDataProvider {
32 public:
CodeDataProvider(AotBuilder * aot_builder)33 explicit CodeDataProvider(AotBuilder *aot_builder) : aot_builder_(aot_builder) {}
34
FillData(Span<uint8_t> stream,size_t stream_begin) const35 void FillData(Span<uint8_t> stream, size_t stream_begin) const override
36 {
37 const size_t code_offset = CodeInfo::GetCodeOffset(aot_builder_->GetArch());
38 CodePrefix prefix;
39 size_t curr_pos = stream_begin;
40 for (size_t i = 0; i < aot_builder_->methods_.size(); i++) {
41 auto &method = aot_builder_->methods_[i];
42 auto &method_header = aot_builder_->method_headers_[i];
43 prefix.code_size = method.GetCode().size();
44 prefix.code_info_offset = code_offset + RoundUp(method.GetCode().size(), CodeInfo::ALIGNMENT);
45 prefix.code_info_size = method.GetCodeInfo().size();
46 // Prefix
47 curr_pos = stream_begin + method_header.code_offset;
48 const char *data = reinterpret_cast<char *>(&prefix);
49 CopyToSpan(stream, data, sizeof(prefix), curr_pos);
50 curr_pos += sizeof(prefix);
51
52 // Code
53 curr_pos += code_offset - sizeof(prefix);
54 data = reinterpret_cast<const char *>(method.GetCode().data());
55 CopyToSpan(stream, data, method.GetCode().size(), curr_pos);
56 curr_pos += method.GetCode().size();
57
58 // CodeInfo
59 curr_pos += RoundUp(method.GetCode().size(), CodeInfo::ALIGNMENT) - method.GetCode().size();
60 data = reinterpret_cast<const char *>(method.GetCodeInfo().data());
61 CopyToSpan(stream, data, method.GetCodeInfo().size(), curr_pos);
62 }
63 }
64
GetDataSize() const65 size_t GetDataSize() const override
66 {
67 return aot_builder_->current_code_size_;
68 }
69
70 private:
71 AotBuilder *aot_builder_;
72 };
73
StartFile(const std::string & name,uint32_t checksum)74 void AotBuilder::StartFile(const std::string &name, uint32_t checksum)
75 {
76 auto &file_header = file_headers_.emplace_back();
77 file_header.classes_offset = class_headers_.size();
78 file_header.file_checksum = checksum;
79 file_header.file_offset = 0;
80 file_header.file_name_str = AddString(name);
81 file_header.methods_offset = method_headers_.size();
82 }
83
EndFile()84 void AotBuilder::EndFile()
85 {
86 ASSERT(!file_headers_.empty());
87 auto &file_header = file_headers_.back();
88 file_header.classes_count = class_headers_.size() - file_header.classes_offset;
89 if (file_header.classes_count == 0 && (class_hash_tables_size_.empty() || class_hash_tables_size_.back() == 0)) {
90 /* Just return, if there is nothing compiled in the file */
91 CHECK_EQ(file_header.methods_count, 0U);
92 file_headers_.pop_back();
93 return;
94 }
95 ASSERT(!class_hash_tables_size_.empty());
96 file_header.class_hash_table_offset =
97 (entity_pair_headers_.size() - class_hash_tables_size_.back()) * sizeof(panda_file::EntityPairHeader);
98 file_header.class_hash_table_size = class_hash_tables_size_.back();
99 file_header.methods_count = method_headers_.size() - file_header.methods_offset;
100 // We should keep class headers sorted, since AOT manager uses binary search to find classes.
101 std::sort(class_headers_.begin() + file_header.classes_offset, class_headers_.end(),
102 [](const auto &a, const auto &b) { return a.class_id < b.class_id; });
103 }
104
Write(const std::string & cmdline,const std::string & file_name)105 int AotBuilder::Write(const std::string &cmdline, const std::string &file_name)
106 {
107 switch (arch_) {
108 case Arch::AARCH32:
109 return WriteImpl<Arch::AARCH32>(cmdline, file_name);
110 case Arch::AARCH64:
111 return WriteImpl<Arch::AARCH64>(cmdline, file_name);
112 case Arch::X86:
113 return WriteImpl<Arch::X86>(cmdline, file_name);
114 case Arch::X86_64:
115 return WriteImpl<Arch::X86_64>(cmdline, file_name);
116 default:
117 LOG(ERROR, COMPILER) << "AotBuilder: Unsupported arch";
118 return 1;
119 }
120 }
121
FillHeader(const std::string & cmdline,const std::string & file_name)122 void AotBuilder::FillHeader(const std::string &cmdline, const std::string &file_name)
123 {
124 aot_header_.magic = compiler::AotFile::MAGIC;
125 aot_header_.version = compiler::AotFile::VERSION;
126 aot_header_.checksum = 0; // TODO(msherstennikov)
127 ASSERT(GetRuntime() != nullptr);
128 aot_header_.environment_checksum = GetRuntime()->GetEnvironmentChecksum(arch_);
129 aot_header_.arch = static_cast<uint32_t>(arch_);
130 aot_header_.gc_type = gc_type_;
131 aot_header_.files_offset = sizeof(aot_header_);
132 aot_header_.files_count = file_headers_.size();
133 aot_header_.class_hash_tables_offset =
134 aot_header_.files_offset + aot_header_.files_count * sizeof(compiler::PandaFileHeader);
135 size_t class_hash_tables_size = entity_pair_headers_.size() * sizeof(panda_file::EntityPairHeader);
136 aot_header_.classes_offset = aot_header_.class_hash_tables_offset + class_hash_tables_size;
137 aot_header_.methods_offset = aot_header_.classes_offset + class_headers_.size() * sizeof(compiler::ClassHeader);
138 aot_header_.bitmap_offset = aot_header_.methods_offset + methods_.size() * sizeof(compiler::MethodHeader);
139 size_t bitmaps_size =
140 std::accumulate(class_methods_bitmaps_.begin(), class_methods_bitmaps_.end(), 0U,
141 [](size_t sum, const auto &vec) { return vec.GetContainerSizeInBytes() + sum; });
142 aot_header_.strtab_offset = aot_header_.bitmap_offset + bitmaps_size;
143 aot_header_.file_name_str = AddString(file_name);
144 aot_header_.cmdline_str = AddString(cmdline);
145 aot_header_.boot_aot = static_cast<uint32_t>(boot_aot_);
146 aot_header_.with_cha = static_cast<uint32_t>(with_cha_);
147 aot_header_.class_ctx_str = AddString(class_ctx_);
148 }
149
150 template <Arch arch>
WriteImpl(const std::string & cmdline,const std::string & file_name)151 int AotBuilder::WriteImpl(const std::string &cmdline, const std::string &file_name)
152 {
153 constexpr size_t PAGE_SIZE_BYTES = 0x1000;
154 constexpr size_t CALL_STATIC_SLOT_SIZE = 3;
155 constexpr size_t CALL_VIRTUAL_SLOT_SIZE = 2;
156 constexpr size_t STRING_SLOT_SIZE = 2;
157 constexpr size_t INLINE_CACHE_SLOT_SIZE = 1;
158 ElfBuilder<arch> builder;
159
160 auto text_section = builder.GetTextSection();
161 auto aot_section = builder.GetAotSection();
162 auto got_section = builder.GetGotSection();
163 std::vector<uint8_t> &got_data = got_section->GetVector();
164 // +1 is the extra slot that indicates the end of the aot table
165 auto got_data_size = static_cast<size_t>(RuntimeInterface::IntrinsicId::COUNT) + 1 +
166 CALL_STATIC_SLOT_SIZE * (got_plt_.size() + got_class_.size()) +
167 CALL_VIRTUAL_SLOT_SIZE * got_virt_indexes_.size() + STRING_SLOT_SIZE * got_string_.size() +
168 INLINE_CACHE_SLOT_SIZE * got_intf_inline_cache_.size();
169 // We need to fill the whole segment with aot_got section because it is filled from the end.
170 got_data.resize(RoundUp(PointerSize(arch) * got_data_size, PAGE_SIZE_BYTES), 0);
171
172 GenerateSymbols(builder);
173
174 FillHeader(cmdline, file_name);
175
176 aot_section->AppendData(&aot_header_, sizeof(aot_header_));
177 aot_section->AppendData(file_headers_.data(), file_headers_.size() * sizeof(compiler::PandaFileHeader));
178 aot_section->AppendData(entity_pair_headers_.data(),
179 entity_pair_headers_.size() * sizeof(panda_file::EntityPairHeader));
180 aot_section->AppendData(class_headers_.data(), class_headers_.size() * sizeof(compiler::ClassHeader));
181 aot_section->AppendData(method_headers_.data(), method_headers_.size() * sizeof(compiler::MethodHeader));
182
183 for (auto &bitmap : class_methods_bitmaps_) {
184 aot_section->AppendData(bitmap.data(), bitmap.GetContainerSizeInBytes());
185 }
186 aot_section->AppendData(string_table_.data(), string_table_.size());
187
188 CodeDataProvider code_provider(this);
189 text_section->SetDataProvider(&code_provider);
190
191 using PtrType = typename ArchTraits<arch>::WordType;
192 auto ptr_view = Span(got_data).template SubSpan<PtrType>(0, got_data.size() / sizeof(PtrType));
193 EmitPlt<arch>(ptr_view, got_data_size);
194
195 #ifdef PANDA_COMPILER_CFI
196 builder.SetFrameData(&frame_data_);
197 #endif
198 builder.Build(file_name);
199 builder.Write(file_name);
200 return 0;
201 }
202
203 template <Arch arch>
EmitPlt(Span<typename ArchTraits<arch>::WordType> ptr_view,size_t got_data_size)204 void AotBuilder::EmitPlt(Span<typename ArchTraits<arch>::WordType> ptr_view, size_t got_data_size)
205 {
206 if (!got_plt_.empty() || !got_virt_indexes_.empty() || !got_class_.empty() || !got_string_.empty() ||
207 !got_intf_inline_cache_.empty()) {
208 ASSERT(PointerSize(arch) >= sizeof(uint32_t));
209
210 auto ptr_cnt = ptr_view.Size();
211 auto end = static_cast<size_t>(RuntimeInterface::IntrinsicId::COUNT);
212
213 ptr_view[ptr_cnt - got_data_size] = 0;
214 constexpr size_t IMM_2 = 2;
215 for (auto [method, idx] : got_plt_) {
216 ASSERT(idx <= 0);
217 ptr_view[ptr_cnt - end + idx] = AotFile::AotSlotType::PLT_SLOT;
218 ptr_view[ptr_cnt - end + idx - IMM_2] = method.second;
219 }
220 for (auto [method, idx] : got_virt_indexes_) {
221 ASSERT(idx <= 0);
222 ptr_view[ptr_cnt - end + idx] = AotFile::AotSlotType::VTABLE_INDEX;
223 ptr_view[ptr_cnt - end + idx - 1] = method.second;
224 }
225 for (auto [klass, idx] : got_class_) {
226 ASSERT(idx <= 0);
227 ptr_view[ptr_cnt - end + idx] = AotFile::AotSlotType::CLASS_SLOT;
228 ptr_view[ptr_cnt - end + idx - IMM_2] = klass.second;
229 }
230 for (auto [string_id, idx] : got_string_) {
231 ASSERT(idx <= 0);
232 ptr_view[ptr_cnt - end + idx] = AotFile::AotSlotType::STRING_SLOT;
233 ptr_view[ptr_cnt - end + idx - 1] = string_id.second;
234 }
235 for (auto [cache, idx] : got_intf_inline_cache_) {
236 (void)cache;
237 ASSERT(idx < 0);
238 ptr_view[ptr_cnt - end + idx] = AotFile::AotSlotType::INLINECACHE_SLOT;
239 }
240 }
241 }
242
243 /**
244 * Add method names to the symbol table
245 */
246 template <Arch arch>
GenerateSymbols(ElfBuilder<arch> & builder)247 void AotBuilder::GenerateSymbols(ElfBuilder<arch> &builder)
248 {
249 if (generate_symbols_) {
250 auto text_section = builder.GetTextSection();
251 size_t offset = 0;
252 std::string method_name;
253 for (auto &method : methods_) {
254 if (method.GetMethod()->GetPandaFile() == nullptr) {
255 method_name = "Error: method doesn't belong to any panda file";
256 } else {
257 auto method_casted = reinterpret_cast<RuntimeInterface::MethodPtr>(method.GetMethod());
258 method_name = runtime_->GetMethodFullName(method_casted, true);
259 }
260 builder.template AddSymbol<true>(
261 method_name, method.GetOverallSize(), *text_section, [offset, text_section]() {
262 return text_section->GetAddress() + offset + CodeInfo::GetCodeOffset(arch);
263 });
264 offset += RoundUp(method.GetOverallSize(), ArchTraits<arch>::CODE_ALIGNMENT);
265 }
266 }
267 }
268
AddClassHashTable(const panda_file::File & panda_file)269 void AotBuilder::AddClassHashTable(const panda_file::File &panda_file)
270 {
271 const panda_file::File::Header *header = panda_file.GetHeader();
272 uint32_t num_classes = header->num_classes;
273 if (num_classes == 0) {
274 return;
275 }
276
277 size_t hash_table_size = panda::helpers::math::GetPowerOfTwoValue32(num_classes);
278 std::vector<panda_file::EntityPairHeader> entity_pairs;
279 std::vector<unsigned int> conflict_entity_table;
280 entity_pairs.resize(hash_table_size);
281 conflict_entity_table.resize(hash_table_size);
282 size_t conflict_num = 0;
283
284 auto classes = panda_file.GetClasses();
285 for (size_t i = 0; i < num_classes; ++i) {
286 auto entity_id = panda_file::File::EntityId(classes[i]);
287 auto name = panda_file.GetStringData(entity_id).data;
288 uint32_t hash = GetHash32String(name);
289 uint32_t pos = hash & (hash_table_size - 1);
290 auto &entity_pair = entity_pairs[pos];
291 if (entity_pair.descriptor_hash == 0) {
292 entity_pair.descriptor_hash = hash;
293 entity_pair.entity_id_offset = entity_id.GetOffset();
294 } else {
295 conflict_entity_table[conflict_num] = i;
296 conflict_num++;
297 }
298 }
299 if (conflict_num == 0) {
300 entity_pair_headers_.insert(entity_pair_headers_.end(), entity_pairs.begin(), entity_pairs.end());
301 class_hash_tables_size_.emplace_back(entity_pairs.size());
302 } else {
303 ResolveConflictClassHashTable(panda_file, std::move(conflict_entity_table), conflict_num, entity_pairs);
304 }
305 }
306
ResolveConflictClassHashTable(const panda_file::File & panda_file,std::vector<unsigned int> conflict_entity_table,size_t conflict_num,std::vector<panda_file::EntityPairHeader> & entity_pairs)307 void AotBuilder::ResolveConflictClassHashTable(const panda_file::File &panda_file,
308 std::vector<unsigned int> conflict_entity_table, size_t conflict_num,
309 std::vector<panda_file::EntityPairHeader> &entity_pairs)
310 {
311 auto classes = panda_file.GetClasses();
312 auto hash_table_size = entity_pairs.size();
313 for (size_t j = 0; j < conflict_num; ++j) {
314 if (j > 0 && conflict_entity_table[j - 1] == conflict_entity_table[j]) {
315 break; // Exit for loop if there is no conlict elements anymore
316 }
317 auto i = conflict_entity_table[j];
318 auto entity_id = panda_file::File::EntityId(classes[i]);
319 auto name = panda_file.GetStringData(entity_id).data;
320 uint32_t hash = GetHash32String(name);
321 uint32_t theory_pos = hash & (hash_table_size - 1);
322 ASSERT(entity_pairs[theory_pos].descriptor_hash != 0);
323
324 uint32_t actual_pos = theory_pos;
325 while (actual_pos < (hash_table_size - 1) && entity_pairs[actual_pos].descriptor_hash != 0) {
326 actual_pos++;
327 }
328 if (actual_pos == (hash_table_size - 1) && entity_pairs[actual_pos].descriptor_hash != 0) {
329 actual_pos = 0;
330 while (actual_pos < theory_pos && entity_pairs[actual_pos].descriptor_hash != 0) {
331 actual_pos++;
332 }
333 }
334 ASSERT(entity_pairs[actual_pos].descriptor_hash == 0);
335 auto &entity_pair = entity_pairs[actual_pos];
336 entity_pair.descriptor_hash = hash;
337 entity_pair.entity_id_offset = entity_id.GetOffset();
338 while (entity_pairs[theory_pos].next_pos != 0) {
339 theory_pos = entity_pairs[theory_pos].next_pos - 1;
340 }
341 // add 1 is to distinguish the initial value 0 of next_pos and the situation that the next pos is really 0
342 entity_pairs[theory_pos].next_pos = actual_pos + 1;
343 }
344 entity_pair_headers_.insert(entity_pair_headers_.end(), entity_pairs.begin(), entity_pairs.end());
345 class_hash_tables_size_.emplace_back(entity_pairs.size());
346 }
347
348 } // namespace panda::compiler
349