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