• 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 "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