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