• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 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 "assembler/assembly-parser.h"
19 #include "assembler/meta.h"
20 #include "bytecode_optimizer/optimize_bytecode.h"
21 
22 namespace panda::bytecodeopt {
23 using ArrayValue = panda::pandasm::ArrayValue;
24 using ScalarValue = panda::pandasm::ScalarValue;
25 using AnnotationData = panda::pandasm::AnnotationData;
26 using AnnotationElement = panda::pandasm::AnnotationElement;
27 using TypeInfoMap = std::unordered_map<int32_t, TypeInfoIndex>;
28 
29 class TypeAdaptionTest : public testing::Test {
30 public:
SetUpTestCase(void)31     static void SetUpTestCase(void) {}
TearDownTestCase(void)32     static void TearDownTestCase(void) {}
SetUp()33     void SetUp() {}
TearDown()34     void TearDown() {}
35 
ExtractTypeinfo(const panda::pandasm::Function & fun,const panda::pandasm::Program & prog) const36     TypeInfoMap ExtractTypeinfo(const panda::pandasm::Function &fun, const panda::pandasm::Program &prog) const
37     {
38         const auto &annos = fun.metadata->GetAnnotations();
39         EXPECT_FALSE(annos.empty());
40         const auto &eles = annos[0].GetElements();
41         EXPECT_FALSE(eles.empty());
42         const auto *ele = eles[0].GetValue();
43         EXPECT_NE(ele, nullptr);
44         const auto key = ele->GetAsScalar()->GetValue<std::string>();
45         auto array_liter = prog.literalarray_table.find(key);
46         EXPECT_NE(array_liter, prog.literalarray_table.end());
47         const auto &array = array_liter->second.literals_;
48         // 4: size must be multiple of 4 because values consits of tuple of tag, order, tag, type
49         EXPECT_EQ(array.size() % 4u, 0u);
50         TypeInfoMap type_info;
51         size_t i = 1;  // 1: skip tag of order, so start from 1
52         while (i < array.size()) {
53             auto order = bit_cast<int32_t>(std::get<uint32_t>(array[i].value_));
54             i += 2;  // 2: skip tag between order and type
55             TypeInfoIndex type;
56             if (array[i].tag_ == panda_file::LiteralTag::LITERALARRAY) {
57                 type = std::get<std::string>(array[i].value_);
58             } else {
59                 EXPECT_EQ(array[i].tag_, panda_file::LiteralTag::BUILTINTYPEINDEX);
60                 type = std::get<BuiltinIndexType>(array[i].value_);
61             }
62             type_info.emplace(order, type);
63             i += 2;  // 2: skip tag between order and type
64         }
65         return type_info;
66     }
67 
CheckTypeExist(const TypeInfoMap & typeinfo,int32_t order,const TypeInfoIndex & type) const68     void CheckTypeExist(const TypeInfoMap &typeinfo, int32_t order, const TypeInfoIndex &type) const
69     {
70         auto type_it = typeinfo.find(order);
71         EXPECT_NE(type_it, typeinfo.end());
72         EXPECT_EQ(type_it->second, type);
73     }
74 
AddTypeinfo(panda::pandasm::LiteralArray & lit_arr,int32_t order,TypeInfoIndex type) const75     void AddTypeinfo(panda::pandasm::LiteralArray &lit_arr, int32_t order, TypeInfoIndex type) const
76     {
77         auto &arr = lit_arr.literals_;
78 
79         panda::pandasm::LiteralArray::Literal order_tag;
80         order_tag.tag_ = panda::panda_file::LiteralTag::TAGVALUE;
81         order_tag.value_ = static_cast<uint8_t>(panda::panda_file::LiteralTag::INTEGER);
82         arr.emplace_back(order_tag);
83 
84         panda::pandasm::LiteralArray::Literal order_val;
85         order_val.tag_ = panda::panda_file::LiteralTag::INTEGER;
86         order_val.value_ = static_cast<uint32_t>(order);
87         arr.emplace_back(order_val);
88 
89         panda::pandasm::LiteralArray::Literal type_tag;
90         panda::pandasm::LiteralArray::Literal type_val;
91         type_tag.tag_ = panda::panda_file::LiteralTag::TAGVALUE;
92 
93         if (std::holds_alternative<uint8_t>(type)) {
94             type_tag.value_ = static_cast<uint8_t>(panda::panda_file::LiteralTag::BUILTINTYPEINDEX);
95             type_val.tag_ = panda::panda_file::LiteralTag::BUILTINTYPEINDEX;
96             type_val.value_ = std::get<uint8_t>(type);
97         } else {
98             EXPECT_TRUE(std::holds_alternative<std::string>(type));
99             type_tag.value_ = static_cast<uint8_t>(panda::panda_file::LiteralTag::LITERALARRAY);
100             type_val.tag_ = panda::panda_file::LiteralTag::LITERALARRAY;
101             type_val.value_ = std::get<std::string>(type);
102         }
103         arr.emplace_back(type_tag);
104         arr.emplace_back(type_val);
105     }
106 
SetTypeAnnotationForFunc(const panda::pandasm::LiteralArray & arr,panda::pandasm::Function & func,panda::pandasm::Program & program) const107     void SetTypeAnnotationForFunc(const panda::pandasm::LiteralArray &arr, panda::pandasm::Function &func,
108                                   panda::pandasm::Program &program) const
109     {
110         auto id = std::to_string(program.literalarray_table.size());
111         program.literalarray_table.emplace(id, arr);
112 
113         AnnotationElement element(TSTYPE_ANNO_ELEMENT_NAME, std::make_unique<ScalarValue>(
114             ScalarValue::Create<panda::pandasm::Value::Type::LITERALARRAY>(id)));
115         AnnotationData annotation(TSTYPE_ANNO_RECORD_NAME);
116         annotation.AddElement(std::move(element));
117         std::vector<AnnotationData> annos;
118         annos.emplace_back(annotation);
119         func.metadata->SetAnnotations(std::move(annos));
120         const auto iterator = program.record_table.find(TSTYPE_ANNO_RECORD_NAME.data());
121         EXPECT_NE(iterator, program.record_table.end());
122         iterator->second.metadata->SetAccessFlags(panda::ACC_ANNOTATION);
123         EXPECT_TRUE(program.record_table.find(TSTYPE_ANNO_RECORD_NAME.data())->second.metadata->IsAnnotation());
124     }
125 
126     // add a literalarray as a type
AddAnTypeLiteralArray(panda::pandasm::Program & program) const127     TypeInfoIndex AddAnTypeLiteralArray(panda::pandasm::Program &program) const
128     {
129         panda::pandasm::LiteralArray arr;
130         panda::pandasm::LiteralArray::Literal tag;
131         tag.tag_ = panda::panda_file::LiteralTag::TAGVALUE;
132         tag.value_ = static_cast<uint8_t>(panda::panda_file::LiteralTag::BUILTINTYPEINDEX);
133         arr.literals_.emplace_back(tag);
134         panda::pandasm::LiteralArray::Literal val;
135         val.tag_ = panda::panda_file::LiteralTag::BUILTINTYPEINDEX;
136         val.value_ = static_cast<uint8_t>(0u);
137         arr.literals_.emplace_back(val);
138         const std::string litKey = std::to_string(program.literalarray_table.size());
139         program.literalarray_table.emplace(litKey, arr);
140         TypeInfoIndex ret = litKey;
141         return ret;
142     }
143 
EmitAndOptimize(const std::string & abcFileName,panda::pandasm::Program & program) const144     void EmitAndOptimize(const std::string &abcFileName, panda::pandasm::Program &program) const
145     {
146         std::map<std::string, size_t> *statp = nullptr;
147         panda::pandasm::AsmEmitter::PandaFileToPandaAsmMaps maps {};
148         panda::pandasm::AsmEmitter::PandaFileToPandaAsmMaps *mapsp = &maps;
149         EXPECT_TRUE(panda::pandasm::AsmEmitter::Emit(abcFileName, program, statp, mapsp, false));
150         EXPECT_TRUE(panda::bytecodeopt::OptimizeBytecode(&program, mapsp, abcFileName, true));
151     }
152 };
153 
154 HWTEST_F(TypeAdaptionTest, type_adaption_test_001, testing::ext::TestSize.Level1)
155 {
156     /* ts source code
157     function foo(a:number, b:string, c:string):string
158     {
159         let t:number = 100;
160         if (a > t) {
161             return b;
162         }
163         if (a < t) {
164             return c;
165         }
166         let ret:string = b + c;
167         return ret;
168     }
169     */
170     const auto source = R"(
171         .language ECMAScript
172         .record _ESTypeAnnotation <external>
173         .function any foo(any a0, any a1, any a2, any a3, any a4, any a5) <static> {
174             mov v5, a5
175             mov v4, a4
176             mov v3, a3
177             mov v2, a2
178             mov v1, a1
179             mov v0, a0
180             ldai 0x64
181             sta v6
182             lda v3
183             sta v9
184             lda v6
185             greater 0x0, v9
186             jeqz jump_label_0
187             lda v4
188             return
189         jump_label_0:
190             lda v3
191             sta v9
192             lda v6
193             less 0x1, v9
194             jeqz jump_label_1
195             lda v5
196             return
197         jump_label_1:
198             lda v4
199             sta v9
200             lda v5
201             add2 0x2, v9
202             sta v7
203             lda v7
204             return
205         }
206     )";
207     panda::pandasm::Parser parser;
208     auto res = parser.Parse(source);
209     auto &program = res.Value();
210     const std::string fun_name = "foo:(any,any,any,any,any,any)";
211     auto it = program.function_table.find(fun_name);
212     EXPECT_NE(it, program.function_table.end());
213 
214     auto &func = it->second;
215     panda::pandasm::LiteralArray lit_arr;
216     static const TypeInfoIndex NUM_TYPE = static_cast<uint8_t>(1);
217     static const TypeInfoIndex STR_TYPE = static_cast<uint8_t>(4);
218     // set arg type
219     const auto THIS_TYPE = AddAnTypeLiteralArray(program);
220     AddTypeinfo(lit_arr, -3, THIS_TYPE);  // -3: the arg "this"
221     AddTypeinfo(lit_arr, -4, NUM_TYPE);   // -4: the first arg
222     AddTypeinfo(lit_arr, -5, STR_TYPE);   // -5: the second arg
223     AddTypeinfo(lit_arr, -6, STR_TYPE);   // -6: the third arg
224     // set ins type
225     const size_t LDAI_IDX = 6;
226     EXPECT_EQ(func.ins[LDAI_IDX].opcode, panda::pandasm::Opcode::LDAI);
227     EXPECT_EQ(func.ins[LDAI_IDX + 1].opcode, panda::pandasm::Opcode::STA);
228     AddTypeinfo(lit_arr, static_cast<int32_t>(LDAI_IDX + 1), NUM_TYPE);
229     const size_t ADD_IDX = 27;
230     EXPECT_EQ(func.ins[ADD_IDX].opcode, panda::pandasm::Opcode::ADD2);
231     EXPECT_EQ(func.ins[ADD_IDX + 1].opcode, panda::pandasm::Opcode::STA);
232     int32_t num_invalid = std::count_if(func.ins.begin(), func.ins.begin() + ADD_IDX,
__anonc5413f460102(const auto &in) 233                                         [](const auto &in) { return in.opcode == panda::pandasm::Opcode::INVALID; });
234     AddTypeinfo(lit_arr, ADD_IDX + 1 - num_invalid, STR_TYPE);  // exclude invalid insns because they do not emit
235 
236     SetTypeAnnotationForFunc(lit_arr, func, program);
237 
238     EmitAndOptimize("TypeAdaptionTest.abc", program);
239 
240     // check typeinfo after optimization
241     it = program.function_table.find(fun_name);
242     EXPECT_NE(it, program.function_table.end());
243     const auto &foo = it->second;
244     const auto typeinfo = ExtractTypeinfo(foo, program);
245     CheckTypeExist(typeinfo, -3, THIS_TYPE);  // -3: the arg "this"
246     CheckTypeExist(typeinfo, -4, NUM_TYPE);   // -4: the first arg
247     CheckTypeExist(typeinfo, -5, STR_TYPE);   // -5: the second arg
248     CheckTypeExist(typeinfo, -6, STR_TYPE);   // -6: the third arg
249     auto ldai_it = std::find_if(foo.ins.begin(), foo.ins.end(),
__anonc5413f460202(const auto &in) 250                                 [](const auto &in) { return in.opcode == panda::pandasm::Opcode::LDAI; });
251     EXPECT_NE(ldai_it, foo.ins.end());
252     const auto opt_ldai_idx = static_cast<size_t>(ldai_it - foo.ins.begin());
253     EXPECT_EQ(foo.ins[opt_ldai_idx].opcode, panda::pandasm::Opcode::LDAI);
254     EXPECT_LT(opt_ldai_idx + 1, foo.ins.size());
255     EXPECT_EQ(foo.ins[opt_ldai_idx + 1].opcode, panda::pandasm::Opcode::STA);
256 
257     num_invalid = std::count_if(foo.ins.begin(), ldai_it,
__anonc5413f460302(const auto &in) 258                                 [](const auto &in) { return in.opcode == panda::pandasm::Opcode::INVALID; });
259     int32_t ldai_type_idx = opt_ldai_idx - num_invalid;  // exclude invalid insns because they do not emit
260 
261     CheckTypeExist(typeinfo, ldai_type_idx + 1, NUM_TYPE);  // type is on sta
262 
263     auto add_it = std::find_if(foo.ins.begin(), foo.ins.end(),
__anonc5413f460402(const auto &in) 264                                [](const auto &in) { return in.opcode == panda::pandasm::Opcode::ADD2; });
265     EXPECT_NE(add_it, foo.ins.end());
266     const auto opt_add_idx = static_cast<size_t>(add_it - foo.ins.begin());
267     EXPECT_EQ(foo.ins[opt_add_idx].opcode, panda::pandasm::Opcode::ADD2);
268     EXPECT_LT(opt_add_idx + 1, foo.ins.size());
269     EXPECT_NE(foo.ins[opt_add_idx + 1].opcode, panda::pandasm::Opcode::STA);
270 
271     num_invalid = std::count_if(foo.ins.begin(), add_it,
__anonc5413f460502(const auto &in) 272                                 [](const auto &in) { return in.opcode == panda::pandasm::Opcode::INVALID; });
273     int32_t add_type_idx = opt_add_idx - num_invalid;  // exclude invalid insns because they do not emit
274     CheckTypeExist(typeinfo, add_type_idx, STR_TYPE);  // type is on add2 as it does not have sta
275 }
276 
277 HWTEST_F(TypeAdaptionTest, unconditional_jump_case_test_001, testing::ext::TestSize.Level1)
278 {
279     /* ts source code
280     function foo(results: number) {
281         for (let i = 0; i < 1; i++) {
282             results *= i;
283         }
284         let s:number = results + 2;
285         return s;
286     }
287     */
288     const auto source = R"(
289         .language ECMAScript
290         .record _ESTypeAnnotation <external>
291         .function any foo(any a0, any a1, any a2, any a3) <static> {
292             mov v0, a0
293             mov v1, a1
294             mov v2, a2
295             mov v3, a3
296             ldai 0x0
297             sta v6
298         jump_label_1:
299             lda v6
300             sta v7
301             ldai 0x1
302             less 0x0, v7
303             jeqz jump_label_0
304             lda v3
305             sta v7
306             lda v6
307             mul2 0x1, v7
308             sta v3
309             lda v6
310             sta v7
311             lda v7
312             inc 0x2
313             sta v6
314             lda v7
315             tonumeric 0x3
316             jmp jump_label_1
317         jump_label_0:
318             lda v3
319             sta v6
320             ldai 0x2
321             add2 0x4, v6
322             sta v4
323             lda v4
324             return
325         }
326     )";
327     panda::pandasm::Parser parser;
328     auto res = parser.Parse(source);
329     auto &program = res.Value();
330     const std::string fun_name = "foo:(any,any,any,any)";
331     auto it = program.function_table.find(fun_name);
332     EXPECT_NE(it, program.function_table.end());
333 
334     auto &func = it->second;
335     panda::pandasm::LiteralArray lit_arr;
336     static const TypeInfoIndex NUM_TYPE = static_cast<uint8_t>(1);
337     static const TypeInfoIndex STR_TYPE = static_cast<uint8_t>(4);
338     // set arg type
339     const auto THIS_TYPE = AddAnTypeLiteralArray(program);
340     AddTypeinfo(lit_arr, -3, THIS_TYPE);  // -3: the arg "this"
341     AddTypeinfo(lit_arr, -4, NUM_TYPE);   // -4: the first arg
342     // set ins type
343     const size_t ADD_IDX = 29;
344     EXPECT_EQ(func.ins[ADD_IDX].opcode, panda::pandasm::Opcode::ADD2);
345     EXPECT_EQ(func.ins[ADD_IDX + 1].opcode, panda::pandasm::Opcode::STA);
346     int32_t num_invalid = std::count_if(func.ins.begin(), func.ins.begin() + ADD_IDX,
__anonc5413f460602(const auto &in) 347                                         [](const auto &in) { return in.opcode == panda::pandasm::Opcode::INVALID; });
348     AddTypeinfo(lit_arr, ADD_IDX + 1 - num_invalid, STR_TYPE);  // exclude invalid insns because they do not emit
349 
350     SetTypeAnnotationForFunc(lit_arr, func, program);
351 
352     EmitAndOptimize("TypeAdaptionTest_UnconditionalJump.abc", program);
353 
354     // check typeinfo after optimization
355     it = program.function_table.find(fun_name);
356     EXPECT_NE(it, program.function_table.end());
357     const auto &foo = it->second;
358     const auto typeinfo = ExtractTypeinfo(foo, program);
359     CheckTypeExist(typeinfo, -3, THIS_TYPE);  // -3: the arg "this"
360     CheckTypeExist(typeinfo, -4, NUM_TYPE);   // -4: the first arg
361     auto add_it = std::find_if(foo.ins.begin(), foo.ins.end(),
__anonc5413f460702(const auto &in) 362                                [](const auto &in) { return in.opcode == panda::pandasm::Opcode::ADD2; });
363     EXPECT_NE(add_it, foo.ins.end());
364     const auto opt_add_idx = static_cast<size_t>(add_it - foo.ins.begin());
365     EXPECT_EQ(foo.ins[opt_add_idx].opcode, panda::pandasm::Opcode::ADD2);
366     EXPECT_TRUE(opt_add_idx + 1 < foo.ins.size());
367     EXPECT_NE(foo.ins[opt_add_idx + 1].opcode, panda::pandasm::Opcode::STA);
368 
369     num_invalid = std::count_if(foo.ins.begin(), add_it,
__anonc5413f460802(const auto &in) 370                                 [](const auto &in) { return in.opcode == panda::pandasm::Opcode::INVALID; });
371     int32_t add_type_idx = opt_add_idx - num_invalid;  // exclude invalid insns because they do not emit
372     CheckTypeExist(typeinfo, add_type_idx, STR_TYPE);  // type is on add2 as it does not have sta
373 }
374 
375 }  // namespace panda::bytecodeopt::test
376