• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2021-2024 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  * http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include "compiler_logger.h"
17 #include "checks_elimination.h"
18 #include "optimizer/analysis/alias_analysis.h"
19 #include "optimizer/analysis/bounds_analysis.h"
20 #include "optimizer/analysis/dominators_tree.h"
21 #include "optimizer/analysis/loop_analyzer.h"
22 #include "optimizer/analysis/object_type_propagation.h"
23 #include "optimizer/ir/graph_visitor.h"
24 #include "optimizer/ir/analysis.h"
25 
26 namespace ark::compiler {
27 
RunImpl()28 bool ChecksElimination::RunImpl()
29 {
30     COMPILER_LOG(DEBUG, CHECKS_ELIM) << "Start ChecksElimination";
31     GetGraph()->RunPass<DominatorsTree>();
32     GetGraph()->RunPass<LoopAnalyzer>();
33     GetGraph()->RunPass<ObjectTypePropagation>();
34 
35     VisitGraph();
36 
37     if (g_options.IsCompilerEnableReplacingChecksOnDeoptimization()) {
38         if (!GetGraph()->IsOsrMode()) {
39             ReplaceBoundsCheckToDeoptimizationBeforeLoop();
40             MoveCheckOutOfLoop();
41         }
42         ReplaceBoundsCheckToDeoptimizationInLoop();
43 
44         ReplaceCheckMustThrowByUnconditionalDeoptimize();
45     }
46     if (IsLoopDeleted() && GetGraph()->IsOsrMode()) {
47         CleanupGraphSaveStateOSR(GetGraph());
48     }
49     COMPILER_LOG(DEBUG, CHECKS_ELIM) << "ChecksElimination " << (IsApplied() ? "is" : "isn't") << " applied";
50     COMPILER_LOG(DEBUG, CHECKS_ELIM) << "Finish ChecksElimination";
51     return isApplied_;
52 }
53 
InvalidateAnalyses()54 void ChecksElimination::InvalidateAnalyses()
55 {
56     GetGraph()->InvalidateAnalysis<DominatorsTree>();
57     // Already "LoopAnalyzer" was ran in "CleanupGraphSaveStateOSR"
58     // in case (IsLoopDeleted() && GetGraph()->IsOsrMode())
59     if (!(IsLoopDeleted() && GetGraph()->IsOsrMode())) {
60         GetGraph()->InvalidateAnalysis<LoopAnalyzer>();
61     }
62     GetGraph()->InvalidateAnalysis<BoundsAnalysis>();
63     GetGraph()->InvalidateAnalysis<AliasAnalysis>();
64 }
65 
66 template <bool CHECK_FULL_DOM>
VisitNullCheck(GraphVisitor * v,Inst * inst)67 void ChecksElimination::VisitNullCheck(GraphVisitor *v, Inst *inst)
68 {
69     COMPILER_LOG(DEBUG, CHECKS_ELIM) << "Start visit NullCheck with id = " << inst->GetId();
70 
71     auto ref = inst->GetInput(0).GetInst();
72     static_cast<ChecksElimination *>(v)->TryRemoveDominatedNullChecks(inst, ref);
73 
74     if (!static_cast<ChecksElimination *>(v)->TryRemoveCheck<Opcode::NullCheck, CHECK_FULL_DOM>(inst)) {
75         COMPILER_LOG(DEBUG, CHECKS_ELIM) << "NullCheck couldn't be deleted";
76         COMPILER_LOG(DEBUG, CHECKS_ELIM) << "NullCheck saved for further replacing on deoptimization";
77         static_cast<ChecksElimination *>(v)->PushNewCheckForMoveOutOfLoop(inst);
78     }
79 }
80 
VisitNegativeCheck(GraphVisitor * v,Inst * inst)81 void ChecksElimination::VisitNegativeCheck(GraphVisitor *v, Inst *inst)
82 {
83     COMPILER_LOG(DEBUG, CHECKS_ELIM) << "Start visit NegativeCheck with id = " << inst->GetId();
84     if (!static_cast<ChecksElimination *>(v)->TryRemoveCheck<Opcode::NegativeCheck>(inst)) {
85         COMPILER_LOG(DEBUG, CHECKS_ELIM) << "NegativeCheck couldn't be deleted";
86         static_cast<ChecksElimination *>(v)->PushNewCheckForMoveOutOfLoop(inst);
87     }
88 }
89 
VisitNotPositiveCheck(GraphVisitor * v,Inst * inst)90 void ChecksElimination::VisitNotPositiveCheck(GraphVisitor *v, Inst *inst)
91 {
92     COMPILER_LOG(DEBUG, CHECKS_ELIM) << "Start visit NotPositiveCheck with id = " << inst->GetId();
93     if (!static_cast<ChecksElimination *>(v)->TryRemoveCheck<Opcode::NotPositiveCheck>(inst)) {
94         COMPILER_LOG(DEBUG, CHECKS_ELIM) << "NotPositiveCheck couldn't be deleted";
95         static_cast<ChecksElimination *>(v)->PushNewCheckForMoveOutOfLoop(inst);
96     }
97 }
98 
VisitZeroCheck(GraphVisitor * v,Inst * inst)99 void ChecksElimination::VisitZeroCheck(GraphVisitor *v, Inst *inst)
100 {
101     COMPILER_LOG(DEBUG, CHECKS_ELIM) << "Start visit ZeroCheck with id = " << inst->GetId();
102     if (!static_cast<ChecksElimination *>(v)->TryRemoveCheck<Opcode::ZeroCheck>(inst)) {
103         COMPILER_LOG(DEBUG, CHECKS_ELIM) << "ZeroCheck couldn't be deleted";
104         static_cast<ChecksElimination *>(v)->PushNewCheckForMoveOutOfLoop(inst);
105     }
106 }
107 
VisitRefTypeCheck(GraphVisitor * v,Inst * inst)108 void ChecksElimination::VisitRefTypeCheck(GraphVisitor *v, Inst *inst)
109 {
110     auto visitor = static_cast<ChecksElimination *>(v);
111     auto storeInst = inst->GetDataFlowInput(inst->GetInput(1).GetInst());
112     // Case: a[i] = nullptr
113     if (storeInst->GetOpcode() == Opcode::NullPtr) {
114         visitor->ReplaceUsersAndRemoveCheck(inst, storeInst);
115         return;
116     }
117     auto arrayInst = inst->GetDataFlowInput(0);
118     auto ref = inst->GetInput(1).GetInst();
119     // Case:
120     // a[1] = obj
121     // a[2] = obj
122     visitor->TryRemoveDominatedChecks<Opcode::RefTypeCheck>(inst, [arrayInst, ref](Inst *userInst) {
123         return userInst->GetDataFlowInput(0) == arrayInst && userInst->GetInput(1) == ref;
124     });
125     visitor->GetGraph()->RunPass<ObjectTypePropagation>();
126     auto arrayTypeInfo = arrayInst->GetObjectTypeInfo();
127     if (arrayTypeInfo && arrayTypeInfo.IsExact()) {
128         auto storeTypeInfo = storeInst->GetObjectTypeInfo();
129         auto arrayClass = arrayTypeInfo.GetClass();
130         auto storeClass = (storeTypeInfo) ? storeTypeInfo.GetClass() : nullptr;
131         if (visitor->GetGraph()->GetRuntime()->CheckStoreArray(arrayClass, storeClass)) {
132             visitor->ReplaceUsersAndRemoveCheck(inst, storeInst);
133             return;
134         }
135     }
136     visitor->PushNewCheckForMoveOutOfLoop(inst);
137 }
138 
TryToEliminateAnyTypeCheck(Inst * inst,Inst * instToReplace,AnyBaseType type,AnyBaseType prevType)139 bool ChecksElimination::TryToEliminateAnyTypeCheck(Inst *inst, Inst *instToReplace, AnyBaseType type,
140                                                    AnyBaseType prevType)
141 {
142     auto language = GetGraph()->GetRuntime()->GetMethodSourceLanguage(GetGraph()->GetMethod());
143     auto allowedType = inst->CastToAnyTypeCheck()->GetAllowedInputType();
144     profiling::AnyInputType prevAllowedType;
145     if (instToReplace->GetOpcode() == Opcode::AnyTypeCheck) {
146         prevAllowedType = instToReplace->CastToAnyTypeCheck()->GetAllowedInputType();
147     } else {
148         prevAllowedType = instToReplace->CastToCastValueToAnyType()->GetAllowedInputType();
149     }
150     auto res = IsAnyTypeCanBeSubtypeOf(language, type, prevType, allowedType, prevAllowedType);
151     if (!res) {
152         return false;
153     }
154     if (res.value()) {
155         ReplaceUsersAndRemoveCheck(inst, instToReplace);
156     } else {
157         PushNewCheckMustThrow(inst);
158     }
159     return true;
160 }
161 
UpdateHclassChecks(Inst * inst)162 void ChecksElimination::UpdateHclassChecks(Inst *inst)
163 {
164     // AnyTypeCheck HEAP_OBJECT => LoadObject Class => LoadObject Hclass => HclassCheck
165     bool allUsersInlined = false;
166     for (auto &user : inst->GetUsers()) {
167         auto userInst = user.GetInst();
168         if (userInst->IsCall()) {
169             if (userInst->CastToCallDynamic()->IsInlined()) {
170                 allUsersInlined = true;
171             } else {
172                 return;
173             }
174         } else if (userInst->GetOpcode() == Opcode::LoadObject) {
175             if (userInst->CastToLoadObject()->GetObjectType() == ObjectType::MEM_DYN_CLASS) {
176                 continue;
177             }
178             ASSERT(userInst->CastToLoadObject()->GetObjectType() == ObjectType::MEM_DYN_METHOD);
179             if (!IsInlinedCallLoadMethod(userInst)) {
180                 return;
181             }
182             allUsersInlined = true;
183         } else {
184             return;
185         }
186     }
187     if (!allUsersInlined) {
188         return;
189     }
190     for (auto &user : inst->GetUsers()) {
191         auto userInst = user.GetInst();
192         auto check = GetHclassCheckFromLoads(userInst);
193         if (!check.has_value()) {
194             continue;
195         }
196         check.value()->CastToHclassCheck()->SetCheckFunctionIsNotClassConstructor(false);
197         SetApplied();
198     }
199 }
200 
GetHclassCheckFromLoads(Inst * loadClass)201 std::optional<Inst *> ChecksElimination::GetHclassCheckFromLoads(Inst *loadClass)
202 {
203     if (loadClass->GetOpcode() != Opcode::LoadObject ||
204         loadClass->CastToLoadObject()->GetObjectType() != ObjectType::MEM_DYN_CLASS) {
205         return std::nullopt;
206     }
207     for (auto &usersLoad : loadClass->GetUsers()) {
208         auto loadHclass = usersLoad.GetInst();
209         if (loadHclass->GetOpcode() != Opcode::LoadObject ||
210             loadHclass->CastToLoadObject()->GetObjectType() != ObjectType::MEM_DYN_HCLASS) {
211             continue;
212         }
213         for (auto &usersHclass : loadHclass->GetUsers()) {
214             auto hclassCheck = usersHclass.GetInst();
215             if (hclassCheck->GetOpcode() == Opcode::HclassCheck) {
216                 return hclassCheck;
217             }
218         }
219     }
220     return std::nullopt;
221 }
222 
IsInlinedCallLoadMethod(Inst * inst)223 bool ChecksElimination::IsInlinedCallLoadMethod(Inst *inst)
224 {
225     bool isMethod = inst->GetOpcode() == Opcode::LoadObject &&
226                     inst->CastToLoadObject()->GetObjectType() == ObjectType::MEM_DYN_METHOD;
227     if (!isMethod) {
228         return false;
229     }
230     for (auto &methodUser : inst->GetUsers()) {
231         auto compare = methodUser.GetInst()->CastToCompare();
232         bool isCompare = compare->GetOpcode() == Opcode::Compare && compare->GetCc() == ConditionCode::CC_NE &&
233                          compare->GetInputType(0) == DataType::POINTER;
234         if (!isCompare) {
235             return false;
236         }
237         auto deoptimizeIf = compare->GetFirstUser()->GetInst()->CastToDeoptimizeIf();
238         if (deoptimizeIf->GetDeoptimizeType() != DeoptimizeType::INLINE_DYN) {
239             return false;
240         }
241     }
242     return true;
243 }
244 
TryRemoveDominatedHclassCheck(Inst * inst)245 void ChecksElimination::TryRemoveDominatedHclassCheck(Inst *inst)
246 {
247     ASSERT(inst->GetOpcode() == Opcode::HclassCheck);
248     // AnyTypeCheck HEAP_OBJECT => LoadObject Class => LoadObject Hclass => HclassCheck
249     auto object = inst->GetInput(0).GetInst()->GetInput(0).GetInst()->GetInput(0).GetInst();
250 
251     for (auto &user : object->GetUsers()) {
252         auto userInst = user.GetInst();
253         auto hclassCheck = GetHclassCheckFromLoads(userInst);
254         if (hclassCheck.has_value() && hclassCheck.value() != inst && inst->IsDominate(hclassCheck.value())) {
255             ASSERT(inst->IsDominate(hclassCheck.value()));
256             COMPILER_LOG(DEBUG, CHECKS_ELIM)
257                 << GetOpcodeString(Opcode::HclassCheck) << " with id = " << inst->GetId() << " dominate on "
258                 << GetOpcodeString(Opcode::HclassCheck) << " with id = " << hclassCheck.value()->GetId();
259             auto block = hclassCheck.value()->GetBasicBlock();
260             auto graph = block->GetGraph();
261             hclassCheck.value()->RemoveInputs();
262             // We don't should update because it's possible to delete one flag in check, when all DynCall is inlined
263             inst->CastToHclassCheck()->ExtendFlags(hclassCheck.value());
264             block->ReplaceInst(hclassCheck.value(), graph->CreateInstNOP());
265             SetApplied();
266         }
267     }
268 }
269 
VisitAnyTypeCheck(GraphVisitor * v,Inst * inst)270 void ChecksElimination::VisitAnyTypeCheck(GraphVisitor *v, Inst *inst)
271 {
272     auto visitor = static_cast<ChecksElimination *>(v);
273     auto inputInst = inst->GetInput(0).GetInst();
274     auto type = inst->CastToAnyTypeCheck()->GetAnyType();
275     if (type == AnyBaseType::UNDEFINED_TYPE) {
276         visitor->ReplaceUsersAndRemoveCheck(inst, inputInst);
277         return;
278     }
279     // from:
280     //     2.any  CastValueToAnyType ANY_SUBTYPE v1 -> (v4)
281     //     4.any  AnyTypeCheck ANY_SUBTYPE v2, v3 -> (....)
282     // to:
283     //     2.any  CastValueToAnyType ANY_SUBTYPE v1 -> (...)
284     if (inputInst->GetOpcode() == Opcode::CastValueToAnyType) {
285         visitor->TryToEliminateAnyTypeCheck(inst, inputInst, type, inputInst->CastToCastValueToAnyType()->GetAnyType());
286         return;
287     }
288     // from:
289     //     2.any  AnyTypeCheck ANY_SUBTYPE v1, v0 -> (v4)
290     //     4.any  AnyTypeCheck ANY_SUBTYPE v2, v3 -> (....)
291     // to:
292     //     2.any  AnyTypeCheck ANY_SUBTYPE v1, v0 -> (...)
293     if (inputInst->GetOpcode() == Opcode::AnyTypeCheck) {
294         visitor->TryToEliminateAnyTypeCheck(inst, inputInst, type, inputInst->CastToAnyTypeCheck()->GetAnyType());
295         return;
296     }
297     // from:
298     //     2.any  AnyTypeCheck ANY_SUBTYPE v1, v0 -> (v4)
299     //     4.any  AnyTypeCheck ANY_SUBTYPE v1, v3 -> (....)
300     // to:
301     //     2.any  AnyTypeCheck ANY_SUBTYPE v1, v0 -> (v4,...)
302     bool applied = false;
303     for (auto &user : inputInst->GetUsers()) {
304         auto userInst = user.GetInst();
305         if (userInst == inst) {
306             continue;
307         }
308         if (userInst->GetOpcode() != Opcode::AnyTypeCheck) {
309             continue;
310         }
311         if (!inst->IsDominate(userInst)) {
312             continue;
313         }
314 
315         if (visitor->TryToEliminateAnyTypeCheck(userInst, inst, userInst->CastToAnyTypeCheck()->GetAnyType(), type)) {
316             applied = true;
317         }
318     }
319     if (!applied) {
320         visitor->PushNewCheckForMoveOutOfLoop(inst);
321     }
322     // from:
323     //    39.any  AnyTypeCheck ECMASCRIPT_HEAP_OBJECT_TYPE v37, v38 -> (v65, v40)
324     //    40.ref  LoadObject 4294967288 Class v39 -> (v41)
325     //    41.ref  LoadObject 4294967287 Hclass v40
326     //    42.     HclassCheck [IsFunc, IsNotClassConstr] v41, v38
327     //      ...METHOD IS INLINED ...
328     // to:
329     //    39.any  AnyTypeCheck ECMASCRIPT_HEAP_OBJECT_TYPE v37, v38 -> (v65, v40)
330     //    40.ref  LoadObject 4294967288 Class v39 -> (v41)
331     //    41.ref  LoadObject 4294967287 Hclass v40
332     //    42.     HclassCheck [IsFunc] v41, v38
333     //      ...METHOD IS INLINED ...
334     if (TryIsDynHeapObject(type)) {
335         visitor->UpdateHclassChecks(inst);
336     }
337 }
338 
VisitHclassCheck(GraphVisitor * v,Inst * inst)339 void ChecksElimination::VisitHclassCheck(GraphVisitor *v, Inst *inst)
340 {
341     [[maybe_unused]] auto visitor = static_cast<ChecksElimination *>(v);
342     visitor->TryRemoveDominatedHclassCheck(inst);
343     visitor->PushNewCheckForMoveOutOfLoop(inst);
344 }
345 
346 template <typename RangeChecker, typename... RangeCheckers>
NeedSetFlagNoHoist(Inst * check,Inst * input,RangeChecker rangeChecker,RangeCheckers...rangeCheckers)347 bool NeedSetFlagNoHoist(Inst *check, Inst *input, RangeChecker rangeChecker, RangeCheckers... rangeCheckers)
348 {
349     return NeedSetFlagNoHoist(check, input, rangeChecker) || (NeedSetFlagNoHoist(check, input, rangeCheckers) || ...);
350 }
351 
352 template <typename RangeChecker>
NeedSetFlagNoHoist(Inst * check,Inst * input,RangeChecker rangeChecker)353 bool NeedSetFlagNoHoist(Inst *check, Inst *input, RangeChecker rangeChecker)
354 {
355     auto checkBlock = check->GetBasicBlock();
356     auto inputBlock = input->GetBasicBlock();
357 
358     // 1. The `check` is in the root loop, further analysis is not needed.
359     if (checkBlock->GetLoop()->IsRoot()) {
360         return false;
361     }
362 
363     // 2. If `input` isn't in the root loop and it is hoistable, conservatively enforce `NO_HOIST` flag on `check`'s
364     //    users since `input` itself may be hoisted, allowing the users to be hoisted as well (with the danger of
365     //    breaking bounds). See 'HoistableInput' checked test.
366     if (((!inputBlock->GetLoop()->IsRoot()) && (!input->IsNotHoistable())) || input->IsPhi()) {
367         return true;
368     }
369 
370     // 3. `input` is not hoistable due to `2.` so users can be hoisted only into preheader of some inner loops, which
371     //     are dominated by `checkBlock`. No need to set `NO_HOIST` flag explicitly.
372     if ((inputBlock->GetLoop() == checkBlock->GetLoop()) || (inputBlock->GetLoop()->IsInside(checkBlock->GetLoop()))) {
373         return false;
374     }
375 
376     // Find outermost loop of `check` that is lying in the innermost loop, containing both `check` and `input`.
377     auto cl = checkBlock->GetLoop();
378     auto il = inputBlock->GetLoop();
379     while (cl->GetOuterLoop() != il) {
380         cl = cl->GetOuterLoop();
381         if (cl == nullptr) {
382             // Restart algorithm with an outer `input` loop.
383             cl = checkBlock->GetLoop();
384             il = il->GetOuterLoop();
385         }
386     }
387     auto outermostPrehead = cl->GetPreHeader();
388 
389     // It is hoistable if bounds range are valid at the outermost prehead:
390     auto bri = outermostPrehead->GetGraph()->GetBoundsRangeInfo();
391     auto range = bri->FindBoundsRange(outermostPrehead, input);
392     return !rangeChecker(range);
393 }
394 
395 template <typename... RangeCheckers>
CheckAndSetFlagNoHoist(Inst * inst,Inst * input,RangeCheckers...rangeCheckers)396 void CheckAndSetFlagNoHoist(Inst *inst, Inst *input, RangeCheckers... rangeCheckers)
397 {
398     if (NeedSetFlagNoHoist(inst, input, rangeCheckers...)) {
399         for (auto &user : inst->GetUsers()) {
400             user.GetInst()->SetFlag(inst_flags::NO_HOIST);
401         }
402     }
403 }
404 
VisitBoundsCheck(GraphVisitor * v,Inst * inst)405 void ChecksElimination::VisitBoundsCheck(GraphVisitor *v, Inst *inst)
406 {
407     COMPILER_LOG(DEBUG, CHECKS_ELIM) << "Start visit BoundsCheck with id = " << inst->GetId();
408     auto block = inst->GetBasicBlock();
409     auto lenArray = inst->GetInput(0).GetInst();
410     auto index = inst->GetInput(1).GetInst();
411     auto visitor = static_cast<ChecksElimination *>(v);
412 
413     visitor->TryRemoveDominatedChecks<Opcode::BoundsCheck>(inst, [lenArray, index](Inst *userInst) {
414         return lenArray == userInst->GetInput(0) && index == userInst->GetInput(1);
415     });
416 
417     auto bri = block->GetGraph()->GetBoundsRangeInfo();
418     auto lenArrayRange = bri->FindBoundsRange(block, lenArray);
419     auto upperChecker = [&lenArrayRange, &lenArray](const BoundsRange &idxRange) {
420         return idxRange.IsLess(lenArrayRange) || idxRange.IsLess(lenArray);
421     };
422     auto lowerChecker = [](const BoundsRange &idxRange) { return idxRange.IsNotNegative(); };
423     auto indexRange = bri->FindBoundsRange(block, index);
424     auto correctUpper = upperChecker(indexRange);
425     auto correctLower = lowerChecker(indexRange);
426     if (correctUpper && correctLower) {
427         COMPILER_LOG(DEBUG, CHECKS_ELIM) << "Index of BoundsCheck have correct bounds";
428         CheckAndSetFlagNoHoist(inst, index, upperChecker, lowerChecker);
429         visitor->ReplaceUsersAndRemoveCheck(inst, index);
430         return;
431     }
432 
433     if (indexRange.IsNegative() || indexRange.IsMoreOrEqual(lenArrayRange)) {
434         COMPILER_LOG(DEBUG, CHECKS_ELIM)
435             << "BoundsCheck have incorrect bounds, saved for replace by unconditional deoptimize";
436         visitor->PushNewCheckMustThrow(inst);
437         return;
438     }
439 
440     COMPILER_LOG(DEBUG, CHECKS_ELIM) << "BoundsCheck saved for further replacing on deoptimization";
441     auto loop = GetLoopForBoundsCheck(block, lenArray, index);
442     visitor->PushNewBoundsCheck(loop, inst, {lenArray, index}, !correctUpper, !correctLower);
443 }
444 
VisitCheckCast(GraphVisitor * v,Inst * inst)445 void ChecksElimination::VisitCheckCast(GraphVisitor *v, Inst *inst)
446 {
447     auto visitor = static_cast<ChecksElimination *>(v);
448     visitor->GetGraph()->RunPass<ObjectTypePropagation>();
449     auto result = ObjectTypeCheckElimination::TryEliminateCheckCast(inst);
450     if (result != ObjectTypeCheckElimination::CheckCastEliminateType::INVALID) {
451         visitor->SetApplied();
452         if (result == ObjectTypeCheckElimination::CheckCastEliminateType::MUST_THROW) {
453             visitor->PushNewCheckMustThrow(inst);
454         }
455         return;
456     }
457     if (!inst->CastToCheckCast()->GetOmitNullCheck()) {
458         auto block = inst->GetBasicBlock();
459         auto bri = block->GetGraph()->GetBoundsRangeInfo();
460         auto inputRange = bri->FindBoundsRange(block, inst->GetInput(0).GetInst());
461         if (inputRange.IsMore(BoundsRange(0))) {
462             visitor->SetApplied();
463             inst->CastToCheckCast()->SetOmitNullCheck(true);
464         }
465     }
466     visitor->PushNewCheckForMoveOutOfLoop(inst);
467 }
468 
VisitIsInstance(GraphVisitor * v,Inst * inst)469 void ChecksElimination::VisitIsInstance(GraphVisitor *v, Inst *inst)
470 {
471     if (inst->CastToIsInstance()->GetOmitNullCheck()) {
472         return;
473     }
474     auto block = inst->GetBasicBlock();
475     auto bri = block->GetGraph()->GetBoundsRangeInfo();
476     auto inputRange = bri->FindBoundsRange(block, inst->GetInput(0).GetInst());
477     if (inputRange.IsMore(BoundsRange(0))) {
478         auto visitor = static_cast<ChecksElimination *>(v);
479         visitor->SetApplied();
480         inst->CastToIsInstance()->SetOmitNullCheck(true);
481     }
482 }
483 
VisitAddOverflowCheck(GraphVisitor * v,Inst * inst)484 void ChecksElimination::VisitAddOverflowCheck(GraphVisitor *v, Inst *inst)
485 {
486     ASSERT(inst->GetType() == DataType::INT32);
487     auto input1 = inst->GetInput(0).GetInst();
488     auto input2 = inst->GetInput(1).GetInst();
489     auto visitor = static_cast<ChecksElimination *>(v);
490     visitor->TryRemoveDominatedChecks<Opcode::AddOverflowCheck>(inst, [input1, input2](Inst *userInst) {
491         return (userInst->GetInput(0) == input1 && userInst->GetInput(1) == input2) ||
492                (userInst->GetInput(0) == input2 && userInst->GetInput(1) == input1);
493     });
494     visitor->TryOptimizeOverflowCheck<Opcode::AddOverflowCheck>(inst);
495 }
496 
VisitSubOverflowCheck(GraphVisitor * v,Inst * inst)497 void ChecksElimination::VisitSubOverflowCheck(GraphVisitor *v, Inst *inst)
498 {
499     ASSERT(inst->GetType() == DataType::INT32);
500     auto input1 = inst->GetInput(0).GetInst();
501     auto input2 = inst->GetInput(1).GetInst();
502     auto visitor = static_cast<ChecksElimination *>(v);
503     visitor->TryRemoveDominatedChecks<Opcode::SubOverflowCheck>(inst, [input1, input2](Inst *userInst) {
504         return (userInst->GetInput(0) == input1 && userInst->GetInput(1) == input2);
505     });
506     visitor->TryOptimizeOverflowCheck<Opcode::SubOverflowCheck>(inst);
507 }
508 
VisitNegOverflowAndZeroCheck(GraphVisitor * v,Inst * inst)509 void ChecksElimination::VisitNegOverflowAndZeroCheck(GraphVisitor *v, Inst *inst)
510 {
511     ASSERT(inst->GetType() == DataType::INT32);
512     auto input1 = inst->GetInput(0).GetInst();
513     auto visitor = static_cast<ChecksElimination *>(v);
514     visitor->TryRemoveDominatedChecks<Opcode::NegOverflowAndZeroCheck>(
515         inst, [input1](Inst *userInst) { return (userInst->GetInput(0) == input1); });
516     visitor->TryOptimizeOverflowCheck<Opcode::NegOverflowAndZeroCheck>(inst);
517 }
518 
ReplaceUsersAndRemoveCheck(Inst * instDel,Inst * instRep)519 void ChecksElimination::ReplaceUsersAndRemoveCheck(Inst *instDel, Inst *instRep)
520 {
521     auto block = instDel->GetBasicBlock();
522     auto graph = block->GetGraph();
523     if (HasOsrEntryBetween(instRep, instDel)) {
524         COMPILER_LOG(DEBUG, CHECKS_ELIM) << "Check couldn't be deleted, because in OSR mode we can't replace "
525                                             "instructions with instructions placed before OSR entry";
526         return;
527     }
528     instDel->ReplaceUsers(instRep);
529     instDel->RemoveInputs();
530     block->ReplaceInst(instDel, graph->CreateInstNOP());
531     COMPILER_LOG(DEBUG, CHECKS_ELIM) << "Checks elimination delete " << GetOpcodeString(instDel->GetOpcode())
532                                      << " with id " << instDel->GetId();
533     graph->GetEventWriter().EventChecksElimination(GetOpcodeString(instDel->GetOpcode()), instDel->GetId(),
534                                                    instDel->GetPc());
535     SetApplied();
536 }
537 
IsInstIncOrDec(Inst * inst)538 bool ChecksElimination::IsInstIncOrDec(Inst *inst)
539 {
540     return inst->IsAddSub() && inst->GetInput(1).GetInst()->IsConst();
541 }
542 
GetInc(Inst * inst)543 int64_t ChecksElimination::GetInc(Inst *inst)
544 {
545     ASSERT(IsInstIncOrDec(inst));
546     auto val = static_cast<int64_t>(inst->GetInput(1).GetInst()->CastToConstant()->GetIntValue());
547     if (inst->IsSub()) {
548         val = -val;
549     }
550     return val;
551 }
552 
GetLoopForBoundsCheck(BasicBlock * block,Inst * lenArray,Inst * index)553 Loop *ChecksElimination::GetLoopForBoundsCheck(BasicBlock *block, Inst *lenArray, Inst *index)
554 {
555     auto parentIndex = IsInstIncOrDec(index) ? index->GetInput(0).GetInst() : index;
556     auto indexBlock = parentIndex->GetBasicBlock();
557     ASSERT(indexBlock != nullptr);
558     auto indexLoop = indexBlock->GetLoop();
559     if (auto loopInfo = CountableLoopParser(*indexLoop).Parse()) {
560         auto input = lenArray;
561         if (lenArray->GetOpcode() == Opcode::LenArray) {
562             // new lenArray can be inserted
563             input = lenArray->GetDataFlowInput(0);
564         }
565         if (loopInfo->index == parentIndex && input->GetBasicBlock()->IsDominate(indexBlock)) {
566             ASSERT(indexBlock == indexLoop->GetHeader());
567             return indexLoop;
568         }
569     }
570     return block->GetLoop();
571 }
572 
InitItemForNewIndex(GroupedBoundsChecks * place,Inst * index,Inst * inst,bool checkUpper,bool checkLower)573 void ChecksElimination::InitItemForNewIndex(GroupedBoundsChecks *place, Inst *index, Inst *inst, bool checkUpper,
574                                             bool checkLower)
575 {
576     ASSERT(inst->GetOpcode() == Opcode::BoundsCheck);
577     InstVector insts(GetGraph()->GetLocalAllocator()->Adapter());
578     insts.push_back(inst);
579     int64_t val = 0;
580     Inst *parentIndex = index;
581     if (IsInstIncOrDec(index)) {
582         val = GetInc(index);
583         parentIndex = index->GetInput(0).GetInst();
584     } else if (index->IsConst()) {
585         val = static_cast<int64_t>(index->CastToConstant()->GetIntValue());
586         parentIndex = nullptr;
587     }
588     auto maxVal = checkUpper ? val : std::numeric_limits<int64_t>::min();
589     auto minVal = checkLower ? val : std::numeric_limits<int64_t>::max();
590     place->emplace_back(std::make_tuple(parentIndex, insts, maxVal, minVal));
591 }
592 
PushNewBoundsCheck(Loop * loop,Inst * inst,InstPair helpers,bool checkUpper,bool checkLower)593 void ChecksElimination::PushNewBoundsCheck(Loop *loop, Inst *inst, InstPair helpers, bool checkUpper, bool checkLower)
594 {
595     auto *lenArray = helpers.first;
596     auto *index = helpers.second;
597     ASSERT(loop != nullptr && lenArray != nullptr && index != nullptr && inst != nullptr);
598     ASSERT(inst->GetOpcode() == Opcode::BoundsCheck);
599     auto loopChecksIt =
600         std::find_if(boundsChecks_.begin(), boundsChecks_.end(), [=](auto p) { return p.first == loop; });
601     if (loopChecksIt == boundsChecks_.end()) {
602         auto &loopLenArrays = boundsChecks_.emplace_back(loop, GetGraph()->GetLocalAllocator()->Adapter());
603         auto &lenArrayIndexes = loopLenArrays.second.emplace_back(lenArray, GetGraph()->GetLocalAllocator()->Adapter());
604         InitItemForNewIndex(&lenArrayIndexes.second, index, inst, checkUpper, checkLower);
605     } else {
606         auto *lenArrays = &loopChecksIt->second;
607         auto lenArrayIt =
608             std::find_if(lenArrays->begin(), lenArrays->end(), [lenArray](auto p) { return p.first == lenArray; });
609         if (lenArrayIt == lenArrays->end()) {
610             auto &lenArrayIndexes = lenArrays->emplace_back(lenArray, GetGraph()->GetLocalAllocator()->Adapter());
611             InitItemForNewIndex(&lenArrayIndexes.second, index, inst, checkUpper, checkLower);
612         } else {
613             auto *indexes = &lenArrayIt->second;
614             PushNewBoundsCheckAtExistingIndexes(indexes, index, inst, checkUpper, checkLower);
615         }
616     }
617 }
618 
PushNewBoundsCheckAtExistingIndexes(GroupedBoundsChecks * indexes,Inst * index,Inst * inst,bool checkUpper,bool checkLower)619 void ChecksElimination::PushNewBoundsCheckAtExistingIndexes(GroupedBoundsChecks *indexes, Inst *index, Inst *inst,
620                                                             bool checkUpper, bool checkLower)
621 {
622     auto indexIt = std::find_if(indexes->begin(), indexes->end(), [index](auto p) { return std::get<0>(p) == index; });
623     if (indexIt == indexes->end()) {
624         auto parentIndex = index;
625         int64_t val {};
626         if (IsInstIncOrDec(index)) {
627             parentIndex = index->GetInput(0).GetInst();
628             val = GetInc(index);
629         } else if (index->IsConst()) {
630             parentIndex = nullptr;
631             val = static_cast<int64_t>(index->CastToConstant()->GetIntValue());
632         }
633         auto parentIndexIt =
634             std::find_if(indexes->begin(), indexes->end(), [=](auto p) { return std::get<0>(p) == parentIndex; });
635         if (parentIndex == index || parentIndexIt == indexes->end()) {
636             InitItemForNewIndex(indexes, index, inst, checkUpper, checkLower);
637         } else {
638             auto &boundChecks = std::get<1U>(*parentIndexIt);
639             auto &max = std::get<2U>(*parentIndexIt);
640             auto &min = std::get<3U>(*parentIndexIt);
641             boundChecks.push_back(inst);
642             if (val > max && checkUpper) {
643                 max = val;
644             } else if (val < min && checkLower) {
645                 min = val;
646             }
647         }
648     } else {
649         auto &boundChecks = std::get<1U>(*indexIt);
650         auto &max = std::get<2U>(*indexIt);
651         auto &min = std::get<3U>(*indexIt);
652         boundChecks.push_back(inst);
653         if (max < 0 && checkUpper) {
654             max = 0;
655         }
656         if (min > 0 && checkLower) {
657             min = 0;
658         }
659     }
660 }
661 
TryRemoveDominatedNullChecks(Inst * inst,Inst * ref)662 void ChecksElimination::TryRemoveDominatedNullChecks(Inst *inst, Inst *ref)
663 {
664     for (auto &user : ref->GetUsers()) {
665         auto userInst = user.GetInst();
666         if (((userInst->GetOpcode() == Opcode::IsInstance && !userInst->CastToIsInstance()->GetOmitNullCheck()) ||
667              (userInst->GetOpcode() == Opcode::CheckCast && !userInst->CastToCheckCast()->GetOmitNullCheck())) &&
668             inst->IsDominate(userInst)) {
669             COMPILER_LOG(DEBUG, CHECKS_ELIM)
670                 << "NullCheck with id = " << inst->GetId() << " dominate on " << GetOpcodeString(userInst->GetOpcode())
671                 << " with id = " << userInst->GetId();
672             if (userInst->GetOpcode() == Opcode::IsInstance) {
673                 userInst->CastToIsInstance()->SetOmitNullCheck(true);
674             } else {
675                 userInst->CastToCheckCast()->SetOmitNullCheck(true);
676             }
677             SetApplied();
678         }
679     }
680 }
681 
682 template <Opcode OPC, bool CHECK_FULL_DOM, typename CheckInputs>
TryRemoveDominatedCheck(Inst * inst,Inst * userInst,CheckInputs checkInputs)683 void ChecksElimination::TryRemoveDominatedCheck(Inst *inst, Inst *userInst, CheckInputs checkInputs)
684 {
685     // NOLINTNEXTLINE(readability-magic-numbers)
686     if (userInst->GetOpcode() == OPC && userInst != inst && userInst->GetType() == inst->GetType() &&
687         checkInputs(userInst) &&
688         (CHECK_FULL_DOM ? inst->IsDominate(userInst) : inst->InSameBlockOrDominate(userInst))) {
689         COMPILER_LOG(DEBUG, CHECKS_ELIM)
690             // NOLINTNEXTLINE(readability-magic-numbers)
691             << GetOpcodeString(OPC) << " with id = " << inst->GetId() << " dominate on " << GetOpcodeString(OPC)
692             << " with id = " << userInst->GetId();
693         ReplaceUsersAndRemoveCheck(userInst, inst);
694     }
695 }
696 
697 template <Opcode OPC, bool CHECK_FULL_DOM, typename CheckInputs>
TryRemoveDominatedChecks(Inst * inst,CheckInputs checkInputs)698 void ChecksElimination::TryRemoveDominatedChecks(Inst *inst, CheckInputs checkInputs)
699 {
700     for (auto &directUser : inst->GetInput(0).GetInst()->GetUsers()) {
701         auto directUserInst = directUser.GetInst();
702         if ((OPC != Opcode::NullCheck) && (directUserInst->GetOpcode() == Opcode::NullCheck)) {
703             for (auto &actualUser : directUserInst->GetUsers()) {
704                 auto actualUserInst = actualUser.GetInst();
705                 TryRemoveDominatedCheck<OPC, CHECK_FULL_DOM>(inst, actualUserInst, checkInputs);
706             }
707         } else {
708             TryRemoveDominatedCheck<OPC, CHECK_FULL_DOM>(inst, directUserInst, checkInputs);
709         }
710     }
711 }
712 
713 // Remove consecutive checks: NullCheck -> NullCheck -> NullCheck
714 template <Opcode OPC>
TryRemoveConsecutiveChecks(Inst * inst)715 void ChecksElimination::TryRemoveConsecutiveChecks(Inst *inst)
716 {
717     auto end = inst->GetUsers().end();
718     for (auto user = inst->GetUsers().begin(); user != end;) {
719         auto userInst = (*user).GetInst();
720         // NOLINTNEXTLINE(readability-magic-numbers)
721         if (userInst->GetOpcode() == OPC) {
722             // NOLINTNEXTLINE(readability-magic-numbers)
723             COMPILER_LOG(DEBUG, CHECKS_ELIM) << "Remove consecutive " << GetOpcodeString(OPC);
724             ReplaceUsersAndRemoveCheck(userInst, inst);
725             // Start iteration from beginning, because the new successors may be added.
726             user = inst->GetUsers().begin();
727             end = inst->GetUsers().end();
728         } else {
729             ++user;
730         }
731     }
732 }
733 
734 template <Opcode OPC>
TryRemoveCheckByBounds(Inst * inst,Inst * input)735 bool ChecksElimination::TryRemoveCheckByBounds(Inst *inst, Inst *input)
736 {
737     // NOLINTNEXTLINE(readability-magic-numbers)
738     static_assert(OPC == Opcode::ZeroCheck || OPC == Opcode::NegativeCheck || OPC == Opcode::NotPositiveCheck ||
739                   OPC == Opcode::NullCheck);
740     ASSERT(inst->GetOpcode() == OPC);
741     auto block = inst->GetBasicBlock();
742     auto bri = block->GetGraph()->GetBoundsRangeInfo();
743 
744     auto range = bri->FindBoundsRange(block, input);
745     auto rangeChecker = [](const BoundsRange &r) {
746         // NOLINTNEXTLINE(readability-magic-numbers, readability-braces-around-statements, bugprone-branch-clone)
747         if constexpr (OPC == Opcode::ZeroCheck) {
748             return r.IsLess(BoundsRange(0)) || r.IsMore(BoundsRange(0));
749         } else if constexpr (OPC == Opcode::NullCheck) {  // NOLINT
750             return r.IsMore(BoundsRange(0));
751         } else if constexpr (OPC == Opcode::NegativeCheck) {  // NOLINT
752             return r.IsNotNegative();
753         } else if constexpr (OPC == Opcode::NotPositiveCheck) {  // NOLINT
754             return r.IsPositive();
755         }
756     };
757     bool result = rangeChecker(range);
758     if (result) {
759         // NOLINTNEXTLINE(readability-magic-numbers)
760         COMPILER_LOG(DEBUG, CHECKS_ELIM) << GetOpcodeString(OPC) << " have correct bounds";
761 
762         // As bounds analysis was used to prove the check's excessiveness, users should be moved with care:
763         CheckAndSetFlagNoHoist(inst, input, rangeChecker);
764         ReplaceUsersAndRemoveCheck(inst, input);
765     } else {
766         // NOLINTNEXTLINE(readability-magic-numbers, readability-braces-around-statements)
767         if constexpr (OPC == Opcode::ZeroCheck || OPC == Opcode::NullCheck) {
768             result = range.IsEqual(BoundsRange(0));
769         } else if constexpr (OPC == Opcode::NegativeCheck) {  // NOLINT
770             result = range.IsNegative();
771         } else if constexpr (OPC == Opcode::NotPositiveCheck) {  // NOLINT
772             result = range.IsNotPositive();
773         }
774         if (result) {
775             COMPILER_LOG(DEBUG, CHECKS_ELIM)
776                 // NOLINTNEXTLINE(readability-magic-numbers)
777                 << GetOpcodeString(OPC) << " must throw, saved for replace by unconditional deoptimize";
778             PushNewCheckMustThrow(inst);
779         }
780     }
781     return result;
782 }
783 
784 template <Opcode OPC, bool CHECK_FULL_DOM>
TryRemoveCheck(Inst * inst)785 bool ChecksElimination::TryRemoveCheck(Inst *inst)
786 {
787     // NOLINTNEXTLINE(readability-magic-numbers)
788     static_assert(OPC == Opcode::ZeroCheck || OPC == Opcode::NegativeCheck || OPC == Opcode::NotPositiveCheck ||
789                   OPC == Opcode::NullCheck);
790     ASSERT(inst->GetOpcode() == OPC);
791 
792     // NOLINTNEXTLINE(readability-magic-numbers)
793     TryRemoveDominatedChecks<OPC, CHECK_FULL_DOM>(inst);
794     // NOLINTNEXTLINE(readability-magic-numbers)
795     TryRemoveConsecutiveChecks<OPC>(inst);
796 
797     auto input = inst->GetInput(0).GetInst();
798     // NOLINTNEXTLINE(readability-magic-numbers)
799     return TryRemoveCheckByBounds<OPC>(inst, input);
800 }
801 
802 template <Opcode OPC>
TryOptimizeOverflowCheck(Inst * inst)803 void ChecksElimination::TryOptimizeOverflowCheck(Inst *inst)
804 {
805     auto block = inst->GetBasicBlock();
806     auto bri = block->GetGraph()->GetBoundsRangeInfo();
807     auto range = bri->FindBoundsRange(block, inst);
808     bool canOverflow = true;
809     if constexpr (OPC == Opcode::NegOverflowAndZeroCheck) {
810         canOverflow = range.CanOverflowNeg(DataType::INT32);
811     } else {
812         canOverflow = range.CanOverflow(DataType::INT32);
813     }
814     if (!canOverflow) {
815         block->RemoveOverflowCheck(inst);
816         SetApplied();
817         return;
818     }
819     bool constInputs = true;
820     for (size_t i = 0; i < inst->GetInputsCount() - 1; ++i) {
821         constInputs &= inst->GetInput(i).GetInst()->IsConst();
822     }
823     if (constInputs) {
824         // replace by deopt
825         PushNewCheckMustThrow(inst);
826         return;
827     }
828     PushNewCheckForMoveOutOfLoop(inst);
829 }
830 
FindSaveState(Loop * loop)831 Inst *ChecksElimination::FindSaveState(Loop *loop)
832 {
833     auto block = loop->GetPreHeader();
834     while (block != nullptr) {
835         for (const auto &inst : block->InstsSafeReverse()) {
836             if (inst->GetOpcode() == Opcode::SaveStateDeoptimize || inst->GetOpcode() == Opcode::SaveState) {
837                 return inst;
838             }
839         }
840         auto next = block->GetDominator();
841         // The case when the dominant block is the head of a inner loop
842         if (next != nullptr && next->GetLoop()->GetOuterLoop() == block->GetLoop()) {
843             return nullptr;
844         }
845         block = next;
846     }
847     return nullptr;
848 }
849 
FindOptimalSaveStateForHoist(Inst * inst,Inst ** optimalInsertAfter)850 Inst *ChecksElimination::FindOptimalSaveStateForHoist(Inst *inst, Inst **optimalInsertAfter)
851 {
852     ASSERT(inst->RequireState());
853     auto block = inst->GetBasicBlock();
854     if (block == nullptr) {
855         return nullptr;
856     }
857     auto loop = block->GetLoop();
858     *optimalInsertAfter = nullptr;
859 
860     while (!loop->IsRoot() && !loop->GetHeader()->IsOsrEntry() && !loop->IsIrreducible()) {
861         for (auto backEdge : loop->GetBackEdges()) {
862             if (!block->IsDominate(backEdge)) {
863                 // avoid taking checks out of slowpath
864                 return *optimalInsertAfter;
865             }
866         }
867         // Find save state
868         Inst *ss = FindSaveState(loop);
869         if (ss == nullptr) {
870             return *optimalInsertAfter;
871         }
872         auto insertAfter = ss;
873 
874         // Check that inputs are dominate on ss
875         bool inputsAreDominate = true;
876         for (size_t i = 0; i < inst->GetInputsCount() - 1; ++i) {
877             auto input = inst->GetInput(i).GetInst();
878             if (input->IsDominate(insertAfter)) {
879                 continue;
880             }
881             if (insertAfter->GetBasicBlock() == input->GetBasicBlock()) {
882                 insertAfter = input;
883             } else {
884                 inputsAreDominate = false;
885                 break;
886             }
887         }
888 
889         if (!inputsAreDominate) {
890             return *optimalInsertAfter;
891         }
892         *optimalInsertAfter = insertAfter;
893         if (insertAfter != ss) {
894             // some inputs are dominate on insert_after but not dominate on ss, stop here
895             // the only case when return value is not equal to *optimal_insert_after
896             return ss;
897         }
898         block = loop->GetHeader();  // block will be used to check for hot path
899         loop = loop->GetOuterLoop();
900     }
901     return *optimalInsertAfter;
902 }
903 
InsertInstAfter(Inst * inst,Inst * after,BasicBlock * block)904 void ChecksElimination::InsertInstAfter(Inst *inst, Inst *after, BasicBlock *block)
905 {
906     if (after->IsPhi()) {
907         block->PrependInst(inst);
908     } else {
909         block->InsertAfter(inst, after);
910     }
911 }
912 
InsertBoundsCheckDeoptimization(ConditionCode cc,InstPair args,int64_t val,InstPair helpers,Opcode newLeftOpcode)913 void ChecksElimination::InsertBoundsCheckDeoptimization(ConditionCode cc, InstPair args, int64_t val, InstPair helpers,
914                                                         Opcode newLeftOpcode)
915 {
916     auto [left, right] = args;
917     auto [ss, insertAfter] = helpers;
918     auto block = insertAfter->GetBasicBlock();
919     Inst *newLeft = nullptr;
920     if (val == 0 && left != nullptr) {
921         newLeft = left;
922     } else if (left == nullptr) {
923         ASSERT(newLeftOpcode == Opcode::Add);
924         newLeft = GetGraph()->FindOrCreateConstant(val);
925     } else {
926         auto cnst = GetGraph()->FindOrCreateConstant(val);
927         newLeft = GetGraph()->CreateInst(newLeftOpcode);
928         newLeft->SetType(DataType::INT32);
929         newLeft->SetInput(0, left);
930         newLeft->SetInput(1, cnst);
931         if (newLeft->RequireState()) {
932             newLeft->SetSaveState(ss);
933         }
934         InsertInstAfter(newLeft, insertAfter, block);
935         insertAfter = newLeft;
936     }
937     auto deoptComp = GetGraph()->CreateInstCompare(DataType::BOOL, INVALID_PC, newLeft, right, DataType::INT32, cc);
938     auto deopt = GetGraph()->CreateInstDeoptimizeIf(ss->GetPc(), deoptComp, ss, DeoptimizeType::BOUNDS_CHECK);
939     InsertInstAfter(deoptComp, insertAfter, block);
940     block->InsertAfter(deopt, deoptComp);
941 }
942 
FindLoopInfo(Loop * loop)943 std::optional<LoopInfo> ChecksElimination::FindLoopInfo(Loop *loop)
944 {
945     Inst *ss = FindSaveState(loop);
946     if (ss == nullptr) {
947         COMPILER_LOG(DEBUG, CHECKS_ELIM) << "SaveState isn't founded";
948         return std::nullopt;
949     }
950     auto loopParser = CountableLoopParser(*loop);
951     if (auto loopInfo = loopParser.Parse()) {
952         auto loopInfoValue = loopInfo.value();
953         if (loopInfoValue.normalizedCc == CC_NE) {
954             return std::nullopt;
955         }
956         bool isHeadLoopExit = loopInfoValue.ifImm->GetBasicBlock() == loop->GetHeader();
957         bool hasPreHeaderCompare = CountableLoopParser::HasPreHeaderCompare(loop, loopInfoValue);
958         ASSERT(loopInfoValue.index->GetOpcode() == Opcode::Phi);
959         if (loopInfoValue.isInc) {
960             return std::make_tuple(loopInfoValue, ss, loopInfoValue.init, loopInfoValue.test,
961                                    loopInfoValue.normalizedCc == CC_LE ? CC_LE : CC_LT, isHeadLoopExit,
962                                    hasPreHeaderCompare);
963         }
964         return std::make_tuple(loopInfoValue, ss, loopInfoValue.test, loopInfoValue.init, CC_LE, isHeadLoopExit,
965                                hasPreHeaderCompare);
966     }
967     COMPILER_LOG(DEBUG, CHECKS_ELIM) << "Not countable loop isn't supported";
968     return std::nullopt;
969 }
970 
InsertNewLenArray(Inst * lenArray,Inst * ss)971 Inst *ChecksElimination::InsertNewLenArray(Inst *lenArray, Inst *ss)
972 {
973     if (lenArray->IsDominate(ss)) {
974         return lenArray;
975     }
976     if (lenArray->GetOpcode() == Opcode::LenArray) {
977         auto nullCheck = lenArray->GetInput(0).GetInst();
978         auto ref = lenArray->GetDataFlowInput(nullCheck);
979         if (ref->IsDominate(ss)) {
980             // Build nullcheck + lenarray before loop
981             auto nullcheck = GetGraph()->CreateInstNullCheck(DataType::REFERENCE, ss->GetPc(), ref, ss);
982             nullcheck->SetFlag(inst_flags::CAN_DEOPTIMIZE);
983             auto newLenArray = lenArray->Clone(GetGraph());
984             newLenArray->SetInput(0, nullcheck);
985             auto block = ss->GetBasicBlock();
986             block->InsertAfter(newLenArray, ss);
987             block->InsertAfter(nullcheck, ss);
988             COMPILER_LOG(DEBUG, CHECKS_ELIM) << "Builded new NullCheck(id=" << nullcheck->GetId()
989                                              << ") and LenArray(id=" << newLenArray->GetId() << ") before loop";
990             ChecksElimination::VisitNullCheck<true>(this, nullcheck);
991             return newLenArray;
992         }
993     }
994     return nullptr;
995 }
996 
InsertDeoptimizationForIndexOverflow(CountableLoopInfo * countableLoopInfo,BoundsRange indexUpperRange,Inst * ss)997 void ChecksElimination::InsertDeoptimizationForIndexOverflow(CountableLoopInfo *countableLoopInfo,
998                                                              BoundsRange indexUpperRange, Inst *ss)
999 {
1000     auto loopCc = countableLoopInfo->normalizedCc;
1001     if (loopCc == CC_LT || loopCc == CC_LE) {
1002         auto loopUpper = countableLoopInfo->test;
1003         ASSERT(countableLoopInfo->constStep < INT64_MAX);
1004         auto step = static_cast<int64_t>(countableLoopInfo->constStep);
1005         auto indexType = countableLoopInfo->index->GetType();
1006         ASSERT(indexType == DataType::INT32);
1007         auto maxUpper = BoundsRange::GetMax(indexType) - step + (loopCc == CC_LT ? 1 : 0);
1008         auto bri = loopUpper->GetBasicBlock()->GetGraph()->GetBoundsRangeInfo();
1009         auto loopUpperRange = bri->FindBoundsRange(countableLoopInfo->index->GetBasicBlock(), loopUpper);
1010         // Upper bound of loop index assuming (index + maxAdd < lenArray)
1011         indexUpperRange = indexUpperRange.Add(BoundsRange(step)).FitInType(indexType);
1012         if (!BoundsRange(maxUpper).IsMoreOrEqual(loopUpperRange) && indexUpperRange.IsMaxRange(indexType)) {
1013             // loop index can overflow
1014             Inst *insertAfter = loopUpper->IsDominate(ss) ? ss : loopUpper;
1015             COMPILER_LOG(DEBUG, CHECKS_ELIM) << "Build deoptimize for loop index overflow";
1016             // Create deoptimize if loop index can become negative
1017             InsertBoundsCheckDeoptimization(ConditionCode::CC_LT, {nullptr, loopUpper}, maxUpper, {ss, insertAfter});
1018         }
1019     }
1020 }
1021 
NeedUpperDeoptimization(BasicBlock * header,InstPair insts,FlagPair flags,int64_t maxAdd,bool * insertNewLenArray)1022 bool ChecksElimination::NeedUpperDeoptimization(BasicBlock *header, InstPair insts, FlagPair flags, int64_t maxAdd,
1023                                                 bool *insertNewLenArray)
1024 {
1025     auto *lenArray = insts.first;
1026     auto *upper = insts.second;
1027     auto [ccIsLt, upperRangeIsMax] = flags;
1028     auto bri = GetGraph()->GetBoundsRangeInfo();
1029     auto newUpper = upper;
1030     auto newMaxAdd = maxAdd;
1031     int64_t upperAdd = 0;
1032     if (IsInstIncOrDec(upper)) {
1033         upperAdd = GetInc(upper);
1034         newMaxAdd += upperAdd;
1035         newUpper = upper->GetInput(0).GetInst();
1036     }
1037     if (lenArray == newUpper) {
1038         if (newMaxAdd < 0 || (newMaxAdd == 0 && ccIsLt)) {
1039             return false;
1040         }
1041     }
1042     auto useUpperLen = upperAdd >= 0 || !upperRangeIsMax;
1043     if (useUpperLen && newMaxAdd <= 0) {
1044         auto newUpperRange = bri->FindBoundsRange(header, newUpper);
1045         if (newUpperRange.GetLenArray() == lenArray) {
1046             return false;
1047         }
1048     }
1049     *insertNewLenArray = newUpper != lenArray;
1050     return true;
1051 }
1052 
TryInsertDeoptimizationForLargeStep(ConditionCode cc,InstPair bounds,InstTriple helpers,int64_t maxAdd,uint64_t constStep)1053 bool ChecksElimination::TryInsertDeoptimizationForLargeStep(ConditionCode cc, InstPair bounds, InstTriple helpers,
1054                                                             int64_t maxAdd, uint64_t constStep)
1055 {
1056     auto [lower, upper] = bounds;
1057     auto [insertDeoptAfter, ss, resultLenArray] = helpers;
1058     auto block = insertDeoptAfter->GetBasicBlock();
1059     if (!lower->IsDominate(insertDeoptAfter)) {
1060         if (lower->GetBasicBlock() == block) {
1061             insertDeoptAfter = lower;
1062         } else {
1063             return false;
1064         }
1065     }
1066     auto subValue = lower;
1067     if (cc == CC_LT) {
1068         subValue = GetGraph()->CreateInstAdd(DataType::INT32, INVALID_PC, lower, GetGraph()->FindOrCreateConstant(1));
1069         InsertInstAfter(subValue, insertDeoptAfter, block);
1070         insertDeoptAfter = subValue;
1071     }
1072     auto sub = GetGraph()->CreateInstSub(DataType::INT32, INVALID_PC, upper, subValue);
1073     InsertInstAfter(sub, insertDeoptAfter, block);
1074     auto mod = GetGraph()->CreateInstMod(DataType::INT32, INVALID_PC, sub, GetGraph()->FindOrCreateConstant(constStep));
1075     block->InsertAfter(mod, sub);
1076     if (resultLenArray == upper) {
1077         auto maxAddConst = GetGraph()->FindOrCreateConstant(maxAdd);
1078         // (upper - lower [- 1]) % step </<= maxAdd
1079         InsertBoundsCheckDeoptimization(cc, {mod, maxAddConst}, 0, {ss, mod}, Opcode::NOP);
1080     } else {
1081         // result_len_array - maxAdd </<= upper - (upper - lower [- 1]) % step
1082         auto maxIndexValue = GetGraph()->CreateInstSub(DataType::INT32, INVALID_PC, upper, mod);
1083         block->InsertAfter(maxIndexValue, mod);
1084         auto opcode = maxAdd > 0 ? Opcode::Sub : Opcode::SubOverflowCheck;
1085         InsertBoundsCheckDeoptimization(cc, {resultLenArray, maxIndexValue}, maxAdd, {ss, maxIndexValue}, opcode);
1086     }
1087     return true;
1088 }
1089 
TryInsertDeoptimization(LoopInfo loopInfo,Inst * lenArray,int64_t maxAdd,int64_t minAdd,bool hasCheckInHeader)1090 bool ChecksElimination::TryInsertDeoptimization(LoopInfo loopInfo, Inst *lenArray, int64_t maxAdd, int64_t minAdd,
1091                                                 bool hasCheckInHeader)
1092 {
1093     auto [countableLoopInfo, ss, lower, upper, cc, isHeadLoopExit, hasPreHeaderCompare] = loopInfo;
1094     ASSERT(cc == CC_LT || cc == CC_LE);
1095     auto bri = GetGraph()->GetBoundsRangeInfo();
1096     auto header = countableLoopInfo.index->GetBasicBlock();
1097     auto upperRange = bri->FindBoundsRange(header, upper);
1098     auto lowerRange = bri->FindBoundsRange(header, lower);
1099     auto lenArrayRange = bri->FindBoundsRange(header, lenArray);
1100     auto hasCheckBeforeExit = hasCheckInHeader || !isHeadLoopExit;
1101     if (!hasPreHeaderCompare && !lowerRange.IsLess(upperRange) && hasCheckBeforeExit) {
1102         // if lower > upper, removing BoundsCheck may be wrong for the first iteration
1103         return false;
1104     }
1105     uint64_t lowerInc = (countableLoopInfo.normalizedCc == CC_GT ? 1 : 0);
1106     bool needLowerDeopt = (minAdd != std::numeric_limits<int64_t>::max()) &&
1107                           !lowerRange.Add(BoundsRange(minAdd)).Add(BoundsRange(lowerInc)).IsNotNegative();
1108     bool insertLowerDeopt = lower->IsDominate(ss);
1109     if (needLowerDeopt && !insertLowerDeopt) {
1110         COMPILER_LOG(DEBUG, CHECKS_ELIM) << "Unable to build deoptimize for lower value";
1111         return false;
1112     }
1113 
1114     bool insertNewLenArray = false;
1115     bool correctMax = (maxAdd != std::numeric_limits<int64_t>::min());
1116     bool correctUpperRange = upperRange.Add(BoundsRange(maxAdd)).IsLess(lenArrayRange);
1117     bool upperRangeIsMax = upperRange.IsMaxRange(upper->GetType());
1118     if (correctMax && !correctUpperRange &&
1119         NeedUpperDeoptimization(header, {lenArray, upper}, {cc == CC_LT, upperRangeIsMax}, maxAdd,
1120                                 &insertNewLenArray)) {
1121         if (!TryInsertUpperDeoptimization(loopInfo, lenArray, lowerRange, maxAdd, insertNewLenArray)) {
1122             return false;
1123         }
1124     }
1125     InsertDeoptimizationForIndexOverflow(&countableLoopInfo, lenArrayRange.Sub(BoundsRange(maxAdd)), ss);
1126     if (needLowerDeopt) {
1127         COMPILER_LOG(DEBUG, CHECKS_ELIM) << "Build deoptimize for lower value";
1128         // Create deoptimize if lower < 0 (or -1 for loop with CC_GT)
1129         auto lowerConst = GetGraph()->FindOrCreateConstant(-lowerInc);
1130         InsertBoundsCheckDeoptimization(ConditionCode::CC_LT, {lower, lowerConst}, minAdd, {ss, ss});
1131     }
1132     return true;
1133 }
1134 
TryInsertUpperDeoptimization(LoopInfo loopInfo,Inst * lenArray,BoundsRange lowerRange,int64_t maxAdd,bool insertNewLenArray)1135 bool ChecksElimination::TryInsertUpperDeoptimization(LoopInfo loopInfo, Inst *lenArray, BoundsRange lowerRange,
1136                                                      int64_t maxAdd, bool insertNewLenArray)
1137 {
1138     [[maybe_unused]] auto [countableLoopInfo, ss, lower, upper, cc, isHeadLoopExit, hasPreHeaderCompare] = loopInfo;
1139     auto header = countableLoopInfo.index->GetBasicBlock();
1140     auto resultLenArray = insertNewLenArray ? InsertNewLenArray(lenArray, ss) : lenArray;
1141     if (resultLenArray == nullptr) {
1142         COMPILER_LOG(DEBUG, CHECKS_ELIM) << "Unable to build deoptimize for upper value";
1143         return false;
1144     }
1145     auto insertDeoptAfter = lenArray != resultLenArray ? resultLenArray : ss;
1146     if (!upper->IsDominate(insertDeoptAfter)) {
1147         insertDeoptAfter = upper;
1148     }
1149     ASSERT(insertDeoptAfter->GetBasicBlock()->IsDominate(header));
1150     if (insertDeoptAfter->GetBasicBlock() == header) {
1151         COMPILER_LOG(DEBUG, CHECKS_ELIM) << "Unable to build deoptimize for upper value";
1152         return false;
1153     }
1154     auto constStep = countableLoopInfo.constStep;
1155     COMPILER_LOG(DEBUG, CHECKS_ELIM) << "Try to build deoptimize for upper value";
1156     if (constStep == 1 || (countableLoopInfo.normalizedCc == CC_GT || countableLoopInfo.normalizedCc == CC_GE)) {
1157         auto opcode = maxAdd > 0 ? Opcode::Sub : Opcode::SubOverflowCheck;
1158         // Create deoptimize if resultLenArray - maxAdd <=(<) upper
1159         // resultLenArray is >= 0, so if maxAdd > 0, overflow is not possible
1160         // that's why we do not add maxAdd to upper instead
1161         InsertBoundsCheckDeoptimization(cc, {resultLenArray, upper}, maxAdd, {ss, insertDeoptAfter}, opcode);
1162     } else if (lowerRange.IsConst() && lowerRange.GetLeft() == 0 && countableLoopInfo.normalizedCc == CC_LT &&
1163                resultLenArray == upper && maxAdd == static_cast<int64_t>(constStep) - 1) {
1164         // Example: for (int i = 0; i < len; i += x) process(a[i], ..., a[i + x - 1])
1165         // deoptimize if len % x != 0
1166         auto zeroConst = GetGraph()->FindOrCreateConstant(0);
1167         InsertBoundsCheckDeoptimization(ConditionCode::CC_NE, {resultLenArray, zeroConst}, constStep,
1168                                         {ss, insertDeoptAfter}, Opcode::Mod);
1169     } else if (!TryInsertDeoptimizationForLargeStep(cc, {lower, upper}, {insertDeoptAfter, ss, resultLenArray}, maxAdd,
1170                                                     constStep)) {
1171         COMPILER_LOG(DEBUG, CHECKS_ELIM) << "Unable to build deoptimize for upper value with step > 1";
1172         return false;
1173     }
1174     return true;
1175 }
1176 
HoistLoopInvariantBoundsChecks(Inst * lenArray,GroupedBoundsChecks * indexBoundschecks,Loop * loop)1177 void ChecksElimination::HoistLoopInvariantBoundsChecks(Inst *lenArray, GroupedBoundsChecks *indexBoundschecks,
1178                                                        Loop *loop)
1179 {
1180     auto lenArrLoop = lenArray->GetBasicBlock()->GetLoop();
1181     // lenArray isn't loop invariant
1182     if (lenArrLoop == loop) {
1183         return;
1184     }
1185     for (auto &[index, boundChecks, max, min] : *indexBoundschecks) {
1186         // Check that index is loop invariant, if index is nullptr it means that it was a constant
1187         if (index != nullptr && index->GetBasicBlock()->GetLoop() == loop) {
1188             continue;
1189         }
1190         for (auto boundsCheck : boundChecks) {
1191             PushNewCheckForMoveOutOfLoop(boundsCheck);
1192         }
1193     }
1194 }
1195 
ProcessingGroupBoundsCheck(GroupedBoundsChecks * indexBoundschecks,LoopInfo loopInfo,Inst * lenArray)1196 void ChecksElimination::ProcessingGroupBoundsCheck(GroupedBoundsChecks *indexBoundschecks, LoopInfo loopInfo,
1197                                                    Inst *lenArray)
1198 {
1199     auto phiIndex = std::get<0>(loopInfo).index;
1200     auto phiIndexIt = std::find_if(indexBoundschecks->begin(), indexBoundschecks->end(),
1201                                    [=](auto p) { return std::get<0>(p) == phiIndex; });
1202     if (phiIndexIt == indexBoundschecks->end()) {
1203         HoistLoopInvariantBoundsChecks(lenArray, indexBoundschecks, phiIndex->GetBasicBlock()->GetLoop());
1204         return;
1205     }
1206     const auto &instsToDelete = std::get<1U>(*phiIndexIt);
1207     auto maxAdd = std::get<2U>(*phiIndexIt);
1208     auto minAdd = std::get<3U>(*phiIndexIt);
1209     ASSERT(!instsToDelete.empty());
1210     bool hasCheckInHeader = false;
1211     for (const auto &inst : instsToDelete) {
1212         if (inst->GetBasicBlock() == phiIndex->GetBasicBlock()) {
1213             hasCheckInHeader = true;
1214         }
1215     }
1216     if (TryInsertDeoptimization(loopInfo, lenArray, maxAdd, minAdd, hasCheckInHeader)) {
1217         COMPILER_LOG(DEBUG, CHECKS_ELIM) << "Delete group of BoundsChecks";
1218         // Delete bounds checks instructions
1219         for (const auto &inst : instsToDelete) {
1220             ReplaceUsersAndRemoveCheck(inst, inst->GetInput(1).GetInst());
1221             auto it = std::find_if(indexBoundschecks->begin(), indexBoundschecks->end(),
1222                                    [=](auto p) { return std::get<0>(p) == inst->GetInput(1).GetInst(); });
1223             if (it != indexBoundschecks->end()) {
1224                 std::get<0>(*it) = nullptr;
1225                 std::get<1>(*it).clear();
1226             }
1227         }
1228     }
1229 }
1230 
ProcessingLoop(Loop * loop,LoopNotFullyRedundantBoundsCheck * lenarrIndexChecks)1231 void ChecksElimination::ProcessingLoop(Loop *loop, LoopNotFullyRedundantBoundsCheck *lenarrIndexChecks)
1232 {
1233     auto loopInfo = FindLoopInfo(loop);
1234     if (loopInfo == std::nullopt) {
1235         return;
1236     }
1237     for (auto it = lenarrIndexChecks->rbegin(); it != lenarrIndexChecks->rend(); it++) {
1238         ProcessingGroupBoundsCheck(&it->second, loopInfo.value(), it->first);
1239     }
1240 }
1241 
ReplaceBoundsCheckToDeoptimizationBeforeLoop()1242 void ChecksElimination::ReplaceBoundsCheckToDeoptimizationBeforeLoop()
1243 {
1244     COMPILER_LOG(DEBUG, CHECKS_ELIM) << "Start ReplaceBoundsCheckToDeoptimizationBeforeLoop";
1245     for (auto &[loop, lenarrIndexChecks] : boundsChecks_) {
1246         COMPILER_LOG(DEBUG, CHECKS_ELIM) << "Processing loop with id = " << loop->GetId();
1247         if (loop->IsRoot()) {
1248             COMPILER_LOG(DEBUG, CHECKS_ELIM) << "Skip root loop";
1249             continue;
1250         }
1251         ProcessingLoop(loop, &lenarrIndexChecks);
1252     }
1253     COMPILER_LOG(DEBUG, CHECKS_ELIM) << "Finish ReplaceBoundsCheckToDeoptimizationBeforeLoop";
1254 }
1255 
MoveCheckOutOfLoop()1256 void ChecksElimination::MoveCheckOutOfLoop()
1257 {
1258     for (auto inst : checksForMoveOutOfLoop_) {
1259         Inst *insertAfter = nullptr;
1260         auto ss = FindOptimalSaveStateForHoist(inst, &insertAfter);
1261         if (ss == nullptr) {
1262             continue;
1263         }
1264         if (inst->GetOpcode() == Opcode::CheckCast) {
1265             // input object can become null after hoisting
1266             inst->CastToCheckCast()->SetOmitNullCheck(false);
1267         }
1268         ASSERT(insertAfter != nullptr);
1269         ASSERT(ss->GetBasicBlock() == insertAfter->GetBasicBlock());
1270         auto block = inst->GetBasicBlock();
1271         COMPILER_LOG(DEBUG, CHECKS_ELIM) << "Move check " << GetOpcodeString(inst->GetOpcode())
1272                                          << " with id = " << inst->GetId() << " from bb " << block->GetId() << " to bb "
1273                                          << ss->GetBasicBlock()->GetId();
1274         block->EraseInst(inst);
1275         ss->GetBasicBlock()->InsertAfter(inst, insertAfter);
1276         inst->SetSaveState(ss);
1277         inst->SetPc(ss->GetPc());
1278         inst->SetFlag(inst_flags::CAN_DEOPTIMIZE);
1279         SetApplied();
1280     }
1281 }
1282 
FindSaveState(const InstVector & instsToDelete)1283 Inst *ChecksElimination::FindSaveState(const InstVector &instsToDelete)
1284 {
1285     for (auto boundsCheck : instsToDelete) {
1286         bool isDominate = true;
1287         for (auto boundsCheckTest : instsToDelete) {
1288             if (boundsCheck == boundsCheckTest) {
1289                 continue;
1290             }
1291             isDominate &= boundsCheck->IsDominate(boundsCheckTest);
1292         }
1293         if (isDominate) {
1294             constexpr auto IMM_2 = 2;
1295             return boundsCheck->GetInput(IMM_2).GetInst();
1296         }
1297     }
1298     return nullptr;
1299 }
1300 
ReplaceOneBoundsCheckToDeoptimizationInLoop(std::pair<Loop *,LoopNotFullyRedundantBoundsCheck> & item)1301 void ChecksElimination::ReplaceOneBoundsCheckToDeoptimizationInLoop(
1302     std::pair<Loop *, LoopNotFullyRedundantBoundsCheck> &item)
1303 {
1304     for (auto &[lenArray, indexBoundchecks] : item.second) {
1305         for (auto &[index, instsToDelete, max, min] : indexBoundchecks) {
1306             constexpr auto MIN_BOUNDSCHECKS_NUM = 2;
1307             if (instsToDelete.size() <= MIN_BOUNDSCHECKS_NUM) {
1308                 COMPILER_LOG(DEBUG, CHECKS_ELIM) << "Skip small group of BoundsChecks";
1309                 continue;
1310             }
1311             // Try to replace more than 2 bounds checks to deoptimization in loop
1312             auto saveState = FindSaveState(instsToDelete);
1313             if (saveState == nullptr) {
1314                 COMPILER_LOG(DEBUG, CHECKS_ELIM) << "SaveState isn't founded";
1315                 continue;
1316             }
1317             COMPILER_LOG(DEBUG, CHECKS_ELIM) << "Replace group of BoundsChecks on deoptimization in loop";
1318             auto insertAfter = lenArray->IsDominate(saveState) ? saveState : lenArray;
1319             if (max != std::numeric_limits<int64_t>::min()) {
1320                 // Create deoptimize if max_index >= lenArray
1321                 InsertBoundsCheckDeoptimization(ConditionCode::CC_GE, {index, lenArray}, max, {saveState, insertAfter});
1322             }
1323             if (index != nullptr && min != std::numeric_limits<int64_t>::max()) {
1324                 // Create deoptimize if min_index < 0
1325                 auto zeroConst = GetGraph()->FindOrCreateConstant(0);
1326                 InsertBoundsCheckDeoptimization(ConditionCode::CC_LT, {index, zeroConst}, min,
1327                                                 {saveState, insertAfter});
1328             } else {
1329                 // No lower check needed based on BoundsAnalysis
1330                 // if index is null, group of bounds checks consists of constants
1331                 ASSERT(min >= 0);
1332             }
1333             for (const auto &inst : instsToDelete) {
1334                 COMPILER_LOG(DEBUG, CHECKS_ELIM) << "Delete group of BoundsChecks";
1335                 ReplaceUsersAndRemoveCheck(inst, inst->GetInput(1).GetInst());
1336             }
1337         }
1338     }
1339 }
1340 
ReplaceBoundsCheckToDeoptimizationInLoop()1341 void ChecksElimination::ReplaceBoundsCheckToDeoptimizationInLoop()
1342 {
1343     COMPILER_LOG(DEBUG, CHECKS_ELIM) << "Start ReplaceBoundsCheckToDeoptimizationInLoop";
1344     for (auto &item : boundsChecks_) {
1345         ReplaceOneBoundsCheckToDeoptimizationInLoop(item);
1346     }
1347     COMPILER_LOG(DEBUG, CHECKS_ELIM) << "Finish ReplaceBoundsCheckToDeoptimizationInLoop";
1348 }
1349 
ReplaceCheckMustThrowByUnconditionalDeoptimize()1350 void ChecksElimination::ReplaceCheckMustThrowByUnconditionalDeoptimize()
1351 {
1352     for (auto &inst : checksMustThrow_) {
1353         auto block = inst->GetBasicBlock();
1354         if (block != nullptr) {
1355             COMPILER_LOG(DEBUG, CHECKS_ELIM)
1356                 << "Replace check with id = " << inst->GetId() << " by uncondition deoptimize";
1357             block->ReplaceInstByDeoptimize(inst);
1358             SetApplied();
1359             SetLoopDeleted();
1360         }
1361     }
1362 }
1363 
1364 }  // namespace ark::compiler
1365