1 /*
2 * Copyright (c) 2021-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 "analysis.h"
17
18 #include "optimizer/ir/basicblock.h"
19 #include "optimizer/analysis/dominators_tree.h"
20 #include "optimizer/analysis/loop_analyzer.h"
21 #include "compiler_logger.h"
22 namespace panda::compiler {
23
24 class BasicBlock;
25
26 class IsOsrEntryBlock {
27 public:
operator ()(const BasicBlock * bb) const28 bool operator()(const BasicBlock *bb) const
29 {
30 return bb->IsOsrEntry();
31 }
32 };
33
34 class IsTryBlock {
35 public:
operator ()(const BasicBlock * bb) const36 bool operator()(const BasicBlock *bb) const
37 {
38 return bb->IsTry();
39 }
40 };
41
42 class IsSaveState {
43 public:
operator ()(const Inst * inst) const44 bool operator()(const Inst *inst) const
45 {
46 return inst->IsSaveState() && inst->IsNotRemovable();
47 }
48 };
49
50 static bool IsSaveStateForGc(const Inst *inst);
51
52 class IsSaveStateCanTriggerGc {
53 public:
operator ()(const Inst * inst) const54 bool operator()(const Inst *inst) const
55 {
56 return IsSaveStateForGc(inst);
57 }
58 };
59
60 template <typename T>
FindBlockBetween(BasicBlock * dominateBb,BasicBlock * currentBb,Marker marker)61 bool FindBlockBetween(BasicBlock *dominateBb, BasicBlock *currentBb, Marker marker)
62 {
63 if (dominateBb == currentBb) {
64 return false;
65 }
66 if (currentBb->SetMarker(marker)) {
67 return false;
68 }
69 if (T()(currentBb)) {
70 return true;
71 }
72 for (auto pred : currentBb->GetPredsBlocks()) {
73 if (FindBlockBetween<T>(dominateBb, pred, marker)) {
74 return true;
75 }
76 }
77 return false;
78 }
79
GetClassPtrForObject(Inst * inst,size_t inputNum)80 RuntimeInterface::ClassPtr GetClassPtrForObject(Inst *inst, size_t inputNum)
81 {
82 auto objInst = inst->GetDataFlowInput(inputNum);
83 if (objInst->GetOpcode() != Opcode::NewObject) {
84 return nullptr;
85 }
86 auto initClass = objInst->GetInput(0).GetInst();
87 if (initClass->GetOpcode() == Opcode::LoadAndInitClass) {
88 return initClass->CastToLoadAndInitClass()->GetClass();
89 }
90 ASSERT(initClass->GetOpcode() == Opcode::LoadImmediate);
91 return initClass->CastToLoadImmediate()->GetClass();
92 }
93
HasOsrEntryBetween(Inst * dominateInst,Inst * inst)94 bool HasOsrEntryBetween(Inst *dominateInst, Inst *inst)
95 {
96 ASSERT(dominateInst->IsDominate(inst));
97 auto bb = inst->GetBasicBlock();
98 auto graph = bb->GetGraph();
99 if (!graph->IsOsrMode()) {
100 return false;
101 }
102 MarkerHolder marker(graph);
103 return FindBlockBetween<IsOsrEntryBlock>(dominateInst->GetBasicBlock(), bb, marker.GetMarker());
104 }
105
HasOsrEntryBetween(BasicBlock * dominateBb,BasicBlock * bb)106 bool HasOsrEntryBetween(BasicBlock *dominateBb, BasicBlock *bb)
107 {
108 ASSERT(dominateBb->IsDominate(bb));
109 auto graph = bb->GetGraph();
110 if (!graph->IsOsrMode()) {
111 return false;
112 }
113 MarkerHolder marker(graph);
114 return FindBlockBetween<IsOsrEntryBlock>(dominateBb, bb, marker.GetMarker());
115 }
116
HasTryBlockBetween(Inst * dominateInst,Inst * inst)117 bool HasTryBlockBetween(Inst *dominateInst, Inst *inst)
118 {
119 ASSERT(dominateInst->IsDominate(inst));
120 auto bb = inst->GetBasicBlock();
121 MarkerHolder marker(bb->GetGraph());
122 return FindBlockBetween<IsTryBlock>(dominateInst->GetBasicBlock(), bb, marker.GetMarker());
123 }
124
InstStoredValue(Inst * inst,Inst ** secondValue)125 Inst *InstStoredValue(Inst *inst, Inst **secondValue)
126 {
127 ASSERT_PRINT(inst->IsStore(), "Attempt to take a stored value on non-store instruction");
128 Inst *val = nullptr;
129 *secondValue = nullptr;
130 switch (inst->GetOpcode()) {
131 case Opcode::StoreArray:
132 case Opcode::StoreObject:
133 case Opcode::StoreStatic:
134 case Opcode::StoreArrayI:
135 case Opcode::Store:
136 case Opcode::StoreI:
137 case Opcode::StoreObjectDynamic:
138 // Last input is a stored value
139 val = inst->GetInput(inst->GetInputsCount() - 1).GetInst();
140 break;
141 case Opcode::StoreResolvedObjectField:
142 case Opcode::StoreResolvedObjectFieldStatic:
143 val = inst->GetInput(1).GetInst();
144 break;
145 case Opcode::UnresolvedStoreStatic:
146 val = inst->GetInput(0).GetInst();
147 break;
148 case Opcode::StoreArrayPair:
149 case Opcode::StoreArrayPairI: {
150 val = inst->GetInput(inst->GetInputsCount() - 2U).GetInst();
151 auto secondInst = inst->GetInput(inst->GetInputsCount() - 1U).GetInst();
152 *secondValue = inst->GetDataFlowInput(secondInst);
153 break;
154 }
155 case Opcode::FillConstArray: {
156 return nullptr;
157 }
158 // Unhandled store instructions has been met
159 default:
160 UNREACHABLE();
161 }
162 return inst->GetDataFlowInput(val);
163 }
164
InstStoredValue(Inst * inst)165 Inst *InstStoredValue(Inst *inst)
166 {
167 Inst *secondValue = nullptr;
168 Inst *val = InstStoredValue(inst, &secondValue);
169 ASSERT(secondValue == nullptr);
170 return val;
171 }
172
CopySaveState(Graph * graph,SaveStateInst * inst)173 SaveStateInst *CopySaveState(Graph *graph, SaveStateInst *inst)
174 {
175 auto copy = static_cast<SaveStateInst *>(inst->Clone(graph));
176 ASSERT(copy->GetCallerInst() == inst->GetCallerInst());
177 for (size_t inputIdx = 0; inputIdx < inst->GetInputsCount(); inputIdx++) {
178 copy->AppendInput(inst->GetInput(inputIdx));
179 copy->SetVirtualRegister(inputIdx, inst->GetVirtualRegister(inputIdx));
180 }
181 copy->SetLinearNumber(inst->GetLinearNumber());
182 return copy;
183 }
184
185 template <typename T>
CanArrayAccessBeImplicit(T * array,RuntimeInterface * runtime)186 bool CanArrayAccessBeImplicit(T *array, RuntimeInterface *runtime)
187 {
188 size_t index = array->GetImm();
189 auto arch = array->GetBasicBlock()->GetGraph()->GetArch();
190 size_t offset = runtime->GetArrayDataOffset(arch) + (index << DataType::ShiftByType(array->GetType(), arch));
191 return offset < runtime->GetProtectedMemorySize();
192 }
193
IsSuitableForImplicitNullCheck(const Inst * inst)194 bool IsSuitableForImplicitNullCheck(const Inst *inst)
195 {
196 auto graph = inst->GetBasicBlock()->GetGraph();
197 auto runtime = graph->GetRuntime();
198 size_t maxOffset = runtime->GetProtectedMemorySize();
199 switch (inst->GetOpcode()) {
200 case Opcode::LoadArray:
201 case Opcode::StoreArray:
202 case Opcode::LoadArrayPair:
203 case Opcode::StoreArrayPair: {
204 // we don't know array index, so offset can be more than protected memory
205 return false;
206 }
207 case Opcode::LoadArrayI: {
208 ASSERT(inst->CastToLoadArrayI()->IsArray() || !runtime->IsCompressedStringsEnabled());
209
210 auto instLoadArrayI = inst->CastToLoadArrayI();
211 auto arch = graph->GetArch();
212
213 size_t dataOffset =
214 instLoadArrayI->IsArray() ? runtime->GetArrayDataOffset(arch) : runtime->GetStringDataOffset(arch);
215 size_t shift = DataType::ShiftByType(inst->GetType(), arch);
216 size_t offset = dataOffset + (instLoadArrayI->GetImm() << shift);
217 return offset < maxOffset;
218 }
219 case Opcode::LenArray:
220 return true;
221 case Opcode::LoadObject: {
222 auto loadObj = inst->CastToLoadObject();
223 return GetObjectOffset(graph, loadObj->GetObjectType(), loadObj->GetObjField(), loadObj->GetTypeId()) <
224 maxOffset;
225 }
226 case Opcode::StoreObject: {
227 auto storeObj = inst->CastToStoreObject();
228 return GetObjectOffset(graph, storeObj->GetObjectType(), storeObj->GetObjField(), storeObj->GetTypeId()) <
229 maxOffset;
230 }
231 case Opcode::StoreArrayI:
232 return CanArrayAccessBeImplicit(inst->CastToStoreArrayI(), runtime);
233 case Opcode::LoadArrayPairI:
234 return CanArrayAccessBeImplicit(inst->CastToLoadArrayPairI(), runtime);
235 case Opcode::StoreArrayPairI:
236 return CanArrayAccessBeImplicit(inst->CastToStoreArrayPairI(), runtime);
237
238 default:
239 return false;
240 }
241 }
242
IsInstNotNull(const Inst * inst)243 bool IsInstNotNull(const Inst *inst)
244 {
245 // Allocations cannot return null pointer
246 if (inst->IsAllocation() || inst->IsNullCheck()) {
247 return true;
248 }
249 if (inst->IsParameter() && inst->CastToParameter()->GetArgNumber() == 0) {
250 auto graph = inst->GetBasicBlock()->GetGraph();
251 // The object is not null if object is first parameter and the method is virtual.
252 return !graph->GetRuntime()->IsMethodStatic(graph->GetMethod());
253 }
254 return false;
255 }
256
FindObjectInSaveState(Inst * object,Inst * ss)257 static bool FindObjectInSaveState(Inst *object, Inst *ss)
258 {
259 if (!object->IsMovableObject()) {
260 return true;
261 }
262 while (ss != nullptr && object->IsDominate(ss)) {
263 auto it = std::find_if(ss->GetInputs().begin(), ss->GetInputs().end(),
264 [object, ss](Input input) { return ss->GetDataFlowInput(input.GetInst()) == object; });
265 if (it != ss->GetInputs().end()) {
266 return true;
267 }
268 auto caller = static_cast<SaveStateInst *>(ss)->GetCallerInst();
269 if (caller == nullptr) {
270 break;
271 }
272 ss = caller->GetSaveState();
273 }
274 return false;
275 }
276
277 // Returns true if GC can be triggered at this point
IsSaveStateForGc(const Inst * inst)278 static bool IsSaveStateForGc(const Inst *inst)
279 {
280 if (inst->GetOpcode() == Opcode::SafePoint) {
281 return true;
282 }
283 if (inst->GetOpcode() == Opcode::SaveState) {
284 for (auto &user : inst->GetUsers()) {
285 if (user.GetInst()->IsRuntimeCall()) {
286 return true;
287 }
288 }
289 }
290 return false;
291 }
292
FindAndRemindObjectInSaveState(Inst * object,Inst * inst,Inst ** failedSs)293 bool FindAndRemindObjectInSaveState(Inst *object, Inst *inst, Inst **failedSs)
294 {
295 if (IsSaveStateForGc(inst) && !FindObjectInSaveState(object, inst)) {
296 if (failedSs != nullptr) {
297 *failedSs = inst;
298 }
299 return false;
300 }
301 return true;
302 }
303
304 // Checks if object is correctly used in SaveStates between it and user
CheckObjectRec(Inst * object,const Inst * user,const BasicBlock * block,Inst * startFrom,Marker visited,Inst ** failedSs)305 bool CheckObjectRec(Inst *object, const Inst *user, const BasicBlock *block, Inst *startFrom, Marker visited,
306 Inst **failedSs)
307 {
308 if (startFrom != nullptr) {
309 auto it = InstSafeIterator<IterationType::ALL, IterationDirection::BACKWARD>(*block, startFrom);
310 for (; it != block->AllInstsSafeReverse().end(); ++it) {
311 auto inst = *it;
312 if (inst == nullptr) {
313 break;
314 }
315 if (inst->SetMarker(visited) || inst == object || inst == user) {
316 return true;
317 }
318 if (!FindAndRemindObjectInSaveState(object, inst, failedSs)) {
319 return false;
320 }
321 }
322 }
323 for (auto pred : block->GetPredsBlocks()) {
324 // Catch-begin block has edge from try-end block, and all try-blocks should be visited from this edge.
325 // `object` can be placed inside try-block - after try-begin, so that visiting try-begin is wrong
326 if (block->IsCatchBegin() && pred->IsTryBegin()) {
327 continue;
328 }
329 if (!CheckObjectRec(object, user, pred, pred->GetLastInst(), visited, failedSs)) {
330 return false;
331 }
332 }
333 return true;
334 }
335
336 // Checks if input edges of phi_block come from different branches of dominating if_imm instruction
337 // Returns true if the first input is in true branch, false if it is in false branch, and std::nullopt
338 // if branches intersect
IsIfInverted(BasicBlock * phiBlock,IfImmInst * ifImm)339 std::optional<bool> IsIfInverted(BasicBlock *phiBlock, IfImmInst *ifImm)
340 {
341 auto ifBlock = ifImm->GetBasicBlock();
342 ASSERT(ifBlock == phiBlock->GetDominator());
343 ASSERT(phiBlock->GetPredsBlocks().size() == MAX_SUCCS_NUM);
344 auto trueBb = ifImm->GetEdgeIfInputTrue();
345 auto falseBb = ifImm->GetEdgeIfInputFalse();
346 auto pred0 = phiBlock->GetPredecessor(0);
347 auto pred1 = phiBlock->GetPredecessor(1);
348
349 // Triangle case: phi block is the first in true branch
350 if (trueBb == phiBlock && falseBb->GetPredsBlocks().size() == 1) {
351 return pred0 != ifBlock;
352 }
353 // Triangle case: phi block is the first in false branch
354 if (falseBb == phiBlock && trueBb->GetPredsBlocks().size() == 1) {
355 return pred0 == ifBlock;
356 }
357 // If true_bb has more than one predecessor, there can be a path from false_bb
358 // to true_bb avoiding if_imm
359 if (trueBb->GetPredsBlocks().size() > 1 || falseBb->GetPredsBlocks().size() > 1) {
360 return std::nullopt;
361 }
362 // Every path through first input edge to phi_block comes from true branch
363 // Every path through second input edge to phi_block comes from false branch
364 if (trueBb->IsDominate(pred0) && falseBb->IsDominate(pred1)) {
365 return false;
366 }
367 // Every path through first input edge to phi_block comes from false branch
368 // Every path through second input edge to phi_block comes from true branch
369 if (falseBb->IsDominate(pred0) && trueBb->IsDominate(pred1)) {
370 return true;
371 }
372 // True and false branches intersect
373 return std::nullopt;
374 }
SearchMissingObjInSaveStates(Graph * graph,Inst * source,Inst * target,Inst * stopSearch,BasicBlock * targetBlock)375 ArenaVector<Inst *> *SaveStateBridgesBuilder::SearchMissingObjInSaveStates(Graph *graph, Inst *source, Inst *target,
376 Inst *stopSearch, BasicBlock *targetBlock)
377 {
378 ASSERT(graph != nullptr);
379 ASSERT(source != nullptr);
380 ASSERT(targetBlock != nullptr);
381 ASSERT(source->IsMovableObject());
382
383 if (bridges_ == nullptr) {
384 auto adapter = graph->GetLocalAllocator();
385 bridges_ = adapter->New<ArenaVector<Inst *>>(adapter->Adapter());
386 } else {
387 bridges_->clear();
388 }
389 auto visited = graph->NewMarker();
390 SearchSSOnWay(targetBlock, target, source, visited, bridges_, stopSearch);
391 graph->EraseMarker(visited);
392 return bridges_;
393 }
394
SearchSSOnWay(BasicBlock * block,Inst * startFrom,Inst * sourceInst,Marker visited,ArenaVector<Inst * > * bridges,Inst * stopSearch)395 void SaveStateBridgesBuilder::SearchSSOnWay(BasicBlock *block, Inst *startFrom, Inst *sourceInst, Marker visited,
396 ArenaVector<Inst *> *bridges, Inst *stopSearch)
397 {
398 ASSERT(block != nullptr);
399 ASSERT(sourceInst != nullptr);
400 ASSERT(bridges != nullptr);
401
402 if (startFrom != nullptr) {
403 auto it = InstSafeIterator<IterationType::ALL, IterationDirection::BACKWARD>(*block, startFrom);
404 for (; it != block->AllInstsSafeReverse().end(); ++it) {
405 auto inst = *it;
406 if (inst == nullptr) {
407 break;
408 }
409 COMPILER_LOG(DEBUG, BRIDGES_SS) << " See inst" << *inst;
410
411 if (inst->SetMarker(visited)) {
412 return;
413 }
414 if (IsSaveStateForGc(inst)) {
415 COMPILER_LOG(DEBUG, BRIDGES_SS) << "\tSearch in SS";
416 SearchInSaveStateAndFillBridgeVector(inst, sourceInst, bridges);
417 }
418 // When "stop_search" is nullptr second clause never causes early exit here
419 if (inst == sourceInst || inst == stopSearch) {
420 return;
421 }
422 }
423 }
424 for (auto pred : block->GetPredsBlocks()) {
425 SearchSSOnWay(pred, pred->GetLastInst(), sourceInst, visited, bridges, stopSearch);
426 }
427 }
428
SearchInSaveStateAndFillBridgeVector(Inst * inst,Inst * searchedInst,ArenaVector<Inst * > * bridges)429 void SaveStateBridgesBuilder::SearchInSaveStateAndFillBridgeVector(Inst *inst, Inst *searchedInst,
430 ArenaVector<Inst *> *bridges)
431 {
432 ASSERT(inst != nullptr);
433 ASSERT(searchedInst != nullptr);
434 ASSERT(bridges != nullptr);
435 auto user = std::find_if(inst->GetInputs().begin(), inst->GetInputs().end(), [searchedInst, inst](Input input) {
436 return inst->GetDataFlowInput(input.GetInst()) == searchedInst;
437 });
438 if (user == inst->GetInputs().end()) {
439 COMPILER_LOG(DEBUG, BRIDGES_SS) << "\tNot found";
440 bridges->push_back(inst);
441 }
442 }
443
FixUsagePhiInBB(BasicBlock * block,Inst * inst)444 void SaveStateBridgesBuilder::FixUsagePhiInBB(BasicBlock *block, Inst *inst)
445 {
446 ASSERT(block != nullptr);
447 ASSERT(inst != nullptr);
448 if (inst->IsMovableObject()) {
449 for (auto &user : inst->GetUsers()) {
450 auto targetInst = user.GetInst();
451 COMPILER_LOG(DEBUG, BRIDGES_SS) << " Check usage: Try to do SSB for inst: " << inst->GetId() << "\t"
452 << " For target inst: " << targetInst->GetId() << "\n";
453 // If inst usage in other BB than in all case object must exist until the end of the BB
454 if (targetInst->IsPhi() || targetInst->GetBasicBlock() != block) {
455 targetInst = block->GetLastInst();
456 }
457 SearchAndCreateMissingObjInSaveState(block->GetGraph(), inst, targetInst, block->GetFirstInst());
458 }
459 }
460 }
461
FixUsageInstInOtherBB(BasicBlock * block,Inst * inst)462 void SaveStateBridgesBuilder::FixUsageInstInOtherBB(BasicBlock *block, Inst *inst)
463 {
464 ASSERT(block != nullptr);
465 ASSERT(inst != nullptr);
466 if (inst->IsMovableObject()) {
467 for (auto &user : inst->GetUsers()) {
468 auto targetInst = user.GetInst();
469 // This way "in same block" checked when we saw inputs of instructions
470 if (targetInst->GetBasicBlock() == block) {
471 continue;
472 }
473 COMPILER_LOG(DEBUG, BRIDGES_SS) << " Check inputs: Try to do SSB for real source inst: " << *inst << "\n"
474 << " For target inst: " << *targetInst << "\n";
475 // If inst usage in other BB than in all case object must must exist until the end of the BB
476 targetInst = block->GetLastInst();
477 SearchAndCreateMissingObjInSaveState(block->GetGraph(), inst, targetInst, block->GetFirstInst());
478 }
479 }
480 }
481
DeleteUnrealObjInSaveState(Inst * ss)482 void SaveStateBridgesBuilder::DeleteUnrealObjInSaveState(Inst *ss)
483 {
484 ASSERT(ss != nullptr);
485 size_t indexInput = 0;
486 for (auto &input : ss->GetInputs()) {
487 // If the user of SS before inst
488 auto inputInst = input.GetInst();
489 if (ss->GetBasicBlock() == inputInst->GetBasicBlock() && ss->IsDominate(inputInst)) {
490 ss->RemoveInput(indexInput);
491 COMPILER_LOG(DEBUG, BRIDGES_SS) << " Fixed incorrect user in ss: " << ss->GetId() << " "
492 << " deleted input: " << inputInst->GetId() << "\n";
493 }
494 indexInput++;
495 }
496 }
497
FixSaveStatesInBB(BasicBlock * block)498 void SaveStateBridgesBuilder::FixSaveStatesInBB(BasicBlock *block)
499 {
500 ASSERT(block != nullptr);
501 bool blockInLoop = !(block->GetLoop()->IsRoot());
502 // Check usage ".ref" PHI inst
503 for (auto phi : block->PhiInsts()) {
504 FixUsagePhiInBB(block, phi);
505 }
506 // Check all insts
507 for (auto inst : block->Insts()) {
508 if (IsSaveStateForGc(inst)) {
509 DeleteUnrealObjInSaveState(inst);
510 }
511 // Check reference inputs of instructions
512 for (auto &input : inst->GetInputs()) {
513 // We record the original object in SaveState without checks
514 auto realSourceInst = inst->GetDataFlowInput(input.GetInst());
515 if (!realSourceInst->IsMovableObject()) {
516 continue;
517 }
518 // In case, when usege of object in loop and defenition is not in loop or usage's loop inside defenition's
519 // loop, we should check SaveStates till the end of BasicBlock
520 if (blockInLoop && (block->GetLoop()->IsInside(realSourceInst->GetBasicBlock()->GetLoop()))) {
521 COMPILER_LOG(DEBUG, BRIDGES_SS)
522 << " Check inputs: Try to do SSB for real source inst: " << *realSourceInst << "\n"
523 << " Block in loop: " << block->GetLoop() << " So target is end of BB:" << *(block->GetLastInst())
524 << "\n";
525 SearchAndCreateMissingObjInSaveState(block->GetGraph(), realSourceInst, block->GetLastInst(),
526 block->GetFirstInst());
527 } else {
528 COMPILER_LOG(DEBUG, BRIDGES_SS)
529 << " Check inputs: Try to do SSB for real source inst: " << *realSourceInst << "\n"
530 << " For target inst: " << *inst << "\n";
531 SearchAndCreateMissingObjInSaveState(block->GetGraph(), realSourceInst, inst, block->GetFirstInst());
532 }
533 }
534 // Check usage reference instruction
535 FixUsageInstInOtherBB(block, inst);
536 }
537 }
538
IsSaveStateForGc(Inst * inst)539 bool SaveStateBridgesBuilder::IsSaveStateForGc(Inst *inst)
540 {
541 return inst->GetOpcode() == Opcode::SafePoint || inst->GetOpcode() == Opcode::SaveState;
542 }
543
CreateBridgeInSS(Inst * source,ArenaVector<Inst * > * bridges)544 void SaveStateBridgesBuilder::CreateBridgeInSS(Inst *source, ArenaVector<Inst *> *bridges)
545 {
546 ASSERT(bridges != nullptr);
547 ASSERT(source != nullptr);
548 ASSERT(source->IsMovableObject());
549
550 for (Inst *ss : *bridges) {
551 static_cast<SaveStateInst *>(ss)->AppendBridge(source);
552 }
553 }
554
SearchAndCreateMissingObjInSaveState(Graph * graph,Inst * source,Inst * target,Inst * stopSearchInst,BasicBlock * targetBlock)555 void SaveStateBridgesBuilder::SearchAndCreateMissingObjInSaveState(Graph *graph, Inst *source, Inst *target,
556 Inst *stopSearchInst, BasicBlock *targetBlock)
557 {
558 ASSERT(graph != nullptr);
559 ASSERT(source != nullptr);
560 ASSERT(source->IsMovableObject());
561
562 if (graph->IsBytecodeOptimizer()) {
563 return; // SaveState bridges useless when bytecode optimizer enabled.
564 }
565
566 if (targetBlock == nullptr) {
567 ASSERT(target != nullptr);
568 targetBlock = target->GetBasicBlock();
569 } else {
570 ASSERT(target == targetBlock->GetLastInst());
571 }
572 auto bridges = SearchMissingObjInSaveStates(graph, source, target, stopSearchInst, targetBlock);
573 if (!bridges->empty()) {
574 CreateBridgeInSS(source, bridges);
575 COMPILER_LOG(DEBUG, BRIDGES_SS) << " Created bridge(s)";
576 }
577 }
578
ProcessSSUserPreds(Graph * graph,Inst * inst,Inst * targetInst)579 void SaveStateBridgesBuilder::ProcessSSUserPreds(Graph *graph, Inst *inst, Inst *targetInst)
580 {
581 for (auto predBlock : targetInst->GetBasicBlock()->GetPredsBlocks()) {
582 if (targetInst->CastToPhi()->GetPhiInput(predBlock) == inst) {
583 SearchAndCreateMissingObjInSaveState(graph, inst, predBlock->GetLastInst(), nullptr, predBlock);
584 }
585 }
586 }
587
FixInstUsageInSS(Graph * graph,Inst * inst)588 void SaveStateBridgesBuilder::FixInstUsageInSS(Graph *graph, Inst *inst)
589 {
590 if (!inst->IsMovableObject()) {
591 return;
592 }
593 for (auto &user : inst->GetUsers()) {
594 auto targetInst = user.GetInst();
595 COMPILER_LOG(DEBUG, BRIDGES_SS) << " Check usage: Try to do SSB for real source inst: " << *inst << "\n"
596 << " For target inst: " << *targetInst << "\n";
597 if (targetInst->IsPhi() && !(graph->IsAnalysisValid<DominatorsTree>() && inst->IsDominate(targetInst))) {
598 ProcessSSUserPreds(graph, inst, targetInst);
599 } else {
600 SearchAndCreateMissingObjInSaveState(graph, inst, targetInst);
601 }
602 }
603 }
604
605 // Check instructions don't have their own vregs and thus are not added in SaveStates,
606 // but newly added Phi instructions with check inputs should be added
FixPhisWithCheckInputs(BasicBlock * block)607 void SaveStateBridgesBuilder::FixPhisWithCheckInputs(BasicBlock *block)
608 {
609 if (block == nullptr) {
610 return;
611 }
612 auto graph = block->GetGraph();
613 for (auto phi : block->PhiInsts()) {
614 if (!phi->IsMovableObject()) {
615 continue;
616 }
617 for (auto &input : phi->GetInputs()) {
618 if (input.GetInst()->IsCheck()) {
619 FixInstUsageInSS(graph, phi);
620 break;
621 }
622 }
623 }
624 }
625
DumpBridges(std::ostream & out,Inst * source,ArenaVector<Inst * > * bridges)626 void SaveStateBridgesBuilder::DumpBridges(std::ostream &out, Inst *source, ArenaVector<Inst *> *bridges)
627 {
628 ASSERT(source != nullptr);
629 ASSERT(bridges != nullptr);
630 out << "Inst id " << source->GetId() << " with type ";
631 source->DumpOpcode(&out);
632 out << "need bridge in SS id: ";
633 for (auto ss : *bridges) {
634 out << ss->GetId() << " ";
635 }
636 out << '\n';
637 }
638
StoreValueCanBeObject(Inst * inst)639 bool StoreValueCanBeObject(Inst *inst)
640 {
641 switch (inst->GetOpcode()) {
642 case Opcode::CastValueToAnyType: {
643 auto type = AnyBaseTypeToDataType(inst->CastToCastValueToAnyType()->GetAnyType());
644 return (type == DataType::ANY || type == DataType::REFERENCE);
645 }
646 case Opcode::Constant:
647 return false;
648 default:
649 return true;
650 }
651 }
652
IsConditionEqual(const Inst * inst0,const Inst * inst1,bool inverted)653 bool IsConditionEqual(const Inst *inst0, const Inst *inst1, bool inverted)
654 {
655 if (inst0->GetOpcode() != inst1->GetOpcode()) {
656 return false;
657 }
658 if (inst0->GetOpcode() != Opcode::IfImm) {
659 // investigate why Opcode::If cannot be lowered to Opcode::IfImm and support it if needed
660 return false;
661 }
662 auto ifImm0 = inst0->CastToIfImm();
663 auto ifImm1 = inst1->CastToIfImm();
664 auto opcode = ifImm0->GetInput(0).GetInst()->GetOpcode();
665 if (opcode != ifImm1->GetInput(0).GetInst()->GetOpcode()) {
666 return false;
667 }
668 if (ifImm0->GetImm() != 0 && ifImm0->GetImm() != 1) {
669 return false;
670 }
671 if (ifImm1->GetImm() != 0 && ifImm1->GetImm() != 1) {
672 return false;
673 }
674 if (ifImm0->GetImm() != ifImm1->GetImm()) {
675 inverted = !inverted;
676 }
677 if (opcode != Opcode::Compare) {
678 if (ifImm0->GetInput(0).GetInst() != ifImm1->GetInput(0).GetInst()) {
679 return false;
680 }
681 auto cc = inverted ? GetInverseConditionCode(ifImm0->GetCc()) : ifImm0->GetCc();
682 return cc == ifImm1->GetCc();
683 }
684 auto cmp0 = ifImm0->GetInput(0).GetInst()->CastToCompare();
685 auto cmp1 = ifImm1->GetInput(0).GetInst()->CastToCompare();
686 if (cmp0->GetInput(0).GetInst() == cmp1->GetInput(0).GetInst() &&
687 cmp0->GetInput(1).GetInst() == cmp1->GetInput(1).GetInst()) {
688 if (GetInverseConditionCode(ifImm0->GetCc()) == ifImm1->GetCc()) {
689 inverted = !inverted;
690 } else if (ifImm0->GetCc() != ifImm1->GetCc()) {
691 return false;
692 }
693 auto cc = inverted ? GetInverseConditionCode(cmp0->GetCc()) : cmp0->GetCc();
694 return cc == cmp1->GetCc();
695 }
696 return false;
697 }
698
CleanupGraphSaveStateOSR(Graph * graph)699 void CleanupGraphSaveStateOSR(Graph *graph)
700 {
701 ASSERT(graph != nullptr);
702 ASSERT(graph->IsOsrMode());
703 graph->InvalidateAnalysis<LoopAnalyzer>();
704 graph->RunPass<LoopAnalyzer>();
705 for (auto block : graph->GetBlocksRPO()) {
706 if (block->IsOsrEntry() && !block->IsLoopHeader()) {
707 auto firstInst = block->GetFirstInst();
708 if (firstInst == nullptr) {
709 continue;
710 }
711 if (firstInst->GetOpcode() == Opcode::SaveStateOsr) {
712 block->RemoveInst(firstInst);
713 block->SetOsrEntry(false);
714 }
715 }
716 }
717 }
718
719 template <typename T>
FindInstBetween(Inst * domInst,BasicBlock * currentBb,Marker marker)720 bool FindInstBetween(Inst *domInst, BasicBlock *currentBb, Marker marker)
721 {
722 if (currentBb->SetMarker(marker)) {
723 return false;
724 }
725 bool isSameBlock = domInst->GetBasicBlock() == currentBb;
726 auto currInst = currentBb->GetLastInst();
727 Inst *finish = isSameBlock ? domInst : nullptr;
728 while (currInst != finish) {
729 if (T()(currInst)) {
730 return true;
731 }
732 currInst = currInst->GetPrev();
733 }
734 if (isSameBlock) {
735 return false;
736 }
737 for (auto pred : currentBb->GetPredsBlocks()) {
738 if (FindInstBetween<T>(domInst, pred, marker)) {
739 return true;
740 }
741 }
742 return false;
743 }
744
745 template bool HasSaveStateBetween<IsSaveState>(Inst *dom_inst, Inst *inst);
746 template bool HasSaveStateBetween<IsSaveStateCanTriggerGc>(Inst *dom_inst, Inst *inst);
747
748 template <typename T>
HasSaveStateBetween(Inst * domInst,Inst * inst)749 bool HasSaveStateBetween(Inst *domInst, Inst *inst)
750 {
751 ASSERT(domInst->IsDominate(inst));
752 if (domInst == inst) {
753 return false;
754 }
755 auto bb = inst->GetBasicBlock();
756 bool isSameBlock = domInst->GetBasicBlock() == bb;
757 auto currInst = inst->GetPrev();
758 Inst *finish = isSameBlock ? domInst : nullptr;
759 while (currInst != finish) {
760 if (T()(currInst)) {
761 return true;
762 }
763 currInst = currInst->GetPrev();
764 }
765 if (isSameBlock) {
766 return false;
767 }
768 MarkerHolder marker(bb->GetGraph());
769 for (auto pred : bb->GetPredsBlocks()) {
770 if (FindInstBetween<T>(domInst, pred, marker.GetMarker())) {
771 return true;
772 }
773 }
774 return false;
775 }
776
Append(Inst * inst)777 void InstAppender::Append(Inst *inst)
778 {
779 if (prev_ == nullptr) {
780 block_->AppendInst(inst);
781 } else {
782 block_->InsertAfter(inst, prev_);
783 }
784 prev_ = inst;
785 }
786
Append(std::initializer_list<Inst * > instructions)787 void InstAppender::Append(std::initializer_list<Inst *> instructions)
788 {
789 for (auto *inst : instructions) {
790 Append(inst);
791 }
792 }
793
794 } // namespace panda::compiler
795