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/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 exe_path = GetExecPath();
30 auto pos = exe_path.rfind('/');
31 paoc_path_ = exe_path.substr(0, pos) + "/../bin/ark_aot";
32 aotdump_path_ = exe_path.substr(0, pos) + "/../bin/ark_aotdump";
33 pandastdlib_path_ = GetPaocDirectory() + "/../pandastdlib/arkstdlib.abc";
34 }
35
GetPaocFile() const36 const char *GetPaocFile() const
37 {
38 return paoc_path_.c_str();
39 }
40
GetAotdumpFile() const41 const char *GetAotdumpFile() const
42 {
43 return aotdump_path_.c_str();
44 }
45
GetPandaStdLibFile() const46 const char *GetPandaStdLibFile() const
47 {
48 return pandastdlib_path_.c_str();
49 }
50
GetPaocDirectory() const51 std::string GetPaocDirectory() const
52 {
53 auto pos = paoc_path_.rfind('/');
54 return paoc_path_.substr(0, pos);
55 }
56
GetSourceCode() const57 const std::string GetSourceCode() const
58 {
59 return source_;
60 }
61
62 protected:
63 std::string paoc_path_;
64 std::string aotdump_path_;
65 std::string pandastdlib_path_;
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 panda_fname("AddClassHashTableTest.abc");
103
104 {
105 pandasm::Parser parser;
106 auto res = parser.Parse(GetSourceCode());
107 ASSERT_TRUE(res);
108 ASSERT_TRUE(pandasm::AsmEmitter::Emit(panda_fname.GetFileName(), res.Value()));
109 }
110
111 auto panda_file_ptr = panda_file::OpenPandaFile(panda_fname.GetFileName());
112 ASSERT(panda_file_ptr != nullptr);
113 auto &pfile_ref = *panda_file_ptr.get();
114
115 AotBuilder aot_builder;
116 aot_builder.AddClassHashTable(pfile_ref);
117
118 auto entity_pair_headers = aot_builder.GetEntityPairHeaders();
119 auto class_hash_tables_size = aot_builder.GetClassHashTableSize()->back();
120
121 ASSERT(!entity_pair_headers->empty());
122 ASSERT_EQ(class_hash_tables_size, entity_pair_headers->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 aot_fname("TestTwo.an");
132 TmpFile panda_fname1("Animal.abc");
133 TmpFile panda_fname2("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(panda_fname1.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(panda_fname2.GetFileName(), res.Value()));
167 }
168
169 {
170 auto res = os::exec::Exec(GetPaocFile(), "--paoc-panda-files", panda_fname2.GetFileName(), "--panda-files",
171 panda_fname1.GetFileName(), "--paoc-output", aot_fname.GetFileName(),
172 "--boot-panda-files", GetPandaStdLibFile());
173 ASSERT_TRUE(res);
174 ASSERT_EQ(res.Value(), 0);
175 }
176
177 std::string filename = os::GetAbsolutePath(aot_fname.GetFileName());
178 auto aot_file_ret = AotFile::Open(filename, 0, true);
179 ASSERT(aot_file_ret.Value() != nullptr);
180 auto aot_file = std::move(aot_file_ret.Value());
181
182 for (size_t i = 0; i < aot_file->GetFilesCount(); i++) {
183 auto file_header = aot_file->FileHeaders()[i];
184 auto table = aot_file->GetClassHashTable(file_header);
185
186 ASSERT(!table.empty());
187 ASSERT_EQ(table.size(), file_header.class_hash_table_size);
188 }
189
190 for (size_t i = 0; i < aot_file->GetFilesCount(); i++) {
191 auto file_header = aot_file->FileHeaders()[i];
192 AotPandaFile aot_panda_file(aot_file.get(), &file_header);
193 auto table = aot_panda_file.GetClassHashTable();
194
195 ASSERT(!table.empty());
196 ASSERT_EQ(table.size(), file_header.class_hash_table_size);
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 panda_fname("DumpClassHashTableTest.abc");
207 TmpFile aot_fname("DumpClassHashTableTest.an");
208
209 {
210 pandasm::Parser parser;
211 auto res = parser.Parse(GetSourceCode());
212 ASSERT_TRUE(res);
213 ASSERT_TRUE(pandasm::AsmEmitter::Emit(panda_fname.GetFileName(), res.Value()));
214 }
215
216 {
217 auto res = os::exec::Exec(GetPaocFile(), "--paoc-panda-files", panda_fname.GetFileName(), "--paoc-output",
218 aot_fname.GetFileName(), "--boot-panda-files", GetPandaStdLibFile());
219 ASSERT_TRUE(res);
220 ASSERT_EQ(res.Value(), 0);
221 }
222
223 {
224 std::string filename = os::GetAbsolutePath(aot_fname.GetFileName());
225 auto res = os::exec::Exec(GetAotdumpFile(), "--show-code=disasm", filename.c_str());
226 ASSERT_TRUE(res);
227 ASSERT_EQ(res.Value(), 0);
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 panda_fname("LoadClassHashTableFromAnFileToAbcFileTest.abc");
238 TmpFile aot_fname("LoadClassHashTableFromAnFileToAbcFileTest.an");
239
240 {
241 pandasm::Parser parser;
242 auto res = parser.Parse(GetSourceCode());
243 ASSERT_TRUE(res);
244 ASSERT_TRUE(pandasm::AsmEmitter::Emit(panda_fname.GetFileName(), res.Value()));
245 }
246
247 auto pfile = panda_file::OpenPandaFile(panda_fname.GetFileName());
248
249 {
250 auto res = os::exec::Exec(GetPaocFile(), "--paoc-panda-files", panda_fname.GetFileName(), "--paoc-output",
251 aot_fname.GetFileName(), "--boot-panda-files", GetPandaStdLibFile());
252 ASSERT_TRUE(res);
253 ASSERT_EQ(res.Value(), 0);
254 }
255
256 std::string filename = os::GetAbsolutePath(aot_fname.GetFileName());
257 auto aot_file_ret = AotFile::Open(filename, 0, true);
258 ASSERT(aot_file_ret.Value() != nullptr);
259 auto aot_file = std::move(aot_file_ret.Value());
260
261 auto file_header = aot_file->FileHeaders()[0];
262 AotPandaFile aot_panda_file(aot_file.get(), &file_header);
263 pfile->SetClassHashTable(aot_panda_file.GetClassHashTable());
264
265 ASSERT(!pfile->GetClassHashTable().empty());
266 ASSERT_EQ(pfile->GetClassHashTable().size(), aot_panda_file.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 panda_fname("LoadAbcFileTest.abc");
276 TmpFile aot_fname("LoadAbcFileTest.an");
277
278 {
279 pandasm::Parser parser;
280 auto res = parser.Parse(GetSourceCode());
281 ASSERT_TRUE(res);
282 ASSERT_TRUE(pandasm::AsmEmitter::Emit(panda_fname.GetFileName(), res.Value()));
283 }
284
285 auto pfile = panda_file::OpenPandaFile(panda_fname.GetFileName());
286
287 {
288 auto res = os::exec::Exec(GetPaocFile(), "--gc-type=epsilon", "--paoc-panda-files", panda_fname.GetFileName(),
289 "--paoc-output", aot_fname.GetFileName(), "--boot-panda-files", GetPandaStdLibFile());
290 ASSERT_TRUE(res);
291 ASSERT_EQ(res.Value(), 0);
292 }
293
294 std::string filename = os::GetAbsolutePath(panda_fname.GetFileName());
295 auto res = FileManager::LoadAbcFile(filename, panda_file::File::READ_ONLY);
296 ASSERT_TRUE(res);
297
298 const panda_file::File *pf_ptr = nullptr;
299 Runtime::GetCurrent()->GetClassLinker()->EnumeratePandaFiles([&pf_ptr, filename](const panda_file::File &pf) {
300 if (pf.GetFilename() == filename) {
301 pf_ptr = &pf;
302 return false;
303 }
304 return true;
305 });
306
307 ASSERT(!pf_ptr->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 panda_fname("GetClassIdFromClassHashTableTest.abc");
317 TmpFile aot_fname("GetClassIdFromClassHashTableTest.an");
318
319 {
320 pandasm::Parser parser;
321 auto res = parser.Parse(GetSourceCode());
322 ASSERT_TRUE(res);
323 ASSERT_TRUE(pandasm::AsmEmitter::Emit(panda_fname.GetFileName(), res.Value()));
324 }
325
326 auto pfile = panda_file::OpenPandaFile(panda_fname.GetFileName());
327
328 const uint8_t *descriptor_A = reinterpret_cast<const uint8_t *>("A");
329 const uint8_t *descriptor_B = reinterpret_cast<const uint8_t *>("B");
330 const uint8_t *descriptor_C = reinterpret_cast<const uint8_t *>("C");
331 const uint8_t *descriptor_D = reinterpret_cast<const uint8_t *>("D");
332 const uint8_t *descriptor_E = reinterpret_cast<const uint8_t *>("E");
333
334 auto class_id1_A = pfile->GetClassId(descriptor_A);
335 auto class_id1_B = pfile->GetClassId(descriptor_B);
336 auto class_id1_C = pfile->GetClassId(descriptor_C);
337 auto class_id1_D = pfile->GetClassId(descriptor_D);
338 auto class_id1_E = pfile->GetClassId(descriptor_E);
339
340 {
341 auto res = os::exec::Exec(GetPaocFile(), "--gc-type=epsilon", "--paoc-panda-files", panda_fname.GetFileName(),
342 "--paoc-output", aot_fname.GetFileName(), "--boot-panda-files", GetPandaStdLibFile());
343 ASSERT_TRUE(res);
344 ASSERT_EQ(res.Value(), 0);
345 }
346
347 std::string filename = os::GetAbsolutePath(panda_fname.GetFileName());
348 auto res = FileManager::LoadAbcFile(filename, panda_file::File::READ_ONLY);
349 ASSERT_TRUE(res);
350
351 const panda_file::File *pf_ptr = nullptr;
352 Runtime::GetCurrent()->GetClassLinker()->EnumeratePandaFiles([&pf_ptr, filename](const panda_file::File &pf) {
353 if (pf.GetFilename() == filename) {
354 pf_ptr = &pf;
355 return false;
356 }
357 return true;
358 });
359
360 auto class_id2_A = pf_ptr->GetClassIdFromClassHashTable(descriptor_A);
361 auto class_id2_B = pf_ptr->GetClassIdFromClassHashTable(descriptor_B);
362 auto class_id2_C = pf_ptr->GetClassIdFromClassHashTable(descriptor_C);
363 auto class_id2_D = pf_ptr->GetClassIdFromClassHashTable(descriptor_D);
364 auto class_id2_E = pf_ptr->GetClassIdFromClassHashTable(descriptor_E);
365
366 ASSERT_EQ(class_id1_A, class_id2_A);
367 ASSERT_EQ(class_id1_B, class_id2_B);
368 ASSERT_EQ(class_id1_C, class_id2_C);
369 ASSERT_EQ(class_id1_D, class_id2_D);
370 ASSERT_EQ(class_id1_E, class_id2_E);
371 }
372
373 } // namespace panda::compiler
374