1 /* 2 * Copyright (c) 2021-2024 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_INLINING_H 17 #define COMPILER_OPTIMIZER_OPTIMIZATIONS_INLINING_H 18 19 #include <string> 20 #include "optimizer/pass.h" 21 #include "optimizer/ir/inst.h" 22 #include "optimizer/ir/runtime_interface.h" 23 #include "compiler_options.h" 24 #include "utils/arena_containers.h" 25 26 // NOLINTNEXTLINE(cppcoreguidelines-macro-usage,-warnings-as-errors) 27 #define LOG_INLINING(level) COMPILER_LOG(level, INLINING) << GetLogIndent() 28 29 namespace ark::compiler { 30 struct InlineContext { 31 RuntimeInterface::MethodPtr method {}; 32 bool chaDevirtualize {false}; 33 bool replaceToStatic {false}; 34 RuntimeInterface::IntrinsicId intrinsicId {RuntimeInterface::IntrinsicId::INVALID}; 35 }; 36 37 struct InlinedGraph { 38 Graph *graph {nullptr}; 39 bool hasRuntimeCalls {false}; 40 }; 41 42 class Inlining : public Optimization { 43 public: 44 static constexpr auto MAX_CALL_DEPTH = 20U; 45 using InstPair = std::pair<Inst *, Inst *>; 46 using InstTriple = std::tuple<Inst *, Inst *, Inst *>; 47 using BasicBlockPair = std::pair<BasicBlock *, BasicBlock *>; 48 using FlagPair = std::pair<bool *, bool *>; 49 Inlining(Graph * graph)50 explicit Inlining(Graph *graph) : Inlining(graph, 0, 0, nullptr) {} Inlining(Graph * graph,bool resolveWoInline)51 Inlining(Graph *graph, bool resolveWoInline) : Inlining(graph) 52 { 53 resolveWoInline_ = resolveWoInline; 54 } 55 56 Inlining(Graph *graph, uint32_t instructionsCount, uint32_t methodsInlined, 57 const ArenaVector<RuntimeInterface::MethodPtr> *inlinedStack); 58 59 NO_MOVE_SEMANTIC(Inlining); 60 NO_COPY_SEMANTIC(Inlining); 61 ~Inlining() override = default; 62 63 bool RunImpl() override; 64 IsEnable()65 bool IsEnable() const override 66 { 67 return g_options.IsCompilerInlining(); 68 } 69 GetPassName()70 const char *GetPassName() const override 71 { 72 return "Inline"; 73 } GetCurrentDepth()74 auto GetCurrentDepth() const 75 { 76 ASSERT(!inlinedStack_.empty()); 77 return inlinedStack_.size() - 1; 78 } 79 80 void InvalidateAnalyses() override; 81 82 protected: 83 virtual void RunOptimizations() const; 84 virtual bool IsInstSuitableForInline(Inst *inst) const; 85 virtual bool TryInline(CallInst *callInst); 86 bool TryInlineWithInlineCaches(CallInst *callInst); 87 bool TryInlineExternal(CallInst *callInst, InlineContext *ctx); 88 bool TryInlineExternalAot(CallInst *callInst, InlineContext *ctx); 89 90 Inst *GetNewDefAndCorrectDF(Inst *callInst, Inst *oldDef); 91 92 bool Do(); 93 bool DoInline(CallInst *callInst, InlineContext *ctx); 94 bool DoInlineMethod(CallInst *callInst, InlineContext *ctx); 95 bool DoInlineIntrinsic(CallInst *callInst, InlineContext *ctx); 96 bool DoInlineMonomorphic(CallInst *callInst, RuntimeInterface::ClassPtr receiver); 97 bool DoInlinePolymorphic(CallInst *callInst, ArenaVector<RuntimeInterface::ClassPtr> *receivers); 98 void CreateCompareClass(CallInst *callInst, Inst *getClsInst, RuntimeInterface::ClassPtr receiver, 99 BasicBlock *callBb); 100 BasicBlockPair MakeCallBbs(InstPair insts, BasicBlockPair bbs, [[maybe_unused]] PhiInst **phiInst, 101 [[maybe_unused]] size_t receiversSize); 102 void InlineReceiver(InstTriple insts, BasicBlockPair bbs, FlagPair flags, size_t receiversSize, 103 InlinedGraph inlGraph); 104 bool FinalizeInlineReceiver(InstTriple insts, BasicBlockPair bbs, FlagPair flags, bool needToDeoptimize); 105 void InsertDeoptimizeInst(CallInst *callInst, BasicBlock *callBb, 106 DeoptimizeType deoptType = DeoptimizeType::INLINE_IC); 107 void InsertCallInst(CallInst *callInst, BasicBlock *callBb, BasicBlock *retBb, Inst *phiInst); 108 109 void UpdateDataflow(Graph *graphInl, Inst *callInst, std::variant<BasicBlock *, PhiInst *> use, 110 Inst *newDef = nullptr); 111 void UpdateDataflowForEmptyGraph(Inst *callInst, std::variant<BasicBlock *, PhiInst *> use, BasicBlock *endBlock); 112 void UpdateParameterDataflow(Graph *graphInl, Inst *callInst); 113 void UpdateControlflow(Graph *graphInl, BasicBlock *callBb, BasicBlock *callContBb); 114 void MoveConstants(Graph *graphInl); 115 116 template <bool CHECK_EXTERNAL, bool CHECK_INTRINSICS = false> 117 bool CheckMethodCanBeInlined(const CallInst *callInst, InlineContext *ctx); 118 bool CheckDepthLimit(InlineContext *ctx); 119 template <bool CHECK_EXTERNAL> 120 bool CheckTooBigMethodCanBeInlined(const CallInst *callInst, InlineContext *ctx, bool methodIsTooBig); 121 template <bool CHECK_EXTERNAL> 122 bool CheckMethodSize(const CallInst *callInst, InlineContext *ctx); 123 bool ResolveTarget(CallInst *callInst, InlineContext *ctx); 124 bool CanUseTypeInfo(ObjectTypeInfo typeInfo, RuntimeInterface::MethodPtr method); 125 void InsertChaGuard(CallInst *callInst); 126 127 InlinedGraph BuildGraph(InlineContext *ctx, CallInst *callInst, CallInst *polyCallInst = nullptr); 128 bool CheckBytecode(CallInst *callInst, const InlineContext &ctx, bool *calleeCallRuntime); 129 bool TryBuildGraph(const InlineContext &ctx, Graph *graphInl, CallInst *callInst, CallInst *polyCallInst); 130 bool CheckLoops(bool *calleeCallRuntime, Graph *graphInl); 131 static void PropagateObjectInfo(Graph *graphInl, CallInst *callInst); 132 133 void ProcessCallReturnInstructions(CallInst *callInst, BasicBlock *callContBb, bool hasRuntimeCalls, 134 bool needBarriers = false); 135 size_t CalculateInstructionsCount(Graph *graph); 136 GetCha()137 IClassHierarchyAnalysis *GetCha() 138 { 139 return cha_; 140 } 141 142 bool IsInlineCachesEnabled() const; 143 GetLogIndent()144 std::string GetLogIndent() const 145 { 146 return std::string(GetCurrentDepth() * 2U, ' '); 147 } 148 IsIntrinsic(const InlineContext * ctx)149 bool IsIntrinsic(const InlineContext *ctx) const 150 { 151 return ctx->intrinsicId != RuntimeInterface::IntrinsicId::INVALID; 152 } 153 154 virtual bool SkipBlock(const BasicBlock *block) const; 155 156 protected: 157 // NOLINTBEGIN(misc-non-private-member-variables-in-classes) 158 uint32_t methodsInlined_ {0}; 159 uint32_t instructionsCount_ {0}; 160 uint32_t instructionsLimit_ {0}; 161 ArenaVector<BasicBlock *> returnBlocks_; 162 ArenaUnorderedSet<std::string> blacklist_; 163 ArenaVector<RuntimeInterface::MethodPtr> inlinedStack_; 164 // NOLINTEND(misc-non-private-member-variables-in-classes) 165 166 private: 167 uint32_t vregsCount_ {0}; 168 bool resolveWoInline_ {false}; 169 IClassHierarchyAnalysis *cha_ {nullptr}; 170 }; 171 } // namespace ark::compiler 172 173 #endif // COMPILER_OPTIMIZER_OPTIMIZATIONS_INLINING_H 174