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_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 DoInlineIntrinsicByExpansion(CallInst *callInst, InlineContext *ctx); 97 bool DoInlineIntrinsicByEncoding(CallInst *callInst, InlineContext *ctx); 98 #include "intrinsics_inlining_expansion.inl.h" 99 100 bool DoInlineMonomorphic(CallInst *callInst, RuntimeInterface::ClassPtr receiver); 101 bool DoInlinePolymorphic(CallInst *callInst, ArenaVector<RuntimeInterface::ClassPtr> *receivers); 102 SaveStateInst *GetOrCloneSaveState(CallInst *callInst, BasicBlock *callBb); 103 void CreateCompareClass(CallInst *callInst, Inst *getClsInst, RuntimeInterface::ClassPtr receiver, 104 BasicBlock *callBb); 105 BasicBlockPair MakeCallBbs(InstPair insts, BasicBlockPair bbs, [[maybe_unused]] PhiInst **phiInst, 106 [[maybe_unused]] size_t receiversSize); 107 void InlineReceiver(InstTriple insts, BasicBlockPair bbs, FlagPair flags, size_t receiversSize, 108 InlinedGraph inlGraph); 109 bool FinalizeInlineReceiver(InstTriple insts, BasicBlockPair bbs, FlagPair flags, bool needToDeoptimize); 110 void InsertDeoptimizeInst(CallInst *callInst, BasicBlock *callBb, 111 DeoptimizeType deoptType = DeoptimizeType::INLINE_IC); 112 void InsertCallInst(CallInst *callInst, BasicBlock *callBb, BasicBlock *retBb, Inst *phiInst); 113 114 void UpdateDataflow(Graph *graphInl, Inst *callInst, std::variant<BasicBlock *, PhiInst *> use, 115 Inst *newDef = nullptr); 116 void UpdateDataflowForEmptyGraph(Inst *callInst, std::variant<BasicBlock *, PhiInst *> use, BasicBlock *endBlock); 117 void UpdateParameterDataflow(Graph *graphInl, Inst *callInst); 118 void UpdateControlflow(Graph *graphInl, BasicBlock *callBb, BasicBlock *callContBb); 119 void MoveConstants(Graph *graphInl); 120 121 template <bool CHECK_EXTERNAL, bool CHECK_INTRINSICS = false> 122 bool CheckMethodCanBeInlined(const CallInst *callInst, InlineContext *ctx); 123 bool CheckDepthLimit(InlineContext *ctx); 124 template <bool CHECK_EXTERNAL> 125 bool CheckTooBigMethodCanBeInlined(const CallInst *callInst, InlineContext *ctx, bool methodIsTooBig); 126 template <bool CHECK_EXTERNAL> 127 bool CheckMethodSize(const CallInst *callInst, InlineContext *ctx); 128 bool ResolveTarget(CallInst *callInst, InlineContext *ctx); 129 bool CanUseTypeInfo(ObjectTypeInfo typeInfo, RuntimeInterface::MethodPtr method); 130 void InsertChaGuard(CallInst *callInst, InlineContext *ctx); 131 132 InlinedGraph BuildGraph(InlineContext *ctx, CallInst *callInst, CallInst *polyCallInst = nullptr); 133 bool CheckBytecode(CallInst *callInst, const InlineContext &ctx, bool *calleeCallRuntime); 134 bool TryBuildGraph(const InlineContext &ctx, Graph *graphInl, CallInst *callInst, CallInst *polyCallInst); 135 bool CheckLoops(bool *calleeCallRuntime, Graph *graphInl); 136 static void PropagateObjectInfo(Graph *graphInl, CallInst *callInst); 137 138 void ProcessCallReturnInstructions(CallInst *callInst, BasicBlock *callContBb, bool hasRuntimeCalls, 139 bool needBarriers = false); 140 size_t CalculateInstructionsCount(Graph *graph); 141 GetCha()142 IClassHierarchyAnalysis *GetCha() 143 { 144 return cha_; 145 } 146 147 bool IsInlineCachesEnabled() const; 148 GetLogIndent()149 std::string GetLogIndent() const 150 { 151 return std::string(GetCurrentDepth() * 2U, ' '); 152 } 153 IsIntrinsic(const InlineContext * ctx)154 bool IsIntrinsic(const InlineContext *ctx) const 155 { 156 return ctx->intrinsicId != RuntimeInterface::IntrinsicId::INVALID; 157 } 158 159 virtual bool SkipBlock(const BasicBlock *block) const; 160 161 protected: 162 // NOLINTBEGIN(misc-non-private-member-variables-in-classes) 163 uint32_t methodsInlined_ {0}; 164 uint32_t instructionsCount_ {0}; 165 uint32_t instructionsLimit_ {0}; 166 ArenaVector<BasicBlock *> returnBlocks_; 167 ArenaUnorderedSet<std::string> blacklist_; 168 ArenaVector<RuntimeInterface::MethodPtr> inlinedStack_; 169 // NOLINTEND(misc-non-private-member-variables-in-classes) 170 171 private: 172 uint32_t vregsCount_ {0}; 173 bool resolveWoInline_ {false}; 174 IClassHierarchyAnalysis *cha_ {nullptr}; 175 }; 176 } // namespace ark::compiler 177 178 #endif // COMPILER_OPTIMIZER_OPTIMIZATIONS_INLINING_H 179