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