Add unittest for bytecode

https://gitee.com/openharmony/arkcompiler_runtime_core/issues/I7KLRF

Signed-off-by: lw19901203 <liuwei742@huawei.com>
This commit is contained in:
lw19901203 2023-07-25 14:30:02 +08:00
parent eeee298c28
commit 6a8e952ebc
10 changed files with 1351 additions and 264 deletions

View File

@ -274,21 +274,6 @@ concat_yamls("concat_inst_templates_yamls") {
}
if (!ark_standalone_build) {
group("bcopt_type_adapter_unit_test") {
if (host_os == "mac") {
if (host_cpu == "arm64") {
deps = [ "$ark_root/bytecode_optimizer/tests:bcopt_type_adapter_unit_test($build_root/toolchain/mac:clang_arm64)" ]
} else {
deps = [ "$ark_root/bytecode_optimizer/tests:bcopt_type_adapter_unit_test($build_root/toolchain/mac:clang_x64)" ]
}
} else {
deps = [
"$ark_root/bytecode_optimizer/tests:bcopt_type_adapter_unit_test($build_root/toolchain/linux:clang_x64)",
"$ark_root/bytecode_optimizer/tests:bcopt_type_adapter_unit_test($build_root/toolchain/mingw:mingw_x86_64)",
]
}
}
group("ark_host_linux_defectscanaux_lib") {
deps = []
if (host_os == "linux") {

View File

@ -11,60 +11,12 @@
# See the License for the specific language governing permissions and
# limitations under the License.
import("//arkcompiler/ets_frontend/es2panda/es2abc_config.gni")
import("//arkcompiler/runtime_core/ark_config.gni")
import("$ark_root/tests/test_helper.gni")
module_out_path = "$ark_root/bytecode_optimizer"
ohos_executable("bcopt_type_adapter_unit_test") {
sources = [ "bcopt_type_adaption_test.cpp" ]
configs = [
"$ark_root:ark_config",
"$ark_root/assembler:arkassembler_public_config",
"$ark_root/libpandabase:arkbase_public_config",
"$ark_root/libpandafile:arkfile_public_config",
"$ark_root/compiler:arkcompiler_public_config",
"$ark_root/bytecode_optimizer:bytecodeopt_public_config",
sdk_libc_secshared_config,
]
deps = [ sdk_libc_secshared_dep ]
if (is_linux || is_mingw || is_mac) {
deps += [
"$ark_root/assembler:libarkassembler_frontend_static",
"$ark_root/bytecode_optimizer:libarkbytecodeopt_frontend_static",
"$ark_root/compiler:libarkcompiler_frontend_static",
"$ark_root/libpandabase:libarkbase_frontend_static",
"$ark_root/libpandafile:libarkfile_frontend_static",
"$ark_root/libziparchive:libarkziparchive_frontend_static",
]
} else {
deps += [
"$ark_root/assembler:libarkassembler",
"$ark_root/bytecode_optimizer:libarkbytecodeopt",
"$ark_root/compiler:libarkcompiler",
"$ark_root/libpandabase:libarkbase",
"$ark_root/libpandafile:libarkfile",
"$ark_root/libziparchive:libarkziparchive",
]
}
if (is_linux) {
if (build_public_version) {
ldflags = [ "-static-libstdc++" ]
} else {
libs = [ libcpp_static_lib ]
}
}
output_name = "bcopt_type_adapter_unit_test"
install_enable = false
part_name = "runtime_core"
subsystem_name = "arkcompiler"
}
bcopt_test_config = [
"$ark_root:ark_config",
"$ark_root/assembler:arkassembler_public_config",
@ -85,14 +37,52 @@ bcopt_test_deps = [
sdk_libc_secshared_dep,
]
host_unittest_action("ExcludedKeysTest") {
test_js_path = "//arkcompiler/runtime_core/bytecode_optimizer/tests/js/"
graph_test_js_files = [
"codegenTryCatch",
"optimizeTryCatch",
]
foreach(file, graph_test_js_files) {
es2abc_gen_abc("gen_${file}_abc") {
test_js = "${test_js_path}${file}.js"
test_abc = "$target_out_dir/${file}.abc"
src_js = rebase_path(test_js)
dst_file = rebase_path(test_abc)
in_puts = [ test_js ]
out_puts = [ test_abc ]
}
}
host_unittest_action("BytecodeOptimizerTest") {
module_out_path = module_output_path
sources = [ "excluded_keys_test.cpp" ]
sources = [
"bcopt_type_adaption_test.cpp",
"codegen_test.cpp",
"excluded_keys_test.cpp",
"optimize_bytecode_test.cpp",
"reg_encoder_test.cpp",
]
configs = bcopt_test_config
deps = bcopt_test_deps
test_abc_dir = rebase_path(target_out_dir)
defines = [ "GRAPH_TEST_ABC_DIR=\"${test_abc_dir}/\"" ]
foreach(file, graph_test_js_files) {
deps += [ ":gen_${file}_abc" ]
}
defines += [
"ARK_INTRINSIC_SET",
"ENABLE_BYTECODE_OPT",
"PANDA_WITH_ECMASCRIPT",
]
}
group("host_unittest") {
testonly = true
deps = [ ":ExcludedKeysTestAction" ]
deps = [ ":BytecodeOptimizerTestAction" ]
}

View File

@ -13,114 +13,145 @@
* limitations under the License.
*/
#include "assembler/meta.h"
#include <gtest/gtest.h>
#include "assembler/assembly-parser.h"
#include "assembler/meta.h"
#include "bytecode_optimizer/optimize_bytecode.h"
namespace panda::bytecodeopt::test {
namespace panda::bytecodeopt {
using ArrayValue = panda::pandasm::ArrayValue;
using ScalarValue = panda::pandasm::ScalarValue;
using AnnotationData = panda::pandasm::AnnotationData;
using AnnotationElement = panda::pandasm::AnnotationElement;
using TypeInfoMap = std::unordered_map<int32_t, TypeInfoIndex>;
class TestBase {
class TypeAdaptionTest : public testing::Test {
public:
template <typename T1, typename T2>
inline void TestAssertEqual(const T1 &left, const T2 &right) const
{
if (left != static_cast<T1>(right)) {
std::cout << "assertion equal failed." << std::endl;
UNREACHABLE();
}
}
static void SetUpTestCase(void) {}
static void TearDownTestCase(void) {}
void SetUp() {}
void TearDown() {}
template <typename T1, typename T2>
inline void TestAssertNotEqual(const T1 &left, const T2 &right) const
TypeInfoMap ExtractTypeinfo(const panda::pandasm::Function &fun, const panda::pandasm::Program &prog) const
{
if (left == static_cast<T1>(right)) {
std::cout << "assertion not equal failed." << std::endl;
UNREACHABLE();
}
}
inline void TestAssertTrue(bool val) const
{
if (!val) {
UNREACHABLE();
}
}
inline void TestAssertFalse(bool val) const
{
if (val) {
UNREACHABLE();
}
}
};
class BytecodeOptimizerTypeAdaptionTest : TestBase {
public:
std::unordered_map<int32_t, int32_t> ExtractTypeinfo(const panda::pandasm::Function &fun) const
{
const auto *ele = fun.metadata->GetAnnotations()[0].GetElements()[0].GetValue();
const auto &values = ele->GetAsArray()->GetValues();
std::unordered_map<int32_t, int32_t> type_info;
const size_t PAIR_GAP = 2;
TestAssertEqual(values.size() % PAIR_GAP, 0); // must be even as it consits of pairs
for (size_t i = 0; i < values.size(); i += PAIR_GAP) {
type_info.emplace(values[i].GetValue<int32_t>(), values[i + 1].GetValue<int32_t>());
const auto &annos = fun.metadata->GetAnnotations();
EXPECT_FALSE(annos.empty());
const auto &eles = annos[0].GetElements();
EXPECT_FALSE(eles.empty());
const auto *ele = eles[0].GetValue();
EXPECT_NE(ele, nullptr);
const auto key = ele->GetAsScalar()->GetValue<std::string>();
auto array_liter = prog.literalarray_table.find(key);
EXPECT_NE(array_liter, prog.literalarray_table.end());
const auto &array = array_liter->second.literals_;
// 4: size must be multiple of 4 because values consits of tuple of tag, order, tag, type
EXPECT_EQ(array.size() % 4u, 0u);
TypeInfoMap type_info;
size_t i = 1; // 1: skip tag of order, so start from 1
while (i < array.size()) {
auto order = bit_cast<int32_t>(std::get<uint32_t>(array[i].value_));
i += 2; // 2: skip tag between order and type
TypeInfoIndex type;
if (array[i].tag_ == panda_file::LiteralTag::LITERALARRAY) {
type = std::get<std::string>(array[i].value_);
} else {
EXPECT_EQ(array[i].tag_, panda_file::LiteralTag::BUILTINTYPEINDEX);
type = std::get<BuiltinIndexType>(array[i].value_);
}
type_info.emplace(order, type);
i += 2; // 2: skip tag between order and type
}
return type_info;
}
void CheckTypeExist(const std::unordered_map<int32_t, int32_t> &typeinfo, int32_t order, int32_t type) const
void CheckTypeExist(const TypeInfoMap &typeinfo, int32_t order, const TypeInfoIndex &type) const
{
auto type_it = typeinfo.find(order);
TestAssertNotEqual(type_it, typeinfo.end());
TestAssertEqual(type_it->second, type);
EXPECT_NE(type_it, typeinfo.end());
EXPECT_EQ(type_it->second, type);
}
void AddTypeinfo(std::vector<ScalarValue> *elements, int32_t order, int32_t type) const
void AddTypeinfo(panda::pandasm::LiteralArray &lit_arr, int32_t order, TypeInfoIndex type) const
{
ScalarValue insn_order(ScalarValue::Create<panda::pandasm::Value::Type::I32>(order));
elements->emplace_back(std::move(insn_order));
ScalarValue insn_type(ScalarValue::Create<panda::pandasm::Value::Type::I32>(type));
elements->emplace_back(std::move(insn_type));
auto &arr = lit_arr.literals_;
panda::pandasm::LiteralArray::Literal order_tag;
order_tag.tag_ = panda::panda_file::LiteralTag::TAGVALUE;
order_tag.value_ = static_cast<uint8_t>(panda::panda_file::LiteralTag::INTEGER);
arr.emplace_back(order_tag);
panda::pandasm::LiteralArray::Literal order_val;
order_val.tag_ = panda::panda_file::LiteralTag::INTEGER;
order_val.value_ = static_cast<uint32_t>(order);
arr.emplace_back(order_val);
panda::pandasm::LiteralArray::Literal type_tag;
panda::pandasm::LiteralArray::Literal type_val;
type_tag.tag_ = panda::panda_file::LiteralTag::TAGVALUE;
if (std::holds_alternative<uint8_t>(type)) {
type_tag.value_ = static_cast<uint8_t>(panda::panda_file::LiteralTag::BUILTINTYPEINDEX);
type_val.tag_ = panda::panda_file::LiteralTag::BUILTINTYPEINDEX;
type_val.value_ = std::get<uint8_t>(type);
} else {
EXPECT_TRUE(std::holds_alternative<std::string>(type));
type_tag.value_ = static_cast<uint8_t>(panda::panda_file::LiteralTag::LITERALARRAY);
type_val.tag_ = panda::panda_file::LiteralTag::LITERALARRAY;
type_val.value_ = std::get<std::string>(type);
}
arr.emplace_back(type_tag);
arr.emplace_back(type_val);
}
void SetTypeAnnotationForFunc(const std::vector<ScalarValue> &elements, panda::pandasm::Function &func,
void SetTypeAnnotationForFunc(const panda::pandasm::LiteralArray &arr, panda::pandasm::Function &func,
panda::pandasm::Program &program) const
{
ArrayValue array_value(panda::pandasm::Value::Type::I32, elements);
AnnotationElement anno_element(TSTYPE_ANNO_ELEMENT_NAME, std::make_unique<ArrayValue>(array_value));
auto id = std::to_string(program.literalarray_table.size());
program.literalarray_table.emplace(id, arr);
AnnotationElement element(TSTYPE_ANNO_ELEMENT_NAME, std::make_unique<ScalarValue>(
ScalarValue::Create<panda::pandasm::Value::Type::LITERALARRAY>(id)));
AnnotationData annotation(TSTYPE_ANNO_RECORD_NAME);
annotation.AddElement(std::move(anno_element));
annotation.AddElement(std::move(element));
std::vector<AnnotationData> annos;
annos.emplace_back(std::move(annotation));
annos.emplace_back(annotation);
func.metadata->SetAnnotations(std::move(annos));
const auto iterator = program.record_table.find(TSTYPE_ANNO_RECORD_NAME.data());
TestAssertNotEqual(iterator, program.record_table.end());
EXPECT_NE(iterator, program.record_table.end());
iterator->second.metadata->SetAccessFlags(panda::ACC_ANNOTATION);
TestAssertTrue(program.record_table.find(TSTYPE_ANNO_RECORD_NAME.data())->second.metadata->IsAnnotation());
EXPECT_TRUE(program.record_table.find(TSTYPE_ANNO_RECORD_NAME.data())->second.metadata->IsAnnotation());
}
void EmitAndOptimize(const std::string &abc_file_name, panda::pandasm::Program &program) const
// add a literalarray as a type
TypeInfoIndex AddAnTypeLiteralArray(panda::pandasm::Program &program) const
{
panda::pandasm::LiteralArray arr;
panda::pandasm::LiteralArray::Literal tag;
tag.tag_ = panda::panda_file::LiteralTag::TAGVALUE;
tag.value_ = static_cast<uint8_t>(panda::panda_file::LiteralTag::BUILTINTYPEINDEX);
arr.literals_.emplace_back(tag);
panda::pandasm::LiteralArray::Literal val;
val.tag_ = panda::panda_file::LiteralTag::BUILTINTYPEINDEX;
val.value_ = static_cast<uint8_t>(0u);
arr.literals_.emplace_back(val);
const std::string litKey = std::to_string(program.literalarray_table.size());
program.literalarray_table.emplace(litKey, arr);
TypeInfoIndex ret = litKey;
return ret;
}
void EmitAndOptimize(const std::string &abcFileName, panda::pandasm::Program &program) const
{
std::map<std::string, size_t> *statp = nullptr;
panda::pandasm::AsmEmitter::PandaFileToPandaAsmMaps maps {};
panda::pandasm::AsmEmitter::PandaFileToPandaAsmMaps *mapsp = &maps;
TestAssertTrue(panda::pandasm::AsmEmitter::Emit(abc_file_name, program, statp, mapsp, false));
TestAssertTrue(panda::bytecodeopt::OptimizeBytecode(&program, mapsp, abc_file_name, true));
EXPECT_TRUE(panda::pandasm::AsmEmitter::Emit(abcFileName, program, statp, mapsp, false));
EXPECT_TRUE(panda::bytecodeopt::OptimizeBytecode(&program, mapsp, abcFileName, true));
}
void TypeAdaptionTest() const;
void TypeAdaptionTest_UnconditionalJump() const;
static constexpr int32_t NUM_TYPE = 1;
static constexpr int32_t STR_TYPE = 4;
};
void BytecodeOptimizerTypeAdaptionTest::TypeAdaptionTest() const
HWTEST_F(TypeAdaptionTest, type_adaption_test_001, testing::ext::TestSize.Level1)
{
/* ts source code
function foo(a:number, b:string, c:string):string
@ -139,114 +170,114 @@ void BytecodeOptimizerTypeAdaptionTest::TypeAdaptionTest() const
const auto source = R"(
.language ECMAScript
.record _ESTypeAnnotation <external>
.function any foo(any a0, any a1, any a2) <static> {
mov.dyn v2, a2
mov.dyn v1, a1
mov.dyn v0, a0
ecma.ldlexenvdyn
sta.dyn v6
ldai.dyn 0x64
sta.dyn v3
lda.dyn v0
sta.dyn v5
lda.dyn v3
ecma.greaterdyn v5
.function any foo(any a0, any a1, any a2, any a3, any a4, any a5) <static> {
mov v5, a5
mov v4, a4
mov v3, a3
mov v2, a2
mov v1, a1
mov v0, a0
ldai 0x64
sta v6
lda v3
sta v9
lda v6
greater 0x0, v9
jeqz jump_label_0
lda.dyn v1
sta.dyn v5
lda.dyn v5
return.dyn
lda v4
return
jump_label_0:
lda.dyn v0
sta.dyn v5
lda.dyn v3
ecma.lessdyn v5
lda v3
sta v9
lda v6
less 0x1, v9
jeqz jump_label_1
lda.dyn v2
sta.dyn v5
lda.dyn v5
return.dyn
lda v5
return
jump_label_1:
lda.dyn v1
sta.dyn v5
lda.dyn v2
ecma.add2dyn v5
sta.dyn v4
lda.dyn v4
sta.dyn v5
lda.dyn v5
return.dyn
lda v4
sta v9
lda v5
add2 0x2, v9
sta v7
lda v7
return
}
)";
panda::pandasm::Parser parser;
auto res = parser.Parse(source);
auto &program = res.Value();
const std::string fun_name = "foo:(any,any,any)";
const std::string fun_name = "foo:(any,any,any,any,any,any)";
auto it = program.function_table.find(fun_name);
TestAssertNotEqual(it, program.function_table.end());
EXPECT_NE(it, program.function_table.end());
auto &func = it->second;
std::vector<panda::pandasm::ScalarValue> elements;
panda::pandasm::LiteralArray lit_arr;
static const TypeInfoIndex NUM_TYPE = static_cast<uint8_t>(1);
static const TypeInfoIndex STR_TYPE = static_cast<uint8_t>(4);
// set arg type
AddTypeinfo(&elements, -1, NUM_TYPE); // -1: first arg
AddTypeinfo(&elements, -2, STR_TYPE); // -2: second arg
AddTypeinfo(&elements, -3, STR_TYPE); // -3: third arg
const auto THIS_TYPE = AddAnTypeLiteralArray(program);
AddTypeinfo(lit_arr, -3, THIS_TYPE); // -3: the arg "this"
AddTypeinfo(lit_arr, -4, NUM_TYPE); // -4: the first arg
AddTypeinfo(lit_arr, -5, STR_TYPE); // -5: the second arg
AddTypeinfo(lit_arr, -6, STR_TYPE); // -6: the third arg
// set ins type
const size_t LDAI_IDX = 5;
TestAssertEqual(func.ins[LDAI_IDX].opcode, panda::pandasm::Opcode::LDAI_DYN);
TestAssertEqual(func.ins[LDAI_IDX + 1].opcode, panda::pandasm::Opcode::STA_DYN);
AddTypeinfo(&elements, static_cast<int32_t>(LDAI_IDX + 1), NUM_TYPE);
const size_t ADD_IDX = 30;
TestAssertEqual(func.ins[ADD_IDX].opcode, panda::pandasm::Opcode::ECMA_ADD2DYN);
TestAssertEqual(func.ins[ADD_IDX + 1].opcode, panda::pandasm::Opcode::STA_DYN);
const size_t LDAI_IDX = 6;
EXPECT_EQ(func.ins[LDAI_IDX].opcode, panda::pandasm::Opcode::LDAI);
EXPECT_EQ(func.ins[LDAI_IDX + 1].opcode, panda::pandasm::Opcode::STA);
AddTypeinfo(lit_arr, static_cast<int32_t>(LDAI_IDX + 1), NUM_TYPE);
const size_t ADD_IDX = 27;
EXPECT_EQ(func.ins[ADD_IDX].opcode, panda::pandasm::Opcode::ADD2);
EXPECT_EQ(func.ins[ADD_IDX + 1].opcode, panda::pandasm::Opcode::STA);
int32_t num_invalid = std::count_if(func.ins.begin(), func.ins.begin() + ADD_IDX,
[](const auto &in) { return in.opcode == panda::pandasm::Opcode::INVALID; });
AddTypeinfo(&elements, ADD_IDX + 1 - num_invalid, STR_TYPE); // exclude invalid insns because they do not emit
AddTypeinfo(lit_arr, ADD_IDX + 1 - num_invalid, STR_TYPE); // exclude invalid insns because they do not emit
SetTypeAnnotationForFunc(elements, func, program);
SetTypeAnnotationForFunc(lit_arr, func, program);
EmitAndOptimize("TypeAdaptionTest.abc", program);
// check typeinfo after optimization
it = program.function_table.find(fun_name);
TestAssertNotEqual(it, program.function_table.end());
EXPECT_NE(it, program.function_table.end());
const auto &foo = it->second;
const auto typeinfo = ExtractTypeinfo(foo);
CheckTypeExist(typeinfo, -1, NUM_TYPE); // -1: means first arg
CheckTypeExist(typeinfo, -2, STR_TYPE); // -2: means second arg
CheckTypeExist(typeinfo, -3, STR_TYPE); // -3: means third arg
const auto typeinfo = ExtractTypeinfo(foo, program);
CheckTypeExist(typeinfo, -3, THIS_TYPE); // -3: the arg "this"
CheckTypeExist(typeinfo, -4, NUM_TYPE); // -4: the first arg
CheckTypeExist(typeinfo, -5, STR_TYPE); // -5: the second arg
CheckTypeExist(typeinfo, -6, STR_TYPE); // -6: the third arg
auto ldai_it = std::find_if(foo.ins.begin(), foo.ins.end(),
[](const auto &in) { return in.opcode == panda::pandasm::Opcode::LDAI_DYN; });
TestAssertNotEqual(ldai_it, foo.ins.end());
[](const auto &in) { return in.opcode == panda::pandasm::Opcode::LDAI; });
EXPECT_NE(ldai_it, foo.ins.end());
const auto opt_ldai_idx = static_cast<size_t>(ldai_it - foo.ins.begin());
TestAssertEqual(foo.ins[opt_ldai_idx].opcode, panda::pandasm::Opcode::LDAI_DYN);
TestAssertTrue(opt_ldai_idx + 1 < foo.ins.size());
TestAssertEqual(foo.ins[opt_ldai_idx + 1].opcode, panda::pandasm::Opcode::STA_DYN);
EXPECT_EQ(foo.ins[opt_ldai_idx].opcode, panda::pandasm::Opcode::LDAI);
EXPECT_LT(opt_ldai_idx + 1, foo.ins.size());
EXPECT_EQ(foo.ins[opt_ldai_idx + 1].opcode, panda::pandasm::Opcode::STA);
num_invalid = std::count_if(foo.ins.begin(), ldai_it,
[](const auto &in) { return in.opcode == panda::pandasm::Opcode::INVALID; });
int32_t ldai_type_idx = opt_ldai_idx - num_invalid; // exclude invalid insns because they do not emit
CheckTypeExist(typeinfo, ldai_type_idx + 1, NUM_TYPE); // type is on sta.dyn
CheckTypeExist(typeinfo, ldai_type_idx + 1, NUM_TYPE); // type is on sta
auto add_it = std::find_if(foo.ins.begin(), foo.ins.end(),
[](const auto &in) { return in.opcode == panda::pandasm::Opcode::ECMA_ADD2DYN; });
TestAssertNotEqual(add_it, foo.ins.end());
[](const auto &in) { return in.opcode == panda::pandasm::Opcode::ADD2; });
EXPECT_NE(add_it, foo.ins.end());
const auto opt_add_idx = static_cast<size_t>(add_it - foo.ins.begin());
TestAssertEqual(foo.ins[opt_add_idx].opcode, panda::pandasm::Opcode::ECMA_ADD2DYN);
TestAssertTrue(opt_add_idx + 1 < foo.ins.size());
TestAssertNotEqual(foo.ins[opt_add_idx + 1].opcode, panda::pandasm::Opcode::STA_DYN);
EXPECT_EQ(foo.ins[opt_add_idx].opcode, panda::pandasm::Opcode::ADD2);
EXPECT_LT(opt_add_idx + 1, foo.ins.size());
EXPECT_NE(foo.ins[opt_add_idx + 1].opcode, panda::pandasm::Opcode::STA);
num_invalid = std::count_if(foo.ins.begin(), add_it,
[](const auto &in) { return in.opcode == panda::pandasm::Opcode::INVALID; });
int32_t add_type_idx = opt_add_idx - num_invalid; // exclude invalid insns because they do not emit
CheckTypeExist(typeinfo, add_type_idx, STR_TYPE); // type is on ecma.add2dyn as it does not have sta.dyn
CheckTypeExist(typeinfo, add_type_idx, STR_TYPE); // type is on add2 as it does not have sta
}
void BytecodeOptimizerTypeAdaptionTest::TypeAdaptionTest_UnconditionalJump() const
HWTEST_F(TypeAdaptionTest, unconditional_jump_case_test_001, testing::ext::TestSize.Level1)
{
/* ts source code
function processResults(results: number) {
function foo(results: number) {
for (let i = 0; i < 1; i++) {
results *= i;
}
@ -257,93 +288,88 @@ void BytecodeOptimizerTypeAdaptionTest::TypeAdaptionTest_UnconditionalJump() con
const auto source = R"(
.language ECMAScript
.record _ESTypeAnnotation <external>
.function any foo(any a0) <static> {
mov.dyn v0, a0
ecma.ldlexenvdyn
sta.dyn v6
ldai.dyn 0x0
sta.dyn v1
.function any foo(any a0, any a1, any a2, any a3) <static> {
mov v0, a0
mov v1, a1
mov v2, a2
mov v3, a3
ldai 0x0
sta v6
jump_label_1:
lda.dyn v1
sta.dyn v4
ldai.dyn 0x1
ecma.lessdyn v4
lda v6
sta v7
ldai 0x1
less 0x0, v7
jeqz jump_label_0
lda.dyn v0
sta.dyn v4
lda.dyn v1
ecma.mul2dyn v4
sta.dyn v0
lda.dyn v1
sta.dyn v4
ecma.incdyn v4
sta.dyn v1
ecma.tonumeric v4
lda v3
sta v7
lda v6
mul2 0x1, v7
sta v3
lda v6
sta v7
lda v7
inc 0x2
sta v6
lda v7
tonumeric 0x3
jmp jump_label_1
jump_label_0:
lda.dyn v0
sta.dyn v3
ldai.dyn 0x2
ecma.add2dyn v3
sta.dyn v2
lda.dyn v2
sta.dyn v3
lda.dyn v3
return.dyn
lda v3
sta v6
ldai 0x2
add2 0x4, v6
sta v4
lda v4
return
}
)";
panda::pandasm::Parser parser;
auto res = parser.Parse(source);
auto &program = res.Value();
const std::string fun_name = "foo:(any)";
const std::string fun_name = "foo:(any,any,any,any)";
auto it = program.function_table.find(fun_name);
TestAssertNotEqual(it, program.function_table.end());
EXPECT_NE(it, program.function_table.end());
auto &func = it->second;
std::vector<panda::pandasm::ScalarValue> elements;
panda::pandasm::LiteralArray lit_arr;
static const TypeInfoIndex NUM_TYPE = static_cast<uint8_t>(1);
static const TypeInfoIndex STR_TYPE = static_cast<uint8_t>(4);
// set arg type
AddTypeinfo(&elements, -1, NUM_TYPE); // -1: first arg
const auto THIS_TYPE = AddAnTypeLiteralArray(program);
AddTypeinfo(lit_arr, -3, THIS_TYPE); // -3: the arg "this"
AddTypeinfo(lit_arr, -4, NUM_TYPE); // -4: the first arg
// set ins type
const size_t ADD_IDX = 26;
TestAssertEqual(func.ins[ADD_IDX].opcode, panda::pandasm::Opcode::ECMA_ADD2DYN);
TestAssertEqual(func.ins[ADD_IDX + 1].opcode, panda::pandasm::Opcode::STA_DYN);
const size_t ADD_IDX = 29;
EXPECT_EQ(func.ins[ADD_IDX].opcode, panda::pandasm::Opcode::ADD2);
EXPECT_EQ(func.ins[ADD_IDX + 1].opcode, panda::pandasm::Opcode::STA);
int32_t num_invalid = std::count_if(func.ins.begin(), func.ins.begin() + ADD_IDX,
[](const auto &in) { return in.opcode == panda::pandasm::Opcode::INVALID; });
AddTypeinfo(&elements, ADD_IDX + 1 - num_invalid, STR_TYPE); // exclude invalid insns because they do not emit
AddTypeinfo(lit_arr, ADD_IDX + 1 - num_invalid, STR_TYPE); // exclude invalid insns because they do not emit
SetTypeAnnotationForFunc(elements, func, program);
SetTypeAnnotationForFunc(lit_arr, func, program);
EmitAndOptimize("TypeAdaptionTest_UnconditionalJump.abc", program);
// check typeinfo after optimization
it = program.function_table.find(fun_name);
TestAssertNotEqual(it, program.function_table.end());
EXPECT_NE(it, program.function_table.end());
const auto &foo = it->second;
const auto typeinfo = ExtractTypeinfo(foo);
CheckTypeExist(typeinfo, -1, NUM_TYPE); // -1: first arg
const auto typeinfo = ExtractTypeinfo(foo, program);
CheckTypeExist(typeinfo, -3, THIS_TYPE); // -3: the arg "this"
CheckTypeExist(typeinfo, -4, NUM_TYPE); // -4: the first arg
auto add_it = std::find_if(foo.ins.begin(), foo.ins.end(),
[](const auto &in) { return in.opcode == panda::pandasm::Opcode::ECMA_ADD2DYN; });
TestAssertNotEqual(add_it, foo.ins.end());
[](const auto &in) { return in.opcode == panda::pandasm::Opcode::ADD2; });
EXPECT_NE(add_it, foo.ins.end());
const auto opt_add_idx = static_cast<size_t>(add_it - foo.ins.begin());
TestAssertEqual(foo.ins[opt_add_idx].opcode, panda::pandasm::Opcode::ECMA_ADD2DYN);
TestAssertTrue(opt_add_idx + 1 < foo.ins.size());
TestAssertNotEqual(foo.ins[opt_add_idx + 1].opcode, panda::pandasm::Opcode::STA_DYN);
EXPECT_EQ(foo.ins[opt_add_idx].opcode, panda::pandasm::Opcode::ADD2);
EXPECT_TRUE(opt_add_idx + 1 < foo.ins.size());
EXPECT_NE(foo.ins[opt_add_idx + 1].opcode, panda::pandasm::Opcode::STA);
num_invalid = std::count_if(foo.ins.begin(), add_it,
[](const auto &in) { return in.opcode == panda::pandasm::Opcode::INVALID; });
int32_t add_type_idx = opt_add_idx - num_invalid; // exclude invalid insns because they do not emit
CheckTypeExist(typeinfo, add_type_idx, STR_TYPE); // type is on ecma.add2dyn as it does not have sta.dyn
CheckTypeExist(typeinfo, add_type_idx, STR_TYPE); // type is on add2 as it does not have sta
}
} // namespace panda::bytecodeopt::test
int main()
{
panda::bytecodeopt::test::BytecodeOptimizerTypeAdaptionTest test;
std::cout << "BytecodeOptimizerTypeAdaptionTest: " << std::endl;
test.TypeAdaptionTest();
std::cout << "PASS!" << std::endl;
std::cout << "BytecodeOptimizerTypeAdaptionTest_UnconditionalJump: " << std::endl;
test.TypeAdaptionTest_UnconditionalJump();
std::cout << "PASS!" << std::endl;
return 0;
}
} // namespace panda::bytecodeopt::test

View File

@ -0,0 +1,564 @@
/*
* Copyright (c) 2023 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <gtest/gtest.h>
#include "assembler/assembly-parser.h"
#include "codegen.h"
#include "compiler/optimizer/optimizations/cleanup.h"
#include "compiler/optimizer/optimizations/lowering.h"
#include "compiler/optimizer/optimizations/move_constants.h"
#include "compiler/optimizer/optimizations/regalloc/reg_alloc.h"
#include "compiler/optimizer/optimizations/vn.h"
#include "reg_acc_alloc.h"
#include "reg_encoder.h"
#include "graph_test.h"
#include "mem/pool_manager.h"
using namespace testing::ext;
namespace panda::bytecodeopt {
using namespace compiler;
using namespace pandasm;
using namespace panda;
class CodegenTest : public testing::Test {
public:
static void SetUpTestCase(void) {}
static void TearDownTestCase(void) {}
void SetUp() {}
void TearDown() {}
GraphTest graph_test_;
};
std::unique_ptr<const panda_file::File> ParseAndEmit(const std::string &source)
{
panda::pandasm::Parser parser;
auto res = parser.Parse(source);
EXPECT_EQ(parser.ShowError().err, Error::ErrorType::ERR_NONE);
auto &program = res.Value();
return AsmEmitter::Emit(program);
}
/**
* @tc.name: codegen_test_001
* @tc.desc: Verify the DoLda function.
* @tc.type: FUNC
* @tc.require: issueNumber
*/
HWTEST_F(CodegenTest, codegen_test_001, TestSize.Level1)
{
Register reg = 1; // 1: It's a random number
std::vector<Ins> result;
DoLda(reg, result);
EXPECT_EQ(result[0].regs[0], reg);
EXPECT_EQ(result[0].opcode, panda::pandasm::Opcode::LDA);
}
/**
* @tc.name: codegen_test_002
* @tc.desc: Verify the DoSta function.
* @tc.type: FUNC
* @tc.require: issueNumber
*/
HWTEST_F(CodegenTest, codegen_test_002, TestSize.Level1)
{
Register reg = 1; // 1: It's a random number
std::vector<Ins> result;
DoSta(reg, result);
EXPECT_EQ(result[0].regs[0], reg);
EXPECT_EQ(result[0].opcode, panda::pandasm::Opcode::STA);
}
/**
* @tc.name: codegen_test_003
* @tc.desc: Verify the EmitJump function.
* @tc.type: FUNC
* @tc.require: issueNumber
*/
HWTEST_F(CodegenTest, codegen_test_003, TestSize.Level1)
{
std::string pfile = GRAPH_TEST_ABC_DIR "codegenTryCatch.abc";
const char *test_method_name = "func1";
bool status = false;
graph_test_.TestBuildGraphFromFile(pfile, [test_method_name, &status](Graph* graph, std::string &method_name) {
if (test_method_name != method_name) {
return;
}
EXPECT_NE(graph, nullptr);
for (auto bb : graph->GetBlocksRPO()) {
EXPECT_NE(bb, nullptr);
if (bb->IsTryBegin()) {
status = true;
Function *function = nullptr;
BytecodeOptIrInterface *interface = nullptr;
Program *prog = nullptr;
BytecodeGen bc_gen(graph, function, interface, prog);
bc_gen.EmitJump(bb->GetSuccessor(1));
EXPECT_FALSE(bc_gen.GetResult().empty());
EXPECT_EQ(bc_gen.GetResult().back().ids[0], "label_6");
bc_gen.EmitJump(bb->GetSuccessor(0));
EXPECT_EQ(bc_gen.GetResult().back().ids[0], "label_3");
}
}
});
EXPECT_TRUE(status);
}
/**
* @tc.name: codegen_test_004
* @tc.desc: Verify the EmitJump function.
* @tc.type: FUNC
* @tc.require: issueNumber
*/
HWTEST_F(CodegenTest, codegen_test_004, TestSize.Level1)
{
std::string pfile = GRAPH_TEST_ABC_DIR "codegenTryCatch.abc";
const char *test_method_name = "func2";
bool status = false;
graph_test_.TestBuildGraphFromFile(pfile, [test_method_name, &status](Graph* graph, std::string &method_name) {
if (test_method_name != method_name) {
return;
}
EXPECT_NE(graph, nullptr);
for (auto bb : graph->GetBlocksRPO()) {
EXPECT_NE(bb, nullptr);
for(auto inst : bb->AllInsts()){
if(inst->GetOpcode() == Opcode::IfImm){
status = true;
Function *function = nullptr;
BytecodeOptIrInterface *interface = nullptr;
Program *prog = nullptr;
BytecodeGen bc_gen(graph, function, interface, prog);
bc_gen.EmitJump(bb);
EXPECT_FALSE(bc_gen.GetResult().empty());
EXPECT_EQ(bc_gen.GetResult().back().opcode, panda::pandasm::Opcode::JMP);
}
}
}
});
EXPECT_TRUE(status);
}
/**
* @tc.name: codegen_test_005
* @tc.desc: Verify the VisitConstant function.
* @tc.type: FUNC
* @tc.require: issueNumber
*/
HWTEST_F(CodegenTest, codegen_test_005, TestSize.Level1)
{
std::string pfile = GRAPH_TEST_ABC_DIR "codegenTryCatch.abc";
const char *test_method_name = "func5";
bool status = false;
graph_test_.TestBuildGraphFromFile(pfile, [test_method_name, &status](Graph* graph, std::string &method_name) {
if (test_method_name != method_name) {
return;
}
EXPECT_NE(graph, nullptr);
for (auto bb : graph->GetBlocksRPO()) {
for(auto inst : bb->AllInsts()) {
if (inst->GetOpcode() != Opcode::Constant) {
continue;
}
status = true;
Function *function = nullptr;
BytecodeOptIrInterface *interface = nullptr;
Program *prog = nullptr;
BytecodeGen graph_visitor(graph, function, interface, prog);
ConstantInst *const_inst = inst->CastToConstant();
const_inst->SetType(DataType::Type::INT64);
EXPECT_EQ(const_inst->GetType(), DataType::Type::INT64);
Register reg = INVALID_REG;
const_inst->SetDstReg(reg);
EXPECT_EQ(const_inst->GetDstReg(), reg);
BytecodeGen::VisitConstant(&graph_visitor, const_inst);
EXPECT_FALSE(graph_visitor.GetResult().empty());
EXPECT_EQ(graph_visitor.GetResult().back().opcode, panda::pandasm::Opcode::STA);
ConstantInst *const_inst1 = inst->CastToConstant();
const_inst1->SetType(DataType::Type::FLOAT64);
EXPECT_EQ(const_inst->GetType(), DataType::Type::FLOAT64);
const_inst1->SetDstReg(reg);
EXPECT_EQ(const_inst->GetDstReg(), reg);
BytecodeGen::VisitConstant(&graph_visitor, const_inst1);
EXPECT_FALSE(graph_visitor.GetResult().empty());
EXPECT_EQ(graph_visitor.GetResult().back().opcode, panda::pandasm::Opcode::STA);
ConstantInst *const_inst2 = inst->CastToConstant();
const_inst2->SetType(DataType::Type::INT32);
EXPECT_EQ(const_inst->GetType(), DataType::Type::INT32);
const_inst2->SetDstReg(reg);
EXPECT_EQ(const_inst->GetDstReg(), reg);
BytecodeGen::VisitConstant(&graph_visitor, const_inst2);
EXPECT_FALSE(graph_visitor.GetResult().empty());
EXPECT_EQ(graph_visitor.GetResult().back().opcode, panda::pandasm::Opcode::STA);
}
}
});
EXPECT_TRUE(status);
}
/**
* @tc.name: codegen_test_006
* @tc.desc: Verify the EncodeSta function.
* @tc.type: FUNC
* @tc.require: issueNumber
*/
HWTEST_F(CodegenTest, codegen_test_006, TestSize.Level1)
{
std::string pfile = GRAPH_TEST_ABC_DIR "codegenTryCatch.abc";
const char *test_method_name = "func1";
bool status = false;
graph_test_.TestBuildGraphFromFile(pfile, [test_method_name, &status](Graph* graph, std::string &method_name) {
if (test_method_name != method_name) {
return;
}
EXPECT_NE(graph, nullptr);
for (auto bb : graph->GetBlocksRPO()) {
EXPECT_NE(bb, nullptr);
if (bb->IsTryBegin()) {
status = true;
Register reg = 1; // 1: It's a random number
Function *function = nullptr;
BytecodeOptIrInterface *interface = nullptr;
Program *prog = nullptr;
BytecodeGen bc_gen(graph, function, interface, prog);
bc_gen.EncodeSta(reg, DataType::Type::ANY);
EXPECT_FALSE(bc_gen.GetResult().empty());
EXPECT_EQ(bc_gen.GetResult().back().opcode, panda::pandasm::Opcode::STA);
}
}
});
EXPECT_TRUE(status);
}
/**
* @tc.name: codegen_test_007
* @tc.desc: Verify the VisitLoadString function.
* @tc.type: FUNC
* @tc.require: issueNumber
*/
HWTEST_F(CodegenTest, codegen_test_007, TestSize.Level1)
{
std::string pfile = GRAPH_TEST_ABC_DIR "codegenTryCatch.abc";
const char *test_method_name = "func6";
bool status = false;
graph_test_.TestBuildGraphFromFile(pfile, [test_method_name, &status](Graph* graph, std::string &method_name) {
if (test_method_name != method_name) {
return;
}
EXPECT_NE(graph, nullptr);
for (auto bb : graph->GetVectorBlocks()) {
EXPECT_NE(bb, nullptr);
for(auto inst1 : bb->AllInsts()) {
if (inst1->GetOpcode() != Opcode::LoadString) {
continue;
}
status = true;
Function *function = nullptr;
Program *prog = nullptr;
AsmEmitter::PandaFileToPandaAsmMaps maps;
uint32_t id = 2; // 2: It's a random number
maps.strings.emplace(id, "i32[]");
BytecodeOptIrInterface interface(&maps, prog);
BytecodeGen graph_visitor(graph, function, &interface, prog);
auto inst = inst1->CastToLoadString();
inst->SetTypeId(2);
unsigned index = 5; // 5: It's a random number
unsigned size = 6; // 6: It's a random number
User user(true, index, size);
inst->AddUser(&user);
Register reg1 = ACC_REG_ID;
inst->SetDstReg(reg1);
BytecodeGen::VisitLoadString(&graph_visitor, inst);
EXPECT_FALSE(graph_visitor.GetResult().empty());
EXPECT_EQ(graph_visitor.GetResult().back().opcode, panda::pandasm::Opcode::LDA_STR);
Register reg2 = INVALID_REG;
inst->SetDstReg(reg2);
BytecodeGen::VisitLoadString(&graph_visitor, inst);
EXPECT_FALSE(graph_visitor.GetResult().empty());
EXPECT_EQ(graph_visitor.GetResult().back().opcode, panda::pandasm::Opcode::STA);
}
}
});
EXPECT_TRUE(status);
}
/**
* @tc.name: codegen_test_008
* @tc.desc: Verify the VisitDefault function.
* @tc.type: FUNC
* @tc.require: issueNumber
*/
HWTEST_F(CodegenTest, codegen_test_008, TestSize.Level1)
{
std::string pfile = GRAPH_TEST_ABC_DIR "codegenTryCatch.abc";
const char *test_method_name = "func1";
bool status = false;
graph_test_.TestBuildGraphFromFile(pfile, [test_method_name, &status](Graph* graph, std::string &method_name) {
if (test_method_name != method_name) {
return;
}
EXPECT_NE(graph, nullptr);
for (auto bb : graph->GetBlocksRPO()) {
EXPECT_NE(bb, nullptr);
if (bb->IsTryBegin()) {
status = true;
Function *function = nullptr;
BytecodeOptIrInterface *interface = nullptr;
Program *prog = nullptr;
BytecodeGen bc_gen(graph, function, interface, prog);
bc_gen.VisitDefault(bb->GetFirstInst());
EXPECT_FALSE(bc_gen.GetStatus());
}
}
});
EXPECT_TRUE(status);
}
/**
* @tc.name: codegen_test_009
* @tc.desc: Verify the GetLiteralArrayByOffset function.
* @tc.type: FUNC
* @tc.require: issueNumber
*/
HWTEST_F(CodegenTest, codegen_test_009, TestSize.Level1)
{
AsmEmitter::PandaFileToPandaAsmMaps maps;
uint32_t id = 2; // 2: It's a random number
maps.literalarrays.emplace(id, "i32[]");
maps.strings.emplace(id, "i33[]");
Program *prog = nullptr;
BytecodeOptIrInterface interface(&maps, prog);
uint32_t offset = 2; // 2: It's a random number
EXPECT_EQ(interface.GetLiteralArrayByOffset(offset), "i32[]");
}
/**
* @tc.name: codegen_test_010
* @tc.desc: Verify the GetTypeIdByOffset function.
* @tc.type: FUNC
* @tc.require: issueNumber
*/
HWTEST_F(CodegenTest, codegen_test_010, TestSize.Level1)
{
AsmEmitter::PandaFileToPandaAsmMaps maps;
uint32_t id = 2; // 2: It's a random number
maps.classes.emplace(id, "i32[]");
Program *prog = nullptr;
BytecodeOptIrInterface interface(&maps, prog);
uint32_t offset = 2; // 2: It's a random number
EXPECT_EQ(interface.GetTypeIdByOffset(offset), "i32[]");
}
/**
* @tc.name: codegen_test_011
* @tc.desc: Verify the GetFieldIdByOffset function.
* @tc.type: FUNC
* @tc.require: issueNumber
*/
HWTEST_F(CodegenTest, codegen_test_011, TestSize.Level1)
{
AsmEmitter::PandaFileToPandaAsmMaps maps;
uint32_t id = 2; // 2: It's a random number
maps.fields.emplace(id, "i32[]");
Program *prog = nullptr;
BytecodeOptIrInterface interface(&maps, prog);
uint32_t offset = 2; // 2: It's a random number
interface.GetFieldIdByOffset(offset);
EXPECT_EQ(interface.GetFieldIdByOffset(offset), "i32[]");
EXPECT_TRUE(interface.IsMapsSet());
}
/**
* @tc.name: codegen_test_012
* @tc.desc: Verify the GetMethodArgumentsCount function.
* @tc.type: FUNC
* @tc.require: issueNumber
*/
HWTEST_F(CodegenTest, codegen_test_012, TestSize.Level1)
{
auto source = std::string(R"(
.function u1 foo() {
sta v4
lda v4
return
}
)");
std::unique_ptr<const panda_file::File> pfile = ParseAndEmit(source);
BytecodeOptimizerRuntimeAdapter runtime_adapter(*pfile.get());
RuntimeInterface::MethodPtr caller = nullptr;
BytecodeOptimizerRuntimeAdapter::MethodId id = 251; // 251: It's a random number
EXPECT_EQ(runtime_adapter.GetMethodArgumentsCount(caller, id), 0);
}
/**
* @tc.name: codegen_test_013
* @tc.desc: Verify the GetMethodFullName function.
* @tc.type: FUNC
* @tc.require: issueNumber
*/
HWTEST_F(CodegenTest, codegen_test_013, TestSize.Level1)
{
auto source = std::string(R"(
.function u1 foo() {
sta v4
lda v4
return
}
)");
std::unique_ptr<const panda_file::File> pfile = ParseAndEmit(source);
BytecodeOptimizerRuntimeAdapter runtime_adapter(*pfile.get());
int f = 222; // 222: It's a random number
RuntimeInterface::MethodPtr method;
method=(void*)(long)f;
EXPECT_EQ(runtime_adapter.GetMethodFullName(method, false), "L_ESSlotNumberAnnotation;::SlotNumber");
}
/**
* @tc.name: codegen_test_014
* @tc.desc: Verify the GetBlocksToVisit function.
* @tc.type: FUNC
* @tc.require: issueNumber
*/
HWTEST_F(CodegenTest, codegen_test_014, TestSize.Level1)
{
std::string pfile = GRAPH_TEST_ABC_DIR "codegenTryCatch.abc";
const char *test_method_name = "func1";
bool status = false;
graph_test_.TestBuildGraphFromFile(pfile, [test_method_name, &status](Graph* graph, std::string &method_name) {
if (test_method_name != method_name) {
return;
}
status = true;
EXPECT_NE(graph, nullptr);
Function *function = nullptr;
BytecodeOptIrInterface *interface = nullptr;
Program *prog = nullptr;
size_t size = 10; // 10: It's block size
BytecodeGen bc_gen(graph, function, interface, prog);
EXPECT_EQ(bc_gen.GetBlocksToVisit().size(), size);
});
EXPECT_TRUE(status);
}
/**
* @tc.name: codegen_test_015
* @tc.desc: Verify the RunImpl function.
* @tc.type: FUNC
* @tc.require: issueNumber
*/
HWTEST_F(CodegenTest, codegen_test_015, TestSize.Level1)
{
const auto source = R"(
.function any func_main_0(any a0, any a1, any a2) <static> {
mov v0, a0
mov v1, a1
mov v2, a2
try_begin_label_0:
ldai 0x1
trystglobalbyname 0x0, "a"
try_end_label_0:
jmp handler_end_label_0_0
handler_begin_label_0_0:
sta v4
ldai 0x2
trystglobalbyname 0x1, "a"
handler_end_label_0_0:
ldundefined
returnundefined
.catchall try_begin_label_0, try_end_label_0, handler_begin_label_0_0, handler_end_label_0_0
}
)";
panda::pandasm::Parser parser;
auto res = parser.Parse(source);
auto &prog = res.Value();
auto &function = prog.function_table.at("func_main_0:(any,any,any)");
pandasm::AsmEmitter::PandaFileToPandaAsmMaps maps {};
auto ir_interface = panda::bytecodeopt::BytecodeOptIrInterface(&maps, &prog);
bool status = false;
graph_test_.TestBuildGraphFromFunc(prog, "func_main_0:(any,any,any)", maps, ir_interface, [&function,
&ir_interface, &prog, &status](Graph* graph) {
EXPECT_NE(graph, nullptr);
int32_t pc = -1; // -1: It's a random number
uint8_t id1 = 2; // 2: It's a random number
TypeInfoIndex type = static_cast<BuiltinIndexType>(id1);
graph->GetRuntime()->AddPcTypePair(pc, type);
size_t id = 13; // 13: It's a random number
graph->GetRuntime()->FillInstIdTypePairByPc(id, pc);
for (auto bb : graph->GetBlocksRPO()) {
EXPECT_NE(bb, nullptr);
if(bb->IsTryBegin()){
status = true;
graph->AppendTryBeginBlock(bb);
}
}
EXPECT_TRUE(graph->RunPass<compiler::Cleanup>());
LiteralArray lit;
const auto &key = *(graph->GetRuntime()->GetTypeLiteralArrayKey());
prog.literalarray_table[key] = lit;
EXPECT_FALSE(graph->RunPass<compiler::Cleanup>());
EXPECT_FALSE(graph->RunPass<panda::compiler::ValNum>());
EXPECT_TRUE(graph->RunPass<panda::compiler::Lowering>());
EXPECT_TRUE(graph->RunPass<panda::compiler::MoveConstants>());
EXPECT_FALSE(graph->RunPass<compiler::Cleanup>());
EXPECT_TRUE(graph->RunPass<RegAccAlloc>());
EXPECT_FALSE(graph->RunPass<compiler::Cleanup>());
EXPECT_TRUE(RegAlloc(graph));
EXPECT_FALSE(graph->RunPass<compiler::Cleanup>());
EXPECT_TRUE(graph->RunPass<RegEncoder>());
EXPECT_TRUE(graph->RunPass<BytecodeGen>(&function, &ir_interface, &prog));
});
EXPECT_TRUE(status);
}
} // namespace panda::bytecodeopt

View File

@ -35,7 +35,7 @@ public:
{
panda::pandasm::AsmEmitter::PandaFileToPandaAsmMaps panda_file_to_asm_maps {};
EXPECT_TRUE(panda::pandasm::AsmEmitter::Emit(abc_file_name, program, nullptr, &panda_file_to_asm_maps, false));
EXPECT_TRUE(panda::bytecodeopt::OptimizeBytecode(&program, &panda_file_to_asm_maps, abc_file_name, false));
EXPECT_TRUE(panda::bytecodeopt::OptimizeBytecode(&program, &panda_file_to_asm_maps, abc_file_name, true));
}
};

View File

@ -0,0 +1,108 @@
/*
* Copyright (c) 2023 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef GRAPH_TEST_H
#define GRAPH_TEST_H
#include "bytecode_optimizer/ir_interface.h"
#include "bytecode_optimizer/runtime_adapter.h"
#include "libpandafile/class_data_accessor.h"
#include "libpandafile/class_data_accessor-inl.h"
#include "libpandafile/file.h"
#include "libpandafile/method_data_accessor.h"
#include "libpandabase/mem/arena_allocator.h"
#include "libpandabase/mem/pool_manager.h"
#include "optimizer/ir/graph.h"
#include "optimizer/ir/runtime_interface.h"
#include "optimizer/ir_builder/ir_builder.h"
namespace panda::compiler {
class GraphTest {
public:
GraphTest()
{
PoolManager::Initialize(PoolType::MALLOC);
}
~GraphTest()
{
PoolManager::Finalize();
}
template <class Callback>
void TestBuildGraphFromFile(const std::string &pfile_name, const Callback &cb)
{
auto pfile = panda_file::OpenPandaFile(pfile_name);
for (uint32_t id : pfile->GetClasses()) {
panda_file::File::EntityId record_id {id};
if (pfile->IsExternal(record_id)) {
continue;
}
panda_file::ClassDataAccessor cda {*pfile, record_id};
cda.EnumerateMethods([&pfile, &cb](panda_file::MethodDataAccessor &mda) {
if (!mda.IsExternal()) {
ArenaAllocator allocator {SpaceType::SPACE_TYPE_COMPILER};
ArenaAllocator local_allocator {SpaceType::SPACE_TYPE_COMPILER, nullptr, true};
auto method_ptr = reinterpret_cast<compiler::RuntimeInterface::MethodPtr>(
mda.GetMethodId().GetOffset());
panda::BytecodeOptimizerRuntimeAdapter adapter(mda.GetPandaFile());
auto *graph = allocator.New<Graph>(&allocator, &local_allocator, Arch::NONE, method_ptr, &adapter,
false, nullptr, true, true);
graph->RunPass<panda::compiler::IrBuilder>();
auto method_name = std::string(utf::Mutf8AsCString(pfile->GetStringData(mda.GetNameId()).data));
cb(graph, method_name);
}
});
}
}
template <class Callback>
void TestBuildGraphFromFunc(pandasm::Program &prog, const char *method_name,
pandasm::AsmEmitter::PandaFileToPandaAsmMaps &maps, bytecodeopt::BytecodeOptIrInterface &ir_interface,
const Callback &cb)
{
auto pfile = pandasm::AsmEmitter::Emit(prog, &maps);
for (uint32_t id : pfile->GetClasses()) {
panda_file::File::EntityId record_id {id};
panda_file::ClassDataAccessor cda {*pfile, record_id};
cda.EnumerateMethods([maps, method_name, ir_interface, &cb](panda_file::MethodDataAccessor &mda) {
auto func_name = ir_interface.GetMethodIdByOffset(mda.GetMethodId().GetOffset());
if (func_name != method_name) {
return;
}
ArenaAllocator allocator {SpaceType::SPACE_TYPE_COMPILER};
ArenaAllocator local_allocator {SpaceType::SPACE_TYPE_COMPILER, nullptr, true};
auto method_ptr = reinterpret_cast<compiler::RuntimeInterface::MethodPtr>(
mda.GetMethodId().GetOffset());
panda::BytecodeOptimizerRuntimeAdapter adapter(mda.GetPandaFile());
auto *graph = allocator.New<Graph>(&allocator, &local_allocator, Arch::NONE, method_ptr, &adapter,
false, nullptr, true, true);
graph->RunPass<panda::compiler::IrBuilder>();
cb(graph);
});
}
}
};
} // namespace panda::compiler
#endif // GRAPH_TEST_H

View File

@ -0,0 +1,64 @@
/*
Copyright (c) 2023 Huawei Device Co., Ltd.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
*
http://www.apache.org/licenses/LICENSE-2.0
*
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
function func1() {
let a = 1.5;
try {
a += 1;
} catch(e) {
a++;
}
a++;
return a;
}
function func2(a, b, c) {
var a = 1;
if (a) {
return a;
}
else
a += 1;
}
function func3(a, b, c, d, e) {
return a + b + c + d + e;
}
function func4(x, y) {
var a = x + y;
var b = x - y;
var c = x * y;
var d = x / y;
var e = x % y;
return func3(e, d, c, b, a);
}
function func5(i, j, l) {
for (var i = 0; i <= 4; i++) {
for (var j = 0; j <= 4; j++) {
for(var k = 1; k <= 2; k++){
System.out.printf(Loadstring);
}
}
func4(x, y);
}
}
function func6()
{
return "hello world";
}

View File

@ -0,0 +1,39 @@
/*
Copyright (c) 2023 Huawei Device Co., Ltd.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
*
http://www.apache.org/licenses/LICENSE-2.0
*
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
try {
try {
a = 1;
} catch(e) {
a;
}
if(a == "") {
throw "null";
}
if(x > 100) {
throw "max";
} else {
throw "min";
}
}
catch(err) {
func0(a, 10);
}
finally {
console.log("error");
}

View File

@ -0,0 +1,225 @@
/*
* Copyright (c) 2023 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <gtest/gtest.h>
#include "assembler/assembly-parser.h"
#include "compiler/optimizer/ir/basicblock.h"
#include "graph_test.h"
#include "mem/pool_manager.h"
#include "optimize_bytecode.h"
using namespace testing::ext;
namespace panda::bytecodeopt {
using namespace compiler;
using namespace panda::pandasm;
class OptimizeBytecodeTest : public testing::Test {
public:
static void SetUpTestCase(void) {}
static void TearDownTestCase(void) {}
void SetUp() {}
void TearDown() {}
void EmitAndOptimize(const std::string &abc_file_name, panda::pandasm::Program &program) const
{
panda::pandasm::AsmEmitter::PandaFileToPandaAsmMaps panda_file_to_asm_maps {};
EXPECT_TRUE(panda::pandasm::AsmEmitter::Emit(abc_file_name, program, nullptr, &panda_file_to_asm_maps, false));
EXPECT_TRUE(panda::bytecodeopt::OptimizeBytecode(&program, &panda_file_to_asm_maps, abc_file_name, true));
}
};
/**
* @tc.name: optimize_bytecode_test_001
* @tc.desc: Verify the OptimizeBytecode function.
* @tc.type: FUNC
* @tc.require: issueNumber
*/
HWTEST_F(OptimizeBytecodeTest, optimize_bytecode_test_001, TestSize.Level1) {
const auto source = R"(
.language ECMAScript
.array array_unsigned_byte u8 3 { 1 2 3 }
.record Asm4 { i32 asm1 }
.function any func_main_0(any a0, any a1, any a2) <static> {
ldundefined
stglobalvar 0x0, "j"
ldundefined
stglobalvar 0x1, "n"
lda.str "a b c"
stglobalvar 0x2, "n"
ldglobalvar 0x3, "n"
getpropiterator
sta v0
jump_label_1:
getnextpropname v0
sta v1
ldundefined
eq 0x4, v1
jnez jump_label_0
lda v1
stglobalvar 0x5, "j"
tryldglobalbyname 0x6, "console"
sta v1
ldobjbyname 0x7, "log"
sta v2
ldglobalvar 0x9, "n"
sta v3
ldglobalvar 0xa, "j"
ldobjbyvalue 0xb, v3
sta v3
lda v2
callthis1 0xd, v1, v3
jmp jump_label_1
jump_label_0:
ldundefined
returnundefined
}
)";
panda::pandasm::Parser parser;
auto res = parser.Parse(source);
auto &program = res.Value();
const std::string fun_name = "func_main_0:(any,any,any)";
auto it = program.function_table.find(fun_name);
EXPECT_NE(it, program.function_table.end());
EmitAndOptimize("codegenTryCatch.abc", program);
}
/**
* @tc.name: optimize_bytecode_test_002
* @tc.desc: Verify the OptimizeBytecode function.
* @tc.type: FUNC
* @tc.require: issueNumber
*/
HWTEST_F(OptimizeBytecodeTest, optimize_bytecode_test_002, TestSize.Level1) {
const auto source = R"(
.language ECMAScript
.array array_unsigned_byte u8 3 { 1 2 3 }
.record Asm4 { i32 asm1 }
.function any func_main_0(any a0, any a1, any a2) <static> {
ldundefined
stglobalvar 0x0, "j"
ldundefined
stglobalvar 0x1, "n"
lda.str "a b c"
stglobalvar 0x2, "n"
ldglobalvar 0x3, "n"
getpropiterator
sta v0
jump_label_1:
getnextpropname v0
sta v1
ldundefined
eq 0x4, v1
jnez jump_label_0
lda v1
stglobalvar 0x5, "j"
tryldglobalbyname 0x6, "console"
sta v1
ldobjbyname 0x7, "log"
sta v2
ldglobalvar 0x9, "n"
sta v3
ldglobalvar 0xa, "j"
ldobjbyvalue 0xb, v3
sta v3
lda v2
callthis1 0xd, v1, v3
jmp jump_label_1
jump_label_0:
ldundefined
returnundefined
}
)";
panda::pandasm::Parser parser;
auto res = parser.Parse(source);
auto &program = res.Value();
const std::string fun_name = "func_main_0:(any,any,any)";
auto it = program.function_table.find(fun_name);
EXPECT_NE(it, program.function_table.end());
const std::string abc_file_name = "codegenTryCatch.abc";
panda::pandasm::AsmEmitter::PandaFileToPandaAsmMaps panda_file_to_asm_maps {};
EXPECT_TRUE(panda::pandasm::AsmEmitter::Emit(abc_file_name, program, nullptr, &panda_file_to_asm_maps, false));
}
/**
* @tc.name: optimize_bytecode_test_003
* @tc.desc: Verify the OptimizeBytecode function.
* @tc.type: FUNC
* @tc.require: issueNumber
*/
HWTEST_F(OptimizeBytecodeTest, optimize_bytecode_test_003, TestSize.Level1) {
const auto source = R"(
.language ECMAScript
.array array_unsigned_byte u8 3 { 1 2 3 }
.record Asm4 { i32 asm1 }
.function any func_main_0(any a0, any a1, any a2) <static> {
ldundefined
stglobalvar 0x0, "j"
ldundefined
stglobalvar 0x1, "n"
lda.str "a b c"
stglobalvar 0x2, "n"
ldglobalvar 0x3, "n"
getpropiterator
sta v0
jump_label_1:
getnextpropname v0
sta v1
ldundefined
eq 0x4, v1
jnez jump_label_0
lda v1
stglobalvar 0x5, "j"
tryldglobalbyname 0x6, "console"
sta v1
ldobjbyname 0x7, "log"
sta v2
ldglobalvar 0x9, "n"
sta v3
ldglobalvar 0xa, "j"
ldobjbyvalue 0xb, v3
sta v3
lda v2
callthis1 0xd, v1, v3
jmp jump_label_1
jump_label_0:
ldundefined
returnundefined
}
)";
panda::pandasm::Parser parser;
auto res = parser.Parse(source);
auto &program = res.Value();
const std::string fun_name = "func_main_0:(any,any,any)";
auto it = program.function_table.find(fun_name);
Function::CatchBlock cat;
cat.try_end_label = "random"; // random: It's a random string
it->second.catch_blocks.push_back(cat);
EXPECT_NE(it, program.function_table.end());
const std::string abc_file_name = "codegenTryCatch.abc";
panda::pandasm::AsmEmitter::PandaFileToPandaAsmMaps panda_file_to_asm_maps {};
EXPECT_TRUE(panda::pandasm::AsmEmitter::Emit(abc_file_name, program, nullptr, &panda_file_to_asm_maps, false));
// do not optimize function having catch blocks"
EXPECT_FALSE(panda::bytecodeopt::OptimizeBytecode(&program, &panda_file_to_asm_maps, abc_file_name, true));
}
} // namespace panda::bytecodeopt

View File

@ -0,0 +1,86 @@
/*
* Copyright (c) 2023 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <gtest/gtest.h>
#include "compiler/optimizer/ir/basicblock.h"
#include "compiler/optimizer/optimizations/regalloc/reg_alloc.h"
#include "graph_test.h"
#include "mem/pool_manager.h"
#include "reg_acc_alloc.h"
#include "reg_encoder.h"
using namespace testing::ext;
namespace panda::bytecodeopt {
using namespace compiler;
using namespace panda;
class RegEncoderTest : public testing::Test {
public:
static void SetUpTestCase(void) {}
static void TearDownTestCase(void) {}
void SetUp() {}
void TearDown() {}
GraphTest graph_test_;
};
/**
* @tc.name: reg_encoder_test_001
* @tc.desc: Verify the RunImpl function.
* @tc.type: FUNC
* @tc.require: issueNumber
*/
HWTEST_F(RegEncoderTest, reg_encoder_test_001, TestSize.Level1)
{
std::string pFile = GRAPH_TEST_ABC_DIR "codegenTryCatch.abc";
const char *test_method_name = "func4";
bool status = false;
graph_test_.TestBuildGraphFromFile(pFile, [test_method_name, &status](Graph* graph, std::string &method_name) {
if (test_method_name != method_name) {
return;
}
status = true;
EXPECT_NE(graph, nullptr);
EXPECT_TRUE(RegAlloc(graph));
EXPECT_TRUE(graph->RunPass<RegEncoder>());
});
EXPECT_TRUE(status);
}
/**
* @tc.name: reg_encoder_test_002
* @tc.desc: Verify the RunImpl function.
* @tc.type: FUNC
* @tc.require: issueNumber
*/
HWTEST_F(RegEncoderTest, reg_encoder_test_002, TestSize.Level1)
{
std::string pFile = GRAPH_TEST_ABC_DIR "optimizeTryCatch.abc";
const char *test_method_name = "func_main_0";
bool status = false;
graph_test_.TestBuildGraphFromFile(pFile, [test_method_name, &status](Graph* graph, std::string &method_name) {
if (test_method_name != method_name) {
return;
}
status = true;
EXPECT_NE(graph, nullptr);
EXPECT_TRUE(graph->RunPass<RegAccAlloc>());
});
EXPECT_TRUE(status);
}
} // namespace panda::bytecodeopt