From 70e1aece475489a41a61490ff13eaed567d70162 Mon Sep 17 00:00:00 2001 From: "Sergey V. Ignatov" Date: Fri, 26 May 2023 18:30:48 +0300 Subject: [PATCH] [Compiler] Supported IfConv for float types Signed-off-by: Sergey V. Ignatov --- compiler/optimizer/code_generator/encode.h | 5 + .../code_generator/target/aarch64/encode.cpp | 41 +++++-- .../code_generator/target/aarch64/target.h | 1 + compiler/optimizer/ir/basicblock.cpp | 2 +- compiler/optimizer/ir/graph_checker.cpp | 9 +- .../optimizer/optimizations/if_conversion.cpp | 4 +- .../optimizer/optimizations/if_conversion.h | 8 +- compiler/tests/if_conversion_test.cpp | 9 +- tests/checked/CMakeLists.txt | 1 + tests/checked/ifcvt_float.pa | 101 ++++++++++++++++++ 10 files changed, 161 insertions(+), 20 deletions(-) create mode 100644 tests/checked/ifcvt_float.pa diff --git a/compiler/optimizer/code_generator/encode.h b/compiler/optimizer/code_generator/encode.h index a9063a1fb..ed62a9097 100644 --- a/compiler/optimizer/code_generator/encode.h +++ b/compiler/optimizer/code_generator/encode.h @@ -620,6 +620,11 @@ public: return false; } + virtual bool CanEncodeFloatSelect() + { + return false; + } + virtual void EncodeCompareAndSwap([[maybe_unused]] Reg dst, [[maybe_unused]] Reg obj, [[maybe_unused]] Reg offset, [[maybe_unused]] Reg val, [[maybe_unused]] Reg newval) { diff --git a/compiler/optimizer/code_generator/target/aarch64/encode.cpp b/compiler/optimizer/code_generator/target/aarch64/encode.cpp index 0a38935bd..74daa4c25 100644 --- a/compiler/optimizer/code_generator/target/aarch64/encode.cpp +++ b/compiler/optimizer/code_generator/target/aarch64/encode.cpp @@ -2579,35 +2579,53 @@ void Aarch64Encoder::EncodeCmp(Reg dst, Reg src0, Reg src1, Condition cc) void Aarch64Encoder::EncodeSelect(Reg dst, Reg src0, Reg src1, Reg src2, Reg src3, Condition cc) { - ASSERT(!src0.IsFloat() && !src1.IsFloat()); if (src2.IsScalar()) { GetMasm()->Cmp(VixlReg(src2), VixlReg(src3)); } else { GetMasm()->Fcmp(VixlVReg(src2), VixlVReg(src3)); } - GetMasm()->Csel(VixlReg(dst), VixlReg(src0), VixlReg(src1), Convert(cc)); + if (dst.IsFloat()) { + GetMasm()->Fcsel(VixlVReg(dst), VixlVReg(src0), VixlVReg(src1), Convert(cc)); + } else { + GetMasm()->Csel(VixlReg(dst), VixlReg(src0), VixlReg(src1), Convert(cc)); + } } void Aarch64Encoder::EncodeSelect(Reg dst, Reg src0, Reg src1, Reg src2, Imm imm, Condition cc) { - ASSERT(!src0.IsFloat() && !src1.IsFloat() && !src2.IsFloat()); - GetMasm()->Cmp(VixlReg(src2), VixlImm(imm)); - GetMasm()->Csel(VixlReg(dst), VixlReg(src0), VixlReg(src1), Convert(cc)); + if (src2.IsScalar()) { + GetMasm()->Cmp(VixlReg(src2), VixlImm(imm)); + } else { + GetMasm()->Fcmp(VixlVReg(src2), imm.GetAsDouble()); + } + if (dst.IsFloat()) { + GetMasm()->Fcsel(VixlVReg(dst), VixlVReg(src0), VixlVReg(src1), Convert(cc)); + } else { + GetMasm()->Csel(VixlReg(dst), VixlReg(src0), VixlReg(src1), Convert(cc)); + } } void Aarch64Encoder::EncodeSelectTest(Reg dst, Reg src0, Reg src1, Reg src2, Reg src3, Condition cc) { - ASSERT(!src0.IsFloat() && !src1.IsFloat() && !src2.IsFloat() && !src3.IsFloat()); + ASSERT(!src2.IsFloat() && !src3.IsFloat()); GetMasm()->Tst(VixlReg(src2), VixlReg(src3)); - GetMasm()->Csel(VixlReg(dst), VixlReg(src0), VixlReg(src1), ConvertTest(cc)); + if (dst.IsFloat()) { + GetMasm()->Fcsel(VixlVReg(dst), VixlVReg(src0), VixlVReg(src1), ConvertTest(cc)); + } else { + GetMasm()->Csel(VixlReg(dst), VixlReg(src0), VixlReg(src1), ConvertTest(cc)); + } } void Aarch64Encoder::EncodeSelectTest(Reg dst, Reg src0, Reg src1, Reg src2, Imm imm, Condition cc) { - ASSERT(!src0.IsFloat() && !src1.IsFloat() && !src2.IsFloat()); + ASSERT(!src2.IsFloat()); ASSERT(CanEncodeImmLogical(imm.GetAsInt(), src2.GetSize() > WORD_SIZE ? DOUBLE_WORD_SIZE : WORD_SIZE)); GetMasm()->Tst(VixlReg(src2), VixlImm(imm)); - GetMasm()->Csel(VixlReg(dst), VixlReg(src0), VixlReg(src1), ConvertTest(cc)); + if (dst.IsFloat()) { + GetMasm()->Fcsel(VixlVReg(dst), VixlVReg(src0), VixlVReg(src1), ConvertTest(cc)); + } else { + GetMasm()->Csel(VixlReg(dst), VixlReg(src0), VixlReg(src1), ConvertTest(cc)); + } } void Aarch64Encoder::EncodeLdp(Reg dst0, Reg dst1, bool dst_signed, MemRef mem) @@ -2852,6 +2870,11 @@ bool Aarch64Encoder::CanEncodeShiftedOperand(ShiftOpcode opcode, ShiftType shift } } +bool Aarch64Encoder::CanEncodeFloatSelect() +{ + return true; +} + Reg Aarch64Encoder::AcquireScratchRegister(TypeInfo type) { ASSERT(GetMasm()->GetCurrentScratchRegisterScope() == nullptr); diff --git a/compiler/optimizer/code_generator/target/aarch64/target.h b/compiler/optimizer/code_generator/target/aarch64/target.h index 7ca1a6554..14308bb06 100644 --- a/compiler/optimizer/code_generator/target/aarch64/target.h +++ b/compiler/optimizer/code_generator/target/aarch64/target.h @@ -579,6 +579,7 @@ public: bool CanEncodeImmAddSubCmp(int64_t imm, uint32_t size, bool signed_compare) override; bool CanEncodeImmLogical(uint64_t imm, uint32_t size) override; bool CanEncodeScale(uint64_t imm, uint32_t size) override; + bool CanEncodeFloatSelect() override; void EncodeCompareAndSwap(Reg dst, Reg obj, Reg offset, Reg val, Reg newval) override; void EncodeUnsafeGetAndSet(Reg dst, Reg obj, Reg offset, Reg val) override; diff --git a/compiler/optimizer/ir/basicblock.cpp b/compiler/optimizer/ir/basicblock.cpp index 4c7be62c1..c3afa6d6c 100644 --- a/compiler/optimizer/ir/basicblock.cpp +++ b/compiler/optimizer/ir/basicblock.cpp @@ -448,7 +448,7 @@ void BasicBlock::GenerateSelect(Inst *phi, Inst *inst1, Inst *inst2, const Saved { auto other = if_info->succ->GetSuccessor(0); Inst *select = nullptr; - ASSERT(!DataType::IsFloatType(phi->GetType())); + ASSERT(GetGraph()->GetEncoder()->CanEncodeFloatSelect() || !DataType::IsFloatType(phi->GetType())); if (if_info->if_opcode == Opcode::IfImm) { select = GetGraph()->CreateInstSelectImm(phi->GetType(), if_info->if_pc, if_info->if_cc, if_info->if_imm); select->CastToSelectImm()->SetOperandsType(if_info->if_type); diff --git a/compiler/optimizer/ir/graph_checker.cpp b/compiler/optimizer/ir/graph_checker.cpp index 0492e7067..8b2ee1f5b 100644 --- a/compiler/optimizer/ir/graph_checker.cpp +++ b/compiler/optimizer/ir/graph_checker.cpp @@ -1994,14 +1994,17 @@ void GraphChecker::VisitSelect([[maybe_unused]] GraphVisitor *v, [[maybe_unused] } ASSERT_DO_EXT_VISITOR( - DataType::GetCommonType(inst->GetType()) == DataType::INT64 || inst->GetType() == DataType::REFERENCE || + DataType::GetCommonType(inst->GetType()) == DataType::INT64 || + IsFloatType(DataType::GetCommonType(inst->GetType())) || inst->GetType() == DataType::REFERENCE || inst->GetType() == DataType::ANY, (std::cerr << "Select instruction type is not integer or reference or any", inst->Dump(&std::cerr))); ASSERT_DO_EXT_VISITOR(DataType::GetCommonType(op0->GetType()) == DataType::INT64 || + IsFloatType(DataType::GetCommonType(op0->GetType())) || op0->GetType() == DataType::REFERENCE || op0->GetType() == DataType::ANY, (std::cerr << "Select instruction 1st operand type is not integer or reference or any", inst->Dump(&std::cerr))); ASSERT_DO_EXT_VISITOR(DataType::GetCommonType(op1->GetType()) == DataType::INT64 || + IsFloatType(DataType::GetCommonType(op1->GetType())) || op1->GetType() == DataType::REFERENCE || op1->GetType() == DataType::ANY, (std::cerr << "Select instruction 2nd operand type is not integer or reference or any", inst->Dump(&std::cerr))); @@ -2054,13 +2057,15 @@ void GraphChecker::VisitSelectImm([[maybe_unused]] GraphVisitor *v, [[maybe_unus ASSERT_DO_EXT_VISITOR( DataType::GetCommonType(inst->GetType()) == DataType::INT64 || inst->GetType() == DataType::REFERENCE || - (is_dynamic && inst->GetType() == DataType::ANY), + IsFloatType(DataType::GetCommonType(inst->GetType())) || (is_dynamic && inst->GetType() == DataType::ANY), (std::cerr << "SelectImm instruction type is not integer or reference or any", inst->Dump(&std::cerr))); ASSERT_DO_EXT_VISITOR(DataType::GetCommonType(op0->GetType()) == DataType::INT64 || + IsFloatType(DataType::GetCommonType(op0->GetType())) || op0->GetType() == DataType::REFERENCE || (is_dynamic && op0->GetType() == DataType::ANY), (std::cerr << "SelectImm instruction 1st operand type is not integer or reference or any", inst->Dump(&std::cerr))); ASSERT_DO_EXT_VISITOR(DataType::GetCommonType(op1->GetType()) == DataType::INT64 || + IsFloatType(DataType::GetCommonType(op1->GetType())) || op1->GetType() == DataType::REFERENCE || (is_dynamic && op1->GetType() == DataType::ANY), (std::cerr << "SelectImm instruction 2nd operand type is not integer or reference or any", inst->Dump(&std::cerr))); diff --git a/compiler/optimizer/optimizations/if_conversion.cpp b/compiler/optimizer/optimizations/if_conversion.cpp index 6d2be8b8c..5a0d9a06f 100644 --- a/compiler/optimizer/optimizations/if_conversion.cpp +++ b/compiler/optimizer/optimizations/if_conversion.cpp @@ -227,8 +227,8 @@ bool IfConversion::IsPhisAllowed(BasicBlock *bb, BasicBlock *pred1, BasicBlock * continue; } - // Select can't support float operands - if (DataType::IsFloatType(phi->GetType())) { + // Select can be supported for float operands on the specific architectures (arm64 now) + if (DataType::IsFloatType(phi->GetType()) && !can_encode_float_select_) { return false; } diff --git a/compiler/optimizer/optimizations/if_conversion.h b/compiler/optimizer/optimizations/if_conversion.h index c79464167..7ef847e6a 100644 --- a/compiler/optimizer/optimizations/if_conversion.h +++ b/compiler/optimizer/optimizations/if_conversion.h @@ -25,7 +25,10 @@ class Graph; class IfConversion : public Optimization { public: - explicit IfConversion(Graph *graph, uint32_t limit = 2) : Optimization(graph), limit_(limit) {} + explicit IfConversion(Graph *graph, uint32_t limit = 2) : Optimization(graph), limit_(limit) + { + can_encode_float_select_ = graph->GetEncoder()->CanEncodeFloatSelect(); + } NO_MOVE_SEMANTIC(IfConversion); NO_COPY_SEMANTIC(IfConversion); @@ -47,11 +50,12 @@ public: private: uint32_t limit_; + bool can_encode_float_select_ {false}; bool TryTriangle(BasicBlock *bb); bool TryDiamond(BasicBlock *bb); static bool LoopInvariantPreventConversion(BasicBlock *bb); static bool IsConvertable(BasicBlock *bb, uint32_t *inst_count); - static bool IsPhisAllowed(BasicBlock *bb, BasicBlock *pred1, BasicBlock *pred2, uint32_t *phi_count); + bool IsPhisAllowed(BasicBlock *bb, BasicBlock *pred1, BasicBlock *pred2, uint32_t *phi_count); static bool IsConditionChainPhi(Inst *phi); }; } // namespace panda::compiler diff --git a/compiler/tests/if_conversion_test.cpp b/compiler/tests/if_conversion_test.cpp index 57f3ffe55..134189908 100644 --- a/compiler/tests/if_conversion_test.cpp +++ b/compiler/tests/if_conversion_test.cpp @@ -18,14 +18,15 @@ namespace panda::compiler { class IfConversionTest : public GraphTest { -#ifndef NDEBUG public: IfConversionTest() { + SetGraphArch(panda::RUNTIME_ARCH); +#ifndef NDEBUG // GraphChecker hack: LowLevel instructions may appear only after Lowering pass: GetGraph()->SetLowLevelInstructionsEnabled(); - } #endif + } }; // NOLINTBEGIN(readability-magic-numbers) @@ -812,7 +813,7 @@ TEST_F(IfConversionTest, TriangleFloat) } } - ASSERT_FALSE(GetGraph()->RunPass()); + ASSERT_EQ(GetGraph()->RunPass(), GetGraph()->GetEncoder()->CanEncodeFloatSelect()); } TEST_F(IfConversionTest, TrianglePhiFloat) @@ -836,7 +837,7 @@ TEST_F(IfConversionTest, TrianglePhiFloat) } } - ASSERT_FALSE(GetGraph()->RunPass()); + ASSERT_EQ(GetGraph()->RunPass(), GetGraph()->GetEncoder()->CanEncodeFloatSelect()); } TEST_F(IfConversionTest, LoopInvariantPreventTriangle) diff --git a/tests/checked/CMakeLists.txt b/tests/checked/CMakeLists.txt index 25cd961fc..fc421fd8f 100644 --- a/tests/checked/CMakeLists.txt +++ b/tests/checked/CMakeLists.txt @@ -196,6 +196,7 @@ if (PANDA_COMPILER_TARGET_AARCH64 AND PANDA_COMPILER_TARGET_AARCH32) endif() panda_add_checked_test(FILE ${CMAKE_CURRENT_SOURCE_DIR}/ifcvt.pa) if (PANDA_COMPILER_TARGET_AARCH64) + panda_add_checked_test(FILE ${CMAKE_CURRENT_SOURCE_DIR}/ifcvt_float.pa) panda_add_checked_test(FILE ${CMAKE_CURRENT_SOURCE_DIR}/cleanup_sso.pa) endif() #Test for issues 1376 and 1413 diff --git a/tests/checked/ifcvt_float.pa b/tests/checked/ifcvt_float.pa new file mode 100644 index 000000000..88ebdf9e9 --- /dev/null +++ b/tests/checked/ifcvt_float.pa @@ -0,0 +1,101 @@ +# 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 Float IfConversion in JIT +#! SKIP_IF @architecture != "arm64" +#! RUN force_jit: true, options: "", entry: "_GLOBAL::main" +#! EVENT /Compilation,_GLOBAL::__noinline__test_dd,.*COMPILED/ +#! EVENT /Compilation,_GLOBAL::__noinline__test_di,.*COMPILED/ +#! EVENT /Compilation,_GLOBAL::__noinline__test_id,.*COMPILED/ +#! METHOD "_GLOBAL::__noinline__test_dd" +#! PASS_BEFORE "IfConversion" +#! INST_NOT "Select" +#! PASS_AFTER "IfConversion" +#! INST "Select" +#! METHOD "_GLOBAL::__noinline__test_id" +#! PASS_BEFORE "IfConversion" +#! INST_NOT "Select" +#! PASS_AFTER "IfConversion" +#! INST "Select" +#! METHOD "_GLOBAL::__noinline__test_di" +#! PASS_BEFORE "IfConversion" +#! INST_NOT "Select" +#! PASS_AFTER "IfConversion" +#! INST_NOT "Select" +#! INST "Fcmp" + +.function f64 __noinline__test_dd (f64 a0) <> { + fldai.64 3.0 + fcmpg.64 a0 + jnez let_1_0 + fmovi.64 v0, 2.0 + jmp exit +let_1_0: + fmovi.64 v0, 1.0 +exit: + lda.64 v0 + return.64 +} + +.function f64 __noinline__test_id (i32 a0) { + ldai 3 + ucmp a0 + jnez let_1_0 + fmovi.64 v0, 2.0 + jmp exit +let_1_0: + fmovi.64 v0, 1.0 +exit: + lda.64 v0 + return.64 +} + +.function i32 __noinline__test_di (f64 a0) { + fldai.64 3.0 + fcmpg.64 a0 + jnez let_1_0 + movi v0, 2 + jmp exit +let_1_0: + movi v0, 1 +exit: + return +} + +.function i32 main() { + fmovi.64 v0, 1.0 + call __noinline__test_dd, v0 + fmovi.64 v1, 1.0 + fcmpg.64 v1 + jnez failure_exit_1 + movi v0, 1 + call __noinline__test_id, v0 + fmovi.64 v1, 1.0 + fcmpg.64 v1 + jnez failure_exit_2 + fmovi.64 v0, 1.0 + call __noinline__test_di, v0 + addi -1 + jnez failure_exit_3 + ldai 0 + return +failure_exit_1: + ldai 1 + return +failure_exit_2: + ldai 2 + return +failure_exit_3: + ldai 3 + return +} \ No newline at end of file -- Gitee