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 "unit_test.h"
17 #include "optimizer/optimizations/checks_elimination.h"
18 #include "optimizer/optimizations/cleanup.h"
19 #include "optimizer/ir/runtime_interface.h"
20 #include "optimizer/ir/graph_cloner.h"
21
22 namespace ark::compiler {
23 class ChecksEliminationTest : public CommonTest {
24 public:
ChecksEliminationTest()25 ChecksEliminationTest() : graph_(CreateGraphStartEndBlocks()) {}
26
GetGraph()27 Graph *GetGraph()
28 {
29 return graph_;
30 }
31
32 struct RuntimeInterfaceNotStaticMethod : public compiler::RuntimeInterface {
IsMethodStaticark::compiler::ChecksEliminationTest::RuntimeInterfaceNotStaticMethod33 bool IsMethodStatic([[maybe_unused]] MethodPtr method) const override
34 {
35 return false;
36 }
37 };
38
39 // NOLINTBEGIN(readability-magic-numbers)
40 template <bool IS_APPLIED>
SimpleTest(int32_t index,int32_t arrayLen)41 void SimpleTest(int32_t index, int32_t arrayLen)
42 {
43 auto graph1 = CreateEmptyGraph();
44 GRAPH(graph1)
45 {
46 CONSTANT(0U, arrayLen);
47 CONSTANT(1U, index);
48 BASIC_BLOCK(2U, 1U)
49 {
50 INST(2U, Opcode::SaveState).Inputs(0U, 1U).SrcVregs({0U, 1U});
51 INST(44U, Opcode::LoadAndInitClass).ref().Inputs(2U).TypeId(68U);
52 INST(3U, Opcode::NewArray).ref().Inputs(44U, 0U, 2U);
53 INST(4U, Opcode::BoundsCheck).s32().Inputs(0U, 1U, 2U);
54 INST(5U, Opcode::LoadArray).s32().Inputs(3U, 4U);
55 INST(6U, Opcode::Return).s32().Inputs(5U);
56 }
57 }
58 graph1->RunPass<ChecksElimination>();
59 auto graph2 = CreateEmptyGraph();
60 if constexpr (IS_APPLIED) {
61 GRAPH(graph2)
62 {
63 CONSTANT(0U, arrayLen);
64 CONSTANT(1U, index);
65 BASIC_BLOCK(2U, 1U)
66 {
67 INST(2U, Opcode::SaveState).Inputs(0U, 1U).SrcVregs({0U, 1U});
68 INST(44U, Opcode::LoadAndInitClass).ref().Inputs(2U).TypeId(68U);
69 INST(3U, Opcode::NewArray).ref().Inputs(44U, 0U, 2U);
70 INST(4U, Opcode::NOP);
71 INST(5U, Opcode::LoadArray).s32().Inputs(3U, 1U);
72 INST(6U, Opcode::Return).s32().Inputs(5U);
73 }
74 }
75 } else {
76 GRAPH(graph2)
77 {
78 CONSTANT(0U, arrayLen);
79 CONSTANT(1U, index);
80 BASIC_BLOCK(2U, 1U)
81 {
82 INST(2U, Opcode::SaveState).Inputs(0U, 1U).SrcVregs({0U, 1U});
83 INST(44U, Opcode::LoadAndInitClass).ref().Inputs(2U).TypeId(68U);
84 INST(3U, Opcode::NewArray).ref().Inputs(44U, 0U, 2U);
85 INST(4U, Opcode::Deoptimize).DeoptimizeType(DeoptimizeType::BOUNDS_CHECK).Inputs(2U);
86 }
87 }
88 }
89 ASSERT_TRUE(GraphComparator().Compare(graph1, graph2));
90 }
91
92 enum AppliedType { NOT_APPLIED, REMOVED, REPLACED };
93
ArithmeticTestInput(int32_t index,int32_t arrayLen,Opcode opc,int32_t val)94 Graph *ArithmeticTestInput(int32_t index, int32_t arrayLen, Opcode opc, int32_t val)
95 {
96 auto graph = CreateEmptyGraph();
97 GRAPH(graph)
98 {
99 CONSTANT(0U, arrayLen);
100 CONSTANT(1U, index);
101 CONSTANT(2U, val);
102 BASIC_BLOCK(2U, 1U)
103 {
104 INST(3U, Opcode::SaveState).Inputs(0U, 1U, 2U).SrcVregs({0U, 1U, 2U});
105 INST(44U, Opcode::LoadAndInitClass).ref().Inputs(3U).TypeId(68U);
106 INST(4U, Opcode::NewArray).ref().Inputs(44U, 0U, 3U);
107 INST(5U, opc).s32().Inputs(1U, 2U);
108 INST(6U, Opcode::BoundsCheck).s32().Inputs(0U, 5U, 3U);
109 INST(7U, Opcode::LoadArray).s32().Inputs(4U, 6U);
110 INST(8U, Opcode::Return).s32().Inputs(7U);
111 }
112 }
113 return graph;
114 }
115
ArithmeticTestOutput1(int32_t index,int32_t arrayLen,Opcode opc,int32_t val)116 Graph *ArithmeticTestOutput1(int32_t index, int32_t arrayLen, Opcode opc, int32_t val)
117 {
118 auto graph = CreateEmptyGraph();
119
120 GRAPH(graph)
121 {
122 CONSTANT(0U, arrayLen);
123 CONSTANT(1U, index);
124 CONSTANT(2U, val);
125 BASIC_BLOCK(2U, 1U)
126 {
127 INST(3U, Opcode::SaveState).Inputs(0U, 1U, 2U).SrcVregs({0U, 1U, 2U});
128 INST(44U, Opcode::LoadAndInitClass).ref().Inputs(3U).TypeId(68U);
129 INST(4U, Opcode::NewArray).ref().Inputs(44U, 0U, 3U);
130 INST(5U, opc).s32().Inputs(1U, 2U);
131 INST(6U, Opcode::NOP);
132 INST(7U, Opcode::LoadArray).s32().Inputs(4U, 5U);
133 INST(8U, Opcode::Return).s32().Inputs(7U);
134 }
135 }
136
137 return graph;
138 }
139
ArithmeticTestOutput2(int32_t index,int32_t arrayLen,Opcode opc,int32_t val)140 Graph *ArithmeticTestOutput2(int32_t index, int32_t arrayLen, Opcode opc, int32_t val)
141 {
142 auto graph = CreateEmptyGraph();
143
144 GRAPH(graph)
145 {
146 CONSTANT(0U, arrayLen);
147 CONSTANT(1U, index);
148 CONSTANT(2U, val);
149 BASIC_BLOCK(2U, 1U)
150 {
151 INST(3U, Opcode::SaveState).Inputs(0U, 1U, 2U).SrcVregs({0U, 1U, 2U});
152 INST(44U, Opcode::LoadAndInitClass).ref().Inputs(3U).TypeId(68U);
153 INST(4U, Opcode::NewArray).ref().Inputs(44U, 0U, 3U);
154 INST(5U, opc).s32().Inputs(1U, 2U);
155 INST(6U, Opcode::Deoptimize).DeoptimizeType(DeoptimizeType::BOUNDS_CHECK).Inputs(3U);
156 }
157 }
158 return graph;
159 }
160
161 template <AppliedType APPLIED_TYPE>
ArithmeticTest(int32_t index,int32_t arrayLen,Opcode opc,int32_t val)162 void ArithmeticTest(int32_t index, int32_t arrayLen, Opcode opc, int32_t val)
163 {
164 auto graph1 = ArithmeticTestInput(index, arrayLen, opc, val);
165 auto clone = GraphCloner(graph1, graph1->GetAllocator(), graph1->GetLocalAllocator()).CloneGraph();
166 bool result = graph1->RunPass<ChecksElimination>();
167 Graph *graph2 = nullptr;
168 if constexpr (APPLIED_TYPE == AppliedType::REMOVED) {
169 graph2 = ArithmeticTestOutput1(index, arrayLen, opc, val);
170 ASSERT_TRUE(result);
171 } else if constexpr (APPLIED_TYPE == AppliedType::REPLACED) {
172 graph2 = ArithmeticTestOutput2(index, arrayLen, opc, val);
173 ASSERT_TRUE(result);
174 } else {
175 ASSERT_FALSE(result);
176 }
177 ASSERT_TRUE(GraphComparator().Compare(graph1, (APPLIED_TYPE == AppliedType::NOT_APPLIED) ? clone : graph2));
178 }
179
ModTestInput(int32_t arrayLen,int32_t mod)180 Graph *ModTestInput(int32_t arrayLen, int32_t mod)
181 {
182 auto graph = CreateEmptyGraph();
183
184 GRAPH(graph)
185 {
186 CONSTANT(0U, arrayLen);
187 CONSTANT(1U, mod);
188 CONSTANT(12U, 0U);
189 PARAMETER(2U, 0U).s32();
190 BASIC_BLOCK(2U, 3U, 4U)
191 {
192 INST(10U, Opcode::Compare).b().CC(CC_GE).SrcType(DataType::Type::INT32).Inputs(2U, 12U);
193 INST(11U, Opcode::IfImm).SrcType(compiler::DataType::BOOL).CC(compiler::CC_NE).Imm(0U).Inputs(10U);
194 }
195 BASIC_BLOCK(3U, 1U)
196 {
197 INST(3U, Opcode::SaveState).Inputs(0U, 1U, 2U).SrcVregs({0U, 1U, 2U});
198 INST(44U, Opcode::LoadAndInitClass).ref().Inputs(3U).TypeId(68U);
199 INST(4U, Opcode::NewArray).ref().Inputs(44U, 0U, 3U);
200 INST(5U, Opcode::Mod).s32().Inputs(2U, 1U);
201 INST(6U, Opcode::BoundsCheck).s32().Inputs(0U, 5U, 3U);
202 INST(7U, Opcode::LoadArray).s32().Inputs(4U, 6U);
203 INST(8U, Opcode::Return).s32().Inputs(7U);
204 }
205 BASIC_BLOCK(4U, 1U)
206 {
207 INST(9U, Opcode::Return).s32().Inputs(1U);
208 }
209 }
210
211 return graph;
212 }
213
ModTestOutput(int32_t arrayLen,int32_t mod)214 Graph *ModTestOutput(int32_t arrayLen, int32_t mod)
215 {
216 auto graph = CreateEmptyGraph();
217
218 GRAPH(graph)
219 {
220 CONSTANT(0U, arrayLen);
221 CONSTANT(1U, mod);
222 CONSTANT(12U, 0U);
223 PARAMETER(2U, 0U).s32();
224 BASIC_BLOCK(2U, 3U, 4U)
225 {
226 INST(10U, Opcode::Compare).b().CC(CC_GE).SrcType(DataType::Type::INT32).Inputs(2U, 12U);
227 INST(11U, Opcode::IfImm).SrcType(compiler::DataType::BOOL).CC(compiler::CC_NE).Imm(0U).Inputs(10U);
228 }
229 BASIC_BLOCK(3U, 1U)
230 {
231 INST(3U, Opcode::SaveState).Inputs(0U, 1U, 2U).SrcVregs({0U, 1U, 2U});
232 INST(44U, Opcode::LoadAndInitClass).ref().Inputs(3U).TypeId(68U);
233 INST(4U, Opcode::NewArray).ref().Inputs(44U, 0U, 3U);
234 INST(5U, Opcode::Mod).s32().Inputs(2U, 1U);
235 INST(6U, Opcode::NOP);
236 INST(7U, Opcode::LoadArray).s32().Inputs(4U, 5U);
237 INST(8U, Opcode::Return).s32().Inputs(7U);
238 }
239 BASIC_BLOCK(4U, 1U)
240 {
241 INST(9U, Opcode::Return).s32().Inputs(1U);
242 }
243 }
244 return graph;
245 }
246
247 template <bool IS_APPLIED>
ModTest(int32_t arrayLen,int32_t mod)248 void ModTest(int32_t arrayLen, int32_t mod)
249 {
250 auto graph1 = ModTestInput(arrayLen, mod);
251
252 Graph *graph2 = nullptr;
253 if constexpr (IS_APPLIED) {
254 graph2 = ModTestOutput(arrayLen, mod);
255 } else {
256 graph2 = GraphCloner(graph1, graph1->GetAllocator(), graph1->GetLocalAllocator()).CloneGraph();
257 }
258 graph1->RunPass<ChecksElimination>();
259 ASSERT_TRUE(GraphComparator().Compare(graph1, graph2));
260 }
261
PhiTestInput(int32_t index,int32_t lenArray,int32_t mod)262 Graph *PhiTestInput(int32_t index, int32_t lenArray, int32_t mod)
263 {
264 auto graph = CreateEmptyGraph();
265
266 GRAPH(graph)
267 {
268 PARAMETER(0U, 0U).b();
269 PARAMETER(1U, 1U).s32();
270 CONSTANT(2U, lenArray); // len array
271 CONSTANT(3U, index); // index 2
272 CONSTANT(12U, mod);
273 CONSTANT(13U, 0U);
274 BASIC_BLOCK(11U, 2U, 5U)
275 {
276 INST(41U, Opcode::Compare).b().CC(CC_GE).SrcType(DataType::Type::INT32).Inputs(1U, 13U);
277 INST(42U, Opcode::IfImm).SrcType(compiler::DataType::BOOL).CC(compiler::CC_NE).Imm(0U).Inputs(41U);
278 }
279 BASIC_BLOCK(2U, 3U, 4U)
280 {
281 INST(43U, Opcode::SaveState).Inputs(0U).SrcVregs({0U});
282 INST(44U, Opcode::LoadAndInitClass).ref().Inputs().TypeId(68U);
283 INST(4U, Opcode::NewArray).ref().Inputs(44U, 2U, 43U);
284 INST(5U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(0U);
285 }
286 BASIC_BLOCK(3U, 4U)
287 {
288 INST(6U, Opcode::Mod).s32().Inputs(1U, 12U);
289 }
290 BASIC_BLOCK(4U, -1)
291 {
292 INST(7U, Opcode::Phi).s32().Inputs(3U, 6U);
293 INST(8U, Opcode::SaveState).Inputs(0U, 1U, 2U, 3U, 4U).SrcVregs({0U, 1U, 2U, 3U, 4U});
294 INST(9U, Opcode::BoundsCheck).s32().Inputs(2U, 7U, 8U);
295 INST(10U, Opcode::LoadArray).s32().Inputs(4U, 9U);
296 INST(11U, Opcode::Return).s32().Inputs(10U);
297 }
298 BASIC_BLOCK(5U, -1)
299 {
300 INST(19U, Opcode::Return).s32().Inputs(1U);
301 }
302 }
303
304 return graph;
305 }
PhiTestOutput1(int32_t index,int32_t lenArray,int32_t mod)306 Graph *PhiTestOutput1(int32_t index, int32_t lenArray, int32_t mod)
307 {
308 auto graph = CreateEmptyGraph();
309
310 GRAPH(graph)
311 {
312 PARAMETER(0U, 0U).b();
313 PARAMETER(1U, 1U).s32(); // index 1
314 CONSTANT(2U, lenArray); // len array
315 CONSTANT(3U, index); // index 2
316 CONSTANT(12U, mod);
317 CONSTANT(13U, 0U);
318 BASIC_BLOCK(11U, 2U, 5U)
319 {
320 INST(41U, Opcode::Compare).b().CC(CC_GE).SrcType(DataType::Type::INT32).Inputs(1U, 13U);
321 INST(42U, Opcode::IfImm).SrcType(compiler::DataType::BOOL).CC(compiler::CC_NE).Imm(0U).Inputs(41U);
322 }
323 BASIC_BLOCK(2U, 3U, 4U)
324 {
325 INST(43U, Opcode::SaveState).Inputs(0U).SrcVregs({0U});
326 INST(44U, Opcode::LoadAndInitClass).ref().Inputs().TypeId(68U);
327 INST(4U, Opcode::NewArray).ref().Inputs(44U, 2U, 43U);
328 INST(5U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(0U);
329 }
330 BASIC_BLOCK(3U, 4U)
331 {
332 INST(6U, Opcode::Mod).s32().Inputs(1U, 12U);
333 }
334 BASIC_BLOCK(4U, -1)
335 {
336 INST(7U, Opcode::Phi).s32().Inputs(3U, 6U);
337 INST(8U, Opcode::SaveState).Inputs(0U, 1U, 2U, 3U, 4U).SrcVregs({0U, 1U, 2U, 3U, 4U});
338 INST(9U, Opcode::NOP);
339 INST(10U, Opcode::LoadArray).s32().Inputs(4U, 7U);
340 INST(11U, Opcode::Return).s32().Inputs(10U);
341 }
342 BASIC_BLOCK(5U, -1)
343 {
344 INST(19U, Opcode::Return).s32().Inputs(1U);
345 }
346 }
347
348 return graph;
349 }
350
351 template <bool IS_APPLIED>
PhiTest(int32_t index,int32_t lenArray,int32_t mod)352 void PhiTest(int32_t index, int32_t lenArray, int32_t mod)
353 {
354 auto graph1 = PhiTestInput(index, lenArray, mod);
355 [[maybe_unused]] Graph *graph2 = nullptr;
356 if constexpr (IS_APPLIED) {
357 ASSERT_TRUE(graph1->RunPass<ChecksElimination>());
358 graph2 = PhiTestOutput1(index, lenArray, mod);
359 } else {
360 ASSERT_FALSE(graph1->RunPass<ChecksElimination>());
361 graph2 = PhiTestInput(index, lenArray, mod);
362 }
363 ASSERT_TRUE(GraphComparator().Compare(graph1, graph2));
364 }
365
366 void BuildHoistRefTypeCheckGraph();
367 void BuildHoistCheckCastGraph();
368 Graph *BuildGraphLoopWithUnknowLowerUpperValueDown(ConditionCode cc);
369 void BuildGraphNullCheckTest3();
370 void BuildGraphNullCheckTest4();
371 void BuildGraphHoistNegativeCheckTest();
372 void BuildGraphHoistZeroCheckTest();
373 void BuildGraphIfTestTrueBlock();
374 void BuildGraphIfTestFalseBlock();
375 void BuildGraphIfTestTrueBlock1();
376 void BuildGraphIfTestTrueBlock2();
377 void BuildGraphIfTestTrueBlock3();
378 void BuildGraphIfTestFalseBlock1();
379 void BuildGraphIfTestFalseBlock2();
380 void BuildGraphIfTestFalseBlock3();
381 void BuildGraphSimpleLoopTestInc();
382 void BuildGraphSimpleLoopTestIncAfterPeeling();
383 void BuildGraphSimpleLoopTestIncAfterPeeling1();
384 void BuildGraphSimpleLoopTestDec();
385 void BuildGraphLoopWithUnknowLowerUpperValue();
386 void BuildGraphUpperOOB();
387 void BuildGraphUpperWithDec();
388 void BuildGraphUnknownUpperWithDec();
389 void BuildGraphLoopWithUnknowLowerValue();
390 void BuildGraphLoopWithUnknowUpperValueLE();
391 void BuildGraphLoopWithUnknowUpperValueLT();
392 void BuildGraphLoopSeveralBoundsChecks();
393 void BuildGraphLoopSeveralIndexesBoundsChecks();
394 void BuildGraphHeadExitLoop();
395 void BuildGraphLoopTest1();
396 void BuildGraphBatchLoopTest();
397 void BuildGraphGroupedBoundsCheck();
398 void BuildGraphGroupedBoundsCheckConstIndex();
399 void BuildGraphRefTypeCheck();
400 void BuildGraphRefTypeCheckFirstNullCheckEliminated();
401 void BuildGraphBugWithNullCheck();
402 void BuildGraphNullAndBoundsChecksNestedLoop();
403 void BuildGraphLoopWithTwoPhi();
404 void BuildGraphLoopWithBigStepGE();
405 void BuildGraphLoopWithBigStepLE();
406 void BuildGraphLoopWithBigStepLT();
407 void BuildGraphLoopWithBoundsCheckUnderIfGE();
408 void BuildGraphLoopWithBoundsCheckUnderIfLT();
409 void BuildGraphCheckCastAfterIsInstance();
410 void BuildGraphOverflowCheckOptimize();
411
412 private:
413 Graph *graph_ {nullptr};
414 };
415
TEST_F(ChecksEliminationTest,NullCheckTest)416 TEST_F(ChecksEliminationTest, NullCheckTest)
417 {
418 // Check Elimination for NullCheck is applied.
419 GRAPH(GetGraph())
420 {
421 PARAMETER(0U, 0U).ref();
422 CONSTANT(1U, 10U);
423 BASIC_BLOCK(2U, 1U)
424 {
425 INST(2U, Opcode::SaveState).Inputs(0U, 1U).SrcVregs({0U, 1U});
426 INST(3U, Opcode::NullCheck).ref().Inputs(0U, 2U);
427 INST(4U, Opcode::LoadArray).s32().Inputs(3U, 1U);
428 INST(5U, Opcode::SaveState).Inputs(0U, 1U).SrcVregs({0U, 1U});
429 INST(6U, Opcode::NullCheck).ref().Inputs(0U, 5U);
430 INST(7U, Opcode::StoreArray).s32().Inputs(6U, 1U, 4U);
431 INST(8U, Opcode::Return).s32().Inputs(4U);
432 }
433 }
434 ASSERT_TRUE(GetGraph()->RunPass<ChecksElimination>());
435 auto graph = CreateEmptyGraph();
436 GRAPH(graph)
437 {
438 PARAMETER(0U, 0U).ref();
439 CONSTANT(1U, 10U);
440 BASIC_BLOCK(2U, 1U)
441 {
442 INST(2U, Opcode::SaveState).Inputs(0U, 1U).SrcVregs({0U, 1U});
443 INST(3U, Opcode::NullCheck).ref().Inputs(0U, 2U);
444 INST(4U, Opcode::LoadArray).s32().Inputs(3U, 1U);
445 INST(5U, Opcode::SaveState).Inputs(0U, 1U).SrcVregs({0U, 1U});
446 INST(6U, Opcode::NOP);
447 INST(7U, Opcode::StoreArray).s32().Inputs(3U, 1U, 4U);
448 INST(8U, Opcode::Return).s32().Inputs(4U);
449 }
450 }
451 ASSERT_TRUE(GraphComparator().Compare(GetGraph(), graph));
452 }
453
TEST_F(ChecksEliminationTest,NullCheckTest1)454 TEST_F(ChecksEliminationTest, NullCheckTest1)
455 {
456 // Check Elimination for NullCheck isn't applied. Different inputs in NullCheck insts.
457 GRAPH(GetGraph())
458 {
459 PARAMETER(0U, 0U).ref();
460 PARAMETER(1U, 1U).ref();
461 CONSTANT(2U, 10U);
462
463 BASIC_BLOCK(2U, 1U)
464 {
465 INST(3U, Opcode::SaveState).Inputs(0U, 1U, 2U).SrcVregs({0U, 1U, 2U});
466 INST(4U, Opcode::NullCheck).ref().Inputs(0U, 3U);
467 INST(5U, Opcode::LoadArray).s32().Inputs(4U, 2U);
468 INST(6U, Opcode::SaveState).Inputs(0U, 1U, 2U).SrcVregs({0U, 1U, 2U});
469 INST(7U, Opcode::NullCheck).ref().Inputs(1U, 6U);
470 INST(8U, Opcode::StoreArray).s32().Inputs(7U, 2U, 5U);
471 INST(9U, Opcode::Return).s32().Inputs(5U);
472 }
473 }
474 auto clone = GraphCloner(GetGraph(), GetGraph()->GetAllocator(), GetGraph()->GetLocalAllocator()).CloneGraph();
475 ASSERT_FALSE(GetGraph()->RunPass<ChecksElimination>());
476 ASSERT_TRUE(GraphComparator().Compare(GetGraph(), clone));
477 }
478
TEST_F(ChecksEliminationTest,NullCheckTest2)479 TEST_F(ChecksEliminationTest, NullCheckTest2)
480 {
481 // Check Elimination for NullCheck isn't applied. NullCheck(5) isn't dominator of NullCheck(8).
482 GRAPH(GetGraph())
483 {
484 PARAMETER(0U, 0U).ref();
485 CONSTANT(1U, 10U);
486
487 BASIC_BLOCK(2U, 3U, 4U)
488 {
489 INST(2U, Opcode::Compare).b().SrcType(DataType::Type::INT64).Inputs(1U, 1U);
490 INST(3U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(2U);
491 }
492
493 BASIC_BLOCK(3U, 5U)
494 {
495 INST(4U, Opcode::SaveState).Inputs(0U, 1U).SrcVregs({0U, 1U});
496 INST(5U, Opcode::NullCheck).ref().Inputs(0U, 4U);
497 INST(6U, Opcode::LoadArray).s32().Inputs(5U, 1U);
498 }
499
500 BASIC_BLOCK(4U, 5U)
501 {
502 INST(7U, Opcode::SaveState).Inputs(0U, 1U).SrcVregs({0U, 1U});
503 INST(8U, Opcode::NullCheck).ref().Inputs(0U, 7U);
504 INST(9U, Opcode::LoadArray).s32().Inputs(8U, 1U);
505 }
506
507 BASIC_BLOCK(5U, 1U)
508 {
509 INST(10U, Opcode::Phi).s32().Inputs(6U, 9U);
510 INST(11U, Opcode::Return).s32().Inputs(10U);
511 }
512 }
513 auto clone = GraphCloner(GetGraph(), GetGraph()->GetAllocator(), GetGraph()->GetLocalAllocator()).CloneGraph();
514 ASSERT_FALSE(GetGraph()->RunPass<ChecksElimination>());
515 ASSERT_TRUE(GraphComparator().Compare(GetGraph(), clone));
516 }
517
BuildGraphNullCheckTest3()518 void ChecksEliminationTest::BuildGraphNullCheckTest3()
519 {
520 GRAPH(GetGraph())
521 {
522 CONSTANT(0U, 0U); // initial
523 CONSTANT(1U, 1U); // increment
524 CONSTANT(2U, 10U);
525 PARAMETER(3U, 0U).ref();
526 BASIC_BLOCK(2U, 3U, 5U)
527 {
528 INST(16U, Opcode::LenArray).s32().Inputs(3U);
529 INST(20U, Opcode::SaveStateDeoptimize).Inputs(0U, 1U, 2U, 3U).SrcVregs({0U, 1U, 2U, 3U});
530 INST(5U, Opcode::Compare).SrcType(DataType::INT32).CC(CC_LT).b().Inputs(0U, 2U); // 0 < 10
531 INST(6U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(5U);
532 }
533 BASIC_BLOCK(3U, 3U, 5U)
534 {
535 INST(4U, Opcode::Phi).s32().Inputs(0U, 10U);
536 INST(7U, Opcode::SaveState).Inputs(0U, 1U, 2U, 3U).SrcVregs({0U, 1U, 2U, 3U});
537 INST(15U, Opcode::NullCheck).ref().Inputs(3U, 7U);
538 INST(8U, Opcode::BoundsCheck).s32().Inputs(16U, 4U, 7U);
539 INST(9U, Opcode::StoreArray).s32().Inputs(15U, 8U, 0U); // a[i] = 0
540 INST(10U, Opcode::Add).s32().Inputs(4U, 1U); // i++
541 INST(13U, Opcode::Compare).CC(CC_LT).b().Inputs(10U, 2U); // i < 10
542 INST(14U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(13U);
543 }
544 BASIC_BLOCK(5U, 1U)
545 {
546 INST(12U, Opcode::Return).ref().Inputs(3U);
547 }
548 }
549 }
550
TEST_F(ChecksEliminationTest,NullCheckTest3)551 TEST_F(ChecksEliminationTest, NullCheckTest3)
552 {
553 BuildGraphNullCheckTest3();
554 ASSERT_TRUE(GetGraph()->RunPass<ChecksElimination>());
555 auto graph = CreateEmptyGraph();
556 GRAPH(graph)
557 {
558 CONSTANT(0U, 0U); // initial
559 CONSTANT(1U, 1U); // increment
560 CONSTANT(2U, 10U);
561 PARAMETER(3U, 0U).ref();
562 BASIC_BLOCK(2U, 3U, 5U)
563 {
564 INST(16U, Opcode::LenArray).s32().Inputs(3U);
565 INST(20U, Opcode::SaveStateDeoptimize).Inputs(0U, 1U, 2U, 3U).SrcVregs({0U, 1U, 2U, 3U});
566 INST(19U, Opcode::NullCheck).ref().Inputs(3U, 20U).SetFlag(inst_flags::CAN_DEOPTIMIZE);
567 INST(23U, Opcode::Compare).SrcType(DataType::INT32).CC(CC_LT).b().Inputs(16U, 2U); // len_array < 10
568 INST(24U, Opcode::DeoptimizeIf).DeoptimizeType(DeoptimizeType::BOUNDS_CHECK).Inputs(23U, 20U);
569 INST(5U, Opcode::Compare).SrcType(DataType::INT32).CC(CC_LT).b().Inputs(0U, 2U); // 0 < 10
570 INST(6U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(5U);
571 }
572 BASIC_BLOCK(3U, 3U, 5U)
573 {
574 INST(4U, Opcode::Phi).s32().Inputs(0U, 10U);
575 INST(7U, Opcode::SaveState).Inputs(0U, 1U, 2U, 3U).SrcVregs({0U, 1U, 2U, 3U});
576 INST(8U, Opcode::NOP);
577 INST(9U, Opcode::StoreArray).s32().Inputs(19U, 4U, 0U); // a[i] = 0
578 INST(10U, Opcode::Add).s32().Inputs(4U, 1U); // i++
579 INST(13U, Opcode::Compare).CC(CC_LT).b().Inputs(10U, 2U); // i < 10
580 INST(14U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(13U);
581 }
582 BASIC_BLOCK(5U, 1U)
583 {
584 INST(12U, Opcode::Return).ref().Inputs(3U);
585 }
586 }
587 ASSERT_TRUE(GraphComparator().Compare(GetGraph(), graph));
588 }
589
BuildGraphNullCheckTest4()590 void ChecksEliminationTest::BuildGraphNullCheckTest4()
591 {
592 GRAPH(GetGraph())
593 {
594 CONSTANT(0U, 0U); // initial
595 CONSTANT(1U, 1U); // increment
596 CONSTANT(2U, 10U);
597 CONSTANT(3U, nullptr);
598 BASIC_BLOCK(2U, 3U, 5U)
599 {
600 INST(20U, Opcode::SaveStateDeoptimize).Inputs(0U, 1U, 2U, 3U).SrcVregs({0U, 1U, 2U, 3U});
601 INST(5U, Opcode::Compare).SrcType(DataType::INT32).CC(CC_LT).b().Inputs(0U, 2U); // 0 < 10
602 INST(6U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(5U);
603 }
604 BASIC_BLOCK(3U, 3U, 5U)
605 {
606 INST(4U, Opcode::Phi).s32().Inputs(0U, 10U);
607 INST(7U, Opcode::SaveState).Inputs(0U, 1U, 2U, 3U).SrcVregs({0U, 1U, 2U, 3U});
608 INST(15U, Opcode::NullCheck).ref().Inputs(3U, 7U);
609 INST(9U, Opcode::StoreArray).s32().Inputs(15U, 4U, 0U); // a[i] = 0
610 INST(10U, Opcode::Add).s32().Inputs(4U, 1U); // i++
611 INST(13U, Opcode::Compare).CC(CC_LT).b().Inputs(10U, 2U); // i < 10
612 INST(14U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(13U);
613 }
614 BASIC_BLOCK(5U, 1U)
615 {
616 INST(12U, Opcode::Return).ref().Inputs(3U);
617 }
618 }
619 }
620
TEST_F(ChecksEliminationTest,NullCheckTest4)621 TEST_F(ChecksEliminationTest, NullCheckTest4)
622 {
623 BuildGraphNullCheckTest4();
624 ASSERT_TRUE(GetGraph()->RunPass<ChecksElimination>());
625 auto graph = CreateEmptyGraph();
626 GRAPH(graph)
627 {
628 CONSTANT(0U, 0U); // initial
629 CONSTANT(1U, 1U); // increment
630 CONSTANT(2U, 10U);
631 CONSTANT(3U, nullptr);
632 BASIC_BLOCK(2U, 3U, 5U)
633 {
634 INST(20U, Opcode::SaveStateDeoptimize).Inputs(0U, 1U, 2U, 3U).SrcVregs({0U, 1U, 2U, 3U});
635 INST(5U, Opcode::Compare).SrcType(DataType::INT32).CC(CC_LT).b().Inputs(0U, 2U); // 0 < 10
636 INST(6U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(5U);
637 }
638 BASIC_BLOCK(3U, 1U)
639 {
640 INST(7U, Opcode::SaveState).Inputs(0U, 1U, 2U, 3U).SrcVregs({0U, 1U, 2U, 3U});
641 INST(15U, Opcode::Deoptimize).DeoptimizeType(DeoptimizeType::NULL_CHECK).Inputs(7U);
642 }
643 BASIC_BLOCK(5U, 1U)
644 {
645 INST(12U, Opcode::Return).ref().Inputs(3U);
646 }
647 }
648 ASSERT_TRUE(GraphComparator().Compare(GetGraph(), graph));
649 }
650
TEST_F(ChecksEliminationTest,NegativeCheckTest)651 TEST_F(ChecksEliminationTest, NegativeCheckTest)
652 {
653 // Check Elimination for NegativeCheck is applied.
654 GRAPH(GetGraph())
655 {
656 CONSTANT(0U, 10U);
657 BASIC_BLOCK(2U, 1U)
658 {
659 INST(1U, Opcode::SaveState).Inputs(0U).SrcVregs({0U});
660 INST(2U, Opcode::NegativeCheck).s32().Inputs(0U, 1U);
661 INST(44U, Opcode::LoadAndInitClass).ref().Inputs(1U).TypeId(68U);
662 INST(3U, Opcode::NewArray).ref().Inputs(44U, 0U, 1U);
663 INST(4U, Opcode::Return).ref().Inputs(3U);
664 }
665 }
666 ASSERT_TRUE(GetGraph()->RunPass<ChecksElimination>());
667 auto graph = CreateEmptyGraph();
668 GRAPH(graph)
669 {
670 CONSTANT(0U, 10U);
671 BASIC_BLOCK(2U, 1U)
672 {
673 INST(1U, Opcode::SaveState).Inputs(0U).SrcVregs({0U});
674 INST(2U, Opcode::NOP);
675 INST(44U, Opcode::LoadAndInitClass).ref().Inputs(1U).TypeId(68U);
676 INST(3U, Opcode::NewArray).ref().Inputs(44U, 0U, 1U);
677 INST(4U, Opcode::Return).ref().Inputs(3U);
678 }
679 }
680 ASSERT_TRUE(GraphComparator().Compare(GetGraph(), graph));
681 }
682
TEST_F(ChecksEliminationTest,NegativeCheckTestZero)683 TEST_F(ChecksEliminationTest, NegativeCheckTestZero)
684 {
685 // Check Elimination for NegativeCheck is applied.
686 GRAPH(GetGraph())
687 {
688 CONSTANT(0U, 0U);
689 BASIC_BLOCK(2U, 1U)
690 {
691 INST(1U, Opcode::SaveState).Inputs(0U).SrcVregs({0U});
692 INST(2U, Opcode::NegativeCheck).s32().Inputs(0U, 1U);
693 INST(44U, Opcode::LoadAndInitClass).ref().Inputs(1U).TypeId(68U);
694 INST(3U, Opcode::NewArray).ref().Inputs(44U, 2U, 1U);
695 INST(4U, Opcode::Return).ref().Inputs(3U);
696 }
697 }
698 ASSERT_TRUE(GetGraph()->RunPass<ChecksElimination>());
699 auto graph = CreateEmptyGraph();
700 GRAPH(graph)
701 {
702 CONSTANT(0U, 0U);
703 BASIC_BLOCK(2U, 1U)
704 {
705 INST(1U, Opcode::SaveState).Inputs(0U).SrcVregs({0U});
706 INST(2U, Opcode::NOP);
707 INST(44U, Opcode::LoadAndInitClass).ref().Inputs(1U).TypeId(68U);
708 INST(3U, Opcode::NewArray).ref().Inputs(44U, 0U, 1U);
709 INST(4U, Opcode::Return).ref().Inputs(3U);
710 }
711 }
712 ASSERT_TRUE(GraphComparator().Compare(GetGraph(), graph));
713 }
714
TEST_F(ChecksEliminationTest,NegativeCheckTest1)715 TEST_F(ChecksEliminationTest, NegativeCheckTest1)
716 {
717 // Check Elimination for NegativeCheck is applied. Negative constant.
718 // Check replaced by deoptimize
719 GRAPH(GetGraph())
720 {
721 CONSTANT(0U, -5L);
722 BASIC_BLOCK(2U, 1U)
723 {
724 INST(1U, Opcode::SaveState).Inputs(0U).SrcVregs({0U});
725 INST(2U, Opcode::NegativeCheck).s32().Inputs(0U, 1U);
726 INST(44U, Opcode::LoadAndInitClass).ref().Inputs(1U).TypeId(68U);
727 INST(3U, Opcode::NewArray).ref().Inputs(44U, 2U, 1U);
728 INST(4U, Opcode::Return).ref().Inputs(3U);
729 }
730 }
731 ASSERT_TRUE(GetGraph()->RunPass<ChecksElimination>());
732 auto graph = CreateEmptyGraph();
733 GRAPH(graph)
734 {
735 CONSTANT(0U, -5L);
736 BASIC_BLOCK(2U, 1U)
737 {
738 INST(1U, Opcode::SaveState).Inputs(0U).SrcVregs({0U});
739 INST(2U, Opcode::Deoptimize).DeoptimizeType(DeoptimizeType::NEGATIVE_CHECK).Inputs(1U);
740 }
741 }
742 ASSERT_TRUE(GraphComparator().Compare(GetGraph(), graph));
743 }
744
TEST_F(ChecksEliminationTest,NegativeCheckTest3)745 TEST_F(ChecksEliminationTest, NegativeCheckTest3)
746 {
747 // Check Elimination for NegativeCheck is applied. Positiv value.
748 GRAPH(GetGraph())
749 {
750 CONSTANT(0U, -5L);
751 BASIC_BLOCK(2U, 1U)
752 {
753 INST(5U, Opcode::Neg).s32().Inputs(0U);
754 INST(1U, Opcode::SaveState).Inputs(0U).SrcVregs({0U});
755 INST(2U, Opcode::NegativeCheck).s32().Inputs(5U, 1U);
756 INST(44U, Opcode::LoadAndInitClass).ref().Inputs(1U).TypeId(68U);
757 INST(3U, Opcode::NewArray).ref().Inputs(44U, 2U, 1U);
758 INST(4U, Opcode::Return).ref().Inputs(3U);
759 }
760 }
761 ASSERT_TRUE(GetGraph()->RunPass<ChecksElimination>());
762 auto graph = CreateEmptyGraph();
763 GRAPH(graph)
764 {
765 CONSTANT(0U, -5L);
766 BASIC_BLOCK(2U, 1U)
767 {
768 INST(5U, Opcode::Neg).s32().Inputs(0U);
769 INST(1U, Opcode::SaveState).Inputs(0U).SrcVregs({0U});
770 INST(2U, Opcode::NOP);
771 INST(44U, Opcode::LoadAndInitClass).ref().Inputs(1U).TypeId(68U);
772 INST(3U, Opcode::NewArray).ref().Inputs(44U, 5U, 1U);
773 INST(4U, Opcode::Return).ref().Inputs(3U);
774 }
775 }
776 ASSERT_TRUE(GraphComparator().Compare(GetGraph(), graph));
777 }
778
TEST_F(ChecksEliminationTest,NegativeCheckTest4)779 TEST_F(ChecksEliminationTest, NegativeCheckTest4)
780 {
781 // Check Elimination for NegativeCheck is applied. Negative value.
782 // Check replaced by deoptimize
783 GRAPH(GetGraph())
784 {
785 CONSTANT(0U, 5U);
786 BASIC_BLOCK(2U, 1U)
787 {
788 INST(5U, Opcode::Neg).s32().Inputs(0U);
789 INST(1U, Opcode::SaveState).Inputs(0U).SrcVregs({0U});
790 INST(2U, Opcode::NegativeCheck).s32().Inputs(5U, 1U);
791 INST(44U, Opcode::LoadAndInitClass).ref().Inputs(1U).TypeId(68U);
792 INST(3U, Opcode::NewArray).ref().Inputs(44U, 2U, 1U);
793 INST(4U, Opcode::Return).ref().Inputs(3U);
794 }
795 }
796 ASSERT_TRUE(GetGraph()->RunPass<ChecksElimination>());
797 auto graph = CreateEmptyGraph();
798 GRAPH(graph)
799 {
800 CONSTANT(0U, 5U);
801 BASIC_BLOCK(2U, 1U)
802 {
803 INST(5U, Opcode::Neg).s32().Inputs(0U);
804 INST(1U, Opcode::SaveState).Inputs(0U).SrcVregs({0U});
805 INST(2U, Opcode::Deoptimize).DeoptimizeType(DeoptimizeType::NEGATIVE_CHECK).Inputs(1U);
806 }
807 }
808 ASSERT_TRUE(GraphComparator().Compare(GetGraph(), graph));
809 }
810
BuildGraphHoistNegativeCheckTest()811 void ChecksEliminationTest::BuildGraphHoistNegativeCheckTest()
812 {
813 GRAPH(GetGraph())
814 {
815 CONSTANT(0U, 0U); // initial
816 CONSTANT(1U, 1U); // increment
817 CONSTANT(2U, 10U);
818 PARAMETER(3U, 0U).s32();
819 BASIC_BLOCK(2U, 3U, 5U)
820 {
821 INST(20U, Opcode::SaveStateDeoptimize).Inputs(0U, 1U, 2U, 3U).SrcVregs({0U, 1U, 2U, 3U});
822 INST(5U, Opcode::Compare).SrcType(DataType::INT32).CC(CC_LT).b().Inputs(0U, 2U); // 0 < 10
823 INST(6U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(5U);
824 }
825 BASIC_BLOCK(3U, 3U, 5U)
826 {
827 INST(4U, Opcode::Phi).s32().Inputs(0U, 10U);
828 INST(7U, Opcode::SaveState).Inputs(0U, 1U, 2U, 3U).SrcVregs({0U, 1U, 2U, 3U});
829 INST(8U, Opcode::NegativeCheck).s32().Inputs(3U, 7U);
830 INST(9U, Opcode::LoadAndInitClass).ref().Inputs(7U).TypeId(68U);
831 INST(11U, Opcode::NewArray).ref().Inputs(9U, 8U, 7U);
832 INST(10U, Opcode::Add).s32().Inputs(4U, 1U); // i++
833 INST(13U, Opcode::Compare).CC(CC_LT).b().Inputs(10U, 2U); // i < 10
834 INST(14U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(13U);
835 }
836 BASIC_BLOCK(5U, 1U)
837 {
838 INST(12U, Opcode::ReturnVoid).v0id();
839 }
840 }
841 }
842
TEST_F(ChecksEliminationTest,HoistNegativeCheckTest)843 TEST_F(ChecksEliminationTest, HoistNegativeCheckTest)
844 {
845 BuildGraphHoistNegativeCheckTest();
846 ASSERT_TRUE(GetGraph()->RunPass<ChecksElimination>());
847 auto graph = CreateEmptyGraph();
848 GRAPH(graph)
849 {
850 CONSTANT(0U, 0U); // initial
851 CONSTANT(1U, 1U); // increment
852 CONSTANT(2U, 10U);
853 PARAMETER(3U, 0U).s32();
854 BASIC_BLOCK(2U, 3U, 5U)
855 {
856 INST(20U, Opcode::SaveStateDeoptimize).Inputs(0U, 1U, 2U, 3U).SrcVregs({0U, 1U, 2U, 3U});
857 INST(22U, Opcode::NegativeCheck).s32().Inputs(3U, 20U).SetFlag(inst_flags::CAN_DEOPTIMIZE);
858 INST(5U, Opcode::Compare).SrcType(DataType::INT32).CC(CC_LT).b().Inputs(0U, 2U); // 0 < 10
859 INST(6U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(5U);
860 }
861 BASIC_BLOCK(3U, 3U, 5U)
862 {
863 INST(4U, Opcode::Phi).s32().Inputs(0U, 10U);
864 INST(7U, Opcode::SaveState).Inputs(0U, 1U, 2U, 3U).SrcVregs({0U, 1U, 2U, 3U});
865 INST(9U, Opcode::LoadAndInitClass).ref().Inputs(7U).TypeId(68U);
866 INST(11U, Opcode::NewArray).ref().Inputs(9U, 22U, 7U);
867 INST(10U, Opcode::Add).s32().Inputs(4U, 1U); // i++
868 INST(13U, Opcode::Compare).CC(CC_LT).b().Inputs(10U, 2U); // i < 10
869 INST(14U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(13U);
870 }
871 BASIC_BLOCK(5U, 1U)
872 {
873 INST(12U, Opcode::ReturnVoid).v0id();
874 }
875 }
876 ASSERT_TRUE(GraphComparator().Compare(GetGraph(), graph));
877 }
878
TEST_F(ChecksEliminationTest,ZeroCheckTest)879 TEST_F(ChecksEliminationTest, ZeroCheckTest)
880 {
881 // Check Elimination for ZeroCheck is applied.
882 GRAPH(GetGraph())
883 {
884 CONSTANT(0U, 10U);
885 CONSTANT(1U, 12U);
886 BASIC_BLOCK(2U, 1U)
887 {
888 INST(2U, Opcode::SaveState).Inputs(0U).SrcVregs({0U});
889 INST(3U, Opcode::ZeroCheck).s32().Inputs(1U, 2U);
890 INST(4U, Opcode::Div).s32().Inputs(0U, 3U);
891 INST(5U, Opcode::Return).s32().Inputs(4U);
892 }
893 }
894 ASSERT_TRUE(GetGraph()->RunPass<ChecksElimination>());
895 auto graph = CreateEmptyGraph();
896 GRAPH(graph)
897 {
898 CONSTANT(0U, 10U);
899 CONSTANT(1U, 12U);
900 BASIC_BLOCK(2U, 1U)
901 {
902 INST(2U, Opcode::SaveState).Inputs(0U).SrcVregs({0U});
903 INST(3U, Opcode::NOP);
904 INST(4U, Opcode::Div).s32().Inputs(0U, 1U);
905 INST(5U, Opcode::Return).s32().Inputs(4U);
906 }
907 }
908 ASSERT_TRUE(GraphComparator().Compare(GetGraph(), graph));
909 }
910
TEST_F(ChecksEliminationTest,ZeroCheckTestBigConst)911 TEST_F(ChecksEliminationTest, ZeroCheckTestBigConst)
912 {
913 // Check Elimination for ZeroCheck is applied.
914 GRAPH(GetGraph())
915 {
916 CONSTANT(0U, 10U);
917 CONSTANT(1U, 0x8000000000000000U);
918 BASIC_BLOCK(2U, 1U)
919 {
920 INST(2U, Opcode::SaveState).Inputs(0U).SrcVregs({0U});
921 INST(3U, Opcode::ZeroCheck).s32().Inputs(1U, 2U);
922 INST(4U, Opcode::Div).s32().Inputs(0U, 3U);
923 INST(5U, Opcode::Return).s32().Inputs(4U);
924 }
925 }
926 ASSERT_TRUE(GetGraph()->RunPass<ChecksElimination>());
927 auto graph = CreateEmptyGraph();
928 GRAPH(graph)
929 {
930 CONSTANT(0U, 10U);
931 CONSTANT(1U, 0x8000000000000000U);
932 BASIC_BLOCK(2U, 1U)
933 {
934 INST(2U, Opcode::SaveState).Inputs(0U).SrcVregs({0U});
935 INST(3U, Opcode::NOP);
936 INST(4U, Opcode::Div).s32().Inputs(0U, 1U);
937 INST(5U, Opcode::Return).s32().Inputs(4U);
938 }
939 }
940 ASSERT_TRUE(GraphComparator().Compare(GetGraph(), graph));
941 }
942
TEST_F(ChecksEliminationTest,ZeroCheckTest1)943 TEST_F(ChecksEliminationTest, ZeroCheckTest1)
944 {
945 // Check Elimination for ZeroCheck isn't applied. Zero constant.
946 GRAPH(GetGraph())
947 {
948 CONSTANT(0U, 10U);
949 CONSTANT(1U, 0U);
950 BASIC_BLOCK(2U, 1U)
951 {
952 INST(2U, Opcode::SaveState).Inputs(0U).SrcVregs({0U});
953 INST(3U, Opcode::ZeroCheck).s32().Inputs(1U, 2U);
954 INST(4U, Opcode::Div).s32().Inputs(0U, 3U);
955 INST(5U, Opcode::Return).s32().Inputs(4U);
956 }
957 }
958 ASSERT_TRUE(GetGraph()->RunPass<ChecksElimination>());
959 auto graph = CreateEmptyGraph();
960 GRAPH(graph)
961 {
962 CONSTANT(0U, 10U);
963 CONSTANT(1U, 0U);
964 BASIC_BLOCK(2U, 1U)
965 {
966 INST(2U, Opcode::SaveState).Inputs(0U).SrcVregs({0U});
967 INST(3U, Opcode::Deoptimize).DeoptimizeType(DeoptimizeType::ZERO_CHECK).Inputs(2U);
968 }
969 }
970 ASSERT_TRUE(GraphComparator().Compare(GetGraph(), graph));
971 }
972
TEST_F(ChecksEliminationTest,ZeroCheckTest2)973 TEST_F(ChecksEliminationTest, ZeroCheckTest2)
974 {
975 // Check Elimination for ZeroCheck is applied. Positiv value.
976 GRAPH(GetGraph())
977 {
978 CONSTANT(0U, -5L);
979 BASIC_BLOCK(2U, 1U)
980 {
981 INST(5U, Opcode::Abs).s32().Inputs(0U);
982 INST(1U, Opcode::SaveState).Inputs(0U).SrcVregs({0U});
983 INST(2U, Opcode::NegativeCheck).s32().Inputs(5U, 1U);
984 INST(44U, Opcode::LoadAndInitClass).ref().Inputs(1U).TypeId(68U);
985 INST(3U, Opcode::NewArray).ref().Inputs(44U, 2U, 1U);
986 INST(4U, Opcode::Return).ref().Inputs(3U);
987 }
988 }
989 ASSERT_TRUE(GetGraph()->RunPass<ChecksElimination>());
990 auto graph = CreateEmptyGraph();
991 GRAPH(graph)
992 {
993 CONSTANT(0U, -5L);
994 BASIC_BLOCK(2U, 1U)
995 {
996 INST(5U, Opcode::Abs).s32().Inputs(0U);
997 INST(1U, Opcode::SaveState).Inputs(0U).SrcVregs({0U});
998 INST(2U, Opcode::NOP);
999 INST(44U, Opcode::LoadAndInitClass).ref().Inputs(1U).TypeId(68U);
1000 INST(3U, Opcode::NewArray).ref().Inputs(44U, 5U, 1U);
1001 INST(4U, Opcode::Return).ref().Inputs(3U);
1002 }
1003 }
1004 ASSERT_TRUE(GraphComparator().Compare(GetGraph(), graph));
1005 }
1006
TEST_F(ChecksEliminationTest,NegativeZeroCheckTest)1007 TEST_F(ChecksEliminationTest, NegativeZeroCheckTest)
1008 {
1009 // Test with NegativeCheck and ZeroCheck.
1010 GRAPH(GetGraph())
1011 {
1012 CONSTANT(0U, 1U);
1013 CONSTANT(1U, 2U);
1014 BASIC_BLOCK(2U, 1U)
1015 {
1016 INST(2U, Opcode::SaveState).Inputs(0U, 1U).SrcVregs({0U, 1U});
1017 INST(3U, Opcode::NegativeCheck).s32().Inputs(0U, 2U);
1018 INST(44U, Opcode::LoadAndInitClass).ref().Inputs(2U).TypeId(68U);
1019 INST(4U, Opcode::NewArray).ref().Inputs(44U, 3U, 2U);
1020 INST(5U, Opcode::ZeroCheck).s32().Inputs(0U, 2U);
1021 INST(6U, Opcode::Div).s32().Inputs(1U, 5U);
1022 INST(7U, Opcode::StoreArray).s32().Inputs(4U, 0U, 6U);
1023 INST(8U, Opcode::Return).ref().Inputs(4U);
1024 }
1025 }
1026 ASSERT_TRUE(GetGraph()->RunPass<ChecksElimination>());
1027 auto graph = CreateEmptyGraph();
1028 GRAPH(graph)
1029 {
1030 CONSTANT(0U, 1U);
1031 CONSTANT(1U, 2U);
1032 BASIC_BLOCK(2U, 1U)
1033 {
1034 INST(2U, Opcode::SaveState).Inputs(0U, 1U).SrcVregs({0U, 1U});
1035 INST(3U, Opcode::NOP);
1036 INST(44U, Opcode::LoadAndInitClass).ref().Inputs(2U).TypeId(68U);
1037 INST(4U, Opcode::NewArray).ref().Inputs(44U, 0U, 2U);
1038 INST(5U, Opcode::NOP);
1039 INST(6U, Opcode::Div).s32().Inputs(1U, 0U);
1040 INST(7U, Opcode::StoreArray).s32().Inputs(4U, 0U, 6U);
1041 INST(8U, Opcode::Return).ref().Inputs(4U);
1042 }
1043 }
1044 ASSERT_TRUE(GraphComparator().Compare(GetGraph(), graph));
1045 }
1046
BuildGraphHoistZeroCheckTest()1047 void ChecksEliminationTest::BuildGraphHoistZeroCheckTest()
1048 {
1049 {
1050 GRAPH(GetGraph())
1051 {
1052 CONSTANT(0U, 0U); // initial
1053 CONSTANT(1U, 1U); // increment
1054 CONSTANT(2U, 10U);
1055 PARAMETER(3U, 0U).s32();
1056 BASIC_BLOCK(2U, 3U, 5U)
1057 {
1058 INST(20U, Opcode::SaveStateDeoptimize).Inputs(0U, 1U, 2U, 3U).SrcVregs({0U, 1U, 2U, 3U});
1059 INST(5U, Opcode::Compare).SrcType(DataType::INT32).CC(CC_LT).b().Inputs(0U, 2U); // 0 < 10
1060 INST(6U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(5U);
1061 }
1062 BASIC_BLOCK(3U, 3U, 5U)
1063 {
1064 INST(4U, Opcode::Phi).s32().Inputs(0U, 10U); // i
1065 INST(16U, Opcode::Phi).s32().Inputs(0U, 15U); // s
1066 INST(7U, Opcode::SaveState).Inputs(0U, 1U, 2U, 3U).SrcVregs({0U, 1U, 2U, 3U});
1067 INST(8U, Opcode::ZeroCheck).s32().Inputs(3U, 7U);
1068 INST(17U, Opcode::Div).s32().Inputs(4U, 8U);
1069 INST(15U, Opcode::Add).s32().Inputs(16U, 17U); // s += i / x
1070 INST(10U, Opcode::Add).s32().Inputs(4U, 1U); // i++
1071 INST(13U, Opcode::Compare).CC(CC_LT).b().Inputs(10U, 2U); // i < 10
1072 INST(14U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(13U);
1073 }
1074 BASIC_BLOCK(5U, 1U)
1075 {
1076 INST(18U, Opcode::Phi).s32().Inputs(0U, 16U);
1077 INST(12U, Opcode::Return).s32().Inputs(18U);
1078 }
1079 }
1080 }
1081 }
TEST_F(ChecksEliminationTest,HoistZeroCheckTest)1082 TEST_F(ChecksEliminationTest, HoistZeroCheckTest)
1083 {
1084 BuildGraphHoistZeroCheckTest();
1085 ASSERT_TRUE(GetGraph()->RunPass<ChecksElimination>());
1086 auto graph = CreateEmptyGraph();
1087 GRAPH(graph)
1088 {
1089 CONSTANT(0U, 0U); // initial
1090 CONSTANT(1U, 1U); // increment
1091 CONSTANT(2U, 10U);
1092 PARAMETER(3U, 0U).s32();
1093 BASIC_BLOCK(2U, 3U, 5U)
1094 {
1095 INST(20U, Opcode::SaveStateDeoptimize).Inputs(0U, 1U, 2U, 3U).SrcVregs({0U, 1U, 2U, 3U});
1096 INST(22U, Opcode::ZeroCheck).s32().Inputs(3U, 20U).SetFlag(inst_flags::CAN_DEOPTIMIZE);
1097 INST(5U, Opcode::Compare).SrcType(DataType::INT32).CC(CC_LT).b().Inputs(0U, 2U); // 0 < 10
1098 INST(6U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(5U);
1099 }
1100 BASIC_BLOCK(3U, 3U, 5U)
1101 {
1102 INST(4U, Opcode::Phi).s32().Inputs(0U, 10U); // i
1103 INST(16U, Opcode::Phi).s32().Inputs(0U, 15U); // s
1104 INST(7U, Opcode::SaveState).Inputs(0U, 1U, 2U, 3U).SrcVregs({0U, 1U, 2U, 3U});
1105 INST(17U, Opcode::Div).s32().Inputs(4U, 22U);
1106 INST(15U, Opcode::Add).s32().Inputs(16U, 17U); // s += i / x
1107 INST(10U, Opcode::Add).s32().Inputs(4U, 1U); // i++
1108 INST(13U, Opcode::Compare).CC(CC_LT).b().Inputs(10U, 2U); // i < 10
1109 INST(14U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(13U);
1110 }
1111 BASIC_BLOCK(5U, 1U)
1112 {
1113 INST(18U, Opcode::Phi).s32().Inputs(0U, 16U);
1114 INST(12U, Opcode::Return).s32().Inputs(18U);
1115 }
1116 }
1117 ASSERT_TRUE(GraphComparator().Compare(GetGraph(), graph));
1118 }
1119
TEST_F(ChecksEliminationTest,SimpleBoundsCheckTest)1120 TEST_F(ChecksEliminationTest, SimpleBoundsCheckTest)
1121 {
1122 SimpleTest<true>(2U, 10U);
1123 SimpleTest<true>(9U, 10U);
1124 SimpleTest<true>(0U, 10U);
1125 SimpleTest<false>(10U, 10U);
1126 SimpleTest<false>(12U, 10U);
1127 SimpleTest<false>(-1L, 10U);
1128 }
1129
TEST_F(ChecksEliminationTest,SimpleBoundsCheckTest1)1130 TEST_F(ChecksEliminationTest, SimpleBoundsCheckTest1)
1131 {
1132 // not applied, we know nothing about len array
1133 GRAPH(GetGraph())
1134 {
1135 PARAMETER(0U, 0U).s32(); // len array
1136 CONSTANT(1U, 5U);
1137 BASIC_BLOCK(2U, 1U)
1138 {
1139 INST(2U, Opcode::SaveState).Inputs(0U, 1U).SrcVregs({0U, 1U});
1140 INST(44U, Opcode::LoadAndInitClass).ref().Inputs(2U).TypeId(68U);
1141 INST(3U, Opcode::NewArray).ref().Inputs(44U, 0U, 2U);
1142 INST(4U, Opcode::BoundsCheck).s32().Inputs(0U, 1U, 2U);
1143 INST(5U, Opcode::LoadArray).s32().Inputs(3U, 4U);
1144 INST(6U, Opcode::Return).s32().Inputs(5U);
1145 }
1146 }
1147 auto clone = GraphCloner(GetGraph(), GetGraph()->GetAllocator(), GetGraph()->GetLocalAllocator()).CloneGraph();
1148 ASSERT_FALSE(GetGraph()->RunPass<ChecksElimination>());
1149 ASSERT_TRUE(GraphComparator().Compare(GetGraph(), clone));
1150 }
1151
TEST_F(ChecksEliminationTest,SimpleBoundsCheckTest2)1152 TEST_F(ChecksEliminationTest, SimpleBoundsCheckTest2)
1153 {
1154 // not applied, we know nothing about index
1155 GRAPH(GetGraph())
1156 {
1157 CONSTANT(0U, 10U);
1158 PARAMETER(1U, 0U).s32(); // index
1159 BASIC_BLOCK(2U, 1U)
1160 {
1161 INST(2U, Opcode::SaveState).Inputs(0U, 1U).SrcVregs({0U, 1U});
1162 INST(44U, Opcode::LoadAndInitClass).ref().Inputs(2U).TypeId(68U);
1163 INST(3U, Opcode::NewArray).ref().Inputs(44U, 0U, 2U);
1164 INST(4U, Opcode::BoundsCheck).s32().Inputs(0U, 1U, 2U);
1165 INST(5U, Opcode::LoadArray).s32().Inputs(3U, 4U);
1166 INST(6U, Opcode::Return).s32().Inputs(5U);
1167 }
1168 }
1169 auto clone = GraphCloner(GetGraph(), GetGraph()->GetAllocator(), GetGraph()->GetLocalAllocator()).CloneGraph();
1170 ASSERT_FALSE(GetGraph()->RunPass<ChecksElimination>());
1171 ASSERT_TRUE(GraphComparator().Compare(GetGraph(), clone));
1172 }
1173
TEST_F(ChecksEliminationTest,ArithmeticTest)1174 TEST_F(ChecksEliminationTest, ArithmeticTest)
1175 {
1176 ArithmeticTest<AppliedType::REMOVED>(-1L, 20U, Opcode::Add, 1U);
1177 ArithmeticTest<AppliedType::REMOVED>(-1L, 20U, Opcode::Add, 5U);
1178 ArithmeticTest<AppliedType::REMOVED>(-1L, 20U, Opcode::Add, 20U);
1179 ArithmeticTest<AppliedType::REMOVED>(-100L, 20U, Opcode::Add, 115U);
1180 ArithmeticTest<AppliedType::REPLACED>(-1L, 20U, Opcode::Add, 0U);
1181 ArithmeticTest<AppliedType::REPLACED>(0U, 20U, Opcode::Add, 20U);
1182 ArithmeticTest<AppliedType::REPLACED>(-100L, 20U, Opcode::Add, 5U);
1183 ArithmeticTest<AppliedType::REPLACED>(-100L, 20U, Opcode::Add, 125U);
1184 // overflow
1185 ArithmeticTest<AppliedType::NOT_APPLIED>(INT32_MAX, INT32_MAX, Opcode::Add, 1U);
1186 ArithmeticTest<AppliedType::NOT_APPLIED>(INT32_MIN, INT32_MAX, Opcode::Add, -1L);
1187 ArithmeticTest<AppliedType::NOT_APPLIED>(1U, INT32_MAX, Opcode::Add, INT32_MAX);
1188 ArithmeticTest<AppliedType::NOT_APPLIED>(-1L, INT32_MAX, Opcode::Add, INT32_MIN);
1189
1190 ArithmeticTest<AppliedType::REMOVED>(20U, 20U, Opcode::Sub, 1U);
1191 ArithmeticTest<AppliedType::REMOVED>(20U, 20U, Opcode::Sub, 5U);
1192 ArithmeticTest<AppliedType::REMOVED>(20U, 20U, Opcode::Sub, 19U);
1193 ArithmeticTest<AppliedType::REPLACED>(25U, 20U, Opcode::Sub, 3U);
1194 ArithmeticTest<AppliedType::REPLACED>(20U, 20U, Opcode::Sub, 60U);
1195 // overflow
1196 ArithmeticTest<AppliedType::NOT_APPLIED>(INT32_MIN, INT32_MAX, Opcode::Sub, 1U);
1197 ArithmeticTest<AppliedType::NOT_APPLIED>(INT32_MAX, INT32_MAX, Opcode::Sub, -1L);
1198 ArithmeticTest<AppliedType::NOT_APPLIED>(1U, INT32_MAX, Opcode::Sub, INT32_MIN);
1199 ArithmeticTest<AppliedType::REPLACED>(-1L, INT32_MAX, Opcode::Sub, INT32_MAX);
1200
1201 ArithmeticTest<AppliedType::REMOVED>(1U, 20U, Opcode::Mod, 20U);
1202 ArithmeticTest<AppliedType::REMOVED>(101U, 20U, Opcode::Mod, 5U);
1203 ArithmeticTest<AppliedType::REMOVED>(205U, 20U, Opcode::Mod, 5U);
1204 ArithmeticTest<AppliedType::REMOVED>(16U, 5U, Opcode::Mod, 3U);
1205 }
1206
TEST_F(ChecksEliminationTest,ArithmeticTestMul)1207 TEST_F(ChecksEliminationTest, ArithmeticTestMul)
1208 {
1209 ArithmeticTest<AppliedType::REMOVED>(5U, 20U, Opcode::Mul, 2U);
1210 ArithmeticTest<AppliedType::REMOVED>(-5L, 20U, Opcode::Mul, -2L);
1211 ArithmeticTest<AppliedType::REMOVED>(3U, 20U, Opcode::Mul, 5U);
1212 ArithmeticTest<AppliedType::REPLACED>(5U, 20U, Opcode::Mul, -2L);
1213 ArithmeticTest<AppliedType::REPLACED>(-2L, 20U, Opcode::Mul, 5U);
1214 // Zero
1215 ArithmeticTest<AppliedType::REMOVED>(INT32_MAX, 20U, Opcode::Mul, 0U);
1216 ArithmeticTest<AppliedType::REMOVED>(0U, 20U, Opcode::Mul, INT32_MAX);
1217 ArithmeticTest<AppliedType::REMOVED>(INT32_MIN, 20U, Opcode::Mul, 0U);
1218 ArithmeticTest<AppliedType::REMOVED>(0U, 20U, Opcode::Mul, INT32_MIN);
1219 // overflow
1220 ArithmeticTest<AppliedType::NOT_APPLIED>(INT32_MAX, 20U, Opcode::Mul, 2U);
1221 ArithmeticTest<AppliedType::NOT_APPLIED>(INT32_MIN, 20U, Opcode::Mul, -2L);
1222 ArithmeticTest<AppliedType::NOT_APPLIED>(-2L, 20U, Opcode::Mul, INT32_MIN);
1223 ArithmeticTest<AppliedType::NOT_APPLIED>(2U, 20U, Opcode::Mul, INT32_MAX);
1224 ArithmeticTest<AppliedType::NOT_APPLIED>(INT32_MAX, 20U, Opcode::Mul, -2L);
1225 ArithmeticTest<AppliedType::NOT_APPLIED>(INT32_MIN, 20U, Opcode::Mul, 2U);
1226 ArithmeticTest<AppliedType::NOT_APPLIED>(2U, 20U, Opcode::Mul, INT32_MIN);
1227 ArithmeticTest<AppliedType::NOT_APPLIED>(-2L, 20U, Opcode::Mul, INT32_MAX);
1228 }
1229
TEST_F(ChecksEliminationTest,ModTest)1230 TEST_F(ChecksEliminationTest, ModTest)
1231 {
1232 ModTest<true>(10U, 10U);
1233 ModTest<true>(10U, 5U);
1234 ModTest<false>(10U, 11U);
1235 ModTest<false>(10U, 20U);
1236 }
1237
BuildGraphIfTestTrueBlock()1238 void ChecksEliminationTest::BuildGraphIfTestTrueBlock()
1239 {
1240 // we can norrow bounds range for true block
1241 GRAPH(GetGraph())
1242 {
1243 PARAMETER(0U, 0U).s32(); // index
1244 PARAMETER(1U, 1U).ref();
1245 CONSTANT(2U, 10U); // len array
1246 CONSTANT(3U, 0U);
1247 BASIC_BLOCK(2U, 3U, 5U)
1248 {
1249 INST(4U, Opcode::Compare).b().CC(CC_LT).Inputs(0U, 2U);
1250 INST(5U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(4U);
1251 }
1252 BASIC_BLOCK(3U, 4U, 5U)
1253 {
1254 INST(6U, Opcode::Compare).b().CC(CC_GE).Inputs(0U, 3U);
1255 INST(7U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(6U);
1256 }
1257 BASIC_BLOCK(4U, 1U)
1258 {
1259 INST(8U, Opcode::SaveState).Inputs(0U, 1U, 2U, 3U).SrcVregs({0U, 1U, 2U, 3U});
1260 INST(9U, Opcode::BoundsCheck).s32().Inputs(2U, 0U, 8U);
1261 INST(10U, Opcode::LoadArray).s32().Inputs(1U, 9U);
1262 INST(11U, Opcode::Return).s32().Inputs(10U);
1263 }
1264 BASIC_BLOCK(5U, 1U)
1265 {
1266 INST(12U, Opcode::Return).s32().Inputs(3U);
1267 }
1268 }
1269 }
1270
TEST_F(ChecksEliminationTest,IfTestTrueBlock)1271 TEST_F(ChecksEliminationTest, IfTestTrueBlock)
1272 {
1273 BuildGraphIfTestTrueBlock();
1274 ASSERT_TRUE(GetGraph()->RunPass<ChecksElimination>());
1275 auto graph = CreateEmptyGraph();
1276 GRAPH(graph)
1277 {
1278 PARAMETER(0U, 0U).s32();
1279 PARAMETER(1U, 1U).ref();
1280 CONSTANT(2U, 10U);
1281 CONSTANT(3U, 0U);
1282 BASIC_BLOCK(2U, 3U, 5U)
1283 {
1284 INST(4U, Opcode::Compare).b().CC(CC_LT).Inputs(0U, 2U);
1285 INST(5U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(4U);
1286 }
1287 BASIC_BLOCK(3U, 4U, 5U)
1288 {
1289 INST(6U, Opcode::Compare).b().CC(CC_GE).Inputs(0U, 3U);
1290 INST(7U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(6U);
1291 }
1292 BASIC_BLOCK(4U, 1U)
1293 {
1294 INST(8U, Opcode::SaveState).Inputs(0U, 1U, 2U, 3U).SrcVregs({0U, 1U, 2U, 3U});
1295 INST(9U, Opcode::NOP);
1296 INST(10U, Opcode::LoadArray).s32().Inputs(1U, 0U);
1297 INST(11U, Opcode::Return).s32().Inputs(10U);
1298 }
1299 BASIC_BLOCK(5U, 1U)
1300 {
1301 INST(12U, Opcode::Return).s32().Inputs(3U);
1302 }
1303 }
1304 ASSERT_TRUE(GraphComparator().Compare(GetGraph(), graph));
1305 }
1306
BuildGraphIfTestFalseBlock()1307 void ChecksEliminationTest::BuildGraphIfTestFalseBlock()
1308 {
1309 // we can norrow bounds range for false block
1310 GRAPH(GetGraph())
1311 {
1312 PARAMETER(0U, 0U).s32(); // index
1313 PARAMETER(1U, 1U).ref();
1314 CONSTANT(2U, 10U); // len array
1315 CONSTANT(3U, 0U);
1316 BASIC_BLOCK(2U, 5U, 3U)
1317 {
1318 INST(4U, Opcode::Compare).b().CC(CC_GE).Inputs(0U, 2U);
1319 INST(5U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(4U);
1320 }
1321 BASIC_BLOCK(3U, 5U, 4U)
1322 {
1323 INST(6U, Opcode::Compare).b().CC(CC_LT).Inputs(0U, 3U);
1324 INST(7U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(6U);
1325 }
1326 BASIC_BLOCK(4U, 1U)
1327 {
1328 INST(8U, Opcode::SaveState).Inputs(0U, 1U, 2U, 3U).SrcVregs({0U, 1U, 2U, 3U});
1329 INST(9U, Opcode::BoundsCheck).s32().Inputs(2U, 0U, 8U);
1330 INST(10U, Opcode::LoadArray).s32().Inputs(1U, 9U);
1331 INST(11U, Opcode::Return).s32().Inputs(10U);
1332 }
1333 BASIC_BLOCK(5U, 1U)
1334 {
1335 INST(12U, Opcode::Return).s32().Inputs(3U);
1336 }
1337 }
1338 }
1339
TEST_F(ChecksEliminationTest,IfTestFalseBlock)1340 TEST_F(ChecksEliminationTest, IfTestFalseBlock)
1341 {
1342 BuildGraphIfTestFalseBlock();
1343 GetGraph()->RunPass<ChecksElimination>();
1344 auto graph = CreateEmptyGraph();
1345 GRAPH(graph)
1346 {
1347 PARAMETER(0U, 0U).s32();
1348 PARAMETER(1U, 1U).ref();
1349 CONSTANT(2U, 10U);
1350 CONSTANT(3U, 0U);
1351 BASIC_BLOCK(2U, 5U, 3U)
1352 {
1353 INST(4U, Opcode::Compare).b().CC(CC_GE).Inputs(0U, 2U); // index >= len array
1354 INST(5U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(4U);
1355 }
1356 BASIC_BLOCK(3U, 5U, 4U)
1357 {
1358 INST(6U, Opcode::Compare).b().CC(CC_LT).Inputs(0U, 3U); // index < 0
1359 INST(7U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(6U);
1360 }
1361 BASIC_BLOCK(4U, 1U)
1362 {
1363 INST(8U, Opcode::SaveState).Inputs(0U, 1U, 2U, 3U).SrcVregs({0U, 1U, 2U, 3U});
1364 INST(9U, Opcode::NOP);
1365 INST(10U, Opcode::LoadArray).s32().Inputs(1U, 0U);
1366 INST(11U, Opcode::Return).s32().Inputs(10U);
1367 }
1368 BASIC_BLOCK(5U, 1U)
1369 {
1370 INST(12U, Opcode::Return).s32().Inputs(3U);
1371 }
1372 }
1373 ASSERT_TRUE(GraphComparator().Compare(GetGraph(), graph));
1374 }
1375
BuildGraphIfTestTrueBlock1()1376 void ChecksEliminationTest::BuildGraphIfTestTrueBlock1()
1377 {
1378 // we can norrow bounds range for true block
1379 GRAPH(GetGraph())
1380 {
1381 PARAMETER(0U, 0U).s32(); // index
1382 PARAMETER(1U, 1U).ref();
1383 CONSTANT(3U, 0U);
1384 BASIC_BLOCK(6U, 3U, 5U)
1385 {
1386 INST(14U, Opcode::LenArray).s32().Inputs(1U);
1387 INST(4U, Opcode::Compare).b().CC(CC_LT).Inputs(0U, 14U);
1388 INST(5U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(4U);
1389 }
1390 BASIC_BLOCK(3U, 4U, 5U)
1391 {
1392 INST(6U, Opcode::Compare).b().CC(CC_GE).Inputs(0U, 3U);
1393 INST(7U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(6U);
1394 }
1395 BASIC_BLOCK(4U, 1U)
1396 {
1397 INST(8U, Opcode::SaveState).Inputs(0U, 1U, 3U).SrcVregs({0U, 1U, 3U});
1398 INST(9U, Opcode::BoundsCheck).s32().Inputs(14U, 0U, 8U);
1399 INST(10U, Opcode::LoadArray).s32().Inputs(1U, 9U);
1400 INST(11U, Opcode::Return).s32().Inputs(10U);
1401 }
1402 BASIC_BLOCK(5U, 1U)
1403 {
1404 INST(12U, Opcode::Return).s32().Inputs(3U);
1405 }
1406 }
1407 }
1408
TEST_F(ChecksEliminationTest,IfTestTrueBlock1)1409 TEST_F(ChecksEliminationTest, IfTestTrueBlock1)
1410 {
1411 BuildGraphIfTestTrueBlock1();
1412 ASSERT_TRUE(GetGraph()->RunPass<ChecksElimination>());
1413 auto graph = CreateEmptyGraph();
1414 GRAPH(graph)
1415 {
1416 PARAMETER(0U, 0U).s32(); // index
1417 PARAMETER(1U, 1U).ref();
1418 CONSTANT(3U, 0U);
1419 BASIC_BLOCK(6U, 3U, 5U)
1420 {
1421 INST(14U, Opcode::LenArray).s32().Inputs(1U);
1422 INST(4U, Opcode::Compare).b().CC(CC_LT).Inputs(0U, 14U);
1423 INST(5U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(4U);
1424 }
1425 BASIC_BLOCK(3U, 4U, 5U)
1426 {
1427 INST(6U, Opcode::Compare).b().CC(CC_GE).Inputs(0U, 3U);
1428 INST(7U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(6U);
1429 }
1430 BASIC_BLOCK(4U, 1U)
1431 {
1432 INST(8U, Opcode::SaveState).Inputs(0U, 1U, 3U).SrcVregs({0U, 1U, 3U});
1433 INST(9U, Opcode::NOP);
1434 INST(10U, Opcode::LoadArray).s32().Inputs(1U, 0U);
1435 INST(11U, Opcode::Return).s32().Inputs(10U);
1436 }
1437 BASIC_BLOCK(5U, 1U)
1438 {
1439 INST(12U, Opcode::Return).s32().Inputs(3U);
1440 }
1441 }
1442 ASSERT_TRUE(GraphComparator().Compare(GetGraph(), graph));
1443 }
1444
BuildGraphIfTestTrueBlock2()1445 void ChecksEliminationTest::BuildGraphIfTestTrueBlock2()
1446 {
1447 // we can norrow bounds range for true block
1448 GRAPH(GetGraph())
1449 {
1450 PARAMETER(0U, 0U).s32(); // index
1451 PARAMETER(1U, 1U).ref();
1452 CONSTANT(3U, 0U);
1453 BASIC_BLOCK(2U, 3U, 5U)
1454 {
1455 INST(14U, Opcode::LenArray).s32().Inputs(1U);
1456 INST(4U, Opcode::Compare).b().CC(CC_GE).Inputs(0U, 3U);
1457 INST(5U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(4U);
1458 }
1459 BASIC_BLOCK(3U, 4U, 5U)
1460 {
1461 INST(6U, Opcode::Compare).b().CC(CC_LT).Inputs(0U, 14U);
1462 INST(7U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(6U);
1463 }
1464 BASIC_BLOCK(4U, 1U)
1465 {
1466 INST(8U, Opcode::SaveState).Inputs(0U, 1U, 3U).SrcVregs({0U, 1U, 3U});
1467 INST(9U, Opcode::BoundsCheck).s32().Inputs(14U, 0U, 8U);
1468 INST(10U, Opcode::LoadArray).s32().Inputs(1U, 9U);
1469 INST(11U, Opcode::Return).s32().Inputs(10U);
1470 }
1471 BASIC_BLOCK(5U, 1U)
1472 {
1473 INST(12U, Opcode::Return).s32().Inputs(3U);
1474 }
1475 }
1476 }
1477
TEST_F(ChecksEliminationTest,IfTestTrueBlock2)1478 TEST_F(ChecksEliminationTest, IfTestTrueBlock2)
1479 {
1480 BuildGraphIfTestTrueBlock2();
1481 ASSERT_TRUE(GetGraph()->RunPass<ChecksElimination>());
1482 auto graph = CreateEmptyGraph();
1483 GRAPH(graph)
1484 {
1485 PARAMETER(0U, 0U).s32(); // index
1486 PARAMETER(1U, 1U).ref();
1487 CONSTANT(3U, 0U);
1488 BASIC_BLOCK(6U, 3U, 5U)
1489 {
1490 INST(14U, Opcode::LenArray).s32().Inputs(1U);
1491 INST(4U, Opcode::Compare).b().CC(CC_GE).Inputs(0U, 3U);
1492 INST(5U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(4U);
1493 }
1494 BASIC_BLOCK(3U, 4U, 5U)
1495 {
1496 INST(6U, Opcode::Compare).b().CC(CC_LT).Inputs(0U, 14U);
1497 INST(7U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(6U);
1498 }
1499 BASIC_BLOCK(4U, 1U)
1500 {
1501 INST(8U, Opcode::SaveState).Inputs(0U, 1U, 3U).SrcVregs({0U, 1U, 3U});
1502 INST(9U, Opcode::NOP);
1503 INST(10U, Opcode::LoadArray).s32().Inputs(1U, 0U);
1504 INST(11U, Opcode::Return).s32().Inputs(10U);
1505 }
1506 BASIC_BLOCK(5U, 1U)
1507 {
1508 INST(12U, Opcode::Return).s32().Inputs(3U);
1509 }
1510 }
1511 ASSERT_TRUE(GraphComparator().Compare(GetGraph(), graph));
1512 }
1513
BuildGraphIfTestTrueBlock3()1514 void ChecksEliminationTest::BuildGraphIfTestTrueBlock3()
1515 {
1516 // we can norrow bounds range for true block
1517 GRAPH(GetGraph())
1518 {
1519 PARAMETER(0U, 0U).s32(); // index
1520 PARAMETER(1U, 1U).ref();
1521 CONSTANT(3U, 0U);
1522 BASIC_BLOCK(6U, 3U, 5U)
1523 {
1524 INST(14U, Opcode::LenArray).s32().Inputs(1U);
1525 INST(4U, Opcode::Compare).b().CC(CC_GT).Inputs(14U, 0U);
1526 INST(5U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(4U);
1527 }
1528 BASIC_BLOCK(3U, 4U, 5U)
1529 {
1530 INST(6U, Opcode::Compare).b().CC(CC_GE).Inputs(0U, 3U);
1531 INST(7U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(6U);
1532 }
1533 BASIC_BLOCK(4U, 1U)
1534 {
1535 INST(8U, Opcode::SaveState).Inputs(0U, 1U, 3U).SrcVregs({0U, 1U, 3U});
1536 INST(9U, Opcode::BoundsCheck).s32().Inputs(14U, 0U, 8U);
1537 INST(10U, Opcode::LoadArray).s32().Inputs(1U, 9U);
1538 INST(11U, Opcode::Return).s32().Inputs(10U);
1539 }
1540 BASIC_BLOCK(5U, 1U)
1541 {
1542 INST(12U, Opcode::Return).s32().Inputs(3U);
1543 }
1544 }
1545 }
1546
TEST_F(ChecksEliminationTest,IfTestTrueBlock3)1547 TEST_F(ChecksEliminationTest, IfTestTrueBlock3)
1548 {
1549 BuildGraphIfTestTrueBlock3();
1550 ASSERT_TRUE(GetGraph()->RunPass<ChecksElimination>());
1551 auto graph = CreateEmptyGraph();
1552 GRAPH(graph)
1553 {
1554 PARAMETER(0U, 0U).s32(); // index
1555 PARAMETER(1U, 1U).ref();
1556 CONSTANT(3U, 0U);
1557 BASIC_BLOCK(6U, 3U, 5U)
1558 {
1559 INST(14U, Opcode::LenArray).s32().Inputs(1U);
1560 INST(4U, Opcode::Compare).b().CC(CC_GT).Inputs(14U, 0U);
1561 INST(5U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(4U);
1562 }
1563 BASIC_BLOCK(3U, 4U, 5U)
1564 {
1565 INST(6U, Opcode::Compare).b().CC(CC_GE).Inputs(0U, 3U);
1566 INST(7U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(6U);
1567 }
1568 BASIC_BLOCK(4U, 1U)
1569 {
1570 INST(8U, Opcode::SaveState).Inputs(0U, 1U, 3U).SrcVregs({0U, 1U, 3U});
1571 INST(9U, Opcode::NOP);
1572 INST(10U, Opcode::LoadArray).s32().Inputs(1U, 0U);
1573 INST(11U, Opcode::Return).s32().Inputs(10U);
1574 }
1575 BASIC_BLOCK(5U, 1U)
1576 {
1577 INST(12U, Opcode::Return).s32().Inputs(3U);
1578 }
1579 }
1580 ASSERT_TRUE(GraphComparator().Compare(GetGraph(), graph));
1581 }
1582
BuildGraphIfTestFalseBlock1()1583 void ChecksEliminationTest::BuildGraphIfTestFalseBlock1()
1584 {
1585 // we can norrow bounds range for false block
1586 GRAPH(GetGraph())
1587 {
1588 PARAMETER(0U, 0U).s32(); // index
1589 PARAMETER(1U, 1U).ref();
1590 CONSTANT(3U, 0U);
1591 BASIC_BLOCK(2U, 5U, 3U)
1592 {
1593 INST(14U, Opcode::LenArray).s32().Inputs(1U);
1594 INST(4U, Opcode::Compare).b().CC(CC_GE).Inputs(0U, 14U); // index >= len array
1595 INST(5U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(4U);
1596 }
1597 BASIC_BLOCK(3U, 5U, 4U)
1598 {
1599 INST(6U, Opcode::Compare).b().CC(CC_LT).Inputs(0U, 3U); // index < 0
1600 INST(7U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(6U);
1601 }
1602 BASIC_BLOCK(4U, 1U)
1603 {
1604 INST(8U, Opcode::SaveState).Inputs(0U, 1U, 3U).SrcVregs({0U, 1U, 3U});
1605 INST(9U, Opcode::BoundsCheck).s32().Inputs(14U, 0U, 8U);
1606 INST(10U, Opcode::LoadArray).s32().Inputs(1U, 9U);
1607 INST(11U, Opcode::Return).s32().Inputs(10U);
1608 }
1609 BASIC_BLOCK(5U, 1U)
1610 {
1611 INST(12U, Opcode::Return).s32().Inputs(3U);
1612 }
1613 }
1614 }
1615
TEST_F(ChecksEliminationTest,IfTestFalseBlock1)1616 TEST_F(ChecksEliminationTest, IfTestFalseBlock1)
1617 {
1618 BuildGraphIfTestFalseBlock1();
1619 ASSERT_TRUE(GetGraph()->RunPass<ChecksElimination>());
1620 auto graph = CreateEmptyGraph();
1621 GRAPH(graph)
1622 {
1623 PARAMETER(0U, 0U).s32();
1624 PARAMETER(1U, 1U).ref();
1625 CONSTANT(3U, 0U);
1626 BASIC_BLOCK(2U, 5U, 3U)
1627 {
1628 INST(14U, Opcode::LenArray).s32().Inputs(1U);
1629 INST(4U, Opcode::Compare).b().CC(CC_GE).Inputs(0U, 14U); // index >= len array
1630 INST(5U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(4U);
1631 }
1632 BASIC_BLOCK(3U, 5U, 4U)
1633 {
1634 INST(6U, Opcode::Compare).b().CC(CC_LT).Inputs(0U, 3U); // index < 0
1635 INST(7U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(6U);
1636 }
1637 BASIC_BLOCK(4U, 1U)
1638 {
1639 INST(8U, Opcode::SaveState).Inputs(0U, 1U, 3U).SrcVregs({0U, 1U, 3U});
1640 INST(9U, Opcode::NOP);
1641 INST(10U, Opcode::LoadArray).s32().Inputs(1U, 0U);
1642 INST(11U, Opcode::Return).s32().Inputs(10U);
1643 }
1644 BASIC_BLOCK(5U, 1U)
1645 {
1646 INST(12U, Opcode::Return).s32().Inputs(3U);
1647 }
1648 }
1649 ASSERT_TRUE(GraphComparator().Compare(GetGraph(), graph));
1650 }
1651
BuildGraphIfTestFalseBlock2()1652 void ChecksEliminationTest::BuildGraphIfTestFalseBlock2()
1653 {
1654 // we can norrow bounds range for false block
1655 GRAPH(GetGraph())
1656 {
1657 PARAMETER(0U, 0U).s32(); // index
1658 PARAMETER(1U, 1U).ref();
1659 CONSTANT(3U, 0U);
1660 BASIC_BLOCK(2U, 5U, 3U)
1661 {
1662 INST(14U, Opcode::LenArray).s32().Inputs(1U);
1663 INST(4U, Opcode::Compare).b().CC(CC_LT).Inputs(0U, 3U); // index < 0
1664 INST(5U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(4U);
1665 }
1666 BASIC_BLOCK(3U, 5U, 4U)
1667 {
1668 INST(6U, Opcode::Compare).b().CC(CC_GE).Inputs(0U, 14U); // index >= len array
1669 INST(7U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(6U);
1670 }
1671 BASIC_BLOCK(4U, 1U)
1672 {
1673 INST(8U, Opcode::SaveState).Inputs(0U, 1U, 3U).SrcVregs({0U, 1U, 3U});
1674 INST(9U, Opcode::BoundsCheck).s32().Inputs(14U, 0U, 8U);
1675 INST(10U, Opcode::LoadArray).s32().Inputs(1U, 9U);
1676 INST(11U, Opcode::Return).s32().Inputs(10U);
1677 }
1678 BASIC_BLOCK(5U, 1U)
1679 {
1680 INST(12U, Opcode::Return).s32().Inputs(3U);
1681 }
1682 }
1683 }
1684
TEST_F(ChecksEliminationTest,IfTestFalseBlock2)1685 TEST_F(ChecksEliminationTest, IfTestFalseBlock2)
1686 {
1687 BuildGraphIfTestFalseBlock2();
1688 ASSERT_TRUE(GetGraph()->RunPass<ChecksElimination>());
1689 auto graph = CreateEmptyGraph();
1690 GRAPH(graph)
1691 {
1692 PARAMETER(0U, 0U).s32();
1693 PARAMETER(1U, 1U).ref();
1694 CONSTANT(3U, 0U);
1695 BASIC_BLOCK(2U, 5U, 3U)
1696 {
1697 INST(14U, Opcode::LenArray).s32().Inputs(1U);
1698 INST(4U, Opcode::Compare).b().CC(CC_LT).Inputs(0U, 3U); // index < 0
1699 INST(5U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(4U);
1700 }
1701 BASIC_BLOCK(3U, 5U, 4U)
1702 {
1703 INST(6U, Opcode::Compare).b().CC(CC_GE).Inputs(0U, 14U); // index >= len array
1704 INST(7U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(6U);
1705 }
1706 BASIC_BLOCK(4U, 1U)
1707 {
1708 INST(8U, Opcode::SaveState).Inputs(0U, 1U, 3U).SrcVregs({0U, 1U, 3U});
1709 INST(9U, Opcode::NOP);
1710 INST(10U, Opcode::LoadArray).s32().Inputs(1U, 0U);
1711 INST(11U, Opcode::Return).s32().Inputs(10U);
1712 }
1713 BASIC_BLOCK(5U, 1U)
1714 {
1715 INST(12U, Opcode::Return).s32().Inputs(3U);
1716 }
1717 }
1718 ASSERT_TRUE(GraphComparator().Compare(GetGraph(), graph));
1719 }
1720
BuildGraphIfTestFalseBlock3()1721 void ChecksEliminationTest::BuildGraphIfTestFalseBlock3()
1722 {
1723 // we can norrow bounds range for false block
1724 GRAPH(GetGraph())
1725 {
1726 PARAMETER(0U, 0U).s32(); // index
1727 PARAMETER(1U, 1U).ref();
1728 CONSTANT(3U, 0U);
1729 BASIC_BLOCK(2U, 5U, 3U)
1730 {
1731 INST(14U, Opcode::LenArray).s32().Inputs(1U);
1732 INST(4U, Opcode::Compare).b().CC(CC_LE).Inputs(14U, 0U); // len array < index
1733 INST(5U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(4U);
1734 }
1735 BASIC_BLOCK(3U, 5U, 4U)
1736 {
1737 INST(6U, Opcode::Compare).b().CC(CC_LT).Inputs(0U, 3U); // index < 0
1738 INST(7U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(6U);
1739 }
1740 BASIC_BLOCK(4U, 1U)
1741 {
1742 INST(8U, Opcode::SaveState).Inputs(0U, 1U, 3U).SrcVregs({0U, 1U, 3U});
1743 INST(9U, Opcode::BoundsCheck).s32().Inputs(14U, 0U, 8U);
1744 INST(10U, Opcode::LoadArray).s32().Inputs(1U, 9U);
1745 INST(11U, Opcode::Return).s32().Inputs(10U);
1746 }
1747 BASIC_BLOCK(5U, 1U)
1748 {
1749 INST(12U, Opcode::Return).s32().Inputs(3U);
1750 }
1751 }
1752 }
1753
TEST_F(ChecksEliminationTest,IfTestFalseBlock3)1754 TEST_F(ChecksEliminationTest, IfTestFalseBlock3)
1755 {
1756 BuildGraphIfTestFalseBlock3();
1757 ASSERT_TRUE(GetGraph()->RunPass<ChecksElimination>());
1758 auto graph = CreateEmptyGraph();
1759 GRAPH(graph)
1760 {
1761 PARAMETER(0U, 0U).s32();
1762 PARAMETER(1U, 1U).ref();
1763 CONSTANT(3U, 0U);
1764 BASIC_BLOCK(2U, 5U, 3U)
1765 {
1766 INST(14U, Opcode::LenArray).s32().Inputs(1U);
1767 INST(4U, Opcode::Compare).b().CC(CC_LE).Inputs(14U, 0U); // len array < index
1768 INST(5U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(4U);
1769 }
1770 BASIC_BLOCK(3U, 5U, 4U)
1771 {
1772 INST(6U, Opcode::Compare).b().CC(CC_LT).Inputs(0U, 3U); // index < 0
1773 INST(7U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(6U);
1774 }
1775 BASIC_BLOCK(4U, 1U)
1776 {
1777 INST(8U, Opcode::SaveState).Inputs(0U, 1U, 3U).SrcVregs({0U, 1U, 3U});
1778 INST(9U, Opcode::NOP);
1779 INST(10U, Opcode::LoadArray).s32().Inputs(1U, 0U);
1780 INST(11U, Opcode::Return).s32().Inputs(10U);
1781 }
1782 BASIC_BLOCK(5U, 1U)
1783 {
1784 INST(12U, Opcode::Return).s32().Inputs(3U);
1785 }
1786 }
1787 ASSERT_TRUE(GraphComparator().Compare(GetGraph(), graph));
1788 }
1789
TEST_F(ChecksEliminationTest,IfTest1)1790 TEST_F(ChecksEliminationTest, IfTest1)
1791 {
1792 // not applied if without compare
1793 GRAPH(GetGraph())
1794 {
1795 PARAMETER(0U, 0U).s32(); // index
1796 PARAMETER(1U, 1U).ref();
1797 PARAMETER(13U, 2U).b();
1798 CONSTANT(2U, 10U); // len array
1799 CONSTANT(3U, 0U);
1800 BASIC_BLOCK(2U, 3U, 5U)
1801 {
1802 INST(5U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(13U);
1803 }
1804 BASIC_BLOCK(3U, 4U, 5U)
1805 {
1806 INST(7U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(13U);
1807 }
1808 BASIC_BLOCK(4U, 1U)
1809 {
1810 INST(8U, Opcode::SaveState).Inputs(0U, 1U, 2U, 3U).SrcVregs({0U, 1U, 2U, 3U});
1811 INST(9U, Opcode::BoundsCheck).s32().Inputs(2U, 0U, 8U);
1812 INST(10U, Opcode::LoadArray).s32().Inputs(1U, 9U);
1813 INST(11U, Opcode::Return).s32().Inputs(10U);
1814 }
1815 BASIC_BLOCK(5U, 1U)
1816 {
1817 INST(12U, Opcode::Return).s32().Inputs(3U);
1818 }
1819 }
1820 auto clone = GraphCloner(GetGraph(), GetGraph()->GetAllocator(), GetGraph()->GetLocalAllocator()).CloneGraph();
1821 ASSERT_FALSE(GetGraph()->RunPass<ChecksElimination>());
1822 ASSERT_TRUE(GraphComparator().Compare(GetGraph(), clone));
1823 }
1824
TEST_F(ChecksEliminationTest,IfTest2)1825 TEST_F(ChecksEliminationTest, IfTest2)
1826 {
1827 // not applied, compare intputs not int32
1828 GRAPH(GetGraph())
1829 {
1830 PARAMETER(0U, 0U).s32(); // index
1831 PARAMETER(1U, 1U).ref();
1832 PARAMETER(13U, 2U).s64();
1833 PARAMETER(14U, 3U).s64();
1834 CONSTANT(2U, 10U); // len array
1835 CONSTANT(3U, 0U);
1836 BASIC_BLOCK(2U, 3U, 5U)
1837 {
1838 INST(15U, Opcode::Compare).b().CC(CC_GE).Inputs(13U, 14U);
1839 INST(5U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(15U);
1840 }
1841 BASIC_BLOCK(3U, 4U, 5U)
1842 {
1843 INST(16U, Opcode::Compare).b().CC(CC_GE).Inputs(13U, 14U);
1844 INST(7U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(16U);
1845 }
1846 BASIC_BLOCK(4U, 1U)
1847 {
1848 INST(8U, Opcode::SaveState).Inputs(0U, 1U, 2U, 3U).SrcVregs({0U, 1U, 2U, 3U});
1849 INST(9U, Opcode::BoundsCheck).s32().Inputs(2U, 0U, 8U);
1850 INST(10U, Opcode::LoadArray).s32().Inputs(1U, 9U);
1851 INST(11U, Opcode::Return).s32().Inputs(10U);
1852 }
1853 BASIC_BLOCK(5U, 1U)
1854 {
1855 INST(12U, Opcode::Return).s32().Inputs(3U);
1856 }
1857 }
1858 auto clone = GraphCloner(GetGraph(), GetGraph()->GetAllocator(), GetGraph()->GetLocalAllocator()).CloneGraph();
1859 ASSERT_FALSE(GetGraph()->RunPass<ChecksElimination>());
1860 ASSERT_TRUE(GraphComparator().Compare(GetGraph(), clone));
1861 }
1862
TEST_F(ChecksEliminationTest,PhiTest)1863 TEST_F(ChecksEliminationTest, PhiTest)
1864 {
1865 PhiTest<true>(5U, 10U, 10U);
1866 PhiTest<true>(9U, 10U, 5U);
1867 PhiTest<false>(10U, 10U, 10U);
1868 PhiTest<false>(-1L, 10U, 10U);
1869 PhiTest<false>(5U, 10U, 11U);
1870 }
1871
BuildGraphSimpleLoopTestInc()1872 void ChecksEliminationTest::BuildGraphSimpleLoopTestInc()
1873 {
1874 // new_array(len_array)
1875 // For(int i = 0, i < len_array, i++) begin
1876 // boundscheck(len_array, i) - can remove
1877 // a[i] = 0
1878 // end
1879 // return new_array
1880 GRAPH(GetGraph())
1881 {
1882 CONSTANT(0U, 0U); // initial
1883 CONSTANT(1U, 1U); // increment
1884 CONSTANT(2U, 10U); // len_array
1885 BASIC_BLOCK(2U, 3U)
1886 {
1887 INST(43U, Opcode::SaveState).Inputs(0U, 1U, 2U).SrcVregs({0U, 1U, 2U});
1888 INST(44U, Opcode::LoadAndInitClass).ref().Inputs().TypeId(68U);
1889 INST(3U, Opcode::NewArray).ref().Inputs(44U, 2U, 43U);
1890 }
1891 BASIC_BLOCK(3U, 4U, 5U)
1892 {
1893 INST(4U, Opcode::Phi).s32().Inputs(0U, 10U);
1894 INST(5U, Opcode::Compare).CC(CC_LT).b().Inputs(4U, 2U); // i < len_array
1895 INST(6U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(5U);
1896 }
1897 BASIC_BLOCK(4U, 3U)
1898 {
1899 INST(7U, Opcode::SaveState).Inputs(0U, 1U, 2U, 3U).SrcVregs({0U, 1U, 2U, 3U});
1900 INST(8U, Opcode::BoundsCheck).s32().Inputs(2U, 4U, 7U);
1901 INST(9U, Opcode::StoreArray).s32().Inputs(3U, 8U, 0U); // a[i] = 0
1902 INST(10U, Opcode::Add).s32().Inputs(4U, 1U); // i++
1903 }
1904 BASIC_BLOCK(5U, 1U)
1905 {
1906 INST(12U, Opcode::Return).ref().Inputs(3U);
1907 }
1908 }
1909 }
1910
TEST_F(ChecksEliminationTest,SimpleLoopTestInc)1911 TEST_F(ChecksEliminationTest, SimpleLoopTestInc)
1912 {
1913 BuildGraphSimpleLoopTestInc();
1914 ASSERT_TRUE(GetGraph()->RunPass<ChecksElimination>());
1915 auto graph = CreateEmptyGraph();
1916 GRAPH(graph)
1917 {
1918 CONSTANT(0U, 0U); // initial
1919 CONSTANT(1U, 1U); // increment
1920 CONSTANT(2U, 10U); // len_array
1921 BASIC_BLOCK(2U, 3U)
1922 {
1923 INST(43U, Opcode::SaveState).Inputs(0U, 1U, 2U).SrcVregs({0U, 1U, 2U});
1924 INST(44U, Opcode::LoadAndInitClass).ref().Inputs().TypeId(68U);
1925 INST(3U, Opcode::NewArray).ref().Inputs(44U, 2U, 43U);
1926 }
1927 BASIC_BLOCK(3U, 4U, 5U)
1928 {
1929 INST(4U, Opcode::Phi).s32().Inputs(0U, 10U);
1930 INST(5U, Opcode::Compare).CC(CC_LT).b().Inputs(4U, 2U); // i < len_array
1931 INST(6U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(5U);
1932 }
1933 BASIC_BLOCK(4U, 3U)
1934 {
1935 INST(7U, Opcode::SaveState).Inputs(0U, 1U, 2U, 3U).SrcVregs({0U, 1U, 2U, 3U});
1936 INST(8U, Opcode::NOP);
1937 INST(9U, Opcode::StoreArray).s32().Inputs(3U, 4U, 0U); // a[i] = 0
1938 INST(10U, Opcode::Add).s32().Inputs(4U, 1U); // i++
1939 }
1940 BASIC_BLOCK(5U, 1U)
1941 {
1942 INST(12U, Opcode::Return).ref().Inputs(3U);
1943 }
1944 }
1945 ASSERT_TRUE(GraphComparator().Compare(GetGraph(), graph));
1946 }
1947
BuildGraphSimpleLoopTestIncAfterPeeling()1948 void ChecksEliminationTest::BuildGraphSimpleLoopTestIncAfterPeeling()
1949 {
1950 // new_array(len_array)
1951 // For(int i = 0, i < len_array, i++) begin
1952 // boundscheck(len_array, i) - can remove
1953 // a[i] = 0
1954 // end
1955 // return new_array
1956 GRAPH(GetGraph())
1957 {
1958 CONSTANT(0U, 0U); // initial
1959 CONSTANT(1U, 1U); // increment
1960 CONSTANT(2U, 10U); // len_array
1961 BASIC_BLOCK(2U, 3U, 5U)
1962 {
1963 INST(43U, Opcode::SaveState).Inputs(0U, 1U, 2U).SrcVregs({0U, 1U, 2U});
1964 INST(44U, Opcode::LoadAndInitClass).ref().Inputs().TypeId(68U);
1965 INST(3U, Opcode::NewArray).ref().Inputs(44U, 2U, 43U);
1966 INST(5U, Opcode::Compare).SrcType(DataType::INT32).CC(CC_LT).b().Inputs(0U, 2U); // 0 < len_array
1967 INST(6U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(5U);
1968 }
1969 BASIC_BLOCK(3U, 3U, 5U)
1970 {
1971 INST(4U, Opcode::Phi).s32().Inputs(0U, 10U);
1972 INST(7U, Opcode::SaveState).Inputs(0U, 1U, 2U, 3U).SrcVregs({0U, 1U, 2U, 3U});
1973 INST(8U, Opcode::BoundsCheck).s32().Inputs(2U, 4U, 7U);
1974 INST(9U, Opcode::StoreArray).s32().Inputs(3U, 8U, 0U); // a[i] = 0
1975 INST(10U, Opcode::Add).s32().Inputs(4U, 1U); // i++
1976 INST(13U, Opcode::Compare).CC(CC_LT).b().Inputs(10U, 2U); // i < len_array
1977 INST(14U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(13U);
1978 }
1979 BASIC_BLOCK(5U, 1U)
1980 {
1981 INST(12U, Opcode::Return).ref().Inputs(3U);
1982 }
1983 }
1984 }
1985
TEST_F(ChecksEliminationTest,SimpleLoopTestIncAfterPeeling)1986 TEST_F(ChecksEliminationTest, SimpleLoopTestIncAfterPeeling)
1987 {
1988 BuildGraphSimpleLoopTestIncAfterPeeling();
1989 ASSERT_TRUE(GetGraph()->RunPass<ChecksElimination>());
1990 auto graph = CreateEmptyGraph();
1991 GRAPH(graph)
1992 {
1993 CONSTANT(0U, 0U); // initial
1994 CONSTANT(1U, 1U); // increment
1995 CONSTANT(2U, 10U); // len_array
1996 BASIC_BLOCK(2U, 3U, 5U)
1997 {
1998 INST(43U, Opcode::SaveState).Inputs(0U, 1U, 2U).SrcVregs({0U, 1U, 2U});
1999 INST(44U, Opcode::LoadAndInitClass).ref().Inputs().TypeId(68U);
2000 INST(3U, Opcode::NewArray).ref().Inputs(44U, 2U, 43U);
2001 INST(5U, Opcode::Compare).SrcType(DataType::INT32).CC(CC_LT).b().Inputs(0U, 2U); // 0 < len_array
2002 INST(6U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(5U);
2003 }
2004 BASIC_BLOCK(3U, 3U, 5U)
2005 {
2006 INST(4U, Opcode::Phi).s32().Inputs(0U, 10U);
2007 INST(7U, Opcode::SaveState).Inputs(0U, 1U, 2U, 3U).SrcVregs({0U, 1U, 2U, 3U});
2008 INST(8U, Opcode::NOP);
2009 INST(9U, Opcode::StoreArray).s32().Inputs(3U, 4U, 0U); // a[i] = 0
2010 INST(10U, Opcode::Add).s32().Inputs(4U, 1U); // i++
2011 INST(13U, Opcode::Compare).CC(CC_LT).b().Inputs(10U, 2U); // i < len_array
2012 INST(14U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(13U);
2013 }
2014 BASIC_BLOCK(5U, 1U)
2015 {
2016 INST(12U, Opcode::Return).ref().Inputs(3U);
2017 }
2018 }
2019 ASSERT_TRUE(GraphComparator().Compare(GetGraph(), graph));
2020 }
2021
BuildGraphSimpleLoopTestIncAfterPeeling1()2022 void ChecksEliminationTest::BuildGraphSimpleLoopTestIncAfterPeeling1()
2023 {
2024 // new_array(len_array)
2025 // For(int i = 0, i < len_array, i++) begin
2026 // boundscheck(len_array, i) - can remove
2027 // a[i] = 0
2028 // end
2029 // return new_array
2030 GRAPH(GetGraph())
2031 {
2032 CONSTANT(0U, 0U); // initial
2033 CONSTANT(1U, 1U); // increment
2034 PARAMETER(3U, 0U).ref(); // array
2035 BASIC_BLOCK(2U, 3U, 5U)
2036 {
2037 INST(16U, Opcode::LenArray).s32().Inputs(3U);
2038 INST(5U, Opcode::Compare).SrcType(DataType::INT32).CC(CC_LT).b().Inputs(0U, 16U); // 0 < len_array
2039 INST(6U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(5U);
2040 }
2041 BASIC_BLOCK(3U, 3U, 5U)
2042 {
2043 INST(4U, Opcode::Phi).s32().Inputs(0U, 10U);
2044 INST(7U, Opcode::SaveState).Inputs(0U, 1U, 3U).SrcVregs({0U, 1U, 3U});
2045 INST(8U, Opcode::BoundsCheck).s32().Inputs(16U, 4U, 7U);
2046 INST(9U, Opcode::StoreArray).s32().Inputs(3U, 8U, 0U); // a[i] = 0
2047 INST(10U, Opcode::Add).s32().Inputs(4U, 1U); // i++
2048 INST(13U, Opcode::Compare).CC(CC_LT).b().Inputs(10U, 16U); // i < len_array
2049 INST(14U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(13U);
2050 }
2051 BASIC_BLOCK(5U, 1U)
2052 {
2053 INST(12U, Opcode::Return).ref().Inputs(3U);
2054 }
2055 }
2056 }
2057
TEST_F(ChecksEliminationTest,SimpleLoopTestIncAfterPeeling1)2058 TEST_F(ChecksEliminationTest, SimpleLoopTestIncAfterPeeling1)
2059 {
2060 BuildGraphSimpleLoopTestIncAfterPeeling1();
2061 ASSERT_TRUE(GetGraph()->RunPass<ChecksElimination>());
2062 auto graph = CreateEmptyGraph();
2063 GRAPH(graph)
2064 {
2065 CONSTANT(0U, 0U); // initial
2066 CONSTANT(1U, 1U); // increment
2067 PARAMETER(3U, 0U).ref(); // array
2068 BASIC_BLOCK(2U, 3U, 5U)
2069 {
2070 INST(16U, Opcode::LenArray).s32().Inputs(3U);
2071 INST(5U, Opcode::Compare).SrcType(DataType::INT32).CC(CC_LT).b().Inputs(0U, 16U); // 0 < len_array
2072 INST(6U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(5U);
2073 }
2074 BASIC_BLOCK(3U, 3U, 5U)
2075 {
2076 INST(4U, Opcode::Phi).s32().Inputs(0U, 10U);
2077 INST(7U, Opcode::SaveState).Inputs(0U, 1U, 3U).SrcVregs({0U, 1U, 3U});
2078 INST(8U, Opcode::NOP);
2079 INST(9U, Opcode::StoreArray).s32().Inputs(3U, 4U, 0U); // a[i] = 0
2080 INST(10U, Opcode::Add).s32().Inputs(4U, 1U); // i++
2081 INST(13U, Opcode::Compare).CC(CC_LT).b().Inputs(10U, 16U); // i < len_array
2082 INST(14U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(13U);
2083 }
2084 BASIC_BLOCK(5U, 1U)
2085 {
2086 INST(12U, Opcode::Return).ref().Inputs(3U);
2087 }
2088 }
2089 ASSERT_TRUE(GraphComparator().Compare(GetGraph(), graph));
2090 }
2091
BuildGraphSimpleLoopTestDec()2092 void ChecksEliminationTest::BuildGraphSimpleLoopTestDec()
2093 {
2094 // new_array(len_array)
2095 // For(int i = len_array-1, i >= 0, i--) begin
2096 // boundscheck(len_array, i) - can remove
2097 // a[i] = 0
2098 // end
2099 // return new_array
2100 GRAPH(GetGraph())
2101 {
2102 CONSTANT(0U, 0U);
2103 CONSTANT(1U, 1U); // increment
2104 CONSTANT(2U, 10U); // initial and len_array
2105 BASIC_BLOCK(2U, 3U)
2106 {
2107 INST(43U, Opcode::SaveState).Inputs(0U).SrcVregs({0U});
2108 INST(44U, Opcode::LoadAndInitClass).ref().Inputs().TypeId(68U);
2109 INST(3U, Opcode::NewArray).ref().Inputs(44U, 2U, 43U);
2110 INST(13U, Opcode::Sub).s32().Inputs(2U, 1U); // len_array - 1
2111 }
2112 BASIC_BLOCK(3U, 4U, 5U)
2113 {
2114 INST(4U, Opcode::Phi).s32().Inputs(13U, 10U);
2115 INST(5U, Opcode::Compare).CC(CC_GE).b().Inputs(4U, 0U); // i >= 0
2116 INST(6U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(5U);
2117 }
2118 BASIC_BLOCK(4U, 3U)
2119 {
2120 INST(7U, Opcode::SaveState).Inputs(0U, 1U, 2U, 3U).SrcVregs({0U, 1U, 2U, 3U});
2121 INST(8U, Opcode::BoundsCheck).s32().Inputs(2U, 4U, 7U);
2122 INST(9U, Opcode::StoreArray).s32().Inputs(3U, 8U, 0U); // a[i] = 0
2123 INST(10U, Opcode::Sub).s32().Inputs(4U, 1U); // i--
2124 }
2125 BASIC_BLOCK(5U, 1U)
2126 {
2127 INST(12U, Opcode::Return).ref().Inputs(3U);
2128 }
2129 }
2130 }
2131
TEST_F(ChecksEliminationTest,SimpleLoopTestDec)2132 TEST_F(ChecksEliminationTest, SimpleLoopTestDec)
2133 {
2134 BuildGraphSimpleLoopTestDec();
2135 ASSERT_TRUE(GetGraph()->RunPass<ChecksElimination>());
2136 auto graph = CreateEmptyGraph();
2137 GRAPH(graph)
2138 {
2139 CONSTANT(0U, 0U);
2140 CONSTANT(1U, 1U); // increment
2141 CONSTANT(2U, 10U); // initial and len_array
2142 BASIC_BLOCK(2U, 3U)
2143 {
2144 INST(43U, Opcode::SaveState).Inputs(0U).SrcVregs({0U});
2145 INST(44U, Opcode::LoadAndInitClass).ref().Inputs().TypeId(68U);
2146 INST(3U, Opcode::NewArray).ref().Inputs(44U, 2U, 43U);
2147 INST(13U, Opcode::Sub).s32().Inputs(2U, 1U); // len_array - 1
2148 }
2149 BASIC_BLOCK(3U, 4U, 5U)
2150 {
2151 INST(4U, Opcode::Phi).s32().Inputs(13U, 10U);
2152 INST(5U, Opcode::Compare).CC(CC_GE).b().Inputs(4U, 0U); // i >= 0
2153 INST(6U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(5U);
2154 }
2155 BASIC_BLOCK(4U, 3U)
2156 {
2157 INST(7U, Opcode::SaveState).Inputs(0U, 1U, 2U, 3U).SrcVregs({0U, 1U, 2U, 3U});
2158 INST(8U, Opcode::NOP);
2159 INST(9U, Opcode::StoreArray).s32().Inputs(3U, 4U, 0U); // a[i] = 0
2160 INST(10U, Opcode::Sub).s32().Inputs(4U, 1U); // i--
2161 }
2162 BASIC_BLOCK(5U, 1U)
2163 {
2164 INST(12U, Opcode::Return).ref().Inputs(3U);
2165 }
2166 }
2167 ASSERT_TRUE(GraphComparator().Compare(GetGraph(), graph));
2168 }
2169
BuildGraphLoopWithUnknowLowerUpperValue()2170 void ChecksEliminationTest::BuildGraphLoopWithUnknowLowerUpperValue()
2171 {
2172 // applied
2173 GRAPH(GetGraph())
2174 {
2175 CONSTANT(0U, 1U); // increment
2176 PARAMETER(1U, 0U).s32(); // lower
2177 PARAMETER(2U, 1U).s32(); // upper
2178 PARAMETER(3U, 2U).ref(); // array
2179 BASIC_BLOCK(7U, 3U, 6U)
2180 {
2181 INST(4U, Opcode::SaveState).Inputs(0U, 3U).SrcVregs({0U, 1U});
2182 INST(5U, Opcode::NullCheck).ref().Inputs(3U, 4U);
2183 INST(6U, Opcode::LenArray).s32().Inputs(5U);
2184 INST(30U, Opcode::SaveStateDeoptimize).Inputs(0U).SrcVregs({0U});
2185 INST(7U, Opcode::Compare).CC(ConditionCode::CC_LT).b().Inputs(1U, 2U); // lower < upper
2186 INST(8U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(7U);
2187 }
2188 BASIC_BLOCK(3U, 3U, 6U)
2189 {
2190 INST(9U, Opcode::Phi).s32().Inputs(1U, 13U);
2191 INST(10U, Opcode::SaveState).Inputs(0U, 3U).SrcVregs({0U, 1U});
2192 INST(11U, Opcode::BoundsCheck).s32().Inputs(6U, 9U, 10U);
2193 INST(12U, Opcode::StoreArray).s32().Inputs(3U, 11U, 0U); // a[i] = 0
2194 INST(13U, Opcode::Add).s32().Inputs(9U, 0U); // i++
2195 INST(14U, Opcode::Compare).CC(ConditionCode::CC_LT).b().Inputs(13U, 2U); // i < upper
2196 INST(15U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(14U);
2197 }
2198 BASIC_BLOCK(6U, 1U)
2199 {
2200 INST(16U, Opcode::Phi).s32().Inputs(0U, 13U);
2201 INST(17U, Opcode::Return).s32().Inputs(16U);
2202 }
2203 }
2204 }
2205
TEST_F(ChecksEliminationTest,LoopWithUnknowLowerUpperValue)2206 TEST_F(ChecksEliminationTest, LoopWithUnknowLowerUpperValue)
2207 {
2208 BuildGraphLoopWithUnknowLowerUpperValue();
2209 ASSERT_TRUE(GetGraph()->RunPass<ChecksElimination>());
2210 auto graph1 = CreateEmptyGraph();
2211 GRAPH(graph1)
2212 {
2213 CONSTANT(0U, 1U); // increment
2214 PARAMETER(1U, 0U).s32(); // lower
2215 PARAMETER(2U, 1U).s32(); // upper
2216 PARAMETER(3U, 2U).ref(); // array
2217 CONSTANT(22U, 0U);
2218 BASIC_BLOCK(7U, 3U, 6U)
2219 {
2220 INST(4U, Opcode::SaveState).Inputs(0U, 3U).SrcVregs({0U, 1U});
2221 INST(5U, Opcode::NullCheck).ref().Inputs(3U, 4U);
2222 INST(6U, Opcode::LenArray).s32().Inputs(5U);
2223 INST(30U, Opcode::SaveStateDeoptimize).Inputs(0U).SrcVregs({0U});
2224 INST(20U, Opcode::Compare).CC(ConditionCode::CC_LT).b().Inputs(1U, 22U); // (lower) < 0
2225 INST(21U, Opcode::DeoptimizeIf).DeoptimizeType(DeoptimizeType::BOUNDS_CHECK).Inputs(20U, 30U);
2226 INST(18U, Opcode::Compare).CC(ConditionCode::CC_LT).b().Inputs(6U, 2U); // len_array < (upper)
2227 INST(19U, Opcode::DeoptimizeIf).DeoptimizeType(DeoptimizeType::BOUNDS_CHECK).Inputs(18U, 30U);
2228 INST(7U, Opcode::Compare).CC(ConditionCode::CC_LT).b().Inputs(1U, 2U); // lower < X
2229 INST(8U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(7U);
2230 }
2231 BASIC_BLOCK(3U, 3U, 6U)
2232 {
2233 INST(9U, Opcode::Phi).s32().Inputs(1U, 13U);
2234 INST(10U, Opcode::SaveState).Inputs(0U, 3U).SrcVregs({0U, 1U});
2235 INST(11U, Opcode::NOP);
2236 INST(12U, Opcode::StoreArray).s32().Inputs(3U, 9U, 0U); // a[i] = 0
2237 INST(13U, Opcode::Add).s32().Inputs(9U, 0U); // i++
2238 INST(14U, Opcode::Compare).CC(ConditionCode::CC_LT).b().Inputs(13U, 2U); // i < upper
2239 INST(15U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(14U);
2240 }
2241 BASIC_BLOCK(6U, 1U)
2242 {
2243 INST(16U, Opcode::Phi).s32().Inputs(0U, 13U);
2244 INST(17U, Opcode::Return).s32().Inputs(16U);
2245 }
2246 }
2247 ASSERT_TRUE(GraphComparator().Compare(GetGraph(), graph1));
2248 }
2249
BuildGraphLoopWithUnknowLowerUpperValueDown(ConditionCode cc)2250 Graph *ChecksEliminationTest::BuildGraphLoopWithUnknowLowerUpperValueDown(ConditionCode cc)
2251 {
2252 auto graph = CreateEmptyGraph();
2253 GRAPH(graph)
2254 {
2255 CONSTANT(0U, 1U); // increment
2256 PARAMETER(1U, 0U).s32(); // lower
2257 PARAMETER(2U, 1U).s32(); // upper
2258 PARAMETER(3U, 2U).ref(); // array
2259 BASIC_BLOCK(7U, 3U, 6U)
2260 {
2261 INST(4U, Opcode::SaveState).Inputs(0U, 3U).SrcVregs({0U, 1U});
2262 INST(5U, Opcode::NullCheck).ref().Inputs(3U, 4U);
2263 INST(6U, Opcode::LenArray).s32().Inputs(5U);
2264 INST(30U, Opcode::SaveStateDeoptimize).Inputs(0U).SrcVregs({0U});
2265 INST(7U, Opcode::Compare).CC(cc).b().Inputs(2U, 1U); // upper >(>=) lower
2266 INST(8U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(7U);
2267 }
2268 BASIC_BLOCK(3U, 3U, 6U)
2269 {
2270 INST(9U, Opcode::Phi).s32().Inputs(2U, 13U);
2271 INST(10U, Opcode::SaveState).Inputs(0U, 3U).SrcVregs({0U, 1U});
2272 INST(11U, Opcode::BoundsCheck).s32().Inputs(6U, 9U, 10U);
2273 INST(12U, Opcode::StoreArray).s32().Inputs(3U, 11U, 0U); // a[i] = 0
2274 INST(13U, Opcode::Sub).s32().Inputs(9U, 0U); // i--
2275 INST(14U, Opcode::Compare).CC(cc).b().Inputs(13U, 1U); // i >(>=) lower
2276 INST(15U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(14U);
2277 }
2278 BASIC_BLOCK(6U, 1U)
2279 {
2280 INST(16U, Opcode::Phi).s32().Inputs(0U, 13U);
2281 INST(17U, Opcode::Return).s32().Inputs(16U);
2282 }
2283 }
2284 return graph;
2285 }
2286
TEST_F(ChecksEliminationTest,LoopWithUnknowLowerUpperValueDown)2287 TEST_F(ChecksEliminationTest, LoopWithUnknowLowerUpperValueDown)
2288 {
2289 for (auto cc : {CC_GT, CC_GE}) {
2290 // applied
2291 auto *graph = BuildGraphLoopWithUnknowLowerUpperValueDown(cc);
2292 EXPECT_TRUE(graph->RunPass<ChecksElimination>());
2293 auto graph1 = CreateEmptyGraph();
2294 GRAPH(graph1)
2295 {
2296 CONSTANT(0U, 1U); // increment
2297 PARAMETER(1U, 0U).s32(); // lower
2298 PARAMETER(2U, 1U).s32(); // upper
2299 PARAMETER(3U, 2U).ref(); // array
2300 CONSTANT(22U, cc == CC_GT ? static_cast<uint64_t>(-1L) : 0U);
2301 BASIC_BLOCK(7U, 3U, 6U)
2302 {
2303 INST(4U, Opcode::SaveState).Inputs(0U, 3U).SrcVregs({0U, 1U});
2304 INST(5U, Opcode::NullCheck).ref().Inputs(3U, 4U);
2305 INST(6U, Opcode::LenArray).s32().Inputs(5U);
2306 INST(30U, Opcode::SaveStateDeoptimize).Inputs(0U).SrcVregs({0U});
2307 INST(20U, Opcode::Compare).CC(ConditionCode::CC_LT).b().Inputs(1U, 22U); // (lower) < -1 (0)
2308 INST(21U, Opcode::DeoptimizeIf).DeoptimizeType(DeoptimizeType::BOUNDS_CHECK).Inputs(20U, 30U);
2309 INST(18U, Opcode::Compare).CC(ConditionCode::CC_LE).b().Inputs(6U, 2U); // len_array <= (upper)
2310 INST(19U, Opcode::DeoptimizeIf).DeoptimizeType(DeoptimizeType::BOUNDS_CHECK).Inputs(18U, 30U);
2311 INST(7U, Opcode::Compare).CC(cc).b().Inputs(2U, 1U); // upper >(>=) lower
2312 INST(8U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(7U);
2313 }
2314 BASIC_BLOCK(3U, 3U, 6U)
2315 {
2316 INST(9U, Opcode::Phi).s32().Inputs(2U, 13U);
2317 INST(10U, Opcode::SaveState).Inputs(0U, 3U).SrcVregs({0U, 1U});
2318 INST(11U, Opcode::NOP);
2319 INST(12U, Opcode::StoreArray).s32().Inputs(3U, 9U, 0U); // a[i] = 0
2320 INST(13U, Opcode::Sub).s32().Inputs(9U, 0U); // i--
2321 INST(14U, Opcode::Compare).CC(cc).b().Inputs(13U, 1U); // i >(>=) lower
2322 INST(15U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(14U);
2323 }
2324 BASIC_BLOCK(6U, 1U)
2325 {
2326 INST(16U, Opcode::Phi).s32().Inputs(0U, 13U);
2327 INST(17U, Opcode::Return).s32().Inputs(16U);
2328 }
2329 }
2330 EXPECT_TRUE(GraphComparator().Compare(graph, graph1));
2331 }
2332 }
2333
BuildGraphUpperOOB()2334 void ChecksEliminationTest::BuildGraphUpperOOB()
2335 {
2336 GRAPH(GetGraph())
2337 {
2338 PARAMETER(0U, 0U).ref(); // array
2339 CONSTANT(3U, 0U);
2340 CONSTANT(25U, 1U);
2341 BASIC_BLOCK(4U, 3U, 2U)
2342 {
2343 INST(2U, Opcode::SaveStateDeoptimize).Inputs(0U).SrcVregs({0U});
2344 INST(27U, Opcode::SaveState).Inputs(3U, 0U).SrcVregs({0U, 3U});
2345 INST(28U, Opcode::NullCheck).ref().Inputs(0U, 27U);
2346 INST(29U, Opcode::LenArray).s32().Inputs(28U);
2347 INST(30U, Opcode::Compare).Inputs(29U, 3U).CC(CC_LT).b();
2348 INST(31U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(30U);
2349 }
2350 BASIC_BLOCK(2U, 3U, 2U)
2351 {
2352 INST(4U, Opcode::Phi).s32().Inputs(3U, 24U);
2353 INST(15U, Opcode::SaveState).Inputs(4U, 0U, 4U).SrcVregs({0U, 3U, 4U});
2354 INST(16U, Opcode::NullCheck).ref().Inputs(0U, 15U);
2355 INST(18U, Opcode::BoundsCheck).s32().Inputs(29U, 4U, 15U);
2356 INST(19U, Opcode::StoreArray).s32().Inputs(16U, 18U, 3U);
2357 INST(24U, Opcode::Add).s32().Inputs(4U, 25U);
2358 INST(10U, Opcode::Compare).b().CC(CC_LT).Inputs(29U, 24U);
2359 INST(11U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(10U);
2360 }
2361 BASIC_BLOCK(3U, -1L)
2362 {
2363 INST(26U, Opcode::ReturnVoid).v0id();
2364 }
2365 }
2366 }
2367
TEST_F(ChecksEliminationTest,UpperOOB)2368 TEST_F(ChecksEliminationTest, UpperOOB)
2369 {
2370 BuildGraphUpperOOB();
2371 ASSERT_TRUE(GetGraph()->RunPass<ChecksElimination>());
2372 auto graph1 = CreateEmptyGraph();
2373 GRAPH(graph1)
2374 {
2375 PARAMETER(0U, 0U).ref(); // array
2376 CONSTANT(3U, 0U);
2377 CONSTANT(25U, 1U);
2378 CONSTANT(35U, 0x7ffffffeU);
2379 BASIC_BLOCK(4U, 3U, 2U)
2380 {
2381 INST(2U, Opcode::SaveStateDeoptimize).Inputs(0U).SrcVregs({0U});
2382 INST(27U, Opcode::SaveState).Inputs(3U, 0U).SrcVregs({0U, 3U});
2383 INST(28U, Opcode::NullCheck).ref().Inputs(0U, 27U);
2384 INST(29U, Opcode::LenArray).s32().Inputs(28U);
2385 INST(33U, Opcode::Compare).Inputs(35U, 29U).CC(CC_LT).b();
2386 INST(34U, Opcode::DeoptimizeIf).DeoptimizeType(DeoptimizeType::BOUNDS_CHECK).Inputs(33U, 27U);
2387 INST(36U, Opcode::Compare).Inputs(29U, 29U).CC(CC_LE).b();
2388 INST(37U, Opcode::DeoptimizeIf).DeoptimizeType(DeoptimizeType::BOUNDS_CHECK).Inputs(36U, 27U);
2389 INST(30U, Opcode::Compare).Inputs(29U, 3U).CC(CC_LT).b();
2390 INST(31U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(30U);
2391 }
2392 BASIC_BLOCK(2U, 3U, 2U)
2393 {
2394 INST(4U, Opcode::Phi).s32().Inputs(3U, 24U);
2395 INST(15U, Opcode::SaveState).Inputs(4U, 0U, 4U).SrcVregs({0U, 3U, 4U});
2396 INST(16U, Opcode::NOP);
2397 INST(18U, Opcode::NOP);
2398 INST(19U, Opcode::StoreArray).s32().Inputs(28U, 4U, 3U);
2399 INST(24U, Opcode::Add).s32().Inputs(4U, 25U);
2400 INST(10U, Opcode::Compare).b().CC(CC_LT).Inputs(29U, 24U);
2401 INST(11U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(10U);
2402 }
2403 BASIC_BLOCK(3U, 1U)
2404 {
2405 INST(26U, Opcode::ReturnVoid).v0id();
2406 }
2407 }
2408 ASSERT_TRUE(GraphComparator().Compare(GetGraph(), graph1));
2409 }
2410
BuildGraphUpperWithDec()2411 void ChecksEliminationTest::BuildGraphUpperWithDec()
2412 {
2413 GRAPH(GetGraph())
2414 {
2415 PARAMETER(0U, 0U).ref(); // array
2416 CONSTANT(3U, 0U); // lower
2417 CONSTANT(25U, 1U); // increment
2418 BASIC_BLOCK(4U, 3U, 2U)
2419 {
2420 INST(2U, Opcode::SaveStateDeoptimize).Inputs(0U).SrcVregs({0U});
2421 INST(27U, Opcode::SaveState).Inputs(0U).SrcVregs({0U});
2422 INST(28U, Opcode::NullCheck).ref().Inputs(0U, 27U);
2423 INST(29U, Opcode::LenArray).s32().Inputs(28U);
2424 INST(30U, Opcode::Sub).s32().Inputs(29U, 25U); // upper = len_array - 1
2425 INST(31U, Opcode::Compare).Inputs(30U, 3U).CC(CC_LE).b(); // lower < upper
2426 INST(32U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(31U);
2427 }
2428 BASIC_BLOCK(2U, 3U, 2U)
2429 {
2430 INST(4U, Opcode::Phi).s32().Inputs(3U, 5U);
2431 INST(5U, Opcode::Add).s32().Inputs(4U, 25U);
2432 INST(15U, Opcode::SaveState).Inputs(0U).SrcVregs({0U});
2433 INST(16U, Opcode::NullCheck).ref().Inputs(0U, 15U);
2434 INST(18U, Opcode::BoundsCheck).s32().Inputs(29U, 5U, 15U);
2435 INST(19U, Opcode::LoadArray).s32().Inputs(16U, 18U);
2436 INST(20U, Opcode::SaveState).Inputs(0U).SrcVregs({0U});
2437 INST(21U, Opcode::NullCheck).ref().Inputs(0U, 20U);
2438 INST(22U, Opcode::BoundsCheck).s32().Inputs(29U, 4U, 20U);
2439 INST(23U, Opcode::StoreArray).s32().Inputs(21U, 22U, 19U); // a[i] = a[i + 1]
2440 INST(10U, Opcode::Compare).b().CC(CC_LE).Inputs(30U, 5U);
2441 INST(11U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(10U);
2442 }
2443 BASIC_BLOCK(3U, -1L)
2444 {
2445 INST(26U, Opcode::ReturnVoid).v0id();
2446 }
2447 }
2448 }
2449
TEST_F(ChecksEliminationTest,UpperWithDec)2450 TEST_F(ChecksEliminationTest, UpperWithDec)
2451 {
2452 BuildGraphUpperWithDec();
2453 ASSERT_TRUE(GetGraph()->RunPass<ChecksElimination>());
2454 auto graph1 = CreateEmptyGraph();
2455 GRAPH(graph1)
2456 {
2457 PARAMETER(0U, 0U).ref(); // array
2458 CONSTANT(3U, 0U); // lower
2459 CONSTANT(25U, 1U); // increment
2460 BASIC_BLOCK(4U, 3U, 2U)
2461 {
2462 INST(2U, Opcode::SaveStateDeoptimize).Inputs(0U).SrcVregs({0U});
2463 INST(27U, Opcode::SaveState).Inputs(0U).SrcVregs({0U});
2464 INST(28U, Opcode::NullCheck).ref().Inputs(0U, 27U);
2465 INST(29U, Opcode::LenArray).s32().Inputs(28U);
2466 INST(30U, Opcode::Sub).s32().Inputs(29U, 25U); // upper = len_array - 1
2467 INST(31U, Opcode::Compare).Inputs(30U, 3U).CC(CC_LE).b(); // lower < upper
2468 INST(32U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(31U);
2469 }
2470 BASIC_BLOCK(2U, 3U, 2U)
2471 {
2472 INST(4U, Opcode::Phi).s32().Inputs(3U, 5U);
2473 INST(5U, Opcode::Add).s32().Inputs(4U, 25U);
2474 INST(15U, Opcode::SaveState).Inputs(0U).SrcVregs({0U});
2475 INST(16U, Opcode::NOP);
2476 INST(18U, Opcode::NOP);
2477 INST(19U, Opcode::LoadArray).s32().Inputs(28U, 5U);
2478 INST(20U, Opcode::SaveState).Inputs(0U).SrcVregs({0U});
2479 INST(21U, Opcode::NOP);
2480 INST(22U, Opcode::NOP);
2481 INST(23U, Opcode::StoreArray).s32().Inputs(28U, 4U, 19U); // a[i] = a[i + 1]
2482 INST(10U, Opcode::Compare).b().CC(CC_LE).Inputs(30U, 5U); // i < upper
2483 INST(11U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(10U);
2484 }
2485 BASIC_BLOCK(3U, -1L)
2486 {
2487 INST(26U, Opcode::ReturnVoid).v0id();
2488 }
2489 }
2490 ASSERT_TRUE(GraphComparator().Compare(GetGraph(), graph1));
2491 }
2492
BuildGraphUnknownUpperWithDec()2493 void ChecksEliminationTest::BuildGraphUnknownUpperWithDec()
2494 {
2495 GRAPH(GetGraph())
2496 {
2497 PARAMETER(0U, 0U).ref(); // array
2498 PARAMETER(1U, 1U).s32(); // X
2499 CONSTANT(3U, 0U); // lower
2500 CONSTANT(6U, 3U);
2501 CONSTANT(25U, 1U); // increment
2502 BASIC_BLOCK(4U, 3U, 2U)
2503 {
2504 INST(2U, Opcode::SaveStateDeoptimize).Inputs(0U).SrcVregs({0U});
2505 INST(27U, Opcode::SaveState).Inputs(0U).SrcVregs({0U});
2506 INST(28U, Opcode::NullCheck).ref().Inputs(0U, 27U);
2507 INST(29U, Opcode::LenArray).s32().Inputs(28U);
2508 INST(30U, Opcode::Sub).s32().Inputs(1U, 6U); // upper = X - 3
2509 INST(31U, Opcode::Compare).Inputs(30U, 3U).CC(CC_LE).b(); // lower < upper
2510 INST(32U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(31U);
2511 }
2512 BASIC_BLOCK(2U, 3U, 2U)
2513 {
2514 INST(4U, Opcode::Phi).s32().Inputs(3U, 5U);
2515 INST(5U, Opcode::Add).s32().Inputs(4U, 25U);
2516 INST(15U, Opcode::SaveState).Inputs(0U).SrcVregs({0U});
2517 INST(16U, Opcode::NullCheck).ref().Inputs(0U, 15U);
2518 INST(18U, Opcode::BoundsCheck).s32().Inputs(29U, 5U, 15U);
2519 INST(19U, Opcode::LoadArray).s32().Inputs(16U, 18U);
2520 INST(20U, Opcode::SaveState).Inputs(0U).SrcVregs({0U});
2521 INST(21U, Opcode::NullCheck).ref().Inputs(0U, 20U);
2522 INST(22U, Opcode::BoundsCheck).s32().Inputs(29U, 4U, 20U);
2523 INST(23U, Opcode::StoreArray).s32().Inputs(21U, 22U, 19U); // a[i] = a[i + 1]
2524 INST(10U, Opcode::Compare).b().CC(CC_LE).Inputs(30U, 5U); // i + 1 < upper
2525 INST(11U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(10U);
2526 }
2527 BASIC_BLOCK(3U, -1L)
2528 {
2529 INST(26U, Opcode::ReturnVoid).v0id();
2530 }
2531 }
2532 }
2533
TEST_F(ChecksEliminationTest,UnknownUpperWithDec)2534 TEST_F(ChecksEliminationTest, UnknownUpperWithDec)
2535 {
2536 BuildGraphUnknownUpperWithDec();
2537 ASSERT_TRUE(GetGraph()->RunPass<ChecksElimination>());
2538 auto graph1 = CreateEmptyGraph();
2539 GRAPH(graph1)
2540 {
2541 PARAMETER(0U, 0U).ref(); // array
2542 PARAMETER(1U, 1U).s32(); // X
2543 CONSTANT(3U, 0U); // lower
2544 CONSTANT(6U, 3U);
2545 CONSTANT(25U, 1U); // increment
2546 BASIC_BLOCK(4U, 3U, 2U)
2547 {
2548 INST(2U, Opcode::SaveStateDeoptimize).Inputs(0U).SrcVregs({0U});
2549 INST(27U, Opcode::SaveState).Inputs(0U).SrcVregs({0U});
2550 INST(37U, Opcode::NullCheck).ref().Inputs(0U, 27U).SetFlag(inst_flags::CAN_DEOPTIMIZE);
2551 INST(35U, Opcode::LenArray).s32().Inputs(37U);
2552 INST(39U, Opcode::NOP);
2553 INST(29U, Opcode::LenArray).s32().Inputs(37U);
2554 INST(30U, Opcode::Sub).s32().Inputs(1U, 6U); // upper = X - 3
2555 INST(41U, Opcode::Sub).s32().Inputs(35U, 25U);
2556 // Deoptimize if len_array - 1 < X - 3
2557 INST(42U, Opcode::Compare).SrcType(DataType::INT32).CC(CC_LT).b().Inputs(41U, 30U);
2558 INST(43U, Opcode::DeoptimizeIf).DeoptimizeType(DeoptimizeType::BOUNDS_CHECK).Inputs(42U, 27U);
2559 INST(31U, Opcode::Compare).Inputs(30U, 3U).CC(CC_LE).b(); // lower < upper
2560 INST(32U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(31U);
2561 }
2562 BASIC_BLOCK(2U, 3U, 2U)
2563 {
2564 INST(4U, Opcode::Phi).s32().Inputs(3U, 5U);
2565 INST(5U, Opcode::Add).s32().Inputs(4U, 25U);
2566 INST(15U, Opcode::SaveState).Inputs(0U).SrcVregs({0U});
2567 INST(16U, Opcode::NOP);
2568 INST(18U, Opcode::NOP);
2569 INST(19U, Opcode::LoadArray).s32().Inputs(37U, 5U);
2570 INST(20U, Opcode::SaveState).Inputs(0U).SrcVregs({0U});
2571 INST(21U, Opcode::NOP);
2572 INST(22U, Opcode::NOP);
2573 INST(23U, Opcode::StoreArray).s32().Inputs(37U, 4U, 19U); // a[i] = a[i + 1]
2574 INST(10U, Opcode::Compare).b().CC(CC_LE).Inputs(30U, 5U); // i + 1 < upper
2575 INST(11U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(10U);
2576 }
2577 BASIC_BLOCK(3U, -1L)
2578 {
2579 INST(26U, Opcode::ReturnVoid).v0id();
2580 }
2581 }
2582 ASSERT_TRUE(GraphComparator().Compare(GetGraph(), graph1));
2583 }
2584
TEST_F(ChecksEliminationTest,LoopWithoutPreHeaderCompare)2585 TEST_F(ChecksEliminationTest, LoopWithoutPreHeaderCompare)
2586 {
2587 // not applied
2588 GRAPH(GetGraph())
2589 {
2590 CONSTANT(0U, 1U); // increment
2591 PARAMETER(1U, 0U).s32(); // lower
2592 PARAMETER(2U, 1U).s32(); // upper
2593 PARAMETER(3U, 2U).ref(); // array
2594 BASIC_BLOCK(7U, 3U)
2595 {
2596 INST(4U, Opcode::SaveState).Inputs(0U, 3U).SrcVregs({0U, 1U});
2597 INST(5U, Opcode::NullCheck).ref().Inputs(3U, 4U);
2598 INST(6U, Opcode::LenArray).s32().Inputs(5U);
2599 INST(30U, Opcode::SaveStateDeoptimize).Inputs(0U).SrcVregs({0U});
2600 }
2601 BASIC_BLOCK(3U, 4U)
2602 {
2603 INST(9U, Opcode::Phi).s32().Inputs(1U, 13U);
2604 INST(10U, Opcode::SaveState).Inputs(0U, 3U).SrcVregs({0U, 1U});
2605 INST(11U, Opcode::BoundsCheck).s32().Inputs(6U, 9U, 10U);
2606 INST(12U, Opcode::StoreArray).s32().Inputs(3U, 11U, 0U); // a[i] = 0
2607 }
2608 BASIC_BLOCK(4U, 3U, 6U)
2609 {
2610 INST(13U, Opcode::Add).s32().Inputs(9U, 0U); // i++
2611 INST(14U, Opcode::Compare).CC(ConditionCode::CC_LT).b().Inputs(13U, 2U); // i < upper
2612 INST(15U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(14U);
2613 }
2614 BASIC_BLOCK(6U, 1U)
2615 {
2616 INST(17U, Opcode::Return).s32().Inputs(13U);
2617 }
2618 }
2619 auto clone = GraphCloner(GetGraph(), GetGraph()->GetAllocator(), GetGraph()->GetLocalAllocator()).CloneGraph();
2620 ASSERT_FALSE(GetGraph()->RunPass<ChecksElimination>());
2621 ASSERT_TRUE(GraphComparator().Compare(GetGraph(), clone));
2622 }
2623
BuildGraphLoopWithUnknowLowerValue()2624 void ChecksEliminationTest::BuildGraphLoopWithUnknowLowerValue()
2625 {
2626 // applied
2627 GRAPH(GetGraph())
2628 {
2629 CONSTANT(0U, 1U); // increment
2630 PARAMETER(1U, 0U).s32(); // lower
2631 PARAMETER(3U, 2U).ref(); // array
2632 BASIC_BLOCK(7U, 3U, 6U)
2633 {
2634 INST(4U, Opcode::SaveState).Inputs(0U, 3U).SrcVregs({0U, 1U});
2635 INST(5U, Opcode::NullCheck).ref().Inputs(3U, 4U);
2636 INST(6U, Opcode::LenArray).s32().Inputs(5U);
2637 INST(30U, Opcode::SaveStateDeoptimize).Inputs(0U).SrcVregs({0U});
2638 INST(7U, Opcode::Compare).CC(ConditionCode::CC_LT).b().Inputs(1U, 6U); // lower < len_array
2639 INST(8U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(7U);
2640 }
2641 BASIC_BLOCK(3U, 3U, 6U)
2642 {
2643 INST(9U, Opcode::Phi).s32().Inputs(1U, 13U);
2644 INST(10U, Opcode::SaveState).Inputs(0U, 3U).SrcVregs({0U, 1U});
2645 INST(11U, Opcode::BoundsCheck).s32().Inputs(6U, 9U, 10U);
2646 INST(12U, Opcode::StoreArray).s32().Inputs(3U, 11U, 0U); // a[i] = 0
2647 INST(13U, Opcode::Add).s32().Inputs(9U, 0U); // i++
2648 INST(14U, Opcode::Compare).CC(ConditionCode::CC_LT).b().Inputs(13U, 6U); // i < len_array
2649 INST(15U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(14U);
2650 }
2651 BASIC_BLOCK(6U, 1U)
2652 {
2653 INST(16U, Opcode::Phi).s32().Inputs(0U, 13U);
2654 INST(17U, Opcode::Return).s32().Inputs(16U);
2655 }
2656 }
2657 }
2658
TEST_F(ChecksEliminationTest,LoopWithUnknowLowerValue)2659 TEST_F(ChecksEliminationTest, LoopWithUnknowLowerValue)
2660 {
2661 BuildGraphLoopWithUnknowLowerValue();
2662 ASSERT_TRUE(GetGraph()->RunPass<ChecksElimination>());
2663 auto graph1 = CreateEmptyGraph();
2664 GRAPH(graph1)
2665 {
2666 CONSTANT(0U, 1U); // increment
2667 PARAMETER(1U, 0U).s32(); // lower
2668 PARAMETER(3U, 2U).ref(); // array
2669 CONSTANT(22U, 0U);
2670 BASIC_BLOCK(7U, 3U, 6U)
2671 {
2672 INST(4U, Opcode::SaveState).Inputs(0U, 3U).SrcVregs({0U, 1U});
2673 INST(5U, Opcode::NullCheck).ref().Inputs(3U, 4U);
2674 INST(6U, Opcode::LenArray).s32().Inputs(5U);
2675 INST(30U, Opcode::SaveStateDeoptimize).Inputs(0U).SrcVregs({0U});
2676 INST(20U, Opcode::Compare).CC(ConditionCode::CC_LT).b().Inputs(1U, 22U); // lower < 0
2677 INST(21U, Opcode::DeoptimizeIf).DeoptimizeType(DeoptimizeType::BOUNDS_CHECK).Inputs(20U, 30U);
2678 INST(7U, Opcode::Compare).CC(ConditionCode::CC_LT).b().Inputs(1U, 6U); // lower < len_aray
2679 INST(8U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(7U);
2680 }
2681 BASIC_BLOCK(3U, 3U, 6U)
2682 {
2683 INST(9U, Opcode::Phi).s32().Inputs(1U, 13U);
2684 INST(10U, Opcode::SaveState).Inputs(0U, 3U).SrcVregs({0U, 1U});
2685 INST(11U, Opcode::NOP);
2686 INST(12U, Opcode::StoreArray).s32().Inputs(3U, 9U, 0U); // a[i] = 0
2687 INST(13U, Opcode::Add).s32().Inputs(9U, 0U); // i++
2688 INST(14U, Opcode::Compare).CC(ConditionCode::CC_LT).b().Inputs(13U, 6U); // i < upper
2689 INST(15U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(14U);
2690 }
2691 BASIC_BLOCK(6U, 1U)
2692 {
2693 INST(16U, Opcode::Phi).s32().Inputs(0U, 13U);
2694 INST(17U, Opcode::Return).s32().Inputs(16U);
2695 }
2696 }
2697 ASSERT_TRUE(GraphComparator().Compare(GetGraph(), graph1));
2698 }
2699
BuildGraphLoopWithUnknowUpperValueLE()2700 void ChecksEliminationTest::BuildGraphLoopWithUnknowUpperValueLE()
2701 {
2702 // applied
2703 GRAPH(GetGraph())
2704 {
2705 CONSTANT(0U, 0U); // initial
2706 CONSTANT(1U, 1U); // increment
2707 PARAMETER(2U, 0U).s32(); // X
2708 PARAMETER(3U, 1U).ref(); // array
2709 BASIC_BLOCK(7U, 3U, 6U)
2710 {
2711 INST(4U, Opcode::SaveState).Inputs(0U, 1U, 2U, 3U).SrcVregs({0U, 1U, 2U, 3U});
2712 INST(5U, Opcode::NullCheck).ref().Inputs(3U, 4U);
2713 INST(6U, Opcode::LenArray).s32().Inputs(5U);
2714 INST(30U, Opcode::SaveStateDeoptimize).Inputs(0U).SrcVregs({0U});
2715 INST(7U, Opcode::Compare).CC(ConditionCode::CC_LE).b().Inputs(0U, 2U); // i <= X
2716 INST(8U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(7U);
2717 }
2718 BASIC_BLOCK(3U, 3U, 6U)
2719 {
2720 INST(9U, Opcode::Phi).s32().Inputs(0U, 13U);
2721 INST(10U, Opcode::SaveState).Inputs(0U, 1U, 2U, 3U).SrcVregs({0U, 1U, 2U, 3U});
2722 INST(11U, Opcode::BoundsCheck).s32().Inputs(6U, 9U, 10U);
2723 INST(12U, Opcode::StoreArray).s32().Inputs(3U, 11U, 0U); // a[i] = 0
2724 INST(13U, Opcode::Add).s32().Inputs(9U, 1U); // i++
2725 INST(14U, Opcode::Compare).CC(ConditionCode::CC_LE).b().Inputs(13U, 2U); // i <= X
2726 INST(15U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(14U);
2727 }
2728 BASIC_BLOCK(6U, 1U)
2729 {
2730 INST(16U, Opcode::Phi).s32().Inputs(0U, 13U);
2731 INST(17U, Opcode::Return).s32().Inputs(16U);
2732 }
2733 }
2734 ASSERT_TRUE(GetGraph()->RunPass<ChecksElimination>());
2735 }
2736
TEST_F(ChecksEliminationTest,LoopWithUnknowUpperValueLE)2737 TEST_F(ChecksEliminationTest, LoopWithUnknowUpperValueLE)
2738 {
2739 BuildGraphLoopWithUnknowUpperValueLE();
2740 auto graph1 = CreateEmptyGraph();
2741 GRAPH(graph1)
2742 {
2743 CONSTANT(0U, 0U); // initial
2744 CONSTANT(1U, 1U); // increment
2745 PARAMETER(2U, 0U).s32(); // X
2746 PARAMETER(3U, 1U).ref(); // array
2747 CONSTANT(31U, 0x7ffffffeU); // max
2748 BASIC_BLOCK(7U, 3U, 6U)
2749 {
2750 INST(4U, Opcode::SaveState).Inputs(0U, 1U, 2U, 3U).SrcVregs({0U, 1U, 2U, 3U});
2751 INST(5U, Opcode::NullCheck).ref().Inputs(3U, 4U);
2752 INST(6U, Opcode::LenArray).s32().Inputs(5U);
2753 INST(30U, Opcode::SaveStateDeoptimize).Inputs(0U).SrcVregs({0U});
2754 INST(18U, Opcode::Compare).CC(ConditionCode::CC_LT).b().Inputs(31U, 2U); // INT_MAX - 1 < X - infinite loop
2755 INST(19U, Opcode::DeoptimizeIf).DeoptimizeType(DeoptimizeType::BOUNDS_CHECK).Inputs(18U, 30U);
2756 INST(32U, Opcode::Compare).CC(ConditionCode::CC_LE).b().Inputs(6U, 2U); // len_array < X
2757 INST(33U, Opcode::DeoptimizeIf).DeoptimizeType(DeoptimizeType::BOUNDS_CHECK).Inputs(32U, 30U);
2758 INST(7U, Opcode::Compare).CC(ConditionCode::CC_LE).b().Inputs(0U, 2U); // i <= X
2759 INST(8U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(7U);
2760 }
2761 BASIC_BLOCK(3U, 3U, 6U)
2762 {
2763 INST(9U, Opcode::Phi).s32().Inputs(0U, 13U);
2764 INST(10U, Opcode::SaveState).Inputs(0U, 1U, 2U, 3U).SrcVregs({0U, 1U, 2U, 3U});
2765 INST(11U, Opcode::NOP);
2766 INST(12U, Opcode::StoreArray).s32().Inputs(3U, 9U, 0U); // a[i] = 0
2767 INST(13U, Opcode::Add).s32().Inputs(9U, 1U); // i++
2768 INST(14U, Opcode::Compare).CC(ConditionCode::CC_LE).b().Inputs(13U, 2U); // i <= X
2769 INST(15U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(14U);
2770 }
2771 BASIC_BLOCK(6U, 1U)
2772 {
2773 INST(16U, Opcode::Phi).s32().Inputs(0U, 13U);
2774 INST(17U, Opcode::Return).s32().Inputs(16U);
2775 }
2776 }
2777 ASSERT_TRUE(GraphComparator().Compare(GetGraph(), graph1));
2778 }
2779
BuildGraphLoopWithUnknowUpperValueLT()2780 void ChecksEliminationTest::BuildGraphLoopWithUnknowUpperValueLT()
2781 {
2782 // applied
2783 GRAPH(GetGraph())
2784 {
2785 CONSTANT(0U, 0U); // initial
2786 CONSTANT(1U, 1U); // increment
2787 PARAMETER(2U, 0U).s32(); // X
2788 PARAMETER(3U, 1U).ref(); // array
2789 BASIC_BLOCK(7U, 3U, 6U)
2790 {
2791 INST(4U, Opcode::SaveState).Inputs(0U, 1U, 2U, 3U).SrcVregs({0U, 1U, 2U, 3U});
2792 INST(5U, Opcode::NullCheck).ref().Inputs(3U, 4U);
2793 INST(6U, Opcode::LenArray).s32().Inputs(5U);
2794 INST(30U, Opcode::SaveStateDeoptimize).Inputs(0U).SrcVregs({0U});
2795 INST(7U, Opcode::Compare).CC(ConditionCode::CC_LT).b().Inputs(0U, 2U); // i < X
2796 INST(8U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(7U);
2797 }
2798 BASIC_BLOCK(3U, 3U, 6U)
2799 {
2800 INST(9U, Opcode::Phi).s32().Inputs(0U, 13U);
2801 INST(10U, Opcode::SaveState).Inputs(0U, 1U, 2U, 3U).SrcVregs({0U, 1U, 2U, 3U});
2802 INST(11U, Opcode::BoundsCheck).s32().Inputs(6U, 9U, 10U);
2803 INST(12U, Opcode::StoreArray).s32().Inputs(3U, 11U, 0U); // a[i] = 0
2804 INST(13U, Opcode::Add).s32().Inputs(9U, 1U); // i++
2805 INST(14U, Opcode::Compare).CC(ConditionCode::CC_LT).b().Inputs(13U, 2U); // i < X
2806 INST(15U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(14U);
2807 }
2808 BASIC_BLOCK(6U, 1U)
2809 {
2810 INST(16U, Opcode::Phi).s32().Inputs(0U, 13U);
2811 INST(17U, Opcode::Return).s32().Inputs(16U);
2812 }
2813 }
2814 }
2815
TEST_F(ChecksEliminationTest,LoopWithUnknowUpperValueLT)2816 TEST_F(ChecksEliminationTest, LoopWithUnknowUpperValueLT)
2817 {
2818 BuildGraphLoopWithUnknowUpperValueLT();
2819 ASSERT_TRUE(GetGraph()->RunPass<ChecksElimination>());
2820
2821 auto graph1 = CreateEmptyGraph();
2822 GRAPH(graph1)
2823 {
2824 CONSTANT(0U, 0U); // initial
2825 CONSTANT(1U, 1U); // increment
2826 PARAMETER(2U, 0U).s32(); // X
2827 PARAMETER(3U, 1U).ref(); // array
2828 BASIC_BLOCK(7U, 3U, 6U)
2829 {
2830 INST(4U, Opcode::SaveState).Inputs(0U, 1U, 2U, 3U).SrcVregs({0U, 1U, 2U, 3U});
2831 INST(5U, Opcode::NullCheck).ref().Inputs(3U, 4U);
2832 INST(6U, Opcode::LenArray).s32().Inputs(5U);
2833 INST(30U, Opcode::SaveStateDeoptimize).Inputs(0U).SrcVregs({0U});
2834 INST(18U, Opcode::Compare).CC(ConditionCode::CC_LT).b().Inputs(6U, 2U);
2835 INST(19U, Opcode::DeoptimizeIf).DeoptimizeType(DeoptimizeType::BOUNDS_CHECK).Inputs(18U, 30U);
2836 INST(7U, Opcode::Compare).CC(ConditionCode::CC_LT).b().Inputs(0U, 2U); // i < X
2837 INST(8U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(7U);
2838 }
2839 BASIC_BLOCK(3U, 3U, 6U)
2840 {
2841 INST(9U, Opcode::Phi).s32().Inputs(0U, 13U);
2842 INST(10U, Opcode::SaveState).Inputs(0U, 1U, 2U, 3U).SrcVregs({0U, 1U, 2U, 3U});
2843 INST(11U, Opcode::NOP);
2844 INST(12U, Opcode::StoreArray).s32().Inputs(3U, 9U, 0U); // a[i] = 0
2845 INST(13U, Opcode::Add).s32().Inputs(9U, 1U); // i++
2846 INST(14U, Opcode::Compare).CC(ConditionCode::CC_LT).b().Inputs(13U, 2U); // i < X
2847 INST(15U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(14U);
2848 }
2849 BASIC_BLOCK(6U, 1U)
2850 {
2851 INST(16U, Opcode::Phi).s32().Inputs(0U, 13U);
2852 INST(17U, Opcode::Return).s32().Inputs(16U);
2853 }
2854 }
2855 ASSERT_TRUE(GraphComparator().Compare(GetGraph(), graph1));
2856 }
2857
BuildGraphLoopSeveralBoundsChecks()2858 void ChecksEliminationTest::BuildGraphLoopSeveralBoundsChecks()
2859 {
2860 // applied
2861 // array coping
2862 GRAPH(GetGraph())
2863 {
2864 CONSTANT(0U, 0U); // initial
2865 CONSTANT(1U, 1U); // increment
2866 PARAMETER(2U, 0U).ref(); // array1
2867 PARAMETER(3U, 1U).ref(); // array2
2868 PARAMETER(4U, 2U).s32(); // X
2869
2870 BASIC_BLOCK(7U, 3U, 6U)
2871 {
2872 // check array 1
2873 INST(5U, Opcode::SaveState).Inputs(0U, 1U, 2U, 3U).SrcVregs({0U, 1U, 2U, 3U});
2874 INST(6U, Opcode::NullCheck).ref().Inputs(2U, 5U);
2875 INST(7U, Opcode::LenArray).s32().Inputs(6U);
2876 // check array 2
2877 INST(8U, Opcode::SaveState).Inputs(0U, 1U, 2U, 3U).SrcVregs({0U, 1U, 2U, 3U});
2878 INST(9U, Opcode::NullCheck).ref().Inputs(3U, 8U);
2879 INST(10U, Opcode::LenArray).s32().Inputs(9U);
2880
2881 INST(30U, Opcode::SaveStateDeoptimize).Inputs(0U).SrcVregs({0U});
2882
2883 INST(11U, Opcode::Compare).CC(ConditionCode::CC_LT).b().Inputs(0U, 4U); // 0 < X
2884 INST(12U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(11U);
2885 }
2886 BASIC_BLOCK(3U, 3U, 6U)
2887 {
2888 INST(13U, Opcode::Phi).s32().Inputs(0U, 20U); // i
2889 INST(14U, Opcode::SaveState).Inputs(0U, 1U, 2U, 3U).SrcVregs({0U, 1U, 2U, 3U});
2890 INST(15U, Opcode::BoundsCheck).s32().Inputs(7U, 13U, 14U);
2891 INST(16U, Opcode::LoadArray).s32().Inputs(2U, 15U);
2892 INST(17U, Opcode::SaveState).Inputs(0U, 1U, 2U, 3U).SrcVregs({0U, 1U, 2U, 3U});
2893 INST(18U, Opcode::BoundsCheck).s32().Inputs(10U, 13U, 17U);
2894 INST(19U, Opcode::StoreArray).s32().Inputs(3U, 18U, 16U); // array2[i] = array1[i]
2895 INST(20U, Opcode::Add).s32().Inputs(13U, 1U); // i++
2896 INST(21U, Opcode::Compare).CC(ConditionCode::CC_LT).b().Inputs(20U, 4U); // i < X
2897 INST(22U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(21U);
2898 }
2899 BASIC_BLOCK(6U, 1U)
2900 {
2901 INST(23U, Opcode::Phi).s32().Inputs(0U, 20U);
2902 INST(24U, Opcode::Return).s32().Inputs(23U);
2903 }
2904 }
2905 }
2906
TEST_F(ChecksEliminationTest,LoopSeveralBoundsChecks)2907 TEST_F(ChecksEliminationTest, LoopSeveralBoundsChecks)
2908 {
2909 BuildGraphLoopSeveralBoundsChecks();
2910 ASSERT_TRUE(GetGraph()->RunPass<ChecksElimination>());
2911 auto graph1 = CreateEmptyGraph();
2912 GRAPH(graph1)
2913 {
2914 CONSTANT(0U, 0U); // initial
2915 CONSTANT(1U, 1U); // increment
2916 PARAMETER(2U, 0U).ref(); // array1
2917 PARAMETER(3U, 1U).ref(); // array2
2918 PARAMETER(4U, 2U).s32(); // X
2919
2920 BASIC_BLOCK(7U, 3U, 6U)
2921 {
2922 // check array 1
2923 INST(5U, Opcode::SaveState).Inputs(0U, 1U, 2U, 3U).SrcVregs({0U, 1U, 2U, 3U});
2924 INST(6U, Opcode::NullCheck).ref().Inputs(2U, 5U);
2925 INST(7U, Opcode::LenArray).s32().Inputs(6U);
2926 // check array 2
2927 INST(8U, Opcode::SaveState).Inputs(0U, 1U, 2U, 3U).SrcVregs({0U, 1U, 2U, 3U});
2928 INST(9U, Opcode::NullCheck).ref().Inputs(3U, 8U);
2929 INST(10U, Opcode::LenArray).s32().Inputs(9U);
2930
2931 INST(30U, Opcode::SaveStateDeoptimize).Inputs(0U).SrcVregs({0U});
2932 INST(27U, Opcode::Compare).CC(ConditionCode::CC_LT).b().Inputs(7U, 4U); // len_array1 < X
2933 INST(28U, Opcode::DeoptimizeIf).DeoptimizeType(DeoptimizeType::BOUNDS_CHECK).Inputs(27U, 30U);
2934 INST(25U, Opcode::Compare).CC(ConditionCode::CC_LT).b().Inputs(10U, 4U); // len_array2 < X
2935 INST(26U, Opcode::DeoptimizeIf).DeoptimizeType(DeoptimizeType::BOUNDS_CHECK).Inputs(25U, 30U);
2936
2937 INST(11U, Opcode::Compare).CC(ConditionCode::CC_LT).b().Inputs(0U, 4U); // 0 < X
2938 INST(12U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(11U);
2939 }
2940 BASIC_BLOCK(3U, 3U, 6U)
2941 {
2942 INST(13U, Opcode::Phi).s32().Inputs(0U, 20U); // i
2943 INST(14U, Opcode::SaveState).Inputs(0U, 1U, 2U, 3U).SrcVregs({0U, 1U, 2U, 3U});
2944 INST(15U, Opcode::NOP);
2945 INST(16U, Opcode::LoadArray).s32().Inputs(2U, 13U);
2946 INST(17U, Opcode::SaveState).Inputs(0U, 1U, 2U, 3U).SrcVregs({0U, 1U, 2U, 3U});
2947 INST(18U, Opcode::NOP);
2948 INST(19U, Opcode::StoreArray).s32().Inputs(3U, 13U, 16U); // array2[i] = array1[i]
2949 INST(20U, Opcode::Add).s32().Inputs(13U, 1U); // i++
2950 INST(21U, Opcode::Compare).CC(ConditionCode::CC_LT).b().Inputs(20U, 4U); // i < X
2951 INST(22U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(21U);
2952 }
2953 BASIC_BLOCK(6U, 1U)
2954 {
2955 INST(23U, Opcode::Phi).s32().Inputs(0U, 20U);
2956 INST(24U, Opcode::Return).s32().Inputs(23U);
2957 }
2958 }
2959 ASSERT_TRUE(GraphComparator().Compare(GetGraph(), graph1));
2960 }
2961
BuildGraphLoopSeveralIndexesBoundsChecks()2962 void ChecksEliminationTest::BuildGraphLoopSeveralIndexesBoundsChecks()
2963 {
2964 // applied
2965 // array coping
2966 GRAPH(GetGraph())
2967 {
2968 CONSTANT(0U, 0U); // initial
2969 CONSTANT(1U, 1U); // increment
2970 PARAMETER(2U, 0U).ref(); // array
2971 PARAMETER(3U, 1U).s32(); // Y
2972 PARAMETER(4U, 2U).s32(); // X
2973
2974 BASIC_BLOCK(7U, 3U, 6U)
2975 {
2976 // check array
2977 INST(5U, Opcode::SaveState).Inputs(0U, 1U, 2U).SrcVregs({0U, 1U, 2U});
2978 INST(6U, Opcode::NullCheck).ref().Inputs(2U, 5U);
2979 INST(7U, Opcode::LenArray).s32().Inputs(6U);
2980
2981 INST(36U, Opcode::SaveStateDeoptimize).Inputs(0U).SrcVregs({0U});
2982
2983 INST(11U, Opcode::Compare).CC(ConditionCode::CC_LT).b().Inputs(3U, 4U); // Y < X
2984 INST(12U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(11U);
2985 }
2986 BASIC_BLOCK(3U, 3U, 6U)
2987 {
2988 INST(13U, Opcode::Phi).s32().Inputs(3U, 20U); // i
2989
2990 INST(14U, Opcode::SaveState).Inputs(0U, 1U, 2U).SrcVregs({0U, 1U, 2U});
2991 INST(15U, Opcode::BoundsCheck).s32().Inputs(7U, 13U, 14U);
2992 INST(16U, Opcode::LoadArray).s32().Inputs(2U, 15U); // array[i]
2993
2994 INST(26U, Opcode::Sub).s32().Inputs(13U, 1U); // i - 1
2995 INST(27U, Opcode::SaveState).Inputs(0U, 1U, 2U).SrcVregs({0U, 1U, 2U});
2996 INST(28U, Opcode::BoundsCheck).s32().Inputs(7U, 26U, 27U);
2997 INST(29U, Opcode::LoadArray).s32().Inputs(2U, 28U); // array[i-1]
2998
2999 INST(30U, Opcode::Add).s32().Inputs(13U, 1U); // i + 1
3000 INST(31U, Opcode::SaveState).Inputs(0U, 1U, 2U).SrcVregs({0U, 1U, 2U});
3001 INST(32U, Opcode::BoundsCheck).s32().Inputs(7U, 30U, 31U);
3002 INST(33U, Opcode::LoadArray).s32().Inputs(2U, 32U); // array[i+1]
3003
3004 INST(34U, Opcode::Add).s32().Inputs(16U, 29U); // array[i-1] + array[i]
3005 INST(35U, Opcode::Add).s32().Inputs(34U, 33U); // array[i-1] + array[i] + array[i+1]
3006
3007 INST(17U, Opcode::SaveState).Inputs(0U, 1U, 2U).SrcVregs({0U, 1U, 2U});
3008 INST(18U, Opcode::BoundsCheck).s32().Inputs(7U, 13U, 17U);
3009 INST(19U, Opcode::StoreArray).s32().Inputs(2U, 18U, 35U); // array[i] = array[i-1] + array[i] + array[i+1]
3010
3011 INST(20U, Opcode::Add).s32().Inputs(13U, 1U); // i++
3012 INST(21U, Opcode::Compare).CC(ConditionCode::CC_LT).b().Inputs(20U, 4U); // i < X
3013 INST(22U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(21U);
3014 }
3015 BASIC_BLOCK(6U, 1U)
3016 {
3017 INST(23U, Opcode::Phi).s32().Inputs(0U, 20U);
3018 INST(24U, Opcode::Return).s32().Inputs(23U);
3019 }
3020 }
3021 }
3022
3023 // CC-OFFNXT(huge_method, G.FUN.01) graph creation
TEST_F(ChecksEliminationTest,LoopSeveralIndexesBoundsChecks)3024 TEST_F(ChecksEliminationTest, LoopSeveralIndexesBoundsChecks)
3025 {
3026 BuildGraphLoopSeveralIndexesBoundsChecks();
3027 ASSERT_TRUE(GetGraph()->RunPass<ChecksElimination>());
3028 auto graph1 = CreateEmptyGraph();
3029 GRAPH(graph1)
3030 {
3031 CONSTANT(0U, 0U); // initial
3032 CONSTANT(1U, 1U); // increment
3033 PARAMETER(2U, 0U).ref(); // array
3034 PARAMETER(3U, 1U).s32(); // Y
3035 PARAMETER(4U, 2U).s32(); // X
3036 CONSTANT(43U, -1L);
3037 BASIC_BLOCK(7U, 3U, 6U)
3038 {
3039 // check array
3040 INST(5U, Opcode::SaveState).Inputs(0U, 1U, 2U).SrcVregs({0U, 1U, 2U});
3041 INST(6U, Opcode::NullCheck).ref().Inputs(2U, 5U);
3042 INST(7U, Opcode::LenArray).s32().Inputs(6U);
3043
3044 INST(36U, Opcode::SaveStateDeoptimize).Inputs(0U).SrcVregs({0U});
3045 INST(37U, Opcode::Add).s32().Inputs(3U, 43U);
3046 INST(38U, Opcode::Compare).CC(ConditionCode::CC_LT).b().Inputs(37U, 0U); // Y-1 < 0
3047 INST(39U, Opcode::DeoptimizeIf).DeoptimizeType(DeoptimizeType::BOUNDS_CHECK).Inputs(38U, 36U);
3048
3049 INST(40U, Opcode::Sub).s32().Inputs(7U, 1U);
3050 INST(41U, Opcode::Compare).CC(ConditionCode::CC_LT).b().Inputs(40U, 4U); // len_array - 1 < X
3051 INST(42U, Opcode::DeoptimizeIf).DeoptimizeType(DeoptimizeType::BOUNDS_CHECK).Inputs(41U, 36U);
3052
3053 INST(11U, Opcode::Compare).CC(ConditionCode::CC_LT).b().Inputs(3U, 4U); // Y < X
3054 INST(12U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(11U);
3055 }
3056 BASIC_BLOCK(3U, 3U, 6U)
3057 {
3058 INST(13U, Opcode::Phi).s32().Inputs(3U, 20U); // i
3059
3060 INST(14U, Opcode::SaveState).Inputs(0U, 1U, 2U).SrcVregs({0U, 1U, 2U});
3061 INST(15U, Opcode::NOP);
3062 INST(16U, Opcode::LoadArray).s32().Inputs(2U, 13U); // array[i]
3063
3064 INST(26U, Opcode::Sub).s32().Inputs(13U, 1U); // i - 1
3065 INST(27U, Opcode::SaveState).Inputs(0U, 1U, 2U).SrcVregs({0U, 1U, 2U});
3066 INST(28U, Opcode::NOP);
3067 INST(29U, Opcode::LoadArray).s32().Inputs(2U, 26U); // array[i-1]
3068
3069 INST(30U, Opcode::Add).s32().Inputs(13U, 1U); // i + 1
3070 INST(31U, Opcode::SaveState).Inputs(0U, 1U, 2U).SrcVregs({0U, 1U, 2U});
3071 INST(32U, Opcode::NOP);
3072 INST(33U, Opcode::LoadArray).s32().Inputs(2U, 30U); // array[i+1]
3073
3074 INST(34U, Opcode::Add).s32().Inputs(16U, 29U); // array[i-1] + array[i]
3075 INST(35U, Opcode::Add).s32().Inputs(34U, 33U); // array[i-1] + array[i] + array[i+1]
3076
3077 INST(17U, Opcode::SaveState).Inputs(0U, 1U, 2U).SrcVregs({0U, 1U, 2U});
3078 INST(18U, Opcode::NOP);
3079 INST(19U, Opcode::StoreArray).s32().Inputs(2U, 13U, 35U); // array[i] = array[i-1] + array[i] + array[i+1]
3080
3081 INST(20U, Opcode::Add).s32().Inputs(13U, 1U); // i++
3082 INST(21U, Opcode::Compare).CC(ConditionCode::CC_LT).b().Inputs(20U, 4U); // i < X
3083 INST(22U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(21U);
3084 }
3085 BASIC_BLOCK(6U, 1U)
3086 {
3087 INST(23U, Opcode::Phi).s32().Inputs(0U, 20U);
3088 INST(24U, Opcode::Return).s32().Inputs(23U);
3089 }
3090 }
3091 ASSERT_TRUE(GraphComparator().Compare(GetGraph(), graph1));
3092 }
3093
BuildGraphHeadExitLoop()3094 void ChecksEliminationTest::BuildGraphHeadExitLoop()
3095 {
3096 GRAPH(GetGraph())
3097 {
3098 CONSTANT(0U, 0U); // initial
3099 CONSTANT(1U, 1U); // increment
3100 PARAMETER(2U, 0U).ref();
3101 PARAMETER(3U, 1U).s32(); // X
3102 BASIC_BLOCK(2U, 3U)
3103 {
3104 INST(4U, Opcode::SaveState).Inputs(0U, 1U, 2U).SrcVregs({0U, 1U, 2U});
3105 INST(5U, Opcode::NullCheck).ref().Inputs(2U, 4U);
3106 INST(6U, Opcode::LenArray).s32().Inputs(5U);
3107 INST(30U, Opcode::SaveStateDeoptimize).Inputs(0U).SrcVregs({0U});
3108 }
3109 BASIC_BLOCK(3U, 4U, 5U)
3110 {
3111 INST(7U, Opcode::Phi).s32().Inputs(0U, 13U);
3112 INST(8U, Opcode::Compare).CC(CC_LT).b().Inputs(7U, 3U); // i < X
3113 INST(9U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(8U);
3114 }
3115 BASIC_BLOCK(4U, 3U)
3116 {
3117 INST(10U, Opcode::SaveState).Inputs(0U, 1U, 2U).SrcVregs({0U, 1U, 2U});
3118 INST(11U, Opcode::BoundsCheck).s32().Inputs(6U, 7U, 10U);
3119 INST(12U, Opcode::StoreArray).s32().Inputs(5U, 11U, 0U); // a[i] = 0
3120 INST(13U, Opcode::Add).s32().Inputs(7U, 1U); // i++
3121 }
3122 BASIC_BLOCK(5U, 1U)
3123 {
3124 INST(14U, Opcode::Return).ref().Inputs(5U);
3125 }
3126 }
3127 }
3128
TEST_F(ChecksEliminationTest,HeadExitLoop)3129 TEST_F(ChecksEliminationTest, HeadExitLoop)
3130 {
3131 BuildGraphHeadExitLoop();
3132 ASSERT_TRUE(GetGraph()->RunPass<ChecksElimination>());
3133 auto graph = CreateEmptyGraph();
3134 GRAPH(graph)
3135 {
3136 CONSTANT(0U, 0U); // initial
3137 CONSTANT(1U, 1U); // increment
3138 PARAMETER(2U, 0U).ref();
3139 PARAMETER(3U, 1U).s32();
3140 BASIC_BLOCK(2U, 3U)
3141 {
3142 INST(4U, Opcode::SaveState).Inputs(0U, 1U, 2U).SrcVregs({0U, 1U, 2U});
3143 INST(5U, Opcode::NullCheck).ref().Inputs(2U, 4U);
3144 INST(6U, Opcode::LenArray).s32().Inputs(5U);
3145 INST(30U, Opcode::SaveStateDeoptimize).Inputs(0U).SrcVregs({0U});
3146 INST(15U, Opcode::Compare).b().CC(ConditionCode::CC_LT).Inputs(6U, 3U); // len_array < X
3147 INST(16U, Opcode::DeoptimizeIf).DeoptimizeType(DeoptimizeType::BOUNDS_CHECK).Inputs(15U, 30U);
3148 }
3149 BASIC_BLOCK(3U, 4U, 5U)
3150 {
3151 INST(7U, Opcode::Phi).s32().Inputs(0U, 13U);
3152 INST(8U, Opcode::Compare).CC(CC_LT).b().Inputs(7U, 3U); // i < X
3153 INST(9U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(8U);
3154 }
3155 BASIC_BLOCK(4U, 3U)
3156 {
3157 INST(10U, Opcode::SaveState).Inputs(0U, 1U, 2U).SrcVregs({0U, 1U, 2U});
3158 INST(11U, Opcode::NOP);
3159 INST(12U, Opcode::StoreArray).s32().Inputs(5U, 7U, 0U); // a[i] = 0
3160 INST(13U, Opcode::Add).s32().Inputs(7U, 1U); // i++
3161 }
3162 BASIC_BLOCK(5U, 1U)
3163 {
3164 INST(14U, Opcode::Return).ref().Inputs(5U);
3165 }
3166 }
3167 ASSERT_TRUE(GraphComparator().Compare(GetGraph(), graph));
3168 }
3169
TEST_F(ChecksEliminationTest,BoundsCheckInHeader)3170 TEST_F(ChecksEliminationTest, BoundsCheckInHeader)
3171 {
3172 GRAPH(GetGraph())
3173 {
3174 CONSTANT(0U, 0U); // initial
3175 CONSTANT(1U, 1U); // increment
3176 PARAMETER(2U, 0U).ref();
3177 PARAMETER(3U, 1U).s32();
3178 BASIC_BLOCK(2U, 3U)
3179 {
3180 INST(4U, Opcode::SaveState).Inputs(0U, 1U, 2U).SrcVregs({0U, 1U, 2U});
3181 INST(5U, Opcode::NullCheck).ref().Inputs(2U, 4U);
3182 INST(6U, Opcode::LenArray).s32().Inputs(5U);
3183 INST(30U, Opcode::SaveStateDeoptimize).Inputs(0U).SrcVregs({0U});
3184 }
3185 BASIC_BLOCK(3U, 4U, 5U)
3186 {
3187 INST(7U, Opcode::Phi).s32().Inputs(0U, 13U);
3188 INST(10U, Opcode::SaveState).Inputs(0U, 1U, 2U).SrcVregs({0U, 1U, 2U});
3189 INST(11U, Opcode::BoundsCheck).s32().Inputs(6U, 7U, 10U);
3190 INST(12U, Opcode::StoreArray).s32().Inputs(5U, 11U, 0U); // a[i] = 0
3191 INST(8U, Opcode::Compare).CC(CC_LT).b().Inputs(7U, 3U); // i < X
3192 INST(9U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(8U);
3193 }
3194 BASIC_BLOCK(4U, 3U)
3195 {
3196 INST(13U, Opcode::Add).s32().Inputs(7U, 1U); // i++
3197 }
3198 BASIC_BLOCK(5U, 1U)
3199 {
3200 INST(14U, Opcode::Return).ref().Inputs(5U);
3201 }
3202 }
3203 auto clone = GraphCloner(GetGraph(), GetGraph()->GetAllocator(), GetGraph()->GetLocalAllocator()).CloneGraph();
3204 ASSERT_FALSE(GetGraph()->RunPass<ChecksElimination>());
3205 ASSERT_TRUE(GraphComparator().Compare(GetGraph(), clone));
3206 }
3207
BuildGraphLoopTest1()3208 void ChecksEliminationTest::BuildGraphLoopTest1()
3209 {
3210 GRAPH(GetGraph())
3211 {
3212 CONSTANT(0U, 0U); // initial
3213 CONSTANT(1U, 1U); // increment
3214 PARAMETER(13U, 0U).ref(); // Array
3215 PARAMETER(27U, 1U).s32(); // X
3216 BASIC_BLOCK(2U, 6U, 3U)
3217 {
3218 INST(30U, Opcode::SaveStateDeoptimize).Inputs(0U).SrcVregs({0U});
3219 INST(14U, Opcode::Compare).CC(ConditionCode::CC_GE).b().Inputs(0U, 27U); // i < X
3220 INST(15U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(14U);
3221 }
3222 BASIC_BLOCK(3U, 6U, 3U)
3223 {
3224 INST(4U, Opcode::Phi).s32().Inputs(0U, 10U);
3225 INST(7U, Opcode::SaveState).Inputs(0U, 1U, 13U, 27U).SrcVregs({0U, 1U, 2U, 3U});
3226 INST(16U, Opcode::NullCheck).ref().Inputs(13U, 7U);
3227 INST(17U, Opcode::LenArray).s32().Inputs(16U);
3228 INST(8U, Opcode::BoundsCheck).s32().Inputs(17U, 4U, 7U);
3229 INST(9U, Opcode::StoreArray).s32().Inputs(16U, 8U, 0U); // a[i] = 0
3230 INST(10U, Opcode::Add).s32().Inputs(4U, 1U); // i++
3231 INST(5U, Opcode::Compare).CC(ConditionCode::CC_GE).b().Inputs(10U, 27U); // i < X
3232 INST(6U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(5U);
3233 }
3234 BASIC_BLOCK(6U, 1U)
3235 {
3236 INST(26U, Opcode::Phi).s32().Inputs(0U, 10U);
3237 INST(12U, Opcode::Return).s32().Inputs(26U);
3238 }
3239 }
3240 }
3241
TEST_F(ChecksEliminationTest,LoopTest1)3242 TEST_F(ChecksEliminationTest, LoopTest1)
3243 {
3244 BuildGraphLoopTest1();
3245 ASSERT_TRUE(GetGraph()->RunPass<ChecksElimination>());
3246 auto graph1 = CreateEmptyGraph();
3247 GRAPH(graph1)
3248 {
3249 CONSTANT(0U, 0U); // initial
3250 CONSTANT(1U, 1U); // increment
3251 PARAMETER(3U, 0U).ref();
3252 PARAMETER(2U, 1U).s32();
3253 BASIC_BLOCK(2U, 5U, 3U)
3254 {
3255 INST(20U, Opcode::SaveStateDeoptimize).Inputs(0U).SrcVregs({0U});
3256 INST(33U, Opcode::NullCheck).ref().Inputs(3U, 20U).SetFlag(inst_flags::CAN_DEOPTIMIZE); // array != nullptr
3257 INST(22U, Opcode::LenArray).s32().Inputs(33U);
3258 INST(23U, Opcode::Compare).SrcType(DataType::INT32).CC(CC_LT).b().Inputs(22U, 2U); // len_array < X
3259 INST(24U, Opcode::DeoptimizeIf).DeoptimizeType(DeoptimizeType::BOUNDS_CHECK).Inputs(23U, 20U);
3260 INST(5U, Opcode::Compare).SrcType(DataType::INT32).CC(CC_GE).b().Inputs(0U, 2U); // 0 < X
3261 INST(6U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(5U);
3262 }
3263 BASIC_BLOCK(3U, 5U, 3U)
3264 {
3265 INST(4U, Opcode::Phi).s32().Inputs(0U, 10U);
3266 INST(7U, Opcode::SaveState).Inputs(0U, 1U, 3U, 2U).SrcVregs({0U, 1U, 2U, 3U});
3267 INST(15U, Opcode::NOP);
3268 INST(16U, Opcode::LenArray).s32().Inputs(33U);
3269 INST(8U, Opcode::NOP);
3270 INST(9U, Opcode::StoreArray).s32().Inputs(33U, 4U, 0U); // a[i] = 0
3271 INST(10U, Opcode::Add).s32().Inputs(4U, 1U); // i++
3272 INST(13U, Opcode::Compare).CC(CC_GE).b().Inputs(10U, 2U); // i < X
3273 INST(14U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(13U);
3274 }
3275 BASIC_BLOCK(5U, 1U)
3276 {
3277 INST(26U, Opcode::Phi).s32().Inputs(0U, 10U);
3278 INST(12U, Opcode::Return).s32().Inputs(26U);
3279 }
3280 }
3281 ASSERT_TRUE(GraphComparator().Compare(GetGraph(), graph1));
3282 }
3283
BuildGraphBatchLoopTest()3284 void ChecksEliminationTest::BuildGraphBatchLoopTest()
3285 {
3286 GRAPH(GetGraph())
3287 {
3288 CONSTANT(0U, 0U); // initial
3289 CONSTANT(1U, 1U);
3290 CONSTANT(2U, 2U);
3291 CONSTANT(3U, 3U); // increment
3292 PARAMETER(4U, 0U).ref(); // Array
3293 BASIC_BLOCK(2U, 4U, 3U)
3294 {
3295 INST(5U, Opcode::SaveState).Inputs(0U, 1U, 2U, 3U, 4U).SrcVregs({0U, 1U, 2U, 3U, 4U});
3296 INST(6U, Opcode::NullCheck).ref().Inputs(4U, 5U);
3297 INST(7U, Opcode::LenArray).s32().Inputs(6U);
3298 INST(30U, Opcode::SaveStateDeoptimize).Inputs(4U).SrcVregs({0U});
3299 INST(25U, Opcode::Compare).CC(CC_GE).b().Inputs(0U, 7U);
3300 INST(26U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(25U);
3301 }
3302 BASIC_BLOCK(3U, 4U, 3U)
3303 {
3304 INST(8U, Opcode::Phi).s32().Inputs(0U, 20U);
3305 INST(9U, Opcode::SaveState).Inputs(0U, 1U, 2U, 3U, 4U, 7U).SrcVregs({0U, 1U, 2U, 3U, 4U, 7U});
3306 INST(10U, Opcode::NullCheck).ref().Inputs(4U, 9U);
3307
3308 INST(12U, Opcode::Add).s32().Inputs(8U, 1U);
3309 INST(13U, Opcode::BoundsCheck).s32().Inputs(7U, 12U, 9U);
3310 INST(14U, Opcode::LoadArray).s32().Inputs(10U, 13U);
3311
3312 INST(15U, Opcode::BoundsCheck).s32().Inputs(7U, 8U, 9U);
3313 INST(16U, Opcode::StoreArray).s32().Inputs(10U, 15U, 14U); // a[i] = a[i + 1]
3314
3315 INST(17U, Opcode::Add).s32().Inputs(8U, 2U);
3316 INST(18U, Opcode::BoundsCheck).s32().Inputs(7U, 17U, 9U);
3317 INST(19U, Opcode::StoreArray).s32().Inputs(10U, 18U, 14U); // a[i + 2] = a[i + 1]
3318
3319 INST(20U, Opcode::Add).s32().Inputs(8U, 3U); // i += 3
3320 INST(21U, Opcode::Compare).CC(CC_GE).b().Inputs(20U, 7U); // i < X
3321 INST(22U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(21U);
3322 }
3323 BASIC_BLOCK(4U, 1U)
3324 {
3325 INST(23U, Opcode::ReturnVoid).v0id();
3326 }
3327 }
3328 }
3329
TEST_F(ChecksEliminationTest,BatchLoopTest)3330 TEST_F(ChecksEliminationTest, BatchLoopTest)
3331 {
3332 BuildGraphBatchLoopTest();
3333 ASSERT_TRUE(GetGraph()->RunPass<ChecksElimination>());
3334 auto graph1 = CreateEmptyGraph();
3335 GRAPH(graph1)
3336 {
3337 CONSTANT(0U, 0U); // initial
3338 CONSTANT(1U, 1U);
3339 CONSTANT(2U, 2U);
3340 CONSTANT(3U, 3U); // increment
3341 PARAMETER(4U, 0U).ref(); // Array
3342 CONSTANT(36U, 0x7ffffffdU);
3343 BASIC_BLOCK(2U, 4U, 3U)
3344 {
3345 INST(5U, Opcode::SaveState).Inputs(0U, 1U, 2U, 3U, 4U).SrcVregs({0U, 1U, 2U, 3U, 4U});
3346 INST(6U, Opcode::NullCheck).ref().Inputs(4U, 5U);
3347 INST(7U, Opcode::LenArray).s32().Inputs(6U);
3348 INST(30U, Opcode::SaveStateDeoptimize).Inputs(4U).SrcVregs({0U});
3349 INST(31U, Opcode::Compare).CC(CC_LT).b().Inputs(36U, 7U);
3350 INST(32U, Opcode::DeoptimizeIf).DeoptimizeType(DeoptimizeType::BOUNDS_CHECK).Inputs(31U, 30U);
3351 INST(33U, Opcode::Mod).s32().Inputs(7U, 3U);
3352 INST(34U, Opcode::Compare).CC(CC_NE).b().Inputs(33U, 0U);
3353 INST(35U, Opcode::DeoptimizeIf).DeoptimizeType(DeoptimizeType::BOUNDS_CHECK).Inputs(34U, 30U);
3354 INST(25U, Opcode::Compare).CC(CC_GE).b().Inputs(0U, 7U);
3355 INST(26U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(25U);
3356 }
3357 BASIC_BLOCK(3U, 4U, 3U)
3358 {
3359 INST(8U, Opcode::Phi).s32().Inputs(0U, 20U);
3360 INST(9U, Opcode::SaveState).Inputs(0U, 1U, 2U, 3U, 4U, 7U).SrcVregs({0U, 1U, 2U, 3U, 4U, 7U});
3361 INST(10U, Opcode::NOP);
3362
3363 INST(12U, Opcode::Add).s32().Inputs(8U, 1U);
3364 INST(13U, Opcode::NOP);
3365 INST(14U, Opcode::LoadArray).s32().Inputs(6U, 12U);
3366
3367 INST(15U, Opcode::NOP);
3368 INST(16U, Opcode::StoreArray).s32().Inputs(6U, 8U, 14U); // a[i] = a[i + 1]
3369
3370 INST(17U, Opcode::Add).s32().Inputs(8U, 2U);
3371 INST(18U, Opcode::NOP);
3372 INST(19U, Opcode::StoreArray).s32().Inputs(6U, 17U, 14U); // a[i + 2] = a[i + 1]
3373
3374 INST(20U, Opcode::Add).s32().Inputs(8U, 3U); // i += 3
3375 INST(21U, Opcode::Compare).CC(CC_GE).b().Inputs(20U, 7U); // i < X
3376 INST(22U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(21U);
3377 }
3378 BASIC_BLOCK(4U, 1U)
3379 {
3380 INST(23U, Opcode::ReturnVoid).v0id();
3381 }
3382 }
3383 ASSERT_TRUE(GraphComparator().Compare(GetGraph(), graph1));
3384 }
3385
TEST_F(ChecksEliminationTest,NewlyAlllocatedCheck)3386 TEST_F(ChecksEliminationTest, NewlyAlllocatedCheck)
3387 {
3388 GRAPH(GetGraph())
3389 {
3390 CONSTANT(0U, 0x1U).s64();
3391 CONSTANT(1U, 0x0U).s64();
3392 CONSTANT(5U, 0x2aU).s64();
3393 BASIC_BLOCK(2U, -1L)
3394 {
3395 INST(43U, Opcode::SaveState).Inputs(0U).SrcVregs({0U});
3396 INST(44U, Opcode::LoadAndInitClass).ref().Inputs().TypeId(68U);
3397 INST(4U, Opcode::NewArray).ref().Inputs(44U, 0U, 43U).TypeId(79U);
3398 INST(6U, Opcode::SaveState).Inputs(5U, 1U, 4U).SrcVregs({2U, 1U, 0U});
3399 INST(7U, Opcode::NullCheck).ref().Inputs(4U, 6U);
3400 INST(10U, Opcode::StoreArray).s32().Inputs(7U, 1U, 5U);
3401 INST(11U, Opcode::Return).ref().Inputs(4U);
3402 }
3403 }
3404 ASSERT_TRUE(GetGraph()->RunPass<ChecksElimination>());
3405 auto graph1 = CreateEmptyGraph();
3406 GRAPH(graph1)
3407 {
3408 CONSTANT(0U, 0x1U).s64();
3409 CONSTANT(1U, 0x0U).s64();
3410 CONSTANT(5U, 0x2aU).s64();
3411 BASIC_BLOCK(2U, -1L)
3412 {
3413 INST(43U, Opcode::SaveState).Inputs(0U).SrcVregs({0U});
3414 INST(44U, Opcode::LoadAndInitClass).ref().Inputs().TypeId(68U);
3415 INST(4U, Opcode::NewArray).ref().Inputs(44U, 0U, 43U).TypeId(79U);
3416 INST(6U, Opcode::SaveState).Inputs(5U, 1U, 4U).SrcVregs({2U, 1U, 0U});
3417 INST(7U, Opcode::NOP);
3418 INST(10U, Opcode::StoreArray).s32().Inputs(4U, 1U, 5U);
3419 INST(11U, Opcode::Return).ref().Inputs(4U);
3420 }
3421 }
3422 ASSERT_TRUE(GraphComparator().Compare(GetGraph(), graph1));
3423 }
3424
TEST_F(ChecksEliminationTest,DominatedBoundsCheck)3425 TEST_F(ChecksEliminationTest, DominatedBoundsCheck)
3426 {
3427 GRAPH(GetGraph())
3428 {
3429 PARAMETER(0U, 0U).ref();
3430 PARAMETER(1U, 1U).s64();
3431 BASIC_BLOCK(2U, -1L)
3432 {
3433 INST(2U, Opcode::SaveState).Inputs(1U, 1U, 0U).SrcVregs({2U, 1U, 0U});
3434 INST(4U, Opcode::LenArray).s32().Inputs(0U);
3435 INST(5U, Opcode::BoundsCheck).s32().Inputs(4U, 1U, 2U);
3436 INST(6U, Opcode::LoadArray).s64().Inputs(0U, 5U);
3437 INST(7U, Opcode::SaveState).Inputs(6U, 1U, 0U).SrcVregs({2U, 1U, 0U});
3438 INST(10U, Opcode::BoundsCheck).s32().Inputs(4U, 1U, 7U);
3439 INST(11U, Opcode::StoreArray).s64().Inputs(0U, 10U, 6U);
3440 INST(12U, Opcode::ReturnVoid).v0id();
3441 }
3442 }
3443 ASSERT_TRUE(GetGraph()->RunPass<ChecksElimination>());
3444 auto graph1 = CreateEmptyGraph();
3445 GRAPH(graph1)
3446 {
3447 PARAMETER(0U, 0U).ref();
3448 PARAMETER(1U, 1U).s64();
3449 BASIC_BLOCK(2U, -1L)
3450 {
3451 INST(2U, Opcode::SaveState).Inputs(1U, 1U, 0U).SrcVregs({2U, 1U, 0U});
3452 INST(4U, Opcode::LenArray).s32().Inputs(0U);
3453 INST(5U, Opcode::BoundsCheck).s32().Inputs(4U, 1U, 2U);
3454 INST(6U, Opcode::LoadArray).s64().Inputs(0U, 5U);
3455 INST(7U, Opcode::SaveState).Inputs(6U, 1U, 0U).SrcVregs({2U, 1U, 0U});
3456 INST(10U, Opcode::NOP);
3457 INST(11U, Opcode::StoreArray).s64().Inputs(0U, 5U, 6U);
3458 INST(12U, Opcode::ReturnVoid).v0id();
3459 }
3460 }
3461 ASSERT_TRUE(GraphComparator().Compare(GetGraph(), graph1));
3462 }
3463
BuildGraphGroupedBoundsCheck()3464 void ChecksEliminationTest::BuildGraphGroupedBoundsCheck()
3465 {
3466 GRAPH(GetGraph())
3467 {
3468 PARAMETER(0U, 0U).ref(); // a
3469 PARAMETER(1U, 1U).s32(); // x
3470 CONSTANT(2U, 1U);
3471 CONSTANT(5U, -2L);
3472 BASIC_BLOCK(2U, -1L)
3473 {
3474 INST(6U, Opcode::LenArray).s32().Inputs(0U);
3475 // a[x] = 1
3476 INST(7U, Opcode::SaveState).Inputs(0U, 1U).SrcVregs({0U, 1U});
3477 INST(8U, Opcode::BoundsCheck).s32().Inputs(6U, 1U, 7U);
3478 INST(9U, Opcode::StoreArray).s64().Inputs(0U, 8U, 2U);
3479 // a[x-1] = 1
3480 INST(10U, Opcode::Sub).s32().Inputs(1U, 2U);
3481 INST(11U, Opcode::SaveState).Inputs(0U, 1U).SrcVregs({0U, 1U});
3482 INST(12U, Opcode::BoundsCheck).s32().Inputs(6U, 10U, 11U);
3483 INST(13U, Opcode::StoreArray).s64().Inputs(0U, 12U, 2U);
3484 // a[x+1] = 1
3485 INST(14U, Opcode::Add).s32().Inputs(1U, 2U);
3486 INST(15U, Opcode::SaveState).Inputs(0U, 1U).SrcVregs({0U, 1U});
3487 INST(16U, Opcode::BoundsCheck).s32().Inputs(6U, 14U, 15U);
3488 INST(17U, Opcode::StoreArray).s64().Inputs(0U, 16U, 2U);
3489 // a[x+(-2)] = 1
3490 INST(18U, Opcode::Add).s32().Inputs(1U, 5U);
3491 INST(19U, Opcode::SaveState).Inputs(0U, 1U).SrcVregs({0U, 1U});
3492 INST(20U, Opcode::BoundsCheck).s32().Inputs(6U, 18U, 19U);
3493 INST(21U, Opcode::StoreArray).s64().Inputs(0U, 20U, 2U);
3494 // a[x-(-2)] = 1
3495 INST(22U, Opcode::Sub).s32().Inputs(1U, 5U);
3496 INST(23U, Opcode::SaveState).Inputs(0U, 1U).SrcVregs({0U, 1U});
3497 INST(24U, Opcode::BoundsCheck).s32().Inputs(6U, 22U, 23U);
3498 INST(25U, Opcode::StoreArray).s64().Inputs(0U, 24U, 2U);
3499 INST(26U, Opcode::ReturnVoid).v0id();
3500 }
3501 }
3502 }
3503
TEST_F(ChecksEliminationTest,GroupedBoundsCheck)3504 TEST_F(ChecksEliminationTest, GroupedBoundsCheck)
3505 {
3506 BuildGraphGroupedBoundsCheck();
3507 ASSERT_TRUE(GetGraph()->RunPass<ChecksElimination>());
3508 auto graph1 = CreateEmptyGraph();
3509 GRAPH(graph1)
3510 {
3511 PARAMETER(0U, 0U).ref(); // a
3512 PARAMETER(1U, 1U).s32(); // x
3513 CONSTANT(2U, 1U);
3514 CONSTANT(5U, -2L);
3515 CONSTANT(3U, 2U);
3516 CONSTANT(4U, 0U);
3517 BASIC_BLOCK(2U, -1L)
3518 {
3519 INST(6U, Opcode::LenArray).s32().Inputs(0U);
3520 INST(7U, Opcode::SaveState).Inputs(0U, 1U).SrcVregs({0U, 1U});
3521
3522 INST(30U, Opcode::Add).s32().Inputs(1U, 5U);
3523 INST(31U, Opcode::Compare).b().CC(CC_LT).SrcType(DataType::INT32).Inputs(30U, 4U);
3524 INST(32U, Opcode::DeoptimizeIf).DeoptimizeType(DeoptimizeType::BOUNDS_CHECK).Inputs(31U, 7U);
3525 INST(27U, Opcode::Add).s32().Inputs(1U, 3U);
3526 INST(28U, Opcode::Compare).b().CC(CC_GE).SrcType(DataType::INT32).Inputs(27U, 6U);
3527 INST(29U, Opcode::DeoptimizeIf).DeoptimizeType(DeoptimizeType::BOUNDS_CHECK).Inputs(28U, 7U);
3528
3529 // a[x] = 1
3530 INST(8U, Opcode::NOP);
3531 INST(9U, Opcode::StoreArray).s64().Inputs(0U, 1U, 2U);
3532 // a[x-1] = 1
3533 INST(10U, Opcode::Sub).s32().Inputs(1U, 2U);
3534 INST(11U, Opcode::SaveState).Inputs(0U, 1U).SrcVregs({0U, 1U});
3535 INST(12U, Opcode::NOP);
3536 INST(13U, Opcode::StoreArray).s64().Inputs(0U, 10U, 2U);
3537 // a[x+1] = 1
3538 INST(14U, Opcode::Add).s32().Inputs(1U, 2U);
3539 INST(15U, Opcode::SaveState).Inputs(0U, 1U).SrcVregs({0U, 1U});
3540 INST(16U, Opcode::NOP);
3541 INST(17U, Opcode::StoreArray).s64().Inputs(0U, 14U, 2U);
3542 // a[x+(-2)] = 1
3543 INST(18U, Opcode::Add).s32().Inputs(1U, 5U);
3544 INST(19U, Opcode::SaveState).Inputs(0U, 1U).SrcVregs({0U, 1U});
3545 INST(20U, Opcode::NOP);
3546 INST(21U, Opcode::StoreArray).s64().Inputs(0U, 18U, 2U);
3547 // a[x-(-2)] = 1
3548 INST(22U, Opcode::Sub).s32().Inputs(1U, 5U);
3549 INST(23U, Opcode::SaveState).Inputs(0U, 1U).SrcVregs({0U, 1U});
3550 INST(24U, Opcode::NOP);
3551 INST(25U, Opcode::StoreArray).s64().Inputs(0U, 22U, 2U);
3552 INST(26U, Opcode::ReturnVoid).v0id();
3553 }
3554 }
3555 ASSERT_TRUE(GraphComparator().Compare(GetGraph(), graph1));
3556 }
3557
BuildGraphGroupedBoundsCheckConstIndex()3558 void ChecksEliminationTest::BuildGraphGroupedBoundsCheckConstIndex()
3559 {
3560 GRAPH(GetGraph())
3561 {
3562 PARAMETER(0U, 0U).ref(); // a
3563 CONSTANT(2U, 0U);
3564 CONSTANT(3U, 1U);
3565 CONSTANT(4U, 2U);
3566 CONSTANT(5U, 3U);
3567 BASIC_BLOCK(2U, -1L)
3568 {
3569 INST(6U, Opcode::LenArray).s32().Inputs(0U);
3570 INST(7U, Opcode::SaveState).Inputs(0U).SrcVregs({0U});
3571
3572 // a[0] = 1
3573 INST(8U, Opcode::BoundsCheck).s32().Inputs(6U, 2U, 7U);
3574 INST(9U, Opcode::StoreArray).s64().Inputs(0U, 8U, 2U);
3575 // a[1] = 1
3576 INST(11U, Opcode::SaveState).Inputs(0U).SrcVregs({0U});
3577 INST(12U, Opcode::BoundsCheck).s32().Inputs(6U, 3U, 11U);
3578 INST(13U, Opcode::StoreArray).s64().Inputs(0U, 12U, 2U);
3579 // a[2] = 1
3580 INST(15U, Opcode::SaveState).Inputs(0U).SrcVregs({0U});
3581 INST(16U, Opcode::BoundsCheck).s32().Inputs(6U, 4U, 15U);
3582 INST(17U, Opcode::StoreArray).s64().Inputs(0U, 16U, 2U);
3583 // a[3] = 1
3584 INST(19U, Opcode::SaveState).Inputs(0U).SrcVregs({0U});
3585 INST(20U, Opcode::BoundsCheck).s32().Inputs(6U, 5U, 19U);
3586 INST(21U, Opcode::StoreArray).s64().Inputs(0U, 20U, 2U);
3587 INST(26U, Opcode::ReturnVoid).v0id();
3588 }
3589 }
3590 }
3591
TEST_F(ChecksEliminationTest,GroupedBoundsCheckConstIndex)3592 TEST_F(ChecksEliminationTest, GroupedBoundsCheckConstIndex)
3593 {
3594 BuildGraphGroupedBoundsCheckConstIndex();
3595 ASSERT_TRUE(GetGraph()->RunPass<ChecksElimination>());
3596 auto graph1 = CreateEmptyGraph();
3597 GRAPH(graph1)
3598 {
3599 PARAMETER(0U, 0U).ref(); // a
3600 CONSTANT(2U, 0U);
3601 CONSTANT(3U, 1U);
3602 CONSTANT(4U, 2U);
3603 CONSTANT(5U, 3U);
3604 BASIC_BLOCK(2U, -1L)
3605 {
3606 INST(6U, Opcode::LenArray).s32().Inputs(0U);
3607 INST(7U, Opcode::SaveState).Inputs(0U).SrcVregs({0U});
3608
3609 INST(28U, Opcode::Compare).b().CC(CC_GE).SrcType(DataType::INT32).Inputs(5U, 6U);
3610 INST(29U, Opcode::DeoptimizeIf).DeoptimizeType(DeoptimizeType::BOUNDS_CHECK).Inputs(28U, 7U);
3611
3612 // a[0] = 1
3613 INST(8U, Opcode::NOP);
3614 INST(9U, Opcode::StoreArray).s64().Inputs(0U, 2U, 2U);
3615 // a[1] = 1
3616 INST(11U, Opcode::SaveState).Inputs(0U).SrcVregs({0U});
3617 INST(12U, Opcode::NOP);
3618 INST(13U, Opcode::StoreArray).s64().Inputs(0U, 3U, 2U);
3619 // a[2] = 1
3620 INST(15U, Opcode::SaveState).Inputs(0U).SrcVregs({0U});
3621 INST(16U, Opcode::NOP);
3622 INST(17U, Opcode::StoreArray).s64().Inputs(0U, 4U, 2U);
3623 // a[3] = 1
3624 INST(19U, Opcode::SaveState).Inputs(0U).SrcVregs({0U});
3625 INST(20U, Opcode::NOP);
3626 INST(21U, Opcode::StoreArray).s64().Inputs(0U, 5U, 2U);
3627 INST(26U, Opcode::ReturnVoid).v0id();
3628 }
3629 }
3630 ASSERT_TRUE(GraphComparator().Compare(GetGraph(), graph1));
3631 }
3632
TEST_F(ChecksEliminationTest,ConsecutiveNullChecks)3633 TEST_F(ChecksEliminationTest, ConsecutiveNullChecks)
3634 {
3635 builder_->EnableGraphChecker(false);
3636 GRAPH(GetGraph())
3637 {
3638 PARAMETER(0U, 0U).ref();
3639 BASIC_BLOCK(2U, 1U)
3640 {
3641 INST(5U, Opcode::SaveState).Inputs(0U).SrcVregs({0U});
3642 INST(1U, Opcode::NullCheck).ref().Inputs(0U, 5U);
3643 INST(2U, Opcode::NullCheck).ref().Inputs(1U, 5U);
3644 INST(3U, Opcode::NullCheck).ref().Inputs(2U, 5U);
3645 INST(6U, Opcode::StoreObject).ref().Inputs(3U, 0U).TypeId(1U);
3646 INST(4U, Opcode::Return).ref().Inputs(3U);
3647 }
3648 }
3649 builder_->EnableGraphChecker(true);
3650 ASSERT_TRUE(GetGraph()->RunPass<ChecksElimination>());
3651 auto graph = CreateEmptyGraph();
3652 GRAPH(graph)
3653 {
3654 PARAMETER(0U, 0U).ref();
3655 BASIC_BLOCK(2U, 1U)
3656 {
3657 INST(5U, Opcode::SaveState).Inputs(0U).SrcVregs({0U});
3658 INST(1U, Opcode::NullCheck).ref().Inputs(0U, 5U);
3659 INST(2U, Opcode::NOP);
3660 INST(3U, Opcode::NOP);
3661 INST(6U, Opcode::StoreObject).ref().Inputs(1U, 0U).TypeId(1U);
3662 INST(4U, Opcode::Return).ref().Inputs(1U);
3663 }
3664 }
3665 ASSERT_TRUE(GraphComparator().Compare(GetGraph(), graph));
3666 }
3667
TEST_F(ChecksEliminationTest,NullCheckInCallVirt)3668 TEST_F(ChecksEliminationTest, NullCheckInCallVirt)
3669 {
3670 GRAPH(GetGraph())
3671 {
3672 PARAMETER(0U, 0U).ref();
3673 PARAMETER(1U, 1U).ref();
3674 BASIC_BLOCK(2U, 1U)
3675 {
3676 INST(5U, Opcode::SaveState).Inputs(0U, 1U).SrcVregs({0U, 1U});
3677 INST(2U, Opcode::NullCheck).ref().Inputs(0U, 5U);
3678 INST(3U, Opcode::NullCheck).ref().Inputs(1U, 5U);
3679 INST(4U, Opcode::CallVirtual)
3680 .s32()
3681 .Inputs({{DataType::REFERENCE, 2U}, {DataType::REFERENCE, 3U}, {DataType::NO_TYPE, 5U}});
3682 INST(6U, Opcode::Return).s32().Inputs(4U);
3683 }
3684 }
3685 // Doesn't remove nullchecks if the method static
3686 auto clone = GraphCloner(GetGraph(), GetGraph()->GetAllocator(), GetGraph()->GetLocalAllocator()).CloneGraph();
3687 ASSERT_FALSE(GetGraph()->RunPass<ChecksElimination>());
3688 ASSERT_TRUE(GraphComparator().Compare(GetGraph(), clone));
3689
3690 // Removes nullcheck from first parameter for not static method
3691 RuntimeInterfaceNotStaticMethod runtime;
3692 GetGraph()->SetRuntime(&runtime);
3693 ASSERT_TRUE(GetGraph()->RunPass<ChecksElimination>());
3694 auto graph1 = CreateEmptyGraph();
3695 GRAPH(graph1)
3696 {
3697 PARAMETER(0U, 0U).ref();
3698 PARAMETER(1U, 1U).ref();
3699 BASIC_BLOCK(2U, 1U)
3700 {
3701 INST(5U, Opcode::SaveState).Inputs(0U, 1U).SrcVregs({0U, 1U});
3702 INST(2U, Opcode::NOP);
3703 INST(3U, Opcode::NullCheck).ref().Inputs(1U, 5U);
3704 INST(4U, Opcode::CallVirtual)
3705 .s32()
3706 .Inputs({{DataType::REFERENCE, 0U}, {DataType::REFERENCE, 3U}, {DataType::NO_TYPE, 5U}});
3707 INST(6U, Opcode::Return).s32().Inputs(4U);
3708 }
3709 }
3710 ASSERT_TRUE(GraphComparator().Compare(GetGraph(), graph1));
3711 }
3712
TEST_F(ChecksEliminationTest,DominatedChecksWithDifferentTypes)3713 TEST_F(ChecksEliminationTest, DominatedChecksWithDifferentTypes)
3714 {
3715 GRAPH(GetGraph())
3716 {
3717 PARAMETER(0U, 0U).s64();
3718 PARAMETER(1U, 1U).s64();
3719 BASIC_BLOCK(2U, 1U)
3720 {
3721 INST(2U, Opcode::SaveState).Inputs(0U, 1U).SrcVregs({0U, 1U});
3722 INST(3U, Opcode::ZeroCheck).s32().Inputs(0U, 2U);
3723 INST(4U, Opcode::Div).s32().Inputs(1U, 3U);
3724 INST(5U, Opcode::SaveState).Inputs(0U, 1U).SrcVregs({0U, 1U});
3725 INST(6U, Opcode::ZeroCheck).s64().Inputs(0U, 5U);
3726 INST(7U, Opcode::Mod).s64().Inputs(1U, 6U);
3727 INST(20U, Opcode::SaveState).NoVregs();
3728 INST(8U, Opcode::CallStatic).s32().InputsAutoType(4U, 7U, 20U);
3729 INST(9U, Opcode::Return).s32().Inputs(8U);
3730 }
3731 }
3732 auto initial = GraphCloner(GetGraph(), GetGraph()->GetAllocator(), GetGraph()->GetLocalAllocator()).CloneGraph();
3733 ASSERT_FALSE(GetGraph()->RunPass<ChecksElimination>());
3734 ASSERT_TRUE(GraphComparator().Compare(GetGraph(), initial));
3735 }
3736
BuildGraphRefTypeCheck()3737 void ChecksEliminationTest::BuildGraphRefTypeCheck()
3738 {
3739 GRAPH(GetGraph())
3740 {
3741 PARAMETER(0U, 0U).ref();
3742 PARAMETER(1U, 1U).ref();
3743 PARAMETER(2U, 2U).s32();
3744 PARAMETER(3U, 3U).s32();
3745 PARAMETER(4U, 4U).s32();
3746 CONSTANT(5U, nullptr);
3747 BASIC_BLOCK(2U, 1U)
3748 {
3749 INST(10U, Opcode::SaveState).Inputs(0U, 1U, 2U, 2U, 5U).SrcVregs({0U, 1U, 2U, 3U, 4U});
3750 INST(11U, Opcode::NullCheck).ref().Inputs(1U, 10U);
3751 INST(12U, Opcode::LenArray).s32().Inputs(11U);
3752 INST(13U, Opcode::BoundsCheck).s32().Inputs(12U, 2U, 10U);
3753 INST(14U, Opcode::RefTypeCheck).ref().Inputs(11U, 0U, 10U);
3754 INST(15U, Opcode::StoreArray).ref().Inputs(11U, 13U, 14U);
3755
3756 INST(20U, Opcode::SaveState).Inputs(0U, 1U, 2U, 2U, 5U).SrcVregs({0U, 1U, 2U, 3U, 4U});
3757 INST(21U, Opcode::NullCheck).ref().Inputs(1U, 20U);
3758 INST(22U, Opcode::LenArray).s32().Inputs(21U);
3759 INST(23U, Opcode::BoundsCheck).s32().Inputs(22U, 3U, 20U);
3760 INST(24U, Opcode::RefTypeCheck).ref().Inputs(21U, 0U, 20U);
3761 INST(25U, Opcode::StoreArray).ref().Inputs(21U, 23U, 24U);
3762
3763 INST(30U, Opcode::SaveState).Inputs(0U, 1U, 2U, 2U, 5U).SrcVregs({0U, 1U, 2U, 3U, 4U});
3764 INST(31U, Opcode::NullCheck).ref().Inputs(1U, 30U);
3765 INST(32U, Opcode::LenArray).s32().Inputs(31U);
3766 INST(33U, Opcode::BoundsCheck).s32().Inputs(32U, 4U, 30U);
3767 INST(34U, Opcode::RefTypeCheck).ref().Inputs(31U, 5U, 30U);
3768 INST(35U, Opcode::StoreArray).ref().Inputs(31U, 33U, 34U);
3769
3770 INST(6U, Opcode::ReturnVoid).v0id();
3771 }
3772 }
3773 }
3774
TEST_F(ChecksEliminationTest,RefTypeCheck)3775 TEST_F(ChecksEliminationTest, RefTypeCheck)
3776 {
3777 BuildGraphRefTypeCheck();
3778 // `24, Opcode::RefTypeCheck` is removed because `14, Opcode::RefTypeCheck` checks equal array and eqaul
3779 // Reference `34, Opcode::RefTypeCheck` is removed because store value id NullPtr
3780 ASSERT_TRUE(GetGraph()->RunPass<ChecksElimination>());
3781 auto graph = CreateEmptyGraph();
3782 GRAPH(graph)
3783 {
3784 PARAMETER(0U, 0U).ref();
3785 PARAMETER(1U, 1U).ref();
3786 PARAMETER(2U, 2U).s32();
3787 PARAMETER(3U, 3U).s32();
3788 PARAMETER(4U, 4U).s32();
3789 CONSTANT(5U, nullptr);
3790 BASIC_BLOCK(2U, 1U)
3791 {
3792 INST(10U, Opcode::SaveState).Inputs(0U, 1U, 2U, 2U, 5U).SrcVregs({0U, 1U, 2U, 3U, 4U});
3793 INST(11U, Opcode::NullCheck).ref().Inputs(1U, 10U);
3794 INST(12U, Opcode::LenArray).s32().Inputs(11U);
3795 INST(13U, Opcode::BoundsCheck).s32().Inputs(12U, 2U, 10U);
3796 INST(14U, Opcode::RefTypeCheck).ref().Inputs(11U, 0U, 10U);
3797 INST(15U, Opcode::StoreArray).ref().Inputs(11U, 13U, 14U);
3798
3799 INST(20U, Opcode::SaveState).Inputs(0U, 1U, 2U, 2U, 5U).SrcVregs({0U, 1U, 2U, 3U, 4U});
3800 INST(21U, Opcode::NOP);
3801 INST(22U, Opcode::LenArray).s32().Inputs(11U);
3802 INST(23U, Opcode::BoundsCheck).s32().Inputs(22U, 3U, 20U);
3803 INST(24U, Opcode::NOP);
3804 INST(25U, Opcode::StoreArray).ref().Inputs(11U, 23U, 14U);
3805
3806 INST(30U, Opcode::SaveState).Inputs(0U, 1U, 2U, 2U, 5U).SrcVregs({0U, 1U, 2U, 3U, 4U});
3807 INST(31U, Opcode::NOP);
3808 INST(32U, Opcode::LenArray).s32().Inputs(11U);
3809 INST(33U, Opcode::BoundsCheck).s32().Inputs(32U, 4U, 30U);
3810 INST(34U, Opcode::NOP);
3811 INST(35U, Opcode::StoreArray).ref().Inputs(11U, 33U, 5U);
3812
3813 INST(6U, Opcode::ReturnVoid).v0id();
3814 }
3815 }
3816 ASSERT_TRUE(GraphComparator().Compare(GetGraph(), graph));
3817 }
3818
BuildGraphRefTypeCheckFirstNullCheckEliminated()3819 void ChecksEliminationTest::BuildGraphRefTypeCheckFirstNullCheckEliminated()
3820 {
3821 GRAPH(GetGraph())
3822 {
3823 PARAMETER(0U, 0U).ref();
3824 PARAMETER(1U, 2U).s32();
3825 PARAMETER(2U, 3U).s32();
3826 CONSTANT(3U, 10U);
3827 BASIC_BLOCK(2U, 1U)
3828 {
3829 INST(4U, Opcode::SaveState).Inputs(0U, 1U, 2U).SrcVregs({0U, 1U, 2U});
3830 INST(5U, Opcode::NewArray).ref().Inputs(0U, 3U, 4U);
3831 INST(6U, Opcode::RefTypeCheck).ref().Inputs(5U, 0U, 4U);
3832 INST(7U, Opcode::StoreArray).ref().Inputs(5U, 1U, 6U);
3833
3834 INST(14U, Opcode::SaveState).Inputs(0U, 1U, 2U).SrcVregs({0U, 1U, 2U});
3835 INST(15U, Opcode::NullCheck).ref().Inputs(5U, 14U);
3836 INST(16U, Opcode::RefTypeCheck).ref().Inputs(15U, 0U, 14U);
3837 INST(17U, Opcode::StoreArray).ref().Inputs(5U, 2U, 16U);
3838
3839 INST(18U, Opcode::ReturnVoid).v0id();
3840 }
3841 }
3842 }
3843
TEST_F(ChecksEliminationTest,RefTypeCheckFirstNullCheckEliminated)3844 TEST_F(ChecksEliminationTest, RefTypeCheckFirstNullCheckEliminated)
3845 {
3846 BuildGraphRefTypeCheckFirstNullCheckEliminated();
3847 ASSERT_TRUE(GetGraph()->RunPass<ChecksElimination>());
3848 auto graph = CreateEmptyGraph();
3849 GRAPH(graph)
3850 {
3851 PARAMETER(0U, 0U).ref();
3852 PARAMETER(1U, 2U).s32();
3853 PARAMETER(2U, 3U).s32();
3854 CONSTANT(3U, 10U);
3855 BASIC_BLOCK(2U, 1U)
3856 {
3857 INST(4U, Opcode::SaveState).Inputs(0U, 1U, 2U).SrcVregs({0U, 1U, 2U});
3858 INST(5U, Opcode::NewArray).ref().Inputs(0U, 3U, 4U);
3859 INST(6U, Opcode::RefTypeCheck).ref().Inputs(5U, 0U, 4U);
3860 INST(7U, Opcode::StoreArray).ref().Inputs(5U, 1U, 6U);
3861
3862 INST(14U, Opcode::SaveState).Inputs(0U, 1U, 2U).SrcVregs({0U, 1U, 2U});
3863 INST(15U, Opcode::NOP);
3864 INST(16U, Opcode::NOP);
3865 INST(17U, Opcode::StoreArray).ref().Inputs(5U, 2U, 6U);
3866
3867 INST(18U, Opcode::ReturnVoid).v0id();
3868 }
3869 }
3870 ASSERT_TRUE(GraphComparator().Compare(GetGraph(), graph));
3871 }
3872
TEST_F(ChecksEliminationTest,RefTypeCheckEqualInputs)3873 TEST_F(ChecksEliminationTest, RefTypeCheckEqualInputs)
3874 {
3875 GRAPH(GetGraph())
3876 {
3877 PARAMETER(0U, 0U).ref();
3878 PARAMETER(1U, 2U).s32();
3879 PARAMETER(2U, 3U).s32();
3880 CONSTANT(3U, 10U);
3881 BASIC_BLOCK(2U, 1U)
3882 {
3883 INST(4U, Opcode::SaveState).Inputs(0U, 1U, 2U).SrcVregs({0U, 1U, 2U});
3884 INST(5U, Opcode::NewArray).ref().Inputs(0U, 3U, 4U);
3885 INST(6U, Opcode::RefTypeCheck).ref().Inputs(5U, 0U, 4U);
3886 INST(7U, Opcode::StoreArray).ref().Inputs(5U, 1U, 6U);
3887
3888 INST(14U, Opcode::SaveState).Inputs(0U, 1U, 2U).SrcVregs({0U, 1U, 2U});
3889 INST(16U, Opcode::RefTypeCheck).ref().Inputs(5U, 5U, 14U);
3890 INST(17U, Opcode::StoreArray).ref().Inputs(5U, 2U, 16U);
3891
3892 INST(18U, Opcode::ReturnVoid).v0id();
3893 }
3894 }
3895 auto clone = GraphCloner(GetGraph(), GetGraph()->GetAllocator(), GetGraph()->GetLocalAllocator()).CloneGraph();
3896 ASSERT_FALSE(GetGraph()->RunPass<ChecksElimination>());
3897 ASSERT_TRUE(GraphComparator().Compare(clone, GetGraph()));
3898 }
3899
BuildHoistRefTypeCheckGraph()3900 void ChecksEliminationTest::BuildHoistRefTypeCheckGraph()
3901 {
3902 GRAPH(GetGraph())
3903 {
3904 CONSTANT(0U, 0U); // initial
3905 CONSTANT(1U, 1U); // increment
3906 CONSTANT(2U, 10U);
3907 PARAMETER(28U, 0U).ref();
3908 PARAMETER(29U, 1U).ref();
3909 BASIC_BLOCK(2U, 3U, 5U)
3910 {
3911 INST(20U, Opcode::SaveStateDeoptimize).Inputs(0U, 1U, 2U, 28U, 29U).SrcVregs({0U, 1U, 2U, 3U, 4U});
3912 INST(5U, Opcode::Compare).SrcType(DataType::INT32).CC(CC_LT).b().Inputs(0U, 2U); // 0 < 10
3913 INST(6U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(5U);
3914 }
3915 BASIC_BLOCK(3U, 3U, 5U)
3916 {
3917 INST(4U, Opcode::Phi).s32().Inputs(0U, 10U); // i
3918
3919 INST(30U, Opcode::SaveState).Inputs(28U, 29U).SrcVregs({0U, 1U});
3920 INST(31U, Opcode::NullCheck).ref().Inputs(29U, 30U);
3921 INST(32U, Opcode::LenArray).s32().Inputs(31U);
3922 INST(34U, Opcode::RefTypeCheck).ref().Inputs(31U, 28U, 30U);
3923 INST(35U, Opcode::StoreArray).ref().Inputs(31U, 4U, 34U);
3924
3925 INST(10U, Opcode::Add).s32().Inputs(4U, 1U); // i++
3926 INST(13U, Opcode::Compare).CC(CC_LT).b().Inputs(10U, 2U); // i < 10
3927 INST(14U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(13U);
3928 }
3929 BASIC_BLOCK(5U, 1U)
3930 {
3931 INST(12U, Opcode::ReturnVoid).v0id();
3932 }
3933 }
3934 }
3935
TEST_F(ChecksEliminationTest,HoistRefTypeCheckTest)3936 TEST_F(ChecksEliminationTest, HoistRefTypeCheckTest)
3937 {
3938 BuildHoistRefTypeCheckGraph();
3939 ASSERT_TRUE(GetGraph()->RunPass<ChecksElimination>());
3940
3941 auto graph = CreateEmptyGraph();
3942 GRAPH(graph)
3943 {
3944 CONSTANT(0U, 0U); // initial
3945 CONSTANT(1U, 1U); // increment
3946 CONSTANT(2U, 10U);
3947 PARAMETER(28U, 0U).ref();
3948 PARAMETER(29U, 1U).ref();
3949 BASIC_BLOCK(2U, 3U, 5U)
3950 {
3951 INST(20U, Opcode::SaveStateDeoptimize).Inputs(0U, 1U, 2U, 28U, 29U).SrcVregs({0U, 1U, 2U, 3U, 4U});
3952 INST(31U, Opcode::NullCheck).ref().Inputs(29U, 20U).SetFlag(inst_flags::CAN_DEOPTIMIZE);
3953 INST(34U, Opcode::RefTypeCheck).ref().Inputs(31U, 28U, 20U).SetFlag(inst_flags::CAN_DEOPTIMIZE);
3954 INST(5U, Opcode::Compare).SrcType(DataType::INT32).CC(CC_LT).b().Inputs(0U, 2U); // 0 < 10
3955 INST(6U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(5U);
3956 }
3957 BASIC_BLOCK(3U, 3U, 5U)
3958 {
3959 INST(4U, Opcode::Phi).s32().Inputs(0U, 10U); // i
3960
3961 INST(30U, Opcode::SaveState).Inputs(28U, 29U).SrcVregs({0U, 1U});
3962 INST(32U, Opcode::LenArray).s32().Inputs(31U);
3963 INST(35U, Opcode::StoreArray).ref().Inputs(31U, 4U, 34U);
3964
3965 INST(10U, Opcode::Add).s32().Inputs(4U, 1U); // i++
3966 INST(13U, Opcode::Compare).CC(CC_LT).b().Inputs(10U, 2U); // i < 10
3967 INST(14U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(13U);
3968 }
3969 BASIC_BLOCK(5U, 1U)
3970 {
3971 INST(12U, Opcode::ReturnVoid).v0id();
3972 }
3973 }
3974 ASSERT_TRUE(GraphComparator().Compare(GetGraph(), graph));
3975 }
3976
TEST_F(ChecksEliminationTest,NotLenArrayInput)3977 TEST_F(ChecksEliminationTest, NotLenArrayInput)
3978 {
3979 GRAPH(GetGraph())
3980 {
3981 PARAMETER(0U, 0U).s32();
3982 CONSTANT(4U, 1U);
3983 CONSTANT(7U, 10U);
3984 BASIC_BLOCK(2U, 3U)
3985 {
3986 INST(1U, Opcode::SaveStateDeoptimize).Inputs(0U).SrcVregs({0U});
3987 }
3988 BASIC_BLOCK(3U, 4U, 5U)
3989 {
3990 INST(3U, Opcode::Phi).s32().Inputs(0U, 5U);
3991 INST(6U, Opcode::Compare).b().CC(CC_GE).Inputs(3U, 7U);
3992 INST(8U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(6U);
3993 }
3994 BASIC_BLOCK(5U, 3U)
3995 {
3996 INST(9U, Opcode::SaveState).Inputs(0U).SrcVregs({0U});
3997 INST(10U, Opcode::NegativeCheck).s32().Inputs(3U, 9U);
3998 INST(44U, Opcode::LoadAndInitClass).ref().Inputs(9U).TypeId(68U);
3999 INST(11U, Opcode::NewArray).ref().Inputs(44U, 10U, 9U);
4000 INST(12U, Opcode::BoundsCheck).s32().Inputs(3U, 0U, 9U);
4001 INST(13U, Opcode::StoreArray).s32().Inputs(11U, 12U, 0U);
4002 INST(5U, Opcode::Add).s32().Inputs(3U, 4U);
4003 }
4004 BASIC_BLOCK(4U, -1L)
4005 {
4006 INST(2U, Opcode::ReturnVoid).v0id();
4007 }
4008 }
4009 auto clone = GraphCloner(GetGraph(), GetGraph()->GetAllocator(), GetGraph()->GetLocalAllocator()).CloneGraph();
4010 ASSERT_FALSE(GetGraph()->RunPass<ChecksElimination>());
4011 ASSERT_TRUE(GraphComparator().Compare(clone, GetGraph()));
4012 }
4013
BuildGraphBugWithNullCheck()4014 void ChecksEliminationTest::BuildGraphBugWithNullCheck()
4015 {
4016 GRAPH(GetGraph())
4017 {
4018 CONSTANT(0U, 0U); // initial
4019 CONSTANT(1U, 1U); // increment
4020 PARAMETER(27U, 1U).s32(); // X
4021 BASIC_BLOCK(2U, 6U, 3U)
4022 {
4023 INST(43U, Opcode::SaveState).Inputs(0U).SrcVregs({0U});
4024 INST(44U, Opcode::LoadAndInitClass).ref().Inputs().TypeId(68U);
4025 INST(13U, Opcode::NewArray).ref().Inputs(44U, 27U, 43U);
4026 INST(30U, Opcode::SaveStateDeoptimize).Inputs(0U).SrcVregs({0U});
4027 INST(14U, Opcode::Compare).CC(ConditionCode::CC_GE).b().Inputs(0U, 27U); // i < X
4028 INST(15U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(14U);
4029 }
4030 BASIC_BLOCK(3U, 6U, 3U)
4031 {
4032 INST(4U, Opcode::Phi).s32().Inputs(0U, 10U);
4033 INST(7U, Opcode::SaveState).Inputs(0U, 1U, 13U, 27U).SrcVregs({0U, 1U, 2U, 3U});
4034 INST(16U, Opcode::NullCheck).ref().Inputs(13U, 7U);
4035 INST(17U, Opcode::LenArray).s32().Inputs(16U);
4036 INST(8U, Opcode::BoundsCheck).s32().Inputs(17U, 4U, 7U);
4037 INST(9U, Opcode::StoreArray).s32().Inputs(16U, 8U, 0U); // a[i] = 0
4038 INST(10U, Opcode::Add).s32().Inputs(4U, 1U); // i++
4039 INST(5U, Opcode::Compare).CC(ConditionCode::CC_GE).b().Inputs(10U, 27U); // i < X
4040 INST(6U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(5U);
4041 }
4042 BASIC_BLOCK(6U, 1U)
4043 {
4044 INST(26U, Opcode::Phi).s32().Inputs(0U, 10U);
4045 INST(12U, Opcode::Return).s32().Inputs(26U);
4046 }
4047 }
4048 }
4049
TEST_F(ChecksEliminationTest,BugWithNullCheck)4050 TEST_F(ChecksEliminationTest, BugWithNullCheck)
4051 {
4052 BuildGraphBugWithNullCheck();
4053 ASSERT_TRUE(GetGraph()->RunPass<ChecksElimination>());
4054 auto graph1 = CreateEmptyGraph();
4055 GRAPH(graph1)
4056 {
4057 CONSTANT(0U, 0U); // initial
4058 CONSTANT(1U, 1U); // increment
4059 PARAMETER(2U, 1U).s32();
4060 BASIC_BLOCK(2U, 5U, 3U)
4061 {
4062 INST(43U, Opcode::SaveState).Inputs(0U).SrcVregs({0U});
4063 INST(44U, Opcode::LoadAndInitClass).ref().Inputs().TypeId(68U);
4064 INST(3U, Opcode::NewArray).ref().Inputs(44U, 2U, 43U);
4065 INST(20U, Opcode::SaveStateDeoptimize).Inputs(0U).SrcVregs({0U});
4066 INST(34U, Opcode::NOP);
4067 INST(22U, Opcode::LenArray).s32().Inputs(3U);
4068 INST(23U, Opcode::Compare).SrcType(DataType::INT32).CC(CC_LT).b().Inputs(22U, 2U); // len_array < X
4069 INST(24U, Opcode::DeoptimizeIf).DeoptimizeType(DeoptimizeType::BOUNDS_CHECK).Inputs(23U, 20U);
4070 INST(5U, Opcode::Compare).SrcType(DataType::INT32).CC(CC_GE).b().Inputs(0U, 2U); // 0 < X
4071 INST(6U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(5U);
4072 }
4073 BASIC_BLOCK(3U, 5U, 3U)
4074 {
4075 INST(4U, Opcode::Phi).s32().Inputs(0U, 10U);
4076 INST(7U, Opcode::SaveState).Inputs(0U, 1U, 3U, 2U).SrcVregs({0U, 1U, 2U, 3U});
4077 INST(15U, Opcode::NOP);
4078 INST(16U, Opcode::LenArray).s32().Inputs(3U);
4079 INST(8U, Opcode::NOP);
4080 INST(9U, Opcode::StoreArray).s32().Inputs(3U, 4U, 0U); // a[i] = 0
4081 INST(10U, Opcode::Add).s32().Inputs(4U, 1U); // i++
4082 INST(13U, Opcode::Compare).CC(CC_GE).b().Inputs(10U, 2U); // i < X
4083 INST(14U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(13U);
4084 }
4085 BASIC_BLOCK(5U, 1U)
4086 {
4087 INST(26U, Opcode::Phi).s32().Inputs(0U, 10U);
4088 INST(12U, Opcode::Return).s32().Inputs(26U);
4089 }
4090 }
4091 ASSERT_TRUE(GraphComparator().Compare(GetGraph(), graph1));
4092 }
4093
4094 // CC-OFFNXT(huge_method, G.FUN.01) graph creation
BuildGraphNullAndBoundsChecksNestedLoop()4095 void ChecksEliminationTest::BuildGraphNullAndBoundsChecksNestedLoop()
4096 {
4097 GRAPH(GetGraph())
4098 {
4099 PARAMETER(0U, 0U).ref();
4100 CONSTANT(3U, 0U);
4101 CONSTANT(9U, 4U);
4102 CONSTANT(32U, 1U);
4103
4104 // fill 2D array
4105 BASIC_BLOCK(2U, 3U)
4106 {
4107 INST(2U, Opcode::SaveStateDeoptimize).Inputs(0U).SrcVregs({0U});
4108 }
4109 BASIC_BLOCK(3U, 7U, 8U)
4110 {
4111 INST(5U, Opcode::Phi).s32().Inputs(3U, 33U);
4112 INST(4U, Opcode::Compare).CC(CC_LE).b().Inputs(9U, 5U);
4113 INST(11U, Opcode::IfImm).CC(CC_NE).Imm(0U).Inputs(4U);
4114 }
4115 BASIC_BLOCK(8U, 4U)
4116 {
4117 INST(38U, Opcode::SaveStateDeoptimize).Inputs(0U, 3U, 5U).SrcVregs({0U, 1U, 2U});
4118 }
4119 BASIC_BLOCK(4U, 6U, 5U)
4120 {
4121 INST(15U, Opcode::Phi).s32().Inputs(3U, 31U);
4122 INST(19U, Opcode::Compare).CC(CC_LE).b().Inputs(9U, 15U);
4123 INST(20U, Opcode::IfImm).CC(CC_NE).Imm(0U).Inputs(19U);
4124 }
4125 BASIC_BLOCK(5U, 4U)
4126 {
4127 INST(21U, Opcode::SaveState).Inputs(0U, 3U, 5U, 15U).SrcVregs({0U, 1U, 2U, 3U});
4128 INST(22U, Opcode::NullCheck).ref().Inputs(0U, 21U);
4129 INST(23U, Opcode::LenArray).s32().Inputs(22U);
4130 INST(24U, Opcode::BoundsCheck).s32().Inputs(23U, 5U, 21U);
4131 INST(25U, Opcode::LoadArray).ref().Inputs(22U, 24U);
4132
4133 INST(26U, Opcode::SaveState).Inputs(0U, 3U, 5U, 15U, 25U).SrcVregs({0U, 1U, 2U, 3U, 4U});
4134 INST(27U, Opcode::NullCheck).ref().Inputs(25U, 26U);
4135 INST(28U, Opcode::LenArray).s32().Inputs(27U);
4136 INST(29U, Opcode::BoundsCheck).s32().Inputs(28U, 15U, 26U);
4137 INST(30U, Opcode::StoreArray).s32().Inputs(27U, 29U, 5U); // a[i][j] = i
4138 INST(31U, Opcode::Add).s32().Inputs(15U, 32U);
4139 }
4140 BASIC_BLOCK(6U, 3U)
4141 {
4142 INST(33U, Opcode::Add).s32().Inputs(5U, 32U);
4143 }
4144 BASIC_BLOCK(7U, -1L)
4145 {
4146 INST(34U, Opcode::ReturnVoid).v0id();
4147 }
4148 }
4149 }
4150
4151 // CC-OFFNXT(huge_method, G.FUN.01) graph creation
TEST_F(ChecksEliminationTest,NullAndBoundsChecksNestedLoop)4152 TEST_F(ChecksEliminationTest, NullAndBoundsChecksNestedLoop)
4153 {
4154 BuildGraphNullAndBoundsChecksNestedLoop();
4155 ASSERT_TRUE(GetGraph()->RunPass<ChecksElimination>());
4156
4157 auto graph = CreateEmptyGraph();
4158 GRAPH(graph)
4159 {
4160 PARAMETER(0U, 0U).ref();
4161 CONSTANT(3U, 0U);
4162 CONSTANT(9U, 4U);
4163 CONSTANT(32U, 1U);
4164
4165 BASIC_BLOCK(2U, 3U)
4166 {
4167 INST(2U, Opcode::SaveStateDeoptimize).Inputs(0U).SrcVregs({0U});
4168 INST(35U, Opcode::NullCheck).ref().Inputs(0U, 2U).SetFlag(inst_flags::CAN_DEOPTIMIZE);
4169 INST(39U, Opcode::LenArray).s32().Inputs(35U);
4170 INST(44U, Opcode::Compare).b().SrcType(DataType::INT32).CC(CC_LT).Inputs(39U, 9U);
4171 INST(45U, Opcode::DeoptimizeIf).DeoptimizeType(DeoptimizeType::BOUNDS_CHECK).Inputs(44U, 2U);
4172 }
4173 BASIC_BLOCK(3U, 7U, 8U)
4174 {
4175 INST(5U, Opcode::Phi).s32().Inputs(3U, 33U);
4176 INST(4U, Opcode::Compare).CC(CC_LE).b().Inputs(9U, 5U);
4177 INST(11U, Opcode::IfImm).CC(CC_NE).Imm(0U).Inputs(4U);
4178 }
4179 BASIC_BLOCK(8U, 4U)
4180 {
4181 INST(38U, Opcode::SaveStateDeoptimize).Inputs(0U, 3U, 5U).SrcVregs({0U, 1U, 2U});
4182 // we could put DeoptimizeIf NULL_CHECK here, but this is suboptimal
4183 }
4184 BASIC_BLOCK(4U, 6U, 5U)
4185 {
4186 INST(15U, Opcode::Phi).s32().Inputs(3U, 31U);
4187 INST(19U, Opcode::Compare).CC(CC_LE).b().Inputs(9U, 15U);
4188 INST(20U, Opcode::IfImm).CC(CC_NE).Imm(0U).Inputs(19U);
4189 }
4190 BASIC_BLOCK(5U, 4U)
4191 {
4192 INST(21U, Opcode::SaveState).Inputs(0U, 3U, 5U, 15U).SrcVregs({0U, 1U, 2U, 3U});
4193 INST(22U, Opcode::NOP);
4194 INST(23U, Opcode::LenArray).s32().Inputs(35U);
4195 INST(24U, Opcode::NOP);
4196 INST(25U, Opcode::LoadArray).ref().Inputs(35U, 5U);
4197 INST(26U, Opcode::SaveState).Inputs(0U, 3U, 5U, 15U, 25U).SrcVregs({0U, 1U, 2U, 3U, 4U});
4198 INST(27U, Opcode::NullCheck).ref().Inputs(25U, 26U);
4199 INST(28U, Opcode::LenArray).s32().Inputs(27U);
4200 INST(29U, Opcode::BoundsCheck).s32().Inputs(28U, 15U, 26U);
4201 INST(30U, Opcode::StoreArray).s32().Inputs(27U, 29U, 5U); // a[i][j] = i
4202 INST(31U, Opcode::Add).s32().Inputs(15U, 32U);
4203 }
4204 BASIC_BLOCK(6U, 3U)
4205 {
4206 INST(33U, Opcode::Add).s32().Inputs(5U, 32U);
4207 }
4208 BASIC_BLOCK(7U, -1L)
4209 {
4210 INST(34U, Opcode::ReturnVoid).v0id();
4211 }
4212 }
4213 ASSERT_TRUE(GraphComparator().Compare(GetGraph(), graph));
4214 }
4215
BuildGraphLoopWithTwoPhi()4216 void ChecksEliminationTest::BuildGraphLoopWithTwoPhi()
4217 {
4218 GRAPH(GetGraph())
4219 {
4220 PARAMETER(0U, 0U).ref();
4221 PARAMETER(1U, 1U).s32();
4222 PARAMETER(2U, 2U).s32();
4223 CONSTANT(3U, 0U);
4224 CONSTANT(4U, 1U);
4225 BASIC_BLOCK(2U, 3U)
4226 {
4227 INST(5U, Opcode::SaveStateDeoptimize).Inputs(0U, 1U, 2U, 3U, 4U).SrcVregs({0U, 1U, 2U, 3U, 4U});
4228 INST(6U, Opcode::SaveState).Inputs(0U, 1U, 2U, 3U, 4U).SrcVregs({0U, 1U, 2U, 3U, 4U});
4229 INST(7U, Opcode::NullCheck).ref().Inputs(0U, 6U);
4230 INST(8U, Opcode::LoadObject).ref().Inputs(7U);
4231 }
4232 BASIC_BLOCK(3U, 4U, 5U)
4233 {
4234 INST(9U, Opcode::Phi).s32().Inputs(1U, 17U);
4235 INST(18U, Opcode::Phi).s32().Inputs(3U, 19U);
4236 INST(10U, Opcode::Compare).b().CC(CC_GE).Inputs(9U, 2U);
4237 INST(11U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(10U);
4238 }
4239 BASIC_BLOCK(5U, 3U)
4240 {
4241 INST(12U, Opcode::SaveState).Inputs(0U, 1U, 2U, 3U, 4U, 8U).SrcVregs({0U, 1U, 2U, 3U, 4U, 5U});
4242 INST(13U, Opcode::NullCheck).ref().Inputs(8U, 12U);
4243 INST(14U, Opcode::LenArray).s32().Inputs(13U);
4244 INST(15U, Opcode::BoundsCheck).s32().Inputs(14U, 9U, 12U);
4245 INST(16U, Opcode::LoadArray).s32().Inputs(13U, 15U);
4246 INST(17U, Opcode::Add).s32().Inputs(9U, 4U);
4247 INST(19U, Opcode::Add).s32().Inputs(18U, 16U);
4248 }
4249 BASIC_BLOCK(4U, -1L)
4250 {
4251 INST(20U, Opcode::Return).s32().Inputs(18U);
4252 }
4253 }
4254 }
4255
TEST_F(ChecksEliminationTest,LoopWithTwoPhi)4256 TEST_F(ChecksEliminationTest, LoopWithTwoPhi)
4257 {
4258 BuildGraphLoopWithTwoPhi();
4259 ASSERT_TRUE(GetGraph()->RunPass<ChecksElimination>());
4260 auto graph1 = CreateEmptyGraph();
4261 GRAPH(graph1)
4262 {
4263 PARAMETER(0U, 0U).ref();
4264 PARAMETER(1U, 1U).s32();
4265 PARAMETER(2U, 2U).s32();
4266 CONSTANT(3U, 0U);
4267 CONSTANT(4U, 1U);
4268 BASIC_BLOCK(2U, 3U)
4269 {
4270 INST(5U, Opcode::SaveStateDeoptimize).Inputs(0U, 1U, 2U, 3U, 4U).SrcVregs({0U, 1U, 2U, 3U, 4U});
4271 INST(6U, Opcode::SaveState).Inputs(0U, 1U, 2U, 3U, 4U).SrcVregs({0U, 1U, 2U, 3U, 4U});
4272 INST(7U, Opcode::NullCheck).ref().Inputs(0U, 6U);
4273 INST(8U, Opcode::LoadObject).ref().Inputs(7U);
4274 INST(22U, Opcode::NullCheck).ref().Inputs(8U, 6U).SetFlag(inst_flags::CAN_DEOPTIMIZE);
4275 }
4276 BASIC_BLOCK(3U, 4U, 5U)
4277 {
4278 INST(9U, Opcode::Phi).s32().Inputs(1U, 17U);
4279 INST(18U, Opcode::Phi).s32().Inputs(3U, 19U);
4280 INST(10U, Opcode::Compare).b().CC(CC_GE).Inputs(9U, 2U);
4281 INST(11U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(10U);
4282 }
4283 BASIC_BLOCK(5U, 3U)
4284 {
4285 INST(12U, Opcode::SaveState).Inputs(0U, 1U, 2U, 3U, 4U, 8U).SrcVregs({0U, 1U, 2U, 3U, 4U, 5U});
4286 INST(14U, Opcode::LenArray).s32().Inputs(22U);
4287 INST(15U, Opcode::BoundsCheck).s32().Inputs(14U, 9U, 12U);
4288 INST(16U, Opcode::LoadArray).s32().Inputs(22U, 15U);
4289 INST(17U, Opcode::Add).s32().Inputs(9U, 4U);
4290 INST(19U, Opcode::Add).s32().Inputs(18U, 16U);
4291 }
4292 BASIC_BLOCK(4U, -1L)
4293 {
4294 INST(20U, Opcode::Return).s32().Inputs(18U);
4295 }
4296 }
4297 ASSERT_TRUE(GraphComparator().Compare(GetGraph(), graph1));
4298 }
4299
BuildGraphLoopWithBigStepGE()4300 void ChecksEliminationTest::BuildGraphLoopWithBigStepGE()
4301 {
4302 GRAPH(GetGraph())
4303 {
4304 CONSTANT(0U, 0U);
4305 CONSTANT(1U, 4U); // increment
4306 CONSTANT(2U, 3U);
4307 PARAMETER(13U, 0U).ref(); // Array
4308 PARAMETER(27U, 1U).s32(); // X
4309 BASIC_BLOCK(2U, 3U)
4310 {
4311 INST(30U, Opcode::SaveStateDeoptimize).Inputs(0U).SrcVregs({0U});
4312 }
4313 BASIC_BLOCK(3U, 5U, 4U)
4314 {
4315 INST(4U, Opcode::Phi).s32().Inputs(27U, 10U);
4316 INST(14U, Opcode::Compare).CC(ConditionCode::CC_LT).b().Inputs(4U, 0U); // i >= 0
4317 INST(15U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(14U);
4318 }
4319 BASIC_BLOCK(4U, 3U)
4320 {
4321 INST(7U, Opcode::SaveState).Inputs(0U, 1U, 13U, 27U).SrcVregs({0U, 1U, 2U, 3U});
4322 INST(16U, Opcode::NullCheck).ref().Inputs(13U, 7U);
4323 INST(17U, Opcode::LenArray).s32().Inputs(16U);
4324 INST(8U, Opcode::BoundsCheck).s32().Inputs(17U, 4U, 7U);
4325 INST(9U, Opcode::StoreArray).s32().Inputs(16U, 8U, 4U); // a[i] = i
4326 INST(10U, Opcode::Sub).s32().Inputs(4U, 1U); // i -= 4
4327 }
4328 BASIC_BLOCK(5U, 1U)
4329 {
4330 INST(12U, Opcode::ReturnVoid).v0id();
4331 }
4332 }
4333 }
4334
TEST_F(ChecksEliminationTest,LoopWithBigStepGE)4335 TEST_F(ChecksEliminationTest, LoopWithBigStepGE)
4336 {
4337 BuildGraphLoopWithBigStepGE();
4338 ASSERT_TRUE(GetGraph()->RunPass<ChecksElimination>());
4339 auto graph1 = CreateEmptyGraph();
4340 GRAPH(graph1)
4341 {
4342 CONSTANT(0U, 0U);
4343 CONSTANT(1U, 4U); // increment
4344 CONSTANT(2U, 3U);
4345 PARAMETER(13U, 0U).ref(); // Array
4346 PARAMETER(27U, 1U).s32(); // X
4347 BASIC_BLOCK(2U, 3U)
4348 {
4349 INST(30U, Opcode::SaveStateDeoptimize).Inputs(0U).SrcVregs({0U});
4350 INST(33U, Opcode::NullCheck).ref().Inputs(13U, 30U).SetFlag(inst_flags::CAN_DEOPTIMIZE);
4351 INST(31U, Opcode::LenArray).s32().Inputs(33U);
4352 INST(36U, Opcode::Compare).SrcType(DataType::INT32).CC(CC_LE).b().Inputs(31U, 27U);
4353 // DeoptimizeIf len_array <= X
4354 INST(37U, Opcode::DeoptimizeIf).DeoptimizeType(DeoptimizeType::BOUNDS_CHECK).Inputs(36U, 30U);
4355 }
4356 BASIC_BLOCK(3U, 5U, 4U)
4357 {
4358 INST(4U, Opcode::Phi).s32().Inputs(27U, 10U);
4359 INST(14U, Opcode::Compare).CC(ConditionCode::CC_LT).b().Inputs(4U, 0U); // i >= 0
4360 INST(15U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(14U);
4361 }
4362 BASIC_BLOCK(4U, 3U)
4363 {
4364 INST(7U, Opcode::SaveState).Inputs(0U, 1U, 13U, 27U).SrcVregs({0U, 1U, 2U, 3U});
4365 INST(16U, Opcode::NOP);
4366 INST(17U, Opcode::LenArray).s32().Inputs(33U);
4367 INST(8U, Opcode::NOP);
4368 INST(9U, Opcode::StoreArray).s32().Inputs(33U, 4U, 4U); // a[i] = i
4369 INST(10U, Opcode::Sub).s32().Inputs(4U, 1U); // i -= 4
4370 }
4371 BASIC_BLOCK(5U, 1U)
4372 {
4373 INST(12U, Opcode::ReturnVoid).v0id();
4374 }
4375 }
4376 ASSERT_TRUE(GraphComparator().Compare(GetGraph(), graph1));
4377 }
4378
BuildGraphLoopWithBigStepLE()4379 void ChecksEliminationTest::BuildGraphLoopWithBigStepLE()
4380 {
4381 GRAPH(GetGraph())
4382 {
4383 CONSTANT(0U, 2U); // initial
4384 CONSTANT(1U, 8U); // increment
4385 CONSTANT(2U, 3U);
4386 PARAMETER(13U, 0U).ref(); // Array
4387 PARAMETER(27U, 1U).s32(); // X
4388 BASIC_BLOCK(2U, 6U, 3U)
4389 {
4390 INST(30U, Opcode::SaveStateDeoptimize).Inputs(0U).SrcVregs({0U});
4391 INST(14U, Opcode::Compare).CC(ConditionCode::CC_GT).b().Inputs(0U, 27U); // i <= X
4392 INST(15U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(14U);
4393 }
4394 BASIC_BLOCK(3U, 6U, 3U)
4395 {
4396 INST(4U, Opcode::Phi).s32().Inputs(0U, 10U);
4397 INST(7U, Opcode::SaveState).Inputs(0U, 1U, 13U, 27U).SrcVregs({0U, 1U, 2U, 3U});
4398 INST(16U, Opcode::NullCheck).ref().Inputs(13U, 7U);
4399 INST(17U, Opcode::LenArray).s32().Inputs(16U);
4400 INST(8U, Opcode::BoundsCheck).s32().Inputs(17U, 4U, 7U);
4401 INST(9U, Opcode::LoadArray).s32().Inputs(16U, 8U); // load a[i]
4402 INST(18U, Opcode::Add).s32().Inputs(4U, 2U);
4403 INST(19U, Opcode::BoundsCheck).s32().Inputs(17U, 18U, 7U);
4404 INST(20U, Opcode::StoreArray).s32().Inputs(16U, 19U, 9U); // a[i + 3] = a[i]
4405 INST(10U, Opcode::Add).s32().Inputs(4U, 1U); // i += 8
4406 INST(5U, Opcode::Compare).CC(ConditionCode::CC_GT).b().Inputs(10U, 27U); // i <= X
4407 INST(6U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(5U);
4408 }
4409 BASIC_BLOCK(6U, 1U)
4410 {
4411 INST(26U, Opcode::Phi).s32().Inputs(0U, 10U);
4412 INST(12U, Opcode::Return).s32().Inputs(26U);
4413 }
4414 }
4415 }
4416
4417 // CC-OFFNXT(huge_method, G.FUN.01) graph creation
TEST_F(ChecksEliminationTest,LoopWithBigStepLE)4418 TEST_F(ChecksEliminationTest, LoopWithBigStepLE)
4419 {
4420 BuildGraphLoopWithBigStepLE();
4421 ASSERT_TRUE(GetGraph()->RunPass<ChecksElimination>());
4422 auto graph1 = CreateEmptyGraph();
4423 GRAPH(graph1)
4424 {
4425 CONSTANT(0U, 2U); // initial
4426 CONSTANT(1U, 8U); // increment
4427 CONSTANT(2U, 3U);
4428 PARAMETER(13U, 0U).ref(); // Array
4429 PARAMETER(27U, 1U).s32(); // X
4430 CONSTANT(42U, 0x7ffffff7U);
4431
4432 BASIC_BLOCK(2U, 6U, 3U)
4433 {
4434 INST(30U, Opcode::SaveStateDeoptimize).Inputs(0U).SrcVregs({0U});
4435 INST(44U, Opcode::Compare).Inputs(42U, 27U).CC(CC_LT).b();
4436 INST(45U, Opcode::DeoptimizeIf).DeoptimizeType(DeoptimizeType::BOUNDS_CHECK).Inputs(44U, 30U);
4437 INST(33U, Opcode::NullCheck).ref().Inputs(13U, 30U).SetFlag(inst_flags::CAN_DEOPTIMIZE);
4438 INST(31U, Opcode::LenArray).s32().Inputs(33U);
4439 INST(36U, Opcode::Sub).s32().Inputs(27U, 0U);
4440 INST(37U, Opcode::Mod).s32().Inputs(36U, 1U);
4441 INST(38U, Opcode::Sub).s32().Inputs(27U, 37U);
4442 INST(39U, Opcode::Sub).s32().Inputs(31U, 2U);
4443 INST(40U, Opcode::Compare).b().CC(CC_LE).Inputs(39U, 38U);
4444 // DeoptimizeIf len - 3 <= X - (X - 2) % 8
4445 INST(41U, Opcode::DeoptimizeIf).DeoptimizeType(DeoptimizeType::BOUNDS_CHECK).Inputs(40U, 30U);
4446 INST(14U, Opcode::Compare).CC(ConditionCode::CC_GT).b().Inputs(0U, 27U); // i <= X
4447 INST(15U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(14U);
4448 }
4449 BASIC_BLOCK(3U, 6U, 3U)
4450 {
4451 INST(4U, Opcode::Phi).s32().Inputs(0U, 10U);
4452 INST(7U, Opcode::SaveState).Inputs(0U, 1U, 13U, 27U).SrcVregs({0U, 1U, 2U, 3U});
4453 INST(16U, Opcode::NOP);
4454 INST(17U, Opcode::LenArray).s32().Inputs(33U);
4455 INST(8U, Opcode::NOP);
4456 INST(9U, Opcode::LoadArray).s32().Inputs(33U, 4U); // load a[i]
4457 INST(18U, Opcode::Add).s32().Inputs(4U, 2U);
4458 INST(19U, Opcode::NOP);
4459 INST(20U, Opcode::StoreArray).s32().Inputs(33U, 18U, 9U); // a[i + 3] = a[i]
4460 INST(10U, Opcode::Add).s32().Inputs(4U, 1U); // i += 8
4461 INST(5U, Opcode::Compare).CC(ConditionCode::CC_GT).b().Inputs(10U, 27U); // i <= X
4462 INST(6U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(5U);
4463 }
4464 BASIC_BLOCK(6U, 1U)
4465 {
4466 INST(26U, Opcode::Phi).s32().Inputs(0U, 10U);
4467 INST(12U, Opcode::Return).s32().Inputs(26U);
4468 }
4469 }
4470 ASSERT_TRUE(GraphComparator().Compare(GetGraph(), graph1));
4471 }
4472
BuildGraphLoopWithBigStepLT()4473 void ChecksEliminationTest::BuildGraphLoopWithBigStepLT()
4474 {
4475 GRAPH(GetGraph())
4476 {
4477 CONSTANT(0U, 2U); // initial
4478 CONSTANT(1U, 8U); // increment
4479 CONSTANT(2U, 3U);
4480 PARAMETER(13U, 0U).ref(); // Array
4481 PARAMETER(27U, 1U).s32(); // X
4482 BASIC_BLOCK(2U, 6U, 3U)
4483 {
4484 INST(30U, Opcode::SaveStateDeoptimize).Inputs(0U).SrcVregs({0U});
4485 INST(14U, Opcode::Compare).CC(ConditionCode::CC_GE).b().Inputs(0U, 27U); // i < X
4486 INST(15U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(14U);
4487 }
4488 BASIC_BLOCK(3U, 6U, 3U)
4489 {
4490 INST(4U, Opcode::Phi).s32().Inputs(0U, 10U);
4491 INST(7U, Opcode::SaveState).Inputs(0U, 1U, 13U, 27U).SrcVregs({0U, 1U, 2U, 3U});
4492 INST(16U, Opcode::NullCheck).ref().Inputs(13U, 7U);
4493 INST(17U, Opcode::LenArray).s32().Inputs(16U);
4494 INST(8U, Opcode::BoundsCheck).s32().Inputs(17U, 4U, 7U);
4495 INST(9U, Opcode::LoadArray).s32().Inputs(16U, 8U); // load a[i]
4496 INST(18U, Opcode::Add).s32().Inputs(4U, 2U);
4497 INST(19U, Opcode::BoundsCheck).s32().Inputs(17U, 18U, 7U);
4498 INST(20U, Opcode::StoreArray).s32().Inputs(16U, 19U, 9U); // a[i + 3] = a[i]
4499 INST(10U, Opcode::Add).s32().Inputs(4U, 1U); // i += 8
4500 INST(5U, Opcode::Compare).CC(ConditionCode::CC_GE).b().Inputs(10U, 27U); // i < X
4501 INST(6U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(5U);
4502 }
4503 BASIC_BLOCK(6U, 1U)
4504 {
4505 INST(26U, Opcode::Phi).s32().Inputs(0U, 10U);
4506 INST(12U, Opcode::Return).s32().Inputs(26U);
4507 }
4508 }
4509 }
4510
4511 // CC-OFFNXT(huge_method, G.FUN.01) graph creation
TEST_F(ChecksEliminationTest,LoopWithBigStepLT)4512 TEST_F(ChecksEliminationTest, LoopWithBigStepLT)
4513 {
4514 BuildGraphLoopWithBigStepLT();
4515 ASSERT_TRUE(GetGraph()->RunPass<ChecksElimination>());
4516 auto graph1 = CreateEmptyGraph();
4517 GRAPH(graph1)
4518 {
4519 CONSTANT(0U, 2U); // initial
4520 CONSTANT(1U, 8U); // increment
4521 CONSTANT(2U, 3U);
4522 PARAMETER(13U, 0U).ref(); // Array
4523 PARAMETER(27U, 1U).s32(); // X
4524 CONSTANT(43U, 1U);
4525 CONSTANT(42U, 0x7ffffff8U);
4526
4527 BASIC_BLOCK(2U, 6U, 3U)
4528 {
4529 INST(30U, Opcode::SaveStateDeoptimize).Inputs(0U).SrcVregs({0U});
4530 INST(44U, Opcode::Compare).Inputs(42U, 27U).CC(CC_LT).b();
4531 INST(45U, Opcode::DeoptimizeIf).DeoptimizeType(DeoptimizeType::BOUNDS_CHECK).Inputs(44U, 30U);
4532 INST(33U, Opcode::NullCheck).ref().Inputs(13U, 30U).SetFlag(inst_flags::CAN_DEOPTIMIZE);
4533 INST(31U, Opcode::LenArray).s32().Inputs(33U);
4534 INST(35U, Opcode::Add).s32().Inputs(0U, 43U);
4535 INST(36U, Opcode::Sub).s32().Inputs(27U, 35U);
4536 INST(37U, Opcode::Mod).s32().Inputs(36U, 1U);
4537 INST(38U, Opcode::Sub).s32().Inputs(27U, 37U);
4538 INST(39U, Opcode::Sub).s32().Inputs(31U, 2U);
4539 INST(40U, Opcode::Compare).b().CC(CC_LT).Inputs(39U, 38U);
4540 // DeoptimizeIf len - 3 < X - (X - (2 + 1)) % 8
4541 INST(41U, Opcode::DeoptimizeIf).DeoptimizeType(DeoptimizeType::BOUNDS_CHECK).Inputs(40U, 30U);
4542 INST(14U, Opcode::Compare).CC(ConditionCode::CC_GE).b().Inputs(0U, 27U); // i < X
4543 INST(15U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(14U);
4544 }
4545 BASIC_BLOCK(3U, 6U, 3U)
4546 {
4547 INST(4U, Opcode::Phi).s32().Inputs(0U, 10U);
4548 INST(7U, Opcode::SaveState).Inputs(0U, 1U, 13U, 27U).SrcVregs({0U, 1U, 2U, 3U});
4549 INST(16U, Opcode::NOP);
4550 INST(17U, Opcode::LenArray).s32().Inputs(33U);
4551 INST(8U, Opcode::NOP);
4552 INST(9U, Opcode::LoadArray).s32().Inputs(33U, 4U); // load a[i]
4553 INST(18U, Opcode::Add).s32().Inputs(4U, 2U);
4554 INST(19U, Opcode::NOP);
4555 INST(20U, Opcode::StoreArray).s32().Inputs(33U, 18U, 9U); // a[i + 3] = a[i]
4556 INST(10U, Opcode::Add).s32().Inputs(4U, 1U); // i += 8
4557 INST(5U, Opcode::Compare).CC(ConditionCode::CC_GE).b().Inputs(10U, 27U); // i < X
4558 INST(6U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(5U);
4559 }
4560 BASIC_BLOCK(6U, 1U)
4561 {
4562 INST(26U, Opcode::Phi).s32().Inputs(0U, 10U);
4563 INST(12U, Opcode::Return).s32().Inputs(26U);
4564 }
4565 }
4566 ASSERT_TRUE(GraphComparator().Compare(GetGraph(), graph1));
4567 }
4568
4569 // CC-OFFNXT(huge_method, G.FUN.01) graph creation
BuildGraphLoopWithBoundsCheckUnderIfGE()4570 void ChecksEliminationTest::BuildGraphLoopWithBoundsCheckUnderIfGE()
4571 {
4572 GRAPH(GetGraph())
4573 {
4574 CONSTANT(0U, 0U); // initial
4575 CONSTANT(1U, 1U); // increment
4576 CONSTANT(3U, 3U);
4577 PARAMETER(2U, 0U).ref(); // array
4578 PARAMETER(4U, 1U).s32(); // X
4579
4580 BASIC_BLOCK(2U, 3U)
4581 {
4582 INST(5U, Opcode::SaveState).Inputs(0U, 1U, 2U).SrcVregs({0U, 1U, 2U});
4583 INST(6U, Opcode::NullCheck).ref().Inputs(2U, 5U);
4584 INST(7U, Opcode::LenArray).s32().Inputs(6U);
4585
4586 INST(30U, Opcode::SaveStateDeoptimize).Inputs(0U).SrcVregs({0U});
4587 }
4588
4589 BASIC_BLOCK(3U, 4U, 8U)
4590 {
4591 INST(8U, Opcode::Phi).s32().Inputs(0U, 24U); // i
4592 INST(25U, Opcode::Phi).s32().Inputs(0U, 23U); // sum
4593 INST(9U, Opcode::Compare).CC(CC_LT).b().Inputs(8U, 4U); // i < X
4594 INST(10U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(9U);
4595 }
4596 BASIC_BLOCK(4U, 5U, 6U)
4597 {
4598 INST(14U, Opcode::Compare).CC(CC_LE).b().Inputs(3U, 8U); // 3 <= i
4599 INST(15U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(14U);
4600 }
4601 BASIC_BLOCK(5U, 7U)
4602 {
4603 INST(16U, Opcode::Sub).s32().Inputs(8U, 3U);
4604 INST(27U, Opcode::SaveState).Inputs(0U, 1U, 2U, 3U).SrcVregs({0U, 1U, 2U, 3U});
4605 INST(17U, Opcode::BoundsCheck).s32().Inputs(7U, 16U, 27U);
4606 INST(18U, Opcode::LoadArray).s32().Inputs(6U, 17U); // a[i - 3]
4607 }
4608 BASIC_BLOCK(6U, 7U)
4609 {
4610 INST(19U, Opcode::SaveState).Inputs(0U, 1U, 2U, 3U).SrcVregs({0U, 1U, 2U, 3U});
4611 INST(20U, Opcode::BoundsCheck).s32().Inputs(7U, 8U, 19U);
4612 INST(21U, Opcode::LoadArray).s32().Inputs(6U, 20U); // a[i]
4613 }
4614 BASIC_BLOCK(7U, 3U)
4615 {
4616 INST(22U, Opcode::Phi).s32().Inputs(18U, 21U);
4617 INST(23U, Opcode::Add).s32().Inputs(25U, 22U);
4618 INST(24U, Opcode::Add).s32().Inputs(8U, 1U);
4619 }
4620 BASIC_BLOCK(8U, 1U)
4621 {
4622 INST(26U, Opcode::Return).s32().Inputs(25U);
4623 }
4624 }
4625 }
4626
4627 // Lower bound is correct in each branch based on BoundsAnalysis, build deoptimize only for upper bound
4628 // CC-OFFNXT(huge_method, G.FUN.01) graph creation
TEST_F(ChecksEliminationTest,LoopWithBoundsCheckUnderIfGE)4629 TEST_F(ChecksEliminationTest, LoopWithBoundsCheckUnderIfGE)
4630 {
4631 BuildGraphLoopWithBoundsCheckUnderIfGE();
4632 ASSERT_TRUE(GetGraph()->RunPass<ChecksElimination>());
4633 auto graph1 = CreateEmptyGraph();
4634 GRAPH(graph1)
4635 {
4636 CONSTANT(0U, 0U); // initial
4637 CONSTANT(1U, 1U); // increment
4638 CONSTANT(3U, 3U);
4639 PARAMETER(2U, 0U).ref(); // array
4640 PARAMETER(4U, 1U).s32(); // X
4641
4642 BASIC_BLOCK(2U, 3U)
4643 {
4644 INST(5U, Opcode::SaveState).Inputs(0U, 1U, 2U).SrcVregs({0U, 1U, 2U});
4645 INST(6U, Opcode::NullCheck).ref().Inputs(2U, 5U);
4646 INST(7U, Opcode::LenArray).s32().Inputs(6U);
4647
4648 INST(30U, Opcode::SaveStateDeoptimize).Inputs(0U).SrcVregs({0U});
4649 INST(31U, Opcode::Compare).b().CC(CC_LT).Inputs(7U, 4U);
4650 INST(32U, Opcode::DeoptimizeIf).DeoptimizeType(DeoptimizeType::BOUNDS_CHECK).Inputs(31U, 30U);
4651 }
4652
4653 BASIC_BLOCK(3U, 4U, 8U)
4654 {
4655 INST(8U, Opcode::Phi).s32().Inputs(0U, 24U); // i
4656 INST(25U, Opcode::Phi).s32().Inputs(0U, 23U); // sum
4657 INST(9U, Opcode::Compare).CC(CC_LT).b().Inputs(8U, 4U); // i < X
4658 INST(10U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(9U);
4659 }
4660 BASIC_BLOCK(4U, 5U, 6U)
4661 {
4662 INST(14U, Opcode::Compare).CC(CC_LE).b().Inputs(3U, 8U); // 3 <= i
4663 INST(15U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(14U);
4664 }
4665 BASIC_BLOCK(5U, 7U)
4666 {
4667 INST(16U, Opcode::Sub).s32().Inputs(8U, 3U);
4668 INST(27U, Opcode::SaveState).Inputs(0U, 1U, 2U, 3U).SrcVregs({0U, 1U, 2U, 3U});
4669 INST(17U, Opcode::NOP);
4670 INST(18U, Opcode::LoadArray).s32().Inputs(6U, 16U); // a[i - 3]
4671 }
4672 BASIC_BLOCK(6U, 7U)
4673 {
4674 INST(19U, Opcode::SaveState).Inputs(0U, 1U, 2U, 3U).SrcVregs({0U, 1U, 2U, 3U});
4675 INST(20U, Opcode::NOP);
4676 INST(21U, Opcode::LoadArray).s32().Inputs(6U, 8U); // a[i]
4677 }
4678 BASIC_BLOCK(7U, 3U)
4679 {
4680 INST(22U, Opcode::Phi).s32().Inputs(18U, 21U);
4681 INST(23U, Opcode::Add).s32().Inputs(25U, 22U);
4682 INST(24U, Opcode::Add).s32().Inputs(8U, 1U);
4683 }
4684 BASIC_BLOCK(8U, 1U)
4685 {
4686 INST(26U, Opcode::Return).s32().Inputs(25U);
4687 }
4688 }
4689 ASSERT_TRUE(GraphComparator().Compare(GetGraph(), graph1));
4690 }
4691
BuildGraphLoopWithBoundsCheckUnderIfLT()4692 void ChecksEliminationTest::BuildGraphLoopWithBoundsCheckUnderIfLT()
4693 {
4694 GRAPH(GetGraph())
4695 {
4696 CONSTANT(0U, 0U); // initial
4697 CONSTANT(1U, 1U); // increment
4698 CONSTANT(3U, 3U);
4699 PARAMETER(2U, 0U).ref(); // array
4700 PARAMETER(4U, 1U).s32(); // X
4701
4702 BASIC_BLOCK(2U, 3U, 8U)
4703 {
4704 INST(5U, Opcode::SaveState).Inputs(0U, 1U, 2U).SrcVregs({0U, 1U, 2U});
4705 INST(6U, Opcode::NullCheck).ref().Inputs(2U, 5U);
4706 INST(7U, Opcode::LenArray).s32().Inputs(6U);
4707
4708 INST(30U, Opcode::SaveStateDeoptimize).Inputs(0U).SrcVregs({0U});
4709 INST(31U, Opcode::Compare).CC(CC_LT).b().Inputs(4U, 7U); // X < len_array
4710 INST(32U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(31U);
4711 }
4712
4713 BASIC_BLOCK(3U, 4U, 8U)
4714 {
4715 INST(8U, Opcode::Phi).s32().Inputs(4U, 24U); // i = X
4716 INST(25U, Opcode::Phi).s32().Inputs(0U, 23U); // sum
4717 INST(9U, Opcode::Compare).CC(CC_LT).b().Inputs(8U, 7U); // i < len_array
4718 INST(10U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(9U);
4719 }
4720 BASIC_BLOCK(4U, 5U, 6U)
4721 {
4722 INST(13U, Opcode::Add).s32().Inputs(8U, 3U);
4723 INST(14U, Opcode::Compare).CC(CC_LT).b().Inputs(13U, 7U); // i + 3 < len_array
4724 INST(15U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(14U);
4725 }
4726 BASIC_BLOCK(5U, 6U)
4727 {
4728 INST(20U, Opcode::SaveState).Inputs(0U, 1U, 2U, 3U).SrcVregs({0U, 1U, 2U, 3U});
4729 INST(17U, Opcode::BoundsCheck).s32().Inputs(7U, 13U, 20U);
4730 INST(18U, Opcode::LoadArray).s32().Inputs(6U, 17U);
4731 INST(19U, Opcode::Mul).s32().Inputs(8U, 18U); // i * a[i + 3]
4732 }
4733 BASIC_BLOCK(6U, 3U)
4734 {
4735 INST(22U, Opcode::Phi).s32().Inputs(8U, 19U);
4736 INST(23U, Opcode::Add).s32().Inputs(25U, 22U);
4737 INST(24U, Opcode::Add).s32().Inputs(8U, 1U);
4738 }
4739 BASIC_BLOCK(8U, 1U)
4740 {
4741 INST(26U, Opcode::Phi).s32().Inputs(0U, 25U);
4742 INST(27U, Opcode::Return).s32().Inputs(26U);
4743 }
4744 }
4745 }
4746
4747 // Upper bound is correct in each branch based on BoundsAnalysis, build deoptimize only for lower bound
4748 // CC-OFFNXT(huge_method, G.FUN.01) graph creation
TEST_F(ChecksEliminationTest,LoopWithBoundsCheckUnderIfLT)4749 TEST_F(ChecksEliminationTest, LoopWithBoundsCheckUnderIfLT)
4750 {
4751 BuildGraphLoopWithBoundsCheckUnderIfLT();
4752 ASSERT_TRUE(GetGraph()->RunPass<ChecksElimination>());
4753 auto graph1 = CreateEmptyGraph();
4754 GRAPH(graph1)
4755 {
4756 CONSTANT(0U, 0U); // initial
4757 CONSTANT(1U, 1U); // increment
4758 CONSTANT(3U, 3U);
4759 PARAMETER(2U, 0U).ref(); // array
4760 PARAMETER(4U, 1U).s32(); // X
4761
4762 BASIC_BLOCK(2U, 3U, 8U)
4763 {
4764 INST(5U, Opcode::SaveState).Inputs(0U, 1U, 2U).SrcVregs({0U, 1U, 2U});
4765 INST(6U, Opcode::NullCheck).ref().Inputs(2U, 5U);
4766 INST(7U, Opcode::LenArray).s32().Inputs(6U);
4767
4768 INST(30U, Opcode::SaveStateDeoptimize).Inputs(0U).SrcVregs({0U});
4769 INST(33U, Opcode::Add).s32().Inputs(4U, 3U);
4770 INST(34U, Opcode::Compare).b().CC(CC_LT).Inputs(33U, 0U); // X + 3 < 0
4771 INST(35U, Opcode::DeoptimizeIf).DeoptimizeType(DeoptimizeType::BOUNDS_CHECK).Inputs(34U, 30U);
4772 INST(31U, Opcode::Compare).CC(CC_LT).b().Inputs(4U, 7U); // X < len_array
4773 INST(32U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(31U);
4774 }
4775 BASIC_BLOCK(3U, 4U, 8U)
4776 {
4777 INST(8U, Opcode::Phi).s32().Inputs(4U, 24U); // i = X
4778 INST(25U, Opcode::Phi).s32().Inputs(0U, 23U); // sum
4779 INST(9U, Opcode::Compare).CC(CC_LT).b().Inputs(8U, 7U); // i < len_array
4780 INST(10U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(9U);
4781 }
4782 BASIC_BLOCK(4U, 5U, 6U)
4783 {
4784 INST(13U, Opcode::Add).s32().Inputs(8U, 3U);
4785 INST(14U, Opcode::Compare).CC(CC_LT).b().Inputs(13U, 7U); // i + 3 < len_array
4786 INST(15U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(14U);
4787 }
4788 BASIC_BLOCK(5U, 6U)
4789 {
4790 INST(20U, Opcode::SaveState).Inputs(0U, 1U, 2U, 3U).SrcVregs({0U, 1U, 2U, 3U});
4791 INST(17U, Opcode::NOP);
4792 INST(18U, Opcode::LoadArray).s32().Inputs(6U, 13U);
4793 INST(19U, Opcode::Mul).s32().Inputs(8U, 18U); // i * a[i + 3]
4794 }
4795 BASIC_BLOCK(6U, 3U)
4796 {
4797 INST(22U, Opcode::Phi).s32().Inputs(8U, 19U);
4798 INST(23U, Opcode::Add).s32().Inputs(25U, 22U);
4799 INST(24U, Opcode::Add).s32().Inputs(8U, 1U);
4800 }
4801 BASIC_BLOCK(8U, 1U)
4802 {
4803 INST(26U, Opcode::Phi).s32().Inputs(0U, 25U);
4804 INST(27U, Opcode::Return).s32().Inputs(26U);
4805 }
4806 }
4807 ASSERT_TRUE(GraphComparator().Compare(GetGraph(), graph1));
4808 }
4809
TEST_F(ChecksEliminationTest,DeoptTest)4810 TEST_F(ChecksEliminationTest, DeoptTest)
4811 {
4812 GRAPH(GetGraph())
4813 {
4814 CONSTANT(0U, 0U);
4815 CONSTANT(1U, nullptr);
4816 BASIC_BLOCK(2U, 1U)
4817 {
4818 INST(2U, Opcode::SaveState).Inputs(0U, 1U).SrcVregs({0U, 1U});
4819 INST(3U, Opcode::NullCheck).ref().Inputs(1U, 2U);
4820 INST(4U, Opcode::ZeroCheck).s32().Inputs(0U, 2U);
4821 INST(5U, Opcode::ReturnVoid).v0id();
4822 }
4823 }
4824 ASSERT_TRUE(GetGraph()->RunPass<ChecksElimination>());
4825 auto graph = CreateEmptyGraph();
4826 GRAPH(graph)
4827 {
4828 CONSTANT(0U, 0U);
4829 CONSTANT(1U, nullptr);
4830 BASIC_BLOCK(2U, 1U)
4831 {
4832 INST(2U, Opcode::SaveState).Inputs(0U, 1U).SrcVregs({0U, 1U});
4833 INST(3U, Opcode::Deoptimize).DeoptimizeType(DeoptimizeType::NULL_CHECK).Inputs(2U);
4834 }
4835 }
4836 }
4837
TEST_F(ChecksEliminationTest,CheckCastEqualInputs)4838 TEST_F(ChecksEliminationTest, CheckCastEqualInputs)
4839 {
4840 // Check Elimination for CheckCast is applied.
4841 GRAPH(GetGraph())
4842 {
4843 PARAMETER(0U, 0U).ref();
4844 BASIC_BLOCK(2U, 1U)
4845 {
4846 INST(1U, Opcode::SaveState).Inputs(0U).SrcVregs({0U});
4847 INST(2U, Opcode::LoadClass).ref().Inputs(1U).TypeId(1U);
4848 INST(3U, Opcode::CheckCast).TypeId(1U).Inputs(0U, 2U, 1U);
4849 INST(4U, Opcode::CheckCast).TypeId(1U).Inputs(0U, 2U, 1U);
4850 INST(5U, Opcode::Return).ref().Inputs(0U);
4851 }
4852 }
4853 ASSERT_TRUE(GetGraph()->RunPass<ChecksElimination>());
4854 auto graph = CreateEmptyGraph();
4855 GRAPH(graph)
4856 {
4857 PARAMETER(0U, 0U).ref();
4858 BASIC_BLOCK(2U, 1U)
4859 {
4860 INST(1U, Opcode::SaveState).Inputs(0U).SrcVregs({0U});
4861 INST(2U, Opcode::LoadClass).ref().Inputs(1U).TypeId(1U);
4862 INST(3U, Opcode::CheckCast).TypeId(1U).Inputs(0U, 2U, 1U);
4863 INST(4U, Opcode::NOP);
4864 INST(5U, Opcode::Return).ref().Inputs(0U);
4865 }
4866 }
4867 ASSERT_TRUE(GraphComparator().Compare(GetGraph(), graph));
4868 }
4869
TEST_F(ChecksEliminationTest,CheckCastDifferentInputs)4870 TEST_F(ChecksEliminationTest, CheckCastDifferentInputs)
4871 {
4872 // Check Elimination for CheckCast is not applied.
4873 GRAPH(GetGraph())
4874 {
4875 PARAMETER(0U, 0U).ref();
4876 PARAMETER(1U, 1U).ref();
4877 BASIC_BLOCK(2U, 1U)
4878 {
4879 INST(8U, Opcode::SaveState).Inputs(0U).SrcVregs({0U});
4880 INST(2U, Opcode::LoadClass).ref().Inputs(8U).TypeId(1U);
4881 INST(3U, Opcode::LoadClass).ref().Inputs(8U).TypeId(2U);
4882 INST(4U, Opcode::CheckCast).TypeId(1U).Inputs(0U, 2U, 8U);
4883 INST(5U, Opcode::CheckCast).TypeId(2U).Inputs(0U, 3U, 8U);
4884 INST(6U, Opcode::CheckCast).TypeId(2U).Inputs(1U, 3U, 8U);
4885 INST(7U, Opcode::Return).ref().Inputs(0U);
4886 }
4887 }
4888 auto clone = GraphCloner(GetGraph(), GetGraph()->GetAllocator(), GetGraph()->GetLocalAllocator()).CloneGraph();
4889 ASSERT_FALSE(GetGraph()->RunPass<ChecksElimination>());
4890 ASSERT_TRUE(GraphComparator().Compare(GetGraph(), clone));
4891 }
4892
BuildGraphCheckCastAfterIsInstance()4893 void ChecksEliminationTest::BuildGraphCheckCastAfterIsInstance()
4894 {
4895 GRAPH(GetGraph())
4896 {
4897 PARAMETER(0U, 0U).ref();
4898 CONSTANT(9U, nullptr);
4899 BASIC_BLOCK(2U, 3U, 4U)
4900 {
4901 INST(1U, Opcode::SaveState).Inputs(0U).SrcVregs({0U});
4902 INST(2U, Opcode::LoadClass).ref().Inputs(1U).TypeId(1U);
4903 INST(3U, Opcode::IsInstance).b().Inputs(0U, 2U).TypeId(1U);
4904 INST(4U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_EQ).Imm(0U).Inputs(3U);
4905 }
4906
4907 BASIC_BLOCK(4U, 1U)
4908 {
4909 INST(5U, Opcode::CheckCast).TypeId(1U).Inputs(0U, 2U, 1U);
4910 INST(6U, Opcode::LoadClass).ref().Inputs(1U).TypeId(2U);
4911 INST(7U, Opcode::CheckCast).TypeId(2U).Inputs(0U, 2U, 1U);
4912 INST(8U, Opcode::Return).ref().Inputs(0U);
4913 }
4914
4915 BASIC_BLOCK(3U, 1U)
4916 {
4917 INST(10U, Opcode::Return).ref().Inputs(9U);
4918 }
4919 }
4920 }
4921
TEST_F(ChecksEliminationTest,CheckCastAfterIsInstance)4922 TEST_F(ChecksEliminationTest, CheckCastAfterIsInstance)
4923 {
4924 // CheckCast after successful IsInstance can be removed.
4925 BuildGraphCheckCastAfterIsInstance();
4926
4927 ASSERT_TRUE(GetGraph()->RunPass<ChecksElimination>());
4928 ASSERT_TRUE(INS(7U).CastToCheckCast()->GetOmitNullCheck());
4929
4930 auto graph = CreateEmptyGraph();
4931 GRAPH(graph)
4932 {
4933 PARAMETER(0U, 0U).ref();
4934 CONSTANT(9U, nullptr);
4935 BASIC_BLOCK(2U, 3U, 4U)
4936 {
4937 INST(1U, Opcode::SaveState).Inputs(0U).SrcVregs({0U});
4938 INST(2U, Opcode::LoadClass).ref().Inputs(1U).TypeId(1U);
4939 INST(3U, Opcode::IsInstance).b().Inputs(0U, 2U).TypeId(1U);
4940 INST(4U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_EQ).Imm(0U).Inputs(3U);
4941 }
4942
4943 BASIC_BLOCK(4U, 1U)
4944 {
4945 INST(5U, Opcode::NOP);
4946 INST(6U, Opcode::LoadClass).ref().Inputs(1U).TypeId(2U);
4947 INST(7U, Opcode::CheckCast).TypeId(2U).Inputs(0U, 2U, 1U);
4948 INST(8U, Opcode::Return).ref().Inputs(0U);
4949 }
4950
4951 BASIC_BLOCK(3U, 1U)
4952 {
4953 INST(10U, Opcode::Return).ref().Inputs(9U);
4954 }
4955 }
4956 ASSERT_TRUE(GraphComparator().Compare(GetGraph(), graph));
4957 }
4958
TEST_F(ChecksEliminationTest,CheckCastAfterIsInstanceTriangleCase)4959 TEST_F(ChecksEliminationTest, CheckCastAfterIsInstanceTriangleCase)
4960 {
4961 // CheckCast cannot be removed because dominating IsInstance can be false.
4962 GRAPH(GetGraph())
4963 {
4964 PARAMETER(0U, 0U).ref();
4965 CONSTANT(9U, nullptr);
4966 BASIC_BLOCK(2U, 3U, 4U)
4967 {
4968 INST(1U, Opcode::SaveState).Inputs(0U).SrcVregs({0U});
4969 INST(2U, Opcode::LoadClass).ref().Inputs(1U).TypeId(1U);
4970 INST(3U, Opcode::IsInstance).b().Inputs(0U, 2U).TypeId(1U);
4971 INST(4U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_EQ).Imm(0U).Inputs(3U);
4972 }
4973
4974 BASIC_BLOCK(3U, 4U) {}
4975
4976 BASIC_BLOCK(4U, 1U)
4977 {
4978 INST(5U, Opcode::Phi).ref().Inputs({{2U, 9U}, {3U, 0U}});
4979 INST(6U, Opcode::CheckCast).TypeId(1U).Inputs(0U, 2U, 1U);
4980 INST(7U, Opcode::Return).ref().Inputs(5U);
4981 }
4982 }
4983 auto clone = GraphCloner(GetGraph(), GetGraph()->GetAllocator(), GetGraph()->GetLocalAllocator()).CloneGraph();
4984 ASSERT_FALSE(GetGraph()->RunPass<ChecksElimination>());
4985 ASSERT_TRUE(GraphComparator().Compare(GetGraph(), clone));
4986 }
4987
TEST_F(ChecksEliminationTest,CheckCastAfterIsInstanceDiamondCase)4988 TEST_F(ChecksEliminationTest, CheckCastAfterIsInstanceDiamondCase)
4989 {
4990 // CheckCast cannot be removed because dominating IsInstance can be false.
4991 GRAPH(GetGraph())
4992 {
4993 PARAMETER(0U, 0U).ref();
4994 CONSTANT(9U, nullptr);
4995 BASIC_BLOCK(2U, 3U, 4U)
4996 {
4997 INST(1U, Opcode::SaveState).Inputs(0U).SrcVregs({0U});
4998 INST(2U, Opcode::LoadClass).ref().Inputs(1U).TypeId(1U);
4999 INST(3U, Opcode::IsInstance).b().Inputs(0U, 2U).TypeId(1U);
5000 INST(4U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_EQ).Imm(0U).Inputs(3U);
5001 }
5002
5003 BASIC_BLOCK(3U, 5U) {}
5004
5005 BASIC_BLOCK(4U, 5U) {}
5006
5007 BASIC_BLOCK(5U, 1U)
5008 {
5009 INST(5U, Opcode::Phi).ref().Inputs({{3U, 9U}, {4U, 0U}});
5010 INST(6U, Opcode::CheckCast).TypeId(1U).Inputs(0U, 2U, 1U);
5011 INST(7U, Opcode::Return).ref().Inputs(5U);
5012 }
5013 }
5014 auto clone = GraphCloner(GetGraph(), GetGraph()->GetAllocator(), GetGraph()->GetLocalAllocator()).CloneGraph();
5015 ASSERT_FALSE(GetGraph()->RunPass<ChecksElimination>());
5016 ASSERT_TRUE(GraphComparator().Compare(GetGraph(), clone));
5017 }
5018
BuildHoistCheckCastGraph()5019 void ChecksEliminationTest::BuildHoistCheckCastGraph()
5020 {
5021 GRAPH(GetGraph())
5022 {
5023 CONSTANT(0U, 0U); // initial
5024 CONSTANT(1U, 1U); // increment
5025 CONSTANT(2U, 10U);
5026 PARAMETER(3U, 0U).ref();
5027 BASIC_BLOCK(2U, 3U, 5U)
5028 {
5029 INST(21U, Opcode::SaveState).Inputs(0U, 1U, 2U, 3U).SrcVregs({0U, 1U, 2U, 3U});
5030 INST(22U, Opcode::LoadClass).ref().Inputs(21U).TypeId(1U);
5031 INST(20U, Opcode::SaveStateDeoptimize).Inputs(0U, 1U, 2U, 3U).SrcVregs({0U, 1U, 2U, 3U});
5032 INST(5U, Opcode::Compare).SrcType(DataType::INT32).CC(CC_LT).b().Inputs(0U, 2U); // 0 < 10
5033 INST(6U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(5U);
5034 }
5035 BASIC_BLOCK(3U, 3U, 5U)
5036 {
5037 INST(4U, Opcode::Phi).s32().Inputs(0U, 10U); // i
5038 INST(7U, Opcode::SaveState).Inputs(0U, 1U, 2U, 3U).SrcVregs({0U, 1U, 2U, 3U});
5039 INST(8U, Opcode::CheckCast).Inputs(3U, 22U, 7U).TypeId(1U);
5040 INST(9U, Opcode::LoadObject).ref().Inputs(3U);
5041 INST(23U, Opcode::SaveState).Inputs(0U, 1U, 2U, 3U, 9U).SrcVregs({0U, 1U, 2U, 3U, 4U});
5042 INST(11U, Opcode::CheckCast).Inputs(9U, 22U, 23U).TypeId(1U);
5043
5044 INST(10U, Opcode::Add).s32().Inputs(4U, 1U); // i++
5045 INST(13U, Opcode::Compare).CC(CC_LT).b().Inputs(10U, 2U); // i < 10
5046 INST(14U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(13U);
5047 }
5048 BASIC_BLOCK(5U, 1U)
5049 {
5050 INST(12U, Opcode::ReturnVoid).v0id();
5051 }
5052 }
5053 }
5054
TEST_F(ChecksEliminationTest,HoistCheckCastTest)5055 TEST_F(ChecksEliminationTest, HoistCheckCastTest)
5056 {
5057 BuildHoistCheckCastGraph();
5058 ASSERT_TRUE(GetGraph()->RunPass<ChecksElimination>());
5059 auto graph = CreateEmptyGraph();
5060 GRAPH(graph)
5061 {
5062 CONSTANT(0U, 0U); // initial
5063 CONSTANT(1U, 1U); // increment
5064 CONSTANT(2U, 10U);
5065 PARAMETER(3U, 0U).ref();
5066 BASIC_BLOCK(2U, 3U, 5U)
5067 {
5068 INST(21U, Opcode::SaveState).Inputs(0U, 1U, 2U, 3U).SrcVregs({0U, 1U, 2U, 3U});
5069 INST(22U, Opcode::LoadClass).ref().Inputs(21U).TypeId(1U);
5070 INST(20U, Opcode::SaveStateDeoptimize).Inputs(0U, 1U, 2U, 3U).SrcVregs({0U, 1U, 2U, 3U});
5071 INST(8U, Opcode::CheckCast).Inputs(3U, 22U, 20U).TypeId(1U).SetFlag(inst_flags::CAN_DEOPTIMIZE);
5072 INST(5U, Opcode::Compare).SrcType(DataType::INT32).CC(CC_LT).b().Inputs(0U, 2U); // 0 < 10
5073 INST(6U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(5U);
5074 }
5075 BASIC_BLOCK(3U, 3U, 5U)
5076 {
5077 INST(4U, Opcode::Phi).s32().Inputs(0U, 10U); // i
5078 INST(7U, Opcode::SaveState).Inputs(0U, 1U, 2U, 3U).SrcVregs({0U, 1U, 2U, 3U});
5079 INST(9U, Opcode::LoadObject).ref().Inputs(3U);
5080 INST(23U, Opcode::SaveState).Inputs(0U, 1U, 2U, 3U, 9U).SrcVregs({0U, 1U, 2U, 3U, 4U});
5081 INST(11U, Opcode::CheckCast).Inputs(9U, 22U, 23U).TypeId(1U);
5082
5083 INST(10U, Opcode::Add).s32().Inputs(4U, 1U); // i++
5084 INST(13U, Opcode::Compare).CC(CC_LT).b().Inputs(10U, 2U); // i < 10
5085 INST(14U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(13U);
5086 }
5087 BASIC_BLOCK(5U, 1U)
5088 {
5089 INST(12U, Opcode::ReturnVoid).v0id();
5090 }
5091 }
5092 ASSERT_TRUE(GraphComparator().Compare(GetGraph(), graph));
5093 }
5094
TEST_F(ChecksEliminationTest,NullCheckAfterIsInstance)5095 TEST_F(ChecksEliminationTest, NullCheckAfterIsInstance)
5096 {
5097 // NullCheck after successful IsInstance can be removed.
5098 GRAPH(GetGraph())
5099 {
5100 PARAMETER(0U, 0U).ref();
5101 CONSTANT(9U, nullptr);
5102 BASIC_BLOCK(2U, 3U, 4U)
5103 {
5104 INST(1U, Opcode::SaveState).Inputs(0U).SrcVregs({0U});
5105 INST(2U, Opcode::LoadClass).ref().Inputs(1U).TypeId(1U);
5106 INST(3U, Opcode::IsInstance).b().Inputs(0U, 2U).TypeId(1U);
5107 INST(4U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_EQ).Imm(0U).Inputs(3U);
5108 }
5109
5110 BASIC_BLOCK(4U, 1U)
5111 {
5112 INST(5U, Opcode::NullCheck).ref().Inputs(0U, 1U);
5113 INST(6U, Opcode::Return).ref().Inputs(5U);
5114 }
5115
5116 BASIC_BLOCK(3U, 1U)
5117 {
5118 INST(10U, Opcode::Return).ref().Inputs(9U);
5119 }
5120 }
5121 ASSERT_TRUE(GetGraph()->RunPass<ChecksElimination>());
5122
5123 auto graph = CreateEmptyGraph();
5124 GRAPH(graph)
5125 {
5126 PARAMETER(0U, 0U).ref();
5127 CONSTANT(9U, nullptr);
5128 BASIC_BLOCK(2U, 3U, 4U)
5129 {
5130 INST(1U, Opcode::SaveState).Inputs(0U).SrcVregs({0U});
5131 INST(2U, Opcode::LoadClass).ref().Inputs(1U).TypeId(1U);
5132 INST(3U, Opcode::IsInstance).b().Inputs(0U, 2U).TypeId(1U);
5133 INST(4U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_EQ).Imm(0U).Inputs(3U);
5134 }
5135
5136 BASIC_BLOCK(4U, 1U)
5137 {
5138 INST(5U, Opcode::NOP);
5139 INST(6U, Opcode::Return).ref().Inputs(0U);
5140 }
5141
5142 BASIC_BLOCK(3U, 1U)
5143 {
5144 INST(10U, Opcode::Return).ref().Inputs(9U);
5145 }
5146 }
5147 ASSERT_TRUE(GraphComparator().Compare(GetGraph(), graph));
5148 }
5149
TEST_F(ChecksEliminationTest,OmitNullCheck)5150 TEST_F(ChecksEliminationTest, OmitNullCheck)
5151 {
5152 // Check Elimination for NullCheck is applied.
5153 GRAPH(GetGraph())
5154 {
5155 PARAMETER(0U, 0U).ref();
5156 CONSTANT(1U, 10U);
5157 BASIC_BLOCK(2U, 1U)
5158 {
5159 INST(2U, Opcode::SaveState).Inputs(0U, 1U).SrcVregs({0U, 1U});
5160 INST(3U, Opcode::NullCheck).ref().Inputs(0U, 2U);
5161 INST(4U, Opcode::LoadArray).s32().Inputs(3U, 1U);
5162 INST(5U, Opcode::SaveState).Inputs(0U, 1U).SrcVregs({0U, 1U});
5163 INST(6U, Opcode::LoadClass).ref().Inputs(5U);
5164 INST(7U, Opcode::CheckCast).TypeId(1U).Inputs(0U, 6U, 5U);
5165 INST(8U, Opcode::LoadClass).ref().Inputs(5U);
5166 INST(9U, Opcode::IsInstance).TypeId(2U).b().Inputs(0U, 8U, 5U);
5167 INST(10U, Opcode::Return).b().Inputs(9U);
5168 }
5169 }
5170 auto clone = GraphCloner(GetGraph(), GetGraph()->GetAllocator(), GetGraph()->GetLocalAllocator()).CloneGraph();
5171 ASSERT_TRUE(GetGraph()->RunPass<ChecksElimination>());
5172 ASSERT_TRUE(GraphComparator().Compare(GetGraph(), clone));
5173 ASSERT_TRUE(INS(7U).CastToCheckCast()->GetOmitNullCheck());
5174 ASSERT_TRUE(INS(9U).CastToIsInstance()->GetOmitNullCheck());
5175 }
5176
TEST_F(ChecksEliminationTest,DoNotOmitNullCheck)5177 TEST_F(ChecksEliminationTest, DoNotOmitNullCheck)
5178 {
5179 // NullCheck inside CheckCast and IsInstance cannot be omitted. NullCheck doesn't dominate them.
5180 GRAPH(GetGraph())
5181 {
5182 PARAMETER(0U, 0U).ref();
5183 CONSTANT(1U, 10U);
5184
5185 BASIC_BLOCK(2U, 3U, 4U)
5186 {
5187 INST(2U, Opcode::Compare).b().SrcType(DataType::Type::INT64).Inputs(1U, 1U);
5188 INST(3U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(2U);
5189 }
5190
5191 BASIC_BLOCK(3U, 5U)
5192 {
5193 INST(4U, Opcode::SaveState).Inputs(0U, 1U).SrcVregs({0U, 1U});
5194 INST(5U, Opcode::NullCheck).ref().Inputs(0U, 4U);
5195 INST(6U, Opcode::LoadArray).s32().Inputs(5U, 1U);
5196 }
5197
5198 BASIC_BLOCK(4U, 5U)
5199 {
5200 INST(7U, Opcode::SaveState).Inputs(0U, 1U).SrcVregs({0U, 1U});
5201 INST(8U, Opcode::LoadClass).TypeId(1U).ref().Inputs(7U);
5202 INST(9U, Opcode::CheckCast).TypeId(1U).Inputs(0U, 8U, 7U);
5203 INST(10U, Opcode::LoadClass).TypeId(2U).ref().Inputs(7U);
5204 INST(11U, Opcode::IsInstance).TypeId(2U).b().Inputs(0U, 10U, 7U);
5205 }
5206
5207 BASIC_BLOCK(5U, 1U)
5208 {
5209 INST(12U, Opcode::Phi).s32().Inputs(6U, 1U);
5210 INST(13U, Opcode::Return).s32().Inputs(12U);
5211 }
5212 }
5213 auto clone = GraphCloner(GetGraph(), GetGraph()->GetAllocator(), GetGraph()->GetLocalAllocator()).CloneGraph();
5214 ASSERT_FALSE(GetGraph()->RunPass<ChecksElimination>());
5215 ASSERT_TRUE(GraphComparator().Compare(GetGraph(), clone));
5216 ASSERT_FALSE(INS(9U).CastToCheckCast()->GetOmitNullCheck());
5217 ASSERT_FALSE(INS(11U).CastToIsInstance()->GetOmitNullCheck());
5218 }
5219
5220 // NOTE(schernykh): It's possible to remove boundschecks from this test, but BoundsAnalysis must be upgrade for
5221 // it.
TEST_F(ChecksEliminationTest,OptimizeBoundsCheckElimination)5222 TEST_F(ChecksEliminationTest, OptimizeBoundsCheckElimination)
5223 {
5224 GRAPH(GetGraph())
5225 {
5226 CONSTANT(0U, 0U);
5227 CONSTANT(14U, 1U);
5228 PARAMETER(1U, 0U).s32();
5229 PARAMETER(2U, 1U).s32();
5230 BASIC_BLOCK(2U, 6U, 3U)
5231 {
5232 INST(43U, Opcode::SaveState).Inputs(0U).SrcVregs({0U});
5233 INST(44U, Opcode::LoadAndInitClass).ref().Inputs().TypeId(68U);
5234 INST(3U, Opcode::NewArray).ref().Inputs(44U, 1U, 43U);
5235 INST(4U, Opcode::Compare).b().SrcType(DataType::Type::INT32).CC(CC_LT).Inputs(2U, 0U);
5236 INST(5U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(4U);
5237 }
5238 BASIC_BLOCK(3U, 6U, 4U)
5239 {
5240 INST(6U, Opcode::Compare).b().SrcType(DataType::Type::INT32).CC(CC_GE).Inputs(2U, 1U);
5241 INST(7U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(6U);
5242 }
5243 BASIC_BLOCK(4U, 6U, 5U)
5244 {
5245 INST(8U, Opcode::Add).s32().Inputs(2U, 14U);
5246 INST(9U, Opcode::Compare).b().SrcType(DataType::Type::INT32).CC(CC_GE).Inputs(8U, 1U);
5247 INST(10U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(9U);
5248 }
5249 BASIC_BLOCK(5U, 6U)
5250 {
5251 INST(11U, Opcode::SaveState).Inputs(0U, 1U, 2U).SrcVregs({0U, 1U, 2U});
5252 INST(12U, Opcode::BoundsCheck).s32().Inputs(1U, 8U, 11U);
5253 INST(13U, Opcode::StoreArray).s32().Inputs(3U, 8U, 0U);
5254 }
5255 BASIC_BLOCK(6U, 1U)
5256 {
5257 INST(15U, Opcode::ReturnVoid).v0id();
5258 }
5259 }
5260 auto clone = GraphCloner(GetGraph(), GetGraph()->GetAllocator(), GetGraph()->GetLocalAllocator()).CloneGraph();
5261 ASSERT_FALSE(GetGraph()->RunPass<ChecksElimination>());
5262 ASSERT_TRUE(GraphComparator().Compare(GetGraph(), clone));
5263 }
5264
TEST_F(ChecksEliminationTest,BoundsCheckEqualInputs)5265 TEST_F(ChecksEliminationTest, BoundsCheckEqualInputs)
5266 {
5267 GRAPH(GetGraph())
5268 {
5269 PARAMETER(1U, 2U).s32();
5270 PARAMETER(2U, 3U).s32();
5271 CONSTANT(3U, 10U);
5272 BASIC_BLOCK(2U, 1U)
5273 {
5274 INST(4U, Opcode::SaveState).Inputs(1U, 2U).SrcVregs({1U, 2U});
5275 INST(44U, Opcode::LoadAndInitClass).ref().Inputs().TypeId(68U);
5276 INST(5U, Opcode::NewArray).ref().Inputs(44U, 3U, 4U);
5277 INST(6U, Opcode::BoundsCheck).s32().Inputs(3U, 1U, 4U);
5278 INST(7U, Opcode::StoreArray).s32().Inputs(5U, 6U, 3U);
5279
5280 INST(14U, Opcode::SaveState).Inputs(1U, 2U).SrcVregs({1U, 2U});
5281 INST(15U, Opcode::NewArray).ref().Inputs(44U, 2U, 14U);
5282 INST(16U, Opcode::BoundsCheck).s32().Inputs(2U, 3U, 14U);
5283 INST(17U, Opcode::StoreArray).s32().Inputs(15U, 16U, 3U);
5284
5285 INST(18U, Opcode::ReturnVoid).v0id();
5286 }
5287 }
5288 auto clone = GraphCloner(GetGraph(), GetGraph()->GetAllocator(), GetGraph()->GetLocalAllocator()).CloneGraph();
5289 ASSERT_FALSE(GetGraph()->RunPass<ChecksElimination>());
5290 ASSERT_TRUE(GraphComparator().Compare(clone, GetGraph()));
5291 }
5292
5293 // dominated checks
TEST_F(ChecksEliminationTest,AddSubOverflowCheckDom)5294 TEST_F(ChecksEliminationTest, AddSubOverflowCheckDom)
5295 {
5296 GRAPH(GetGraph())
5297 {
5298 PARAMETER(0U, 0U).s32();
5299 PARAMETER(1U, 1U).s32();
5300 BASIC_BLOCK(2U, -1L)
5301 {
5302 INST(2U, Opcode::SaveState).Inputs(0U, 1U).SrcVregs({1U, 2U});
5303 INST(3U, Opcode::AddOverflowCheck).s32().Inputs(0U, 1U, 2U); // main
5304
5305 INST(4U, Opcode::SaveState).Inputs(0U, 1U).SrcVregs({1U, 2U});
5306 INST(5U, Opcode::AddOverflowCheck).s32().Inputs(0U, 1U, 4U); // redundant
5307
5308 INST(6U, Opcode::SaveState).Inputs(0U, 1U).SrcVregs({1U, 2U});
5309 INST(7U, Opcode::AddOverflowCheck).s32().Inputs(1U, 0U, 6U); // redundant, swapped inputs
5310
5311 INST(8U, Opcode::SaveState).Inputs(0U, 1U).SrcVregs({1U, 2U});
5312 INST(9U, Opcode::SubOverflowCheck).s32().Inputs(0U, 1U, 8U); // main
5313
5314 INST(10U, Opcode::SaveState).Inputs(0U, 1U).SrcVregs({1U, 2U});
5315 INST(11U, Opcode::SubOverflowCheck).s32().Inputs(0U, 1U, 10U); // redundant
5316
5317 INST(12U, Opcode::SaveState).Inputs(0U, 1U).SrcVregs({1U, 2U});
5318 INST(13U, Opcode::SubOverflowCheck).s32().Inputs(1U, 0U, 12U); // not redundant, swapped inputs
5319
5320 INST(14U, Opcode::ReturnVoid).v0id();
5321 }
5322 }
5323 ASSERT_TRUE(GetGraph()->RunPass<ChecksElimination>());
5324 ASSERT_TRUE(GetGraph()->RunPass<Cleanup>());
5325 auto graph = CreateEmptyGraph();
5326 GRAPH(graph)
5327 {
5328 PARAMETER(0U, 0U).s32();
5329 PARAMETER(1U, 1U).s32();
5330 BASIC_BLOCK(2U, -1L)
5331 {
5332 INST(2U, Opcode::SaveState).Inputs(0U, 1U).SrcVregs({1U, 2U});
5333 INST(3U, Opcode::AddOverflowCheck).s32().Inputs(0U, 1U, 2U); // main
5334
5335 INST(8U, Opcode::SaveState).Inputs(0U, 1U).SrcVregs({1U, 2U});
5336 INST(9U, Opcode::SubOverflowCheck).s32().Inputs(0U, 1U, 8U); // main
5337
5338 INST(12U, Opcode::SaveState).Inputs(0U, 1U).SrcVregs({1U, 2U});
5339 INST(13U, Opcode::SubOverflowCheck).s32().Inputs(1U, 0U, 12U); // not redundant, swapped inputs
5340
5341 INST(14U, Opcode::ReturnVoid).v0id();
5342 }
5343 }
5344 ASSERT_TRUE(GraphComparator().Compare(graph, GetGraph()));
5345 }
5346
BuildGraphOverflowCheckOptimize()5347 void ChecksEliminationTest::BuildGraphOverflowCheckOptimize()
5348 {
5349 GRAPH(GetGraph())
5350 {
5351 PARAMETER(0U, 0U).s32();
5352 PARAMETER(1U, 1U).s32();
5353 CONSTANT(6U, 6U);
5354 CONSTANT(1000U, 0U);
5355 CONSTANT(13U, INT32_MAX);
5356 CONSTANT(14U, INT32_MIN);
5357 BASIC_BLOCK(2U, 3U, 4U)
5358 {
5359 INST(2U, Opcode::SaveState).Inputs(0U, 1U).SrcVregs({1U, 2U});
5360 INST(3U, Opcode::AddOverflowCheck).s32().Inputs(0U, 1U, 2U); // maybe overflow
5361
5362 INST(4U, Opcode::SaveState).Inputs(0U, 1U).SrcVregs({1U, 2U});
5363 INST(5U, Opcode::SubOverflowCheck).s32().Inputs(0U, 1U, 4U); // maybe overflow
5364
5365 INST(20U, Opcode::SaveState).Inputs(0U, 1U).SrcVregs({1U, 2U});
5366 INST(30U, Opcode::AddOverflowCheck).s32().Inputs(0U, 6U, 20U); // maybe overflow
5367
5368 INST(40U, Opcode::SaveState).Inputs(0U, 1U).SrcVregs({1U, 2U});
5369 INST(50U, Opcode::SubOverflowCheck).s32().Inputs(0U, 6U, 40U); // maybe overflow
5370
5371 INST(7U, Opcode::Div).s32().Inputs(0U, 6U);
5372 INST(8U, Opcode::Div).s32().Inputs(1U, 6U);
5373
5374 INST(9U, Opcode::SaveState).Inputs(0U, 1U).SrcVregs({1U, 2U});
5375 INST(10U, Opcode::AddOverflowCheck).s32().Inputs(7U, 8U, 9U); // can't overflow
5376
5377 INST(11U, Opcode::SaveState).Inputs(0U, 1U).SrcVregs({1U, 2U});
5378 INST(12U, Opcode::SubOverflowCheck).s32().Inputs(7U, 8U, 11U); // can't overflow
5379
5380 INST(15U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(1000U);
5381 }
5382 BASIC_BLOCK(3U, 5U)
5383 {
5384 INST(16U, Opcode::SaveState).Inputs(0U, 1U).SrcVregs({1U, 2U});
5385 INST(17U, Opcode::AddOverflowCheck).s32().Inputs(13U, 6U, 16U); // must overflow
5386 }
5387 BASIC_BLOCK(4U, 5U)
5388 {
5389 INST(18U, Opcode::SaveState).Inputs(0U, 1U).SrcVregs({1U, 2U});
5390 INST(19U, Opcode::SubOverflowCheck).s32().Inputs(14U, 6U, 18U); // must overflow
5391 }
5392 BASIC_BLOCK(5U, -1L)
5393 {
5394 INST(100U, Opcode::ReturnVoid).v0id();
5395 }
5396 }
5397 }
5398
TEST_F(ChecksEliminationTest,OverflowCheckOptimize)5399 TEST_F(ChecksEliminationTest, OverflowCheckOptimize)
5400 {
5401 BuildGraphOverflowCheckOptimize();
5402 ASSERT_TRUE(GetGraph()->RunPass<ChecksElimination>());
5403 auto graph = CreateEmptyGraph();
5404 GRAPH(graph)
5405 {
5406 PARAMETER(0U, 0U).s32();
5407 PARAMETER(1U, 1U).s32();
5408 CONSTANT(6U, 6U);
5409 CONSTANT(1000U, 0U);
5410 CONSTANT(13U, INT32_MAX);
5411 CONSTANT(14U, INT32_MIN);
5412 BASIC_BLOCK(2U, 3U, 4U)
5413 {
5414 INST(2U, Opcode::SaveState).Inputs(0U, 1U).SrcVregs({1U, 2U});
5415 INST(3U, Opcode::AddOverflowCheck).s32().Inputs(0U, 1U, 2U); // maybe overflow
5416
5417 INST(4U, Opcode::SaveState).Inputs(0U, 1U).SrcVregs({1U, 2U});
5418 INST(5U, Opcode::SubOverflowCheck).s32().Inputs(0U, 1U, 4U); // maybe overflow
5419
5420 INST(20U, Opcode::SaveState).Inputs(0U, 1U).SrcVregs({1U, 2U});
5421 INST(30U, Opcode::AddOverflowCheck).s32().Inputs(0U, 6U, 20U); // maybe overflow
5422
5423 INST(40U, Opcode::SaveState).Inputs(0U, 1U).SrcVregs({1U, 2U});
5424 INST(50U, Opcode::SubOverflowCheck).s32().Inputs(0U, 6U, 40U); // maybe overflow
5425
5426 INST(7U, Opcode::Div).s32().Inputs(0U, 6U);
5427 INST(8U, Opcode::Div).s32().Inputs(1U, 6U);
5428
5429 INST(9U, Opcode::SaveState).Inputs(0U, 1U).SrcVregs({1U, 2U});
5430 INST(10U, Opcode::Add).s32().Inputs(7U, 8U);
5431
5432 INST(11U, Opcode::SaveState).Inputs(0U, 1U).SrcVregs({1U, 2U});
5433 INST(12U, Opcode::Sub).s32().Inputs(7U, 8U);
5434
5435 INST(15U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(1000U);
5436 }
5437 BASIC_BLOCK(3U, -1L)
5438 {
5439 INST(16U, Opcode::SaveState).Inputs(0U, 1U).SrcVregs({1U, 2U});
5440 INST(17U, Opcode::Deoptimize).DeoptimizeType(DeoptimizeType::OVERFLOW_TYPE).Inputs(16U); // must overflow
5441 }
5442 BASIC_BLOCK(4U, -1L)
5443 {
5444 INST(18U, Opcode::SaveState).Inputs(0U, 1U).SrcVregs({1U, 2U});
5445 INST(19U, Opcode::Deoptimize).DeoptimizeType(DeoptimizeType::OVERFLOW_TYPE).Inputs(18U); // must overflow
5446 }
5447 }
5448 ASSERT_TRUE(GraphComparator().Compare(graph, GetGraph()));
5449 }
5450
TEST_F(ChecksEliminationTest,LoopWithOverflowCheck)5451 TEST_F(ChecksEliminationTest, LoopWithOverflowCheck)
5452 {
5453 GRAPH(GetGraph())
5454 {
5455 PARAMETER(0U, 0U).s32();
5456 PARAMETER(1U, 1U).s32();
5457 CONSTANT(2U, 0U);
5458 BASIC_BLOCK(2U, 3U)
5459 {
5460 INST(3U, Opcode::SaveState).Inputs(0U, 1U).SrcVregs({0U, 1U});
5461 }
5462 BASIC_BLOCK(3U, 3U, 4U)
5463 {
5464 INST(4U, Opcode::SaveState).Inputs(0U, 1U).SrcVregs({0U, 1U});
5465 INST(5U, Opcode::AddOverflowCheck).s32().Inputs(0U, 1U, 4U);
5466 INST(6U, Opcode::SubOverflowCheck).s32().Inputs(0U, 1U, 4U);
5467 INST(7U, Opcode::IfImm).SrcType(DataType::BOOL).Inputs(2U).Imm(0U);
5468 }
5469 BASIC_BLOCK(4U, 1U)
5470 {
5471 INST(8U, Opcode::ReturnVoid).v0id();
5472 }
5473 }
5474 ASSERT_TRUE(GetGraph()->RunPass<ChecksElimination>());
5475 auto graph = CreateEmptyGraph();
5476 GRAPH(graph)
5477 {
5478 PARAMETER(0U, 0U).s32();
5479 PARAMETER(1U, 1U).s32();
5480 CONSTANT(2U, 0U);
5481 BASIC_BLOCK(2U, 3U)
5482 {
5483 INST(3U, Opcode::SaveState).Inputs(0U, 1U).SrcVregs({0U, 1U});
5484 INST(5U, Opcode::SubOverflowCheck).s32().Inputs(0U, 1U, 3U);
5485 INST(6U, Opcode::AddOverflowCheck).s32().Inputs(0U, 1U, 3U);
5486 }
5487 BASIC_BLOCK(3U, 3U, 4U)
5488 {
5489 INST(4U, Opcode::SaveState).Inputs(0U, 1U).SrcVregs({0U, 1U});
5490 INST(7U, Opcode::IfImm).SrcType(DataType::BOOL).Inputs(2U).Imm(0U);
5491 }
5492 BASIC_BLOCK(4U, 1U)
5493 {
5494 INST(8U, Opcode::ReturnVoid).v0id();
5495 }
5496 }
5497 ASSERT_TRUE(GraphComparator().Compare(graph, GetGraph()));
5498 }
5499
TEST_F(ChecksEliminationTest,LoopWithAddOverflowCheck)5500 TEST_F(ChecksEliminationTest, LoopWithAddOverflowCheck)
5501 {
5502 GRAPH(GetGraph())
5503 {
5504 PARAMETER(0U, 0U).s32();
5505 CONSTANT(1U, 1U);
5506 CONSTANT(10U, 10U);
5507
5508 BASIC_BLOCK(2U, 3U, 4U)
5509 {
5510 INST(2U, Opcode::Phi).s32().Inputs(1U, 4U);
5511 INST(6U, Opcode::Compare).b().Inputs(2U, 10U).CC(CC_LT);
5512 INST(5U, Opcode::IfImm).SrcType(DataType::BOOL).Inputs(6U).Imm(0U).CC(CC_NE);
5513 }
5514 BASIC_BLOCK(3U, 2U)
5515 {
5516 INST(3U, Opcode::SaveState).Inputs(0U, 1U, 10U).SrcVregs({0U, 1U, 10U});
5517 INST(4U, Opcode::AddOverflowCheck).s32().Inputs(2U, 1U, 3U); // can't be overflow
5518 }
5519 BASIC_BLOCK(4U, 1U)
5520 {
5521 INST(7U, Opcode::Return).s32().Inputs(2U);
5522 }
5523 }
5524 ASSERT_TRUE(GetGraph()->RunPass<ChecksElimination>());
5525 auto graph = CreateEmptyGraph();
5526 GRAPH(graph)
5527 {
5528 PARAMETER(0U, 0U).s32();
5529 CONSTANT(1U, 1U);
5530 CONSTANT(10U, 10U);
5531
5532 BASIC_BLOCK(2U, 3U, 4U)
5533 {
5534 INST(2U, Opcode::Phi).s32().Inputs(1U, 4U);
5535 INST(6U, Opcode::Compare).b().Inputs(2U, 10U).CC(CC_LT);
5536 INST(5U, Opcode::IfImm).SrcType(DataType::BOOL).Inputs(6U).Imm(0U).CC(CC_NE);
5537 }
5538 BASIC_BLOCK(3U, 2U)
5539 {
5540 INST(3U, Opcode::SaveState).Inputs(0U, 1U, 10U).SrcVregs({0U, 1U, 10U});
5541 INST(4U, Opcode::Add).s32().Inputs(2U, 1U);
5542 }
5543 BASIC_BLOCK(4U, 1U)
5544 {
5545 INST(7U, Opcode::Return).s32().Inputs(2U);
5546 }
5547 }
5548 ASSERT_TRUE(GraphComparator().Compare(graph, GetGraph()));
5549 }
5550
TEST_F(ChecksEliminationTest,LoopWithSubOverflowCheck)5551 TEST_F(ChecksEliminationTest, LoopWithSubOverflowCheck)
5552 {
5553 GRAPH(GetGraph())
5554 {
5555 PARAMETER(0U, 0U).s32();
5556 CONSTANT(1U, 1U);
5557 CONSTANT(10U, 10U);
5558
5559 BASIC_BLOCK(2U, 3U, 4U)
5560 {
5561 INST(2U, Opcode::Phi).s32().Inputs(10U, 4U);
5562 INST(6U, Opcode::Compare).b().Inputs(2U, 1U).CC(CC_GT);
5563 INST(5U, Opcode::IfImm).SrcType(DataType::BOOL).Inputs(6U).Imm(0U).CC(CC_NE);
5564 }
5565 BASIC_BLOCK(3U, 2U)
5566 {
5567 INST(3U, Opcode::SaveState).Inputs(0U, 1U, 10U).SrcVregs({0U, 1U, 10U});
5568 INST(4U, Opcode::SubOverflowCheck).s32().Inputs(2U, 1U, 3U); // can't be overflow
5569 }
5570 BASIC_BLOCK(4U, 1U)
5571 {
5572 INST(7U, Opcode::Return).s32().Inputs(2U);
5573 }
5574 }
5575 ASSERT_TRUE(GetGraph()->RunPass<ChecksElimination>());
5576 auto graph = CreateEmptyGraph();
5577 GRAPH(graph)
5578 {
5579 PARAMETER(0U, 0U).s32();
5580 CONSTANT(1U, 1U);
5581 CONSTANT(10U, 10U);
5582
5583 BASIC_BLOCK(2U, 3U, 4U)
5584 {
5585 INST(2U, Opcode::Phi).s32().Inputs(10U, 4U);
5586 INST(6U, Opcode::Compare).b().Inputs(2U, 1U).CC(CC_GT);
5587 INST(5U, Opcode::IfImm).SrcType(DataType::BOOL).Inputs(6U).Imm(0U).CC(CC_NE);
5588 }
5589 BASIC_BLOCK(3U, 2U)
5590 {
5591 INST(3U, Opcode::SaveState).Inputs(0U, 1U, 10U).SrcVregs({0U, 1U, 10U});
5592 INST(4U, Opcode::Sub).s32().Inputs(2U, 1U);
5593 }
5594 BASIC_BLOCK(4U, 1U)
5595 {
5596 INST(7U, Opcode::Return).s32().Inputs(2U);
5597 }
5598 }
5599 ASSERT_TRUE(GraphComparator().Compare(graph, GetGraph()));
5600 }
5601
TEST_F(ChecksEliminationTest,AndWithAddOverFlowCheck)5602 TEST_F(ChecksEliminationTest, AndWithAddOverFlowCheck)
5603 {
5604 GRAPH(GetGraph())
5605 {
5606 PARAMETER(0U, 0U).s64();
5607 PARAMETER(1U, 1U).s64();
5608 CONSTANT(2U, 0x3U);
5609 BASIC_BLOCK(2U, -1L)
5610 {
5611 INST(3U, Opcode::And).s32().Inputs(0U, 2U);
5612 INST(4U, Opcode::And).s32().Inputs(1U, 2U);
5613 INST(5U, Opcode::SaveState).Inputs(0U, 1U).SrcVregs({0U, 1U});
5614 INST(6U, Opcode::AddOverflowCheck).s32().Inputs(3U, 4U, 5U);
5615 INST(7U, Opcode::Return).s32().Inputs(6U);
5616 }
5617 }
5618 ASSERT_TRUE(GetGraph()->RunPass<ChecksElimination>());
5619 auto graph = CreateEmptyGraph();
5620 GRAPH(graph)
5621 {
5622 PARAMETER(0U, 0U).s64();
5623 PARAMETER(1U, 1U).s64();
5624 CONSTANT(2U, 0x3U);
5625 BASIC_BLOCK(2U, -1L)
5626 {
5627 INST(3U, Opcode::And).s32().Inputs(0U, 2U);
5628 INST(4U, Opcode::And).s32().Inputs(1U, 2U);
5629 INST(5U, Opcode::SaveState).Inputs(0U, 1U).SrcVregs({0U, 1U});
5630 INST(6U, Opcode::Add).s32().Inputs(3U, 4U);
5631 INST(7U, Opcode::Return).s32().Inputs(6U);
5632 }
5633 }
5634 ASSERT_TRUE(GraphComparator().Compare(graph, GetGraph()));
5635 }
5636
5637 // Must Deoptimize
TEST_F(ChecksEliminationTest,NegOverflowAndZeroCheck1)5638 TEST_F(ChecksEliminationTest, NegOverflowAndZeroCheck1)
5639 {
5640 for (auto cnst : {0, INT32_MIN}) {
5641 auto graph1 = CreateEmptyGraph();
5642 GRAPH(graph1)
5643 {
5644 CONSTANT(0U, cnst);
5645 BASIC_BLOCK(2U, -1L)
5646 {
5647 INST(5U, Opcode::SaveState).Inputs(0U).SrcVregs({0U});
5648 INST(6U, Opcode::NegOverflowAndZeroCheck).s32().Inputs(0U, 5U);
5649 INST(7U, Opcode::Return).s32().Inputs(6U);
5650 }
5651 }
5652 ASSERT_TRUE(graph1->RunPass<ChecksElimination>());
5653 auto graph2 = CreateEmptyGraph();
5654 GRAPH(graph2)
5655 {
5656 CONSTANT(0U, cnst);
5657 BASIC_BLOCK(2U, -1L)
5658 {
5659 INST(5U, Opcode::SaveState).Inputs(0U).SrcVregs({0U});
5660 INST(6U, Opcode::Deoptimize).DeoptimizeType(DeoptimizeType::OVERFLOW_TYPE).Inputs(5U);
5661 }
5662 }
5663 ASSERT_TRUE(GraphComparator().Compare(graph1, graph2));
5664 }
5665 }
5666
5667 // Remove dominated check
TEST_F(ChecksEliminationTest,NegOverflowAndZeroCheck2)5668 TEST_F(ChecksEliminationTest, NegOverflowAndZeroCheck2)
5669 {
5670 auto graph1 = CreateEmptyGraph();
5671 GRAPH(graph1)
5672 {
5673 PARAMETER(0U, 0U).u32();
5674 BASIC_BLOCK(2U, -1L)
5675 {
5676 INST(3U, Opcode::SaveState).Inputs(0U).SrcVregs({0U});
5677 INST(4U, Opcode::NegOverflowAndZeroCheck).s32().Inputs(0U, 3U);
5678 INST(5U, Opcode::SaveState).Inputs(0U).SrcVregs({0U});
5679 INST(6U, Opcode::NegOverflowAndZeroCheck).s32().Inputs(0U, 5U);
5680 INST(7U, Opcode::CallStatic).s32().InputsAutoType(4U, 6U, 3U);
5681 INST(8U, Opcode::Return).s32().Inputs(7U);
5682 }
5683 }
5684 ASSERT_TRUE(graph1->RunPass<ChecksElimination>());
5685 ASSERT_TRUE(graph1->RunPass<Cleanup>());
5686 auto graph2 = CreateEmptyGraph();
5687 GRAPH(graph2)
5688 {
5689 PARAMETER(0U, 0U).u32();
5690 BASIC_BLOCK(2U, -1L)
5691 {
5692 INST(3U, Opcode::SaveState).Inputs(0U).SrcVregs({0U});
5693 INST(4U, Opcode::NegOverflowAndZeroCheck).s32().Inputs(0U, 3U);
5694 INST(7U, Opcode::CallStatic).s32().InputsAutoType(4U, 4U, 3U);
5695 INST(8U, Opcode::Return).s32().Inputs(7U);
5696 }
5697 }
5698 ASSERT_TRUE(GraphComparator().Compare(graph1, graph2));
5699 }
5700
5701 // Replace by Neg
TEST_F(ChecksEliminationTest,NegOverflowAndZeroCheck3)5702 TEST_F(ChecksEliminationTest, NegOverflowAndZeroCheck3)
5703 {
5704 auto graph1 = CreateEmptyGraph();
5705 GRAPH(graph1)
5706 {
5707 CONSTANT(0U, 1U);
5708 BASIC_BLOCK(2U, -1L)
5709 {
5710 INST(5U, Opcode::SaveState).Inputs(0U).SrcVregs({0U});
5711 INST(6U, Opcode::NegOverflowAndZeroCheck).s32().Inputs(0U, 5U);
5712 INST(7U, Opcode::Return).s32().Inputs(6U);
5713 }
5714 }
5715 ASSERT_TRUE(graph1->RunPass<ChecksElimination>());
5716 auto graph2 = CreateEmptyGraph();
5717 GRAPH(graph2)
5718 {
5719 CONSTANT(0U, 1U);
5720 BASIC_BLOCK(2U, -1L)
5721 {
5722 INST(5U, Opcode::SaveState).Inputs(0U).SrcVregs({0U});
5723 INST(6U, Opcode::Neg).s32().Inputs(0U);
5724 INST(7U, Opcode::Return).s32().Inputs(6U);
5725 }
5726 }
5727 ASSERT_TRUE(GraphComparator().Compare(graph1, graph2));
5728 }
5729
TEST_F(ChecksEliminationTest,OsrMode)5730 TEST_F(ChecksEliminationTest, OsrMode)
5731 {
5732 auto osrGraph = CreateOsrGraph();
5733 GRAPH(osrGraph)
5734 {
5735 PARAMETER(0U, 0U).u32();
5736 PARAMETER(42U, 1U).ref();
5737 CONSTANT(1U, 0U);
5738 CONSTANT(2U, 1U);
5739
5740 BASIC_BLOCK(2U, 3U)
5741 {
5742 INST(15U, Opcode::SaveState).Inputs(42U).SrcVregs({42U});
5743 INST(16U, Opcode::NullCheck).ref().Inputs(42U, 15U);
5744 INST(17U, Opcode::LenArray).s32().Inputs(16U);
5745 }
5746 BASIC_BLOCK(3U, 5U, 4U)
5747 {
5748 INST(3U, Opcode::Phi).s32().Inputs(0U, 7U);
5749 INST(4U, Opcode::SaveStateOsr).Inputs(3U, 42U).SrcVregs({1U, 42U});
5750 INST(5U, Opcode::Compare).b().SrcType(DataType::Type::INT32).CC(CC_LE).Inputs(3U, 1U);
5751 INST(6U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(5U);
5752 }
5753 BASIC_BLOCK(4U, 3U)
5754 {
5755 INST(7U, Opcode::Sub).s32().Inputs(3U, 2U);
5756 INST(8U, Opcode::SaveState).Inputs(42U).SrcVregs({42U});
5757 INST(9U, Opcode::NullCheck).ref().Inputs(42U, 8U);
5758 INST(10U, Opcode::LenArray).s32().Inputs(9U);
5759 }
5760 BASIC_BLOCK(5U, 6U)
5761 {
5762 INST(11U, Opcode::SaveState).Inputs(42U).SrcVregs({42U});
5763 INST(12U, Opcode::NullCheck).ref().Inputs(42U, 11U);
5764 INST(13U, Opcode::LenArray).s32().Inputs(12U);
5765 }
5766 BASIC_BLOCK(6U, -1L)
5767 {
5768 INST(14U, Opcode::Return).u32().Inputs(3U);
5769 }
5770 }
5771 ASSERT_FALSE(osrGraph->RunPass<ChecksElimination>());
5772 }
5773
5774 // NOLINTEND(readability-magic-numbers)
5775
5776 } // namespace ark::compiler
5777