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