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 switch (intrinsic->GetIntrinsicId()) {
138 #ifndef __clang_analyzer__
139 #include "intrinsics_inline.inl"
140 #endif
141 default: {
142 return false;
143 }
144 }
145 }
146
TryInline(CallInst * callInst)147 bool InlineIntrinsics::TryInline(CallInst *callInst)
148 {
149 if ((callInst->GetCallMethod() == nullptr)) {
150 return false;
151 }
152 switch (GetGraph()->GetRuntime()->GetMethodSourceLanguage(callInst->GetCallMethod())) {
153 #include "intrinsics_inline_native_method.inl"
154 default: {
155 return false;
156 }
157 }
158 }
159
TryInline(IntrinsicInst * intrinsic)160 bool InlineIntrinsics::TryInline(IntrinsicInst *intrinsic)
161 {
162 if (!IsIntrinsicInlinedByInputTypes(intrinsic->GetIntrinsicId())) {
163 return DoInline(intrinsic);
164 }
165 types_.clear();
166 savedInputs_.clear();
167 AnyBaseType type = AnyBaseType::UNDEFINED_TYPE;
168 for (auto &input : intrinsic->GetInputs()) {
169 auto inputInst = input.GetInst();
170 if (inputInst->IsSaveState()) {
171 continue;
172 }
173 auto inputType = GetAssumedAnyType(inputInst);
174 if (inputType != AnyBaseType::UNDEFINED_TYPE) {
175 type = inputType;
176 } else if (inputInst->GetOpcode() == Opcode::AnyTypeCheck &&
177 inputInst->CastToAnyTypeCheck()->IsTypeWasProfiled()) {
178 // Type is mixed and compiler cannot make optimization for that case.
179 // Any deduced type will also cause deoptimization. So avoid intrinsic inline here.
180 // Revise it in the future optimizations.
181 return false;
182 }
183 types_.emplace_back(inputType);
184 savedInputs_.emplace_back(inputInst);
185 }
186 // last input is SaveState
187 ASSERT(types_.size() + 1 == intrinsic->GetInputsCount());
188
189 if (!GetGraph()->GetRuntime()->IsDestroyed(GetGraph()->GetMethod()) && type != AnyBaseType::UNDEFINED_TYPE &&
190 AnyBaseTypeToDataType(type) != DataType::ANY) {
191 // Set known type to undefined input types.
192 // Do not set type based on special input types like Undefined or Null
193 for (auto &currType : types_) {
194 if (currType == AnyBaseType::UNDEFINED_TYPE) {
195 currType = type;
196 }
197 }
198 }
199 if (DoInline(intrinsic)) {
200 for (size_t i = 0; i < savedInputs_.size(); i++) {
201 ASSERT(!savedInputs_[i]->IsSaveState());
202 if (savedInputs_[i]->GetOpcode() == Opcode::AnyTypeCheck) {
203 savedInputs_[i]->CastToAnyTypeCheck()->SetAnyType(types_[i]);
204 }
205 }
206 return true;
207 }
208 return false;
209 }
210
211 } // namespace ark::compiler
212