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