1 /*
2 * Copyright (c) 2021-2023 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 /// Fills text section in the ELF builder by the code from the methods in AotBuilder.
29 class CodeDataProvider : public ElfSectionDataProvider {
30 public:
CodeDataProvider(AotBuilder * aotBuilder)31 explicit CodeDataProvider(AotBuilder *aotBuilder) : aotBuilder_(aotBuilder) {}
32
FillData(Span<uint8_t> stream,size_t streamBegin) const33 void FillData(Span<uint8_t> stream, size_t streamBegin) const override
34 {
35 const size_t codeOffset = CodeInfo::GetCodeOffset(aotBuilder_->GetArch());
36 CodePrefix prefix;
37 size_t currPos = streamBegin;
38 for (size_t i = 0; i < aotBuilder_->methods_.size(); i++) {
39 auto &method = aotBuilder_->methods_[i];
40 auto &methodHeader = aotBuilder_->methodHeaders_[i];
41 prefix.codeSize = method.GetCode().size();
42 prefix.codeInfoOffset = codeOffset + RoundUp(method.GetCode().size(), CodeInfo::ALIGNMENT);
43 prefix.codeInfoSize = method.GetCodeInfo().size();
44 // Prefix
45 currPos = streamBegin + methodHeader.codeOffset;
46 const char *data = reinterpret_cast<char *>(&prefix);
47 CopyToSpan(stream, data, sizeof(prefix), currPos);
48 currPos += sizeof(prefix);
49
50 // Code
51 currPos += codeOffset - sizeof(prefix);
52 data = reinterpret_cast<const char *>(method.GetCode().data());
53 CopyToSpan(stream, data, method.GetCode().size(), currPos);
54 currPos += method.GetCode().size();
55
56 // CodeInfo
57 currPos += RoundUp(method.GetCode().size(), CodeInfo::ALIGNMENT) - method.GetCode().size();
58 data = reinterpret_cast<const char *>(method.GetCodeInfo().data());
59 CopyToSpan(stream, data, method.GetCodeInfo().size(), currPos);
60 }
61 }
62
GetDataSize() const63 size_t GetDataSize() const override
64 {
65 return aotBuilder_->currentCodeSize_;
66 }
67
68 private:
69 AotBuilder *aotBuilder_;
70 };
71
StartFile(const std::string & name,uint32_t checksum)72 void AotBuilder::StartFile(const std::string &name, uint32_t checksum)
73 {
74 auto &fileHeader = fileHeaders_.emplace_back();
75 fileHeader.classesOffset = classHeaders_.size();
76 fileHeader.fileChecksum = checksum;
77 fileHeader.fileOffset = 0;
78 fileHeader.fileNameStr = AddString(name);
79 fileHeader.methodsOffset = methodHeaders_.size();
80 }
81
EndFile()82 void AotBuilder::EndFile()
83 {
84 ASSERT(!fileHeaders_.empty());
85 auto &fileHeader = fileHeaders_.back();
86 fileHeader.classesCount = classHeaders_.size() - fileHeader.classesOffset;
87 if (fileHeader.classesCount == 0 && (classHashTablesSize_.empty() || classHashTablesSize_.back() == 0)) {
88 /* Just return, if there is nothing compiled in the file */
89 CHECK_EQ(fileHeader.methodsCount, 0U);
90 fileHeaders_.pop_back();
91 return;
92 }
93 ASSERT(!classHashTablesSize_.empty());
94 fileHeader.classHashTableOffset =
95 (entityPairHeaders_.size() - classHashTablesSize_.back()) * sizeof(panda_file::EntityPairHeader);
96 fileHeader.classHashTableSize = classHashTablesSize_.back();
97 fileHeader.methodsCount = methodHeaders_.size() - fileHeader.methodsOffset;
98 // We should keep class headers sorted, since AOT manager uses binary search to find classes.
99 std::sort(classHeaders_.begin() + fileHeader.classesOffset, classHeaders_.end(),
100 [](const auto &a, const auto &b) { return a.classId < b.classId; });
101 }
102
Write(const std::string & cmdline,const std::string & fileName)103 int AotBuilder::Write(const std::string &cmdline, const std::string &fileName)
104 {
105 switch (arch_) {
106 case Arch::AARCH32:
107 return WriteImpl<Arch::AARCH32>(cmdline, fileName);
108 case Arch::AARCH64:
109 return WriteImpl<Arch::AARCH64>(cmdline, fileName);
110 case Arch::X86:
111 return WriteImpl<Arch::X86>(cmdline, fileName);
112 case Arch::X86_64:
113 return WriteImpl<Arch::X86_64>(cmdline, fileName);
114 default:
115 LOG(ERROR, COMPILER) << "AotBuilder: Unsupported arch";
116 return 1;
117 }
118 }
119
FillHeader(const std::string & cmdline,const std::string & fileName)120 void AotBuilder::FillHeader(const std::string &cmdline, const std::string &fileName)
121 {
122 aotHeader_.magic = compiler::AotFile::MAGIC;
123 aotHeader_.version = compiler::AotFile::VERSION;
124 aotHeader_.checksum = 0; // NOTE(msherstennikov)
125 ASSERT(GetRuntime() != nullptr);
126 aotHeader_.environmentChecksum = GetRuntime()->GetEnvironmentChecksum(arch_);
127 aotHeader_.arch = static_cast<uint32_t>(arch_);
128 aotHeader_.gcType = gcType_;
129 aotHeader_.filesOffset = sizeof(aotHeader_);
130 aotHeader_.filesCount = fileHeaders_.size();
131 aotHeader_.classHashTablesOffset =
132 aotHeader_.filesOffset + aotHeader_.filesCount * sizeof(compiler::PandaFileHeader);
133 size_t classHashTablesSize = entityPairHeaders_.size() * sizeof(panda_file::EntityPairHeader);
134 aotHeader_.classesOffset = aotHeader_.classHashTablesOffset + classHashTablesSize;
135 aotHeader_.methodsOffset = aotHeader_.classesOffset + classHeaders_.size() * sizeof(compiler::ClassHeader);
136 aotHeader_.bitmapOffset = aotHeader_.methodsOffset + methods_.size() * sizeof(compiler::MethodHeader);
137 size_t bitmapsSize =
138 std::accumulate(classMethodsBitmaps_.begin(), classMethodsBitmaps_.end(), 0U,
139 [](size_t sum, const auto &vec) { return vec.GetContainerSizeInBytes() + sum; });
140 aotHeader_.strtabOffset = aotHeader_.bitmapOffset + bitmapsSize;
141 aotHeader_.fileNameStr = AddString(fileName);
142 aotHeader_.cmdlineStr = AddString(cmdline);
143 aotHeader_.bootAot = static_cast<uint32_t>(bootAot_);
144 aotHeader_.withCha = static_cast<uint32_t>(withCha_);
145 aotHeader_.classCtxStr = AddString(classCtx_);
146 }
147
148 template <Arch ARCH>
WriteImpl(const std::string & cmdline,const std::string & fileName)149 int AotBuilder::WriteImpl(const std::string &cmdline, const std::string &fileName)
150 {
151 ElfBuilder<ARCH> builder;
152
153 PrepareElfBuilder(builder, cmdline, fileName);
154 builder.Build(fileName);
155 builder.Write(fileName);
156
157 return 0;
158 }
159
160 template <Arch ARCH>
PrepareElfBuilder(ElfBuilder<ARCH> & builder,const std::string & cmdline,const std::string & fileName)161 int AotBuilder::PrepareElfBuilder(ElfBuilder<ARCH> &builder, const std::string &cmdline, const std::string &fileName)
162 {
163 constexpr size_t PAGE_SIZE_BYTES = 0x1000;
164 constexpr size_t CALL_STATIC_SLOT_SIZE = 3;
165 constexpr size_t CALL_VIRTUAL_SLOT_SIZE = 2;
166 constexpr size_t STRING_SLOT_SIZE = 2;
167 constexpr size_t INLINE_CACHE_SLOT_SIZE = 1;
168 constexpr size_t COMMON_SLOT_SIZE = 1;
169
170 auto codeProvider = std::make_unique<CodeDataProvider>(this);
171 builder.GetTextSection()->SetDataProvider(std::move(codeProvider));
172
173 builder.PreSizeRoDataSections(roDatas_.size());
174 for (const auto &roData : roDatas_) {
175 builder.AddRoDataSection(roData.name, roData.alignment);
176 }
177 auto roDataSections = builder.GetRoDataSections();
178 auto aotSection = builder.GetAotSection();
179 auto gotSection = builder.GetGotSection();
180 std::vector<uint8_t> &gotData = gotSection->GetVector();
181 // +1 is the extra slot that indicates the end of the aot table
182 auto gotDataSize = static_cast<size_t>(RuntimeInterface::IntrinsicId::COUNT) + 1 +
183 CALL_STATIC_SLOT_SIZE * (gotPlt_.size() + gotClass_.size()) +
184 CALL_VIRTUAL_SLOT_SIZE * gotVirtIndexes_.size() + STRING_SLOT_SIZE * gotString_.size() +
185 INLINE_CACHE_SLOT_SIZE * gotIntfInlineCache_.size() + COMMON_SLOT_SIZE * gotCommon_.size();
186 // We need to fill the whole segment with aot_got section because it is filled from the end.
187 gotData.resize(RoundUp(PointerSize(ARCH) * gotDataSize, PAGE_SIZE_BYTES), 0);
188
189 GenerateSymbols(builder);
190
191 FillHeader(cmdline, fileName);
192
193 aotSection->AppendData(&aotHeader_, sizeof(aotHeader_));
194 aotSection->AppendData(fileHeaders_.data(), fileHeaders_.size() * sizeof(compiler::PandaFileHeader));
195 aotSection->AppendData(entityPairHeaders_.data(), entityPairHeaders_.size() * sizeof(panda_file::EntityPairHeader));
196 aotSection->AppendData(classHeaders_.data(), classHeaders_.size() * sizeof(compiler::ClassHeader));
197 aotSection->AppendData(methodHeaders_.data(), methodHeaders_.size() * sizeof(compiler::MethodHeader));
198
199 for (auto &bitmap : classMethodsBitmaps_) {
200 aotSection->AppendData(bitmap.data(), bitmap.GetContainerSizeInBytes());
201 }
202 aotSection->AppendData(stringTable_.data(), stringTable_.size());
203
204 for (size_t i = 0; i < roDatas_.size(); i++) {
205 auto §ion = roDatas_.at(i);
206 auto dataSection = roDataSections->at(i);
207 ASSERT(section.name == dataSection->GetName());
208 dataSection->AppendData(section.content.data(), section.content.size());
209 }
210
211 using PtrType = typename ArchTraits<ARCH>::WordType;
212 auto ptrView = Span(gotData).template SubSpan<PtrType>(0, gotData.size() / sizeof(PtrType));
213 EmitPlt<ARCH>(ptrView, gotDataSize);
214
215 #ifdef PANDA_COMPILER_DEBUG_INFO
216 builder.SetFrameData(&frameData_);
217 #endif
218
219 return 0;
220 }
221 template int AotBuilder::PrepareElfBuilder<Arch::X86>(ElfBuilder<Arch::X86> &builder, const std::string &cmdline,
222 const std::string &file_name);
223 template int AotBuilder::PrepareElfBuilder<Arch::X86_64>(ElfBuilder<Arch::X86_64> &builder, const std::string &cmdline,
224 const std::string &file_name);
225 template int AotBuilder::PrepareElfBuilder<Arch::AARCH32>(ElfBuilder<Arch::AARCH32> &builder,
226 const std::string &cmdline, const std::string &file_name);
227 template int AotBuilder::PrepareElfBuilder<Arch::AARCH64>(ElfBuilder<Arch::AARCH64> &builder,
228 const std::string &cmdline, const std::string &file_name);
229
230 template <Arch ARCH>
EmitPlt(Span<typename ArchTraits<ARCH>::WordType> ptrView,size_t gotDataSize)231 void AotBuilder::EmitPlt(Span<typename ArchTraits<ARCH>::WordType> ptrView, size_t gotDataSize)
232 {
233 if (!gotPlt_.empty() || !gotVirtIndexes_.empty() || !gotClass_.empty() || !gotString_.empty() ||
234 !gotIntfInlineCache_.empty() || !gotCommon_.empty()) {
235 ASSERT(PointerSize(ARCH) >= sizeof(uint32_t));
236
237 auto ptrCnt = ptrView.Size();
238 auto end = static_cast<size_t>(RuntimeInterface::IntrinsicId::COUNT);
239
240 ptrView[ptrCnt - gotDataSize] = 0;
241 constexpr size_t IMM_2 = 2;
242 for (auto [method, idx] : gotPlt_) {
243 ASSERT(idx <= 0);
244 ptrView[ptrCnt - end + idx] = AotFile::AotSlotType::PLT_SLOT;
245 ptrView[ptrCnt - end + idx - IMM_2] = method.second;
246 }
247 for (auto [method, idx] : gotVirtIndexes_) {
248 ASSERT(idx <= 0);
249 ptrView[ptrCnt - end + idx] = AotFile::AotSlotType::VTABLE_INDEX;
250 ptrView[ptrCnt - end + idx - 1] = method.second;
251 }
252 for (auto [klass, idx] : gotClass_) {
253 ASSERT(idx <= 0);
254 ptrView[ptrCnt - end + idx] = AotFile::AotSlotType::CLASS_SLOT;
255 ptrView[ptrCnt - end + idx - IMM_2] = klass.second;
256 }
257 for (auto [string_id, idx] : gotString_) {
258 ASSERT(idx <= 0);
259 ptrView[ptrCnt - end + idx] = AotFile::AotSlotType::STRING_SLOT;
260 ptrView[ptrCnt - end + idx - 1] = string_id.second;
261 }
262 for (auto [cache, idx] : gotIntfInlineCache_) {
263 (void)cache;
264 ASSERT(idx < 0);
265 ptrView[ptrCnt - end + idx] = AotFile::AotSlotType::INLINECACHE_SLOT;
266 }
267 for (auto [cache, idx] : gotCommon_) {
268 (void)cache;
269 ASSERT(idx < 0);
270 ptrView[ptrCnt - end + idx] = AotFile::AotSlotType::COMMON_SLOT;
271 }
272 }
273 }
274
275 /// Add method names to the symbol table
276 template <Arch ARCH>
GenerateSymbols(ElfBuilder<ARCH> & builder)277 void AotBuilder::GenerateSymbols(ElfBuilder<ARCH> &builder)
278 {
279 if (generateSymbols_) {
280 auto textSection = builder.GetTextSection();
281 std::string name;
282 ASSERT(methods_.size() == methodHeaders_.size());
283 for (size_t i = 0; i < methods_.size(); i++) {
284 auto method = methods_.at(i).GetMethod();
285 if (method->GetPandaFile() == nullptr) {
286 name = "Error: method doesn't belong to any panda file";
287 } else {
288 auto methodCasted = reinterpret_cast<RuntimeInterface::MethodPtr>(method);
289 name = runtime_->GetMethodFullName(methodCasted, true);
290 }
291 size_t offset = methodHeaders_[i].codeOffset;
292 builder.template AddSymbol<true>(name, methodHeaders_[i].codeSize, *textSection, [offset, textSection]() {
293 return textSection->GetAddress() + offset + CodeInfo::GetCodeOffset(ARCH);
294 });
295 }
296 }
297 }
298
AddClassHashTable(const panda_file::File & pandaFile)299 void AotBuilder::AddClassHashTable(const panda_file::File &pandaFile)
300 {
301 const panda_file::File::Header *header = pandaFile.GetHeader();
302 uint32_t numClasses = header->numClasses;
303 if (numClasses == 0) {
304 return;
305 }
306
307 size_t hashTableSize = panda::helpers::math::GetPowerOfTwoValue32(numClasses);
308 std::vector<panda_file::EntityPairHeader> entityPairs;
309 std::vector<unsigned int> conflictEntityTable;
310 entityPairs.resize(hashTableSize);
311 conflictEntityTable.resize(hashTableSize);
312 size_t conflictNum = 0;
313
314 auto classes = pandaFile.GetClasses();
315 for (size_t i = 0; i < numClasses; ++i) {
316 auto entityId = panda_file::File::EntityId(classes[i]);
317 auto name = pandaFile.GetStringData(entityId).data;
318 uint32_t hash = GetHash32String(name);
319 uint32_t pos = hash & (hashTableSize - 1);
320 auto &entityPair = entityPairs[pos];
321 if (entityPair.descriptorHash == 0) {
322 entityPair.descriptorHash = hash;
323 entityPair.entityIdOffset = entityId.GetOffset();
324 } else {
325 conflictEntityTable[conflictNum] = i;
326 conflictNum++;
327 }
328 }
329 if (conflictNum == 0) {
330 entityPairHeaders_.insert(entityPairHeaders_.end(), entityPairs.begin(), entityPairs.end());
331 classHashTablesSize_.emplace_back(entityPairs.size());
332 } else {
333 ResolveConflictClassHashTable(pandaFile, std::move(conflictEntityTable), conflictNum, entityPairs);
334 }
335 }
336
ResolveConflictClassHashTable(const panda_file::File & pandaFile,std::vector<unsigned int> conflictEntityTable,size_t conflictNum,std::vector<panda_file::EntityPairHeader> & entityPairs)337 void AotBuilder::ResolveConflictClassHashTable(const panda_file::File &pandaFile,
338 std::vector<unsigned int> conflictEntityTable, size_t conflictNum,
339 std::vector<panda_file::EntityPairHeader> &entityPairs)
340 {
341 auto classes = pandaFile.GetClasses();
342 auto hashTableSize = entityPairs.size();
343 for (size_t j = 0; j < conflictNum; ++j) {
344 if (j > 0 && conflictEntityTable[j - 1] == conflictEntityTable[j]) {
345 break; // Exit for loop if there is no conlict elements anymore
346 }
347 auto i = conflictEntityTable[j];
348 auto entityId = panda_file::File::EntityId(classes[i]);
349 auto name = pandaFile.GetStringData(entityId).data;
350 uint32_t hash = GetHash32String(name);
351 uint32_t theoryPos = hash & (hashTableSize - 1);
352 ASSERT(entityPairs[theoryPos].descriptorHash != 0);
353
354 uint32_t actualPos = theoryPos;
355 while (actualPos < (hashTableSize - 1) && entityPairs[actualPos].descriptorHash != 0) {
356 actualPos++;
357 }
358 if (actualPos == (hashTableSize - 1) && entityPairs[actualPos].descriptorHash != 0) {
359 actualPos = 0;
360 while (actualPos < theoryPos && entityPairs[actualPos].descriptorHash != 0) {
361 actualPos++;
362 }
363 }
364 ASSERT(entityPairs[actualPos].descriptorHash == 0);
365 auto &entityPair = entityPairs[actualPos];
366 entityPair.descriptorHash = hash;
367 entityPair.entityIdOffset = entityId.GetOffset();
368 while (entityPairs[theoryPos].nextPos != 0) {
369 theoryPos = entityPairs[theoryPos].nextPos - 1;
370 }
371 // add 1 is to distinguish the initial value 0 of next_pos and the situation that the next pos is really 0
372 entityPairs[theoryPos].nextPos = actualPos + 1;
373 }
374 entityPairHeaders_.insert(entityPairHeaders_.end(), entityPairs.begin(), entityPairs.end());
375 classHashTablesSize_.emplace_back(entityPairs.size());
376 }
377
378 } // namespace panda::compiler
379