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