1 /**
2 * Copyright (c) 2021-2022 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 "optimizer/analysis/alias_analysis.h"
18 #include "optimizer/analysis/dominators_tree.h"
19 #include "deoptimize_elimination.h"
20
21 namespace panda::compiler {
22
RunImpl()23 bool DeoptimizeElimination::RunImpl()
24 {
25 uint64_t insts_number = VisitGraphAndCount();
26
27 ReplaceDeoptimizeIfByUnconditionalDeoptimize();
28
29 if (!HaveCalls() && insts_number <= options.GetCompilerSafepointEliminationLimit()) {
30 RemoveSafePoints();
31 }
32
33 return IsApplied();
34 }
35
ReplaceDeoptimizeIfByUnconditionalDeoptimize()36 void DeoptimizeElimination::ReplaceDeoptimizeIfByUnconditionalDeoptimize()
37 {
38 for (auto &inst : deoptimize_must_throw_) {
39 auto block = inst->GetBasicBlock();
40 if (block != nullptr) {
41 block->ReplaceInstByDeoptimize(inst);
42 SetApplied();
43 }
44 }
45 }
46
RemoveSafePoints()47 void DeoptimizeElimination::RemoveSafePoints()
48 {
49 auto block = GetGraph()->GetStartBlock();
50 ASSERT(block != nullptr && block->IsStartBlock());
51 for (auto sp : block->Insts()) {
52 if (sp->GetOpcode() == Opcode::SafePoint) {
53 sp->ClearFlag(inst_flags::NO_DCE);
54 SetApplied();
55 COMPILER_LOG(DEBUG, DEOPTIMIZE_ELIM) << "SafePoint " << sp->GetId() << " is deleted from start block";
56 block->GetGraph()->GetEventWriter().EventDeoptimizeElimination(GetOpcodeString(sp->GetOpcode()),
57 sp->GetId(), sp->GetPc());
58 }
59 }
60 }
61
RequireRegMap(Inst * inst)62 bool DeoptimizeElimination::RequireRegMap(Inst *inst)
63 {
64 for (auto &user : inst->GetUsers()) {
65 auto user_inst = user.GetInst();
66 if (user_inst->RequireRegMap()) {
67 return true;
68 }
69 if (user_inst->GetOpcode() == Opcode::CallStatic || user_inst->GetOpcode() == Opcode::CallVirtual) {
70 // Inlined method can contain Deoptimize or DeoptimizeIf
71 if (static_cast<CallInst *>(user_inst)->IsInlined()) {
72 return true;
73 }
74 }
75 }
76 return false;
77 }
78
VisitDefault(Inst * inst)79 void DeoptimizeElimination::VisitDefault(Inst *inst)
80 {
81 if (inst->GetType() != DataType::REFERENCE) {
82 return;
83 }
84 for (auto &user : inst->GetUsers()) {
85 auto user_inst = user.GetInst();
86 if (!user_inst->IsSaveState()) {
87 return;
88 }
89 if (user_inst->GetOpcode() == Opcode::SafePoint) {
90 continue;
91 }
92 if (RequireRegMap(user_inst)) {
93 return;
94 }
95 }
96
97 inst->RemoveUsers<true>();
98
99 SetApplied();
100 COMPILER_LOG(DEBUG, DEOPTIMIZE_ELIM) << "All users the instructions " << inst->GetId() << " are SaveStates";
101 inst->GetBasicBlock()->GetGraph()->GetEventWriter().EventDeoptimizeElimination(GetOpcodeString(inst->GetOpcode()),
102 inst->GetId(), inst->GetPc());
103 }
104
VisitSaveState(GraphVisitor * v,Inst * inst)105 void DeoptimizeElimination::VisitSaveState(GraphVisitor *v, Inst *inst)
106 {
107 auto visitor = static_cast<DeoptimizeElimination *>(v);
108 if (visitor->TryToRemoveRedundantSaveState(inst)) {
109 return;
110 }
111
112 if (visitor->RequireRegMap(inst)) {
113 return;
114 }
115
116 auto ss = inst->CastToSaveState();
117 if (ss->RemoveNumericInputs()) {
118 visitor->SetApplied();
119 COMPILER_LOG(DEBUG, DEOPTIMIZE_ELIM) << "SaveState " << ss->GetId() << " numeric inputs were deleted";
120 ss->GetBasicBlock()->GetGraph()->GetEventWriter().EventDeoptimizeElimination(GetOpcodeString(ss->GetOpcode()),
121 ss->GetId(), ss->GetPc());
122 #ifndef NDEBUG
123 ss->SetInputsWereDeleted();
124 #endif
125 }
126 }
127
VisitSaveStateDeoptimize(GraphVisitor * v,Inst * inst)128 void DeoptimizeElimination::VisitSaveStateDeoptimize(GraphVisitor *v, Inst *inst)
129 {
130 static_cast<DeoptimizeElimination *>(v)->TryToRemoveRedundantSaveState(inst);
131 }
132
VisitDeoptimizeIf(GraphVisitor * v,Inst * inst)133 void DeoptimizeElimination::VisitDeoptimizeIf(GraphVisitor *v, Inst *inst)
134 {
135 auto input = inst->GetInput(0).GetInst();
136 auto block = inst->GetBasicBlock();
137 auto graph = block->GetGraph();
138 auto visitor = static_cast<DeoptimizeElimination *>(v);
139 if (input->IsConst()) {
140 if (input->CastToConstant()->GetIntValue() == 0) {
141 visitor->RemoveDeoptimizeIf(inst);
142 } else {
143 visitor->PushNewDeoptimizeIf(inst);
144 }
145 } else if (input->GetOpcode() == Opcode::IsMustDeoptimize) {
146 if (visitor->CanRemoveGuard(input)) {
147 visitor->RemoveGuard(input);
148 }
149 } else {
150 for (auto &user : input->GetUsers()) {
151 auto user_inst = user.GetInst();
152 if (user_inst != inst && user_inst->GetOpcode() == Opcode::DeoptimizeIf &&
153 !(graph->IsOsrMode() && block->GetLoop() != user_inst->GetBasicBlock()->GetLoop()) &&
154 inst->InSameBlockOrDominate(user_inst)) {
155 ASSERT(inst->IsDominate(user_inst));
156 visitor->RemoveDeoptimizeIf(user_inst);
157 }
158 }
159 }
160 }
161
TryToRemoveRedundantSaveState(Inst * inst)162 bool DeoptimizeElimination::TryToRemoveRedundantSaveState(Inst *inst)
163 {
164 if (inst->GetUsers().Empty()) {
165 auto block = inst->GetBasicBlock();
166 block->ReplaceInst(inst, block->GetGraph()->CreateInstNOP());
167 inst->RemoveInputs();
168 SetApplied();
169 COMPILER_LOG(DEBUG, DEOPTIMIZE_ELIM) << "SaveState " << inst->GetId() << " without users is deleted";
170 block->GetGraph()->GetEventWriter().EventDeoptimizeElimination(GetOpcodeString(inst->GetOpcode()),
171 inst->GetId(), inst->GetPc());
172 return true;
173 }
174 return false;
175 }
176
CanRemoveGuard(Inst * guard)177 bool DeoptimizeElimination::CanRemoveGuard(Inst *guard)
178 {
179 auto guard_block = guard->GetBasicBlock();
180 auto it = InstSafeIterator<IterationType::INST, IterationDirection::BACKWARD>(*guard_block, guard);
181 for (++it; it != guard_block->InstsSafeReverse().end(); ++it) {
182 auto inst = *it;
183 if (inst->IsRuntimeCall()) {
184 return false;
185 }
186 if (inst->GetOpcode() == Opcode::IsMustDeoptimize) {
187 return true;
188 }
189 }
190 auto mrk = guard_block->GetGraph()->NewMarker();
191 auto remove_mrk = guard_block->GetGraph()->NewMarker();
192
193 /*
194 * Run search recursively from current block to start block.
195 * We can remove guard, if guard is met in all ways and there should be no call instructions between current
196 * guard and found guards.
197 */
198 bool can_remove = true;
199 for (auto succ_block : guard_block->GetPredsBlocks()) {
200 can_remove &= CanRemoveGuardRec(succ_block, guard, mrk, remove_mrk);
201 if (!can_remove) {
202 break;
203 }
204 }
205 guard_block->GetGraph()->EraseMarker(mrk);
206 guard_block->GetGraph()->EraseMarker(remove_mrk);
207 return can_remove;
208 }
209
CanRemoveGuardRec(BasicBlock * block,Inst * guard,const Marker & mrk,const Marker & remove_mrk)210 bool DeoptimizeElimination::CanRemoveGuardRec(BasicBlock *block, Inst *guard, const Marker &mrk,
211 const Marker &remove_mrk)
212 {
213 if (block->IsStartBlock()) {
214 return false;
215 }
216 auto block_type = GetBlockType(block);
217 if (block->SetMarker(mrk)) {
218 return block->IsMarked(remove_mrk);
219 }
220 if (block_type == BlockType::INVALID) {
221 for (auto inst : block->InstsSafeReverse()) {
222 if (inst->IsRuntimeCall()) {
223 PushNewBlockType(block, BlockType::RUNTIME_CALL);
224 return false;
225 }
226 if (inst->GetOpcode() == Opcode::IsMustDeoptimize) {
227 [[maybe_unused]] auto result = block->SetMarker(remove_mrk);
228 ASSERT(!result);
229 PushNewBlockType(block, BlockType::GUARD);
230 return true;
231 }
232 }
233 PushNewBlockType(block, BlockType::NOTHING);
234 } else if (block_type != BlockType::NOTHING) {
235 if (block_type == BlockType::GUARD) {
236 [[maybe_unused]] auto result = block->SetMarker(remove_mrk);
237 ASSERT(!result);
238 return true;
239 }
240 return false;
241 }
242 for (const auto &succ_block : block->GetPredsBlocks()) {
243 if (!CanRemoveGuardRec(succ_block, guard, mrk, remove_mrk)) {
244 return false;
245 }
246 }
247 [[maybe_unused]] auto result = block->SetMarker(remove_mrk);
248 ASSERT(!result);
249 return true;
250 }
251
RemoveGuard(Inst * guard)252 void DeoptimizeElimination::RemoveGuard(Inst *guard)
253 {
254 ASSERT(guard->GetOpcode() == Opcode::IsMustDeoptimize);
255 ASSERT(guard->HasSingleUser());
256
257 auto deopt = guard->GetNext();
258 ASSERT(deopt->GetOpcode() == Opcode::DeoptimizeIf);
259 auto block = guard->GetBasicBlock();
260 auto graph = block->GetGraph();
261 guard->RemoveInputs();
262 block->ReplaceInst(guard, graph->CreateInstNOP());
263
264 COMPILER_LOG(DEBUG, DEOPTIMIZE_ELIM) << "Dublicated Guard " << guard->GetId() << " is deleted";
265 graph->GetEventWriter().EventDeoptimizeElimination(GetOpcodeString(guard->GetOpcode()), guard->GetId(),
266 guard->GetPc());
267 RemoveDeoptimizeIf(deopt);
268 }
269
RemoveDeoptimizeIf(Inst * inst)270 void DeoptimizeElimination::RemoveDeoptimizeIf(Inst *inst)
271 {
272 auto block = inst->GetBasicBlock();
273 auto graph = block->GetGraph();
274 auto savestate = inst->GetInput(1).GetInst();
275
276 inst->RemoveInputs();
277 block->ReplaceInst(inst, graph->CreateInstNOP());
278
279 COMPILER_LOG(DEBUG, DEOPTIMIZE_ELIM) << "Dublicated or redundant DeoptimizeIf " << inst->GetId() << " is deleted";
280 graph->GetEventWriter().EventDeoptimizeElimination(GetOpcodeString(inst->GetOpcode()), inst->GetId(),
281 inst->GetPc());
282
283 if (savestate->GetUsers().Empty()) {
284 savestate->GetBasicBlock()->ReplaceInst(savestate, graph->CreateInstNOP());
285 savestate->RemoveInputs();
286
287 COMPILER_LOG(DEBUG, DEOPTIMIZE_ELIM) << "SaveState " << savestate->GetId() << " without users is deleted";
288 graph->GetEventWriter().EventDeoptimizeElimination(GetOpcodeString(savestate->GetOpcode()), savestate->GetId(),
289 savestate->GetPc());
290 }
291 SetApplied();
292 }
293
InvalidateAnalyses()294 void DeoptimizeElimination::InvalidateAnalyses()
295 {
296 GetGraph()->InvalidateAnalysis<LoopAnalyzer>();
297 GetGraph()->InvalidateAnalysis<DominatorsTree>();
298 GetGraph()->InvalidateAnalysis<BoundsAnalysis>();
299 GetGraph()->InvalidateAnalysis<AliasAnalysis>();
300 }
301 } // namespace panda::compiler
302