• 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 ark::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 class IsSaveStateCanTriggerGc {
51 public:
operator ()(const Inst * inst) const52     bool operator()(const Inst *inst) const
53     {
54         return IsSaveStateForGc(inst);
55     }
56 };
57 
58 template <typename T>
FindBlockBetween(BasicBlock * dominateBb,BasicBlock * currentBb,Marker marker)59 bool FindBlockBetween(BasicBlock *dominateBb, BasicBlock *currentBb, Marker marker)
60 {
61     if (dominateBb == currentBb) {
62         return false;
63     }
64     if (currentBb->SetMarker(marker)) {
65         return false;
66     }
67     if (T()(currentBb)) {
68         return true;
69     }
70     for (auto pred : currentBb->GetPredsBlocks()) {
71         if (FindBlockBetween<T>(dominateBb, pred, marker)) {
72             return true;
73         }
74     }
75     return false;
76 }
77 
GetClassPtrForObject(Inst * inst,size_t inputNum)78 RuntimeInterface::ClassPtr GetClassPtrForObject(Inst *inst, size_t inputNum)
79 {
80     auto objInst = inst->GetDataFlowInput(inputNum);
81     if (objInst->GetOpcode() != Opcode::NewObject) {
82         return nullptr;
83     }
84     return GetObjectClass(objInst);
85 }
86 
GetClass(Inst * inst)87 RuntimeInterface::ClassPtr GetClass(Inst *inst)
88 {
89     if (inst->IsClassInst()) {
90         return static_cast<ClassInst *>(inst)->GetClass();
91     }
92     if (inst->GetOpcode() == Opcode::LoadImmediate) {
93         return inst->CastToLoadImmediate()->GetClass();
94     }
95     if (inst->GetOpcode() == Opcode::LoadRuntimeClass) {
96         return inst->CastToLoadRuntimeClass()->GetClass();
97     }
98     if (inst->IsPhi()) {
99         auto graph = inst->GetBasicBlock()->GetGraph();
100         graph->RunPass<ObjectTypePropagation>();
101         auto typeInfo = inst->GetObjectTypeInfo();
102         if (typeInfo.IsValid() && typeInfo.IsExact()) {
103             return typeInfo.GetClass();
104         }
105         return nullptr;
106     }
107     UNREACHABLE();
108     return nullptr;
109 }
110 
GetObjectClass(Inst * inst)111 RuntimeInterface::ClassPtr GetObjectClass(Inst *inst)
112 {
113     ASSERT(inst->GetInputsCount() > 0);
114     return GetClass(inst->GetDataFlowInput(0));
115 }
116 
117 template bool HasOsrEntryBetween(Inst *dominate, Inst *current);
118 template bool HasOsrEntryBetween(BasicBlock *dominate, BasicBlock *current);
119 
120 template <typename T>
HasOsrEntryBetween(T * dominate,T * current)121 bool HasOsrEntryBetween(T *dominate, T *current)
122 {
123     ASSERT(dominate->IsDominate(current));
124     BasicBlock *dominateBb = nullptr;
125     BasicBlock *bb = nullptr;
126     if constexpr (std::is_same_v<T, Inst>) {
127         dominateBb = dominate->GetBasicBlock();
128         bb = current->GetBasicBlock();
129     } else if constexpr (std::is_same_v<T, BasicBlock>) {
130         dominateBb = dominate;
131         bb = current;
132     }
133     auto graph = bb->GetGraph();
134     if (!graph->IsOsrMode() && dominateBb->GetLoop() != bb->GetLoop()) {
135         return false;
136     }
137     MarkerHolder marker(graph);
138     return FindBlockBetween<IsOsrEntryBlock>(dominateBb, bb, marker.GetMarker());
139 }
140 
HasTryBlockBetween(Inst * dominateInst,Inst * inst)141 bool HasTryBlockBetween(Inst *dominateInst, Inst *inst)
142 {
143     ASSERT(dominateInst->IsDominate(inst));
144     auto bb = inst->GetBasicBlock();
145     MarkerHolder marker(bb->GetGraph());
146     return FindBlockBetween<IsTryBlock>(dominateInst->GetBasicBlock(), bb, marker.GetMarker());
147 }
148 
IsSbAppendStringIntrinsic(Inst * inst)149 bool IsSbAppendStringIntrinsic(Inst *inst)
150 {
151     if (UNLIKELY(!inst->IsIntrinsic())) {
152         return false;
153     }
154     auto id = inst->CastToIntrinsic()->GetIntrinsicId();
155     return inst->GetBasicBlock()->GetGraph()->GetRuntime()->IsIntrinsicStringBuilderAppendString(id);
156 }
157 
IntrinsicStoredValue(Inst * inst)158 Inst *IntrinsicStoredValue(Inst *inst)
159 {
160     ASSERT(inst->IsIntrinsic());
161     if (IsSbAppendStringIntrinsic(inst)) {
162         // input 0 - StringBuilder reference
163         // input 1 - String reference to be stored
164         return inst->GetInput(1).GetInst();
165     }
166     UNREACHABLE();
167     return nullptr;
168 }
169 
InstStoredValue(Inst * inst,Inst ** secondValue)170 Inst *InstStoredValue(Inst *inst, Inst **secondValue)
171 {
172     ASSERT_PRINT(inst->IsStore() || IsSbAppendStringIntrinsic(inst),
173                  "Attempt to take a stored value on non-store instruction");
174 
175     Inst *val = nullptr;
176     *secondValue = nullptr;
177     switch (inst->GetOpcode()) {
178         case Opcode::StoreArray:
179         case Opcode::StoreObject:
180         case Opcode::StoreStatic:
181         case Opcode::StoreArrayI:
182         case Opcode::Store:
183         case Opcode::StoreI:
184         case Opcode::StoreObjectDynamic:
185             // Last input is a stored value
186             val = inst->GetInput(inst->GetInputsCount() - 1).GetInst();
187             break;
188         case Opcode::StoreResolvedObjectField:
189         case Opcode::StoreResolvedObjectFieldStatic:
190             val = inst->GetInput(1).GetInst();
191             break;
192         case Opcode::UnresolvedStoreStatic:
193             val = inst->GetInput(0).GetInst();
194             break;
195         case Opcode::StoreArrayPair:
196         case Opcode::StoreArrayPairI: {
197             val = inst->GetInput(inst->GetInputsCount() - 2U).GetInst();
198             auto secondInst = inst->GetInput(inst->GetInputsCount() - 1U).GetInst();
199             *secondValue = inst->GetDataFlowInput(secondInst);
200             break;
201         }
202         case Opcode::StoreObjectPair: {
203             val = inst->GetInput(1).GetInst();
204             auto secondInst = inst->GetInput(2).GetInst();
205             *secondValue = inst->GetDataFlowInput(secondInst);
206             break;
207         }
208         case Opcode::Intrinsic: {
209             val = IntrinsicStoredValue(inst);
210             break;
211         }
212         case Opcode::FillConstArray: {
213             return nullptr;
214         }
215         // Unhandled store instructions has been met
216         default:
217             UNREACHABLE();
218     }
219     return inst->GetDataFlowInput(val);
220 }
221 
InstStoredValue(Inst * inst)222 Inst *InstStoredValue(Inst *inst)
223 {
224     Inst *secondValue = nullptr;
225     Inst *val = InstStoredValue(inst, &secondValue);
226     ASSERT(secondValue == nullptr);
227     return val;
228 }
229 
CopySaveState(Graph * graph,SaveStateInst * inst)230 SaveStateInst *CopySaveState(Graph *graph, SaveStateInst *inst)
231 {
232     auto copy = static_cast<SaveStateInst *>(inst->Clone(graph));
233     ASSERT(copy->GetCallerInst() == inst->GetCallerInst());
234     for (size_t inputIdx = 0; inputIdx < inst->GetInputsCount(); inputIdx++) {
235         copy->AppendInput(inst->GetInput(inputIdx));
236         copy->SetVirtualRegister(inputIdx, inst->GetVirtualRegister(inputIdx));
237     }
238     copy->SetLinearNumber(inst->GetLinearNumber());
239     return copy;
240 }
241 
242 template <typename T>
CanArrayAccessBeImplicit(T * array,RuntimeInterface * runtime)243 bool CanArrayAccessBeImplicit(T *array, RuntimeInterface *runtime)
244 {
245     size_t index = array->GetImm();
246     auto arch = array->GetBasicBlock()->GetGraph()->GetArch();
247     size_t offset = runtime->GetArrayDataOffset(arch) + (index << DataType::ShiftByType(array->GetType(), arch));
248     return offset < runtime->GetProtectedMemorySize();
249 }
250 
CanLoadArrayIBeImplicit(const LoadInstI * inst,const Graph * graph,RuntimeInterface * runtime,size_t maxoffset)251 bool CanLoadArrayIBeImplicit(const LoadInstI *inst, const Graph *graph, RuntimeInterface *runtime, size_t maxoffset)
252 {
253     ASSERT(inst->CastToLoadArrayI()->IsArray() || !runtime->IsCompressedStringsEnabled());
254     auto arch = graph->GetArch();
255     size_t dataoffset = inst->IsArray() ? runtime->GetArrayDataOffset(arch) : runtime->GetStringDataOffset(arch);
256     size_t shift = DataType::ShiftByType(inst->GetType(), arch);
257     size_t offset = dataoffset + (inst->GetImm() << shift);
258     return offset < maxoffset;
259 }
260 
261 template <typename T>
CanObjectAccessBeImplicit(T * object,const Graph * graph,size_t maxoffset)262 bool CanObjectAccessBeImplicit(T *object, const Graph *graph, size_t maxoffset)
263 {
264     return GetObjectOffset(graph, object->GetObjectType(), object->GetObjField(), object->GetTypeId()) < maxoffset;
265 }
266 
267 template <typename T>
CanObjectPairAccessBeImplicit(T * objpair,const Graph * graph,size_t maxoffset)268 bool CanObjectPairAccessBeImplicit(T *objpair, const Graph *graph, size_t maxoffset)
269 {
270     return GetObjectOffset(graph, objpair->GetObjectType(), objpair->GetObjField1(), objpair->GetTypeId1()) < maxoffset;
271 }
272 
CheckFcmpInputs(Inst * input0,Inst * input1)273 bool CheckFcmpInputs(Inst *input0, Inst *input1)
274 {
275     if (input0->GetOpcode() != Opcode::Cast || input1->GetOpcode() != Opcode::Cast) {
276         return false;
277     }
278     if (input0->CastToCast()->GetOperandsType() != DataType::INT32 ||
279         input1->CastToCast()->GetOperandsType() != DataType::INT32) {
280         return false;
281     }
282     return true;
283 }
284 
CheckFcmpWithConstInput(Inst * input0,Inst * input1)285 bool CheckFcmpWithConstInput(Inst *input0, Inst *input1)
286 {
287     if (input0->IsConst() && input1->GetOpcode() == Opcode::Cast &&
288         input1->CastToCast()->GetOperandsType() == DataType::INT32) {
289         return true;
290     }
291     if (input1->IsConst() && input0->GetOpcode() == Opcode::Cast &&
292         input0->CastToCast()->GetOperandsType() == DataType::INT32) {
293         return true;
294     }
295     return false;
296 }
297 
298 // Get power of 2
299 // if n not power of 2 return -1;
GetPowerOfTwo(uint64_t n)300 int64_t GetPowerOfTwo(uint64_t n)
301 {
302     if (!helpers::math::IsPowerOfTwo(n)) {
303         return -1;
304     }
305     return helpers::math::GetIntLog2(n);
306 }
307 
IsInputTypeMismatch(Inst * inst,int32_t inputIndex,Arch arch)308 bool IsInputTypeMismatch(Inst *inst, int32_t inputIndex, Arch arch)
309 {
310     auto inputInst = inst->GetInput(inputIndex).GetInst();
311     auto inputTypeSize = DataType::GetTypeSize(inputInst->GetType(), arch);
312     auto instInputSize = DataType::GetTypeSize(inst->GetInputType(inputIndex), arch);
313     return (inputTypeSize > instInputSize) ||
314            (inputTypeSize == instInputSize &&
315             DataType::IsTypeSigned(inputInst->GetType()) != DataType::IsTypeSigned(inst->GetInputType(inputIndex)));
316 }
317 
ApplyForCastJoin(Inst * cast,Inst * input,Inst * origInst,Arch arch)318 bool ApplyForCastJoin(Inst *cast, Inst *input, Inst *origInst, Arch arch)
319 {
320     auto inputTypeMismatch = IsInputTypeMismatch(cast, 0, arch);
321 #ifndef NDEBUG
322     ASSERT(!inputTypeMismatch);
323 #else
324     if (inputTypeMismatch) {
325         return false;
326     }
327 #endif
328     auto inputType = input->GetType();
329     auto inputTypeSize = DataType::GetTypeSize(inputType, arch);
330     auto currType = cast->GetType();
331     auto origType = origInst->GetType();
332     return DataType::GetCommonType(inputType) == DataType::INT64 &&
333            DataType::GetCommonType(currType) == DataType::INT64 &&
334            DataType::GetCommonType(origType) == DataType::INT64 &&
335            inputTypeSize > DataType::GetTypeSize(currType, arch) &&
336            DataType::GetTypeSize(origType, arch) > inputTypeSize;
337 }
338 
IsCastAllowedInBytecode(const Inst * inst)339 bool IsCastAllowedInBytecode(const Inst *inst)
340 {
341     auto type = inst->GetType();
342     switch (type) {
343         case DataType::Type::INT32:
344         case DataType::Type::INT64:
345         case DataType::Type::FLOAT32:
346         case DataType::Type::FLOAT64:
347             return true;
348         default:
349             return false;
350     }
351 }
352 
353 // OverflowCheck can be removed if all its indirect users truncate input to int32
CanRemoveOverflowCheck(Inst * inst,Marker marker)354 bool CanRemoveOverflowCheck(Inst *inst, Marker marker)
355 {
356     if (inst->SetMarker(marker)) {
357         return true;
358     }
359     if (inst->GetOpcode() == Opcode::AnyTypeCheck) {
360         auto anyTypeCheck = inst->CastToAnyTypeCheck();
361         auto graph = inst->GetBasicBlock()->GetGraph();
362         auto language = graph->GetRuntime()->GetMethodSourceLanguage(graph->GetMethod());
363         auto intAnyType = NumericDataTypeToAnyType(DataType::INT32, language);
364         // Bail out if this AnyTypeCheck can be triggered
365         if (IsAnyTypeCanBeSubtypeOf(language, anyTypeCheck->GetAnyType(), intAnyType,
366                                     anyTypeCheck->GetAllowedInputType()) != true) {
367             return false;
368         }
369     }
370     switch (inst->GetOpcode()) {
371         case Opcode::Shl:
372         case Opcode::Shr:
373         case Opcode::AShr:
374         case Opcode::And:
375         case Opcode::Or:
376         case Opcode::Xor:
377             return true;
378         // Next 3 opcodes should be removed in Peepholes and ChecksElimination, but Cast(f64).i32 or Or(x, 0)
379         // may be optimized out by that moment
380         case Opcode::CastAnyTypeValue:
381         case Opcode::CastValueToAnyType:
382         case Opcode::AnyTypeCheck:
383 
384         case Opcode::AddOverflowCheck:
385         case Opcode::SubOverflowCheck:
386         case Opcode::NegOverflowAndZeroCheck:
387         case Opcode::Phi:
388         // We check SaveState because if some of its users cannot be removed, result of OverflowCheck
389         // may be used in interpreter after deoptimization
390         case Opcode::SaveState:
391             for (auto &user : inst->GetUsers()) {
392                 auto userInst = user.GetInst();
393                 bool canRemove;
394                 if (DataType::IsFloatType(inst->GetType())) {
395                     canRemove = userInst->GetOpcode() == Opcode::Cast && userInst->CastToCast()->IsDynamicCast();
396                 } else {
397                     canRemove = CanRemoveOverflowCheck(userInst, marker);
398                 }
399                 if (!canRemove) {
400                     return false;
401                 }
402             }
403             return true;
404         default:
405             return false;
406     }
407 }
408 
IsSuitableForImplicitNullCheck(const Inst * inst)409 bool IsSuitableForImplicitNullCheck(const Inst *inst)
410 {
411     auto graph = inst->GetBasicBlock()->GetGraph();
412     auto runtime = graph->GetRuntime();
413     size_t maxOffset = runtime->GetProtectedMemorySize();
414     switch (inst->GetOpcode()) {
415         case Opcode::LoadArray:
416         case Opcode::StoreArray:
417         case Opcode::LoadArrayPair:
418         case Opcode::StoreArrayPair: {
419             // we don't know array index, so offset can be more than protected memory
420             return false;
421         }
422         case Opcode::LoadArrayI:
423             return CanLoadArrayIBeImplicit(inst->CastToLoadArrayI(), graph, runtime, maxOffset);
424         case Opcode::LenArray:
425             return true;
426         case Opcode::LoadObject:
427             return CanObjectAccessBeImplicit(inst->CastToLoadObject(), graph, maxOffset);
428         case Opcode::StoreObject:
429             return CanObjectAccessBeImplicit(inst->CastToStoreObject(), graph, maxOffset);
430         case Opcode::StoreArrayI:
431             return CanArrayAccessBeImplicit(inst->CastToStoreArrayI(), runtime);
432         case Opcode::LoadArrayPairI:
433             return CanArrayAccessBeImplicit(inst->CastToLoadArrayPairI(), runtime);
434         case Opcode::StoreArrayPairI:
435             return CanArrayAccessBeImplicit(inst->CastToStoreArrayPairI(), runtime);
436         case Opcode::LoadObjectPair:
437             return CanObjectPairAccessBeImplicit(inst->CastToLoadObjectPair(), graph, maxOffset);
438         case Opcode::StoreObjectPair:
439             return CanObjectPairAccessBeImplicit(inst->CastToStoreObjectPair(), graph, maxOffset);
440         default:
441             return false;
442     }
443 }
444 
IsInstNotNull(const Inst * inst)445 bool IsInstNotNull(const Inst *inst)
446 {
447     // Allocations cannot return null pointer
448     if (inst->IsAllocation() || inst->IsNullCheck() || inst->NoNullPtr()) {
449         return true;
450     }
451     if (inst->IsParameter() && inst->CastToParameter()->GetArgNumber() == 0) {
452         auto graph = inst->GetBasicBlock()->GetGraph();
453         // The object is not null if object is first parameter and the method is virtual.
454         return !graph->GetRuntime()->IsMethodStatic(graph->GetMethod());
455     }
456     return false;
457 }
458 
459 // Returns true if GC can be triggered at this point
IsSaveStateForGc(const Inst * inst)460 bool IsSaveStateForGc(const Inst *inst)
461 {
462     if (inst->GetOpcode() == Opcode::SafePoint) {
463         return true;
464     }
465     if (inst->GetOpcode() == Opcode::SaveState) {
466         for (auto &user : inst->GetUsers()) {
467             if (user.GetInst()->IsRuntimeCall()) {
468                 return true;
469             }
470         }
471     }
472     return false;
473 }
474 
475 // Checks if input edges of phi_block come from different branches of dominating if_imm instruction
476 // Returns true if the first input is in true branch, false if it is in false branch, and std::nullopt
477 // if branches intersect
IsIfInverted(BasicBlock * phiBlock,IfImmInst * ifImm)478 std::optional<bool> IsIfInverted(BasicBlock *phiBlock, IfImmInst *ifImm)
479 {
480     auto ifBlock = ifImm->GetBasicBlock();
481     ASSERT(ifBlock == phiBlock->GetDominator());
482     ASSERT(phiBlock->GetPredsBlocks().size() == MAX_SUCCS_NUM);
483     auto trueBb = ifImm->GetEdgeIfInputTrue();
484     auto falseBb = ifImm->GetEdgeIfInputFalse();
485     auto pred0 = phiBlock->GetPredecessor(0);
486     auto pred1 = phiBlock->GetPredecessor(1);
487 
488     // Triangle case: phi block is the first in true branch
489     if (trueBb == phiBlock && falseBb->GetPredsBlocks().size() == 1) {
490         return pred0 != ifBlock;
491     }
492     // Triangle case: phi block is the first in false branch
493     if (falseBb == phiBlock && trueBb->GetPredsBlocks().size() == 1) {
494         return pred0 == ifBlock;
495     }
496     // If true_bb has more than one predecessor, there can be a path from false_bb
497     // to true_bb avoiding if_imm
498     if (trueBb->GetPredsBlocks().size() > 1 || falseBb->GetPredsBlocks().size() > 1) {
499         return std::nullopt;
500     }
501     // Every path through first input edge to phi_block comes from true branch
502     // Every path through second input edge to phi_block comes from false branch
503     if (trueBb->IsDominate(pred0) && falseBb->IsDominate(pred1)) {
504         return false;
505     }
506     // Every path through first input edge to phi_block comes from false branch
507     // Every path through second input edge to phi_block comes from true branch
508     if (falseBb->IsDominate(pred0) && trueBb->IsDominate(pred1)) {
509         return true;
510     }
511     // True and false branches intersect
512     return std::nullopt;
513 }
514 
SearchMissingObjInSaveStates(Graph * graph,Inst * source,Inst * target,Inst * stopSearch,BasicBlock * targetBlock)515 ArenaVector<Inst *> *SaveStateBridgesBuilder::SearchMissingObjInSaveStates(Graph *graph, Inst *source, Inst *target,
516                                                                            Inst *stopSearch, BasicBlock *targetBlock)
517 {
518     ASSERT(graph != nullptr);
519     ASSERT(source != nullptr);
520     ASSERT(targetBlock != nullptr);
521     ASSERT(source->IsMovableObject());
522 
523     if (bridges_ == nullptr) {
524         auto adapter = graph->GetLocalAllocator();
525         bridges_ = adapter->New<ArenaVector<Inst *>>(adapter->Adapter());
526     } else {
527         bridges_->clear();
528     }
529     auto visited = graph->NewMarker();
530     SearchSSOnWay(targetBlock, target, source, visited, stopSearch);
531     graph->EraseMarker(visited);
532     return bridges_;
533 }
534 
SearchSSOnWay(BasicBlock * block,Inst * startFrom,Inst * sourceInst,Marker visited,Inst * stopSearch)535 void SaveStateBridgesBuilder::SearchSSOnWay(BasicBlock *block, Inst *startFrom, Inst *sourceInst, Marker visited,
536                                             Inst *stopSearch)
537 {
538     ASSERT(block != nullptr);
539     ASSERT(sourceInst != nullptr);
540     ASSERT(bridges_ != nullptr);
541 
542     if (startFrom != nullptr) {
543         auto it = InstSafeIterator<IterationType::ALL, IterationDirection::BACKWARD>(*block, startFrom);
544         for (; it != block->AllInstsSafeReverse().end(); ++it) {
545             auto inst = *it;
546             if (inst == nullptr) {
547                 break;
548             }
549             COMPILER_LOG(DEBUG, BRIDGES_SS) << " See inst" << *inst;
550 
551             if (inst->SetMarker(visited)) {
552                 return;
553             }
554             if (SaveStateBridgesBuilder::IsSaveStateForGc(inst)) {
555                 COMPILER_LOG(DEBUG, BRIDGES_SS) << "\tSearch in SS";
556                 SearchInSaveStateAndFillBridgeVector(inst, sourceInst);
557             }
558             // When "stop_search" is nullptr second clause never causes early exit here
559             if (inst == sourceInst || inst == stopSearch) {
560                 return;
561             }
562         }
563     }
564     for (auto pred : block->GetPredsBlocks()) {
565         SearchSSOnWay(pred, pred->GetLastInst(), sourceInst, visited, stopSearch);
566     }
567 }
568 
SearchInSaveStateAndFillBridgeVector(Inst * inst,Inst * searchedInst)569 void SaveStateBridgesBuilder::SearchInSaveStateAndFillBridgeVector(Inst *inst, Inst *searchedInst)
570 {
571     ASSERT(inst != nullptr);
572     ASSERT(searchedInst != nullptr);
573     ASSERT(bridges_ != nullptr);
574     auto user = std::find_if(inst->GetInputs().begin(), inst->GetInputs().end(), [searchedInst, inst](Input input) {
575         return inst->GetDataFlowInput(input.GetInst()) == searchedInst;
576     });
577     if (user == inst->GetInputs().end()) {
578         COMPILER_LOG(DEBUG, BRIDGES_SS) << "\tNot found";
579         bridges_->push_back(inst);
580     }
581 }
582 
FixUsagePhiInBB(BasicBlock * block,Inst * inst)583 void SaveStateBridgesBuilder::FixUsagePhiInBB(BasicBlock *block, Inst *inst)
584 {
585     ASSERT(block != nullptr);
586     ASSERT(inst != nullptr);
587     if (inst->IsMovableObject()) {
588         for (auto &user : inst->GetUsers()) {
589             auto targetInst = user.GetInst();
590             COMPILER_LOG(DEBUG, BRIDGES_SS) << " Check usage: Try to do SSB for inst: " << inst->GetId() << "\t"
591                                             << " For target inst: " << targetInst->GetId() << "\n";
592             // If inst usage in other BB than in all case object must exist until the end of the BB
593             if (targetInst->IsPhi() || targetInst->GetBasicBlock() != block) {
594                 targetInst = block->GetLastInst();
595             }
596             SearchAndCreateMissingObjInSaveState(block->GetGraph(), inst, targetInst, block->GetFirstInst());
597         }
598     }
599 }
600 
FixUsageInstInOtherBB(BasicBlock * block,Inst * inst)601 void SaveStateBridgesBuilder::FixUsageInstInOtherBB(BasicBlock *block, Inst *inst)
602 {
603     ASSERT(block != nullptr);
604     ASSERT(inst != nullptr);
605     if (inst->IsMovableObject()) {
606         for (auto &user : inst->GetUsers()) {
607             auto targetInst = user.GetInst();
608             // This way "in same block" checked when we saw inputs of instructions
609             if (targetInst->GetBasicBlock() == block) {
610                 continue;
611             }
612             COMPILER_LOG(DEBUG, BRIDGES_SS) << " Check inputs: Try to do SSB for real source inst: " << *inst << "\n"
613                                             << "  For target inst: " << *targetInst << "\n";
614             // If inst usage in other BB than in all case object must must exist until the end of the BB
615             targetInst = block->GetLastInst();
616             SearchAndCreateMissingObjInSaveState(block->GetGraph(), inst, targetInst, block->GetFirstInst());
617         }
618     }
619 }
620 
DeleteUnrealObjInSaveState(Inst * ss)621 void SaveStateBridgesBuilder::DeleteUnrealObjInSaveState(Inst *ss)
622 {
623     ASSERT(ss != nullptr);
624     size_t indexInput = 0;
625     for (auto &input : ss->GetInputs()) {
626         // If the user of SS before inst
627         auto inputInst = input.GetInst();
628         if (ss->GetBasicBlock() == inputInst->GetBasicBlock() && ss->IsDominate(inputInst)) {
629             ss->RemoveInput(indexInput);
630             COMPILER_LOG(DEBUG, BRIDGES_SS) << " Fixed incorrect user in ss: " << ss->GetId() << "  "
631                                             << " deleted input: " << inputInst->GetId() << "\n";
632         }
633         indexInput++;
634     }
635 }
636 
FixSaveStatesInBB(BasicBlock * block)637 void SaveStateBridgesBuilder::FixSaveStatesInBB(BasicBlock *block)
638 {
639     ASSERT(block != nullptr);
640     bool blockInLoop = !(block->GetLoop()->IsRoot());
641     // Check usage ".ref" PHI inst
642     for (auto phi : block->PhiInsts()) {
643         FixUsagePhiInBB(block, phi);
644     }
645     // Check all insts
646     for (auto inst : block->Insts()) {
647         if (IsSaveStateForGc(inst)) {
648             DeleteUnrealObjInSaveState(inst);
649         }
650         // Check reference inputs of instructions
651         for (auto &input : inst->GetInputs()) {
652             // We record the original object in SaveState without checks
653             auto realSourceInst = inst->GetDataFlowInput(input.GetInst());
654             if (!realSourceInst->IsMovableObject()) {
655                 continue;
656             }
657             // In case, when usege of object in loop and definition is not in loop or usage's loop inside
658             // definition's loop, we should check SaveStates till the end of BasicBlock
659             if (blockInLoop && (block->GetLoop()->IsInside(realSourceInst->GetBasicBlock()->GetLoop()))) {
660                 COMPILER_LOG(DEBUG, BRIDGES_SS)
661                     << " Check inputs: Try to do SSB for real source inst: " << *realSourceInst << "\n"
662                     << "  Block in loop:  " << block->GetLoop() << " So target is end of BB:" << *(block->GetLastInst())
663                     << "\n";
664                 SearchAndCreateMissingObjInSaveState(block->GetGraph(), realSourceInst, block->GetLastInst(),
665                                                      block->GetFirstInst());
666             } else {
667                 COMPILER_LOG(DEBUG, BRIDGES_SS)
668                     << " Check inputs: Try to do SSB for real source inst: " << *realSourceInst << "\n"
669                     << "  For target inst: " << *inst << "\n";
670                 SearchAndCreateMissingObjInSaveState(block->GetGraph(), realSourceInst, inst, block->GetFirstInst());
671             }
672         }
673         // Check usage reference instruction
674         FixUsageInstInOtherBB(block, inst);
675     }
676 }
677 
IsSaveStateForGc(Inst * inst)678 bool SaveStateBridgesBuilder::IsSaveStateForGc(Inst *inst)
679 {
680     return inst->GetOpcode() == Opcode::SafePoint || inst->GetOpcode() == Opcode::SaveState;
681 }
682 
CreateBridgeInSS(Inst * source)683 void SaveStateBridgesBuilder::CreateBridgeInSS(Inst *source)
684 {
685     ASSERT(bridges_ != nullptr);
686     ASSERT(source != nullptr);
687     ASSERT(source->IsMovableObject());
688 
689     for (Inst *ss : *bridges_) {
690         static_cast<SaveStateInst *>(ss)->AppendBridge(source);
691     }
692 }
693 
SearchAndCreateMissingObjInSaveState(Graph * graph,Inst * source,Inst * target,Inst * stopSearchInst,BasicBlock * targetBlock)694 void SaveStateBridgesBuilder::SearchAndCreateMissingObjInSaveState(Graph *graph, Inst *source, Inst *target,
695                                                                    Inst *stopSearchInst, BasicBlock *targetBlock)
696 {
697     ASSERT(graph != nullptr);
698     ASSERT(source != nullptr);
699     ASSERT(source->IsMovableObject());
700 
701     if (graph->IsBytecodeOptimizer()) {
702         return;  // SaveState bridges useless when bytecode optimizer enabled.
703     }
704 
705     if (targetBlock == nullptr) {
706         ASSERT(target != nullptr);
707         targetBlock = target->GetBasicBlock();
708     } else {
709         // if target is nullptr, we won't traverse the target block
710         ASSERT(target == nullptr || target->GetBasicBlock() == targetBlock);
711     }
712     SearchMissingObjInSaveStates(graph, source, target, stopSearchInst, targetBlock);
713     if (!bridges_->empty()) {
714         CreateBridgeInSS(source);
715         COMPILER_LOG(DEBUG, BRIDGES_SS) << " Created bridge(s)";
716     }
717 }
718 
ProcessSSUserPreds(Graph * graph,Inst * inst,Inst * targetInst)719 void SaveStateBridgesBuilder::ProcessSSUserPreds(Graph *graph, Inst *inst, Inst *targetInst)
720 {
721     for (auto predBlock : targetInst->GetBasicBlock()->GetPredsBlocks()) {
722         if (targetInst->CastToPhi()->GetPhiInput(predBlock) == inst) {
723             SearchAndCreateMissingObjInSaveState(graph, inst, predBlock->GetLastInst(), nullptr, predBlock);
724         }
725     }
726 }
727 
FixInstUsageInSS(Graph * graph,Inst * inst)728 void SaveStateBridgesBuilder::FixInstUsageInSS(Graph *graph, Inst *inst)
729 {
730     if (!inst->IsMovableObject()) {
731         return;
732     }
733     for (auto &user : inst->GetUsers()) {
734         auto targetInst = user.GetInst();
735         COMPILER_LOG(DEBUG, BRIDGES_SS) << " Check usage: Try to do SSB for real source inst: " << *inst << "\n"
736                                         << "  For target inst: " << *targetInst << "\n";
737         if (targetInst->IsPhi() && !(graph->IsAnalysisValid<DominatorsTree>() && inst->IsDominate(targetInst))) {
738             ProcessSSUserPreds(graph, inst, targetInst);
739         } else {
740             SearchAndCreateMissingObjInSaveState(graph, inst, targetInst);
741         }
742     }
743 }
744 
745 // Check instructions don't have their own vregs and thus are not added in SaveStates,
746 // but newly added Phi instructions with check inputs should be added
FixPhisWithCheckInputs(BasicBlock * block)747 void SaveStateBridgesBuilder::FixPhisWithCheckInputs(BasicBlock *block)
748 {
749     if (block == nullptr) {
750         return;
751     }
752     auto graph = block->GetGraph();
753     for (auto phi : block->PhiInsts()) {
754         if (!phi->IsMovableObject()) {
755             continue;
756         }
757         for (auto &input : phi->GetInputs()) {
758             if (input.GetInst()->IsCheck()) {
759                 FixInstUsageInSS(graph, phi);
760                 break;
761             }
762         }
763     }
764 }
765 
DumpBridges(std::ostream & out,Inst * source)766 void SaveStateBridgesBuilder::DumpBridges(std::ostream &out, Inst *source)
767 {
768     ASSERT(source != nullptr);
769     ASSERT(bridges_ != nullptr);
770     out << "Inst id " << source->GetId() << " with type ";
771     source->DumpOpcode(&out);
772     out << "need bridge in SS id: ";
773     for (auto ss : *bridges_) {
774         out << ss->GetId() << " ";
775     }
776     out << '\n';
777 }
778 
StoreValueCanBeObject(Inst * inst)779 bool StoreValueCanBeObject(Inst *inst)
780 {
781     switch (inst->GetOpcode()) {
782         case Opcode::CastValueToAnyType: {
783             auto type = AnyBaseTypeToDataType(inst->CastToCastValueToAnyType()->GetAnyType());
784             return (type == DataType::ANY || type == DataType::REFERENCE);
785         }
786         case Opcode::Constant:
787             return false;
788         default:
789             return true;
790     }
791 }
792 
IsConditionEqual(const Inst * inst0,const Inst * inst1,bool inverted)793 bool IsConditionEqual(const Inst *inst0, const Inst *inst1, bool inverted)
794 {
795     if (inst0->GetOpcode() != inst1->GetOpcode()) {
796         return false;
797     }
798     if (inst0->GetOpcode() != Opcode::IfImm) {
799         // investigate why Opcode::If cannot be lowered to Opcode::IfImm and support it if needed
800         return false;
801     }
802     auto ifImm0 = inst0->CastToIfImm();
803     auto ifImm1 = inst1->CastToIfImm();
804     auto opcode = ifImm0->GetInput(0).GetInst()->GetOpcode();
805     if (opcode != ifImm1->GetInput(0).GetInst()->GetOpcode()) {
806         return false;
807     }
808     if (ifImm0->GetImm() != 0 && ifImm0->GetImm() != 1) {
809         return false;
810     }
811     if (ifImm1->GetImm() != 0 && ifImm1->GetImm() != 1) {
812         return false;
813     }
814     if (ifImm0->GetImm() != ifImm1->GetImm()) {
815         inverted = !inverted;
816     }
817     if (opcode != Opcode::Compare) {
818         if (ifImm0->GetInput(0).GetInst() != ifImm1->GetInput(0).GetInst()) {
819             return false;
820         }
821         auto cc = inverted ? GetInverseConditionCode(ifImm0->GetCc()) : ifImm0->GetCc();
822         return cc == ifImm1->GetCc();
823     }
824     auto cmp0 = ifImm0->GetInput(0).GetInst()->CastToCompare();
825     auto cmp1 = ifImm1->GetInput(0).GetInst()->CastToCompare();
826     if (cmp0->GetInput(0).GetInst() == cmp1->GetInput(0).GetInst() &&
827         cmp0->GetInput(1).GetInst() == cmp1->GetInput(1).GetInst()) {
828         if (GetInverseConditionCode(ifImm0->GetCc()) == ifImm1->GetCc()) {
829             inverted = !inverted;
830         } else if (ifImm0->GetCc() != ifImm1->GetCc()) {
831             return false;
832         }
833         auto cc = inverted ? GetInverseConditionCode(cmp0->GetCc()) : cmp0->GetCc();
834         return cc == cmp1->GetCc();
835     }
836     return false;
837 }
838 
CleanupGraphSaveStateOSR(Graph * graph)839 void CleanupGraphSaveStateOSR(Graph *graph)
840 {
841     ASSERT(graph != nullptr);
842     ASSERT(graph->IsOsrMode());
843     graph->InvalidateAnalysis<LoopAnalyzer>();
844     graph->RunPass<LoopAnalyzer>();
845     for (auto block : graph->GetBlocksRPO()) {
846         if (block->IsOsrEntry() && !block->IsLoopHeader()) {
847             auto firstInst = block->GetFirstInst();
848             if (firstInst == nullptr) {
849                 continue;
850             }
851             if (firstInst->GetOpcode() == Opcode::SaveStateOsr) {
852                 block->RemoveInst(firstInst);
853                 block->SetOsrEntry(false);
854             }
855         }
856     }
857 }
858 
859 template <typename T>
FindInThisBlock(Inst * currInst,Inst * finish)860 bool FindInThisBlock(Inst *currInst, Inst *finish)
861 {
862     while (currInst != finish) {
863         if (T()(currInst)) {
864             return true;
865         }
866         currInst = currInst->GetPrev();
867     }
868     return false;
869 }
870 
871 template <typename T>
FindInstBetween(Inst * domInst,BasicBlock * currentBb,Marker marker)872 bool FindInstBetween(Inst *domInst, BasicBlock *currentBb, Marker marker)
873 {
874     if (currentBb->SetMarker(marker)) {
875         return false;
876     }
877     bool isSameBlock = domInst->GetBasicBlock() == currentBb;
878     auto currInst = currentBb->GetLastInst();
879     Inst *finish = isSameBlock ? domInst : nullptr;
880     if (FindInThisBlock<T>(currInst, finish)) {
881         return true;
882     }
883     if (isSameBlock) {
884         return false;
885     }
886     for (auto pred : currentBb->GetPredsBlocks()) {
887         if (FindInstBetween<T>(domInst, pred, marker)) {
888             return true;
889         }
890     }
891     return false;
892 }
893 
894 template bool HasSaveStateBetween<IsSaveState>(Inst *dom_inst, Inst *inst);
895 template bool HasSaveStateBetween<IsSaveStateCanTriggerGc>(Inst *dom_inst, Inst *inst);
896 
897 template <typename T>
HasSaveStateBetween(Inst * domInst,Inst * inst)898 bool HasSaveStateBetween(Inst *domInst, Inst *inst)
899 {
900     ASSERT(domInst->IsDominate(inst));
901     if (domInst == inst) {
902         return false;
903     }
904     auto bb = inst->GetBasicBlock();
905     bool isSameBlock = domInst->GetBasicBlock() == bb;
906     auto currInst = inst->GetPrev();
907     Inst *finish = isSameBlock ? domInst : nullptr;
908     if (FindInThisBlock<T>(currInst, finish)) {
909         return true;
910     }
911     if (isSameBlock) {
912         return false;
913     }
914     MarkerHolder marker(bb->GetGraph());
915     for (auto pred : bb->GetPredsBlocks()) {
916         if (FindInstBetween<T>(domInst, pred, marker.GetMarker())) {
917             return true;
918         }
919     }
920     return false;
921 }
922 
Append(Inst * inst)923 void InstAppender::Append(Inst *inst)
924 {
925     if (prev_ == nullptr) {
926         block_->AppendInst(inst);
927     } else {
928         block_->InsertAfter(inst, prev_);
929     }
930     prev_ = inst;
931 }
932 
Append(std::initializer_list<Inst * > instructions)933 void InstAppender::Append(std::initializer_list<Inst *> instructions)
934 {
935     for (auto *inst : instructions) {
936         Append(inst);
937     }
938 }
939 
940 }  // namespace ark::compiler
941