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