diff --git a/compiler/optimizer/optimizations/memory_coalescing.cpp b/compiler/optimizer/optimizations/memory_coalescing.cpp index 4db777ec13b40ad59aedead509d326f5d91a055f..bf5b8630449dda0d6a849ee8e8ee3b1ce5a1e656 100644 --- a/compiler/optimizer/optimizations/memory_coalescing.cpp +++ b/compiler/optimizer/optimizations/memory_coalescing.cpp @@ -17,6 +17,7 @@ #include "optimizer/ir/graph_visitor.h" #include "optimizer/ir/basicblock.h" #include "optimizer/ir/inst.h" +#include "optimizer/ir/analysis.h" #include "optimizer/analysis/alias_analysis.h" #include "optimizer/analysis/rpo.h" #include "optimizer/analysis/loop_analyzer.h" @@ -228,24 +229,50 @@ public: static_cast(v)->HandleArrayAccessI(inst); } - void VisitDefault(Inst *inst) override + static bool IsNotAcceptableForStore(Inst *inst) { - if (inst->CanThrow()) { - candidates_.clear(); - return; + if (inst->GetOpcode() == Opcode::SaveState) { + for (auto &user : inst->GetUsers()) { + auto *ui = user.GetInst(); + if (ui->CanThrow() || ui->CanDeoptimize()) { + return true; + } + } } + return inst->GetOpcode() == Opcode::SaveStateDeoptimize; + } + + void VisitDefault(Inst *inst) override + { if (inst->IsMemory()) { candidates_.push_back(inst); return; } + if (inst->IsSaveState()) { + // 1. Load & Store can be moved through SafePoint + if (inst->GetOpcode() == Opcode::SafePoint) { + return; + } + // 2. Load & Store can't be moved through SaveStateOsr + if (inst->GetOpcode() == Opcode::SaveStateOsr) { + candidates_.clear(); + return; + } + // 3. Load can be moved through SaveState and SaveStateDeoptimize + // 4. Store can't be moved through SaveStateDeoptimize and SaveState with Users that are IsCheck or + // CanDeoptimize. It is checked in IsNotAcceptableForStore + if (IsNotAcceptableForStore(inst)) { + InvalidateStores(); + return; + } + } if (inst->IsBarrier()) { candidates_.clear(); return; } - // Do not move stores and ref loads over SaveState - // TODO(ekudriashov): re-consider logic after implementing SaveState input fixing - if (inst->IsSaveState()) { - InvalidateAllStoresAndRefLoads(); + if (inst->CanThrow()) { + InvalidateStores(); + return; } } @@ -261,10 +288,10 @@ public: #include "optimizer/ir/visitor.inc" private: - void InvalidateAllStoresAndRefLoads() + void InvalidateStores() { for (auto cand : candidates_) { - if (cand->IsStore() || (cand->IsLoad() && cand->GetType() == DataType::REFERENCE)) { + if (cand->IsStore()) { cand->SetMarker(mrk_invalid_); } } @@ -730,6 +757,14 @@ bool MemoryCoalescing::RunImpl() bb->RemoveInst(pair.second.second); }; + if (!collector.GetPairs().empty()) { + SaveStateBridgesBuilder ssb; + for (auto bb : GetGraph()->GetBlocksRPO()) { + if (!bb->IsEmpty() && !bb->IsStartBlock()) { + ssb.FixSaveStatesInBB(bb); + } + } + } COMPILER_LOG(DEBUG, MEMORY_COALESCING) << "Memory Coalescing complete"; return !collector.GetPairs().empty(); } diff --git a/compiler/tests/memory_coalescing_test.cpp b/compiler/tests/memory_coalescing_test.cpp index 7ad08d947582eac3f5f7e6ccb72bc9bdc73fd105..de15ec6469725223bdcd37ef60ae79cd3cc36433 100644 --- a/compiler/tests/memory_coalescing_test.cpp +++ b/compiler/tests/memory_coalescing_test.cpp @@ -910,10 +910,38 @@ TEST_F(MemoryCoalescingTest, CoalescingOverSaveState) INST(40, Opcode::Return).s64().Inputs(53); } } - auto initial = GraphCloner(GetGraph(), GetGraph()->GetAllocator(), GetGraph()->GetLocalAllocator()).CloneGraph(); - ASSERT_FALSE(GetGraph()->RunPass()); GraphChecker(GetGraph()).Check(); - ASSERT_TRUE(GraphComparator().Compare(GetGraph(), initial)); + ASSERT_TRUE(GetGraph()->RunPass()); + GraphChecker(GetGraph()).Check(); + + Graph *opt_graph = CreateEmptyGraph(); + GRAPH(opt_graph) + { + PARAMETER(0, 0).ref(); + + BASIC_BLOCK(2, -1) + { + INST(1, Opcode::SaveState).Inputs(0).SrcVregs({6}); + INST(2, Opcode::NullCheck).ref().Inputs(0, 1); + + INST(41, Opcode::SaveState).Inputs(0).SrcVregs({6}); + INST(43, Opcode::LenArray).s32().Inputs(2); + INST(241, Opcode::BoundsCheckI).s32().Inputs(43, 41).Imm(0x0); + + INST(246, Opcode::LoadArrayPairI).s64().Inputs(2).Imm(0x0); + INST(248, Opcode::LoadPairPart).s64().Inputs(246).Imm(0x0); + INST(247, Opcode::LoadPairPart).s64().Inputs(246).Imm(0x1); + + INST(47, Opcode::SaveState).Inputs(248, 0).SrcVregs({3, 6}); + INST(49, Opcode::LenArray).s32().Inputs(2); + INST(244, Opcode::BoundsCheckI).s32().Inputs(49, 47).Imm(0x1); + + INST(53, Opcode::Add).s64().Inputs(248, 247); + INST(40, Opcode::Return).s64().Inputs(53); + } + } + GraphChecker(opt_graph).Check(); + ASSERT_TRUE(GraphComparator().Compare(GetGraph(), opt_graph)); } TEST_F(MemoryCoalescingTest, AlignmentTest) @@ -1192,6 +1220,403 @@ TEST_F(MemoryCoalescingTest, NestedLoadCoalescing) } ASSERT_TRUE(GetGraph()->RunPass()); GraphChecker(GetGraph()).Check(); +} + +TEST_F(MemoryCoalescingTest, OnlySingleCoalescingOverSafePoint) +{ + // Coalescing is supported only for aarch64 + if (GetGraph()->GetArch() != Arch::AARCH64) { + return; + } + GRAPH(GetGraph()) + { + PARAMETER(0, 0).ref(); + BASIC_BLOCK(2, -1) + { + INST(26, Opcode::LoadArrayI).s64().Inputs(0).Imm(0x0); + INST(27, Opcode::SafePoint).NoVregs(); + INST(28, Opcode::LoadArrayI).s64().Inputs(0).Imm(0x1); + + INST(30, Opcode::LoadArrayI).s64().Inputs(0).Imm(0x1); + INST(21, Opcode::Add).s64().Inputs(28, 26); + INST(22, Opcode::Add).s64().Inputs(30, 21); + INST(23, Opcode::Return).s64().Inputs(22); + } + } + Graph *opt_graph = CreateEmptyGraph(); + GRAPH(opt_graph) + { + PARAMETER(0, 0).ref(); + BASIC_BLOCK(2, -1) + { + INST(31, Opcode::LoadArrayPairI).s64().Inputs(0).Imm(0x0); + INST(33, Opcode::LoadPairPart).s64().Inputs(31).Imm(0x0); + INST(32, Opcode::LoadPairPart).s64().Inputs(31).Imm(0x1); + INST(27, Opcode::SafePoint).Inputs(0).SrcVregs({VirtualRegister::BRIDGE}); + + INST(30, Opcode::LoadArrayI).s64().Inputs(0).Imm(0x1); + INST(21, Opcode::Add).s64().Inputs(32, 33); + INST(22, Opcode::Add).s64().Inputs(30, 21); + INST(23, Opcode::Return).s64().Inputs(22); + } + } + ASSERT_TRUE(GetGraph()->RunPass()); + GraphChecker(GetGraph()).Check(); + ASSERT_TRUE(GraphComparator().Compare(GetGraph(), opt_graph)); +} + +TEST_F(MemoryCoalescingTest, PseudoPartsOverSafePoints) +{ + // Coalescing is supported only for aarch64 + if (GetGraph()->GetArch() != Arch::AARCH64) { + return; + } + GRAPH(GetGraph()) + { + PARAMETER(0, 0).ref(); + BASIC_BLOCK(2, -1) + { + INST(1, Opcode::LoadArrayI).s64().Inputs(0).Imm(0x0); + INST(27, Opcode::SafePoint).NoVregs(); + INST(2, Opcode::LoadArrayI).s64().Inputs(0).Imm(0x1); + + INST(3, Opcode::StoreArrayI).s64().Inputs(0, 2).Imm(0x0); + INST(28, Opcode::SafePoint).NoVregs(); + INST(4, Opcode::StoreArrayI).s64().Inputs(0, 1).Imm(0x1); + INST(5, Opcode::ReturnVoid).v0id(); + } + } + Graph *opt_graph = CreateEmptyGraph(); + GRAPH(opt_graph) + { + PARAMETER(0, 0).ref(); + BASIC_BLOCK(2, -1) + { + INST(6, Opcode::LoadArrayPairI).s64().Inputs(0).Imm(0x0); + INST(7, Opcode::LoadPairPart).s64().Inputs(6).Imm(0x0); + INST(8, Opcode::LoadPairPart).s64().Inputs(6).Imm(0x1); + INST(27, Opcode::SafePoint).Inputs(0).SrcVregs({VirtualRegister::BRIDGE}); + + INST(9, Opcode::StoreArrayPairI).s64().Inputs(0, 8, 7).Imm(0x0); + INST(28, Opcode::SafePoint).NoVregs(); + INST(5, Opcode::ReturnVoid).v0id(); + } + } + ASSERT_TRUE(GetGraph()->RunPass()); + GraphChecker(GetGraph()).Check(); + ASSERT_TRUE(GraphComparator().Compare(GetGraph(), opt_graph)); +} + +TEST_F(MemoryCoalescingTest, OnlySingleCoalescingOverSaveState) +{ + // Coalescing is supported only for aarch64 + if (GetGraph()->GetArch() != Arch::AARCH64) { + return; + } + GRAPH(GetGraph()) + { + PARAMETER(0, 0).ref(); + BASIC_BLOCK(2, -1) + { + INST(26, Opcode::LoadArrayI).s64().Inputs(0).Imm(0x0); + INST(2, Opcode::SaveState).NoVregs(); + INST(28, Opcode::LoadArrayI).s64().Inputs(0).Imm(0x1); + + INST(30, Opcode::LoadArrayI).s64().Inputs(0).Imm(0x1); + INST(21, Opcode::Add).s64().Inputs(28, 26); + INST(22, Opcode::Add).s64().Inputs(30, 21); + INST(23, Opcode::Return).s64().Inputs(22); + } + } + Graph *opt_graph = CreateEmptyGraph(); + GRAPH(opt_graph) + { + PARAMETER(0, 0).ref(); + BASIC_BLOCK(2, -1) + { + INST(31, Opcode::LoadArrayPairI).s64().Inputs(0).Imm(0x0); + INST(33, Opcode::LoadPairPart).s64().Inputs(31).Imm(0x0); + INST(32, Opcode::LoadPairPart).s64().Inputs(31).Imm(0x1); + INST(2, Opcode::SaveState).Inputs(0).SrcVregs({VirtualRegister::BRIDGE}); + + INST(30, Opcode::LoadArrayI).s64().Inputs(0).Imm(0x1); + INST(21, Opcode::Add).s64().Inputs(32, 33); + INST(22, Opcode::Add).s64().Inputs(30, 21); + INST(23, Opcode::Return).s64().Inputs(22); + } + } + ASSERT_TRUE(GetGraph()->RunPass()); + GraphChecker(GetGraph()).Check(); + ASSERT_TRUE(GraphComparator().Compare(GetGraph(), opt_graph)); +} + +TEST_F(MemoryCoalescingTest, OverSaveState) +{ + // Coalescing is supported only for aarch64 + if (GetGraph()->GetArch() != Arch::AARCH64) { + return; + } + GRAPH(GetGraph()) + { + PARAMETER(0, 0).ref(); + BASIC_BLOCK(2, -1) + { + INST(1, Opcode::LoadArrayI).s64().Inputs(0).Imm(0x0); + INST(11, Opcode::SaveState).NoVregs(); + INST(2, Opcode::LoadArrayI).s64().Inputs(0).Imm(0x1); + + INST(3, Opcode::StoreArrayI).s64().Inputs(0, 2).Imm(0x0); + INST(12, Opcode::SaveState).NoVregs(); + INST(4, Opcode::StoreArrayI).s64().Inputs(0, 1).Imm(0x1); + INST(5, Opcode::ReturnVoid).v0id(); + } + } + Graph *opt_graph = CreateEmptyGraph(); + GRAPH(opt_graph) + { + PARAMETER(0, 0).ref(); + BASIC_BLOCK(2, -1) + { + INST(6, Opcode::LoadArrayPairI).s64().Inputs(0).Imm(0x0); + INST(7, Opcode::LoadPairPart).s64().Inputs(6).Imm(0x0); + INST(8, Opcode::LoadPairPart).s64().Inputs(6).Imm(0x1); + INST(27, Opcode::SaveState).Inputs(0).SrcVregs({VirtualRegister::BRIDGE}); + + INST(9, Opcode::StoreArrayPairI).s64().Inputs(0, 8, 7).Imm(0x0); + INST(28, Opcode::SaveState).NoVregs(); + INST(5, Opcode::ReturnVoid).v0id(); + } + } + ASSERT_TRUE(GetGraph()->RunPass()); + GraphChecker(GetGraph()).Check(); + ASSERT_TRUE(GraphComparator().Compare(GetGraph(), opt_graph)); +} + +TEST_F(MemoryCoalescingTest, OnlySingleCoalescingOverSaveStateDeoptimize) +{ + // Coalescing is supported only for aarch64 + if (GetGraph()->GetArch() != Arch::AARCH64) { + return; + } + GRAPH(GetGraph()) + { + PARAMETER(0, 0).ref(); + BASIC_BLOCK(2, -1) + { + INST(26, Opcode::LoadArrayI).s64().Inputs(0).Imm(0x0); + INST(2, Opcode::SaveStateDeoptimize).Inputs(0).SrcVregs({0}); + INST(28, Opcode::LoadArrayI).s64().Inputs(0).Imm(0x1); + + INST(30, Opcode::LoadArrayI).s64().Inputs(0).Imm(0x1); + INST(21, Opcode::Add).s64().Inputs(28, 26); + INST(22, Opcode::Add).s64().Inputs(30, 21); + INST(23, Opcode::Return).s64().Inputs(22); + } + } + Graph *opt_graph = CreateEmptyGraph(); + GRAPH(opt_graph) + { + PARAMETER(0, 0).ref(); + BASIC_BLOCK(2, -1) + { + INST(31, Opcode::LoadArrayPairI).s64().Inputs(0).Imm(0x0); + INST(33, Opcode::LoadPairPart).s64().Inputs(31).Imm(0x0); + INST(32, Opcode::LoadPairPart).s64().Inputs(31).Imm(0x1); + INST(2, Opcode::SaveStateDeoptimize).Inputs(0).SrcVregs({0}); + + INST(30, Opcode::LoadArrayI).s64().Inputs(0).Imm(0x1); + INST(21, Opcode::Add).s64().Inputs(32, 33); + INST(22, Opcode::Add).s64().Inputs(30, 21); + INST(23, Opcode::Return).s64().Inputs(22); + } + } + ASSERT_TRUE(GetGraph()->RunPass()); + GraphChecker(GetGraph()).Check(); + ASSERT_TRUE(GraphComparator().Compare(GetGraph(), opt_graph)); +} + +TEST_F(MemoryCoalescingTest, OverSaveStateDeoptimize) +{ + // Coalescing is supported only for aarch64 + if (GetGraph()->GetArch() != Arch::AARCH64) { + return; + } + GRAPH(GetGraph()) + { + PARAMETER(0, 0).ref(); + BASIC_BLOCK(2, -1) + { + INST(1, Opcode::LoadArrayI).s64().Inputs(0).Imm(0x0); + INST(11, Opcode::SaveStateDeoptimize).Inputs(0).SrcVregs({0}); + INST(2, Opcode::LoadArrayI).s64().Inputs(0).Imm(0x1); + + INST(3, Opcode::StoreArrayI).s64().Inputs(0, 2).Imm(0x0); + INST(12, Opcode::SaveStateDeoptimize).Inputs(0).SrcVregs({0}); + INST(4, Opcode::StoreArrayI).s64().Inputs(0, 1).Imm(0x1); + INST(5, Opcode::ReturnVoid).v0id(); + } + } + ASSERT_TRUE(GetGraph()->RunPass()); + GraphChecker(GetGraph()).Check(); + + Graph *opt_graph = CreateEmptyGraph(); + GRAPH(opt_graph) + { + PARAMETER(0, 0).ref(); + BASIC_BLOCK(2, -1) + { + INST(6, Opcode::LoadArrayPairI).s64().Inputs(0).Imm(0x0); + INST(7, Opcode::LoadPairPart).s64().Inputs(6).Imm(0x0); + INST(8, Opcode::LoadPairPart).s64().Inputs(6).Imm(0x1); + INST(27, Opcode::SaveStateDeoptimize).Inputs(0).SrcVregs({0}); + + INST(3, Opcode::StoreArrayI).s64().Inputs(0, 8).Imm(0x0); + INST(12, Opcode::SaveStateDeoptimize).Inputs(0).SrcVregs({0}); + INST(4, Opcode::StoreArrayI).s64().Inputs(0, 7).Imm(0x1); + INST(5, Opcode::ReturnVoid).v0id(); + } + } + ASSERT_TRUE(GraphComparator().Compare(GetGraph(), opt_graph)); +} + +TEST_F(MemoryCoalescingTest, OnlySingleCoalescingOverSaveStateWithCheckUser) +{ + // Coalescing is supported only for aarch64 + if (GetGraph()->GetArch() != Arch::AARCH64) { + return; + } + GRAPH(GetGraph()) + { + PARAMETER(0, 0).ref(); + BASIC_BLOCK(2, -1) + { + INST(26, Opcode::LoadArrayI).s64().Inputs(0).Imm(0x0); + INST(2, Opcode::SaveState).Inputs(0).SrcVregs({0}); + INST(28, Opcode::LoadArrayI).s64().Inputs(0).Imm(0x1); + + INST(30, Opcode::LoadArrayI).s64().Inputs(0).Imm(0x1); + INST(21, Opcode::Add).s64().Inputs(28, 26); + INST(22, Opcode::Add).s64().Inputs(30, 21); + INST(3, Opcode::NullCheck).ref().Inputs(0, 2); + INST(23, Opcode::Return).s64().Inputs(22); + } + } + Graph *opt_graph = CreateEmptyGraph(); + GRAPH(opt_graph) + { + PARAMETER(0, 0).ref(); + BASIC_BLOCK(2, -1) + { + INST(31, Opcode::LoadArrayPairI).s64().Inputs(0).Imm(0x0); + INST(33, Opcode::LoadPairPart).s64().Inputs(31).Imm(0x0); + INST(32, Opcode::LoadPairPart).s64().Inputs(31).Imm(0x1); + INST(2, Opcode::SaveState).Inputs(0).SrcVregs({0}); + + INST(30, Opcode::LoadArrayI).s64().Inputs(0).Imm(0x1); + INST(21, Opcode::Add).s64().Inputs(32, 33); + INST(22, Opcode::Add).s64().Inputs(30, 21); + INST(3, Opcode::NullCheck).ref().Inputs(0, 2); + INST(23, Opcode::Return).s64().Inputs(22); + } + } + ASSERT_TRUE(GetGraph()->RunPass()); + GraphChecker(GetGraph()).Check(); + ASSERT_TRUE(GraphComparator().Compare(GetGraph(), opt_graph)); +} + +TEST_F(MemoryCoalescingTest, OverSaveStateWithCheckUser) +{ + // Coalescing is supported only for aarch64 + if (GetGraph()->GetArch() != Arch::AARCH64) { + return; + } + GRAPH(GetGraph()) + { + PARAMETER(0, 0).ref(); + BASIC_BLOCK(2, -1) + { + INST(1, Opcode::LoadArrayI).s64().Inputs(0).Imm(0x0); + INST(11, Opcode::SaveState).Inputs(0).SrcVregs({0}); + INST(2, Opcode::LoadArrayI).s64().Inputs(0).Imm(0x1); + + INST(3, Opcode::StoreArrayI).s64().Inputs(0, 2).Imm(0x0); + INST(12, Opcode::SaveState).Inputs(0).SrcVregs({0}); + INST(4, Opcode::StoreArrayI).s64().Inputs(0, 1).Imm(0x1); + INST(13, Opcode::NullCheck).ref().Inputs(0, 11); + INST(14, Opcode::NullCheck).ref().Inputs(0, 12); + INST(5, Opcode::ReturnVoid).v0id(); + } + } + ASSERT_TRUE(GetGraph()->RunPass()); + GraphChecker(GetGraph()).Check(); + + Graph *opt_graph = CreateEmptyGraph(); + GRAPH(opt_graph) + { + PARAMETER(0, 0).ref(); + BASIC_BLOCK(2, -1) + { + INST(6, Opcode::LoadArrayPairI).s64().Inputs(0).Imm(0x0); + INST(7, Opcode::LoadPairPart).s64().Inputs(6).Imm(0x0); + INST(8, Opcode::LoadPairPart).s64().Inputs(6).Imm(0x1); + INST(11, Opcode::SaveState).Inputs(0).SrcVregs({0}); + + INST(3, Opcode::StoreArrayI).s64().Inputs(0, 8).Imm(0x0); + INST(12, Opcode::SaveState).Inputs(0).SrcVregs({0}); + INST(4, Opcode::StoreArrayI).s64().Inputs(0, 7).Imm(0x1); + INST(13, Opcode::NullCheck).ref().Inputs(0, 11); + INST(14, Opcode::NullCheck).ref().Inputs(0, 12); + INST(5, Opcode::ReturnVoid).v0id(); + } + } + ASSERT_TRUE(GraphComparator().Compare(GetGraph(), opt_graph)); +} + +TEST_F(MemoryCoalescingTest, OverSaveStateWithDeoptimizeUser) +{ + // Coalescing is supported only for aarch64 + if (GetGraph()->GetArch() != Arch::AARCH64) { + return; + } + GRAPH(GetGraph()) + { + PARAMETER(0, 0).ref(); + BASIC_BLOCK(2, -1) + { + INST(1, Opcode::LoadArrayI).s64().Inputs(0).Imm(0x0); + INST(11, Opcode::SaveState).Inputs(0).SrcVregs({0}); + INST(2, Opcode::LoadArrayI).s64().Inputs(0).Imm(0x1); + + INST(3, Opcode::StoreArrayI).s64().Inputs(0, 2).Imm(0x0); + INST(12, Opcode::SaveState).Inputs(0).SrcVregs({0}); + INST(4, Opcode::StoreArrayI).s64().Inputs(0, 1).Imm(0x1); + INST(13, Opcode::Deoptimize).DeoptimizeType(DeoptimizeType::NEGATIVE_CHECK).Inputs(11); + INST(14, Opcode::Deoptimize).DeoptimizeType(DeoptimizeType::NEGATIVE_CHECK).Inputs(12); + INST(5, Opcode::ReturnVoid).v0id(); + } + } + ASSERT_TRUE(GetGraph()->RunPass()); + GraphChecker(GetGraph()).Check(); + + Graph *opt_graph = CreateEmptyGraph(); + GRAPH(opt_graph) + { + PARAMETER(0, 0).ref(); + BASIC_BLOCK(2, -1) + { + INST(6, Opcode::LoadArrayPairI).s64().Inputs(0).Imm(0x0); + INST(7, Opcode::LoadPairPart).s64().Inputs(6).Imm(0x0); + INST(8, Opcode::LoadPairPart).s64().Inputs(6).Imm(0x1); + INST(11, Opcode::SaveState).Inputs(0).SrcVregs({0}); + + INST(3, Opcode::StoreArrayI).s64().Inputs(0, 8).Imm(0x0); + INST(12, Opcode::SaveState).Inputs(0).SrcVregs({0}); + INST(4, Opcode::StoreArrayI).s64().Inputs(0, 7).Imm(0x1); + INST(13, Opcode::Deoptimize).DeoptimizeType(DeoptimizeType::NEGATIVE_CHECK).Inputs(11); + INST(14, Opcode::Deoptimize).DeoptimizeType(DeoptimizeType::NEGATIVE_CHECK).Inputs(12); + INST(5, Opcode::ReturnVoid).v0id(); + } + } ASSERT_TRUE(GraphComparator().Compare(GetGraph(), opt_graph)); } // NOLINTEND(readability-magic-numbers) diff --git a/plugins/ets/tests/checked/CMakeLists.txt b/plugins/ets/tests/checked/CMakeLists.txt index 5e7068cd98a69387592fe99e037de9c2ca676371..f5469f45f6b03dc450a329650ea0d244f11b69f9 100644 --- a/plugins/ets/tests/checked/CMakeLists.txt +++ b/plugins/ets/tests/checked/CMakeLists.txt @@ -122,6 +122,9 @@ if (PANDA_TARGET_AMD64 OR NOT PANDA_ARM64_TESTS_WITH_SANITIZER) # AOT mode for arm32 is not supported panda_add_checked_test_ets(FILE ${CMAKE_CURRENT_SOURCE_DIR}/ets_string_equals.ets SUPPORT_RELEASE true) endif() + if (PANDA_TARGET_ARM64) + panda_add_checked_test_ets(FILE ${CMAKE_CURRENT_SOURCE_DIR}/memory_coalescing.ets) + endif() panda_add_checked_test_ets(FILE ${CMAKE_CURRENT_SOURCE_DIR}/ets_runtime_equals.ets SUPPORT_RELEASE true) panda_add_checked_test_ets(FILE ${CMAKE_CURRENT_SOURCE_DIR}/ets_abs.ets) panda_add_checked_test_ets(FILE ${CMAKE_CURRENT_SOURCE_DIR}/ets_min.ets) diff --git a/plugins/ets/tests/checked/memory_coalescing.ets b/plugins/ets/tests/checked/memory_coalescing.ets new file mode 100644 index 0000000000000000000000000000000000000000..dfa0918538b2bbc8274bd6c945249976627d0d45 --- /dev/null +++ b/plugins/ets/tests/checked/memory_coalescing.ets @@ -0,0 +1,181 @@ +/* + * 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. + */ + +//! CHECKER Memory Coalescing JIT +//! SKIP_IF @architecture != "arm64" +//! RUN force_jit: true, options: "--compiler-loop-unroll=false", entry: "ETSGLOBAL::main" +//! EVENT /Compilation,ETSGLOBAL::main,.*,COMPILED/ +//! EVENT /Compilation,Test::__noinline__fnSum,.*,COMPILED/ +//! EVENT /Compilation,Test::__noinline__fnSumMem,.*,COMPILED/ +//! EVENT /Compilation,Test::__noinline__fnSumGC,.*,COMPILED/ +//! METHOD "Test::__noinline__fnSum" +//! PASS_BEFORE "MemoryCoalescing" +//! INST_NOT "LoadArrayPair" +//! INST_NOT "LoadPairPart" +//! INST_NOT "StoreArrayPair" +//! PASS_AFTER "MemoryCoalescing" +//! INST "LoadArrayPair" +//! INST "LoadPairPart" +//! INST "StoreArrayPair" +//! METHOD "Test::__noinline__fnSumMem" +//! PASS_BEFORE "MemoryCoalescing" +//! INST_NOT "LoadArrayPair" +//! INST_NOT "LoadPairPart" +//! INST_NOT "StoreArrayPair" +//! PASS_AFTER "MemoryCoalescing" +//! INST_NOT "LoadArrayPair" +//! INST_NOT "LoadPairPart" +//! INST_NOT "StoreArrayPair" +//! METHOD "Test::__noinline__fnSumGC" +//! PASS_BEFORE "MemoryCoalescing" +//! INST_NOT "LoadArrayPair" +//! INST_NOT "LoadPairPart" +//! INST_NOT "StoreArrayPair" +//! PASS_AFTER "MemoryCoalescing" +//! INST_NOT "LoadArrayPair" +//! INST_NOT "LoadPairPart" +//! INST_NOT "StoreArrayPair" +//! METHOD "Test::__noinline__fnSumCls" +//! PASS_BEFORE "MemoryCoalescing" +//! INST_NOT "LoadArrayPair" +//! INST_NOT "LoadPairPart" +//! INST_NOT "StoreArrayPair" +//! PASS_AFTER "MemoryCoalescing" +//! INST "LoadArrayPair" +//! INST "LoadPairPart" +//! INST_NOT "StoreArrayPair" + + +class A { + public f : int; + public d : int; +} + +export class Test { + + private static __noinline__fnSum(u: double[], v: double[]): void { + let t: double = 0; + for (let j: int = 0; j < u.length; j = j + 2) { + t += u[j]; + t += u[j + 1]; + } + for (let i: int = 0; i < v.length; i = i + 2) { + v[i] = t; + v[i + 1] = t + 1; + } + } + + private static __noinline__fnMem(u: double[], v: double[], i: int): void { + u[i] = v[i]; + } + + // memory coalescing can't be performed over CallStatic that is barrier + private static __noinline__fnSumMem(u: double[], v: double[]): void { + let t: double = 0; + for (let j: int = 0; j < u.length; j = j + 2) { + t += u[j]; + Test.__noinline__fnMem(u, v, j); + t += u[j + 1]; + } + for (let i: int = 0; i < v.length; i = i + 2) { + v[i] = t; + Test.__noinline__fnMem(u, v, i); + v[i + 1] = t + 1; + } + } + + // memory coalescing can't be performed over GC call that is barrier + private static __noinline__fnSumGC(u: double[], v: double[]): void { + try { + let t: double = 0; + for (let j: int = 0; j < u.length; j = j + 2) { + t += u[j]; + GC.startGC(GC.FULL_CAUSE); + t += u[j + 1]; + } + for (let i: int = 0; i < v.length; i = i + 2) { + v[i] = t; + GC.startGC(GC.FULL_CAUSE); + v[i + 1] = t + 1; + } + } catch (exception) { + } + } + + // memory coalescing over NullCheck can be performed for Load and can't for Store + private static __noinline__fnSumCls(u: double[], v: double[], o: A, d: int): void { + let t: double = 0; + for (let j: int = 0; j < u.length; j = j + 2) { + t += u[j]; + o.f += v.length/(j + d); + t += u[j + 1]; + } + for (let i: int = 0; i < v.length; i = i + 2) { + v[i] = t; + o.f += v.length/(i + d); + v[i + 1] = t + 1; + } + } + + static __noinline__test(n: int): void { + let i: int; + let u: double[] = new double[n]; + let v: double[] = new double[n]; + let vv1: double = 0; + let vv2: double = 0; + let vv3: double = 0; + let vv4: double = 0; + for (i = 0; i < n; i++) { + u[i] = i; v[i] = 0; + } + Test.__noinline__fnSum(u, v); + for (i = 0; i < n; i++) { + vv1 += v[i]; + } + for (i = 0; i < n; i++) { + u[i] = i; v[i] = 0; + } + Test.__noinline__fnSumGC(u, v); + for (i = 0; i < n; i++) { + vv2 += v[i]; + } + for (i = 0; i < n; i++) { + u[i] = i; v[i] = 0; + } + Test.__noinline__fnSumMem(u, v); + for (i = 0; i < n; i++) { + vv3 += v[i]; + } + let a = new A(); + a.d = 100; + for (i = 0; i < n; i++) { + u[i] = i; v[i] = 0; + } + Test.__noinline__fnSumCls(u, v, a, a.d); + for (i = 0; i < n; i++) { + vv4 += v[i]; + } + assert vv1 == vv2: "ERROR: Test incorrect result vv1 != vv2"; + assert vv2 == vv3: "ERROR: Test incorrect result vv2 != vv3"; + assert vv3 == vv4: "ERROR: Test incorrect result vv3 != vv4"; + } +} + +let s0 = new Test(); + +function main(): void { + const n: int = 20; + s0.__noinline__test(n); +}