• 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 <gtest/gtest.h>
17 
18 #include <string>
19 #include <vector>
20 
21 #include "assembly-parser.h"
22 #include "libpandafile/bytecode_emitter.h"
23 #include "libpandafile/bytecode_instruction.h"
24 #include "libpandafile/file_items.h"
25 #include "libpandafile/shorty_iterator.h"
26 #include "libpandafile/value.h"
27 #include "runtime/arch/helpers.h"
28 #include "runtime/bridge/bridge.h"
29 #include "runtime/entrypoints/entrypoints.h"
30 #include "runtime/include/method.h"
31 #include "runtime/include/runtime.h"
32 #include "runtime/include/thread.h"
33 #include "runtime/tests/interpreter/test_runtime_interface.h"
34 #include "invokation_helper.h"
35 
36 using TypeId = panda::panda_file::Type::TypeId;
37 using BytecodeEmitter = panda::BytecodeEmitter;
38 
39 namespace panda::test {
40 
CmpDynImpl(Method *,DecodedTaggedValue v1,DecodedTaggedValue v2)41 int32_t CmpDynImpl(Method *, DecodedTaggedValue v1, DecodedTaggedValue v2)
42 {
43     return v1.value == v2.value && v1.tag == v2.tag ? 0 : 1;
44 }
45 
LdUndefinedImpl(Method * method)46 DecodedTaggedValue LdUndefinedImpl(Method *method)
47 {
48     return Runtime::GetCurrent()->GetLanguageContext(*method).GetInitialDecodedValue();
49 }
50 
51 class CompiledCodeToInterpreterBridgeTest : public testing::Test {
52 public:
SetUp()53     void SetUp()
54     {
55         // See comment in InvokeEntryPoint function
56         if constexpr (RUNTIME_ARCH == Arch::AARCH32) {
57             GTEST_SKIP();
58         }
59 
60         RuntimeOptions options;
61         options.SetShouldLoadBootPandaFiles(false);
62         options.SetShouldInitializeIntrinsics(false);
63         options.SetGcType("epsilon");
64 
65         Runtime::Create(options);
66         thread_ = MTManagedThread::GetCurrent();
67         thread_->ManagedCodeBegin();
68         SetUpHelperFunctions("PandaAssembly");
69     }
70 
~CompiledCodeToInterpreterBridgeTest()71     ~CompiledCodeToInterpreterBridgeTest()
72     {
73         if constexpr (RUNTIME_ARCH == Arch::AARCH32) {
74             return;
75         }
76         thread_->ManagedCodeEnd();
77         Runtime::Destroy();
78     }
79 
SetUpHelperFunctions(const std::string & language)80     void SetUpHelperFunctions(const std::string &language)
81     {
82         Runtime *runtime = Runtime::GetCurrent();
83         ClassLinker *class_linker = runtime->GetClassLinker();
84 
85         std::string source = ".language " + language + "\n" + R"(
86             .record TestUtils {}
87             .function i32 TestUtils.cmpDyn(any a0, any a1) <native>
88             .function any TestUtils.ldundefined() <native>
89         )";
90         pandasm::Parser p;
91         auto res = p.Parse(source);
92         ASSERT_TRUE(res.HasValue());
93         std::unique_ptr<const panda_file::File> pf = pandasm::AsmEmitter::Emit(res.Value());
94         class_linker->AddPandaFile(std::move(pf));
95 
96         auto descriptor = std::make_unique<PandaString>();
97         std::optional<panda::panda_file::SourceLang> lang = panda_file::LanguageFromString(language);
98         if (!lang) {
99             UNREACHABLE();
100         }
101         auto *extension = class_linker->GetExtension(lang.value_or(panda_file::SourceLang::PANDA_ASSEMBLY));
102         Class *klass =
103             extension->GetClass(ClassHelper::GetDescriptor(utf::CStringAsMutf8("TestUtils"), descriptor.get()));
104 
105         Method *cmpDyn = klass->GetDirectMethod(utf::CStringAsMutf8("cmpDyn"));
106         ASSERT_NE(cmpDyn, nullptr);
107         cmpDyn->SetCompiledEntryPoint(reinterpret_cast<const void *>(CmpDynImpl));
108 
109         Method *ldundefined = klass->GetDirectMethod(utf::CStringAsMutf8("ldundefined"));
110         ASSERT_NE(ldundefined, nullptr);
111         ldundefined->SetCompiledEntryPoint(reinterpret_cast<const void *>(LdUndefinedImpl));
112     }
113 
MakeNoArgsMethod(TypeId ret_type,int64_t ret)114     Method *MakeNoArgsMethod(TypeId ret_type, int64_t ret)
115     {
116         Runtime *runtime = Runtime::GetCurrent();
117         ClassLinker *class_linker = runtime->GetClassLinker();
118         LanguageContext ctx = runtime->GetLanguageContext(lang_);
119 
120         std::ostringstream out;
121         out << ".language " << ctx << '\n';
122         if (ret_type == TypeId::REFERENCE) {
123             // 'operator <<' for TypeId::REFERENCE returns 'reference'. So create a class to handle this situation.
124             out << ".record reference {}\n";
125         }
126         out << ".function " << panda_file::Type(ret_type) << " main() {\n";
127         if (TypeId::F32 <= ret_type && ret_type <= TypeId::F64) {
128             out << "fldai.64 " << bit_cast<double>(ret) << '\n';
129             out << "return.64\n";
130         } else if (TypeId::I64 <= ret_type && ret_type <= TypeId::U64) {
131             out << "ldai.64 " << ret << '\n';
132             out << "return.64\n";
133         } else if (ret_type == TypeId::REFERENCE) {
134             out << "lda.null\n";
135             out << "return.obj\n";
136         } else if (ret_type == TypeId::TAGGED) {
137             out << "ldai.dyn " << ret << '\n';
138             out << "return.dyn\n";
139         } else {
140             out << "ldai " << ret << '\n';
141             out << "return\n";
142         }
143         out << "}";
144 
145         pandasm::Parser p;
146         auto res = p.Parse(out.str());
147         std::unique_ptr<const panda_file::File> pf = pandasm::AsmEmitter::Emit(res.Value());
148         class_linker->AddPandaFile(std::move(pf));
149 
150         auto descriptor = std::make_unique<PandaString>();
151         auto *extension = class_linker->GetExtension(ctx);
152         Class *klass =
153             extension->GetClass(ClassHelper::GetDescriptor(utf::CStringAsMutf8("_GLOBAL"), descriptor.get()));
154 
155         Method *main = klass->GetDirectMethod(utf::CStringAsMutf8("main"));
156         main->SetInterpreterEntryPoint();
157         return main;
158     }
159 
MakeCheckArgsMethod(const std::initializer_list<TypeId> & shorty,const std::initializer_list<int64_t> args,bool is_instance=false)160     Method *MakeCheckArgsMethod(const std::initializer_list<TypeId> &shorty, const std::initializer_list<int64_t> args,
161                                 bool is_instance = false)
162     {
163         Runtime *runtime = Runtime::GetCurrent();
164         ClassLinker *class_linker = runtime->GetClassLinker();
165         LanguageContext ctx = runtime->GetLanguageContext(lang_);
166 
167         std::ostringstream out;
168         std::ostringstream signature;
169         std::ostringstream body;
170         uint32_t arg_num = 0;
171 
172         if (is_instance) {
173             signature << "Test a0";
174             body << "lda.null\n";
175             body << "jne.obj a0, fail\n";
176             ++arg_num;
177         }
178         auto shorty_it = shorty.begin();
179         TypeId ret_type = *shorty_it++;
180         auto args_it = args.begin();
181         while (shorty_it != shorty.end()) {
182             if (args_it == args.end()) {
183                 // only dynamic methods could be called with less arguments than declared
184                 ASSERT(*shorty_it == TypeId::TAGGED);
185             }
186             if (arg_num > 0) {
187                 signature << ", ";
188             }
189             if ((TypeId::F32 <= *shorty_it && *shorty_it <= TypeId::U64) || *shorty_it == TypeId::REFERENCE ||
190                 *shorty_it == TypeId::TAGGED) {
191                 signature << panda_file::Type(*shorty_it) << " a" << arg_num;
192                 if (TypeId::F32 <= *shorty_it && *shorty_it <= TypeId::F64) {
193                     body << "fldai.64 " << bit_cast<double>(*args_it) << '\n';
194                     body << "fcmpg.64 a" << arg_num << '\n';
195                     body << "jnez fail\n";
196                 } else if (TypeId::I64 <= *shorty_it && *shorty_it <= TypeId::U64) {
197                     body << "ldai.64 " << *args_it << '\n';
198                     body << "cmp.64 a" << arg_num << '\n';
199                     body << "jnez fail\n";
200                 } else if (*shorty_it == TypeId::TAGGED) {
201                     if (args_it == args.end()) {
202                         body << "call.short TestUtils.ldundefined\n";
203                     } else {
204                         body << "ldai.dyn " << *args_it << '\n';
205                     }
206                     body << "sta.dyn v0\n";
207                     body << "call.short TestUtils.cmpDyn, v0, a" << arg_num << '\n';
208                     body << "jnez fail\n";
209                 } else {
210                     body << "lda.null\n";
211                     body << "jne.obj a" << arg_num << ", fail\n";
212                 }
213             } else {
214                 signature << "i32 a" << arg_num;
215                 body << "ldai " << *args_it << '\n';
216                 body << "jne a" << arg_num << ", fail\n";
217             }
218             ++shorty_it;
219             if (args_it != args.end()) {
220                 ++args_it;
221             }
222             ++arg_num;
223         }
224         if (ret_type == TypeId::TAGGED) {
225             body << "ldai.dyn 1\n";
226             body << "return.dyn\n";
227             body << "fail:\n";
228             body << "ldai.dyn 0\n";
229             body << "return.dyn\n";
230         } else {
231             body << "ldai 1\n";
232             body << "return\n";
233             body << "fail:\n";
234             body << "ldai 0\n";
235             body << "return\n";
236         }
237 
238         out << ".language " << ctx << '\n';
239         out << ".record TestUtils <external>\n";
240         out << ".function i32 TestUtils.cmpDyn(any a0, any a1) <external>\n";
241         out << ".function any TestUtils.ldundefined() <external>\n";
242         out << ".record reference {}\n";
243         out << ".record Test {}\n";
244         out << ".function " << panda_file::Type(ret_type) << " Test.main(" << signature.str() << ") {\n";
245         out << body.str();
246         out << "}";
247 
248         pandasm::Parser p;
249         auto res = p.Parse(out.str());
250         std::unique_ptr<const panda_file::File> pf = pandasm::AsmEmitter::Emit(res.Value());
251         class_linker->AddPandaFile(std::move(pf));
252 
253         auto descriptor = std::make_unique<PandaString>();
254         auto *extension = class_linker->GetExtension(ctx);
255         Class *klass = extension->GetClass(ClassHelper::GetDescriptor(utf::CStringAsMutf8("Test"), descriptor.get()));
256 
257         Method *main = klass->GetDirectMethod(utf::CStringAsMutf8("main"));
258         main->SetInterpreterEntryPoint();
259         return main;
260     }
261 
262     MTManagedThread *thread_ {nullptr};
263     panda_file::SourceLang lang_ {panda_file::SourceLang::PANDA_ASSEMBLY};
264 };
265 
TEST_F(CompiledCodeToInterpreterBridgeTest,InvokeVoidNoArg)266 TEST_F(CompiledCodeToInterpreterBridgeTest, InvokeVoidNoArg)
267 {
268     auto method = MakeNoArgsMethod(TypeId::VOID, 0);
269     InvokeEntryPoint<void>(method);
270 }
271 
TEST_F(CompiledCodeToInterpreterBridgeTest,InvokeIntNoArg)272 TEST_F(CompiledCodeToInterpreterBridgeTest, InvokeIntNoArg)
273 {
274     auto method = MakeNoArgsMethod(TypeId::I32, 5);
275     int32_t res = InvokeEntryPoint<int32_t>(method);
276     ASSERT_EQ(res, 5);
277 }
278 
TEST_F(CompiledCodeToInterpreterBridgeTest,InvokeLongNoArg)279 TEST_F(CompiledCodeToInterpreterBridgeTest, InvokeLongNoArg)
280 {
281     auto method = MakeNoArgsMethod(TypeId::I64, 7);
282 
283     int64_t res = InvokeEntryPoint<int64_t>(method);
284     ASSERT_EQ(res, 7);
285 }
286 
TEST_F(CompiledCodeToInterpreterBridgeTest,InvokeDoubleNoArg)287 TEST_F(CompiledCodeToInterpreterBridgeTest, InvokeDoubleNoArg)
288 {
289     auto method = MakeNoArgsMethod(TypeId::F64, bit_cast<int64_t>(3.0));
290 
291     auto res = InvokeEntryPoint<double>(method);
292     ASSERT_EQ(res, 3.0);
293 }
294 
TEST_F(CompiledCodeToInterpreterBridgeTest,InvokeObjNoArg)295 TEST_F(CompiledCodeToInterpreterBridgeTest, InvokeObjNoArg)
296 {
297     auto method = MakeNoArgsMethod(TypeId::REFERENCE, 0);
298 
299     ObjectHeader *res = InvokeEntryPoint<ObjectHeader *>(method);
300     ASSERT_EQ(res, nullptr);
301 }
302 
TEST_F(CompiledCodeToInterpreterBridgeTest,InvokeTaggedNoArg)303 TEST_F(CompiledCodeToInterpreterBridgeTest, InvokeTaggedNoArg)
304 {
305     auto method = MakeNoArgsMethod(TypeId::TAGGED, 1);
306 
307     DecodedTaggedValue res = InvokeEntryPoint<DecodedTaggedValue>(method);
308     ASSERT_EQ(res.value, coretypes::TaggedValue(1).GetRawData());
309 }
310 
311 /// Args tests:
TEST_F(CompiledCodeToInterpreterBridgeTest,InvokeInt)312 TEST_F(CompiledCodeToInterpreterBridgeTest, InvokeInt)
313 {
314     auto method = MakeCheckArgsMethod({TypeId::I32, TypeId::I32}, {5});
315 
316     int32_t res = InvokeEntryPoint<int32_t>(method, 5);
317     ASSERT_EQ(res, 1);
318 }
319 
TEST_F(CompiledCodeToInterpreterBridgeTest,InvokeInstanceInt)320 TEST_F(CompiledCodeToInterpreterBridgeTest, InvokeInstanceInt)
321 {
322     auto method = MakeCheckArgsMethod({TypeId::I32}, {0, 5}, true);
323 
324     int32_t res = InvokeEntryPoint<int32_t>(method, nullptr, 5);
325     ASSERT_EQ(res, 1);
326 }
327 
TEST_F(CompiledCodeToInterpreterBridgeTest,Invoke3Int)328 TEST_F(CompiledCodeToInterpreterBridgeTest, Invoke3Int)
329 {
330     auto method = MakeCheckArgsMethod({TypeId::I32, TypeId::I32, TypeId::I32, TypeId::I32}, {3, 2, 1});
331 
332     int32_t res = InvokeEntryPoint<int32_t>(method, 3, 2, 1);
333     ASSERT_EQ(res, 1);
334 }
335 
TEST_F(CompiledCodeToInterpreterBridgeTest,InvokeLong)336 TEST_F(CompiledCodeToInterpreterBridgeTest, InvokeLong)
337 {
338     auto method = MakeCheckArgsMethod({TypeId::I32, TypeId::I64}, {7});
339 
340     int32_t res = InvokeEntryPoint<int32_t>(method, 7);
341     ASSERT_EQ(res, 1);
342 }
343 
TEST_F(CompiledCodeToInterpreterBridgeTest,InvokeDouble)344 TEST_F(CompiledCodeToInterpreterBridgeTest, InvokeDouble)
345 {
346     auto method = MakeCheckArgsMethod({TypeId::I32, TypeId::F64}, {bit_cast<int64_t>(2.0)});
347 
348     int32_t res = InvokeEntryPoint<int32_t>(method, 2.0);
349     ASSERT_EQ(res, 1);
350 }
351 
TEST_F(CompiledCodeToInterpreterBridgeTest,Invoke4Int)352 TEST_F(CompiledCodeToInterpreterBridgeTest, Invoke4Int)
353 {
354     auto method = MakeCheckArgsMethod({TypeId::I32, TypeId::I32, TypeId::I32, TypeId::I32, TypeId::I32}, {4, 3, 2, 1});
355 
356     int32_t res = InvokeEntryPoint<int32_t>(method, 4, 3, 2, 1);
357     ASSERT_EQ(res, 1);
358 }
359 
TEST_F(CompiledCodeToInterpreterBridgeTest,Invoke2Long)360 TEST_F(CompiledCodeToInterpreterBridgeTest, Invoke2Long)
361 {
362     auto method = MakeCheckArgsMethod({TypeId::I32, TypeId::I64, TypeId::I64}, {7, 8});
363 
364     int32_t res = InvokeEntryPoint<int32_t>(method, 7, 8);
365     ASSERT_EQ(res, 1);
366 }
367 
TEST_F(CompiledCodeToInterpreterBridgeTest,Invoke4IntDouble)368 TEST_F(CompiledCodeToInterpreterBridgeTest, Invoke4IntDouble)
369 {
370     auto method = MakeCheckArgsMethod({TypeId::I32, TypeId::I32, TypeId::I32, TypeId::I32, TypeId::I32, TypeId::F64},
371                                       {4, 3, 2, 1, bit_cast<int64_t>(8.0)});
372 
373     int32_t res = InvokeEntryPoint<int32_t>(method, 4, 3, 2, 1, 8.0);
374     ASSERT_EQ(res, 1);
375 }
376 
TEST_F(CompiledCodeToInterpreterBridgeTest,Invoke7Int)377 TEST_F(CompiledCodeToInterpreterBridgeTest, Invoke7Int)
378 {
379     auto method = MakeCheckArgsMethod(
380         {TypeId::I32, TypeId::I32, TypeId::I32, TypeId::I32, TypeId::I32, TypeId::I32, TypeId::I32, TypeId::I32},
381         {7, 6, 5, 4, 3, 2, 1});
382 
383     int32_t res = InvokeEntryPoint<int32_t>(method, 7, 6, 5, 4, 3, 2, 1);
384     ASSERT_EQ(res, 1);
385 }
386 
TEST_F(CompiledCodeToInterpreterBridgeTest,Invoke7Int8Double)387 TEST_F(CompiledCodeToInterpreterBridgeTest, Invoke7Int8Double)
388 {
389     auto method = MakeCheckArgsMethod({TypeId::I32, TypeId::I32, TypeId::I32, TypeId::I32, TypeId::I32, TypeId::I32,
390                                        TypeId::I32, TypeId::I32, TypeId::F64, TypeId::F64, TypeId::F64, TypeId::F64,
391                                        TypeId::F64, TypeId::F64, TypeId::F64, TypeId::F64},
392                                       {7, 6, 5, 4, 3, 2, 1, bit_cast<int64_t>(10.0), bit_cast<int64_t>(11.0),
393                                        bit_cast<int64_t>(12.0), bit_cast<int64_t>(13.0), bit_cast<int64_t>(14.0),
394                                        bit_cast<int64_t>(15.0), bit_cast<int64_t>(16.0), bit_cast<int64_t>(17.0)});
395 
396     int32_t res =
397         InvokeEntryPoint<int32_t>(method, 7, 6, 5, 4, 3, 2, 1, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0, 17.0);
398     ASSERT_EQ(res, 1);
399 }
400 
TEST_F(CompiledCodeToInterpreterBridgeTest,Invoke8Int)401 TEST_F(CompiledCodeToInterpreterBridgeTest, Invoke8Int)
402 {
403     auto method = MakeCheckArgsMethod({TypeId::I32, TypeId::I32, TypeId::I32, TypeId::I32, TypeId::I32, TypeId::I32,
404                                        TypeId::I32, TypeId::I32, TypeId::I32},
405                                       {8, 7, 6, 5, 4, 3, 2, 1});
406 
407     int32_t res = InvokeEntryPoint<int32_t>(method, 8, 7, 6, 5, 4, 3, 2, 1);
408     ASSERT_EQ(res, 1);
409 }
410 
TEST_F(CompiledCodeToInterpreterBridgeTest,Invoke8Int9Double)411 TEST_F(CompiledCodeToInterpreterBridgeTest, Invoke8Int9Double)
412 {
413     auto method = MakeCheckArgsMethod({TypeId::I32, TypeId::I32, TypeId::I32, TypeId::I32, TypeId::I32, TypeId::I32,
414                                        TypeId::I32, TypeId::I32, TypeId::I32, TypeId::F64, TypeId::F64, TypeId::F64,
415                                        TypeId::F64, TypeId::F64, TypeId::F64, TypeId::F64, TypeId::F64, TypeId::F64},
416                                       {8, 7, 6, 5, 4, 3, 2, 1, bit_cast<int64_t>(10.0), bit_cast<int64_t>(11.0),
417                                        bit_cast<int64_t>(12.0), bit_cast<int64_t>(13.0), bit_cast<int64_t>(14.0),
418                                        bit_cast<int64_t>(15.0), bit_cast<int64_t>(16.0), bit_cast<int64_t>(17.0),
419                                        bit_cast<int64_t>(18.0)});
420 
421     int32_t res =
422         InvokeEntryPoint<int32_t>(method, 8, 7, 6, 5, 4, 3, 2, 1, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0, 16.0, 17.0, 18.0);
423     ASSERT_EQ(res, 1);
424 }
425 
426 // Dynamic functions
427 
428 }  // namespace panda::test
429