1 /*
2 * Copyright (c) 2021-2024 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 ark::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
GetClassHashTableABC1(const std::string & fn)125 void GetClassHashTableABC1(const std::string &fn)
126 {
127 auto source = R"(
128 .record Animal {}
129 .function i32 Animal.fun() <static> {
130 ldai 1
131 return
132 }
133 )";
134
135 pandasm::Parser parser;
136 auto res = parser.Parse(source);
137 ASSERT_TRUE(res);
138 ASSERT_TRUE(pandasm::AsmEmitter::Emit(fn, res.Value()));
139 }
140
GetClassHashTableABC2(const std::string & fn)141 void GetClassHashTableABC2(const std::string &fn)
142 {
143 auto source = R"(
144 .record Animal <external>
145 .record Cat {}
146 .record Dog {}
147 .record Pig {}
148 .function i32 Animal.fun() <external, static>
149 .function i32 Cat.main() <static> {
150 call.short Animal.fun
151 return
152 }
153 )";
154
155 pandasm::Parser parser;
156 auto res = parser.Parse(source);
157 ASSERT_TRUE(res);
158 ASSERT_TRUE(pandasm::AsmEmitter::Emit(fn, res.Value()));
159 }
160
GetClassHashTableAN(const std::string & fn1,const std::string & fn2,const std::string & fnAot,const std::string & fnPaoc,const std::string & stdlib)161 void GetClassHashTableAN(const std::string &fn1, const std::string &fn2, const std::string &fnAot,
162 const std::string &fnPaoc, const std::string &stdlib)
163 {
164 auto res = os::exec::Exec(fnPaoc.c_str(), "--paoc-panda-files", fn2.c_str(), "--panda-files", fn1.c_str(),
165 "--paoc-output", fnAot.c_str(), "--boot-panda-files", stdlib.c_str());
166 ASSERT_TRUE(res);
167 ASSERT_EQ(res.Value(), 0U);
168 }
169
TEST_F(ClassHashTableTest,GetClassHashTable)170 TEST_F(ClassHashTableTest, GetClassHashTable)
171 {
172 if (RUNTIME_ARCH != Arch::X86_64) {
173 GTEST_SKIP();
174 }
175
176 TmpFile aotFname("TestTwo.an");
177 TmpFile pandaFname1("Animal.abc");
178 TmpFile pandaFname2("Cat.abc");
179
180 GetClassHashTableABC1(pandaFname1.GetFileName());
181 GetClassHashTableABC2(pandaFname2.GetFileName());
182 GetClassHashTableAN(pandaFname1.GetFileName(), pandaFname2.GetFileName(), aotFname.GetFileName(), GetPaocFile(),
183 GetPandaStdLibFile());
184
185 std::string filename = os::GetAbsolutePath(aotFname.GetFileName());
186 auto aotFileRet = AotFile::Open(filename, 0U, true);
187 ASSERT(aotFileRet.Value() != nullptr);
188 auto aotFile = std::move(aotFileRet.Value());
189
190 for (size_t i = 0; i < aotFile->GetFilesCount(); i++) {
191 auto fileHeader = aotFile->FileHeaders()[i];
192 auto table = aotFile->GetClassHashTable(fileHeader);
193
194 ASSERT(!table.empty());
195 ASSERT_EQ(table.size(), fileHeader.classHashTableSize);
196 }
197
198 for (size_t i = 0; i < aotFile->GetFilesCount(); i++) {
199 auto fileHeader = aotFile->FileHeaders()[i];
200 AotPandaFile aotPandaFile(aotFile.get(), &fileHeader);
201 auto table = aotPandaFile.GetClassHashTable();
202
203 ASSERT(!table.empty());
204 ASSERT_EQ(table.size(), fileHeader.classHashTableSize);
205 }
206 }
207
TEST_F(ClassHashTableTest,DumpClassHashTable)208 TEST_F(ClassHashTableTest, DumpClassHashTable)
209 {
210 if (RUNTIME_ARCH != Arch::X86_64) {
211 GTEST_SKIP();
212 }
213
214 TmpFile pandaFname("DumpClassHashTableTest.abc");
215 TmpFile aotFname("DumpClassHashTableTest.an");
216
217 {
218 pandasm::Parser parser;
219 auto res = parser.Parse(GetSourceCode());
220 ASSERT_TRUE(res);
221 ASSERT_TRUE(pandasm::AsmEmitter::Emit(pandaFname.GetFileName(), res.Value()));
222 }
223
224 {
225 auto res = os::exec::Exec(GetPaocFile(), "--paoc-panda-files", pandaFname.GetFileName(), "--paoc-output",
226 aotFname.GetFileName(), "--boot-panda-files", GetPandaStdLibFile());
227 ASSERT_TRUE(res);
228 ASSERT_EQ(res.Value(), 0U);
229 }
230
231 {
232 std::string filename = os::GetAbsolutePath(aotFname.GetFileName());
233 auto res = os::exec::Exec(GetAotdumpFile(), "--show-code=disasm", filename.c_str());
234 ASSERT_TRUE(res);
235 ASSERT_EQ(res.Value(), 0U);
236 }
237 }
238
TEST_F(ClassHashTableTest,LoadClassHashTableFromAnFileToAbcFile)239 TEST_F(ClassHashTableTest, LoadClassHashTableFromAnFileToAbcFile)
240 {
241 if (RUNTIME_ARCH != Arch::X86_64) {
242 GTEST_SKIP();
243 }
244
245 TmpFile pandaFname("LoadClassHashTableFromAnFileToAbcFileTest.abc");
246 TmpFile aotFname("LoadClassHashTableFromAnFileToAbcFileTest.an");
247
248 {
249 pandasm::Parser parser;
250 auto res = parser.Parse(GetSourceCode());
251 ASSERT_TRUE(res);
252 ASSERT_TRUE(pandasm::AsmEmitter::Emit(pandaFname.GetFileName(), res.Value()));
253 }
254
255 auto pfile = panda_file::OpenPandaFile(pandaFname.GetFileName());
256
257 {
258 auto res = os::exec::Exec(GetPaocFile(), "--paoc-panda-files", pandaFname.GetFileName(), "--paoc-output",
259 aotFname.GetFileName(), "--boot-panda-files", GetPandaStdLibFile());
260 ASSERT_TRUE(res);
261 ASSERT_EQ(res.Value(), 0U);
262 }
263
264 std::string filename = os::GetAbsolutePath(aotFname.GetFileName());
265 auto aotFileRet = AotFile::Open(filename, 0U, true);
266 ASSERT(aotFileRet.Value() != nullptr);
267 auto aotFile = std::move(aotFileRet.Value());
268
269 auto fileHeader = aotFile->FileHeaders()[0U];
270 AotPandaFile aotPandaFile(aotFile.get(), &fileHeader);
271 pfile->SetClassHashTable(aotPandaFile.GetClassHashTable());
272
273 ASSERT(!pfile->GetClassHashTable().empty());
274 ASSERT_EQ(pfile->GetClassHashTable().size(), aotPandaFile.GetClassHashTable().size());
275 }
276
TEST_F(ClassHashTableTest,LoadAbcFileCanLoadClassHashTable)277 TEST_F(ClassHashTableTest, LoadAbcFileCanLoadClassHashTable)
278 {
279 if (RUNTIME_ARCH != Arch::X86_64) {
280 GTEST_SKIP();
281 }
282
283 TmpFile pandaFname("LoadAbcFileTest.abc");
284 TmpFile aotFname("LoadAbcFileTest.an");
285
286 {
287 pandasm::Parser parser;
288 auto res = parser.Parse(GetSourceCode());
289 ASSERT_TRUE(res);
290 ASSERT_TRUE(pandasm::AsmEmitter::Emit(pandaFname.GetFileName(), res.Value()));
291 }
292
293 auto pfile = panda_file::OpenPandaFile(pandaFname.GetFileName());
294
295 {
296 auto res = os::exec::Exec(GetPaocFile(), "--gc-type=epsilon", "--paoc-panda-files", pandaFname.GetFileName(),
297 "--paoc-output", aotFname.GetFileName(), "--boot-panda-files", GetPandaStdLibFile());
298 ASSERT_TRUE(res);
299 ASSERT_EQ(res.Value(), 0U);
300 }
301
302 std::string filename = os::GetAbsolutePath(pandaFname.GetFileName());
303 auto res = FileManager::LoadAbcFile(filename, panda_file::File::READ_ONLY);
304 ASSERT_TRUE(res);
305
306 const panda_file::File *pfPtr = nullptr;
307 auto checkFilename = [&pfPtr, filename](const panda_file::File &pf) {
308 if (pf.GetFilename() == filename) {
309 pfPtr = &pf;
310 return false;
311 }
312 return true;
313 };
314 Runtime::GetCurrent()->GetClassLinker()->EnumeratePandaFiles(checkFilename);
315
316 ASSERT(!pfPtr->GetClassHashTable().empty());
317 }
318
GetClassIdFromClassHashTableABC(const std::string & source,const std::string & fnOut)319 void GetClassIdFromClassHashTableABC(const std::string &source, const std::string &fnOut)
320 {
321 pandasm::Parser parser;
322 auto res = parser.Parse(source);
323 ASSERT_TRUE(res);
324 ASSERT_TRUE(pandasm::AsmEmitter::Emit(fnOut, res.Value()));
325 }
326
GetClassIdFromClassHashTableAN(const std::string & fn,const std::string & fnAot,const std::string & fnPaoc,const std::string & stdlib)327 void GetClassIdFromClassHashTableAN(const std::string &fn, const std::string &fnAot, const std::string &fnPaoc,
328 const std::string &stdlib)
329 {
330 auto res = os::exec::Exec(fnPaoc.c_str(), "--gc-type=epsilon", "--paoc-panda-files", fn.c_str(), "--paoc-output",
331 fnAot.c_str(), "--boot-panda-files", stdlib.c_str());
332 ASSERT_TRUE(res);
333 ASSERT_EQ(res.Value(), 0U);
334 }
335
TEST_F(ClassHashTableTest,GetClassIdFromClassHashTable)336 TEST_F(ClassHashTableTest, GetClassIdFromClassHashTable)
337 {
338 if (RUNTIME_ARCH != Arch::X86_64) {
339 GTEST_SKIP();
340 }
341
342 TmpFile pandaFname("GetClassIdFromClassHashTableTest.abc");
343 TmpFile aotFname("GetClassIdFromClassHashTableTest.an");
344
345 GetClassIdFromClassHashTableABC(GetSourceCode(), pandaFname.GetFileName());
346
347 auto pfile = panda_file::OpenPandaFile(pandaFname.GetFileName());
348
349 auto *descriptorA = reinterpret_cast<const uint8_t *>("A");
350 auto *descriptorB = reinterpret_cast<const uint8_t *>("B");
351 auto *descriptorC = reinterpret_cast<const uint8_t *>("C");
352 auto *descriptorD = reinterpret_cast<const uint8_t *>("D");
353 auto *descriptorE = reinterpret_cast<const uint8_t *>("E");
354
355 auto classId1A = pfile->GetClassId(descriptorA);
356 auto classId1B = pfile->GetClassId(descriptorB);
357 auto classId1C = pfile->GetClassId(descriptorC);
358 auto classId1D = pfile->GetClassId(descriptorD);
359 auto classId1E = pfile->GetClassId(descriptorE);
360
361 GetClassIdFromClassHashTableAN(pandaFname.GetFileName(), aotFname.GetFileName(), GetPaocFile(),
362 GetPandaStdLibFile());
363
364 std::string filename = os::GetAbsolutePath(pandaFname.GetFileName());
365 auto res = FileManager::LoadAbcFile(filename, panda_file::File::READ_ONLY);
366 ASSERT_TRUE(res);
367
368 const panda_file::File *pfPtr = nullptr;
369 Runtime::GetCurrent()->GetClassLinker()->EnumeratePandaFiles([&pfPtr, filename](const panda_file::File &pf) {
370 if (pf.GetFilename() == filename) {
371 pfPtr = &pf;
372 return false;
373 }
374 return true;
375 });
376
377 auto classId2A = pfPtr->GetClassIdFromClassHashTable(descriptorA);
378 auto classId2B = pfPtr->GetClassIdFromClassHashTable(descriptorB);
379 auto classId2C = pfPtr->GetClassIdFromClassHashTable(descriptorC);
380 auto classId2D = pfPtr->GetClassIdFromClassHashTable(descriptorD);
381 auto classId2E = pfPtr->GetClassIdFromClassHashTable(descriptorE);
382
383 ASSERT_EQ(classId1A, classId2A);
384 ASSERT_EQ(classId1B, classId2B);
385 ASSERT_EQ(classId1C, classId2C);
386 ASSERT_EQ(classId1D, classId2D);
387 ASSERT_EQ(classId1E, classId2E);
388 }
389
390 } // namespace ark::compiler
391