From 145b9fd2ae9b54a17a28aa690e1223226952f9bc Mon Sep 17 00:00:00 2001 From: ctw-ian Date: Fri, 31 Mar 2023 11:32:50 +0800 Subject: [PATCH] Supporting IR-build of functions with try-catch Issue:https://gitee.com/openharmony/arkcompiler_runtime_core/issues/I6RW02 Signed-off-by: ctw-ian Change-Id: I0783cb829c3175b2cd16c1e1451f125256a7d2eb --- BUILD.gn | 5 + compiler/optimizer/ir/dump.cpp | 2 +- compiler/optimizer/ir/inst.h | 7 + compiler/optimizer/ir/ir-dyn-base-types.h | 8 +- compiler/optimizer/ir_builder/ir_builder.cpp | 36 +- .../templates/inst_builder_gen.cpp.erb | 14 + compiler/tests/BUILD.gn | 76 ++ compiler/tests/irBuilder_tests.cpp | 678 ++++++++++++++++++ compiler/tests/js/nestedTryCatch.js | 24 + compiler/tests/js/simpleTryCatch.js | 20 + 10 files changed, 845 insertions(+), 25 deletions(-) create mode 100644 compiler/tests/BUILD.gn create mode 100644 compiler/tests/irBuilder_tests.cpp create mode 100644 compiler/tests/js/nestedTryCatch.js create mode 100644 compiler/tests/js/simpleTryCatch.js diff --git a/BUILD.gn b/BUILD.gn index 452d4bf834..fe9a9cc4c5 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -311,4 +311,9 @@ if (!ark_standalone_build) { "$ark_root/platforms/tests:host_unittest", ] } + + group("compiler_host_unittest") { + testonly = true + deps = [ "$ark_root/compiler/tests:host_unittest" ] + } } diff --git a/compiler/optimizer/ir/dump.cpp b/compiler/optimizer/ir/dump.cpp index 706bc0cf37..d02cddd512 100644 --- a/compiler/optimizer/ir/dump.cpp +++ b/compiler/optimizer/ir/dump.cpp @@ -379,7 +379,7 @@ void IntrinsicInst::DumpOpcode(std::ostream *out) const { const auto &adapter = GetBasicBlock()->GetGraph()->GetLocalAllocator()->Adapter(); ArenaString intrinsic(ArenaString("Intrinsic.", adapter)); - ArenaString opcode("", adapter); + ArenaString opcode(GetIntrinsicOpcodeName(), adapter); (*out) << std::setw(INDENT_OPCODE) << intrinsic + opcode << " "; } diff --git a/compiler/optimizer/ir/inst.h b/compiler/optimizer/ir/inst.h index a6e245c530..32067694cd 100644 --- a/compiler/optimizer/ir/inst.h +++ b/compiler/optimizer/ir/inst.h @@ -1275,6 +1275,11 @@ public: return opcode_ == Opcode::SaveState; } + bool IsTry() const + { + return opcode_ == Opcode::Try; + } + virtual void SetVnObject([[maybe_unused]] VnObject *vn_obj) {} Register GetDstReg() const @@ -2588,6 +2593,8 @@ protected: using LastField = Relocate; private: + std::string GetIntrinsicOpcodeName() const; + IntrinsicId intrinsic_id_ {RuntimeInterface::IntrinsicId::COUNT}; ArenaVector *imms_ {nullptr}; // record imms appeared in intrinsics }; diff --git a/compiler/optimizer/ir/ir-dyn-base-types.h b/compiler/optimizer/ir/ir-dyn-base-types.h index 2413dde2e5..fa69cd84a4 100644 --- a/compiler/optimizer/ir/ir-dyn-base-types.h +++ b/compiler/optimizer/ir/ir-dyn-base-types.h @@ -80,13 +80,7 @@ inline panda::compiler::DataType::Type AnyBaseTypeToDataType([[maybe_unused]] An inline const char *AnyTypeTypeToString(AnyBaseType any_type) { - static constexpr auto COUNT = static_cast(AnyBaseType::COUNT); - static constexpr std::array ANYBASETYPE_NAMES = { - "UNDEFINED_TYPE", - }; - auto idx = static_cast(any_type); - ASSERT(idx < COUNT); - return ANYBASETYPE_NAMES[idx]; + return "UNDEFINED_TYPE"; } } // namespace panda::compiler diff --git a/compiler/optimizer/ir_builder/ir_builder.cpp b/compiler/optimizer/ir_builder/ir_builder.cpp index bec43c463a..3374338e0b 100644 --- a/compiler/optimizer/ir_builder/ir_builder.cpp +++ b/compiler/optimizer/ir_builder/ir_builder.cpp @@ -146,19 +146,19 @@ bool IrBuilder::BuildInstructionsForBB(BasicBlock *bb, InstBuilder *inst_builder COMPILER_LOG(WARNING, IR_BUILDER) << "Unsupported instruction"; return false; } - if (inst.CanThrow()) { - // One PBC instruction can be expanded to the group of IR's instructions, find first built instruction in - // this group, and then mark all instructions as throwable; All instructions should be marked, since some of - // them can be deleted during optimizations, unnecessary catch-phi moves will be resolved before Register - // Allocator - auto throwable_inst = (current_last_inst == nullptr) ? bb->GetFirstInst() : current_last_inst->GetNext(); - ProcessThrowableInstructions(inst_builder, throwable_inst); - - auto &vb = GetGraph()->GetVectorBlocks(); - for (size_t i = bb_count; i < vb.size(); i++) { - ProcessThrowableInstructions(inst_builder, vb[i]->GetFirstInst()); - } + + // One PBC instruction can be expanded to the group of IR's instructions, find first built instruction in + // this group, and then mark all instructions as throwable; All instructions should be marked, since some of + // them can be deleted during optimizations, unnecessary catch-phi moves will be resolved before Register + // Allocator + auto throwable_inst = (current_last_inst == nullptr) ? bb->GetFirstInst() : current_last_inst->GetNext(); + ProcessThrowableInstructions(inst_builder, throwable_inst); + + auto &vb = GetGraph()->GetVectorBlocks(); + for (size_t i = bb_count; i < vb.size(); i++) { + ProcessThrowableInstructions(inst_builder, vb[i]->GetFirstInst()); } + // Break if we meet terminator instruction. If instruction in the middle of basic block we don't create // further dead instructions. if (inst.IsTerminator() && !inst.IsSuspend()) { @@ -268,9 +268,13 @@ void IrBuilder::CreateTryCatchBoundariesBlocks() panda_file::CodeDataAccessor cda(*panda_file, mda.GetCodeId().value()); cda.EnumerateTryBlocks([this](panda_file::CodeDataAccessor::TryBlock &try_block) { - try_block.EnumerateCatchBlocks([this](panda_file::CodeDataAccessor::CatchBlock &catch_block) { + auto start_pc = try_block.GetStartPc(); + auto end_pc = start_pc + try_block.GetLength(); + auto try_info = InsertTryBlockInfo({start_pc, end_pc}); + try_block.EnumerateCatchBlocks([this, try_info](panda_file::CodeDataAccessor::CatchBlock &catch_block) { auto pc = catch_block.GetHandlerPc(); catches_pc_.insert(pc); + try_info->catches->emplace_back(CatchCodeBlock {pc, 0u}); return true; }); @@ -371,10 +375,8 @@ void IrBuilder::TrackTryBoundaries(size_t pc, const BytecodeInstruction &inst) } } - if (inst.CanThrow()) { - for (auto &try_block : opened_try_blocks_) { - try_block->contains_throwable_inst = true; - } + for (auto &try_block : opened_try_blocks_) { + try_block->contains_throwable_inst = true; } } diff --git a/compiler/optimizer/templates/inst_builder_gen.cpp.erb b/compiler/optimizer/templates/inst_builder_gen.cpp.erb index 8a44afc2c2..7c25cbb590 100644 --- a/compiler/optimizer/templates/inst_builder_gen.cpp.erb +++ b/compiler/optimizer/templates/inst_builder_gen.cpp.erb @@ -376,4 +376,18 @@ void InstBuilder::BuildEcmaAsIntrinsics(const BytecodeInstruction* bc_inst) // N return; } } + +std::string IntrinsicInst::GetIntrinsicOpcodeName() const +{ + switch(GetIntrinsicId()) { +% Panda::instructions.select{|b| b.namespace == "ecmascript"}.each do |inst| + case compiler::RuntimeInterface::IntrinsicId::<%= inst.opcode.upcase %>: { + return "<%= inst.mnemonic%>"; + } +% end + default: { + return ""; + } + } +} } // namespace panda::compiler diff --git a/compiler/tests/BUILD.gn b/compiler/tests/BUILD.gn new file mode 100644 index 0000000000..2c6bae90f9 --- /dev/null +++ b/compiler/tests/BUILD.gn @@ -0,0 +1,76 @@ +# 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. + +import("//arkcompiler/ets_frontend/ts2panda/ts2abc_config.gni") +import("//arkcompiler/runtime_core/ark_config.gni") +import("$ark_root/tests/test_helper.gni") + +compiler_test_configs = [ + "$ark_root:ark_config", + "$ark_root/assembler:arkassembler_public_config", + "$ark_root/bytecode_optimizer:bytecodeopt_public_config", + "$ark_root/compiler:arkcompiler_public_config", + "$ark_root/libpandabase:arkbase_public_config", + "$ark_root/libpandafile:arkfile_public_config", + sdk_libc_secshared_config, +] + +compiler_test_deps = [ + "$ark_root/assembler:libarkassembler", + "$ark_root/bytecode_optimizer:libarkbytecodeopt", + "$ark_root/compiler:libarkcompiler", + "$ark_root/libpandabase:libarkbase", + "$ark_root/libpandafile:libarkfile", + sdk_libc_secshared_dep, +] + +test_js_path = "//arkcompiler/runtime_core/compiler/tests/js/" + +test_js_files = [ + "simpleTryCatch", + "nestedTryCatch", +] + +foreach(file, test_js_files) { + ts2abc_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("IrBuilderTest") { + module_out_path = module_output_path + + sources = [ "irBuilder_tests.cpp" ] + configs = compiler_test_configs + deps = compiler_test_deps + + test_abc_dir = rebase_path(target_out_dir) + + defines = [ "IRBUILDER_ABC_DIR=\"${test_abc_dir}/\"" ] + + foreach(file, test_js_files) { + deps += [ ":gen_${file}_abc" ] + } +} + +group("host_unittest") { + testonly = true + deps = [ ":IrBuilderTestAction" ] +} diff --git a/compiler/tests/irBuilder_tests.cpp b/compiler/tests/irBuilder_tests.cpp new file mode 100644 index 0000000000..2781c57254 --- /dev/null +++ b/compiler/tests/irBuilder_tests.cpp @@ -0,0 +1,678 @@ +/* + * 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 + +#include "assembly-parser.h" +#include "bytecode_optimizer/ir_interface.h" +#include "bytecode_optimizer/runtime_adapter.h" +#include "libpandabase/mem/arena_allocator.h" +#include "libpandabase/mem/pool_manager.h" +#include "libpandafile/class_data_accessor.h" +#include "libpandafile/class_data_accessor-inl.h" +#include "libpandafile/method_data_accessor.h" +#include "libpandafile/file.h" +#include "macros.h" +#include "optimizer/ir/graph.h" +#include "optimizer/ir/inst.h" +#include "optimizer/ir/runtime_interface.h" +#include "optimizer/ir_builder/ir_builder.h" + +namespace panda::compiler { +class IrBuilderTest : public testing::Test { +public: + static void SetUpTestCase(void) {}; + static void TearDownTestCase(void) {}; + void SetUp() {}; + void TearDown() {}; + + IrBuilderTest() + { + PoolManager::Initialize(PoolType::MALLOC); + } + + ~IrBuilderTest() + { + PoolManager::Finalize(); + } + + template + void TestBuildGraphFromFunc(pandasm::Program &prog, const char *methodName, const Callback &cb) + { + pandasm::AsmEmitter::PandaFileToPandaAsmMaps maps {}; + 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([&prog, maps, methodName, &cb](panda_file::MethodDataAccessor &mda) { + auto ir_interface = panda::bytecodeopt::BytecodeOptIrInterface(&maps, &prog); + auto func_name = ir_interface.GetMethodIdByOffset(mda.GetMethodId().GetOffset()); + if (func_name != methodName) { + return; + } + + ArenaAllocator allocator {SpaceType::SPACE_TYPE_COMPILER}; + ArenaAllocator local_allocator {SpaceType::SPACE_TYPE_COMPILER, nullptr, true}; + + auto method_ptr = reinterpret_cast( + mda.GetMethodId().GetOffset()); + panda::BytecodeOptimizerRuntimeAdapter adapter(mda.GetPandaFile()); + auto *graph = allocator.New(&allocator, &local_allocator, Arch::NONE, method_ptr, &adapter, + false, nullptr, true, true); + graph->RunPass(); + cb(graph); + }); + } + } + + template + void TestBuildGraphFromFile(const std::string &pFileName, const Callback &cb) + { + auto pfile = panda_file::OpenPandaFile(pFileName); + 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( + mda.GetMethodId().GetOffset()); + panda::BytecodeOptimizerRuntimeAdapter adapter(mda.GetPandaFile()); + auto *graph = allocator.New(&allocator, &local_allocator, Arch::NONE, method_ptr, &adapter, + false, nullptr, true, true); + graph->RunPass(); + + auto methodName = std::string(utf::Mutf8AsCString(pfile->GetStringData(mda.GetNameId()).data)); + + cb(graph, methodName); + } + }); + } + } +}; + +HWTEST_F(IrBuilderTest, simpleTryCatchAsm, testing::ext::TestSize.Level1) +{ + /** + * try { + * a = 1; + * } catch(e) { + * a = 2; + * } + */ + const auto source = R"( + .language ECMAScript + .function any foo(any a0, any a1, any a2) { + mov v0, a0 + mov v1, a1 + mov v2, a2 + try_begin: + ldai 0x1 + trystglobalbyname 0x0, "a" + try_end: + jmp catch_end + catch_begin: + sta v4 + tryldglobalbyname 0x1, "a" + catch_end: + returnundefined + } + )"; + + panda::pandasm::Parser parser; + auto res = parser.Parse(source); + auto &prog = res.Value(); + for (auto &[name, func] : prog.function_table) { + auto &catchBlock = func.catch_blocks.emplace_back(); + catchBlock.try_begin_label = "try_begin"; + catchBlock.try_end_label = "try_end"; + catchBlock.catch_begin_label = "catch_begin"; + catchBlock.catch_end_label = "catch_end"; + } + + TestBuildGraphFromFunc(prog, "foo:(any,any,any)", [](Graph* graph) { + EXPECT_NE(graph, nullptr); + for (auto bb : graph->GetBlocksRPO()) { + EXPECT_NE(bb, nullptr); + if (bb->IsTryBegin()) { + EXPECT_TRUE(bb->GetPredsBlocks().size() == 1); + EXPECT_TRUE(bb->GetSuccsBlocks().size() == 2); + EXPECT_TRUE(bb->GetSuccessor(0)->IsTry()); + EXPECT_TRUE(bb->GetSuccessor(1)->IsCatch()); + EXPECT_TRUE(bb->GetSuccessor(1)->IsCatchBegin()); + + EXPECT_TRUE(bb->GetFirstInst()->IsTry()); + continue; + } + + if (bb->IsTry()) { + EXPECT_TRUE(bb->GetPredsBlocks().size() == 1); + EXPECT_TRUE(bb->GetPredecessor(0)->IsTryBegin()); + EXPECT_TRUE(bb->GetSuccsBlocks().size() == 1); + + EXPECT_TRUE(bb->GetFirstInst()->IsSaveState()); + EXPECT_TRUE(bb->GetLastInst()->IsIntrinsic()); + EXPECT_TRUE((static_cast(bb->GetLastInst()))->GetIntrinsicId() == + RuntimeInterface::IntrinsicId::TRYSTGLOBALBYNAME_IMM8_ID16); + continue; + } + + if (bb->IsTryEnd()) { + EXPECT_TRUE(bb->GetPredsBlocks().size() == 1); + EXPECT_TRUE(bb->GetPredecessor(0)->IsTry()); + EXPECT_TRUE(bb->GetSuccsBlocks().size() == 2); + EXPECT_TRUE(bb->GetSuccessor(0)->GetGuestPc() == bb->GetGuestPc()); + EXPECT_TRUE(!bb->GetSuccessor(0)->IsCatch()); + EXPECT_TRUE(!bb->GetSuccessor(0)->IsCatchBegin()); + EXPECT_TRUE(bb->GetSuccessor(1)->IsCatch()); + EXPECT_TRUE(bb->GetSuccessor(1)->IsCatchBegin()); + + EXPECT_TRUE(bb->IsEmpty()); + continue; + } + + if (bb->IsCatchBegin()) { + EXPECT_TRUE(bb->GetPredsBlocks().size() == 2); + EXPECT_TRUE(bb->GetPredecessor(0)->IsTryBegin()); + EXPECT_TRUE(bb->GetPredecessor(1)->IsTryEnd()); + EXPECT_TRUE(bb->GetSuccessor(0)->IsCatch()); + + EXPECT_TRUE(bb->IsEmpty()); + continue; + } + + if (bb->IsCatch() && !bb->IsCatchBegin()) { + EXPECT_TRUE(bb->GetPredsBlocks().size() == 1); + EXPECT_TRUE(bb->GetPredecessor(0)->IsCatchBegin()); + + EXPECT_TRUE(bb->GetSuccessor(0)->GetFirstInst()->IsSaveState()); + EXPECT_TRUE(bb->GetSuccessor(0)->GetLastInst()->IsIntrinsic()); + EXPECT_TRUE((static_cast(bb->GetSuccessor(0)->GetLastInst()))->GetIntrinsicId() == + RuntimeInterface::IntrinsicId::RETURNUNDEFINED); + + EXPECT_TRUE(bb->GetFirstInst()->IsSaveState()); + EXPECT_TRUE(bb->GetLastInst()->IsIntrinsic()); + EXPECT_TRUE((static_cast(bb->GetLastInst()))->GetIntrinsicId() == + RuntimeInterface::IntrinsicId::TRYLDGLOBALBYNAME_IMM8_ID16); + continue; + } + } + }); +} + +HWTEST_F(IrBuilderTest, nestedTryCatchAsm, testing::ext::TestSize.Level1) +{ + /** + * try { + * try { + * a = 1; + * } catch(e) { + * a; + * } + * } catch(e) { + * print(e); + * } + */ + const auto source = R"( + .language ECMAScript + .function any foo(any a0, any a1, any a2) { + mov v2, a2 + mov v1, a1 + mov v0, a0 + lda.str "use strict" + LABEL_0: + LABEL_1: + ldai 1 + trystglobalbyname 0, "a" + LABEL_2: + jmp LABEL_3 + LABEL_4: + sta v3 + tryldglobalbyname 1, "a" + LABEL_3: + LABEL_5: + jmp LABEL_6 + LABEL_7: + sta v4 + tryldglobalbyname 2, "print" + sta v5 + lda v4 + sta v6 + lda v5 + callarg1 3, v6 + LABEL_6: + returnundefined + } + )"; + + panda::pandasm::Parser parser; + auto res = parser.Parse(source); + auto &prog = res.Value(); + EXPECT_TRUE(prog.function_table.size() == 1); + for (auto &[name, func] : prog.function_table) { + auto &catchBlock1 = func.catch_blocks.emplace_back(); + catchBlock1.try_begin_label = "LABEL_0"; + catchBlock1.try_end_label = "LABEL_1"; + catchBlock1.catch_begin_label = "LABEL_7"; + catchBlock1.catch_end_label = "LABEL_6"; + + auto &catchBlock2 = func.catch_blocks.emplace_back(); + catchBlock2.try_begin_label = "LABEL_1"; + catchBlock2.try_end_label = "LABEL_2"; + catchBlock2.catch_begin_label = "LABEL_4"; + catchBlock2.catch_end_label = "LABEL_3"; + + auto &catchBlock3 = func.catch_blocks.emplace_back(); + catchBlock3.try_begin_label = "LABEL_2"; + catchBlock3.try_end_label = "LABEL_5"; + catchBlock3.catch_begin_label = "LABEL_7"; + catchBlock3.catch_end_label = "LABEL_6"; + } + + TestBuildGraphFromFunc(prog, "foo:(any,any,any)", [](Graph* graph) { + EXPECT_NE(graph, nullptr); + int32_t numOfTry = 0; + for (auto bb : graph->GetBlocksRPO()) { + EXPECT_NE(bb, nullptr); + if (bb->IsTryBegin()) { + numOfTry++; + + EXPECT_TRUE(bb->GetPredsBlocks().size() == 1); + + EXPECT_TRUE(bb->GetSuccsBlocks().size() == 2); + EXPECT_TRUE(bb->GetSuccessor(0)->IsTry()); + EXPECT_TRUE(bb->GetSuccessor(1)->IsCatch()); + EXPECT_TRUE(bb->GetSuccessor(1)->IsCatchBegin()); + + EXPECT_TRUE(bb->GetSuccessor(0)->GetTryId() == bb->GetTryId()); + continue; + } + + if (bb->IsTry()) { + EXPECT_TRUE(bb->GetPredsBlocks().size() == 1); + if (!bb->GetPredecessor(0)->IsTryBegin()) { + EXPECT_TRUE(bb->GetPredecessor(0)->IsCatchBegin()); + EXPECT_TRUE(bb->GetPredecessor(0)->IsCatch()); + + EXPECT_TRUE(bb->GetFirstInst()->IsSaveState()); + EXPECT_TRUE(bb->GetLastInst()->IsIntrinsic()); + EXPECT_TRUE((static_cast(bb->GetLastInst()))->GetIntrinsicId() == + RuntimeInterface::IntrinsicId::TRYLDGLOBALBYNAME_IMM8_ID16); + continue; + } + + + EXPECT_TRUE(bb->GetPredecessor(0)->GetTryId() == bb->GetTryId()); + EXPECT_TRUE(bb->GetPredecessor(0)->GetGuestPc() == bb->GetGuestPc()); + if (bb->GetPredecessor(0)->GetTryId() == 1) { + EXPECT_TRUE(bb->GetSuccsBlocks().size() == 1); + EXPECT_TRUE(bb->GetSuccessor(0)->IsTryEnd()); + + EXPECT_TRUE(bb->GetFirstInst()->IsSaveState()); + EXPECT_TRUE(bb->GetLastInst()->IsIntrinsic()); + EXPECT_TRUE((static_cast(bb->GetLastInst()))->GetIntrinsicId() == + RuntimeInterface::IntrinsicId::TRYSTGLOBALBYNAME_IMM8_ID16); + continue; + } + + EXPECT_TRUE(bb->GetPredecessor(0)->GetTryId() == 2); + EXPECT_TRUE(bb->GetSuccsBlocks().size() == 1); + EXPECT_TRUE(bb->IsEmpty()); + + for (auto inst : bb->GetSuccessor(0)->AllInsts()) { + EXPECT_TRUE(inst->IsPhi()); + } + continue; + } + + if (bb->IsTryEnd()) { + EXPECT_TRUE(bb->GetPredsBlocks().size() == 1); + EXPECT_TRUE(bb->GetPredecessor(0)->IsTry()); + EXPECT_TRUE(bb->GetPredecessor(0)->GetTryId() == bb->GetTryId()); + + EXPECT_TRUE(bb->IsEmpty()); + + EXPECT_TRUE(bb->GetSuccsBlocks().size() == 2); + if (bb->GetTryId() == 1) { + EXPECT_TRUE(bb->GetSuccessor(0)->IsTryBegin()); + EXPECT_TRUE(bb->GetSuccessor(0)->GetGuestPc() == bb->GetGuestPc()); + + EXPECT_TRUE(bb->GetSuccessor(1)->IsCatchBegin()); + EXPECT_TRUE(bb->GetSuccessor(1)->IsCatch()); + + for (auto inst : bb->GetSuccessor(1)->AllInsts()) { + EXPECT_TRUE(inst->IsCatchPhi()); + } + continue; + } + + EXPECT_TRUE(bb->GetTryId() == 2); + EXPECT_TRUE(bb->GetSuccessor(1)->IsCatchBegin()); + EXPECT_TRUE(bb->GetSuccessor(1)->IsCatch()); + for (auto inst : bb->GetSuccessor(0)->AllInsts()) { + EXPECT_TRUE(inst->IsPhi()); + } + continue; + } + + if (bb->IsCatchBegin()) { + EXPECT_TRUE(bb->GetPredsBlocks().size() == 2); + EXPECT_TRUE(bb->GetPredecessor(0)->IsTryBegin()); + EXPECT_TRUE(bb->GetPredecessor(1)->IsTryEnd()); + + EXPECT_TRUE(bb->GetSuccessor(0)->IsCatch()); + continue; + } + + if (bb->IsCatch() && !bb->IsCatchBegin()) { + EXPECT_TRUE(bb->GetPredsBlocks().size() == 1); + + if (bb->IsTry()) { + EXPECT_TRUE(bb->GetTryId() == 2); + + EXPECT_TRUE(bb->GetSuccsBlocks().size() == 1); + EXPECT_TRUE(bb->GetSuccessor(0)->IsTryEnd()); + EXPECT_TRUE(bb->GetSuccessor(0)->IsCatch()); + + EXPECT_TRUE(bb->GetFirstInst()->IsSaveState()); + EXPECT_TRUE(bb->GetLastInst()->IsIntrinsic()); + EXPECT_TRUE((static_cast(bb->GetLastInst()))->GetIntrinsicId() == + RuntimeInterface::IntrinsicId::TRYLDGLOBALBYNAME_IMM8_ID16); + continue; + } + + if (bb->IsTryEnd()) { + EXPECT_TRUE(bb->GetPredecessor(0)->IsTry()); + EXPECT_TRUE(bb->GetPredecessor(0)->IsCatch()); + + EXPECT_TRUE(bb->GetSuccsBlocks().size() == 2); + for (auto inst : bb->GetSuccessor(0)->AllInsts()) { + EXPECT_TRUE(inst->IsPhi()); + } + EXPECT_TRUE(bb->GetSuccessor(1)->IsCatchBegin()); + EXPECT_TRUE(bb->GetSuccessor(1)->IsCatch()); + + EXPECT_TRUE(bb->IsEmpty()); + continue; + } + + EXPECT_TRUE(bb->GetPredecessor(0)->IsCatchBegin()); + EXPECT_TRUE(bb->GetPredecessor(0)->IsCatch()); + + EXPECT_TRUE(bb->GetSuccsBlocks().size() == 1); + auto *successor = bb->GetSuccessor(0); + EXPECT_TRUE(successor->GetFirstInst()->IsSaveState()); + EXPECT_TRUE(successor->GetLastInst()->IsIntrinsic()); + EXPECT_TRUE((static_cast(successor->GetLastInst()))->GetIntrinsicId() == + RuntimeInterface::IntrinsicId::RETURNUNDEFINED); + for (auto inst : successor->GetPredecessor(0)->AllInsts()) { + EXPECT_TRUE(inst->IsPhi()); + } + + EXPECT_TRUE(bb->GetFirstInst()->IsSaveState()); + EXPECT_TRUE(bb->GetLastInst()->IsIntrinsic()); + EXPECT_TRUE((static_cast(bb->GetLastInst()))->GetIntrinsicId() == + RuntimeInterface::IntrinsicId::CALLARG1_IMM8_V8); + } + } + + EXPECT_EQ(numOfTry, 2); + }); +} + +HWTEST_F(IrBuilderTest, simpleTryCatchAbc, testing::ext::TestSize.Level1) +{ + std::string pFile = IRBUILDER_ABC_DIR "simpleTryCatch.abc"; + const char *testMethodName = "func_main_0"; + TestBuildGraphFromFile(pFile, [&testMethodName](Graph* graph, std::string &methodName) { + if (testMethodName != methodName) { + return; + } + + EXPECT_NE(graph, nullptr); + for (auto bb : graph->GetBlocksRPO()) { + EXPECT_NE(bb, nullptr); + if (bb->IsTryBegin()) { + EXPECT_TRUE(bb->GetPredsBlocks().size() == 1); + EXPECT_TRUE(bb->GetSuccsBlocks().size() == 2); + EXPECT_TRUE(bb->GetSuccessor(0)->IsTry()); + EXPECT_TRUE(bb->GetSuccessor(1)->IsCatch()); + EXPECT_TRUE(bb->GetSuccessor(1)->IsCatchBegin()); + + EXPECT_TRUE(bb->GetFirstInst()->IsTry()); + continue; + } + + if (bb->IsTry()) { + EXPECT_TRUE(bb->GetPredsBlocks().size() == 1); + EXPECT_TRUE(bb->GetPredecessor(0)->IsTryBegin()); + EXPECT_TRUE(bb->GetSuccsBlocks().size() == 1); + + EXPECT_TRUE(bb->GetFirstInst()->IsSaveState()); + EXPECT_TRUE(bb->GetLastInst()->IsIntrinsic()); + EXPECT_TRUE((static_cast(bb->GetLastInst()))->GetIntrinsicId() == + RuntimeInterface::IntrinsicId::TRYSTGLOBALBYNAME_IMM8_ID16); + continue; + } + + if (bb->IsTryEnd()) { + EXPECT_TRUE(bb->GetPredsBlocks().size() == 1); + EXPECT_TRUE(bb->GetPredecessor(0)->IsTry()); + EXPECT_TRUE(bb->GetSuccsBlocks().size() == 2); + EXPECT_TRUE(bb->GetSuccessor(0)->GetGuestPc() == bb->GetGuestPc()); + EXPECT_TRUE(!bb->GetSuccessor(0)->IsCatch()); + EXPECT_TRUE(!bb->GetSuccessor(0)->IsCatchBegin()); + EXPECT_TRUE(bb->GetSuccessor(1)->IsCatch()); + EXPECT_TRUE(bb->GetSuccessor(1)->IsCatchBegin()); + + EXPECT_TRUE(bb->IsEmpty()); + continue; + } + + if (bb->IsCatchBegin()) { + EXPECT_TRUE(bb->GetPredsBlocks().size() == 2); + EXPECT_TRUE(bb->GetPredecessor(0)->IsTryBegin()); + EXPECT_TRUE(bb->GetPredecessor(1)->IsTryEnd()); + EXPECT_TRUE(bb->GetSuccessor(0)->IsCatch()); + + EXPECT_TRUE(bb->IsEmpty()); + continue; + } + + if (bb->IsCatch() && !bb->IsCatchBegin()) { + EXPECT_TRUE(bb->GetPredsBlocks().size() == 1); + EXPECT_TRUE(bb->GetPredecessor(0)->IsCatchBegin()); + + EXPECT_TRUE(bb->GetSuccessor(0)->GetFirstInst()->IsSaveState()); + EXPECT_TRUE(bb->GetSuccessor(0)->GetLastInst()->IsIntrinsic()); + EXPECT_TRUE((static_cast(bb->GetSuccessor(0)->GetLastInst()))->GetIntrinsicId() == + RuntimeInterface::IntrinsicId::RETURNUNDEFINED); + + EXPECT_TRUE(bb->GetFirstInst()->IsSaveState()); + EXPECT_TRUE(bb->GetLastInst()->IsIntrinsic()); + EXPECT_TRUE((static_cast(bb->GetLastInst()))->GetIntrinsicId() == + RuntimeInterface::IntrinsicId::TRYLDGLOBALBYNAME_IMM8_ID16); + continue; + } + } + }); +} + +HWTEST_F(IrBuilderTest, nestedTryCatchAbc, testing::ext::TestSize.Level1) +{ + std::string pFile = IRBUILDER_ABC_DIR "nestedTryCatch.abc"; + const char *testMethodName = "func_main_0"; + TestBuildGraphFromFile(pFile, [testMethodName](Graph* graph, std::string &methodName) { + if (testMethodName != methodName) { + return; + } + + EXPECT_NE(graph, nullptr); + int32_t numOfTry = 0; + for (auto bb : graph->GetBlocksRPO()) { + EXPECT_NE(bb, nullptr); + if (bb->IsTryBegin()) { + numOfTry++; + + EXPECT_TRUE(bb->GetPredsBlocks().size() == 1); + + EXPECT_TRUE(bb->GetSuccsBlocks().size() == 2); + EXPECT_TRUE(bb->GetSuccessor(0)->IsTry()); + EXPECT_TRUE(bb->GetSuccessor(1)->IsCatch()); + EXPECT_TRUE(bb->GetSuccessor(1)->IsCatchBegin()); + + EXPECT_TRUE(bb->GetSuccessor(0)->GetTryId() == bb->GetTryId()); + continue; + } + + if (bb->IsTry()) { + EXPECT_TRUE(bb->GetPredsBlocks().size() == 1); + if (!bb->GetPredecessor(0)->IsTryBegin()) { + EXPECT_TRUE(bb->GetPredecessor(0)->IsCatchBegin()); + EXPECT_TRUE(bb->GetPredecessor(0)->IsCatch()); + + EXPECT_TRUE(bb->GetFirstInst()->IsSaveState()); + EXPECT_TRUE(bb->GetLastInst()->IsIntrinsic()); + EXPECT_TRUE((static_cast(bb->GetLastInst()))->GetIntrinsicId() == + RuntimeInterface::IntrinsicId::TRYLDGLOBALBYNAME_IMM8_ID16); + continue; + } + + EXPECT_TRUE(bb->GetPredecessor(0)->GetTryId() == bb->GetTryId()); + EXPECT_TRUE(bb->GetPredecessor(0)->GetGuestPc() == bb->GetGuestPc()); + if (bb->GetPredecessor(0)->GetTryId() == 0) { + EXPECT_TRUE(bb->GetSuccsBlocks().size() == 1); + EXPECT_TRUE(bb->GetSuccessor(0)->IsTryEnd()); + + EXPECT_TRUE(bb->GetFirstInst()->IsSaveState()); + EXPECT_TRUE(bb->GetLastInst()->IsIntrinsic()); + EXPECT_TRUE((static_cast(bb->GetLastInst()))->GetIntrinsicId() == + RuntimeInterface::IntrinsicId::TRYSTGLOBALBYNAME_IMM8_ID16); + continue; + } + + EXPECT_TRUE(bb->GetPredecessor(0)->GetTryId() == 2); + EXPECT_TRUE(bb->GetSuccsBlocks().size() == 1); + EXPECT_TRUE(bb->IsEmpty()); + + for (auto inst : bb->GetSuccessor(0)->AllInsts()) { + EXPECT_TRUE(inst->IsPhi()); + } + continue; + } + + if (bb->IsTryEnd()) { + EXPECT_TRUE(bb->GetPredsBlocks().size() == 1); + EXPECT_TRUE(bb->GetPredecessor(0)->IsTry()); + EXPECT_TRUE(bb->GetPredecessor(0)->GetTryId() == bb->GetTryId()); + + EXPECT_TRUE(bb->IsEmpty()); + + EXPECT_TRUE(bb->GetSuccsBlocks().size() == 2); + if (bb->GetTryId() == 0) { + EXPECT_TRUE(bb->GetSuccessor(0)->IsTryBegin()); + EXPECT_TRUE(bb->GetSuccessor(0)->GetGuestPc() == bb->GetGuestPc()); + + EXPECT_TRUE(bb->GetSuccessor(1)->IsCatchBegin()); + EXPECT_TRUE(bb->GetSuccessor(1)->IsCatch()); + + for (auto inst : bb->GetSuccessor(1)->AllInsts()) { + EXPECT_TRUE(inst->IsCatchPhi()); + } + continue; + } + + EXPECT_TRUE(bb->GetTryId() == 2); + EXPECT_TRUE(bb->GetSuccessor(1)->IsCatchBegin()); + EXPECT_TRUE(bb->GetSuccessor(1)->IsCatch()); + for (auto inst : bb->GetSuccessor(0)->AllInsts()) { + EXPECT_TRUE(inst->IsPhi()); + } + continue; + } + + if (bb->IsCatchBegin()) { + EXPECT_TRUE(bb->GetPredsBlocks().size() == 2); + EXPECT_TRUE(bb->GetPredecessor(0)->IsTryBegin()); + EXPECT_TRUE(bb->GetPredecessor(1)->IsTryEnd()); + + EXPECT_TRUE(bb->GetSuccessor(0)->IsCatch()); + continue; + } + + if (bb->IsCatch() && !bb->IsCatchBegin()) { + EXPECT_TRUE(bb->GetPredsBlocks().size() == 1); + + if (bb->IsTry()) { + EXPECT_TRUE(bb->GetTryId() == 2); + + EXPECT_TRUE(bb->GetSuccsBlocks().size() == 1); + EXPECT_TRUE(bb->GetSuccessor(0)->IsTryEnd()); + EXPECT_TRUE(bb->GetSuccessor(0)->IsCatch()); + + EXPECT_TRUE(bb->GetFirstInst()->IsSaveState()); + EXPECT_TRUE(bb->GetLastInst()->IsIntrinsic()); + EXPECT_TRUE((static_cast(bb->GetLastInst()))->GetIntrinsicId() == + RuntimeInterface::IntrinsicId::TRYLDGLOBALBYNAME_IMM8_ID16); + continue; + } + + if (bb->IsTryEnd()) { + EXPECT_TRUE(bb->GetPredecessor(0)->IsTry()); + EXPECT_TRUE(bb->GetPredecessor(0)->IsCatch()); + + EXPECT_TRUE(bb->GetSuccsBlocks().size() == 2); + for (auto inst : bb->GetSuccessor(0)->AllInsts()) { + EXPECT_TRUE(inst->IsPhi()); + } + EXPECT_TRUE(bb->GetSuccessor(1)->IsCatchBegin()); + EXPECT_TRUE(bb->GetSuccessor(1)->IsCatch()); + + EXPECT_TRUE(bb->IsEmpty()); + continue; + } + + EXPECT_TRUE(bb->GetPredecessor(0)->IsCatchBegin()); + EXPECT_TRUE(bb->GetPredecessor(0)->IsCatch()); + + EXPECT_TRUE(bb->GetSuccsBlocks().size() == 1); + auto *successor = bb->GetSuccessor(0); + EXPECT_TRUE(successor->GetFirstInst()->IsSaveState()); + EXPECT_TRUE(successor->GetLastInst()->IsIntrinsic()); + EXPECT_TRUE((static_cast(successor->GetLastInst()))->GetIntrinsicId() == + RuntimeInterface::IntrinsicId::RETURNUNDEFINED); + for (auto inst : successor->GetPredecessor(0)->AllInsts()) { + EXPECT_TRUE(inst->IsPhi()); + } + + EXPECT_TRUE(bb->GetFirstInst()->IsSaveState()); + EXPECT_TRUE(bb->GetLastInst()->IsIntrinsic()); + EXPECT_TRUE((static_cast(bb->GetLastInst()))->GetIntrinsicId() == + RuntimeInterface::IntrinsicId::CALLARG1_IMM8_V8); + } + } + + EXPECT_EQ(numOfTry, 2); + }); +} +} // namespace panda::compiler \ No newline at end of file diff --git a/compiler/tests/js/nestedTryCatch.js b/compiler/tests/js/nestedTryCatch.js new file mode 100644 index 0000000000..d35a390601 --- /dev/null +++ b/compiler/tests/js/nestedTryCatch.js @@ -0,0 +1,24 @@ +/* + 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; + } +} catch(e) { + print(e); +} diff --git a/compiler/tests/js/simpleTryCatch.js b/compiler/tests/js/simpleTryCatch.js new file mode 100644 index 0000000000..32e45865d9 --- /dev/null +++ b/compiler/tests/js/simpleTryCatch.js @@ -0,0 +1,20 @@ +/* + 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 { + a = 1; +} catch(e) { + a; +} \ No newline at end of file -- Gitee