• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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