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