• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2023-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 #include "compiler_logger.h"
17 #include "compiler_options.h"
18 #include "optimizer/analysis/alias_analysis.h"
19 #include "optimizer/analysis/bounds_analysis.h"
20 #include "optimizer/analysis/dominators_tree.h"
21 #include "optimizer/analysis/rpo.h"
22 #include "optimizer/analysis/loop_analyzer.h"
23 #include "optimizer/analysis/types_analysis.h"
24 #include "optimizer/ir/analysis.h"
25 #include "optimizer/ir/basicblock.h"
26 #include "optimizer/ir/inst.h"
27 #include "optimizer/optimizations/inline_intrinsics.h"
28 #ifdef PANDA_WITH_IRTOC
29 #ifndef __clang_analyzer__
30 #include "irtoc_ir_inline.h"
31 #endif
32 #endif
33 
34 namespace ark::compiler {
InlineIntrinsics(Graph * graph)35 InlineIntrinsics::InlineIntrinsics(Graph *graph)
36     : Optimization(graph),
37       types_ {graph->GetLocalAllocator()->Adapter()},
38       savedInputs_ {graph->GetLocalAllocator()->Adapter()},
39       namedAccessProfile_ {graph->GetLocalAllocator()->Adapter()}
40 {
41 }
42 
InvalidateAnalyses()43 void InlineIntrinsics::InvalidateAnalyses()
44 {
45     GetGraph()->InvalidateAnalysis<BoundsAnalysis>();
46     GetGraph()->InvalidateAnalysis<AliasAnalysis>();
47     GetGraph()->InvalidateAnalysis<LoopAnalyzer>();
48     InvalidateBlocksOrderAnalyzes(GetGraph());
49 }
50 
RunImpl()51 bool InlineIntrinsics::RunImpl()
52 {
53     bool isApplied = false;
54     GetGraph()->RunPass<TypesAnalysis>();
55     bool clearBbs = false;
56     // We can't replace intrinsics on Deoptimize in OSR mode, because we can get incorrect value
57     bool isReplaceOnDeopt = !GetGraph()->IsAotMode() && !GetGraph()->IsOsrMode();
58     for (auto bb : GetGraph()->GetVectorBlocks()) {
59         if (bb == nullptr || bb->IsEmpty()) {
60             continue;
61         }
62         for (auto inst : bb->InstsSafe()) {
63             if (GetGraph()->GetVectorBlocks()[inst->GetBasicBlock()->GetId()] == nullptr) {
64                 break;
65             }
66             if (inst->GetOpcode() == Opcode::CallDynamic) {
67                 isApplied |= TryInline(inst->CastToCallDynamic());
68                 continue;
69             }
70             if (inst->GetOpcode() != Opcode::Intrinsic) {
71                 continue;
72             }
73             auto intrinsicsInst = inst->CastToIntrinsic();
74             if (g_options.IsCompilerInlineFullIntrinsics() && !intrinsicsInst->CanBeInlined()) {
75                 // skip intrinsics built inside inlined irtoc handler
76                 continue;
77             }
78             if (TryInline(intrinsicsInst)) {
79                 isApplied = true;
80                 continue;
81             }
82             if (isReplaceOnDeopt && intrinsicsInst->IsReplaceOnDeoptimize()) {
83                 inst->GetBasicBlock()->ReplaceInstByDeoptimize(inst);
84                 clearBbs = true;
85                 break;
86             }
87         }
88     }
89     if (clearBbs) {
90         GetGraph()->RemoveUnreachableBlocks();
91         if (GetGraph()->IsOsrMode()) {
92             CleanupGraphSaveStateOSR(GetGraph());
93         }
94         isApplied = true;
95     }
96     return isApplied;
97 }
98 
GetAssumedAnyType(const Inst * inst)99 AnyBaseType InlineIntrinsics::GetAssumedAnyType(const Inst *inst)
100 {
101     switch (inst->GetOpcode()) {
102         case Opcode::Phi:
103             return inst->CastToPhi()->GetAssumedAnyType();
104         case Opcode::CastValueToAnyType:
105             return inst->CastToCastValueToAnyType()->GetAnyType();
106         case Opcode::AnyTypeCheck: {
107             auto type = inst->CastToAnyTypeCheck()->GetAnyType();
108             if (type == AnyBaseType::UNDEFINED_TYPE) {
109                 return GetAssumedAnyType(inst->GetInput(0).GetInst());
110             }
111             return type;
112         }
113         case Opcode::Constant: {
114             if (inst->GetType() != DataType::ANY) {
115                 // NOTE(dkofanov): Probably, we should resolve const's type based on `inst->GetType()`:
116                 return AnyBaseType::UNDEFINED_TYPE;
117             }
118             coretypes::TaggedValue anyConst(inst->CastToConstant()->GetRawValue());
119             auto type = GetGraph()->GetRuntime()->ResolveSpecialAnyTypeByConstant(anyConst);
120             ASSERT(type != AnyBaseType::UNDEFINED_TYPE);
121             return type;
122         }
123         case Opcode::LoadFromConstantPool: {
124             if (inst->CastToLoadFromConstantPool()->IsString()) {
125                 auto language = GetGraph()->GetRuntime()->GetMethodSourceLanguage(GetGraph()->GetMethod());
126                 return GetAnyStringType(language);
127             }
128             return AnyBaseType::UNDEFINED_TYPE;
129         }
130         default:
131             return AnyBaseType::UNDEFINED_TYPE;
132     }
133 }
134 
DoInline(IntrinsicInst * intrinsic)135 bool InlineIntrinsics::DoInline(IntrinsicInst *intrinsic)
136 {
137     // CC-OFFNXT(C_RULE_SWITCH_BRANCH_CHECKER) autogenerated code
138     switch (intrinsic->GetIntrinsicId()) {
139 #ifndef __clang_analyzer__
140 #include "intrinsics_inline.inl"
141 #endif
142         default: {
143             return false;
144         }
145     }
146 }
147 
TryInline(CallInst * callInst)148 bool InlineIntrinsics::TryInline(CallInst *callInst)
149 {
150     if ((callInst->GetCallMethod() == nullptr)) {
151         return false;
152     }
153     // CC-OFFNXT(C_RULE_SWITCH_BRANCH_CHECKER) autogenerated code
154     switch (GetGraph()->GetRuntime()->GetMethodSourceLanguage(callInst->GetCallMethod())) {
155 #include "intrinsics_inline_native_method.inl"
156         default: {
157             return false;
158         }
159     }
160 }
161 
TryInline(IntrinsicInst * intrinsic)162 bool InlineIntrinsics::TryInline(IntrinsicInst *intrinsic)
163 {
164     if (!IsIntrinsicInlinedByInputTypes(intrinsic->GetIntrinsicId())) {
165         return DoInline(intrinsic);
166     }
167     types_.clear();
168     savedInputs_.clear();
169     AnyBaseType type = AnyBaseType::UNDEFINED_TYPE;
170     for (auto &input : intrinsic->GetInputs()) {
171         auto inputInst = input.GetInst();
172         if (inputInst->IsSaveState()) {
173             continue;
174         }
175         auto inputType = GetAssumedAnyType(inputInst);
176         if (inputType != AnyBaseType::UNDEFINED_TYPE) {
177             type = inputType;
178         } else if (inputInst->GetOpcode() == Opcode::AnyTypeCheck &&
179                    inputInst->CastToAnyTypeCheck()->IsTypeWasProfiled()) {
180             // Type is mixed and compiler cannot make optimization for that case.
181             // Any deduced type will also cause deoptimization. So avoid intrinsic inline here.
182             // Revise it in the future optimizations.
183             return false;
184         }
185         types_.emplace_back(inputType);
186         savedInputs_.emplace_back(inputInst);
187     }
188     // last input is SaveState
189     ASSERT(types_.size() + 1 == intrinsic->GetInputsCount());
190 
191     if (!GetGraph()->GetRuntime()->IsDestroyed(GetGraph()->GetMethod()) && type != AnyBaseType::UNDEFINED_TYPE &&
192         AnyBaseTypeToDataType(type) != DataType::ANY) {
193         // Set known type to undefined input types.
194         // Do not set type based on special input types like Undefined or Null
195         for (auto &currType : types_) {
196             if (currType == AnyBaseType::UNDEFINED_TYPE) {
197                 currType = type;
198             }
199         }
200     }
201     if (DoInline(intrinsic)) {
202         for (size_t i = 0; i < savedInputs_.size(); i++) {
203             ASSERT(!savedInputs_[i]->IsSaveState());
204             if (savedInputs_[i]->GetOpcode() == Opcode::AnyTypeCheck) {
205                 savedInputs_[i]->CastToAnyTypeCheck()->SetAnyType(types_[i]);
206             }
207         }
208         return true;
209     }
210     return false;
211 }
212 
213 }  // namespace ark::compiler
214