From 6bb9dcd732b763e40fcee6ece64e020132b1c78a Mon Sep 17 00:00:00 2001 From: Mikhail Kaskov Date: Thu, 17 Aug 2023 20:11:58 +0300 Subject: [PATCH] Supported bool negation peepholes Signed-off-by: Mikhail Kaskov --- compiler/optimizer/ir/ir_constructor.h | 5 + compiler/optimizer/optimizations/lowering.cpp | 52 ++ compiler/optimizer/optimizations/lowering.h | 1 + .../optimizer/optimizations/peepholes.cpp | 196 ++++++- compiler/optimizer/optimizations/peepholes.h | 6 + compiler/tests/lowering_test.cpp | 46 ++ compiler/tests/peepholes_test.cpp | 498 +++++++++++++++++- plugins/ets/tests/checked/CMakeLists.txt | 1 + .../ets/tests/checked/optimize_negation.ets | 55 ++ tests/checked/CMakeLists.txt | 1 + tests/checked/xor_to_compare_to_xor.pa | 46 ++ 11 files changed, 903 insertions(+), 4 deletions(-) create mode 100644 plugins/ets/tests/checked/optimize_negation.ets create mode 100644 tests/checked/xor_to_compare_to_xor.pa diff --git a/compiler/optimizer/ir/ir_constructor.h b/compiler/optimizer/ir/ir_constructor.h index a0c1c32f1..63f56dc63 100644 --- a/compiler/optimizer/ir/ir_constructor.h +++ b/compiler/optimizer/ir/ir_constructor.h @@ -1169,6 +1169,11 @@ public: UNREACHABLE(); } } else { + // Disable check for Negation (Neg+Add), we can't add deeper verification, + // because the graph has not been built yet + if (input_inst->GetOpcode() == Opcode::Add) { + return; + } CHECK_EQ(type, prev_type); } } diff --git a/compiler/optimizer/optimizations/lowering.cpp b/compiler/optimizer/optimizations/lowering.cpp index afe54ca8d..ce90f4ca0 100644 --- a/compiler/optimizer/optimizations/lowering.cpp +++ b/compiler/optimizer/optimizations/lowering.cpp @@ -399,6 +399,58 @@ void Lowering::VisitLoadFromConstantPool([[maybe_unused]] GraphVisitor *v, Inst COMPILER_LOG(DEBUG, LOWERING) << "Lowering is applied for " << GetOpcodeString(inst->GetOpcode()); } +// Replacing Compare EQ with Xor +// 1.i64 Const 0 +// 2.b ... +// 3.b Compare EQ b v2, v1 +// ===> +// 1.i64 Const 1 +// 2.b ... +// 3.i32 Xor v1, v2 +void Lowering::VisitCompare(GraphVisitor *v, Inst *inst) +{ + auto input0 = inst->GetInput(0).GetInst(); + auto input1 = inst->GetInput(1).GetInst(); + + if (inst->CastToCompare()->GetCc() != ConditionCode::CC_EQ) { + return; + } + + // Compare EQ b 0x0, v2 + if (input1->GetType() == DataType::BOOL && input0->IsConst() && input0->CastToConstant()->GetIntValue() == 0U) { + std::swap(input0, input1); + } + + // Compare EQ b v2, 0x0 + bool is_applicable = + input0->GetType() == DataType::BOOL && input1->IsConst() && input1->CastToConstant()->GetIntValue() == 0U; + if (!is_applicable) { + return; + } + // Always there are more than one user of Compare, because previous pass is Cleanup + bool only_ifimm = true; + for (auto &user : inst->GetUsers()) { + if (user.GetInst()->GetOpcode() != Opcode::IfImm) { + only_ifimm = false; + break; + } + } + // Skip optimization, if all users is IfImm, optimization Compare+IfImm will be better + if (only_ifimm) { + return; + } + auto graph = inst->GetBasicBlock()->GetGraph(); + auto cnst = graph->FindOrCreateConstant(1); + auto xor_inst = graph->CreateInstXor(); + xor_inst->SetPc(inst->GetPc()); + xor_inst->SetType(DataType::BOOL); + xor_inst->SetInput(0, input0); + xor_inst->SetInput(1, cnst); + + InsertInstruction(inst, xor_inst); + static_cast(v)->VisitXor(v, xor_inst); +} + template void Lowering::SetInputsAndInsertInstruction(OperandsCapture &operands, Inst *inst, Inst *new_inst) { diff --git a/compiler/optimizer/optimizations/lowering.h b/compiler/optimizer/optimizations/lowering.h index a90ca0e38..532a05354 100644 --- a/compiler/optimizer/optimizations/lowering.h +++ b/compiler/optimizer/optimizations/lowering.h @@ -347,6 +347,7 @@ private: static void VisitNeg([[maybe_unused]] GraphVisitor *v, Inst *inst); static void VisitDeoptimizeIf(GraphVisitor *v, Inst *inst); static void VisitLoadFromConstantPool(GraphVisitor *v, Inst *inst); + static void VisitCompare(GraphVisitor *v, Inst *inst); #include "optimizer/ir/visitor.inc" diff --git a/compiler/optimizer/optimizations/peepholes.cpp b/compiler/optimizer/optimizations/peepholes.cpp index 5a48c4bf9..6cd7b489d 100644 --- a/compiler/optimizer/optimizations/peepholes.cpp +++ b/compiler/optimizer/optimizations/peepholes.cpp @@ -123,18 +123,21 @@ void Peepholes::VisitNot([[maybe_unused]] GraphVisitor *v, Inst *inst) * 2. add const, v1 -> {...} * ===> * 2. add v1, const -> {...} + * * Case 2: Addition with zero * 1. Some inst -> v2 * 2. add v1, 0 -> {...} * ===> * 1. Some inst ->{...} * 2. add v1, 0 -> {} + * * Case 3: Repeated arithmetic with constants * 2. add/sub v1, const1 -> {v3, ...} * 3. add v2, const2 -> {...} * ===> * 2. add/sub v1, const1 -> {...} * 3. add v1, const2 +/- const1 ->{...} + * * Case 4: Addition with Neg * 3. neg v1 -> {5} * 4. neg v2 -> {5} @@ -144,12 +147,14 @@ void Peepholes::VisitNot([[maybe_unused]] GraphVisitor *v, Inst *inst) * 4. neg v2 -> {} * 5. add v1, v2 -> {4} * 6. neg v5 -> {users v5} + * * Case 5: * 3. neg v2 -> {4, ...} * 4. add v1, v3 -> {...} * ===> * 3. neg v2 -> {...} * 4. sub v1, v2 -> {...} + * * Case 6: * Adding and subtracting the same value * 3. sub v2, v1 -> {4, ...} @@ -158,6 +163,46 @@ void Peepholes::VisitNot([[maybe_unused]] GraphVisitor *v, Inst *inst) * 3. sub v2, v1 -> {4, ...} * 4. add v3, v1 -> {} * v2 -> {users v4} + * + * Case 7: + * Replacing Negation pattern (neg + add) with Compare + * 1.i64 Constant 0x1 -> {4, ...} + * 2.b ... -> {3} + * 3.i32 Neg v2 -> {4, ...} + * 4.i32 Add v3, v1 -> {...} + * ===> + * 1.i64 Constant 0x1 -> {...} + * 5.i64 Constant 0x0 -> {v6, ...} + * 2.b ... -> {v3, v6} + * 3.i32 Neg v2 -> {...} + * 4.i32 Add v3, v1 + * 6.b Compare EQ b v2, v5 -> {USERS v4} + * + * Case 8: + * Delete negation of Compare + * 1.i64 Constant 0x1 -> {v4} + * 2.b Compare GT ... -> {v3} + * 3.i32 Neg v2 -> {v4} + * 4.i32 Add v3, v1 -> {...} + * ===> + * 1.i64 Constant 0x1 -> {v4} + * 2.b Compare LE ... -> {v3, USERS v4} + * 3.i32 Neg v2 -> {v4} + * 4.i32 Add v3, v1 + * + * Case 9 + * One of the inputs of Compare is Negation pattern + * 1.i64 Constant 0x1 -> {v4, ...} + * 2.b ... -> {v3} + * 3.i32 Neg v2 -> {v4, ...} + * 4.i32 Add v3, v1 -> {v5, ...} + * 5.b Compare EQ/NE b v4, ... + * =======> + * 1.i64 Constant 0x1 -> {v4, ...} + * 2.b ... -> {v3, v5} + * 3.i32 Neg v2 -> {v4, ...} + * 4.i32 Add v3, v1 -> {...} + * 5.b Compare NE/EQ b v2, ... */ void Peepholes::VisitAdd([[maybe_unused]] GraphVisitor *v, Inst *inst) { @@ -190,6 +235,10 @@ void Peepholes::VisitAdd([[maybe_unused]] GraphVisitor *v, Inst *inst) return; } } else { + // Case 7, 8, 9 + if (visitor->TrySimplifyNegationPattern(inst)) { + return; + } // Case 5 visitor->TrySimplifyNeg(inst); } @@ -806,6 +855,7 @@ void Peepholes::VisitXor([[maybe_unused]] GraphVisitor *v, Inst *inst) PEEPHOLE_IS_APPLIED(static_cast(v), inst); return; } + auto visitor = static_cast(v); // Swap inputs if the first is a constant // 2. Xor const, v1 -> {...} // ===> @@ -826,7 +876,7 @@ void Peepholes::VisitXor([[maybe_unused]] GraphVisitor *v, Inst *inst) return; } CreateAndInsertInst(Opcode::Not, inst, input0); - PEEPHOLE_IS_APPLIED(static_cast(v), inst); + PEEPHOLE_IS_APPLIED(visitor, inst); } else if (input1->CastToConstant()->GetIntValue() == 0) { // Result A xor 0 equal A: // 0.i64 Const 0 @@ -842,7 +892,22 @@ void Peepholes::VisitXor([[maybe_unused]] GraphVisitor *v, Inst *inst) return; } inst->ReplaceUsers(input0); - PEEPHOLE_IS_APPLIED(static_cast(v), inst); + PEEPHOLE_IS_APPLIED(visitor, inst); + } else if (val == 1 && input0->GetType() == DataType::BOOL) { + // Replacing Xor with Compare for more case optimizations + // If this compare is not optimized during pipeline, we will revert it back to xor in Lowering + // 1.i64 Const 1 + // 2.b ... + // 3.i32 Xor v1, v2 + // ===> + // 1.i64 Const 0 + // 2.b ... + // 3.b Compare EQ b v2, v1 + if (SkipThisPeepholeInOSR(inst, input0)) { + return; + } + CreateCompareInsteadOfXorAdd(inst); + PEEPHOLE_IS_APPLIED(visitor, inst); } } } @@ -984,6 +1049,7 @@ bool Peepholes::TrySimplifyCompareAnyType(Inst *inst) return true; } +// This VisitIf is using only for compile IRTOC void Peepholes::VisitIf([[maybe_unused]] GraphVisitor *v, Inst *inst) { // 2. Constant 0x0 @@ -2029,6 +2095,8 @@ void Peepholes::TrySimplifyNeg(Inst *inst) (input1->GetOpcode() == Opcode::Neg && SkipThisPeepholeInOSR(inst, input1->GetInput(0).GetInst()))) { return; } + + // Case 5 if (input0->GetOpcode() == Opcode::Neg && !input1->IsConst()) { inst->SetInput(0, input1); inst->SetInput(1, input0); @@ -2042,6 +2110,37 @@ void Peepholes::TrySimplifyNeg(Inst *inst) } } +bool Peepholes::TrySimplifyCompareNegation(Inst *inst) +{ + ASSERT(inst != nullptr); + ASSERT(inst->GetOpcode() == Opcode::Add); + + // Case 9: Neg -> Add -> Compare + bool optimized = false; + for (auto &user_add : inst->GetUsers()) { + auto suspect_inst = user_add.GetInst(); + if (suspect_inst->GetOpcode() != Opcode::Compare) { + continue; + } + auto compare_inst = suspect_inst->CastToCompare(); + if (compare_inst->GetOperandsType() != DataType::BOOL || + (compare_inst->GetCc() != ConditionCode::CC_EQ && compare_inst->GetCc() != ConditionCode::CC_NE)) { + continue; + } + + unsigned index_cast = compare_inst->GetInput(0).GetInst() == inst ? 0 : 1; + auto bool_value = inst->GetInput(0).GetInst()->GetInput(0).GetInst(); + if (SkipThisPeepholeInOSR(inst, bool_value)) { + continue; + } + compare_inst->SetInput(index_cast, bool_value); + compare_inst->SetCc(GetInverseConditionCode(compare_inst->GetCc())); + PEEPHOLE_IS_APPLIED(this, compare_inst); + optimized = true; + } + return optimized; +} + void Peepholes::TryReplaceDivByShift(Inst *inst) { auto bb = inst->GetBasicBlock(); @@ -2638,6 +2737,41 @@ void Peepholes::VisitLoadFromConstantPool(GraphVisitor *v, Inst *inst) PEEPHOLE_IS_APPLIED(static_cast(v), inst); } +bool Peepholes::CreateCompareInsteadOfXorAdd(Inst *old_inst) +{ + ASSERT(old_inst->GetOpcode() == Opcode::Xor || old_inst->GetOpcode() == Opcode::Add); + auto input0 = old_inst->GetInput(0).GetInst(); + [[maybe_unused]] auto input1 = old_inst->GetInput(1).GetInst(); + + if (old_inst->GetOpcode() == Opcode::Add) { + ASSERT(input0->GetOpcode() == Opcode::Neg); + input0 = input0->GetInput(0).GetInst(); + for (auto &user_add : old_inst->GetUsers()) { + if (SkipThisPeepholeInOSR(user_add.GetInst(), input0)) { + return false; + } + } + } + + // We shouldn't check on OSR with Xor, because old_inst and cmp_inst is placed one by one + ASSERT(input0->GetType() == DataType::BOOL && input1->IsConst() && input1->CastToConstant()->GetIntValue() == 1U); + auto cnst = old_inst->GetBasicBlock()->GetGraph()->FindOrCreateConstant(0); + auto cmp_inst = CreateAndInsertInst(Opcode::Compare, old_inst, input0, cnst); + cmp_inst->SetType(DataType::BOOL); + cmp_inst->CastToCompare()->SetCc(ConditionCode::CC_EQ); + cmp_inst->CastToCompare()->SetOperandsType(DataType::BOOL); + auto type = old_inst->GetType(); + if (type == DataType::UINT64 || type == DataType::INT64) { + auto cast = cmp_inst->GetBasicBlock()->GetGraph()->CreateInstCast(); + cast->SetType(type); + cmp_inst->InsertAfter(cast); + cmp_inst->ReplaceUsers(cast); + cast->SetInput(0, cmp_inst); + cast->SetOperandsType(DataType::BOOL); + } + return true; +} + // Move users from LenArray to constant which used in MultiArray // Case 1 // 1.s64 ... ->{v3} @@ -2697,4 +2831,62 @@ bool Peepholes::OptimizeLenArrayForMultiArray(Inst *len_array, Inst *inst, size_ return false; } +bool Peepholes::IsNegationPattern(Inst *inst) +{ + ASSERT(inst->GetOpcode() == Opcode::Add); + // Negetion pattern is: + // 1. Constant 0x1 + // 2.b ... + // 3.i32 Neg v2 -> v4 + // 4.i32 Add v3, v1 + auto input0 = inst->GetInput(0).GetInst(); + if (input0->GetOpcode() != Opcode::Neg || input0->GetInput(0).GetInst()->GetType() != DataType::BOOL || + !input0->HasSingleUser()) { + return false; + } + // We sure, that constant may be only in input1 + auto input1 = inst->GetInput(1).GetInst(); + return input1->GetOpcode() == Opcode::Constant && input1->CastToConstant()->GetIntValue() == 1; +} + +bool Peepholes::TrySimplifyNegationPattern(Inst *inst) +{ + ASSERT(inst->GetOpcode() == Opcode::Add); + if (!IsNegationPattern(inst)) { + return false; + } + auto suspect_inst = inst->GetInput(0).GetInst()->GetInput(0).GetInst(); + // Case 8 + // We sure, that type of Neg's input is Bool. We shue, that Neg has one user + if (suspect_inst->GetOpcode() == Opcode::Compare && suspect_inst->HasSingleUser()) { + auto compare_inst = suspect_inst->CastToCompare(); + bool is_possible = true; + for (auto &i : inst->GetUsers()) { + if (SkipThisPeepholeInOSR(i.GetInst(), compare_inst)) { + is_possible = false; + break; + } + } + if (is_possible) { + inst->ReplaceUsers(compare_inst); + compare_inst->SetCc(GetInverseConditionCode(compare_inst->GetCc())); + PEEPHOLE_IS_APPLIED(this, compare_inst); + return true; + } + } + + // Case 9 + if (TrySimplifyCompareNegation(inst)) { + return true; + } + + // Case 7 + // This is used last of all if no case has worked! + if (CreateCompareInsteadOfXorAdd(inst)) { + PEEPHOLE_IS_APPLIED(this, inst); + return true; + } + return false; +} + } // namespace panda::compiler diff --git a/compiler/optimizer/optimizations/peepholes.h b/compiler/optimizer/optimizations/peepholes.h index f71ab1302..8b735231b 100644 --- a/compiler/optimizer/optimizations/peepholes.h +++ b/compiler/optimizer/optimizations/peepholes.h @@ -163,6 +163,8 @@ private: static bool GetInputsOfCompareWithConst(const Inst *inst, Inst **input, ConstantInst **const_input, bool *inputs_swapped); + static bool CreateCompareInsteadOfXorAdd(Inst *old_inst); + bool OptimizeLenArrayForMultiArray(Inst *len_array, Inst *inst, size_t index_size); static bool TryEliminatePhi(PhiInst *phi); // It is check can we use this peephole in OSR or not @@ -207,6 +209,10 @@ private: } template static void EliminateInstPrecedingStore(GraphVisitor *v, Inst *inst); + bool TrySimplifyCompareNegation(Inst *inst); + bool IsNegationPattern(Inst *inst); + bool TrySimplifyNegationPattern(Inst *inst); + bool IsCastWithNagationPattern(Inst *inst); private: // Each peephole application has own unique index, it will be used in peepholes dumps diff --git a/compiler/tests/lowering_test.cpp b/compiler/tests/lowering_test.cpp index 8a71138ae..0e59304f9 100644 --- a/compiler/tests/lowering_test.cpp +++ b/compiler/tests/lowering_test.cpp @@ -2496,6 +2496,52 @@ TEST_F(LoweringTest, UnsignedModPowerOfTwo) graph1->RunPass(); ASSERT_TRUE(GraphComparator().Compare(graph1, graph2)); } + +TEST_F(LoweringTest, CompareBoolConstZero) +{ + std::array codes {ConditionCode::CC_NE, ConditionCode::CC_EQ, ConditionCode::CC_BE}; + for (auto code : codes) { + for (auto swap : {true, false}) { + auto graph = CreateEmptyLowLevelGraph(); + GRAPH(graph) + { + CONSTANT(1, 0).s64(); + BASIC_BLOCK(2, 1) + { + INST(2, Opcode::SaveState).NoVregs(); + INST(3, Opcode::CallStatic).b().InputsAutoType(2); + if (swap) { + INST(4, Opcode::Compare).b().Inputs(1, 3).CC(code); + } else { + INST(4, Opcode::Compare).b().Inputs(3, 1).CC(code); + } + INST(5, Opcode::Return).b().Inputs(4); + } + } + ASSERT_TRUE(graph->RunPass()); + graph->RunPass(); + + if (code == ConditionCode::CC_EQ) { + auto graph_final = CreateEmptyGraph(); + GRAPH(graph_final) + { + BASIC_BLOCK(2, 1) + { + INST(2, Opcode::SaveState).NoVregs(); + INST(3, Opcode::CallStatic).b().InputsAutoType(2); + INST(8, Opcode::XorI).b().Inputs(3).Imm(1); + INST(5, Opcode::Return).b().Inputs(8); + } + } + ASSERT_TRUE(GraphComparator().Compare(graph, graph_final)); + } else { + auto clear_graph = + GraphCloner(graph, GetGraph()->GetAllocator(), GetGraph()->GetLocalAllocator()).CloneGraph(); + ASSERT_TRUE(GraphComparator().Compare(graph, clear_graph)); + } + } + } +} // NOLINTEND(readability-magic-numbers) } // namespace panda::compiler diff --git a/compiler/tests/peepholes_test.cpp b/compiler/tests/peepholes_test.cpp index 3d4d9a845..3f92aa6c1 100644 --- a/compiler/tests/peepholes_test.cpp +++ b/compiler/tests/peepholes_test.cpp @@ -866,6 +866,40 @@ TEST_F(PeepholesTest, AddNegTest4) ASSERT_TRUE(INS(5).GetOpcode() == Opcode::Sub); } +TEST_F(PeepholesTest, AddNegConstOne) +{ + GRAPH(GetGraph()) + { + CONSTANT(0, 1); + BASIC_BLOCK(2, -1) + { + INST(1, Opcode::SaveState).NoVregs(); + INST(2, Opcode::CallStatic).b().InputsAutoType(1); + INST(3, Opcode::Neg).s32().Inputs(2); + INST(4, Opcode::Add).s32().Inputs(3, 0); + INST(5, Opcode::Return).s32().Inputs(4); + } + } + + auto graph = CreateEmptyGraph(); + GRAPH(graph) + { + CONSTANT(6, 0); + BASIC_BLOCK(2, -1) + { + INST(1, Opcode::SaveState).NoVregs(); + INST(2, Opcode::CallStatic).b().InputsAutoType(1); + INST(7, Opcode::Compare).s32().b().CC(ConditionCode::CC_EQ).SetType(DataType::BOOL).Inputs(2, 6); + INST(5, Opcode::Return).s32().Inputs(7); + } + } + + ASSERT_TRUE(GetGraph()->RunPass()); + ASSERT_TRUE(GetGraph()->RunPass()); + + ASSERT_TRUE(GraphComparator().Compare(GetGraph(), graph)); +} + TEST_F(PeepholesTest, SameAddAndSubTest) { GRAPH(GetGraph()) @@ -1534,6 +1568,430 @@ TEST_F(PeepholesTest, NegTest2) ASSERT_EQ(INS(4).GetInput(0).GetInst(), sub); } +TEST_F(PeepholesTest, NegationCompare) +{ + GRAPH(GetGraph()) + { + PARAMETER(0, 0).s64(); + PARAMETER(1, 1).s64(); + CONSTANT(5, 0x1); + BASIC_BLOCK(2, -1) + { + INST(2, Opcode::Compare).b().CC(CC_GT).Inputs(0, 1); + INST(3, Opcode::Neg).s32().Inputs(2); + INST(6, Opcode::Add).s32().Inputs(3, 5); + INST(4, Opcode::Return).b().Inputs(6); + } + } + ASSERT_TRUE(GetGraph()->RunPass()); + ASSERT_TRUE(GetGraph()->RunPass()); + + auto graph = CreateEmptyGraph(); + GRAPH(graph) + { + PARAMETER(0, 0).s64(); + PARAMETER(1, 1).s64(); + BASIC_BLOCK(2, -1) + { + INST(2, Opcode::Compare).b().CC(CC_LE).Inputs(0, 1); + INST(4, Opcode::Return).b().Inputs(2); + } + } + ASSERT_TRUE(GraphComparator().Compare(GetGraph(), graph)); + + // Test with OSR + auto default_graph = CreateEmptyGraph(); + GRAPH(default_graph) + { + PARAMETER(0, 0).s64(); + PARAMETER(1, 1).s64(); + CONSTANT(5, 0x1); + BASIC_BLOCK(2, 3) + { + INST(2, Opcode::Compare).b().CC(CC_GT).Inputs(0, 1); + INST(3, Opcode::Neg).s32().Inputs(2); + INST(6, Opcode::Add).s32().Inputs(3, 5); + } + BASIC_BLOCK(3, -1) + { + INST(4, Opcode::Return).b().Inputs(6); + } + } + Graph *graph_osr = + GraphCloner(default_graph, default_graph->GetAllocator(), default_graph->GetLocalAllocator()).CloneGraph(); + graph_osr->SetMode(GraphMode::Osr()); + + Graph *graph_clone = GraphCloner(graph_osr, graph_osr->GetAllocator(), graph_osr->GetLocalAllocator()).CloneGraph(); + + ASSERT_FALSE(graph_osr->RunPass()); + ASSERT_TRUE(GraphComparator().Compare(graph_osr, graph_clone)); + + auto optimized_graph = CreateEmptyGraph(); + GRAPH(optimized_graph) + { + PARAMETER(0, 0).s64(); + PARAMETER(1, 1).s64(); + CONSTANT(5, 0x1); + BASIC_BLOCK(2, 3) + { + INST(2, Opcode::Compare).b().CC(CC_LE).Inputs(0, 1); + INST(3, Opcode::Neg).s32().Inputs(2); + INST(6, Opcode::Add).s32().Inputs(3, 5); + } + BASIC_BLOCK(3, -1) + { + INST(4, Opcode::Return).b().Inputs(2); + } + } + ASSERT_TRUE(default_graph->RunPass()); + ASSERT_TRUE(GraphComparator().Compare(default_graph, optimized_graph)); +} + +TEST_F(PeepholesTest, TransformNegationToCompare) +{ + // Test with OSR + auto default_graph = CreateEmptyGraph(); + GRAPH(default_graph) + { + PARAMETER(0, 0).s64(); + CONSTANT(5, 0x1); + BASIC_BLOCK(2, 3) + { + INST(1, Opcode::SaveState).NoVregs(); + INST(2, Opcode::CallStatic).b().InputsAutoType(1); + INST(3, Opcode::Neg).s32().Inputs(2); + INST(6, Opcode::Add).s32().Inputs(3, 5); + } + BASIC_BLOCK(3, -1) + { + INST(4, Opcode::Return).b().Inputs(6); + } + } + Graph *graph_osr = + GraphCloner(default_graph, default_graph->GetAllocator(), default_graph->GetLocalAllocator()).CloneGraph(); + graph_osr->SetMode(GraphMode::Osr()); + + Graph *graph_clone = GraphCloner(graph_osr, graph_osr->GetAllocator(), graph_osr->GetLocalAllocator()).CloneGraph(); + + ASSERT_FALSE(graph_osr->RunPass()); + ASSERT_TRUE(GraphComparator().Compare(graph_osr, graph_clone)); + // Test without OSR + auto optimized_graph = CreateEmptyGraph(); + GRAPH(optimized_graph) + { + PARAMETER(0, 0).s64(); + CONSTANT(5, 0x1); + CONSTANT(7, 0x0); + BASIC_BLOCK(2, 3) + { + INST(1, Opcode::SaveState).NoVregs(); + INST(2, Opcode::CallStatic).b().InputsAutoType(1); + INST(3, Opcode::Neg).s32().Inputs(2); + INST(6, Opcode::Add).s32().Inputs(3, 5); + INST(8, Opcode::Compare).b().Inputs(2, 7); + } + BASIC_BLOCK(3, -1) + { + INST(4, Opcode::Return).b().Inputs(8); + } + } + ASSERT_TRUE(default_graph->RunPass()); + ASSERT_TRUE(GraphComparator().Compare(default_graph, optimized_graph)); +} + +TEST_F(PeepholesTest, NegCompareNotWork) +{ + GRAPH(GetGraph()) + { + PARAMETER(0, 0).s64(); + PARAMETER(1, 1).s64(); + BASIC_BLOCK(2, -1) + { + INST(2, Opcode::Compare).b().CC(CC_GT).Inputs(0, 1); + INST(3, Opcode::Neg).b().Inputs(2); + INST(5, Opcode::Mul).b().Inputs(2, 3); + INST(4, Opcode::Return).b().Inputs(5); + } + } + Graph *graph_clone = + GraphCloner(GetGraph(), GetGraph()->GetAllocator(), GetGraph()->GetLocalAllocator()).CloneGraph(); + ASSERT_FALSE(GetGraph()->RunPass()); + ASSERT_TRUE(GraphComparator().Compare(GetGraph(), graph_clone)); +} + +TEST_F(PeepholesTest, CompareNegation) +{ + GRAPH(GetGraph()) + { + PARAMETER(0, 0).b(); + PARAMETER(1, 1).b(); + CONSTANT(8, 0x1); + BASIC_BLOCK(2, -1) + { + INST(2, Opcode::Neg).i32().Inputs(0); + INST(9, Opcode::Add).i32().Inputs(2, 8); + INST(4, Opcode::Compare).b().CC(ConditionCode::CC_EQ).SrcType(DataType::BOOL).Inputs(1, 9); + INST(7, Opcode::Return).b().Inputs(4); + } + } + auto graph = CreateEmptyGraph(); + GRAPH(graph) + { + PARAMETER(0, 0).b(); + PARAMETER(1, 1).b(); + BASIC_BLOCK(2, -1) + { + INST(4, Opcode::Compare).b().CC(ConditionCode::CC_NE).SrcType(DataType::BOOL).Inputs(1, 0); + INST(7, Opcode::Return).b().Inputs(4); + } + } + ASSERT_TRUE(GetGraph()->RunPass()); + ASSERT_TRUE(GetGraph()->RunPass()); + ASSERT_TRUE(GraphComparator().Compare(GetGraph(), graph)); + + auto graph1 = CreateEmptyGraph(); + GRAPH(graph1) + { + PARAMETER(0, 0).b(); + PARAMETER(1, 1).b(); + CONSTANT(8, 0x1); + BASIC_BLOCK(2, -1) + { + INST(2, Opcode::Neg).i32().Inputs(0); + INST(9, Opcode::Add).i32().Inputs(2, 8); + INST(4, Opcode::Compare).b().CC(ConditionCode::CC_EQ).SrcType(DataType::BOOL).Inputs(1, 9); + INST(11, Opcode::Add).i32().Inputs(4, 9); + INST(7, Opcode::Return).b().Inputs(11); + } + } + auto graph2 = CreateEmptyGraph(); + GRAPH(graph2) + { + PARAMETER(0, 0).b(); + PARAMETER(1, 1).b(); + CONSTANT(8, 0x1); + BASIC_BLOCK(2, -1) + { + INST(2, Opcode::Neg).i32().Inputs(0); + INST(9, Opcode::Add).i32().Inputs(2, 8); + INST(4, Opcode::Compare).b().CC(ConditionCode::CC_NE).SrcType(DataType::BOOL).Inputs(1, 0); + INST(11, Opcode::Add).i32().Inputs(4, 9); + INST(7, Opcode::Return).b().Inputs(11); + } + } + GraphChecker(graph1).Check(); + ASSERT_TRUE(graph1->RunPass()); + ASSERT_TRUE(GraphComparator().Compare(graph1, graph2)); +} + +TEST_F(PeepholesTest, CompareNegationBoolOsr) +{ + // Peephole don't work with that graph in OSR + GetGraph()->SetMode(GraphMode::Osr()); + GRAPH(GetGraph()) + { + PARAMETER(1, 1).b(); + CONSTANT(3, 0x1); + BASIC_BLOCK(2, 3) + { + INST(6, Opcode::SaveState).NoVregs(); + INST(7, Opcode::CallStatic).b().InputsAutoType(6); + } + BASIC_BLOCK(3, -1) + { + INST(2, Opcode::Neg).s32().Inputs(7); + INST(8, Opcode::Add).s32().Inputs(2, 3); + INST(4, Opcode::Compare).b().CC(ConditionCode::CC_EQ).SrcType(DataType::BOOL).Inputs(8, 1); + INST(5, Opcode::Return).b().Inputs(4); + } + } + ASSERT_FALSE(GetGraph()->RunPass()); + + // Work with that graph + auto optimizable_graph = CreateEmptyGraph(); + optimizable_graph->SetMode(GraphMode::Osr()); + GRAPH(optimizable_graph) + { + PARAMETER(1, 1).b(); + CONSTANT(3, 0x1); + BASIC_BLOCK(2, -1) + { + INST(6, Opcode::SaveState).NoVregs(); + INST(7, Opcode::CallStatic).b().InputsAutoType(6); + + INST(2, Opcode::Neg).s32().Inputs(7); + INST(8, Opcode::Add).s32().Inputs(2, 3); + INST(4, Opcode::Compare).b().CC(ConditionCode::CC_EQ).SrcType(DataType::BOOL).Inputs(8, 1); + INST(5, Opcode::Return).b().Inputs(4); + } + } + + auto optimizable_graph_after = CreateEmptyGraph(); + optimizable_graph_after->SetMode(GraphMode::Osr()); + GRAPH(optimizable_graph_after) + { + PARAMETER(1, 1).b(); + BASIC_BLOCK(2, -1) + { + INST(6, Opcode::SaveState).NoVregs(); + INST(7, Opcode::CallStatic).b().InputsAutoType(6); + + INST(4, Opcode::Compare).b().CC(ConditionCode::CC_NE).SrcType(DataType::BOOL).Inputs(7, 1); + INST(5, Opcode::Return).b().Inputs(4); + } + } + ASSERT_TRUE(optimizable_graph->RunPass()); + ASSERT_TRUE(optimizable_graph->RunPass()); + ASSERT_TRUE(GraphComparator().Compare(optimizable_graph, optimizable_graph_after)); +} + +TEST_F(PeepholesTest, IfImmNegation) +{ + // ConditionCode in IfImm with bool src maybe not only CC_EQ or CC_NE + std::array codes {ConditionCode::CC_NE, ConditionCode::CC_EQ, ConditionCode::CC_GE, + ConditionCode::CC_B}; + for (auto code : codes) { + auto graph = CreateEmptyGraph(); + GRAPH(graph) + { + PARAMETER(0, 0).b(); + PARAMETER(1, 1).b(); + CONSTANT(8, 0x1); + BASIC_BLOCK(2, 3, 4) + { + INST(2, Opcode::Neg).b().Inputs(0); + INST(9, Opcode::Add).i32().Inputs(2, 8); + INST(4, Opcode::IfImm).SrcType(DataType::BOOL).Imm(1).CC(code).Inputs(9); + } + BASIC_BLOCK(3, -1) + { + INST(6, Opcode::Return).b().Inputs(0); + } + BASIC_BLOCK(4, -1) + { + INST(7, Opcode::Return).b().Inputs(0); + } + } + + Graph *final_graph = nullptr; + final_graph = CreateEmptyGraph(); + GRAPH(final_graph) + { + PARAMETER(0, 0).b(); + PARAMETER(1, 1).b(); + CONSTANT(8, 0x1); + CONSTANT(10, 0x0); + BASIC_BLOCK(2, 3, 4) + { + INST(2, Opcode::Neg).b().Inputs(0); + INST(9, Opcode::Add).i32().Inputs(2, 8); + INST(11, Opcode::Compare).b().SrcType(DataType::BOOL).Inputs(0, 10); + // That optimized by combination Compare + IfImm + INST(4, Opcode::IfImm).SrcType(DataType::BOOL).Imm(1).CC(GetInverseConditionCode(code)).Inputs(0); + } + BASIC_BLOCK(3, -1) + { + INST(6, Opcode::Return).b().Inputs(0); + } + BASIC_BLOCK(4, -1) + { + INST(7, Opcode::Return).b().Inputs(0); + } + } + ASSERT_TRUE(graph->RunPass()); + ASSERT_TRUE(GraphComparator().Compare(graph, final_graph)); + } +} + +TEST_F(PeepholesTest, IfImmNegationOsr) +{ + // Peephole don't work with that graph in OSR + GetGraph()->SetMode(GraphMode::Osr()); + GRAPH(GetGraph()) + { + PARAMETER(0, 0).b(); + PARAMETER(1, 1).b(); + CONSTANT(3, 0x1); + BASIC_BLOCK(2, 3) + { + INST(6, Opcode::SaveState).NoVregs(); + INST(7, Opcode::CallStatic).b().InputsAutoType(6); + } + BASIC_BLOCK(3, 4, 5) + { + INST(2, Opcode::Neg).s32().Inputs(7); + INST(9, Opcode::Add).s32().Inputs(2, 3); + INST(4, Opcode::IfImm).SrcType(DataType::BOOL).Imm(1).CC(ConditionCode::CC_EQ).Inputs(9); + } + BASIC_BLOCK(4, -1) + { + INST(5, Opcode::Return).b().Inputs(0); + } + BASIC_BLOCK(5, -1) + { + INST(8, Opcode::Return).b().Inputs(0); + } + } + ASSERT_FALSE(GetGraph()->RunPass()); + + // Work with that graph + auto optimizable_graph = CreateEmptyGraph(); + optimizable_graph->SetMode(GraphMode::Osr()); + GRAPH(optimizable_graph) + { + PARAMETER(0, 0).b(); + PARAMETER(1, 1).b(); + CONSTANT(3, 0x1); + BASIC_BLOCK(2, 4, 5) + { + INST(6, Opcode::SaveState).NoVregs(); + INST(7, Opcode::CallStatic).b().InputsAutoType(6); + + INST(2, Opcode::Neg).s32().Inputs(7); + INST(9, Opcode::Add).s32().Inputs(2, 3); + INST(4, Opcode::IfImm).SrcType(DataType::BOOL).Imm(1).CC(ConditionCode::CC_EQ).Inputs(9); + } + BASIC_BLOCK(4, -1) + { + INST(5, Opcode::Return).b().Inputs(0); + } + BASIC_BLOCK(5, -1) + { + INST(8, Opcode::Return).b().Inputs(0); + } + } + + auto optimizable_graph_after = CreateEmptyGraph(); + optimizable_graph_after->SetMode(GraphMode::Osr()); + GRAPH(optimizable_graph_after) + { + PARAMETER(0, 0).b(); + PARAMETER(1, 1).b(); + CONSTANT(3, 0x1); + CONSTANT(10, 0x0); + BASIC_BLOCK(2, 4, 5) + { + INST(6, Opcode::SaveState).NoVregs(); + INST(7, Opcode::CallStatic).b().InputsAutoType(6); + + INST(2, Opcode::Neg).s32().Inputs(7); + INST(9, Opcode::Add).s32().Inputs(2, 3); + INST(11, Opcode::Compare).b().SrcType(DataType::BOOL).Inputs(7, 10); + INST(4, Opcode::IfImm).SrcType(DataType::BOOL).Imm(1).CC(ConditionCode::CC_NE).Inputs(7); + } + BASIC_BLOCK(4, -1) + { + INST(5, Opcode::Return).b().Inputs(0); + } + BASIC_BLOCK(5, -1) + { + INST(8, Opcode::Return).b().Inputs(0); + } + } + ASSERT_TRUE(optimizable_graph->RunPass()); + ASSERT_TRUE(GraphComparator().Compare(optimizable_graph, optimizable_graph_after)); +} + // Checking the shift with zero constant TEST_F(PeepholesTest, ShlZeroTest) { @@ -5048,6 +5506,9 @@ TEST_F(PeepholesTest, ConditionalAssignmentPreserveIf) { PARAMETER(0, 0).u64(); CONSTANT(4, 1); + if (inverse) { + CONSTANT(5, 0); + } BASIC_BLOCK(2, 3, 4) { @@ -5064,8 +5525,8 @@ TEST_F(PeepholesTest, ConditionalAssignmentPreserveIf) BASIC_BLOCK(3, -1) { if (inverse) { - INST(6, Opcode::Xor).b().Inputs(1, 4); - INST(7, Opcode::Return).b().Inputs(6); + INST(11, Opcode::Compare).b().CC(ConditionCode::CC_EQ).SetType(DataType::BOOL).Inputs(1, 5); + INST(7, Opcode::Return).b().Inputs(11); } else { INST(7, Opcode::Return).b().Inputs(1); } @@ -5936,6 +6397,39 @@ TEST_F(PeepholesTest, OverflowCheckAsBitwiseAndSSInput) ASSERT_TRUE(GraphComparator().Compare(GetGraph(), clone)); } +TEST_F(PeepholesTest, ReplacingXorWithCompare) +{ + GRAPH(GetGraph()) + { + CONSTANT(0, 1); + BASIC_BLOCK(2, -1) + { + INST(1, Opcode::SaveState).NoVregs(); + INST(2, Opcode::CallStatic).b().InputsAutoType(1); + INST(3, Opcode::Xor).s32().Inputs(0, 2); + INST(9, Opcode::Return).s32().Inputs(3); + } + } + + auto graph = CreateEmptyGraph(); + GRAPH(graph) + { + CONSTANT(10, 0); + BASIC_BLOCK(2, -1) + { + INST(1, Opcode::SaveState).NoVregs(); + INST(2, Opcode::CallStatic).b().InputsAutoType(1); + INST(11, Opcode::Compare).b().CC(ConditionCode::CC_EQ).Inputs(2, 10); + INST(9, Opcode::Return).s32().Inputs(11); + } + } + ASSERT_TRUE(GetGraph()->RunPass()); + ASSERT_TRUE(GetGraph()->RunPass()); + + GraphChecker(GetGraph()).Check(); + ASSERT_TRUE(GraphComparator().Compare(GetGraph(), graph)); +} + TEST_F(PeepholesTest, MultiArrayWithLenArrayFirstLayer) { auto class1 = reinterpret_cast(1); diff --git a/plugins/ets/tests/checked/CMakeLists.txt b/plugins/ets/tests/checked/CMakeLists.txt index 3ee3e1838..eac71894c 100644 --- a/plugins/ets/tests/checked/CMakeLists.txt +++ b/plugins/ets/tests/checked/CMakeLists.txt @@ -148,3 +148,4 @@ if (PANDA_TARGET_AMD64 OR NOT PANDA_ARM64_TESTS_WITH_SANITIZER) endif() panda_add_checked_test_ets(FILE ${CMAKE_CURRENT_SOURCE_DIR}/load_array.ets) +panda_add_checked_test_ets(FILE ${CMAKE_CURRENT_SOURCE_DIR}/optimize_negation.ets) diff --git a/plugins/ets/tests/checked/optimize_negation.ets b/plugins/ets/tests/checked/optimize_negation.ets new file mode 100644 index 000000000..7c0a974f3 --- /dev/null +++ b/plugins/ets/tests/checked/optimize_negation.ets @@ -0,0 +1,55 @@ +/* + * 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 Test check optimize bool Negation +//! RUN force_jit: true, options: "--compiler-regex='.*func.*' --compiler-inlining=false", entry: "ETSGLOBAL::main" +//! EVENT /Compilation,ETSGLOBAL::func,.*,COMPILED/ +//! METHOD "ETSGLOBAL::func" +//! +//! PASS_BEFORE "Peepholes" +//! INST "Xor " +//! INST_NOT "Compare" +//! +//! PASS_AFTER "Peepholes" +//! INST "Xor " +//! INST "Compare" +//! +//! PASS_BEFORE "Lowering" +//! INST "Compare" +//! INST_NOT "Xor " +//! INST_NOT "XorI" +//! +//! PASS_AFTER "Lowering" +//! INST "Xor " +//! INST "Compare" +//! INST "XorI" +//! +//! PASS_AFTER_NEXT "Cleanup" +//! INST "XorI" +//! INST_NOT "Xor " +//! INST_NOT "Compare" + +function get_bool() : boolean { + return false +} + +function func() : boolean { + let a : boolean = get_bool() + return !a +} + +function main() : void { + func() +} diff --git a/tests/checked/CMakeLists.txt b/tests/checked/CMakeLists.txt index ae3ea3bd6..1ef7d9aef 100644 --- a/tests/checked/CMakeLists.txt +++ b/tests/checked/CMakeLists.txt @@ -236,3 +236,4 @@ panda_add_checked_test(FILE ${CMAKE_CURRENT_SOURCE_DIR}/compiler_to_interpreter. panda_add_checked_test(FILE ${CMAKE_CURRENT_SOURCE_DIR}/float_intrinsic.pa SUPPORT_RELEASE true) panda_add_checked_test(FILE ${CMAKE_CURRENT_SOURCE_DIR}/move_nullcheck_out_of_loop.pa SUPPORT_RELEASE true) panda_add_checked_test(FILE ${CMAKE_CURRENT_SOURCE_DIR}/check_verifier.pa) +panda_add_checked_test(FILE ${CMAKE_CURRENT_SOURCE_DIR}/xor_to_compare_to_xor.pa) diff --git a/tests/checked/xor_to_compare_to_xor.pa b/tests/checked/xor_to_compare_to_xor.pa new file mode 100644 index 000000000..25a44b2ad --- /dev/null +++ b/tests/checked/xor_to_compare_to_xor.pa @@ -0,0 +1,46 @@ +# Copyright (c) 2021-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 Check that xor transform to Compare and after all to xor +#! RUN force_jit: true, options: "--compiler-regex=.*main.* --compiler-inlining=false", entry: "_GLOBAL::main", result: 0 +#! EVENT /Compilation,_GLOBAL::main,.*COMPILED/ +#! METHOD "_GLOBAL::main" +#! PASS_BEFORE "Peepholes" +#! INST "Neg" +#! INST "Add" +#! INST_NOT "Compare" +#! +#! PASS_AFTER_NEXT "Peepholes" +#! INST "Compare" +#! PASS_AFTER_NEXT "Cleanup" +#! INST_NOT "Add" +#! INST_NOT "Neg" +#! +#! PASS_AFTER "Lowering" +#! INST "XorI" +#! INST "Compare" +#! PASS_AFTER_NEXT "Cleanup" +#! INST "XorI" +#! INST_NOT "Compare" + +.function u1 getVal() { + ldai 1 + return +} + +.function i32 main() { + call getVal + neg + addi 0x1 + return +} -- Gitee