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