1 /* 2 * Copyright (c) 2021-2025 Huawei Device Co., Ltd. 3 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * you may not use this file except in compliance with the License. 5 * You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software 10 * distributed under the License is distributed on an "AS IS" BASIS, 11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 * See the License for the specific language governing permissions and 13 * limitations under the License. 14 */ 15 16 #ifndef COMPILER_OPTIMIZER_OPTIMIZATIONS_PEEPHOLES_H 17 #define COMPILER_OPTIMIZER_OPTIMIZATIONS_PEEPHOLES_H 18 19 #include "optimizer/ir/graph.h" 20 #include "optimizer/pass.h" 21 22 #include "compiler_logger.h" 23 #include "compiler_options.h" 24 #include "optimizer/ir/analysis.h" 25 #include "optimizer/ir/graph_visitor.h" 26 27 namespace ark::compiler { 28 29 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage) 30 #define PEEPHOLE_IS_APPLIED(visitor, inst) (visitor)->SetIsApplied((inst), true, __FILE__, __LINE__) 31 32 // NOLINTNEXTLINE(fuchsia-multiple-inheritance) 33 class PANDA_PUBLIC_API Peepholes : public Optimization, public GraphVisitor { 34 using Optimization::Optimization; 35 36 public: Peepholes(Graph * graph)37 explicit Peepholes(Graph *graph) : Optimization(graph) {} 38 39 NO_MOVE_SEMANTIC(Peepholes); 40 NO_COPY_SEMANTIC(Peepholes); 41 ~Peepholes() override = default; 42 43 bool RunImpl() override; 44 GetPassName()45 const char *GetPassName() const override 46 { 47 return "Peepholes"; 48 } 49 IsApplied()50 bool IsApplied() const 51 { 52 return isApplied_; 53 } 54 IsEnable()55 bool IsEnable() const override 56 { 57 return g_options.IsCompilerPeepholes(); 58 } 59 60 void InvalidateAnalyses() override; 61 GetBlocksToVisit()62 const ArenaVector<BasicBlock *> &GetBlocksToVisit() const override 63 { 64 return GetGraph()->GetBlocksRPO(); 65 } 66 #include "intrinsics_peephole.inl.h" 67 68 static void VisitSafePoint([[maybe_unused]] GraphVisitor *v, Inst *inst); 69 static void VisitNeg([[maybe_unused]] GraphVisitor *v, Inst *inst); 70 static void VisitAbs([[maybe_unused]] GraphVisitor *v, Inst *inst); 71 static void VisitNot([[maybe_unused]] GraphVisitor *v, Inst *inst); 72 static void VisitAdd([[maybe_unused]] GraphVisitor *v, Inst *inst); 73 static void VisitAddFinalize([[maybe_unused]] GraphVisitor *v, Inst *inst, Inst *input0, Inst *input1); 74 static void VisitSub([[maybe_unused]] GraphVisitor *v, Inst *inst); 75 static void VisitSubFinalize([[maybe_unused]] GraphVisitor *v, Inst *inst, Inst *input0, Inst *input1); 76 static void VisitMulOneConst([[maybe_unused]] GraphVisitor *v, Inst *inst, Inst *input0, Inst *input1); 77 static void VisitMul([[maybe_unused]] GraphVisitor *v, Inst *inst); 78 static void VisitDiv([[maybe_unused]] GraphVisitor *v, Inst *inst); 79 static void VisitMin([[maybe_unused]] GraphVisitor *v, Inst *inst); 80 static void VisitMax([[maybe_unused]] GraphVisitor *v, Inst *inst); 81 static void VisitMod([[maybe_unused]] GraphVisitor *v, Inst *inst); 82 static void VisitShl([[maybe_unused]] GraphVisitor *v, Inst *inst); 83 static void VisitShr([[maybe_unused]] GraphVisitor *v, Inst *inst); 84 static void VisitAShr([[maybe_unused]] GraphVisitor *v, Inst *inst); 85 static void VisitAnd([[maybe_unused]] GraphVisitor *v, Inst *inst); 86 static void VisitOr([[maybe_unused]] GraphVisitor *v, Inst *inst); 87 static void VisitXor([[maybe_unused]] GraphVisitor *v, Inst *inst); 88 static void VisitCmp(GraphVisitor *v, Inst *inst); 89 static void VisitCompare([[maybe_unused]] GraphVisitor *v, Inst *inst); 90 static void VisitIf(GraphVisitor *v, Inst *inst); 91 static void VisitCast([[maybe_unused]] GraphVisitor *v, Inst *inst); 92 static void VisitCastCase1([[maybe_unused]] GraphVisitor *v, Inst *inst); 93 static void VisitCastCase2([[maybe_unused]] GraphVisitor *v, Inst *inst); 94 static void VisitCastCase3([[maybe_unused]] GraphVisitor *v, Inst *inst); 95 static void VisitLenArray(GraphVisitor *v, Inst *inst); 96 static void VisitPhi([[maybe_unused]] GraphVisitor *v, Inst *inst); 97 static void VisitSqrt([[maybe_unused]] GraphVisitor *v, Inst *inst); 98 static void VisitIsInstance(GraphVisitor *v, Inst *inst); 99 static void VisitCastAnyTypeValue(GraphVisitor *v, Inst *inst); 100 static void VisitCastValueToAnyType(GraphVisitor *v, Inst *inst); 101 static void VisitCompareAnyType(GraphVisitor *v, Inst *inst); 102 static void VisitStore([[maybe_unused]] GraphVisitor *v, Inst *inst); 103 static void VisitStoreObject([[maybe_unused]] GraphVisitor *v, Inst *inst); 104 static void VisitStoreStatic([[maybe_unused]] GraphVisitor *v, Inst *inst); 105 static void VisitAddOverflowCheck([[maybe_unused]] GraphVisitor *v, Inst *inst); 106 static void VisitSubOverflowCheck([[maybe_unused]] GraphVisitor *v, Inst *inst); 107 static void VisitNegOverflowAndZeroCheck([[maybe_unused]] GraphVisitor *v, Inst *inst); 108 static void VisitGetInstanceClass([[maybe_unused]] GraphVisitor *v, Inst *inst); 109 static void VisitLoadAndInitClass([[maybe_unused]] GraphVisitor *v, Inst *inst); 110 static void VisitInitClass([[maybe_unused]] GraphVisitor *v, Inst *inst); 111 static void VisitIntrinsic([[maybe_unused]] GraphVisitor *v, Inst *inst); 112 static void VisitLoadClass([[maybe_unused]] GraphVisitor *v, Inst *inst); 113 static void VisitLoadConstantPool([[maybe_unused]] GraphVisitor *v, Inst *inst); 114 static void VisitLoadFromConstantPool([[maybe_unused]] GraphVisitor *v, Inst *inst); 115 static void VisitLoadObject([[maybe_unused]] GraphVisitor *v, Inst *inst); 116 static void VisitLoadStatic([[maybe_unused]] GraphVisitor *v, Inst *inst); 117 118 #include "optimizer/ir/visitor.inc" 119 120 private: 121 void SetIsApplied(Inst *inst, bool altFormat = false, const char *file = nullptr, int line = 0) 122 { 123 isApplied_ = true; 124 if (altFormat) { 125 COMPILER_LOG(DEBUG, PEEPHOLE) << "Peephole (" << file << ":" << line << ") is applied for " << *inst; 126 } else { 127 COMPILER_LOG(DEBUG, PEEPHOLE) << "Peephole is applied for " << GetOpcodeString(inst->GetOpcode()); 128 } 129 GetGraph()->GetEventWriter().EventPeephole(GetOpcodeString(inst->GetOpcode()), inst->GetId(), inst->GetPc()); 130 if (g_options.IsCompilerDumpPeepholes()) { 131 std::string name(GetPassName()); 132 name += "_" + std::to_string(applyIndex_); 133 GetGraph()->GetPassManager()->DumpGraph(name.c_str()); 134 applyIndex_++; 135 } 136 } 137 138 // This function check that we can merge two Phi instructions in one basic block. 139 static bool IsPhiUnionPossible(PhiInst *phi1, PhiInst *phi2); 140 141 // Create new instruction instead of current inst 142 static Inst *CreateAndInsertInst(Opcode newOpc, Inst *currInst, Inst *input0, Inst *input1 = nullptr); 143 144 // Try put constant in second input 145 void TrySwapInputs(Inst *inst); 146 147 // Try to remove redundant cast 148 void TrySimplifyCastToOperandsType(Inst *inst); 149 void TrySimplifyShifts(Inst *inst); 150 bool TrySimplifyAddSubWithZeroInput(Inst *inst); 151 bool TrySimplifyAddSubWithConstInput(Inst *inst); 152 template <Opcode OPC, int IDX> 153 void TrySimplifyAddSub(Inst *inst, Inst *input0, Inst *input1); 154 bool TrySimplifyAddSubAdd(Inst *inst, Inst *input0, Inst *input1); 155 bool TrySimplifyAddSubSub(Inst *inst, Inst *input0, Inst *input1); 156 bool TrySimplifySubAddAdd(Inst *inst, Inst *input0, Inst *input1); 157 bool TrySimplifyShlShlAdd(Inst *inst); 158 bool TryReassociateShlShlAddSub(Inst *inst); 159 void TrySimplifyNeg(Inst *inst); 160 void TryReplaceDivByShift(Inst *inst); 161 bool TrySimplifyCompareCaseInputInv(Inst *inst, Inst *input); 162 bool TrySimplifyCompareWithBoolInput(Inst *inst, bool *isOsrBlocked); 163 bool TrySimplifyCmpCompareWithZero(Inst *inst, bool *isOsrBlocked); 164 bool TrySimplifyFloatCmpCompare(Inst **input0, Inst **input1, DataType::Type *cmpOpType, Inst *compareInput, 165 bool *swap); 166 bool TrySimplifyTestEqualInputs(Inst *inst); 167 void TryRemoveOverflowCheck(Inst *inst); 168 static bool TrySimplifyCompareAndZero(Inst *inst, bool *isOsrBlocked); 169 static bool TrySimplifyCompareAnyType(Inst *inst); 170 static bool TrySimplifyCompareAnyTypeCase1(Inst *inst, Inst *input0, Inst *input1); 171 static bool TrySimplifyCompareAnyTypeCase2(Inst *inst, Inst *input0, Inst *input1); 172 static bool TrySimplifyCompareLenArrayWithZero(Inst *inst); 173 // Try to combine constants when arithmetic operations with constants are repeated 174 template <typename T> 175 static bool TryCombineConst(Inst *inst, ConstantInst *cnst1, T combine, bool *isOsrBlocked); 176 static bool TryCombineAddSubConst(Inst *inst, ConstantInst *cnst1, bool *isOsrBlocked); 177 static bool TryCombineShiftConst(Inst *inst, ConstantInst *cnst1, bool *isOsrBlocked); 178 static bool TryCombineMulConst(Inst *inst, ConstantInst *cnst1, bool *isOsrBlocked); 179 180 static bool GetInputsOfCompareWithConst(const Inst *inst, Inst **input, ConstantInst **constInput, 181 bool *inputsSwapped); 182 183 static bool CreateCompareInsteadOfXorAdd(Inst *oldInst); 184 185 bool OptimizeLenArrayForMultiArray(Inst *lenArray, Inst *inst, size_t indexSize); 186 static bool TryEliminatePhi(PhiInst *phi); 187 // It is check can we use this peephole in OSR or not 188 static bool SkipThisPeepholeInOSR(Inst *inst, Inst *newInput); 189 190 // Table for eliminating 'Compare <CC> <INPUT>, <CONST>' 191 enum InputCode { 192 INPUT_TRUE, // Result of compare is TRUE whatever the INPUT is 193 INPUT_ORIG, // Result of compare is equal to the INPUT itself 194 INPUT_INV, // Result of compare is a negation of the INPUT 195 INPUT_FALSE, // Result of compare is FALSE whatever the INPUT is 196 INPUT_UNDEF // Result of compare is undefined 197 }; 198 // clang-format off 199 static constexpr std::array<std::array<InputCode, 4>, CC_LAST + 1> VALUES = {{ 200 /* CONST < 0 CONST == 0 CONST == 1 CONST > 1 CC */ 201 {INPUT_FALSE, INPUT_INV, INPUT_ORIG, INPUT_FALSE}, /* CC_EQ */ 202 {INPUT_TRUE, INPUT_ORIG, INPUT_INV, INPUT_TRUE}, /* CC_NE */ 203 {INPUT_FALSE, INPUT_FALSE, INPUT_INV, INPUT_TRUE}, /* CC_LT */ 204 {INPUT_FALSE, INPUT_INV, INPUT_TRUE, INPUT_TRUE}, /* CC_LE */ 205 {INPUT_TRUE, INPUT_ORIG, INPUT_FALSE, INPUT_FALSE}, /* CC_GT */ 206 {INPUT_TRUE, INPUT_TRUE, INPUT_ORIG, INPUT_FALSE}, /* CC_GE */ 207 /* For unsigned comparison 'CONST < 0' equals to 'CONST > 1' */ 208 {INPUT_TRUE, INPUT_FALSE, INPUT_INV, INPUT_TRUE}, /* CC_B */ 209 {INPUT_TRUE, INPUT_INV, INPUT_TRUE, INPUT_TRUE}, /* CC_BE */ 210 {INPUT_FALSE, INPUT_ORIG, INPUT_FALSE, INPUT_FALSE}, /* CC_A */ 211 {INPUT_FALSE, INPUT_TRUE, INPUT_ORIG, INPUT_FALSE} /* CC_AE */ 212 }}; 213 // clang-format on GetInputCode(const ConstantInst * inst,ConditionCode cc)214 static InputCode GetInputCode(const ConstantInst *inst, ConditionCode cc) 215 { 216 if (inst->GetType() == DataType::INT32) { 217 auto i = std::min<int32_t>(std::max<int32_t>(-1, static_cast<int32_t>(inst->GetInt32Value())), 2U) + 1; 218 return VALUES[cc][i]; 219 } 220 if (inst->GetType() == DataType::INT64) { 221 auto i = std::min<int64_t>(std::max<int64_t>(-1, static_cast<int64_t>(inst->GetIntValue())), 2U) + 1; 222 return VALUES[cc][i]; 223 } 224 UNREACHABLE(); 225 return InputCode::INPUT_UNDEF; 226 } 227 template <typename T> 228 static void EliminateInstPrecedingStore(GraphVisitor *v, Inst *inst); 229 bool TrySimplifyCompareNegation(Inst *inst); 230 bool IsNegationPattern(Inst *inst); 231 bool TrySimplifyNegationPattern(Inst *inst); 232 static bool TryOptimizeBoxedLoadStoreObject(Inst *inst); 233 static bool TryOptimizeXorChain(Inst *inst); 234 235 private: 236 // Each peephole application has own unique index, it will be used in peepholes dumps 237 uint32_t applyIndex_ {0}; 238 239 bool isApplied_ {false}; 240 }; 241 } // namespace ark::compiler 242 243 #endif // COMPILER_OPTIMIZER_OPTIMIZATIONS_PEEPHOLES_H 244