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 <string>
17 #include <vector>
18
19 #include "assembler/assembly-program.h"
20 #include "assembler/assembly-emitter.h"
21 #include "common.h"
22 #include "compiler/optimizer/ir/datatype.h"
23 #include "runtime_adapter.h"
24 #include "utils/utf.h"
25
26 namespace panda::bytecodeopt::test {
27
ParseAndEmit(const std::string & source)28 std::unique_ptr<const panda_file::File> ParseAndEmit(const std::string &source)
29 {
30 panda::pandasm::Parser parser;
31 auto res = parser.Parse(source);
32 if (parser.ShowError().err != pandasm::Error::ErrorType::ERR_NONE) {
33 std::cerr << "Parse failed: " << parser.ShowError().message << std::endl
34 << parser.ShowError().whole_line << std::endl;
35 ADD_FAILURE();
36 }
37 auto &program = res.Value();
38 return pandasm::AsmEmitter::Emit(program);
39 }
40
41 struct Pointers {
42 std::vector<RuntimeInterface::MethodPtr> method;
43 std::vector<RuntimeInterface::ClassPtr> klass;
44 std::vector<RuntimeInterface::FieldPtr> field;
45 };
46
GetPointers(const panda_file::File * arkf)47 Pointers GetPointers(const panda_file::File *arkf)
48 {
49 Pointers pointers;
50
51 for (uint32_t id : arkf->GetClasses()) {
52 panda_file::File::EntityId record_id {id};
53
54 if (arkf->IsExternal(record_id)) {
55 continue;
56 }
57
58 panda_file::ClassDataAccessor cda {*arkf, record_id};
59 auto class_id = cda.GetClassId().GetOffset();
60 auto class_ptr = reinterpret_cast<compiler::RuntimeInterface::ClassPtr>(class_id);
61 pointers.klass.push_back(class_ptr);
62
63 cda.EnumerateFields([&pointers](panda_file::FieldDataAccessor &fda) {
64 auto field_ptr = reinterpret_cast<compiler::RuntimeInterface::FieldPtr>(fda.GetFieldId().GetOffset());
65 pointers.field.push_back(field_ptr);
66 });
67
68 cda.EnumerateMethods([&pointers](panda_file::MethodDataAccessor &mda) {
69 auto method_ptr = reinterpret_cast<compiler::RuntimeInterface::MethodPtr>(mda.GetMethodId().GetOffset());
70 pointers.method.push_back(method_ptr);
71 });
72 }
73
74 return pointers;
75 }
76
TEST(RuntimeAdapter,Common)77 TEST(RuntimeAdapter, Common)
78 {
79 auto source = std::string(R"(
80 .function u1 main() {
81 ldai 1
82 return
83 }
84 )");
85 std::unique_ptr<const panda_file::File> arkf = ParseAndEmit(source);
86 auto pointers = GetPointers(arkf.get());
87
88 ASSERT_EQ(pointers.method.size(), 1);
89 ASSERT_EQ(pointers.klass.size(), 1);
90 ASSERT_EQ(pointers.field.size(), 0);
91
92 BytecodeOptimizerRuntimeAdapter adapter(*arkf.get());
93 auto main = pointers.method[0];
94 auto global = pointers.klass[0];
95
96 EXPECT_FALSE(adapter.IsMethodIntrinsic(main));
97 EXPECT_NE(adapter.GetBinaryFileForMethod(main), nullptr);
98 EXPECT_EQ(adapter.GetMethodById(main, 0), nullptr);
99 EXPECT_NE(adapter.GetMethodId(main), 0);
100 EXPECT_EQ(adapter.GetMethodTotalArgumentType(main, 0), compiler::DataType::Type::ANY);
101 EXPECT_EQ(adapter.GetMethodReturnType(pointers.method[0]), compiler::DataType::Type::BOOL);
102 EXPECT_EQ(adapter.GetMethodRegistersCount(main), 0);
103 EXPECT_NE(adapter.GetMethodCode(main), nullptr);
104 EXPECT_NE(adapter.GetMethodCodeSize(main), 0);
105 EXPECT_TRUE(adapter.IsMethodStatic(main));
106 EXPECT_FALSE(adapter.HasNativeException(main));
107 EXPECT_EQ(adapter.GetClassNameFromMethod(main), std::string("L_GLOBAL;"));
108 EXPECT_EQ(adapter.GetMethodName(main), std::string("main"));
109 EXPECT_EQ(adapter.GetMethodFullName(main, false), std::string("L_GLOBAL;::main"));
110 EXPECT_EQ(adapter.GetBytecodeString(main, 0), std::string("ldai 1"));
111
112 EXPECT_EQ(adapter.GetClassName(global), std::string("L_GLOBAL;"));
113 EXPECT_EQ(adapter.GetClass(main), global);
114 }
115
TEST(RuntimeAdapter,Klass)116 TEST(RuntimeAdapter, Klass)
117 {
118 auto source = std::string(R"(
119 .record R {
120 i32 field
121 }
122
123 .function void R.ctor(R a0) <ctor> {
124 return.void
125 }
126
127 .function void main() {}
128 )");
129 std::unique_ptr<const panda_file::File> arkf = ParseAndEmit(source);
130 auto pointers = GetPointers(arkf.get());
131
132 ASSERT_EQ(pointers.method.size(), 2);
133 ASSERT_EQ(pointers.klass.size(), 2);
134 ASSERT_EQ(pointers.field.size(), 1);
135
136 BytecodeOptimizerRuntimeAdapter adapter(*arkf.get());
137 auto klass = pointers.klass[0];
138 auto ctor = pointers.method[0];
139 auto main = pointers.method[1];
140
141 EXPECT_EQ(adapter.GetMethodName(main), std::string("main"));
142 EXPECT_EQ(adapter.GetMethodName(ctor), std::string(".ctor"));
143
144 auto class_id = reinterpret_cast<uint64_t>(klass);
145 EXPECT_FALSE(adapter.IsMethodExternal(main, ctor));
146 EXPECT_FALSE(adapter.IsConstructor(main, class_id));
147 EXPECT_EQ(adapter.GetMethodTotalArgumentType(ctor, 0), compiler::DataType::Type::REFERENCE);
148 EXPECT_EQ(adapter.GetMethodTotalArgumentType(ctor, 1), compiler::DataType::Type::ANY);
149 EXPECT_EQ(adapter.IsArrayClass(ctor, class_id), false);
150 }
151
TEST(RuntimeAdapter,Methods)152 TEST(RuntimeAdapter, Methods)
153 {
154 auto source = std::string(R"(
155 .record System <external>
156 .function void System.exit(i32 a0) <external>
157
158 .function u64 func_ret_u64(u64 a0) {
159 return
160 }
161
162 .function i16 func_ret_i16(i16 a0) {
163 return
164 }
165
166 .function u1 main(u32 a0, u16 a1, f32 a2, f64 a3) {
167 movi v0, 0
168 call System.exit, v0
169 ldai 1
170 return
171 }
172 )");
173 std::unique_ptr<const panda_file::File> arkf = ParseAndEmit(source);
174 auto pointers = GetPointers(arkf.get());
175
176 ASSERT_EQ(pointers.method.size(), 3);
177 ASSERT_EQ(pointers.klass.size(), 1);
178 ASSERT_EQ(pointers.field.size(), 0);
179
180 BytecodeOptimizerRuntimeAdapter adapter(*arkf.get());
181 auto main = pointers.method[0];
182 auto func_ret_i16 = pointers.method[1];
183 auto func_ret_u64 = pointers.method[2];
184
185 EXPECT_EQ(adapter.GetMethodName(func_ret_u64), std::string("func_ret_u64"));
186 EXPECT_EQ(adapter.GetMethodName(func_ret_i16), std::string("func_ret_i16"));
187 EXPECT_EQ(adapter.GetMethodName(main), std::string("main"));
188
189 EXPECT_EQ(adapter.GetMethodReturnType(func_ret_i16), compiler::DataType::Type::INT16);
190 EXPECT_EQ(adapter.GetMethodReturnType(func_ret_u64), compiler::DataType::Type::UINT64);
191
192 const auto method_id = adapter.ResolveMethodIndex(main, 0);
193 EXPECT_NE(method_id, 0);
194 EXPECT_NE(adapter.GetClassIdForMethod(main, method_id), 0);
195 EXPECT_EQ(adapter.GetMethodArgumentType(main, method_id, 0), compiler::DataType::Type::INT32);
196
197 EXPECT_EQ(adapter.GetMethodTotalArgumentType(main, 0), compiler::DataType::Type::UINT32);
198 EXPECT_EQ(adapter.GetMethodTotalArgumentType(main, 1), compiler::DataType::Type::UINT16);
199 EXPECT_EQ(adapter.GetMethodTotalArgumentType(main, 2), compiler::DataType::Type::FLOAT32);
200 EXPECT_EQ(adapter.GetMethodTotalArgumentType(main, 3), compiler::DataType::Type::FLOAT64);
201 }
202
TEST(RuntimeAdapter,Fields)203 TEST(RuntimeAdapter, Fields)
204 {
205 auto source = std::string(R"(
206 .record R {
207 i32 field
208 }
209
210 .record Record {
211 i64 v_i64 <static>
212 }
213
214 .function void store_to_static(i64 a0){
215 lda.64 a0
216 ststatic.64 Record.v_i64
217 return.void
218 }
219 )");
220 std::unique_ptr<const panda_file::File> arkf = ParseAndEmit(source);
221 auto pointers = GetPointers(arkf.get());
222
223 ASSERT_EQ(pointers.method.size(), 1);
224 ASSERT_EQ(pointers.klass.size(), 3);
225 ASSERT_EQ(pointers.field.size(), 2);
226
227 BytecodeOptimizerRuntimeAdapter adapter(*arkf.get());
228 auto store_to_static = pointers.method[0];
229 auto record_with_static_field = pointers.klass[1];
230 auto field = pointers.field[0];
231
232 EXPECT_EQ(adapter.GetMethodName(store_to_static), std::string("store_to_static"));
233
234 EXPECT_EQ(adapter.GetFieldName(field), std::string("field"));
235 EXPECT_EQ(adapter.GetFieldType(field), compiler::DataType::Type::INT32);
236 const auto field_id = adapter.ResolveFieldIndex(store_to_static, 0);
237 EXPECT_NE(field_id, 0);
238 EXPECT_EQ(adapter.GetClassIdForField(store_to_static, field_id),
239 reinterpret_cast<uint64_t>(record_with_static_field));
240 uint32_t immut_var = 0;
241 const auto field_ptr = adapter.ResolveField(store_to_static, field_id, false, &immut_var);
242 EXPECT_EQ(immut_var, 0);
243 EXPECT_EQ(adapter.GetClassForField(field_ptr), record_with_static_field);
244 EXPECT_EQ(adapter.GetFieldTypeById(store_to_static, field_id), compiler::DataType::Type::INT64);
245 EXPECT_EQ(adapter.IsFieldVolatile(field_ptr), false);
246 }
247
248 } // namespace panda::bytecodeopt::test
249