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/aot_builder/aot_builder.h"
17 #include "aot/aot_manager.h"
18 #include "assembly-parser.h"
19 #include "unit_test.h"
20 #include "os/exec.h"
21 #include "os/filesystem.h"
22 #include "runtime/include/file_manager.h"
23
24 namespace panda::compiler {
25 class ClassHashTableTest : public AsmTest {
26 public:
ClassHashTableTest()27 ClassHashTableTest()
28 {
29 std::string exePath = GetExecPath();
30 auto pos = exePath.rfind('/');
31 paocPath_ = exePath.substr(0U, pos) + "/../bin/ark_aot";
32 aotdumpPath_ = exePath.substr(0U, pos) + "/../bin/ark_aotdump";
33 pandastdlibPath_ = GetPaocDirectory() + "/../pandastdlib/arkstdlib.abc";
34 }
35
GetPaocFile() const36 const char *GetPaocFile() const
37 {
38 return paocPath_.c_str();
39 }
40
GetAotdumpFile() const41 const char *GetAotdumpFile() const
42 {
43 return aotdumpPath_.c_str();
44 }
45
GetPandaStdLibFile() const46 const char *GetPandaStdLibFile() const
47 {
48 return pandastdlibPath_.c_str();
49 }
50
GetPaocDirectory() const51 std::string GetPaocDirectory() const
52 {
53 auto pos = paocPath_.rfind('/');
54 return paocPath_.substr(0U, pos);
55 }
56
GetSourceCode() const57 std::string GetSourceCode() const
58 {
59 return source_;
60 }
61
62 private:
63 std::string paocPath_;
64 std::string aotdumpPath_;
65 std::string pandastdlibPath_;
66 const std::string source_ = R"(
67 .record A {}
68 .record B {}
69 .record C {}
70 .record D {}
71 .record E {}
72
73 .function i32 A.f() {
74 ldai 1
75 return
76 }
77
78 .function i32 B.f() {
79 ldai 2
80 return
81 }
82
83 .function i32 C.f() {
84 ldai 3
85 return
86 }
87
88 .function i32 D.f() {
89 ldai 4
90 return
91 }
92
93 .function i32 main() {
94 ldai 0
95 return
96 }
97 )";
98 };
99
TEST_F(ClassHashTableTest,AddClassHashTable)100 TEST_F(ClassHashTableTest, AddClassHashTable)
101 {
102 TmpFile pandaFname("AddClassHashTableTest.abc");
103
104 {
105 pandasm::Parser parser;
106 auto res = parser.Parse(GetSourceCode());
107 ASSERT_TRUE(res);
108 ASSERT_TRUE(pandasm::AsmEmitter::Emit(pandaFname.GetFileName(), res.Value()));
109 }
110
111 auto pandaFilePtr = panda_file::OpenPandaFile(pandaFname.GetFileName());
112 ASSERT(pandaFilePtr != nullptr);
113 auto &pfileRef = *pandaFilePtr;
114
115 AotBuilder aotBuilder;
116 aotBuilder.AddClassHashTable(pfileRef);
117
118 auto entityPairHeaders = aotBuilder.GetEntityPairHeaders();
119 auto classHashTablesSize = aotBuilder.GetClassHashTableSize()->back();
120
121 ASSERT(!entityPairHeaders->empty());
122 ASSERT_EQ(classHashTablesSize, entityPairHeaders->size());
123 }
124
TEST_F(ClassHashTableTest,GetClassHashTable)125 TEST_F(ClassHashTableTest, GetClassHashTable)
126 {
127 if (RUNTIME_ARCH != Arch::X86_64) {
128 GTEST_SKIP();
129 }
130
131 TmpFile aotFname("TestTwo.an");
132 TmpFile pandaFname1("Animal.abc");
133 TmpFile pandaFname2("Cat.abc");
134
135 {
136 auto source = R"(
137 .record Animal {}
138 .function i32 Animal.fun() <static> {
139 ldai 1
140 return
141 }
142 )";
143
144 pandasm::Parser parser;
145 auto res = parser.Parse(source);
146 ASSERT_TRUE(res);
147 ASSERT_TRUE(pandasm::AsmEmitter::Emit(pandaFname1.GetFileName(), res.Value()));
148 }
149
150 {
151 auto source = R"(
152 .record Animal <external>
153 .record Cat {}
154 .record Dog {}
155 .record Pig {}
156 .function i32 Animal.fun() <external, static>
157 .function i32 Cat.main() <static> {
158 call.short Animal.fun
159 return
160 }
161 )";
162
163 pandasm::Parser parser;
164 auto res = parser.Parse(source);
165 ASSERT_TRUE(res);
166 ASSERT_TRUE(pandasm::AsmEmitter::Emit(pandaFname2.GetFileName(), res.Value()));
167 }
168
169 {
170 auto res = os::exec::Exec(GetPaocFile(), "--paoc-panda-files", pandaFname2.GetFileName(), "--panda-files",
171 pandaFname1.GetFileName(), "--paoc-output", aotFname.GetFileName(),
172 "--boot-panda-files", GetPandaStdLibFile());
173 ASSERT_TRUE(res);
174 ASSERT_EQ(res.Value(), 0U);
175 }
176
177 std::string filename = os::GetAbsolutePath(aotFname.GetFileName());
178 auto aotFileRet = AotFile::Open(filename, 0U, true);
179 ASSERT(aotFileRet.Value() != nullptr);
180 auto aotFile = std::move(aotFileRet.Value());
181
182 for (size_t i = 0; i < aotFile->GetFilesCount(); i++) {
183 auto fileHeader = aotFile->FileHeaders()[i];
184 auto table = aotFile->GetClassHashTable(fileHeader);
185
186 ASSERT(!table.empty());
187 ASSERT_EQ(table.size(), fileHeader.classHashTableSize);
188 }
189
190 for (size_t i = 0; i < aotFile->GetFilesCount(); i++) {
191 auto fileHeader = aotFile->FileHeaders()[i];
192 AotPandaFile aotPandaFile(aotFile.get(), &fileHeader);
193 auto table = aotPandaFile.GetClassHashTable();
194
195 ASSERT(!table.empty());
196 ASSERT_EQ(table.size(), fileHeader.classHashTableSize);
197 }
198 }
199
TEST_F(ClassHashTableTest,DumpClassHashTable)200 TEST_F(ClassHashTableTest, DumpClassHashTable)
201 {
202 if (RUNTIME_ARCH != Arch::X86_64) {
203 GTEST_SKIP();
204 }
205
206 TmpFile pandaFname("DumpClassHashTableTest.abc");
207 TmpFile aotFname("DumpClassHashTableTest.an");
208
209 {
210 pandasm::Parser parser;
211 auto res = parser.Parse(GetSourceCode());
212 ASSERT_TRUE(res);
213 ASSERT_TRUE(pandasm::AsmEmitter::Emit(pandaFname.GetFileName(), res.Value()));
214 }
215
216 {
217 auto res = os::exec::Exec(GetPaocFile(), "--paoc-panda-files", pandaFname.GetFileName(), "--paoc-output",
218 aotFname.GetFileName(), "--boot-panda-files", GetPandaStdLibFile());
219 ASSERT_TRUE(res);
220 ASSERT_EQ(res.Value(), 0U);
221 }
222
223 {
224 std::string filename = os::GetAbsolutePath(aotFname.GetFileName());
225 auto res = os::exec::Exec(GetAotdumpFile(), "--show-code=disasm", filename.c_str());
226 ASSERT_TRUE(res);
227 ASSERT_EQ(res.Value(), 0U);
228 }
229 }
230
TEST_F(ClassHashTableTest,LoadClassHashTableFromAnFileToAbcFile)231 TEST_F(ClassHashTableTest, LoadClassHashTableFromAnFileToAbcFile)
232 {
233 if (RUNTIME_ARCH != Arch::X86_64) {
234 GTEST_SKIP();
235 }
236
237 TmpFile pandaFname("LoadClassHashTableFromAnFileToAbcFileTest.abc");
238 TmpFile aotFname("LoadClassHashTableFromAnFileToAbcFileTest.an");
239
240 {
241 pandasm::Parser parser;
242 auto res = parser.Parse(GetSourceCode());
243 ASSERT_TRUE(res);
244 ASSERT_TRUE(pandasm::AsmEmitter::Emit(pandaFname.GetFileName(), res.Value()));
245 }
246
247 auto pfile = panda_file::OpenPandaFile(pandaFname.GetFileName());
248
249 {
250 auto res = os::exec::Exec(GetPaocFile(), "--paoc-panda-files", pandaFname.GetFileName(), "--paoc-output",
251 aotFname.GetFileName(), "--boot-panda-files", GetPandaStdLibFile());
252 ASSERT_TRUE(res);
253 ASSERT_EQ(res.Value(), 0U);
254 }
255
256 std::string filename = os::GetAbsolutePath(aotFname.GetFileName());
257 auto aotFileRet = AotFile::Open(filename, 0U, true);
258 ASSERT(aotFileRet.Value() != nullptr);
259 auto aotFile = std::move(aotFileRet.Value());
260
261 auto fileHeader = aotFile->FileHeaders()[0U];
262 AotPandaFile aotPandaFile(aotFile.get(), &fileHeader);
263 pfile->SetClassHashTable(aotPandaFile.GetClassHashTable());
264
265 ASSERT(!pfile->GetClassHashTable().empty());
266 ASSERT_EQ(pfile->GetClassHashTable().size(), aotPandaFile.GetClassHashTable().size());
267 }
268
TEST_F(ClassHashTableTest,LoadAbcFileCanLoadClassHashTable)269 TEST_F(ClassHashTableTest, LoadAbcFileCanLoadClassHashTable)
270 {
271 if (RUNTIME_ARCH != Arch::X86_64) {
272 GTEST_SKIP();
273 }
274
275 TmpFile pandaFname("LoadAbcFileTest.abc");
276 TmpFile aotFname("LoadAbcFileTest.an");
277
278 {
279 pandasm::Parser parser;
280 auto res = parser.Parse(GetSourceCode());
281 ASSERT_TRUE(res);
282 ASSERT_TRUE(pandasm::AsmEmitter::Emit(pandaFname.GetFileName(), res.Value()));
283 }
284
285 auto pfile = panda_file::OpenPandaFile(pandaFname.GetFileName());
286
287 {
288 auto res = os::exec::Exec(GetPaocFile(), "--gc-type=epsilon", "--paoc-panda-files", pandaFname.GetFileName(),
289 "--paoc-output", aotFname.GetFileName(), "--boot-panda-files", GetPandaStdLibFile());
290 ASSERT_TRUE(res);
291 ASSERT_EQ(res.Value(), 0U);
292 }
293
294 std::string filename = os::GetAbsolutePath(pandaFname.GetFileName());
295 auto res = FileManager::LoadAbcFile(filename, panda_file::File::READ_ONLY);
296 ASSERT_TRUE(res);
297
298 const panda_file::File *pfPtr = nullptr;
299 Runtime::GetCurrent()->GetClassLinker()->EnumeratePandaFiles([&pfPtr, filename](const panda_file::File &pf) {
300 if (pf.GetFilename() == filename) {
301 pfPtr = &pf;
302 return false;
303 }
304 return true;
305 });
306
307 ASSERT(!pfPtr->GetClassHashTable().empty());
308 }
309
TEST_F(ClassHashTableTest,GetClassIdFromClassHashTable)310 TEST_F(ClassHashTableTest, GetClassIdFromClassHashTable)
311 {
312 if (RUNTIME_ARCH != Arch::X86_64) {
313 GTEST_SKIP();
314 }
315
316 TmpFile pandaFname("GetClassIdFromClassHashTableTest.abc");
317 TmpFile aotFname("GetClassIdFromClassHashTableTest.an");
318
319 {
320 pandasm::Parser parser;
321 auto res = parser.Parse(GetSourceCode());
322 ASSERT_TRUE(res);
323 ASSERT_TRUE(pandasm::AsmEmitter::Emit(pandaFname.GetFileName(), res.Value()));
324 }
325
326 auto pfile = panda_file::OpenPandaFile(pandaFname.GetFileName());
327
328 auto *descriptorA = reinterpret_cast<const uint8_t *>("A");
329 auto *descriptorB = reinterpret_cast<const uint8_t *>("B");
330 auto *descriptorC = reinterpret_cast<const uint8_t *>("C");
331 auto *descriptorD = reinterpret_cast<const uint8_t *>("D");
332 auto *descriptorE = reinterpret_cast<const uint8_t *>("E");
333
334 auto classId1A = pfile->GetClassId(descriptorA);
335 auto classId1B = pfile->GetClassId(descriptorB);
336 auto classId1C = pfile->GetClassId(descriptorC);
337 auto classId1D = pfile->GetClassId(descriptorD);
338 auto classId1E = pfile->GetClassId(descriptorE);
339
340 {
341 auto res = os::exec::Exec(GetPaocFile(), "--gc-type=epsilon", "--paoc-panda-files", pandaFname.GetFileName(),
342 "--paoc-output", aotFname.GetFileName(), "--boot-panda-files", GetPandaStdLibFile());
343 ASSERT_TRUE(res);
344 ASSERT_EQ(res.Value(), 0U);
345 }
346
347 std::string filename = os::GetAbsolutePath(pandaFname.GetFileName());
348 auto res = FileManager::LoadAbcFile(filename, panda_file::File::READ_ONLY);
349 ASSERT_TRUE(res);
350
351 const panda_file::File *pfPtr = nullptr;
352 Runtime::GetCurrent()->GetClassLinker()->EnumeratePandaFiles([&pfPtr, filename](const panda_file::File &pf) {
353 if (pf.GetFilename() == filename) {
354 pfPtr = &pf;
355 return false;
356 }
357 return true;
358 });
359
360 auto classId2A = pfPtr->GetClassIdFromClassHashTable(descriptorA);
361 auto classId2B = pfPtr->GetClassIdFromClassHashTable(descriptorB);
362 auto classId2C = pfPtr->GetClassIdFromClassHashTable(descriptorC);
363 auto classId2D = pfPtr->GetClassIdFromClassHashTable(descriptorD);
364 auto classId2E = pfPtr->GetClassIdFromClassHashTable(descriptorE);
365
366 ASSERT_EQ(classId1A, classId2A);
367 ASSERT_EQ(classId1B, classId2B);
368 ASSERT_EQ(classId1C, classId2C);
369 ASSERT_EQ(classId1D, classId2D);
370 ASSERT_EQ(classId1E, classId2E);
371 }
372
373 } // namespace panda::compiler
374