1 /*
2 * Copyright (c) 2021-2024 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include "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
TEST_F(ChecksEliminationTest,LoopSeveralIndexesBoundsChecks)3023 TEST_F(ChecksEliminationTest, LoopSeveralIndexesBoundsChecks)
3024 {
3025 BuildGraphLoopSeveralIndexesBoundsChecks();
3026 ASSERT_TRUE(GetGraph()->RunPass<ChecksElimination>());
3027 auto graph1 = CreateEmptyGraph();
3028 GRAPH(graph1)
3029 {
3030 CONSTANT(0U, 0U); // initial
3031 CONSTANT(1U, 1U); // increment
3032 PARAMETER(2U, 0U).ref(); // array
3033 PARAMETER(3U, 1U).s32(); // Y
3034 PARAMETER(4U, 2U).s32(); // X
3035 CONSTANT(43U, -1L);
3036 BASIC_BLOCK(7U, 3U, 6U)
3037 {
3038 // check array
3039 INST(5U, Opcode::SaveState).Inputs(0U, 1U, 2U).SrcVregs({0U, 1U, 2U});
3040 INST(6U, Opcode::NullCheck).ref().Inputs(2U, 5U);
3041 INST(7U, Opcode::LenArray).s32().Inputs(6U);
3042
3043 INST(36U, Opcode::SaveStateDeoptimize).Inputs(0U).SrcVregs({0U});
3044 INST(37U, Opcode::Add).s32().Inputs(3U, 43U);
3045 INST(38U, Opcode::Compare).CC(ConditionCode::CC_LT).b().Inputs(37U, 0U); // Y-1 < 0
3046 INST(39U, Opcode::DeoptimizeIf).DeoptimizeType(DeoptimizeType::BOUNDS_CHECK).Inputs(38U, 36U);
3047
3048 INST(40U, Opcode::Sub).s32().Inputs(7U, 1U);
3049 INST(41U, Opcode::Compare).CC(ConditionCode::CC_LT).b().Inputs(40U, 4U); // len_array - 1 < X
3050 INST(42U, Opcode::DeoptimizeIf).DeoptimizeType(DeoptimizeType::BOUNDS_CHECK).Inputs(41U, 36U);
3051
3052 INST(11U, Opcode::Compare).CC(ConditionCode::CC_LT).b().Inputs(3U, 4U); // Y < X
3053 INST(12U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(11U);
3054 }
3055 BASIC_BLOCK(3U, 3U, 6U)
3056 {
3057 INST(13U, Opcode::Phi).s32().Inputs(3U, 20U); // i
3058
3059 INST(14U, Opcode::SaveState).Inputs(0U, 1U, 2U).SrcVregs({0U, 1U, 2U});
3060 INST(15U, Opcode::NOP);
3061 INST(16U, Opcode::LoadArray).s32().Inputs(2U, 13U); // array[i]
3062
3063 INST(26U, Opcode::Sub).s32().Inputs(13U, 1U); // i - 1
3064 INST(27U, Opcode::SaveState).Inputs(0U, 1U, 2U).SrcVregs({0U, 1U, 2U});
3065 INST(28U, Opcode::NOP);
3066 INST(29U, Opcode::LoadArray).s32().Inputs(2U, 26U); // array[i-1]
3067
3068 INST(30U, Opcode::Add).s32().Inputs(13U, 1U); // i + 1
3069 INST(31U, Opcode::SaveState).Inputs(0U, 1U, 2U).SrcVregs({0U, 1U, 2U});
3070 INST(32U, Opcode::NOP);
3071 INST(33U, Opcode::LoadArray).s32().Inputs(2U, 30U); // array[i+1]
3072
3073 INST(34U, Opcode::Add).s32().Inputs(16U, 29U); // array[i-1] + array[i]
3074 INST(35U, Opcode::Add).s32().Inputs(34U, 33U); // array[i-1] + array[i] + array[i+1]
3075
3076 INST(17U, Opcode::SaveState).Inputs(0U, 1U, 2U).SrcVregs({0U, 1U, 2U});
3077 INST(18U, Opcode::NOP);
3078 INST(19U, Opcode::StoreArray).s32().Inputs(2U, 13U, 35U); // array[i] = array[i-1] + array[i] + array[i+1]
3079
3080 INST(20U, Opcode::Add).s32().Inputs(13U, 1U); // i++
3081 INST(21U, Opcode::Compare).CC(ConditionCode::CC_LT).b().Inputs(20U, 4U); // i < X
3082 INST(22U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(21U);
3083 }
3084 BASIC_BLOCK(6U, 1U)
3085 {
3086 INST(23U, Opcode::Phi).s32().Inputs(0U, 20U);
3087 INST(24U, Opcode::Return).s32().Inputs(23U);
3088 }
3089 }
3090 ASSERT_TRUE(GraphComparator().Compare(GetGraph(), graph1));
3091 }
3092
BuildGraphHeadExitLoop()3093 void ChecksEliminationTest::BuildGraphHeadExitLoop()
3094 {
3095 GRAPH(GetGraph())
3096 {
3097 CONSTANT(0U, 0U); // initial
3098 CONSTANT(1U, 1U); // increment
3099 PARAMETER(2U, 0U).ref();
3100 PARAMETER(3U, 1U).s32(); // X
3101 BASIC_BLOCK(2U, 3U)
3102 {
3103 INST(4U, Opcode::SaveState).Inputs(0U, 1U, 2U).SrcVregs({0U, 1U, 2U});
3104 INST(5U, Opcode::NullCheck).ref().Inputs(2U, 4U);
3105 INST(6U, Opcode::LenArray).s32().Inputs(5U);
3106 INST(30U, Opcode::SaveStateDeoptimize).Inputs(0U).SrcVregs({0U});
3107 }
3108 BASIC_BLOCK(3U, 4U, 5U)
3109 {
3110 INST(7U, Opcode::Phi).s32().Inputs(0U, 13U);
3111 INST(8U, Opcode::Compare).CC(CC_LT).b().Inputs(7U, 3U); // i < X
3112 INST(9U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(8U);
3113 }
3114 BASIC_BLOCK(4U, 3U)
3115 {
3116 INST(10U, Opcode::SaveState).Inputs(0U, 1U, 2U).SrcVregs({0U, 1U, 2U});
3117 INST(11U, Opcode::BoundsCheck).s32().Inputs(6U, 7U, 10U);
3118 INST(12U, Opcode::StoreArray).s32().Inputs(5U, 11U, 0U); // a[i] = 0
3119 INST(13U, Opcode::Add).s32().Inputs(7U, 1U); // i++
3120 }
3121 BASIC_BLOCK(5U, 1U)
3122 {
3123 INST(14U, Opcode::Return).ref().Inputs(5U);
3124 }
3125 }
3126 }
3127
TEST_F(ChecksEliminationTest,HeadExitLoop)3128 TEST_F(ChecksEliminationTest, HeadExitLoop)
3129 {
3130 BuildGraphHeadExitLoop();
3131 ASSERT_TRUE(GetGraph()->RunPass<ChecksElimination>());
3132 auto graph = CreateEmptyGraph();
3133 GRAPH(graph)
3134 {
3135 CONSTANT(0U, 0U); // initial
3136 CONSTANT(1U, 1U); // increment
3137 PARAMETER(2U, 0U).ref();
3138 PARAMETER(3U, 1U).s32();
3139 BASIC_BLOCK(2U, 3U)
3140 {
3141 INST(4U, Opcode::SaveState).Inputs(0U, 1U, 2U).SrcVregs({0U, 1U, 2U});
3142 INST(5U, Opcode::NullCheck).ref().Inputs(2U, 4U);
3143 INST(6U, Opcode::LenArray).s32().Inputs(5U);
3144 INST(30U, Opcode::SaveStateDeoptimize).Inputs(0U).SrcVregs({0U});
3145 INST(15U, Opcode::Compare).b().CC(ConditionCode::CC_LT).Inputs(6U, 3U); // len_array < X
3146 INST(16U, Opcode::DeoptimizeIf).DeoptimizeType(DeoptimizeType::BOUNDS_CHECK).Inputs(15U, 30U);
3147 }
3148 BASIC_BLOCK(3U, 4U, 5U)
3149 {
3150 INST(7U, Opcode::Phi).s32().Inputs(0U, 13U);
3151 INST(8U, Opcode::Compare).CC(CC_LT).b().Inputs(7U, 3U); // i < X
3152 INST(9U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(8U);
3153 }
3154 BASIC_BLOCK(4U, 3U)
3155 {
3156 INST(10U, Opcode::SaveState).Inputs(0U, 1U, 2U).SrcVregs({0U, 1U, 2U});
3157 INST(11U, Opcode::NOP);
3158 INST(12U, Opcode::StoreArray).s32().Inputs(5U, 7U, 0U); // a[i] = 0
3159 INST(13U, Opcode::Add).s32().Inputs(7U, 1U); // i++
3160 }
3161 BASIC_BLOCK(5U, 1U)
3162 {
3163 INST(14U, Opcode::Return).ref().Inputs(5U);
3164 }
3165 }
3166 ASSERT_TRUE(GraphComparator().Compare(GetGraph(), graph));
3167 }
3168
TEST_F(ChecksEliminationTest,BoundsCheckInHeader)3169 TEST_F(ChecksEliminationTest, BoundsCheckInHeader)
3170 {
3171 GRAPH(GetGraph())
3172 {
3173 CONSTANT(0U, 0U); // initial
3174 CONSTANT(1U, 1U); // increment
3175 PARAMETER(2U, 0U).ref();
3176 PARAMETER(3U, 1U).s32();
3177 BASIC_BLOCK(2U, 3U)
3178 {
3179 INST(4U, Opcode::SaveState).Inputs(0U, 1U, 2U).SrcVregs({0U, 1U, 2U});
3180 INST(5U, Opcode::NullCheck).ref().Inputs(2U, 4U);
3181 INST(6U, Opcode::LenArray).s32().Inputs(5U);
3182 INST(30U, Opcode::SaveStateDeoptimize).Inputs(0U).SrcVregs({0U});
3183 }
3184 BASIC_BLOCK(3U, 4U, 5U)
3185 {
3186 INST(7U, Opcode::Phi).s32().Inputs(0U, 13U);
3187 INST(10U, Opcode::SaveState).Inputs(0U, 1U, 2U).SrcVregs({0U, 1U, 2U});
3188 INST(11U, Opcode::BoundsCheck).s32().Inputs(6U, 7U, 10U);
3189 INST(12U, Opcode::StoreArray).s32().Inputs(5U, 11U, 0U); // a[i] = 0
3190 INST(8U, Opcode::Compare).CC(CC_LT).b().Inputs(7U, 3U); // i < X
3191 INST(9U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(8U);
3192 }
3193 BASIC_BLOCK(4U, 3U)
3194 {
3195 INST(13U, Opcode::Add).s32().Inputs(7U, 1U); // i++
3196 }
3197 BASIC_BLOCK(5U, 1U)
3198 {
3199 INST(14U, Opcode::Return).ref().Inputs(5U);
3200 }
3201 }
3202 auto clone = GraphCloner(GetGraph(), GetGraph()->GetAllocator(), GetGraph()->GetLocalAllocator()).CloneGraph();
3203 ASSERT_FALSE(GetGraph()->RunPass<ChecksElimination>());
3204 ASSERT_TRUE(GraphComparator().Compare(GetGraph(), clone));
3205 }
3206
BuildGraphLoopTest1()3207 void ChecksEliminationTest::BuildGraphLoopTest1()
3208 {
3209 GRAPH(GetGraph())
3210 {
3211 CONSTANT(0U, 0U); // initial
3212 CONSTANT(1U, 1U); // increment
3213 PARAMETER(13U, 0U).ref(); // Array
3214 PARAMETER(27U, 1U).s32(); // X
3215 BASIC_BLOCK(2U, 6U, 3U)
3216 {
3217 INST(30U, Opcode::SaveStateDeoptimize).Inputs(0U).SrcVregs({0U});
3218 INST(14U, Opcode::Compare).CC(ConditionCode::CC_GE).b().Inputs(0U, 27U); // i < X
3219 INST(15U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(14U);
3220 }
3221 BASIC_BLOCK(3U, 6U, 3U)
3222 {
3223 INST(4U, Opcode::Phi).s32().Inputs(0U, 10U);
3224 INST(7U, Opcode::SaveState).Inputs(0U, 1U, 13U, 27U).SrcVregs({0U, 1U, 2U, 3U});
3225 INST(16U, Opcode::NullCheck).ref().Inputs(13U, 7U);
3226 INST(17U, Opcode::LenArray).s32().Inputs(16U);
3227 INST(8U, Opcode::BoundsCheck).s32().Inputs(17U, 4U, 7U);
3228 INST(9U, Opcode::StoreArray).s32().Inputs(16U, 8U, 0U); // a[i] = 0
3229 INST(10U, Opcode::Add).s32().Inputs(4U, 1U); // i++
3230 INST(5U, Opcode::Compare).CC(ConditionCode::CC_GE).b().Inputs(10U, 27U); // i < X
3231 INST(6U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(5U);
3232 }
3233 BASIC_BLOCK(6U, 1U)
3234 {
3235 INST(26U, Opcode::Phi).s32().Inputs(0U, 10U);
3236 INST(12U, Opcode::Return).s32().Inputs(26U);
3237 }
3238 }
3239 }
3240
TEST_F(ChecksEliminationTest,LoopTest1)3241 TEST_F(ChecksEliminationTest, LoopTest1)
3242 {
3243 BuildGraphLoopTest1();
3244 ASSERT_TRUE(GetGraph()->RunPass<ChecksElimination>());
3245 auto graph1 = CreateEmptyGraph();
3246 GRAPH(graph1)
3247 {
3248 CONSTANT(0U, 0U); // initial
3249 CONSTANT(1U, 1U); // increment
3250 PARAMETER(3U, 0U).ref();
3251 PARAMETER(2U, 1U).s32();
3252 BASIC_BLOCK(2U, 5U, 3U)
3253 {
3254 INST(20U, Opcode::SaveStateDeoptimize).Inputs(0U).SrcVregs({0U});
3255 INST(33U, Opcode::NullCheck).ref().Inputs(3U, 20U).SetFlag(inst_flags::CAN_DEOPTIMIZE); // array != nullptr
3256 INST(22U, Opcode::LenArray).s32().Inputs(33U);
3257 INST(23U, Opcode::Compare).SrcType(DataType::INT32).CC(CC_LT).b().Inputs(22U, 2U); // len_array < X
3258 INST(24U, Opcode::DeoptimizeIf).DeoptimizeType(DeoptimizeType::BOUNDS_CHECK).Inputs(23U, 20U);
3259 INST(5U, Opcode::Compare).SrcType(DataType::INT32).CC(CC_GE).b().Inputs(0U, 2U); // 0 < X
3260 INST(6U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(5U);
3261 }
3262 BASIC_BLOCK(3U, 5U, 3U)
3263 {
3264 INST(4U, Opcode::Phi).s32().Inputs(0U, 10U);
3265 INST(7U, Opcode::SaveState).Inputs(0U, 1U, 3U, 2U).SrcVregs({0U, 1U, 2U, 3U});
3266 INST(15U, Opcode::NOP);
3267 INST(16U, Opcode::LenArray).s32().Inputs(33U);
3268 INST(8U, Opcode::NOP);
3269 INST(9U, Opcode::StoreArray).s32().Inputs(33U, 4U, 0U); // a[i] = 0
3270 INST(10U, Opcode::Add).s32().Inputs(4U, 1U); // i++
3271 INST(13U, Opcode::Compare).CC(CC_GE).b().Inputs(10U, 2U); // i < X
3272 INST(14U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(13U);
3273 }
3274 BASIC_BLOCK(5U, 1U)
3275 {
3276 INST(26U, Opcode::Phi).s32().Inputs(0U, 10U);
3277 INST(12U, Opcode::Return).s32().Inputs(26U);
3278 }
3279 }
3280 ASSERT_TRUE(GraphComparator().Compare(GetGraph(), graph1));
3281 }
3282
BuildGraphBatchLoopTest()3283 void ChecksEliminationTest::BuildGraphBatchLoopTest()
3284 {
3285 GRAPH(GetGraph())
3286 {
3287 CONSTANT(0U, 0U); // initial
3288 CONSTANT(1U, 1U);
3289 CONSTANT(2U, 2U);
3290 CONSTANT(3U, 3U); // increment
3291 PARAMETER(4U, 0U).ref(); // Array
3292 BASIC_BLOCK(2U, 4U, 3U)
3293 {
3294 INST(5U, Opcode::SaveState).Inputs(0U, 1U, 2U, 3U, 4U).SrcVregs({0U, 1U, 2U, 3U, 4U});
3295 INST(6U, Opcode::NullCheck).ref().Inputs(4U, 5U);
3296 INST(7U, Opcode::LenArray).s32().Inputs(6U);
3297 INST(30U, Opcode::SaveStateDeoptimize).Inputs(4U).SrcVregs({0U});
3298 INST(25U, Opcode::Compare).CC(CC_GE).b().Inputs(0U, 7U);
3299 INST(26U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(25U);
3300 }
3301 BASIC_BLOCK(3U, 4U, 3U)
3302 {
3303 INST(8U, Opcode::Phi).s32().Inputs(0U, 20U);
3304 INST(9U, Opcode::SaveState).Inputs(0U, 1U, 2U, 3U, 4U, 7U).SrcVregs({0U, 1U, 2U, 3U, 4U, 7U});
3305 INST(10U, Opcode::NullCheck).ref().Inputs(4U, 9U);
3306
3307 INST(12U, Opcode::Add).s32().Inputs(8U, 1U);
3308 INST(13U, Opcode::BoundsCheck).s32().Inputs(7U, 12U, 9U);
3309 INST(14U, Opcode::LoadArray).s32().Inputs(10U, 13U);
3310
3311 INST(15U, Opcode::BoundsCheck).s32().Inputs(7U, 8U, 9U);
3312 INST(16U, Opcode::StoreArray).s32().Inputs(10U, 15U, 14U); // a[i] = a[i + 1]
3313
3314 INST(17U, Opcode::Add).s32().Inputs(8U, 2U);
3315 INST(18U, Opcode::BoundsCheck).s32().Inputs(7U, 17U, 9U);
3316 INST(19U, Opcode::StoreArray).s32().Inputs(10U, 18U, 14U); // a[i + 2] = a[i + 1]
3317
3318 INST(20U, Opcode::Add).s32().Inputs(8U, 3U); // i += 3
3319 INST(21U, Opcode::Compare).CC(CC_GE).b().Inputs(20U, 7U); // i < X
3320 INST(22U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(21U);
3321 }
3322 BASIC_BLOCK(4U, 1U)
3323 {
3324 INST(23U, Opcode::ReturnVoid).v0id();
3325 }
3326 }
3327 }
3328
TEST_F(ChecksEliminationTest,BatchLoopTest)3329 TEST_F(ChecksEliminationTest, BatchLoopTest)
3330 {
3331 BuildGraphBatchLoopTest();
3332 ASSERT_TRUE(GetGraph()->RunPass<ChecksElimination>());
3333 auto graph1 = CreateEmptyGraph();
3334 GRAPH(graph1)
3335 {
3336 CONSTANT(0U, 0U); // initial
3337 CONSTANT(1U, 1U);
3338 CONSTANT(2U, 2U);
3339 CONSTANT(3U, 3U); // increment
3340 PARAMETER(4U, 0U).ref(); // Array
3341 CONSTANT(36U, 0x7ffffffdU);
3342 BASIC_BLOCK(2U, 4U, 3U)
3343 {
3344 INST(5U, Opcode::SaveState).Inputs(0U, 1U, 2U, 3U, 4U).SrcVregs({0U, 1U, 2U, 3U, 4U});
3345 INST(6U, Opcode::NullCheck).ref().Inputs(4U, 5U);
3346 INST(7U, Opcode::LenArray).s32().Inputs(6U);
3347 INST(30U, Opcode::SaveStateDeoptimize).Inputs(4U).SrcVregs({0U});
3348 INST(31U, Opcode::Compare).CC(CC_LT).b().Inputs(36U, 7U);
3349 INST(32U, Opcode::DeoptimizeIf).DeoptimizeType(DeoptimizeType::BOUNDS_CHECK).Inputs(31U, 30U);
3350 INST(33U, Opcode::Mod).s32().Inputs(7U, 3U);
3351 INST(34U, Opcode::Compare).CC(CC_NE).b().Inputs(33U, 0U);
3352 INST(35U, Opcode::DeoptimizeIf).DeoptimizeType(DeoptimizeType::BOUNDS_CHECK).Inputs(34U, 30U);
3353 INST(25U, Opcode::Compare).CC(CC_GE).b().Inputs(0U, 7U);
3354 INST(26U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(25U);
3355 }
3356 BASIC_BLOCK(3U, 4U, 3U)
3357 {
3358 INST(8U, Opcode::Phi).s32().Inputs(0U, 20U);
3359 INST(9U, Opcode::SaveState).Inputs(0U, 1U, 2U, 3U, 4U, 7U).SrcVregs({0U, 1U, 2U, 3U, 4U, 7U});
3360 INST(10U, Opcode::NOP);
3361
3362 INST(12U, Opcode::Add).s32().Inputs(8U, 1U);
3363 INST(13U, Opcode::NOP);
3364 INST(14U, Opcode::LoadArray).s32().Inputs(6U, 12U);
3365
3366 INST(15U, Opcode::NOP);
3367 INST(16U, Opcode::StoreArray).s32().Inputs(6U, 8U, 14U); // a[i] = a[i + 1]
3368
3369 INST(17U, Opcode::Add).s32().Inputs(8U, 2U);
3370 INST(18U, Opcode::NOP);
3371 INST(19U, Opcode::StoreArray).s32().Inputs(6U, 17U, 14U); // a[i + 2] = a[i + 1]
3372
3373 INST(20U, Opcode::Add).s32().Inputs(8U, 3U); // i += 3
3374 INST(21U, Opcode::Compare).CC(CC_GE).b().Inputs(20U, 7U); // i < X
3375 INST(22U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(21U);
3376 }
3377 BASIC_BLOCK(4U, 1U)
3378 {
3379 INST(23U, Opcode::ReturnVoid).v0id();
3380 }
3381 }
3382 ASSERT_TRUE(GraphComparator().Compare(GetGraph(), graph1));
3383 }
3384
TEST_F(ChecksEliminationTest,NewlyAlllocatedCheck)3385 TEST_F(ChecksEliminationTest, NewlyAlllocatedCheck)
3386 {
3387 GRAPH(GetGraph())
3388 {
3389 CONSTANT(0U, 0x1U).s64();
3390 CONSTANT(1U, 0x0U).s64();
3391 CONSTANT(5U, 0x2aU).s64();
3392 BASIC_BLOCK(2U, -1L)
3393 {
3394 INST(43U, Opcode::SaveState).Inputs(0U).SrcVregs({0U});
3395 INST(44U, Opcode::LoadAndInitClass).ref().Inputs().TypeId(68U);
3396 INST(4U, Opcode::NewArray).ref().Inputs(44U, 0U, 43U).TypeId(79U);
3397 INST(6U, Opcode::SaveState).Inputs(5U, 1U, 4U).SrcVregs({2U, 1U, 0U});
3398 INST(7U, Opcode::NullCheck).ref().Inputs(4U, 6U);
3399 INST(10U, Opcode::StoreArray).s32().Inputs(7U, 1U, 5U);
3400 INST(11U, Opcode::Return).ref().Inputs(4U);
3401 }
3402 }
3403 ASSERT_TRUE(GetGraph()->RunPass<ChecksElimination>());
3404 auto graph1 = CreateEmptyGraph();
3405 GRAPH(graph1)
3406 {
3407 CONSTANT(0U, 0x1U).s64();
3408 CONSTANT(1U, 0x0U).s64();
3409 CONSTANT(5U, 0x2aU).s64();
3410 BASIC_BLOCK(2U, -1L)
3411 {
3412 INST(43U, Opcode::SaveState).Inputs(0U).SrcVregs({0U});
3413 INST(44U, Opcode::LoadAndInitClass).ref().Inputs().TypeId(68U);
3414 INST(4U, Opcode::NewArray).ref().Inputs(44U, 0U, 43U).TypeId(79U);
3415 INST(6U, Opcode::SaveState).Inputs(5U, 1U, 4U).SrcVregs({2U, 1U, 0U});
3416 INST(7U, Opcode::NOP);
3417 INST(10U, Opcode::StoreArray).s32().Inputs(4U, 1U, 5U);
3418 INST(11U, Opcode::Return).ref().Inputs(4U);
3419 }
3420 }
3421 ASSERT_TRUE(GraphComparator().Compare(GetGraph(), graph1));
3422 }
3423
TEST_F(ChecksEliminationTest,DominatedBoundsCheck)3424 TEST_F(ChecksEliminationTest, DominatedBoundsCheck)
3425 {
3426 GRAPH(GetGraph())
3427 {
3428 PARAMETER(0U, 0U).ref();
3429 PARAMETER(1U, 1U).s64();
3430 BASIC_BLOCK(2U, -1L)
3431 {
3432 INST(2U, Opcode::SaveState).Inputs(1U, 1U, 0U).SrcVregs({2U, 1U, 0U});
3433 INST(4U, Opcode::LenArray).s32().Inputs(0U);
3434 INST(5U, Opcode::BoundsCheck).s32().Inputs(4U, 1U, 2U);
3435 INST(6U, Opcode::LoadArray).s64().Inputs(0U, 5U);
3436 INST(7U, Opcode::SaveState).Inputs(6U, 1U, 0U).SrcVregs({2U, 1U, 0U});
3437 INST(10U, Opcode::BoundsCheck).s32().Inputs(4U, 1U, 7U);
3438 INST(11U, Opcode::StoreArray).s64().Inputs(0U, 10U, 6U);
3439 INST(12U, Opcode::ReturnVoid).v0id();
3440 }
3441 }
3442 ASSERT_TRUE(GetGraph()->RunPass<ChecksElimination>());
3443 auto graph1 = CreateEmptyGraph();
3444 GRAPH(graph1)
3445 {
3446 PARAMETER(0U, 0U).ref();
3447 PARAMETER(1U, 1U).s64();
3448 BASIC_BLOCK(2U, -1L)
3449 {
3450 INST(2U, Opcode::SaveState).Inputs(1U, 1U, 0U).SrcVregs({2U, 1U, 0U});
3451 INST(4U, Opcode::LenArray).s32().Inputs(0U);
3452 INST(5U, Opcode::BoundsCheck).s32().Inputs(4U, 1U, 2U);
3453 INST(6U, Opcode::LoadArray).s64().Inputs(0U, 5U);
3454 INST(7U, Opcode::SaveState).Inputs(6U, 1U, 0U).SrcVregs({2U, 1U, 0U});
3455 INST(10U, Opcode::NOP);
3456 INST(11U, Opcode::StoreArray).s64().Inputs(0U, 5U, 6U);
3457 INST(12U, Opcode::ReturnVoid).v0id();
3458 }
3459 }
3460 ASSERT_TRUE(GraphComparator().Compare(GetGraph(), graph1));
3461 }
3462
BuildGraphGroupedBoundsCheck()3463 void ChecksEliminationTest::BuildGraphGroupedBoundsCheck()
3464 {
3465 GRAPH(GetGraph())
3466 {
3467 PARAMETER(0U, 0U).ref(); // a
3468 PARAMETER(1U, 1U).s32(); // x
3469 CONSTANT(2U, 1U);
3470 CONSTANT(5U, -2L);
3471 BASIC_BLOCK(2U, -1L)
3472 {
3473 INST(6U, Opcode::LenArray).s32().Inputs(0U);
3474 // a[x] = 1
3475 INST(7U, Opcode::SaveState).Inputs(0U, 1U).SrcVregs({0U, 1U});
3476 INST(8U, Opcode::BoundsCheck).s32().Inputs(6U, 1U, 7U);
3477 INST(9U, Opcode::StoreArray).s64().Inputs(0U, 8U, 2U);
3478 // a[x-1] = 1
3479 INST(10U, Opcode::Sub).s32().Inputs(1U, 2U);
3480 INST(11U, Opcode::SaveState).Inputs(0U, 1U).SrcVregs({0U, 1U});
3481 INST(12U, Opcode::BoundsCheck).s32().Inputs(6U, 10U, 11U);
3482 INST(13U, Opcode::StoreArray).s64().Inputs(0U, 12U, 2U);
3483 // a[x+1] = 1
3484 INST(14U, Opcode::Add).s32().Inputs(1U, 2U);
3485 INST(15U, Opcode::SaveState).Inputs(0U, 1U).SrcVregs({0U, 1U});
3486 INST(16U, Opcode::BoundsCheck).s32().Inputs(6U, 14U, 15U);
3487 INST(17U, Opcode::StoreArray).s64().Inputs(0U, 16U, 2U);
3488 // a[x+(-2)] = 1
3489 INST(18U, Opcode::Add).s32().Inputs(1U, 5U);
3490 INST(19U, Opcode::SaveState).Inputs(0U, 1U).SrcVregs({0U, 1U});
3491 INST(20U, Opcode::BoundsCheck).s32().Inputs(6U, 18U, 19U);
3492 INST(21U, Opcode::StoreArray).s64().Inputs(0U, 20U, 2U);
3493 // a[x-(-2)] = 1
3494 INST(22U, Opcode::Sub).s32().Inputs(1U, 5U);
3495 INST(23U, Opcode::SaveState).Inputs(0U, 1U).SrcVregs({0U, 1U});
3496 INST(24U, Opcode::BoundsCheck).s32().Inputs(6U, 22U, 23U);
3497 INST(25U, Opcode::StoreArray).s64().Inputs(0U, 24U, 2U);
3498 INST(26U, Opcode::ReturnVoid).v0id();
3499 }
3500 }
3501 }
3502
TEST_F(ChecksEliminationTest,GroupedBoundsCheck)3503 TEST_F(ChecksEliminationTest, GroupedBoundsCheck)
3504 {
3505 BuildGraphGroupedBoundsCheck();
3506 ASSERT_TRUE(GetGraph()->RunPass<ChecksElimination>());
3507 auto graph1 = CreateEmptyGraph();
3508 GRAPH(graph1)
3509 {
3510 PARAMETER(0U, 0U).ref(); // a
3511 PARAMETER(1U, 1U).s32(); // x
3512 CONSTANT(2U, 1U);
3513 CONSTANT(5U, -2L);
3514 CONSTANT(3U, 2U);
3515 CONSTANT(4U, 0U);
3516 BASIC_BLOCK(2U, -1L)
3517 {
3518 INST(6U, Opcode::LenArray).s32().Inputs(0U);
3519 INST(7U, Opcode::SaveState).Inputs(0U, 1U).SrcVregs({0U, 1U});
3520
3521 INST(30U, Opcode::Add).s32().Inputs(1U, 5U);
3522 INST(31U, Opcode::Compare).b().CC(CC_LT).SrcType(DataType::INT32).Inputs(30U, 4U);
3523 INST(32U, Opcode::DeoptimizeIf).DeoptimizeType(DeoptimizeType::BOUNDS_CHECK).Inputs(31U, 7U);
3524 INST(27U, Opcode::Add).s32().Inputs(1U, 3U);
3525 INST(28U, Opcode::Compare).b().CC(CC_GE).SrcType(DataType::INT32).Inputs(27U, 6U);
3526 INST(29U, Opcode::DeoptimizeIf).DeoptimizeType(DeoptimizeType::BOUNDS_CHECK).Inputs(28U, 7U);
3527
3528 // a[x] = 1
3529 INST(8U, Opcode::NOP);
3530 INST(9U, Opcode::StoreArray).s64().Inputs(0U, 1U, 2U);
3531 // a[x-1] = 1
3532 INST(10U, Opcode::Sub).s32().Inputs(1U, 2U);
3533 INST(11U, Opcode::SaveState).Inputs(0U, 1U).SrcVregs({0U, 1U});
3534 INST(12U, Opcode::NOP);
3535 INST(13U, Opcode::StoreArray).s64().Inputs(0U, 10U, 2U);
3536 // a[x+1] = 1
3537 INST(14U, Opcode::Add).s32().Inputs(1U, 2U);
3538 INST(15U, Opcode::SaveState).Inputs(0U, 1U).SrcVregs({0U, 1U});
3539 INST(16U, Opcode::NOP);
3540 INST(17U, Opcode::StoreArray).s64().Inputs(0U, 14U, 2U);
3541 // a[x+(-2)] = 1
3542 INST(18U, Opcode::Add).s32().Inputs(1U, 5U);
3543 INST(19U, Opcode::SaveState).Inputs(0U, 1U).SrcVregs({0U, 1U});
3544 INST(20U, Opcode::NOP);
3545 INST(21U, Opcode::StoreArray).s64().Inputs(0U, 18U, 2U);
3546 // a[x-(-2)] = 1
3547 INST(22U, Opcode::Sub).s32().Inputs(1U, 5U);
3548 INST(23U, Opcode::SaveState).Inputs(0U, 1U).SrcVregs({0U, 1U});
3549 INST(24U, Opcode::NOP);
3550 INST(25U, Opcode::StoreArray).s64().Inputs(0U, 22U, 2U);
3551 INST(26U, Opcode::ReturnVoid).v0id();
3552 }
3553 }
3554 ASSERT_TRUE(GraphComparator().Compare(GetGraph(), graph1));
3555 }
3556
BuildGraphGroupedBoundsCheckConstIndex()3557 void ChecksEliminationTest::BuildGraphGroupedBoundsCheckConstIndex()
3558 {
3559 GRAPH(GetGraph())
3560 {
3561 PARAMETER(0U, 0U).ref(); // a
3562 CONSTANT(2U, 0U);
3563 CONSTANT(3U, 1U);
3564 CONSTANT(4U, 2U);
3565 CONSTANT(5U, 3U);
3566 BASIC_BLOCK(2U, -1L)
3567 {
3568 INST(6U, Opcode::LenArray).s32().Inputs(0U);
3569 INST(7U, Opcode::SaveState).Inputs(0U).SrcVregs({0U});
3570
3571 // a[0] = 1
3572 INST(8U, Opcode::BoundsCheck).s32().Inputs(6U, 2U, 7U);
3573 INST(9U, Opcode::StoreArray).s64().Inputs(0U, 8U, 2U);
3574 // a[1] = 1
3575 INST(11U, Opcode::SaveState).Inputs(0U).SrcVregs({0U});
3576 INST(12U, Opcode::BoundsCheck).s32().Inputs(6U, 3U, 11U);
3577 INST(13U, Opcode::StoreArray).s64().Inputs(0U, 12U, 2U);
3578 // a[2] = 1
3579 INST(15U, Opcode::SaveState).Inputs(0U).SrcVregs({0U});
3580 INST(16U, Opcode::BoundsCheck).s32().Inputs(6U, 4U, 15U);
3581 INST(17U, Opcode::StoreArray).s64().Inputs(0U, 16U, 2U);
3582 // a[3] = 1
3583 INST(19U, Opcode::SaveState).Inputs(0U).SrcVregs({0U});
3584 INST(20U, Opcode::BoundsCheck).s32().Inputs(6U, 5U, 19U);
3585 INST(21U, Opcode::StoreArray).s64().Inputs(0U, 20U, 2U);
3586 INST(26U, Opcode::ReturnVoid).v0id();
3587 }
3588 }
3589 }
3590
TEST_F(ChecksEliminationTest,GroupedBoundsCheckConstIndex)3591 TEST_F(ChecksEliminationTest, GroupedBoundsCheckConstIndex)
3592 {
3593 BuildGraphGroupedBoundsCheckConstIndex();
3594 ASSERT_TRUE(GetGraph()->RunPass<ChecksElimination>());
3595 auto graph1 = CreateEmptyGraph();
3596 GRAPH(graph1)
3597 {
3598 PARAMETER(0U, 0U).ref(); // a
3599 CONSTANT(2U, 0U);
3600 CONSTANT(3U, 1U);
3601 CONSTANT(4U, 2U);
3602 CONSTANT(5U, 3U);
3603 BASIC_BLOCK(2U, -1L)
3604 {
3605 INST(6U, Opcode::LenArray).s32().Inputs(0U);
3606 INST(7U, Opcode::SaveState).Inputs(0U).SrcVregs({0U});
3607
3608 INST(28U, Opcode::Compare).b().CC(CC_GE).SrcType(DataType::INT32).Inputs(5U, 6U);
3609 INST(29U, Opcode::DeoptimizeIf).DeoptimizeType(DeoptimizeType::BOUNDS_CHECK).Inputs(28U, 7U);
3610
3611 // a[0] = 1
3612 INST(8U, Opcode::NOP);
3613 INST(9U, Opcode::StoreArray).s64().Inputs(0U, 2U, 2U);
3614 // a[1] = 1
3615 INST(11U, Opcode::SaveState).Inputs(0U).SrcVregs({0U});
3616 INST(12U, Opcode::NOP);
3617 INST(13U, Opcode::StoreArray).s64().Inputs(0U, 3U, 2U);
3618 // a[2] = 1
3619 INST(15U, Opcode::SaveState).Inputs(0U).SrcVregs({0U});
3620 INST(16U, Opcode::NOP);
3621 INST(17U, Opcode::StoreArray).s64().Inputs(0U, 4U, 2U);
3622 // a[3] = 1
3623 INST(19U, Opcode::SaveState).Inputs(0U).SrcVregs({0U});
3624 INST(20U, Opcode::NOP);
3625 INST(21U, Opcode::StoreArray).s64().Inputs(0U, 5U, 2U);
3626 INST(26U, Opcode::ReturnVoid).v0id();
3627 }
3628 }
3629 ASSERT_TRUE(GraphComparator().Compare(GetGraph(), graph1));
3630 }
3631
TEST_F(ChecksEliminationTest,ConsecutiveNullChecks)3632 TEST_F(ChecksEliminationTest, ConsecutiveNullChecks)
3633 {
3634 builder_->EnableGraphChecker(false);
3635 GRAPH(GetGraph())
3636 {
3637 PARAMETER(0U, 0U).ref();
3638 BASIC_BLOCK(2U, 1U)
3639 {
3640 INST(5U, Opcode::SaveState).Inputs(0U).SrcVregs({0U});
3641 INST(1U, Opcode::NullCheck).ref().Inputs(0U, 5U);
3642 INST(2U, Opcode::NullCheck).ref().Inputs(1U, 5U);
3643 INST(3U, Opcode::NullCheck).ref().Inputs(2U, 5U);
3644 INST(6U, Opcode::StoreObject).ref().Inputs(3U, 0U).TypeId(1U);
3645 INST(4U, Opcode::Return).ref().Inputs(3U);
3646 }
3647 }
3648 builder_->EnableGraphChecker(true);
3649 ASSERT_TRUE(GetGraph()->RunPass<ChecksElimination>());
3650 auto graph = CreateEmptyGraph();
3651 GRAPH(graph)
3652 {
3653 PARAMETER(0U, 0U).ref();
3654 BASIC_BLOCK(2U, 1U)
3655 {
3656 INST(5U, Opcode::SaveState).Inputs(0U).SrcVregs({0U});
3657 INST(1U, Opcode::NullCheck).ref().Inputs(0U, 5U);
3658 INST(2U, Opcode::NOP);
3659 INST(3U, Opcode::NOP);
3660 INST(6U, Opcode::StoreObject).ref().Inputs(1U, 0U).TypeId(1U);
3661 INST(4U, Opcode::Return).ref().Inputs(1U);
3662 }
3663 }
3664 ASSERT_TRUE(GraphComparator().Compare(GetGraph(), graph));
3665 }
3666
TEST_F(ChecksEliminationTest,NullCheckInCallVirt)3667 TEST_F(ChecksEliminationTest, NullCheckInCallVirt)
3668 {
3669 GRAPH(GetGraph())
3670 {
3671 PARAMETER(0U, 0U).ref();
3672 PARAMETER(1U, 1U).ref();
3673 BASIC_BLOCK(2U, 1U)
3674 {
3675 INST(5U, Opcode::SaveState).Inputs(0U, 1U).SrcVregs({0U, 1U});
3676 INST(2U, Opcode::NullCheck).ref().Inputs(0U, 5U);
3677 INST(3U, Opcode::NullCheck).ref().Inputs(1U, 5U);
3678 INST(4U, Opcode::CallVirtual)
3679 .s32()
3680 .Inputs({{DataType::REFERENCE, 2U}, {DataType::REFERENCE, 3U}, {DataType::NO_TYPE, 5U}});
3681 INST(6U, Opcode::Return).s32().Inputs(4U);
3682 }
3683 }
3684 // Doesn't remove nullchecks if the method static
3685 auto clone = GraphCloner(GetGraph(), GetGraph()->GetAllocator(), GetGraph()->GetLocalAllocator()).CloneGraph();
3686 ASSERT_FALSE(GetGraph()->RunPass<ChecksElimination>());
3687 ASSERT_TRUE(GraphComparator().Compare(GetGraph(), clone));
3688
3689 // Removes nullcheck from first parameter for not static method
3690 RuntimeInterfaceNotStaticMethod runtime;
3691 GetGraph()->SetRuntime(&runtime);
3692 ASSERT_TRUE(GetGraph()->RunPass<ChecksElimination>());
3693 auto graph1 = CreateEmptyGraph();
3694 GRAPH(graph1)
3695 {
3696 PARAMETER(0U, 0U).ref();
3697 PARAMETER(1U, 1U).ref();
3698 BASIC_BLOCK(2U, 1U)
3699 {
3700 INST(5U, Opcode::SaveState).Inputs(0U, 1U).SrcVregs({0U, 1U});
3701 INST(2U, Opcode::NOP);
3702 INST(3U, Opcode::NullCheck).ref().Inputs(1U, 5U);
3703 INST(4U, Opcode::CallVirtual)
3704 .s32()
3705 .Inputs({{DataType::REFERENCE, 0U}, {DataType::REFERENCE, 3U}, {DataType::NO_TYPE, 5U}});
3706 INST(6U, Opcode::Return).s32().Inputs(4U);
3707 }
3708 }
3709 ASSERT_TRUE(GraphComparator().Compare(GetGraph(), graph1));
3710 }
3711
TEST_F(ChecksEliminationTest,DominatedChecksWithDifferentTypes)3712 TEST_F(ChecksEliminationTest, DominatedChecksWithDifferentTypes)
3713 {
3714 GRAPH(GetGraph())
3715 {
3716 PARAMETER(0U, 0U).s64();
3717 PARAMETER(1U, 1U).s64();
3718 BASIC_BLOCK(2U, 1U)
3719 {
3720 INST(2U, Opcode::SaveState).Inputs(0U, 1U).SrcVregs({0U, 1U});
3721 INST(3U, Opcode::ZeroCheck).s32().Inputs(0U, 2U);
3722 INST(4U, Opcode::Div).s32().Inputs(1U, 3U);
3723 INST(5U, Opcode::SaveState).Inputs(0U, 1U).SrcVregs({0U, 1U});
3724 INST(6U, Opcode::ZeroCheck).s64().Inputs(0U, 5U);
3725 INST(7U, Opcode::Mod).s64().Inputs(1U, 6U);
3726 INST(20U, Opcode::SaveState).NoVregs();
3727 INST(8U, Opcode::CallStatic).s32().InputsAutoType(4U, 7U, 20U);
3728 INST(9U, Opcode::Return).s32().Inputs(8U);
3729 }
3730 }
3731 auto initial = GraphCloner(GetGraph(), GetGraph()->GetAllocator(), GetGraph()->GetLocalAllocator()).CloneGraph();
3732 ASSERT_FALSE(GetGraph()->RunPass<ChecksElimination>());
3733 ASSERT_TRUE(GraphComparator().Compare(GetGraph(), initial));
3734 }
3735
BuildGraphRefTypeCheck()3736 void ChecksEliminationTest::BuildGraphRefTypeCheck()
3737 {
3738 GRAPH(GetGraph())
3739 {
3740 PARAMETER(0U, 0U).ref();
3741 PARAMETER(1U, 1U).ref();
3742 PARAMETER(2U, 2U).s32();
3743 PARAMETER(3U, 3U).s32();
3744 PARAMETER(4U, 4U).s32();
3745 CONSTANT(5U, nullptr);
3746 BASIC_BLOCK(2U, 1U)
3747 {
3748 INST(10U, Opcode::SaveState).Inputs(0U, 1U, 2U, 2U, 5U).SrcVregs({0U, 1U, 2U, 3U, 4U});
3749 INST(11U, Opcode::NullCheck).ref().Inputs(1U, 10U);
3750 INST(12U, Opcode::LenArray).s32().Inputs(11U);
3751 INST(13U, Opcode::BoundsCheck).s32().Inputs(12U, 2U, 10U);
3752 INST(14U, Opcode::RefTypeCheck).ref().Inputs(11U, 0U, 10U);
3753 INST(15U, Opcode::StoreArray).ref().Inputs(11U, 13U, 14U);
3754
3755 INST(20U, Opcode::SaveState).Inputs(0U, 1U, 2U, 2U, 5U).SrcVregs({0U, 1U, 2U, 3U, 4U});
3756 INST(21U, Opcode::NullCheck).ref().Inputs(1U, 20U);
3757 INST(22U, Opcode::LenArray).s32().Inputs(21U);
3758 INST(23U, Opcode::BoundsCheck).s32().Inputs(22U, 3U, 20U);
3759 INST(24U, Opcode::RefTypeCheck).ref().Inputs(21U, 0U, 20U);
3760 INST(25U, Opcode::StoreArray).ref().Inputs(21U, 23U, 24U);
3761
3762 INST(30U, Opcode::SaveState).Inputs(0U, 1U, 2U, 2U, 5U).SrcVregs({0U, 1U, 2U, 3U, 4U});
3763 INST(31U, Opcode::NullCheck).ref().Inputs(1U, 30U);
3764 INST(32U, Opcode::LenArray).s32().Inputs(31U);
3765 INST(33U, Opcode::BoundsCheck).s32().Inputs(32U, 4U, 30U);
3766 INST(34U, Opcode::RefTypeCheck).ref().Inputs(31U, 5U, 30U);
3767 INST(35U, Opcode::StoreArray).ref().Inputs(31U, 33U, 34U);
3768
3769 INST(6U, Opcode::ReturnVoid).v0id();
3770 }
3771 }
3772 }
3773
TEST_F(ChecksEliminationTest,RefTypeCheck)3774 TEST_F(ChecksEliminationTest, RefTypeCheck)
3775 {
3776 BuildGraphRefTypeCheck();
3777 // `24, Opcode::RefTypeCheck` is removed because `14, Opcode::RefTypeCheck` checks equal array and eqaul
3778 // Reference `34, Opcode::RefTypeCheck` is removed because store value id NullPtr
3779 ASSERT_TRUE(GetGraph()->RunPass<ChecksElimination>());
3780 auto graph = CreateEmptyGraph();
3781 GRAPH(graph)
3782 {
3783 PARAMETER(0U, 0U).ref();
3784 PARAMETER(1U, 1U).ref();
3785 PARAMETER(2U, 2U).s32();
3786 PARAMETER(3U, 3U).s32();
3787 PARAMETER(4U, 4U).s32();
3788 CONSTANT(5U, nullptr);
3789 BASIC_BLOCK(2U, 1U)
3790 {
3791 INST(10U, Opcode::SaveState).Inputs(0U, 1U, 2U, 2U, 5U).SrcVregs({0U, 1U, 2U, 3U, 4U});
3792 INST(11U, Opcode::NullCheck).ref().Inputs(1U, 10U);
3793 INST(12U, Opcode::LenArray).s32().Inputs(11U);
3794 INST(13U, Opcode::BoundsCheck).s32().Inputs(12U, 2U, 10U);
3795 INST(14U, Opcode::RefTypeCheck).ref().Inputs(11U, 0U, 10U);
3796 INST(15U, Opcode::StoreArray).ref().Inputs(11U, 13U, 14U);
3797
3798 INST(20U, Opcode::SaveState).Inputs(0U, 1U, 2U, 2U, 5U).SrcVregs({0U, 1U, 2U, 3U, 4U});
3799 INST(21U, Opcode::NOP);
3800 INST(22U, Opcode::LenArray).s32().Inputs(11U);
3801 INST(23U, Opcode::BoundsCheck).s32().Inputs(22U, 3U, 20U);
3802 INST(24U, Opcode::NOP);
3803 INST(25U, Opcode::StoreArray).ref().Inputs(11U, 23U, 14U);
3804
3805 INST(30U, Opcode::SaveState).Inputs(0U, 1U, 2U, 2U, 5U).SrcVregs({0U, 1U, 2U, 3U, 4U});
3806 INST(31U, Opcode::NOP);
3807 INST(32U, Opcode::LenArray).s32().Inputs(11U);
3808 INST(33U, Opcode::BoundsCheck).s32().Inputs(32U, 4U, 30U);
3809 INST(34U, Opcode::NOP);
3810 INST(35U, Opcode::StoreArray).ref().Inputs(11U, 33U, 5U);
3811
3812 INST(6U, Opcode::ReturnVoid).v0id();
3813 }
3814 }
3815 ASSERT_TRUE(GraphComparator().Compare(GetGraph(), graph));
3816 }
3817
BuildGraphRefTypeCheckFirstNullCheckEliminated()3818 void ChecksEliminationTest::BuildGraphRefTypeCheckFirstNullCheckEliminated()
3819 {
3820 GRAPH(GetGraph())
3821 {
3822 PARAMETER(0U, 0U).ref();
3823 PARAMETER(1U, 2U).s32();
3824 PARAMETER(2U, 3U).s32();
3825 CONSTANT(3U, 10U);
3826 BASIC_BLOCK(2U, 1U)
3827 {
3828 INST(4U, Opcode::SaveState).Inputs(0U, 1U, 2U).SrcVregs({0U, 1U, 2U});
3829 INST(5U, Opcode::NewArray).ref().Inputs(0U, 3U, 4U);
3830 INST(6U, Opcode::RefTypeCheck).ref().Inputs(5U, 0U, 4U);
3831 INST(7U, Opcode::StoreArray).ref().Inputs(5U, 1U, 6U);
3832
3833 INST(14U, Opcode::SaveState).Inputs(0U, 1U, 2U).SrcVregs({0U, 1U, 2U});
3834 INST(15U, Opcode::NullCheck).ref().Inputs(5U, 14U);
3835 INST(16U, Opcode::RefTypeCheck).ref().Inputs(15U, 0U, 14U);
3836 INST(17U, Opcode::StoreArray).ref().Inputs(5U, 2U, 16U);
3837
3838 INST(18U, Opcode::ReturnVoid).v0id();
3839 }
3840 }
3841 }
3842
TEST_F(ChecksEliminationTest,RefTypeCheckFirstNullCheckEliminated)3843 TEST_F(ChecksEliminationTest, RefTypeCheckFirstNullCheckEliminated)
3844 {
3845 BuildGraphRefTypeCheckFirstNullCheckEliminated();
3846 ASSERT_TRUE(GetGraph()->RunPass<ChecksElimination>());
3847 auto graph = CreateEmptyGraph();
3848 GRAPH(graph)
3849 {
3850 PARAMETER(0U, 0U).ref();
3851 PARAMETER(1U, 2U).s32();
3852 PARAMETER(2U, 3U).s32();
3853 CONSTANT(3U, 10U);
3854 BASIC_BLOCK(2U, 1U)
3855 {
3856 INST(4U, Opcode::SaveState).Inputs(0U, 1U, 2U).SrcVregs({0U, 1U, 2U});
3857 INST(5U, Opcode::NewArray).ref().Inputs(0U, 3U, 4U);
3858 INST(6U, Opcode::RefTypeCheck).ref().Inputs(5U, 0U, 4U);
3859 INST(7U, Opcode::StoreArray).ref().Inputs(5U, 1U, 6U);
3860
3861 INST(14U, Opcode::SaveState).Inputs(0U, 1U, 2U).SrcVregs({0U, 1U, 2U});
3862 INST(15U, Opcode::NOP);
3863 INST(16U, Opcode::NOP);
3864 INST(17U, Opcode::StoreArray).ref().Inputs(5U, 2U, 6U);
3865
3866 INST(18U, Opcode::ReturnVoid).v0id();
3867 }
3868 }
3869 ASSERT_TRUE(GraphComparator().Compare(GetGraph(), graph));
3870 }
3871
TEST_F(ChecksEliminationTest,RefTypeCheckEqualInputs)3872 TEST_F(ChecksEliminationTest, RefTypeCheckEqualInputs)
3873 {
3874 GRAPH(GetGraph())
3875 {
3876 PARAMETER(0U, 0U).ref();
3877 PARAMETER(1U, 2U).s32();
3878 PARAMETER(2U, 3U).s32();
3879 CONSTANT(3U, 10U);
3880 BASIC_BLOCK(2U, 1U)
3881 {
3882 INST(4U, Opcode::SaveState).Inputs(0U, 1U, 2U).SrcVregs({0U, 1U, 2U});
3883 INST(5U, Opcode::NewArray).ref().Inputs(0U, 3U, 4U);
3884 INST(6U, Opcode::RefTypeCheck).ref().Inputs(5U, 0U, 4U);
3885 INST(7U, Opcode::StoreArray).ref().Inputs(5U, 1U, 6U);
3886
3887 INST(14U, Opcode::SaveState).Inputs(0U, 1U, 2U).SrcVregs({0U, 1U, 2U});
3888 INST(16U, Opcode::RefTypeCheck).ref().Inputs(5U, 5U, 14U);
3889 INST(17U, Opcode::StoreArray).ref().Inputs(5U, 2U, 16U);
3890
3891 INST(18U, Opcode::ReturnVoid).v0id();
3892 }
3893 }
3894 auto clone = GraphCloner(GetGraph(), GetGraph()->GetAllocator(), GetGraph()->GetLocalAllocator()).CloneGraph();
3895 ASSERT_FALSE(GetGraph()->RunPass<ChecksElimination>());
3896 ASSERT_TRUE(GraphComparator().Compare(clone, GetGraph()));
3897 }
3898
BuildHoistRefTypeCheckGraph()3899 void ChecksEliminationTest::BuildHoistRefTypeCheckGraph()
3900 {
3901 GRAPH(GetGraph())
3902 {
3903 CONSTANT(0U, 0U); // initial
3904 CONSTANT(1U, 1U); // increment
3905 CONSTANT(2U, 10U);
3906 PARAMETER(28U, 0U).ref();
3907 PARAMETER(29U, 1U).ref();
3908 BASIC_BLOCK(2U, 3U, 5U)
3909 {
3910 INST(20U, Opcode::SaveStateDeoptimize).Inputs(0U, 1U, 2U, 28U, 29U).SrcVregs({0U, 1U, 2U, 3U, 4U});
3911 INST(5U, Opcode::Compare).SrcType(DataType::INT32).CC(CC_LT).b().Inputs(0U, 2U); // 0 < 10
3912 INST(6U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(5U);
3913 }
3914 BASIC_BLOCK(3U, 3U, 5U)
3915 {
3916 INST(4U, Opcode::Phi).s32().Inputs(0U, 10U); // i
3917
3918 INST(30U, Opcode::SaveState).Inputs(28U, 29U).SrcVregs({0U, 1U});
3919 INST(31U, Opcode::NullCheck).ref().Inputs(29U, 30U);
3920 INST(32U, Opcode::LenArray).s32().Inputs(31U);
3921 INST(34U, Opcode::RefTypeCheck).ref().Inputs(31U, 28U, 30U);
3922 INST(35U, Opcode::StoreArray).ref().Inputs(31U, 4U, 34U);
3923
3924 INST(10U, Opcode::Add).s32().Inputs(4U, 1U); // i++
3925 INST(13U, Opcode::Compare).CC(CC_LT).b().Inputs(10U, 2U); // i < 10
3926 INST(14U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(13U);
3927 }
3928 BASIC_BLOCK(5U, 1U)
3929 {
3930 INST(12U, Opcode::ReturnVoid).v0id();
3931 }
3932 }
3933 }
3934
TEST_F(ChecksEliminationTest,HoistRefTypeCheckTest)3935 TEST_F(ChecksEliminationTest, HoistRefTypeCheckTest)
3936 {
3937 BuildHoistRefTypeCheckGraph();
3938 ASSERT_TRUE(GetGraph()->RunPass<ChecksElimination>());
3939
3940 auto graph = CreateEmptyGraph();
3941 GRAPH(graph)
3942 {
3943 CONSTANT(0U, 0U); // initial
3944 CONSTANT(1U, 1U); // increment
3945 CONSTANT(2U, 10U);
3946 PARAMETER(28U, 0U).ref();
3947 PARAMETER(29U, 1U).ref();
3948 BASIC_BLOCK(2U, 3U, 5U)
3949 {
3950 INST(20U, Opcode::SaveStateDeoptimize).Inputs(0U, 1U, 2U, 28U, 29U).SrcVregs({0U, 1U, 2U, 3U, 4U});
3951 INST(31U, Opcode::NullCheck).ref().Inputs(29U, 20U).SetFlag(inst_flags::CAN_DEOPTIMIZE);
3952 INST(34U, Opcode::RefTypeCheck).ref().Inputs(31U, 28U, 20U).SetFlag(inst_flags::CAN_DEOPTIMIZE);
3953 INST(5U, Opcode::Compare).SrcType(DataType::INT32).CC(CC_LT).b().Inputs(0U, 2U); // 0 < 10
3954 INST(6U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(5U);
3955 }
3956 BASIC_BLOCK(3U, 3U, 5U)
3957 {
3958 INST(4U, Opcode::Phi).s32().Inputs(0U, 10U); // i
3959
3960 INST(30U, Opcode::SaveState).Inputs(28U, 29U).SrcVregs({0U, 1U});
3961 INST(32U, Opcode::LenArray).s32().Inputs(31U);
3962 INST(35U, Opcode::StoreArray).ref().Inputs(31U, 4U, 34U);
3963
3964 INST(10U, Opcode::Add).s32().Inputs(4U, 1U); // i++
3965 INST(13U, Opcode::Compare).CC(CC_LT).b().Inputs(10U, 2U); // i < 10
3966 INST(14U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(13U);
3967 }
3968 BASIC_BLOCK(5U, 1U)
3969 {
3970 INST(12U, Opcode::ReturnVoid).v0id();
3971 }
3972 }
3973 ASSERT_TRUE(GraphComparator().Compare(GetGraph(), graph));
3974 }
3975
TEST_F(ChecksEliminationTest,NotLenArrayInput)3976 TEST_F(ChecksEliminationTest, NotLenArrayInput)
3977 {
3978 GRAPH(GetGraph())
3979 {
3980 PARAMETER(0U, 0U).s32();
3981 CONSTANT(4U, 1U);
3982 CONSTANT(7U, 10U);
3983 BASIC_BLOCK(2U, 3U)
3984 {
3985 INST(1U, Opcode::SaveStateDeoptimize).Inputs(0U).SrcVregs({0U});
3986 }
3987 BASIC_BLOCK(3U, 4U, 5U)
3988 {
3989 INST(3U, Opcode::Phi).s32().Inputs(0U, 5U);
3990 INST(6U, Opcode::Compare).b().CC(CC_GE).Inputs(3U, 7U);
3991 INST(8U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(6U);
3992 }
3993 BASIC_BLOCK(5U, 3U)
3994 {
3995 INST(9U, Opcode::SaveState).Inputs(0U).SrcVregs({0U});
3996 INST(10U, Opcode::NegativeCheck).s32().Inputs(3U, 9U);
3997 INST(44U, Opcode::LoadAndInitClass).ref().Inputs(9U).TypeId(68U);
3998 INST(11U, Opcode::NewArray).ref().Inputs(44U, 10U, 9U);
3999 INST(12U, Opcode::BoundsCheck).s32().Inputs(3U, 0U, 9U);
4000 INST(13U, Opcode::StoreArray).s32().Inputs(11U, 12U, 0U);
4001 INST(5U, Opcode::Add).s32().Inputs(3U, 4U);
4002 }
4003 BASIC_BLOCK(4U, -1L)
4004 {
4005 INST(2U, Opcode::ReturnVoid).v0id();
4006 }
4007 }
4008 auto clone = GraphCloner(GetGraph(), GetGraph()->GetAllocator(), GetGraph()->GetLocalAllocator()).CloneGraph();
4009 ASSERT_FALSE(GetGraph()->RunPass<ChecksElimination>());
4010 ASSERT_TRUE(GraphComparator().Compare(clone, GetGraph()));
4011 }
4012
BuildGraphBugWithNullCheck()4013 void ChecksEliminationTest::BuildGraphBugWithNullCheck()
4014 {
4015 GRAPH(GetGraph())
4016 {
4017 CONSTANT(0U, 0U); // initial
4018 CONSTANT(1U, 1U); // increment
4019 PARAMETER(27U, 1U).s32(); // X
4020 BASIC_BLOCK(2U, 6U, 3U)
4021 {
4022 INST(43U, Opcode::SaveState).Inputs(0U).SrcVregs({0U});
4023 INST(44U, Opcode::LoadAndInitClass).ref().Inputs().TypeId(68U);
4024 INST(13U, Opcode::NewArray).ref().Inputs(44U, 27U, 43U);
4025 INST(30U, Opcode::SaveStateDeoptimize).Inputs(0U).SrcVregs({0U});
4026 INST(14U, Opcode::Compare).CC(ConditionCode::CC_GE).b().Inputs(0U, 27U); // i < X
4027 INST(15U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(14U);
4028 }
4029 BASIC_BLOCK(3U, 6U, 3U)
4030 {
4031 INST(4U, Opcode::Phi).s32().Inputs(0U, 10U);
4032 INST(7U, Opcode::SaveState).Inputs(0U, 1U, 13U, 27U).SrcVregs({0U, 1U, 2U, 3U});
4033 INST(16U, Opcode::NullCheck).ref().Inputs(13U, 7U);
4034 INST(17U, Opcode::LenArray).s32().Inputs(16U);
4035 INST(8U, Opcode::BoundsCheck).s32().Inputs(17U, 4U, 7U);
4036 INST(9U, Opcode::StoreArray).s32().Inputs(16U, 8U, 0U); // a[i] = 0
4037 INST(10U, Opcode::Add).s32().Inputs(4U, 1U); // i++
4038 INST(5U, Opcode::Compare).CC(ConditionCode::CC_GE).b().Inputs(10U, 27U); // i < X
4039 INST(6U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(5U);
4040 }
4041 BASIC_BLOCK(6U, 1U)
4042 {
4043 INST(26U, Opcode::Phi).s32().Inputs(0U, 10U);
4044 INST(12U, Opcode::Return).s32().Inputs(26U);
4045 }
4046 }
4047 }
4048
TEST_F(ChecksEliminationTest,BugWithNullCheck)4049 TEST_F(ChecksEliminationTest, BugWithNullCheck)
4050 {
4051 BuildGraphBugWithNullCheck();
4052 ASSERT_TRUE(GetGraph()->RunPass<ChecksElimination>());
4053 auto graph1 = CreateEmptyGraph();
4054 GRAPH(graph1)
4055 {
4056 CONSTANT(0U, 0U); // initial
4057 CONSTANT(1U, 1U); // increment
4058 PARAMETER(2U, 1U).s32();
4059 BASIC_BLOCK(2U, 5U, 3U)
4060 {
4061 INST(43U, Opcode::SaveState).Inputs(0U).SrcVregs({0U});
4062 INST(44U, Opcode::LoadAndInitClass).ref().Inputs().TypeId(68U);
4063 INST(3U, Opcode::NewArray).ref().Inputs(44U, 2U, 43U);
4064 INST(20U, Opcode::SaveStateDeoptimize).Inputs(0U).SrcVregs({0U});
4065 INST(34U, Opcode::NOP);
4066 INST(22U, Opcode::LenArray).s32().Inputs(3U);
4067 INST(23U, Opcode::Compare).SrcType(DataType::INT32).CC(CC_LT).b().Inputs(22U, 2U); // len_array < X
4068 INST(24U, Opcode::DeoptimizeIf).DeoptimizeType(DeoptimizeType::BOUNDS_CHECK).Inputs(23U, 20U);
4069 INST(5U, Opcode::Compare).SrcType(DataType::INT32).CC(CC_GE).b().Inputs(0U, 2U); // 0 < X
4070 INST(6U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(5U);
4071 }
4072 BASIC_BLOCK(3U, 5U, 3U)
4073 {
4074 INST(4U, Opcode::Phi).s32().Inputs(0U, 10U);
4075 INST(7U, Opcode::SaveState).Inputs(0U, 1U, 3U, 2U).SrcVregs({0U, 1U, 2U, 3U});
4076 INST(15U, Opcode::NOP);
4077 INST(16U, Opcode::LenArray).s32().Inputs(3U);
4078 INST(8U, Opcode::NOP);
4079 INST(9U, Opcode::StoreArray).s32().Inputs(3U, 4U, 0U); // a[i] = 0
4080 INST(10U, Opcode::Add).s32().Inputs(4U, 1U); // i++
4081 INST(13U, Opcode::Compare).CC(CC_GE).b().Inputs(10U, 2U); // i < X
4082 INST(14U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(13U);
4083 }
4084 BASIC_BLOCK(5U, 1U)
4085 {
4086 INST(26U, Opcode::Phi).s32().Inputs(0U, 10U);
4087 INST(12U, Opcode::Return).s32().Inputs(26U);
4088 }
4089 }
4090 ASSERT_TRUE(GraphComparator().Compare(GetGraph(), graph1));
4091 }
4092
BuildGraphNullAndBoundsChecksNestedLoop()4093 void ChecksEliminationTest::BuildGraphNullAndBoundsChecksNestedLoop()
4094 {
4095 GRAPH(GetGraph())
4096 {
4097 PARAMETER(0U, 0U).ref();
4098 CONSTANT(3U, 0U);
4099 CONSTANT(9U, 4U);
4100 CONSTANT(32U, 1U);
4101
4102 // fill 2D array
4103 BASIC_BLOCK(2U, 3U)
4104 {
4105 INST(2U, Opcode::SaveStateDeoptimize).Inputs(0U).SrcVregs({0U});
4106 }
4107 BASIC_BLOCK(3U, 7U, 8U)
4108 {
4109 INST(5U, Opcode::Phi).s32().Inputs(3U, 33U);
4110 INST(4U, Opcode::Compare).CC(CC_LE).b().Inputs(9U, 5U);
4111 INST(11U, Opcode::IfImm).CC(CC_NE).Imm(0U).Inputs(4U);
4112 }
4113 BASIC_BLOCK(8U, 4U)
4114 {
4115 INST(38U, Opcode::SaveStateDeoptimize).Inputs(0U, 3U, 5U).SrcVregs({0U, 1U, 2U});
4116 }
4117 BASIC_BLOCK(4U, 6U, 5U)
4118 {
4119 INST(15U, Opcode::Phi).s32().Inputs(3U, 31U);
4120 INST(19U, Opcode::Compare).CC(CC_LE).b().Inputs(9U, 15U);
4121 INST(20U, Opcode::IfImm).CC(CC_NE).Imm(0U).Inputs(19U);
4122 }
4123 BASIC_BLOCK(5U, 4U)
4124 {
4125 INST(21U, Opcode::SaveState).Inputs(0U, 3U, 5U, 15U).SrcVregs({0U, 1U, 2U, 3U});
4126 INST(22U, Opcode::NullCheck).ref().Inputs(0U, 21U);
4127 INST(23U, Opcode::LenArray).s32().Inputs(22U);
4128 INST(24U, Opcode::BoundsCheck).s32().Inputs(23U, 5U, 21U);
4129 INST(25U, Opcode::LoadArray).ref().Inputs(22U, 24U);
4130
4131 INST(26U, Opcode::SaveState).Inputs(0U, 3U, 5U, 15U, 25U).SrcVregs({0U, 1U, 2U, 3U, 4U});
4132 INST(27U, Opcode::NullCheck).ref().Inputs(25U, 26U);
4133 INST(28U, Opcode::LenArray).s32().Inputs(27U);
4134 INST(29U, Opcode::BoundsCheck).s32().Inputs(28U, 15U, 26U);
4135 INST(30U, Opcode::StoreArray).s32().Inputs(27U, 29U, 5U); // a[i][j] = i
4136 INST(31U, Opcode::Add).s32().Inputs(15U, 32U);
4137 }
4138 BASIC_BLOCK(6U, 3U)
4139 {
4140 INST(33U, Opcode::Add).s32().Inputs(5U, 32U);
4141 }
4142 BASIC_BLOCK(7U, -1L)
4143 {
4144 INST(34U, Opcode::ReturnVoid).v0id();
4145 }
4146 }
4147 }
4148
TEST_F(ChecksEliminationTest,NullAndBoundsChecksNestedLoop)4149 TEST_F(ChecksEliminationTest, NullAndBoundsChecksNestedLoop)
4150 {
4151 BuildGraphNullAndBoundsChecksNestedLoop();
4152 ASSERT_TRUE(GetGraph()->RunPass<ChecksElimination>());
4153
4154 auto graph = CreateEmptyGraph();
4155 GRAPH(graph)
4156 {
4157 PARAMETER(0U, 0U).ref();
4158 CONSTANT(3U, 0U);
4159 CONSTANT(9U, 4U);
4160 CONSTANT(32U, 1U);
4161
4162 BASIC_BLOCK(2U, 3U)
4163 {
4164 INST(2U, Opcode::SaveStateDeoptimize).Inputs(0U).SrcVregs({0U});
4165 INST(35U, Opcode::NullCheck).ref().Inputs(0U, 2U).SetFlag(inst_flags::CAN_DEOPTIMIZE);
4166 INST(39U, Opcode::LenArray).s32().Inputs(35U);
4167 INST(44U, Opcode::Compare).b().SrcType(DataType::INT32).CC(CC_LT).Inputs(39U, 9U);
4168 INST(45U, Opcode::DeoptimizeIf).DeoptimizeType(DeoptimizeType::BOUNDS_CHECK).Inputs(44U, 2U);
4169 }
4170 BASIC_BLOCK(3U, 7U, 8U)
4171 {
4172 INST(5U, Opcode::Phi).s32().Inputs(3U, 33U);
4173 INST(4U, Opcode::Compare).CC(CC_LE).b().Inputs(9U, 5U);
4174 INST(11U, Opcode::IfImm).CC(CC_NE).Imm(0U).Inputs(4U);
4175 }
4176 BASIC_BLOCK(8U, 4U)
4177 {
4178 INST(38U, Opcode::SaveStateDeoptimize).Inputs(0U, 3U, 5U).SrcVregs({0U, 1U, 2U});
4179 // we could put DeoptimizeIf NULL_CHECK here, but this is suboptimal
4180 }
4181 BASIC_BLOCK(4U, 6U, 5U)
4182 {
4183 INST(15U, Opcode::Phi).s32().Inputs(3U, 31U);
4184 INST(19U, Opcode::Compare).CC(CC_LE).b().Inputs(9U, 15U);
4185 INST(20U, Opcode::IfImm).CC(CC_NE).Imm(0U).Inputs(19U);
4186 }
4187 BASIC_BLOCK(5U, 4U)
4188 {
4189 INST(21U, Opcode::SaveState).Inputs(0U, 3U, 5U, 15U).SrcVregs({0U, 1U, 2U, 3U});
4190 INST(22U, Opcode::NOP);
4191 INST(23U, Opcode::LenArray).s32().Inputs(35U);
4192 INST(24U, Opcode::NOP);
4193 INST(25U, Opcode::LoadArray).ref().Inputs(35U, 5U);
4194 INST(26U, Opcode::SaveState).Inputs(0U, 3U, 5U, 15U, 25U).SrcVregs({0U, 1U, 2U, 3U, 4U});
4195 INST(27U, Opcode::NullCheck).ref().Inputs(25U, 26U);
4196 INST(28U, Opcode::LenArray).s32().Inputs(27U);
4197 INST(29U, Opcode::BoundsCheck).s32().Inputs(28U, 15U, 26U);
4198 INST(30U, Opcode::StoreArray).s32().Inputs(27U, 29U, 5U); // a[i][j] = i
4199 INST(31U, Opcode::Add).s32().Inputs(15U, 32U);
4200 }
4201 BASIC_BLOCK(6U, 3U)
4202 {
4203 INST(33U, Opcode::Add).s32().Inputs(5U, 32U);
4204 }
4205 BASIC_BLOCK(7U, -1L)
4206 {
4207 INST(34U, Opcode::ReturnVoid).v0id();
4208 }
4209 }
4210 ASSERT_TRUE(GraphComparator().Compare(GetGraph(), graph));
4211 }
4212
BuildGraphLoopWithTwoPhi()4213 void ChecksEliminationTest::BuildGraphLoopWithTwoPhi()
4214 {
4215 GRAPH(GetGraph())
4216 {
4217 PARAMETER(0U, 0U).ref();
4218 PARAMETER(1U, 1U).s32();
4219 PARAMETER(2U, 2U).s32();
4220 CONSTANT(3U, 0U);
4221 CONSTANT(4U, 1U);
4222 BASIC_BLOCK(2U, 3U)
4223 {
4224 INST(5U, Opcode::SaveStateDeoptimize).Inputs(0U, 1U, 2U, 3U, 4U).SrcVregs({0U, 1U, 2U, 3U, 4U});
4225 INST(6U, Opcode::SaveState).Inputs(0U, 1U, 2U, 3U, 4U).SrcVregs({0U, 1U, 2U, 3U, 4U});
4226 INST(7U, Opcode::NullCheck).ref().Inputs(0U, 6U);
4227 INST(8U, Opcode::LoadObject).ref().Inputs(7U);
4228 }
4229 BASIC_BLOCK(3U, 4U, 5U)
4230 {
4231 INST(9U, Opcode::Phi).s32().Inputs(1U, 17U);
4232 INST(18U, Opcode::Phi).s32().Inputs(3U, 19U);
4233 INST(10U, Opcode::Compare).b().CC(CC_GE).Inputs(9U, 2U);
4234 INST(11U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(10U);
4235 }
4236 BASIC_BLOCK(5U, 3U)
4237 {
4238 INST(12U, Opcode::SaveState).Inputs(0U, 1U, 2U, 3U, 4U, 8U).SrcVregs({0U, 1U, 2U, 3U, 4U, 5U});
4239 INST(13U, Opcode::NullCheck).ref().Inputs(8U, 12U);
4240 INST(14U, Opcode::LenArray).s32().Inputs(13U);
4241 INST(15U, Opcode::BoundsCheck).s32().Inputs(14U, 9U, 12U);
4242 INST(16U, Opcode::LoadArray).s32().Inputs(13U, 15U);
4243 INST(17U, Opcode::Add).s32().Inputs(9U, 4U);
4244 INST(19U, Opcode::Add).s32().Inputs(18U, 16U);
4245 }
4246 BASIC_BLOCK(4U, -1L)
4247 {
4248 INST(20U, Opcode::Return).s32().Inputs(18U);
4249 }
4250 }
4251 }
4252
TEST_F(ChecksEliminationTest,LoopWithTwoPhi)4253 TEST_F(ChecksEliminationTest, LoopWithTwoPhi)
4254 {
4255 BuildGraphLoopWithTwoPhi();
4256 ASSERT_TRUE(GetGraph()->RunPass<ChecksElimination>());
4257 auto graph1 = CreateEmptyGraph();
4258 GRAPH(graph1)
4259 {
4260 PARAMETER(0U, 0U).ref();
4261 PARAMETER(1U, 1U).s32();
4262 PARAMETER(2U, 2U).s32();
4263 CONSTANT(3U, 0U);
4264 CONSTANT(4U, 1U);
4265 BASIC_BLOCK(2U, 3U)
4266 {
4267 INST(5U, Opcode::SaveStateDeoptimize).Inputs(0U, 1U, 2U, 3U, 4U).SrcVregs({0U, 1U, 2U, 3U, 4U});
4268 INST(6U, Opcode::SaveState).Inputs(0U, 1U, 2U, 3U, 4U).SrcVregs({0U, 1U, 2U, 3U, 4U});
4269 INST(7U, Opcode::NullCheck).ref().Inputs(0U, 6U);
4270 INST(8U, Opcode::LoadObject).ref().Inputs(7U);
4271 INST(22U, Opcode::NullCheck).ref().Inputs(8U, 6U).SetFlag(inst_flags::CAN_DEOPTIMIZE);
4272 }
4273 BASIC_BLOCK(3U, 4U, 5U)
4274 {
4275 INST(9U, Opcode::Phi).s32().Inputs(1U, 17U);
4276 INST(18U, Opcode::Phi).s32().Inputs(3U, 19U);
4277 INST(10U, Opcode::Compare).b().CC(CC_GE).Inputs(9U, 2U);
4278 INST(11U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(10U);
4279 }
4280 BASIC_BLOCK(5U, 3U)
4281 {
4282 INST(12U, Opcode::SaveState).Inputs(0U, 1U, 2U, 3U, 4U, 8U).SrcVregs({0U, 1U, 2U, 3U, 4U, 5U});
4283 INST(14U, Opcode::LenArray).s32().Inputs(22U);
4284 INST(15U, Opcode::BoundsCheck).s32().Inputs(14U, 9U, 12U);
4285 INST(16U, Opcode::LoadArray).s32().Inputs(22U, 15U);
4286 INST(17U, Opcode::Add).s32().Inputs(9U, 4U);
4287 INST(19U, Opcode::Add).s32().Inputs(18U, 16U);
4288 }
4289 BASIC_BLOCK(4U, -1L)
4290 {
4291 INST(20U, Opcode::Return).s32().Inputs(18U);
4292 }
4293 }
4294 ASSERT_TRUE(GraphComparator().Compare(GetGraph(), graph1));
4295 }
4296
BuildGraphLoopWithBigStepGE()4297 void ChecksEliminationTest::BuildGraphLoopWithBigStepGE()
4298 {
4299 GRAPH(GetGraph())
4300 {
4301 CONSTANT(0U, 0U);
4302 CONSTANT(1U, 4U); // increment
4303 CONSTANT(2U, 3U);
4304 PARAMETER(13U, 0U).ref(); // Array
4305 PARAMETER(27U, 1U).s32(); // X
4306 BASIC_BLOCK(2U, 3U)
4307 {
4308 INST(30U, Opcode::SaveStateDeoptimize).Inputs(0U).SrcVregs({0U});
4309 }
4310 BASIC_BLOCK(3U, 5U, 4U)
4311 {
4312 INST(4U, Opcode::Phi).s32().Inputs(27U, 10U);
4313 INST(14U, Opcode::Compare).CC(ConditionCode::CC_LT).b().Inputs(4U, 0U); // i >= 0
4314 INST(15U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(14U);
4315 }
4316 BASIC_BLOCK(4U, 3U)
4317 {
4318 INST(7U, Opcode::SaveState).Inputs(0U, 1U, 13U, 27U).SrcVregs({0U, 1U, 2U, 3U});
4319 INST(16U, Opcode::NullCheck).ref().Inputs(13U, 7U);
4320 INST(17U, Opcode::LenArray).s32().Inputs(16U);
4321 INST(8U, Opcode::BoundsCheck).s32().Inputs(17U, 4U, 7U);
4322 INST(9U, Opcode::StoreArray).s32().Inputs(16U, 8U, 4U); // a[i] = i
4323 INST(10U, Opcode::Sub).s32().Inputs(4U, 1U); // i -= 4
4324 }
4325 BASIC_BLOCK(5U, 1U)
4326 {
4327 INST(12U, Opcode::ReturnVoid).v0id();
4328 }
4329 }
4330 }
4331
TEST_F(ChecksEliminationTest,LoopWithBigStepGE)4332 TEST_F(ChecksEliminationTest, LoopWithBigStepGE)
4333 {
4334 BuildGraphLoopWithBigStepGE();
4335 ASSERT_TRUE(GetGraph()->RunPass<ChecksElimination>());
4336 auto graph1 = CreateEmptyGraph();
4337 GRAPH(graph1)
4338 {
4339 CONSTANT(0U, 0U);
4340 CONSTANT(1U, 4U); // increment
4341 CONSTANT(2U, 3U);
4342 PARAMETER(13U, 0U).ref(); // Array
4343 PARAMETER(27U, 1U).s32(); // X
4344 BASIC_BLOCK(2U, 3U)
4345 {
4346 INST(30U, Opcode::SaveStateDeoptimize).Inputs(0U).SrcVregs({0U});
4347 INST(33U, Opcode::NullCheck).ref().Inputs(13U, 30U).SetFlag(inst_flags::CAN_DEOPTIMIZE);
4348 INST(31U, Opcode::LenArray).s32().Inputs(33U);
4349 INST(36U, Opcode::Compare).SrcType(DataType::INT32).CC(CC_LE).b().Inputs(31U, 27U);
4350 // DeoptimizeIf len_array <= X
4351 INST(37U, Opcode::DeoptimizeIf).DeoptimizeType(DeoptimizeType::BOUNDS_CHECK).Inputs(36U, 30U);
4352 }
4353 BASIC_BLOCK(3U, 5U, 4U)
4354 {
4355 INST(4U, Opcode::Phi).s32().Inputs(27U, 10U);
4356 INST(14U, Opcode::Compare).CC(ConditionCode::CC_LT).b().Inputs(4U, 0U); // i >= 0
4357 INST(15U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(14U);
4358 }
4359 BASIC_BLOCK(4U, 3U)
4360 {
4361 INST(7U, Opcode::SaveState).Inputs(0U, 1U, 13U, 27U).SrcVregs({0U, 1U, 2U, 3U});
4362 INST(16U, Opcode::NOP);
4363 INST(17U, Opcode::LenArray).s32().Inputs(33U);
4364 INST(8U, Opcode::NOP);
4365 INST(9U, Opcode::StoreArray).s32().Inputs(33U, 4U, 4U); // a[i] = i
4366 INST(10U, Opcode::Sub).s32().Inputs(4U, 1U); // i -= 4
4367 }
4368 BASIC_BLOCK(5U, 1U)
4369 {
4370 INST(12U, Opcode::ReturnVoid).v0id();
4371 }
4372 }
4373 ASSERT_TRUE(GraphComparator().Compare(GetGraph(), graph1));
4374 }
4375
BuildGraphLoopWithBigStepLE()4376 void ChecksEliminationTest::BuildGraphLoopWithBigStepLE()
4377 {
4378 GRAPH(GetGraph())
4379 {
4380 CONSTANT(0U, 2U); // initial
4381 CONSTANT(1U, 8U); // increment
4382 CONSTANT(2U, 3U);
4383 PARAMETER(13U, 0U).ref(); // Array
4384 PARAMETER(27U, 1U).s32(); // X
4385 BASIC_BLOCK(2U, 6U, 3U)
4386 {
4387 INST(30U, Opcode::SaveStateDeoptimize).Inputs(0U).SrcVregs({0U});
4388 INST(14U, Opcode::Compare).CC(ConditionCode::CC_GT).b().Inputs(0U, 27U); // i <= X
4389 INST(15U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(14U);
4390 }
4391 BASIC_BLOCK(3U, 6U, 3U)
4392 {
4393 INST(4U, Opcode::Phi).s32().Inputs(0U, 10U);
4394 INST(7U, Opcode::SaveState).Inputs(0U, 1U, 13U, 27U).SrcVregs({0U, 1U, 2U, 3U});
4395 INST(16U, Opcode::NullCheck).ref().Inputs(13U, 7U);
4396 INST(17U, Opcode::LenArray).s32().Inputs(16U);
4397 INST(8U, Opcode::BoundsCheck).s32().Inputs(17U, 4U, 7U);
4398 INST(9U, Opcode::LoadArray).s32().Inputs(16U, 8U); // load a[i]
4399 INST(18U, Opcode::Add).s32().Inputs(4U, 2U);
4400 INST(19U, Opcode::BoundsCheck).s32().Inputs(17U, 18U, 7U);
4401 INST(20U, Opcode::StoreArray).s32().Inputs(16U, 19U, 9U); // a[i + 3] = a[i]
4402 INST(10U, Opcode::Add).s32().Inputs(4U, 1U); // i += 8
4403 INST(5U, Opcode::Compare).CC(ConditionCode::CC_GT).b().Inputs(10U, 27U); // i <= X
4404 INST(6U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(5U);
4405 }
4406 BASIC_BLOCK(6U, 1U)
4407 {
4408 INST(26U, Opcode::Phi).s32().Inputs(0U, 10U);
4409 INST(12U, Opcode::Return).s32().Inputs(26U);
4410 }
4411 }
4412 }
4413
TEST_F(ChecksEliminationTest,LoopWithBigStepLE)4414 TEST_F(ChecksEliminationTest, LoopWithBigStepLE)
4415 {
4416 BuildGraphLoopWithBigStepLE();
4417 ASSERT_TRUE(GetGraph()->RunPass<ChecksElimination>());
4418 auto graph1 = CreateEmptyGraph();
4419 GRAPH(graph1)
4420 {
4421 CONSTANT(0U, 2U); // initial
4422 CONSTANT(1U, 8U); // increment
4423 CONSTANT(2U, 3U);
4424 PARAMETER(13U, 0U).ref(); // Array
4425 PARAMETER(27U, 1U).s32(); // X
4426 CONSTANT(42U, 0x7ffffff7U);
4427
4428 BASIC_BLOCK(2U, 6U, 3U)
4429 {
4430 INST(30U, Opcode::SaveStateDeoptimize).Inputs(0U).SrcVregs({0U});
4431 INST(44U, Opcode::Compare).Inputs(42U, 27U).CC(CC_LT).b();
4432 INST(45U, Opcode::DeoptimizeIf).DeoptimizeType(DeoptimizeType::BOUNDS_CHECK).Inputs(44U, 30U);
4433 INST(33U, Opcode::NullCheck).ref().Inputs(13U, 30U).SetFlag(inst_flags::CAN_DEOPTIMIZE);
4434 INST(31U, Opcode::LenArray).s32().Inputs(33U);
4435 INST(36U, Opcode::Sub).s32().Inputs(27U, 0U);
4436 INST(37U, Opcode::Mod).s32().Inputs(36U, 1U);
4437 INST(38U, Opcode::Sub).s32().Inputs(27U, 37U);
4438 INST(39U, Opcode::Sub).s32().Inputs(31U, 2U);
4439 INST(40U, Opcode::Compare).b().CC(CC_LE).Inputs(39U, 38U);
4440 // DeoptimizeIf len - 3 <= X - (X - 2) % 8
4441 INST(41U, Opcode::DeoptimizeIf).DeoptimizeType(DeoptimizeType::BOUNDS_CHECK).Inputs(40U, 30U);
4442 INST(14U, Opcode::Compare).CC(ConditionCode::CC_GT).b().Inputs(0U, 27U); // i <= X
4443 INST(15U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(14U);
4444 }
4445 BASIC_BLOCK(3U, 6U, 3U)
4446 {
4447 INST(4U, Opcode::Phi).s32().Inputs(0U, 10U);
4448 INST(7U, Opcode::SaveState).Inputs(0U, 1U, 13U, 27U).SrcVregs({0U, 1U, 2U, 3U});
4449 INST(16U, Opcode::NOP);
4450 INST(17U, Opcode::LenArray).s32().Inputs(33U);
4451 INST(8U, Opcode::NOP);
4452 INST(9U, Opcode::LoadArray).s32().Inputs(33U, 4U); // load a[i]
4453 INST(18U, Opcode::Add).s32().Inputs(4U, 2U);
4454 INST(19U, Opcode::NOP);
4455 INST(20U, Opcode::StoreArray).s32().Inputs(33U, 18U, 9U); // a[i + 3] = a[i]
4456 INST(10U, Opcode::Add).s32().Inputs(4U, 1U); // i += 8
4457 INST(5U, Opcode::Compare).CC(ConditionCode::CC_GT).b().Inputs(10U, 27U); // i <= X
4458 INST(6U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(5U);
4459 }
4460 BASIC_BLOCK(6U, 1U)
4461 {
4462 INST(26U, Opcode::Phi).s32().Inputs(0U, 10U);
4463 INST(12U, Opcode::Return).s32().Inputs(26U);
4464 }
4465 }
4466 ASSERT_TRUE(GraphComparator().Compare(GetGraph(), graph1));
4467 }
4468
BuildGraphLoopWithBigStepLT()4469 void ChecksEliminationTest::BuildGraphLoopWithBigStepLT()
4470 {
4471 GRAPH(GetGraph())
4472 {
4473 CONSTANT(0U, 2U); // initial
4474 CONSTANT(1U, 8U); // increment
4475 CONSTANT(2U, 3U);
4476 PARAMETER(13U, 0U).ref(); // Array
4477 PARAMETER(27U, 1U).s32(); // X
4478 BASIC_BLOCK(2U, 6U, 3U)
4479 {
4480 INST(30U, Opcode::SaveStateDeoptimize).Inputs(0U).SrcVregs({0U});
4481 INST(14U, Opcode::Compare).CC(ConditionCode::CC_GE).b().Inputs(0U, 27U); // i < X
4482 INST(15U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(14U);
4483 }
4484 BASIC_BLOCK(3U, 6U, 3U)
4485 {
4486 INST(4U, Opcode::Phi).s32().Inputs(0U, 10U);
4487 INST(7U, Opcode::SaveState).Inputs(0U, 1U, 13U, 27U).SrcVregs({0U, 1U, 2U, 3U});
4488 INST(16U, Opcode::NullCheck).ref().Inputs(13U, 7U);
4489 INST(17U, Opcode::LenArray).s32().Inputs(16U);
4490 INST(8U, Opcode::BoundsCheck).s32().Inputs(17U, 4U, 7U);
4491 INST(9U, Opcode::LoadArray).s32().Inputs(16U, 8U); // load a[i]
4492 INST(18U, Opcode::Add).s32().Inputs(4U, 2U);
4493 INST(19U, Opcode::BoundsCheck).s32().Inputs(17U, 18U, 7U);
4494 INST(20U, Opcode::StoreArray).s32().Inputs(16U, 19U, 9U); // a[i + 3] = a[i]
4495 INST(10U, Opcode::Add).s32().Inputs(4U, 1U); // i += 8
4496 INST(5U, Opcode::Compare).CC(ConditionCode::CC_GE).b().Inputs(10U, 27U); // i < X
4497 INST(6U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(5U);
4498 }
4499 BASIC_BLOCK(6U, 1U)
4500 {
4501 INST(26U, Opcode::Phi).s32().Inputs(0U, 10U);
4502 INST(12U, Opcode::Return).s32().Inputs(26U);
4503 }
4504 }
4505 }
4506
TEST_F(ChecksEliminationTest,LoopWithBigStepLT)4507 TEST_F(ChecksEliminationTest, LoopWithBigStepLT)
4508 {
4509 BuildGraphLoopWithBigStepLT();
4510 ASSERT_TRUE(GetGraph()->RunPass<ChecksElimination>());
4511 auto graph1 = CreateEmptyGraph();
4512 GRAPH(graph1)
4513 {
4514 CONSTANT(0U, 2U); // initial
4515 CONSTANT(1U, 8U); // increment
4516 CONSTANT(2U, 3U);
4517 PARAMETER(13U, 0U).ref(); // Array
4518 PARAMETER(27U, 1U).s32(); // X
4519 CONSTANT(43U, 1U);
4520 CONSTANT(42U, 0x7ffffff8U);
4521
4522 BASIC_BLOCK(2U, 6U, 3U)
4523 {
4524 INST(30U, Opcode::SaveStateDeoptimize).Inputs(0U).SrcVregs({0U});
4525 INST(44U, Opcode::Compare).Inputs(42U, 27U).CC(CC_LT).b();
4526 INST(45U, Opcode::DeoptimizeIf).DeoptimizeType(DeoptimizeType::BOUNDS_CHECK).Inputs(44U, 30U);
4527 INST(33U, Opcode::NullCheck).ref().Inputs(13U, 30U).SetFlag(inst_flags::CAN_DEOPTIMIZE);
4528 INST(31U, Opcode::LenArray).s32().Inputs(33U);
4529 INST(35U, Opcode::Add).s32().Inputs(0U, 43U);
4530 INST(36U, Opcode::Sub).s32().Inputs(27U, 35U);
4531 INST(37U, Opcode::Mod).s32().Inputs(36U, 1U);
4532 INST(38U, Opcode::Sub).s32().Inputs(27U, 37U);
4533 INST(39U, Opcode::Sub).s32().Inputs(31U, 2U);
4534 INST(40U, Opcode::Compare).b().CC(CC_LT).Inputs(39U, 38U);
4535 // DeoptimizeIf len - 3 < X - (X - (2 + 1)) % 8
4536 INST(41U, Opcode::DeoptimizeIf).DeoptimizeType(DeoptimizeType::BOUNDS_CHECK).Inputs(40U, 30U);
4537 INST(14U, Opcode::Compare).CC(ConditionCode::CC_GE).b().Inputs(0U, 27U); // i < X
4538 INST(15U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(14U);
4539 }
4540 BASIC_BLOCK(3U, 6U, 3U)
4541 {
4542 INST(4U, Opcode::Phi).s32().Inputs(0U, 10U);
4543 INST(7U, Opcode::SaveState).Inputs(0U, 1U, 13U, 27U).SrcVregs({0U, 1U, 2U, 3U});
4544 INST(16U, Opcode::NOP);
4545 INST(17U, Opcode::LenArray).s32().Inputs(33U);
4546 INST(8U, Opcode::NOP);
4547 INST(9U, Opcode::LoadArray).s32().Inputs(33U, 4U); // load a[i]
4548 INST(18U, Opcode::Add).s32().Inputs(4U, 2U);
4549 INST(19U, Opcode::NOP);
4550 INST(20U, Opcode::StoreArray).s32().Inputs(33U, 18U, 9U); // a[i + 3] = a[i]
4551 INST(10U, Opcode::Add).s32().Inputs(4U, 1U); // i += 8
4552 INST(5U, Opcode::Compare).CC(ConditionCode::CC_GE).b().Inputs(10U, 27U); // i < X
4553 INST(6U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(5U);
4554 }
4555 BASIC_BLOCK(6U, 1U)
4556 {
4557 INST(26U, Opcode::Phi).s32().Inputs(0U, 10U);
4558 INST(12U, Opcode::Return).s32().Inputs(26U);
4559 }
4560 }
4561 ASSERT_TRUE(GraphComparator().Compare(GetGraph(), graph1));
4562 }
4563
BuildGraphLoopWithBoundsCheckUnderIfGE()4564 void ChecksEliminationTest::BuildGraphLoopWithBoundsCheckUnderIfGE()
4565 {
4566 GRAPH(GetGraph())
4567 {
4568 CONSTANT(0U, 0U); // initial
4569 CONSTANT(1U, 1U); // increment
4570 CONSTANT(3U, 3U);
4571 PARAMETER(2U, 0U).ref(); // array
4572 PARAMETER(4U, 1U).s32(); // X
4573
4574 BASIC_BLOCK(2U, 3U)
4575 {
4576 INST(5U, Opcode::SaveState).Inputs(0U, 1U, 2U).SrcVregs({0U, 1U, 2U});
4577 INST(6U, Opcode::NullCheck).ref().Inputs(2U, 5U);
4578 INST(7U, Opcode::LenArray).s32().Inputs(6U);
4579
4580 INST(30U, Opcode::SaveStateDeoptimize).Inputs(0U).SrcVregs({0U});
4581 }
4582
4583 BASIC_BLOCK(3U, 4U, 8U)
4584 {
4585 INST(8U, Opcode::Phi).s32().Inputs(0U, 24U); // i
4586 INST(25U, Opcode::Phi).s32().Inputs(0U, 23U); // sum
4587 INST(9U, Opcode::Compare).CC(CC_LT).b().Inputs(8U, 4U); // i < X
4588 INST(10U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(9U);
4589 }
4590 BASIC_BLOCK(4U, 5U, 6U)
4591 {
4592 INST(14U, Opcode::Compare).CC(CC_LE).b().Inputs(3U, 8U); // 3 <= i
4593 INST(15U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(14U);
4594 }
4595 BASIC_BLOCK(5U, 7U)
4596 {
4597 INST(16U, Opcode::Sub).s32().Inputs(8U, 3U);
4598 INST(27U, Opcode::SaveState).Inputs(0U, 1U, 2U, 3U).SrcVregs({0U, 1U, 2U, 3U});
4599 INST(17U, Opcode::BoundsCheck).s32().Inputs(7U, 16U, 27U);
4600 INST(18U, Opcode::LoadArray).s32().Inputs(6U, 17U); // a[i - 3]
4601 }
4602 BASIC_BLOCK(6U, 7U)
4603 {
4604 INST(19U, Opcode::SaveState).Inputs(0U, 1U, 2U, 3U).SrcVregs({0U, 1U, 2U, 3U});
4605 INST(20U, Opcode::BoundsCheck).s32().Inputs(7U, 8U, 19U);
4606 INST(21U, Opcode::LoadArray).s32().Inputs(6U, 20U); // a[i]
4607 }
4608 BASIC_BLOCK(7U, 3U)
4609 {
4610 INST(22U, Opcode::Phi).s32().Inputs(18U, 21U);
4611 INST(23U, Opcode::Add).s32().Inputs(25U, 22U);
4612 INST(24U, Opcode::Add).s32().Inputs(8U, 1U);
4613 }
4614 BASIC_BLOCK(8U, 1U)
4615 {
4616 INST(26U, Opcode::Return).s32().Inputs(25U);
4617 }
4618 }
4619 }
4620
4621 // Lower bound is correct in each branch based on BoundsAnalysis, build deoptimize only for upper bound
TEST_F(ChecksEliminationTest,LoopWithBoundsCheckUnderIfGE)4622 TEST_F(ChecksEliminationTest, LoopWithBoundsCheckUnderIfGE)
4623 {
4624 BuildGraphLoopWithBoundsCheckUnderIfGE();
4625 ASSERT_TRUE(GetGraph()->RunPass<ChecksElimination>());
4626 auto graph1 = CreateEmptyGraph();
4627 GRAPH(graph1)
4628 {
4629 CONSTANT(0U, 0U); // initial
4630 CONSTANT(1U, 1U); // increment
4631 CONSTANT(3U, 3U);
4632 PARAMETER(2U, 0U).ref(); // array
4633 PARAMETER(4U, 1U).s32(); // X
4634
4635 BASIC_BLOCK(2U, 3U)
4636 {
4637 INST(5U, Opcode::SaveState).Inputs(0U, 1U, 2U).SrcVregs({0U, 1U, 2U});
4638 INST(6U, Opcode::NullCheck).ref().Inputs(2U, 5U);
4639 INST(7U, Opcode::LenArray).s32().Inputs(6U);
4640
4641 INST(30U, Opcode::SaveStateDeoptimize).Inputs(0U).SrcVregs({0U});
4642 INST(31U, Opcode::Compare).b().CC(CC_LT).Inputs(7U, 4U);
4643 INST(32U, Opcode::DeoptimizeIf).DeoptimizeType(DeoptimizeType::BOUNDS_CHECK).Inputs(31U, 30U);
4644 }
4645
4646 BASIC_BLOCK(3U, 4U, 8U)
4647 {
4648 INST(8U, Opcode::Phi).s32().Inputs(0U, 24U); // i
4649 INST(25U, Opcode::Phi).s32().Inputs(0U, 23U); // sum
4650 INST(9U, Opcode::Compare).CC(CC_LT).b().Inputs(8U, 4U); // i < X
4651 INST(10U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(9U);
4652 }
4653 BASIC_BLOCK(4U, 5U, 6U)
4654 {
4655 INST(14U, Opcode::Compare).CC(CC_LE).b().Inputs(3U, 8U); // 3 <= i
4656 INST(15U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(14U);
4657 }
4658 BASIC_BLOCK(5U, 7U)
4659 {
4660 INST(16U, Opcode::Sub).s32().Inputs(8U, 3U);
4661 INST(27U, Opcode::SaveState).Inputs(0U, 1U, 2U, 3U).SrcVregs({0U, 1U, 2U, 3U});
4662 INST(17U, Opcode::NOP);
4663 INST(18U, Opcode::LoadArray).s32().Inputs(6U, 16U); // a[i - 3]
4664 }
4665 BASIC_BLOCK(6U, 7U)
4666 {
4667 INST(19U, Opcode::SaveState).Inputs(0U, 1U, 2U, 3U).SrcVregs({0U, 1U, 2U, 3U});
4668 INST(20U, Opcode::NOP);
4669 INST(21U, Opcode::LoadArray).s32().Inputs(6U, 8U); // a[i]
4670 }
4671 BASIC_BLOCK(7U, 3U)
4672 {
4673 INST(22U, Opcode::Phi).s32().Inputs(18U, 21U);
4674 INST(23U, Opcode::Add).s32().Inputs(25U, 22U);
4675 INST(24U, Opcode::Add).s32().Inputs(8U, 1U);
4676 }
4677 BASIC_BLOCK(8U, 1U)
4678 {
4679 INST(26U, Opcode::Return).s32().Inputs(25U);
4680 }
4681 }
4682 ASSERT_TRUE(GraphComparator().Compare(GetGraph(), graph1));
4683 }
4684
BuildGraphLoopWithBoundsCheckUnderIfLT()4685 void ChecksEliminationTest::BuildGraphLoopWithBoundsCheckUnderIfLT()
4686 {
4687 GRAPH(GetGraph())
4688 {
4689 CONSTANT(0U, 0U); // initial
4690 CONSTANT(1U, 1U); // increment
4691 CONSTANT(3U, 3U);
4692 PARAMETER(2U, 0U).ref(); // array
4693 PARAMETER(4U, 1U).s32(); // X
4694
4695 BASIC_BLOCK(2U, 3U, 8U)
4696 {
4697 INST(5U, Opcode::SaveState).Inputs(0U, 1U, 2U).SrcVregs({0U, 1U, 2U});
4698 INST(6U, Opcode::NullCheck).ref().Inputs(2U, 5U);
4699 INST(7U, Opcode::LenArray).s32().Inputs(6U);
4700
4701 INST(30U, Opcode::SaveStateDeoptimize).Inputs(0U).SrcVregs({0U});
4702 INST(31U, Opcode::Compare).CC(CC_LT).b().Inputs(4U, 7U); // X < len_array
4703 INST(32U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(31U);
4704 }
4705
4706 BASIC_BLOCK(3U, 4U, 8U)
4707 {
4708 INST(8U, Opcode::Phi).s32().Inputs(4U, 24U); // i = X
4709 INST(25U, Opcode::Phi).s32().Inputs(0U, 23U); // sum
4710 INST(9U, Opcode::Compare).CC(CC_LT).b().Inputs(8U, 7U); // i < len_array
4711 INST(10U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(9U);
4712 }
4713 BASIC_BLOCK(4U, 5U, 6U)
4714 {
4715 INST(13U, Opcode::Add).s32().Inputs(8U, 3U);
4716 INST(14U, Opcode::Compare).CC(CC_LT).b().Inputs(13U, 7U); // i + 3 < len_array
4717 INST(15U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(14U);
4718 }
4719 BASIC_BLOCK(5U, 6U)
4720 {
4721 INST(20U, Opcode::SaveState).Inputs(0U, 1U, 2U, 3U).SrcVregs({0U, 1U, 2U, 3U});
4722 INST(17U, Opcode::BoundsCheck).s32().Inputs(7U, 13U, 20U);
4723 INST(18U, Opcode::LoadArray).s32().Inputs(6U, 17U);
4724 INST(19U, Opcode::Mul).s32().Inputs(8U, 18U); // i * a[i + 3]
4725 }
4726 BASIC_BLOCK(6U, 3U)
4727 {
4728 INST(22U, Opcode::Phi).s32().Inputs(8U, 19U);
4729 INST(23U, Opcode::Add).s32().Inputs(25U, 22U);
4730 INST(24U, Opcode::Add).s32().Inputs(8U, 1U);
4731 }
4732 BASIC_BLOCK(8U, 1U)
4733 {
4734 INST(26U, Opcode::Phi).s32().Inputs(0U, 25U);
4735 INST(27U, Opcode::Return).s32().Inputs(26U);
4736 }
4737 }
4738 }
4739
4740 // Upper bound is correct in each branch based on BoundsAnalysis, build deoptimize only for lower bound
TEST_F(ChecksEliminationTest,LoopWithBoundsCheckUnderIfLT)4741 TEST_F(ChecksEliminationTest, LoopWithBoundsCheckUnderIfLT)
4742 {
4743 BuildGraphLoopWithBoundsCheckUnderIfLT();
4744 ASSERT_TRUE(GetGraph()->RunPass<ChecksElimination>());
4745 auto graph1 = CreateEmptyGraph();
4746 GRAPH(graph1)
4747 {
4748 CONSTANT(0U, 0U); // initial
4749 CONSTANT(1U, 1U); // increment
4750 CONSTANT(3U, 3U);
4751 PARAMETER(2U, 0U).ref(); // array
4752 PARAMETER(4U, 1U).s32(); // X
4753
4754 BASIC_BLOCK(2U, 3U, 8U)
4755 {
4756 INST(5U, Opcode::SaveState).Inputs(0U, 1U, 2U).SrcVregs({0U, 1U, 2U});
4757 INST(6U, Opcode::NullCheck).ref().Inputs(2U, 5U);
4758 INST(7U, Opcode::LenArray).s32().Inputs(6U);
4759
4760 INST(30U, Opcode::SaveStateDeoptimize).Inputs(0U).SrcVregs({0U});
4761 INST(33U, Opcode::Add).s32().Inputs(4U, 3U);
4762 INST(34U, Opcode::Compare).b().CC(CC_LT).Inputs(33U, 0U); // X + 3 < 0
4763 INST(35U, Opcode::DeoptimizeIf).DeoptimizeType(DeoptimizeType::BOUNDS_CHECK).Inputs(34U, 30U);
4764 INST(31U, Opcode::Compare).CC(CC_LT).b().Inputs(4U, 7U); // X < len_array
4765 INST(32U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(31U);
4766 }
4767 BASIC_BLOCK(3U, 4U, 8U)
4768 {
4769 INST(8U, Opcode::Phi).s32().Inputs(4U, 24U); // i = X
4770 INST(25U, Opcode::Phi).s32().Inputs(0U, 23U); // sum
4771 INST(9U, Opcode::Compare).CC(CC_LT).b().Inputs(8U, 7U); // i < len_array
4772 INST(10U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(9U);
4773 }
4774 BASIC_BLOCK(4U, 5U, 6U)
4775 {
4776 INST(13U, Opcode::Add).s32().Inputs(8U, 3U);
4777 INST(14U, Opcode::Compare).CC(CC_LT).b().Inputs(13U, 7U); // i + 3 < len_array
4778 INST(15U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(14U);
4779 }
4780 BASIC_BLOCK(5U, 6U)
4781 {
4782 INST(20U, Opcode::SaveState).Inputs(0U, 1U, 2U, 3U).SrcVregs({0U, 1U, 2U, 3U});
4783 INST(17U, Opcode::NOP);
4784 INST(18U, Opcode::LoadArray).s32().Inputs(6U, 13U);
4785 INST(19U, Opcode::Mul).s32().Inputs(8U, 18U); // i * a[i + 3]
4786 }
4787 BASIC_BLOCK(6U, 3U)
4788 {
4789 INST(22U, Opcode::Phi).s32().Inputs(8U, 19U);
4790 INST(23U, Opcode::Add).s32().Inputs(25U, 22U);
4791 INST(24U, Opcode::Add).s32().Inputs(8U, 1U);
4792 }
4793 BASIC_BLOCK(8U, 1U)
4794 {
4795 INST(26U, Opcode::Phi).s32().Inputs(0U, 25U);
4796 INST(27U, Opcode::Return).s32().Inputs(26U);
4797 }
4798 }
4799 ASSERT_TRUE(GraphComparator().Compare(GetGraph(), graph1));
4800 }
4801
TEST_F(ChecksEliminationTest,DeoptTest)4802 TEST_F(ChecksEliminationTest, DeoptTest)
4803 {
4804 GRAPH(GetGraph())
4805 {
4806 CONSTANT(0U, 0U);
4807 CONSTANT(1U, nullptr);
4808 BASIC_BLOCK(2U, 1U)
4809 {
4810 INST(2U, Opcode::SaveState).Inputs(0U, 1U).SrcVregs({0U, 1U});
4811 INST(3U, Opcode::NullCheck).ref().Inputs(1U, 2U);
4812 INST(4U, Opcode::ZeroCheck).s32().Inputs(0U, 2U);
4813 INST(5U, Opcode::ReturnVoid).v0id();
4814 }
4815 }
4816 ASSERT_TRUE(GetGraph()->RunPass<ChecksElimination>());
4817 auto graph = CreateEmptyGraph();
4818 GRAPH(graph)
4819 {
4820 CONSTANT(0U, 0U);
4821 CONSTANT(1U, nullptr);
4822 BASIC_BLOCK(2U, 1U)
4823 {
4824 INST(2U, Opcode::SaveState).Inputs(0U, 1U).SrcVregs({0U, 1U});
4825 INST(3U, Opcode::Deoptimize).DeoptimizeType(DeoptimizeType::NULL_CHECK).Inputs(2U);
4826 }
4827 }
4828 }
4829
TEST_F(ChecksEliminationTest,CheckCastEqualInputs)4830 TEST_F(ChecksEliminationTest, CheckCastEqualInputs)
4831 {
4832 // Check Elimination for CheckCast is applied.
4833 GRAPH(GetGraph())
4834 {
4835 PARAMETER(0U, 0U).ref();
4836 BASIC_BLOCK(2U, 1U)
4837 {
4838 INST(1U, Opcode::SaveState).Inputs(0U).SrcVregs({0U});
4839 INST(2U, Opcode::LoadClass).ref().Inputs(1U).TypeId(1U);
4840 INST(3U, Opcode::CheckCast).TypeId(1U).Inputs(0U, 2U, 1U);
4841 INST(4U, Opcode::CheckCast).TypeId(1U).Inputs(0U, 2U, 1U);
4842 INST(5U, Opcode::Return).ref().Inputs(0U);
4843 }
4844 }
4845 ASSERT_TRUE(GetGraph()->RunPass<ChecksElimination>());
4846 auto graph = CreateEmptyGraph();
4847 GRAPH(graph)
4848 {
4849 PARAMETER(0U, 0U).ref();
4850 BASIC_BLOCK(2U, 1U)
4851 {
4852 INST(1U, Opcode::SaveState).Inputs(0U).SrcVregs({0U});
4853 INST(2U, Opcode::LoadClass).ref().Inputs(1U).TypeId(1U);
4854 INST(3U, Opcode::CheckCast).TypeId(1U).Inputs(0U, 2U, 1U);
4855 INST(4U, Opcode::NOP);
4856 INST(5U, Opcode::Return).ref().Inputs(0U);
4857 }
4858 }
4859 ASSERT_TRUE(GraphComparator().Compare(GetGraph(), graph));
4860 }
4861
TEST_F(ChecksEliminationTest,CheckCastDifferentInputs)4862 TEST_F(ChecksEliminationTest, CheckCastDifferentInputs)
4863 {
4864 // Check Elimination for CheckCast is not applied.
4865 GRAPH(GetGraph())
4866 {
4867 PARAMETER(0U, 0U).ref();
4868 PARAMETER(1U, 1U).ref();
4869 BASIC_BLOCK(2U, 1U)
4870 {
4871 INST(8U, Opcode::SaveState).Inputs(0U).SrcVregs({0U});
4872 INST(2U, Opcode::LoadClass).ref().Inputs(8U).TypeId(1U);
4873 INST(3U, Opcode::LoadClass).ref().Inputs(8U).TypeId(2U);
4874 INST(4U, Opcode::CheckCast).TypeId(1U).Inputs(0U, 2U, 8U);
4875 INST(5U, Opcode::CheckCast).TypeId(2U).Inputs(0U, 3U, 8U);
4876 INST(6U, Opcode::CheckCast).TypeId(2U).Inputs(1U, 3U, 8U);
4877 INST(7U, Opcode::Return).ref().Inputs(0U);
4878 }
4879 }
4880 auto clone = GraphCloner(GetGraph(), GetGraph()->GetAllocator(), GetGraph()->GetLocalAllocator()).CloneGraph();
4881 ASSERT_FALSE(GetGraph()->RunPass<ChecksElimination>());
4882 ASSERT_TRUE(GraphComparator().Compare(GetGraph(), clone));
4883 }
4884
BuildGraphCheckCastAfterIsInstance()4885 void ChecksEliminationTest::BuildGraphCheckCastAfterIsInstance()
4886 {
4887 GRAPH(GetGraph())
4888 {
4889 PARAMETER(0U, 0U).ref();
4890 CONSTANT(9U, nullptr);
4891 BASIC_BLOCK(2U, 3U, 4U)
4892 {
4893 INST(1U, Opcode::SaveState).Inputs(0U).SrcVregs({0U});
4894 INST(2U, Opcode::LoadClass).ref().Inputs(1U).TypeId(1U);
4895 INST(3U, Opcode::IsInstance).b().Inputs(0U, 2U).TypeId(1U);
4896 INST(4U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_EQ).Imm(0U).Inputs(3U);
4897 }
4898
4899 BASIC_BLOCK(4U, 1U)
4900 {
4901 INST(5U, Opcode::CheckCast).TypeId(1U).Inputs(0U, 2U, 1U);
4902 INST(6U, Opcode::LoadClass).ref().Inputs(1U).TypeId(2U);
4903 INST(7U, Opcode::CheckCast).TypeId(2U).Inputs(0U, 2U, 1U);
4904 INST(8U, Opcode::Return).ref().Inputs(0U);
4905 }
4906
4907 BASIC_BLOCK(3U, 1U)
4908 {
4909 INST(10U, Opcode::Return).ref().Inputs(9U);
4910 }
4911 }
4912 }
4913
TEST_F(ChecksEliminationTest,CheckCastAfterIsInstance)4914 TEST_F(ChecksEliminationTest, CheckCastAfterIsInstance)
4915 {
4916 // CheckCast after successful IsInstance can be removed.
4917 BuildGraphCheckCastAfterIsInstance();
4918
4919 ASSERT_TRUE(GetGraph()->RunPass<ChecksElimination>());
4920 ASSERT_TRUE(INS(7U).CastToCheckCast()->GetOmitNullCheck());
4921
4922 auto graph = CreateEmptyGraph();
4923 GRAPH(graph)
4924 {
4925 PARAMETER(0U, 0U).ref();
4926 CONSTANT(9U, nullptr);
4927 BASIC_BLOCK(2U, 3U, 4U)
4928 {
4929 INST(1U, Opcode::SaveState).Inputs(0U).SrcVregs({0U});
4930 INST(2U, Opcode::LoadClass).ref().Inputs(1U).TypeId(1U);
4931 INST(3U, Opcode::IsInstance).b().Inputs(0U, 2U).TypeId(1U);
4932 INST(4U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_EQ).Imm(0U).Inputs(3U);
4933 }
4934
4935 BASIC_BLOCK(4U, 1U)
4936 {
4937 INST(5U, Opcode::NOP);
4938 INST(6U, Opcode::LoadClass).ref().Inputs(1U).TypeId(2U);
4939 INST(7U, Opcode::CheckCast).TypeId(2U).Inputs(0U, 2U, 1U);
4940 INST(8U, Opcode::Return).ref().Inputs(0U);
4941 }
4942
4943 BASIC_BLOCK(3U, 1U)
4944 {
4945 INST(10U, Opcode::Return).ref().Inputs(9U);
4946 }
4947 }
4948 ASSERT_TRUE(GraphComparator().Compare(GetGraph(), graph));
4949 }
4950
TEST_F(ChecksEliminationTest,CheckCastAfterIsInstanceTriangleCase)4951 TEST_F(ChecksEliminationTest, CheckCastAfterIsInstanceTriangleCase)
4952 {
4953 // CheckCast cannot be removed because dominating IsInstance can be false.
4954 GRAPH(GetGraph())
4955 {
4956 PARAMETER(0U, 0U).ref();
4957 CONSTANT(9U, nullptr);
4958 BASIC_BLOCK(2U, 3U, 4U)
4959 {
4960 INST(1U, Opcode::SaveState).Inputs(0U).SrcVregs({0U});
4961 INST(2U, Opcode::LoadClass).ref().Inputs(1U).TypeId(1U);
4962 INST(3U, Opcode::IsInstance).b().Inputs(0U, 2U).TypeId(1U);
4963 INST(4U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_EQ).Imm(0U).Inputs(3U);
4964 }
4965
4966 BASIC_BLOCK(3U, 4U) {}
4967
4968 BASIC_BLOCK(4U, 1U)
4969 {
4970 INST(5U, Opcode::Phi).ref().Inputs({{2U, 9U}, {3U, 0U}});
4971 INST(6U, Opcode::CheckCast).TypeId(1U).Inputs(0U, 2U, 1U);
4972 INST(7U, Opcode::Return).ref().Inputs(5U);
4973 }
4974 }
4975 auto clone = GraphCloner(GetGraph(), GetGraph()->GetAllocator(), GetGraph()->GetLocalAllocator()).CloneGraph();
4976 ASSERT_FALSE(GetGraph()->RunPass<ChecksElimination>());
4977 ASSERT_TRUE(GraphComparator().Compare(GetGraph(), clone));
4978 }
4979
TEST_F(ChecksEliminationTest,CheckCastAfterIsInstanceDiamondCase)4980 TEST_F(ChecksEliminationTest, CheckCastAfterIsInstanceDiamondCase)
4981 {
4982 // CheckCast cannot be removed because dominating IsInstance can be false.
4983 GRAPH(GetGraph())
4984 {
4985 PARAMETER(0U, 0U).ref();
4986 CONSTANT(9U, nullptr);
4987 BASIC_BLOCK(2U, 3U, 4U)
4988 {
4989 INST(1U, Opcode::SaveState).Inputs(0U).SrcVregs({0U});
4990 INST(2U, Opcode::LoadClass).ref().Inputs(1U).TypeId(1U);
4991 INST(3U, Opcode::IsInstance).b().Inputs(0U, 2U).TypeId(1U);
4992 INST(4U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_EQ).Imm(0U).Inputs(3U);
4993 }
4994
4995 BASIC_BLOCK(3U, 5U) {}
4996
4997 BASIC_BLOCK(4U, 5U) {}
4998
4999 BASIC_BLOCK(5U, 1U)
5000 {
5001 INST(5U, Opcode::Phi).ref().Inputs({{3U, 9U}, {4U, 0U}});
5002 INST(6U, Opcode::CheckCast).TypeId(1U).Inputs(0U, 2U, 1U);
5003 INST(7U, Opcode::Return).ref().Inputs(5U);
5004 }
5005 }
5006 auto clone = GraphCloner(GetGraph(), GetGraph()->GetAllocator(), GetGraph()->GetLocalAllocator()).CloneGraph();
5007 ASSERT_FALSE(GetGraph()->RunPass<ChecksElimination>());
5008 ASSERT_TRUE(GraphComparator().Compare(GetGraph(), clone));
5009 }
5010
BuildHoistCheckCastGraph()5011 void ChecksEliminationTest::BuildHoistCheckCastGraph()
5012 {
5013 GRAPH(GetGraph())
5014 {
5015 CONSTANT(0U, 0U); // initial
5016 CONSTANT(1U, 1U); // increment
5017 CONSTANT(2U, 10U);
5018 PARAMETER(3U, 0U).ref();
5019 BASIC_BLOCK(2U, 3U, 5U)
5020 {
5021 INST(21U, Opcode::SaveState).Inputs(0U, 1U, 2U, 3U).SrcVregs({0U, 1U, 2U, 3U});
5022 INST(22U, Opcode::LoadClass).ref().Inputs(21U).TypeId(1U);
5023 INST(20U, Opcode::SaveStateDeoptimize).Inputs(0U, 1U, 2U, 3U).SrcVregs({0U, 1U, 2U, 3U});
5024 INST(5U, Opcode::Compare).SrcType(DataType::INT32).CC(CC_LT).b().Inputs(0U, 2U); // 0 < 10
5025 INST(6U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(5U);
5026 }
5027 BASIC_BLOCK(3U, 3U, 5U)
5028 {
5029 INST(4U, Opcode::Phi).s32().Inputs(0U, 10U); // i
5030 INST(7U, Opcode::SaveState).Inputs(0U, 1U, 2U, 3U).SrcVregs({0U, 1U, 2U, 3U});
5031 INST(8U, Opcode::CheckCast).ref().Inputs(3U, 22U, 7U).TypeId(1U);
5032 INST(9U, Opcode::LoadObject).ref().Inputs(3U);
5033 INST(23U, Opcode::SaveState).Inputs(0U, 1U, 2U, 3U, 9U).SrcVregs({0U, 1U, 2U, 3U, 4U});
5034 INST(11U, Opcode::CheckCast).ref().Inputs(9U, 22U, 23U).TypeId(1U);
5035
5036 INST(10U, Opcode::Add).s32().Inputs(4U, 1U); // i++
5037 INST(13U, Opcode::Compare).CC(CC_LT).b().Inputs(10U, 2U); // i < 10
5038 INST(14U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(13U);
5039 }
5040 BASIC_BLOCK(5U, 1U)
5041 {
5042 INST(12U, Opcode::ReturnVoid).v0id();
5043 }
5044 }
5045 }
5046
TEST_F(ChecksEliminationTest,HoistCheckCastTest)5047 TEST_F(ChecksEliminationTest, HoistCheckCastTest)
5048 {
5049 BuildHoistCheckCastGraph();
5050 ASSERT_TRUE(GetGraph()->RunPass<ChecksElimination>());
5051 auto graph = CreateEmptyGraph();
5052 GRAPH(graph)
5053 {
5054 CONSTANT(0U, 0U); // initial
5055 CONSTANT(1U, 1U); // increment
5056 CONSTANT(2U, 10U);
5057 PARAMETER(3U, 0U).ref();
5058 BASIC_BLOCK(2U, 3U, 5U)
5059 {
5060 INST(21U, Opcode::SaveState).Inputs(0U, 1U, 2U, 3U).SrcVregs({0U, 1U, 2U, 3U});
5061 INST(22U, Opcode::LoadClass).ref().Inputs(21U).TypeId(1U);
5062 INST(20U, Opcode::SaveStateDeoptimize).Inputs(0U, 1U, 2U, 3U).SrcVregs({0U, 1U, 2U, 3U});
5063 INST(8U, Opcode::CheckCast).ref().Inputs(3U, 22U, 20U).TypeId(1U).SetFlag(inst_flags::CAN_DEOPTIMIZE);
5064 INST(5U, Opcode::Compare).SrcType(DataType::INT32).CC(CC_LT).b().Inputs(0U, 2U); // 0 < 10
5065 INST(6U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(5U);
5066 }
5067 BASIC_BLOCK(3U, 3U, 5U)
5068 {
5069 INST(4U, Opcode::Phi).s32().Inputs(0U, 10U); // i
5070 INST(7U, Opcode::SaveState).Inputs(0U, 1U, 2U, 3U).SrcVregs({0U, 1U, 2U, 3U});
5071 INST(9U, Opcode::LoadObject).ref().Inputs(3U);
5072 INST(23U, Opcode::SaveState).Inputs(0U, 1U, 2U, 3U, 9U).SrcVregs({0U, 1U, 2U, 3U, 4U});
5073 INST(11U, Opcode::CheckCast).ref().Inputs(9U, 22U, 23U).TypeId(1U);
5074
5075 INST(10U, Opcode::Add).s32().Inputs(4U, 1U); // i++
5076 INST(13U, Opcode::Compare).CC(CC_LT).b().Inputs(10U, 2U); // i < 10
5077 INST(14U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(13U);
5078 }
5079 BASIC_BLOCK(5U, 1U)
5080 {
5081 INST(12U, Opcode::ReturnVoid).v0id();
5082 }
5083 }
5084 ASSERT_TRUE(GraphComparator().Compare(GetGraph(), graph));
5085 }
5086
TEST_F(ChecksEliminationTest,NullCheckAfterIsInstance)5087 TEST_F(ChecksEliminationTest, NullCheckAfterIsInstance)
5088 {
5089 // NullCheck after successful IsInstance can be removed.
5090 GRAPH(GetGraph())
5091 {
5092 PARAMETER(0U, 0U).ref();
5093 CONSTANT(9U, nullptr);
5094 BASIC_BLOCK(2U, 3U, 4U)
5095 {
5096 INST(1U, Opcode::SaveState).Inputs(0U).SrcVregs({0U});
5097 INST(2U, Opcode::LoadClass).ref().Inputs(1U).TypeId(1U);
5098 INST(3U, Opcode::IsInstance).b().Inputs(0U, 2U).TypeId(1U);
5099 INST(4U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_EQ).Imm(0U).Inputs(3U);
5100 }
5101
5102 BASIC_BLOCK(4U, 1U)
5103 {
5104 INST(5U, Opcode::NullCheck).ref().Inputs(0U, 1U);
5105 INST(6U, Opcode::Return).ref().Inputs(5U);
5106 }
5107
5108 BASIC_BLOCK(3U, 1U)
5109 {
5110 INST(10U, Opcode::Return).ref().Inputs(9U);
5111 }
5112 }
5113 ASSERT_TRUE(GetGraph()->RunPass<ChecksElimination>());
5114
5115 auto graph = CreateEmptyGraph();
5116 GRAPH(graph)
5117 {
5118 PARAMETER(0U, 0U).ref();
5119 CONSTANT(9U, nullptr);
5120 BASIC_BLOCK(2U, 3U, 4U)
5121 {
5122 INST(1U, Opcode::SaveState).Inputs(0U).SrcVregs({0U});
5123 INST(2U, Opcode::LoadClass).ref().Inputs(1U).TypeId(1U);
5124 INST(3U, Opcode::IsInstance).b().Inputs(0U, 2U).TypeId(1U);
5125 INST(4U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_EQ).Imm(0U).Inputs(3U);
5126 }
5127
5128 BASIC_BLOCK(4U, 1U)
5129 {
5130 INST(5U, Opcode::NOP);
5131 INST(6U, Opcode::Return).ref().Inputs(0U);
5132 }
5133
5134 BASIC_BLOCK(3U, 1U)
5135 {
5136 INST(10U, Opcode::Return).ref().Inputs(9U);
5137 }
5138 }
5139 ASSERT_TRUE(GraphComparator().Compare(GetGraph(), graph));
5140 }
5141
TEST_F(ChecksEliminationTest,OmitNullCheck)5142 TEST_F(ChecksEliminationTest, OmitNullCheck)
5143 {
5144 // Check Elimination for NullCheck is applied.
5145 GRAPH(GetGraph())
5146 {
5147 PARAMETER(0U, 0U).ref();
5148 CONSTANT(1U, 10U);
5149 BASIC_BLOCK(2U, 1U)
5150 {
5151 INST(2U, Opcode::SaveState).Inputs(0U, 1U).SrcVregs({0U, 1U});
5152 INST(3U, Opcode::NullCheck).ref().Inputs(0U, 2U);
5153 INST(4U, Opcode::LoadArray).s32().Inputs(3U, 1U);
5154 INST(5U, Opcode::SaveState).Inputs(0U, 1U).SrcVregs({0U, 1U});
5155 INST(6U, Opcode::LoadClass).ref().Inputs(5U);
5156 INST(7U, Opcode::CheckCast).TypeId(1U).Inputs(0U, 6U, 5U);
5157 INST(8U, Opcode::LoadClass).ref().Inputs(5U);
5158 INST(9U, Opcode::IsInstance).TypeId(2U).b().Inputs(0U, 8U, 5U);
5159 INST(10U, Opcode::Return).b().Inputs(9U);
5160 }
5161 }
5162 auto clone = GraphCloner(GetGraph(), GetGraph()->GetAllocator(), GetGraph()->GetLocalAllocator()).CloneGraph();
5163 ASSERT_TRUE(GetGraph()->RunPass<ChecksElimination>());
5164 ASSERT_TRUE(GraphComparator().Compare(GetGraph(), clone));
5165 ASSERT_TRUE(INS(7U).CastToCheckCast()->GetOmitNullCheck());
5166 ASSERT_TRUE(INS(9U).CastToIsInstance()->GetOmitNullCheck());
5167 }
5168
TEST_F(ChecksEliminationTest,DoNotOmitNullCheck)5169 TEST_F(ChecksEliminationTest, DoNotOmitNullCheck)
5170 {
5171 // NullCheck inside CheckCast and IsInstance cannot be omitted. NullCheck doesn't dominate them.
5172 GRAPH(GetGraph())
5173 {
5174 PARAMETER(0U, 0U).ref();
5175 CONSTANT(1U, 10U);
5176
5177 BASIC_BLOCK(2U, 3U, 4U)
5178 {
5179 INST(2U, Opcode::Compare).b().SrcType(DataType::Type::INT64).Inputs(1U, 1U);
5180 INST(3U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(2U);
5181 }
5182
5183 BASIC_BLOCK(3U, 5U)
5184 {
5185 INST(4U, Opcode::SaveState).Inputs(0U, 1U).SrcVregs({0U, 1U});
5186 INST(5U, Opcode::NullCheck).ref().Inputs(0U, 4U);
5187 INST(6U, Opcode::LoadArray).s32().Inputs(5U, 1U);
5188 }
5189
5190 BASIC_BLOCK(4U, 5U)
5191 {
5192 INST(7U, Opcode::SaveState).Inputs(0U, 1U).SrcVregs({0U, 1U});
5193 INST(8U, Opcode::LoadClass).TypeId(1U).ref().Inputs(7U);
5194 INST(9U, Opcode::CheckCast).TypeId(1U).Inputs(0U, 8U, 7U);
5195 INST(10U, Opcode::LoadClass).TypeId(2U).ref().Inputs(7U);
5196 INST(11U, Opcode::IsInstance).TypeId(2U).b().Inputs(0U, 10U, 7U);
5197 }
5198
5199 BASIC_BLOCK(5U, 1U)
5200 {
5201 INST(12U, Opcode::Phi).s32().Inputs(6U, 1U);
5202 INST(13U, Opcode::Return).s32().Inputs(12U);
5203 }
5204 }
5205 auto clone = GraphCloner(GetGraph(), GetGraph()->GetAllocator(), GetGraph()->GetLocalAllocator()).CloneGraph();
5206 ASSERT_FALSE(GetGraph()->RunPass<ChecksElimination>());
5207 ASSERT_TRUE(GraphComparator().Compare(GetGraph(), clone));
5208 ASSERT_FALSE(INS(9U).CastToCheckCast()->GetOmitNullCheck());
5209 ASSERT_FALSE(INS(11U).CastToIsInstance()->GetOmitNullCheck());
5210 }
5211
5212 // NOTE(schernykh): It's possible to remove boundschecks from this test, but BoundsAnalysis must be upgrade for
5213 // it.
TEST_F(ChecksEliminationTest,OptimizeBoundsCheckElimination)5214 TEST_F(ChecksEliminationTest, OptimizeBoundsCheckElimination)
5215 {
5216 GRAPH(GetGraph())
5217 {
5218 CONSTANT(0U, 0U);
5219 CONSTANT(14U, 1U);
5220 PARAMETER(1U, 0U).s32();
5221 PARAMETER(2U, 1U).s32();
5222 BASIC_BLOCK(2U, 6U, 3U)
5223 {
5224 INST(43U, Opcode::SaveState).Inputs(0U).SrcVregs({0U});
5225 INST(44U, Opcode::LoadAndInitClass).ref().Inputs().TypeId(68U);
5226 INST(3U, Opcode::NewArray).ref().Inputs(44U, 1U, 43U);
5227 INST(4U, Opcode::Compare).b().SrcType(DataType::Type::INT32).CC(CC_LT).Inputs(2U, 0U);
5228 INST(5U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(4U);
5229 }
5230 BASIC_BLOCK(3U, 6U, 4U)
5231 {
5232 INST(6U, Opcode::Compare).b().SrcType(DataType::Type::INT32).CC(CC_GE).Inputs(2U, 1U);
5233 INST(7U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(6U);
5234 }
5235 BASIC_BLOCK(4U, 6U, 5U)
5236 {
5237 INST(8U, Opcode::Add).s32().Inputs(2U, 14U);
5238 INST(9U, Opcode::Compare).b().SrcType(DataType::Type::INT32).CC(CC_GE).Inputs(8U, 1U);
5239 INST(10U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(9U);
5240 }
5241 BASIC_BLOCK(5U, 6U)
5242 {
5243 INST(11U, Opcode::SaveState).Inputs(0U, 1U, 2U).SrcVregs({0U, 1U, 2U});
5244 INST(12U, Opcode::BoundsCheck).s32().Inputs(1U, 8U, 11U);
5245 INST(13U, Opcode::StoreArray).s32().Inputs(3U, 8U, 0U);
5246 }
5247 BASIC_BLOCK(6U, 1U)
5248 {
5249 INST(15U, Opcode::ReturnVoid).v0id();
5250 }
5251 }
5252 auto clone = GraphCloner(GetGraph(), GetGraph()->GetAllocator(), GetGraph()->GetLocalAllocator()).CloneGraph();
5253 ASSERT_FALSE(GetGraph()->RunPass<ChecksElimination>());
5254 ASSERT_TRUE(GraphComparator().Compare(GetGraph(), clone));
5255 }
5256
TEST_F(ChecksEliminationTest,BoundsCheckEqualInputs)5257 TEST_F(ChecksEliminationTest, BoundsCheckEqualInputs)
5258 {
5259 GRAPH(GetGraph())
5260 {
5261 PARAMETER(1U, 2U).s32();
5262 PARAMETER(2U, 3U).s32();
5263 CONSTANT(3U, 10U);
5264 BASIC_BLOCK(2U, 1U)
5265 {
5266 INST(4U, Opcode::SaveState).Inputs(1U, 2U).SrcVregs({1U, 2U});
5267 INST(44U, Opcode::LoadAndInitClass).ref().Inputs().TypeId(68U);
5268 INST(5U, Opcode::NewArray).ref().Inputs(44U, 3U, 4U);
5269 INST(6U, Opcode::BoundsCheck).s32().Inputs(3U, 1U, 4U);
5270 INST(7U, Opcode::StoreArray).s32().Inputs(5U, 6U, 3U);
5271
5272 INST(14U, Opcode::SaveState).Inputs(1U, 2U).SrcVregs({1U, 2U});
5273 INST(15U, Opcode::NewArray).ref().Inputs(44U, 2U, 14U);
5274 INST(16U, Opcode::BoundsCheck).s32().Inputs(2U, 3U, 14U);
5275 INST(17U, Opcode::StoreArray).s32().Inputs(15U, 16U, 3U);
5276
5277 INST(18U, Opcode::ReturnVoid).v0id();
5278 }
5279 }
5280 auto clone = GraphCloner(GetGraph(), GetGraph()->GetAllocator(), GetGraph()->GetLocalAllocator()).CloneGraph();
5281 ASSERT_FALSE(GetGraph()->RunPass<ChecksElimination>());
5282 ASSERT_TRUE(GraphComparator().Compare(clone, GetGraph()));
5283 }
5284
5285 // dominated checks
TEST_F(ChecksEliminationTest,AddSubOverflowCheckDom)5286 TEST_F(ChecksEliminationTest, AddSubOverflowCheckDom)
5287 {
5288 GRAPH(GetGraph())
5289 {
5290 PARAMETER(0U, 0U).s32();
5291 PARAMETER(1U, 1U).s32();
5292 BASIC_BLOCK(2U, -1L)
5293 {
5294 INST(2U, Opcode::SaveState).Inputs(0U, 1U).SrcVregs({1U, 2U});
5295 INST(3U, Opcode::AddOverflowCheck).s32().Inputs(0U, 1U, 2U); // main
5296
5297 INST(4U, Opcode::SaveState).Inputs(0U, 1U).SrcVregs({1U, 2U});
5298 INST(5U, Opcode::AddOverflowCheck).s32().Inputs(0U, 1U, 4U); // redundant
5299
5300 INST(6U, Opcode::SaveState).Inputs(0U, 1U).SrcVregs({1U, 2U});
5301 INST(7U, Opcode::AddOverflowCheck).s32().Inputs(1U, 0U, 6U); // redundant, swapped inputs
5302
5303 INST(8U, Opcode::SaveState).Inputs(0U, 1U).SrcVregs({1U, 2U});
5304 INST(9U, Opcode::SubOverflowCheck).s32().Inputs(0U, 1U, 8U); // main
5305
5306 INST(10U, Opcode::SaveState).Inputs(0U, 1U).SrcVregs({1U, 2U});
5307 INST(11U, Opcode::SubOverflowCheck).s32().Inputs(0U, 1U, 10U); // redundant
5308
5309 INST(12U, Opcode::SaveState).Inputs(0U, 1U).SrcVregs({1U, 2U});
5310 INST(13U, Opcode::SubOverflowCheck).s32().Inputs(1U, 0U, 12U); // not redundant, swapped inputs
5311
5312 INST(14U, Opcode::ReturnVoid).v0id();
5313 }
5314 }
5315 ASSERT_TRUE(GetGraph()->RunPass<ChecksElimination>());
5316 ASSERT_TRUE(GetGraph()->RunPass<Cleanup>());
5317 auto graph = CreateEmptyGraph();
5318 GRAPH(graph)
5319 {
5320 PARAMETER(0U, 0U).s32();
5321 PARAMETER(1U, 1U).s32();
5322 BASIC_BLOCK(2U, -1L)
5323 {
5324 INST(2U, Opcode::SaveState).Inputs(0U, 1U).SrcVregs({1U, 2U});
5325 INST(3U, Opcode::AddOverflowCheck).s32().Inputs(0U, 1U, 2U); // main
5326
5327 INST(8U, Opcode::SaveState).Inputs(0U, 1U).SrcVregs({1U, 2U});
5328 INST(9U, Opcode::SubOverflowCheck).s32().Inputs(0U, 1U, 8U); // main
5329
5330 INST(12U, Opcode::SaveState).Inputs(0U, 1U).SrcVregs({1U, 2U});
5331 INST(13U, Opcode::SubOverflowCheck).s32().Inputs(1U, 0U, 12U); // not redundant, swapped inputs
5332
5333 INST(14U, Opcode::ReturnVoid).v0id();
5334 }
5335 }
5336 ASSERT_TRUE(GraphComparator().Compare(graph, GetGraph()));
5337 }
5338
BuildGraphOverflowCheckOptimize()5339 void ChecksEliminationTest::BuildGraphOverflowCheckOptimize()
5340 {
5341 GRAPH(GetGraph())
5342 {
5343 PARAMETER(0U, 0U).s32();
5344 PARAMETER(1U, 1U).s32();
5345 CONSTANT(6U, 6U);
5346 CONSTANT(1000U, 0U);
5347 CONSTANT(13U, INT32_MAX);
5348 CONSTANT(14U, INT32_MIN);
5349 BASIC_BLOCK(2U, 3U, 4U)
5350 {
5351 INST(2U, Opcode::SaveState).Inputs(0U, 1U).SrcVregs({1U, 2U});
5352 INST(3U, Opcode::AddOverflowCheck).s32().Inputs(0U, 1U, 2U); // maybe overflow
5353
5354 INST(4U, Opcode::SaveState).Inputs(0U, 1U).SrcVregs({1U, 2U});
5355 INST(5U, Opcode::SubOverflowCheck).s32().Inputs(0U, 1U, 4U); // maybe overflow
5356
5357 INST(20U, Opcode::SaveState).Inputs(0U, 1U).SrcVregs({1U, 2U});
5358 INST(30U, Opcode::AddOverflowCheck).s32().Inputs(0U, 6U, 20U); // maybe overflow
5359
5360 INST(40U, Opcode::SaveState).Inputs(0U, 1U).SrcVregs({1U, 2U});
5361 INST(50U, Opcode::SubOverflowCheck).s32().Inputs(0U, 6U, 40U); // maybe overflow
5362
5363 INST(7U, Opcode::Div).s32().Inputs(0U, 6U);
5364 INST(8U, Opcode::Div).s32().Inputs(1U, 6U);
5365
5366 INST(9U, Opcode::SaveState).Inputs(0U, 1U).SrcVregs({1U, 2U});
5367 INST(10U, Opcode::AddOverflowCheck).s32().Inputs(7U, 8U, 9U); // can't overflow
5368
5369 INST(11U, Opcode::SaveState).Inputs(0U, 1U).SrcVregs({1U, 2U});
5370 INST(12U, Opcode::SubOverflowCheck).s32().Inputs(7U, 8U, 11U); // can't overflow
5371
5372 INST(15U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(1000U);
5373 }
5374 BASIC_BLOCK(3U, 5U)
5375 {
5376 INST(16U, Opcode::SaveState).Inputs(0U, 1U).SrcVregs({1U, 2U});
5377 INST(17U, Opcode::AddOverflowCheck).s32().Inputs(13U, 6U, 16U); // must overflow
5378 }
5379 BASIC_BLOCK(4U, 5U)
5380 {
5381 INST(18U, Opcode::SaveState).Inputs(0U, 1U).SrcVregs({1U, 2U});
5382 INST(19U, Opcode::SubOverflowCheck).s32().Inputs(14U, 6U, 18U); // must overflow
5383 }
5384 BASIC_BLOCK(5U, -1L)
5385 {
5386 INST(100U, Opcode::ReturnVoid).v0id();
5387 }
5388 }
5389 }
5390
TEST_F(ChecksEliminationTest,OverflowCheckOptimize)5391 TEST_F(ChecksEliminationTest, OverflowCheckOptimize)
5392 {
5393 BuildGraphOverflowCheckOptimize();
5394 ASSERT_TRUE(GetGraph()->RunPass<ChecksElimination>());
5395 auto graph = CreateEmptyGraph();
5396 GRAPH(graph)
5397 {
5398 PARAMETER(0U, 0U).s32();
5399 PARAMETER(1U, 1U).s32();
5400 CONSTANT(6U, 6U);
5401 CONSTANT(1000U, 0U);
5402 CONSTANT(13U, INT32_MAX);
5403 CONSTANT(14U, INT32_MIN);
5404 BASIC_BLOCK(2U, 3U, 4U)
5405 {
5406 INST(2U, Opcode::SaveState).Inputs(0U, 1U).SrcVregs({1U, 2U});
5407 INST(3U, Opcode::AddOverflowCheck).s32().Inputs(0U, 1U, 2U); // maybe overflow
5408
5409 INST(4U, Opcode::SaveState).Inputs(0U, 1U).SrcVregs({1U, 2U});
5410 INST(5U, Opcode::SubOverflowCheck).s32().Inputs(0U, 1U, 4U); // maybe overflow
5411
5412 INST(20U, Opcode::SaveState).Inputs(0U, 1U).SrcVregs({1U, 2U});
5413 INST(30U, Opcode::AddOverflowCheck).s32().Inputs(0U, 6U, 20U); // maybe overflow
5414
5415 INST(40U, Opcode::SaveState).Inputs(0U, 1U).SrcVregs({1U, 2U});
5416 INST(50U, Opcode::SubOverflowCheck).s32().Inputs(0U, 6U, 40U); // maybe overflow
5417
5418 INST(7U, Opcode::Div).s32().Inputs(0U, 6U);
5419 INST(8U, Opcode::Div).s32().Inputs(1U, 6U);
5420
5421 INST(9U, Opcode::SaveState).Inputs(0U, 1U).SrcVregs({1U, 2U});
5422 INST(10U, Opcode::Add).s32().Inputs(7U, 8U);
5423
5424 INST(11U, Opcode::SaveState).Inputs(0U, 1U).SrcVregs({1U, 2U});
5425 INST(12U, Opcode::Sub).s32().Inputs(7U, 8U);
5426
5427 INST(15U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(1000U);
5428 }
5429 BASIC_BLOCK(3U, -1L)
5430 {
5431 INST(16U, Opcode::SaveState).Inputs(0U, 1U).SrcVregs({1U, 2U});
5432 INST(17U, Opcode::Deoptimize).DeoptimizeType(DeoptimizeType::OVERFLOW).Inputs(16U); // must overflow
5433 }
5434 BASIC_BLOCK(4U, -1L)
5435 {
5436 INST(18U, Opcode::SaveState).Inputs(0U, 1U).SrcVregs({1U, 2U});
5437 INST(19U, Opcode::Deoptimize).DeoptimizeType(DeoptimizeType::OVERFLOW).Inputs(18U); // must overflow
5438 }
5439 }
5440 ASSERT_TRUE(GraphComparator().Compare(graph, GetGraph()));
5441 }
5442
TEST_F(ChecksEliminationTest,LoopWithOverflowCheck)5443 TEST_F(ChecksEliminationTest, LoopWithOverflowCheck)
5444 {
5445 GRAPH(GetGraph())
5446 {
5447 PARAMETER(0U, 0U).s32();
5448 PARAMETER(1U, 1U).s32();
5449 CONSTANT(2U, 0U);
5450 BASIC_BLOCK(2U, 3U)
5451 {
5452 INST(3U, Opcode::SaveState).Inputs(0U, 1U).SrcVregs({0U, 1U});
5453 }
5454 BASIC_BLOCK(3U, 3U, 4U)
5455 {
5456 INST(4U, Opcode::SaveState).Inputs(0U, 1U).SrcVregs({0U, 1U});
5457 INST(5U, Opcode::AddOverflowCheck).s32().Inputs(0U, 1U, 4U);
5458 INST(6U, Opcode::SubOverflowCheck).s32().Inputs(0U, 1U, 4U);
5459 INST(7U, Opcode::IfImm).SrcType(DataType::BOOL).Inputs(2U).Imm(0U);
5460 }
5461 BASIC_BLOCK(4U, 1U)
5462 {
5463 INST(8U, Opcode::ReturnVoid).v0id();
5464 }
5465 }
5466 ASSERT_TRUE(GetGraph()->RunPass<ChecksElimination>());
5467 auto graph = CreateEmptyGraph();
5468 GRAPH(graph)
5469 {
5470 PARAMETER(0U, 0U).s32();
5471 PARAMETER(1U, 1U).s32();
5472 CONSTANT(2U, 0U);
5473 BASIC_BLOCK(2U, 3U)
5474 {
5475 INST(3U, Opcode::SaveState).Inputs(0U, 1U).SrcVregs({0U, 1U});
5476 INST(5U, Opcode::SubOverflowCheck).s32().Inputs(0U, 1U, 3U);
5477 INST(6U, Opcode::AddOverflowCheck).s32().Inputs(0U, 1U, 3U);
5478 }
5479 BASIC_BLOCK(3U, 3U, 4U)
5480 {
5481 INST(4U, Opcode::SaveState).Inputs(0U, 1U).SrcVregs({0U, 1U});
5482 INST(7U, Opcode::IfImm).SrcType(DataType::BOOL).Inputs(2U).Imm(0U);
5483 }
5484 BASIC_BLOCK(4U, 1U)
5485 {
5486 INST(8U, Opcode::ReturnVoid).v0id();
5487 }
5488 }
5489 ASSERT_TRUE(GraphComparator().Compare(graph, GetGraph()));
5490 }
5491
TEST_F(ChecksEliminationTest,LoopWithAddOverflowCheck)5492 TEST_F(ChecksEliminationTest, LoopWithAddOverflowCheck)
5493 {
5494 GRAPH(GetGraph())
5495 {
5496 PARAMETER(0U, 0U).s32();
5497 CONSTANT(1U, 1U);
5498 CONSTANT(10U, 10U);
5499
5500 BASIC_BLOCK(2U, 3U, 4U)
5501 {
5502 INST(2U, Opcode::Phi).s32().Inputs(1U, 4U);
5503 INST(6U, Opcode::Compare).b().Inputs(2U, 10U).CC(CC_LT);
5504 INST(5U, Opcode::IfImm).SrcType(DataType::BOOL).Inputs(6U).Imm(0U).CC(CC_NE);
5505 }
5506 BASIC_BLOCK(3U, 2U)
5507 {
5508 INST(3U, Opcode::SaveState).Inputs(0U, 1U, 10U).SrcVregs({0U, 1U, 10U});
5509 INST(4U, Opcode::AddOverflowCheck).s32().Inputs(2U, 1U, 3U); // can't be overflow
5510 }
5511 BASIC_BLOCK(4U, 1U)
5512 {
5513 INST(7U, Opcode::Return).s32().Inputs(2U);
5514 }
5515 }
5516 ASSERT_TRUE(GetGraph()->RunPass<ChecksElimination>());
5517 auto graph = CreateEmptyGraph();
5518 GRAPH(graph)
5519 {
5520 PARAMETER(0U, 0U).s32();
5521 CONSTANT(1U, 1U);
5522 CONSTANT(10U, 10U);
5523
5524 BASIC_BLOCK(2U, 3U, 4U)
5525 {
5526 INST(2U, Opcode::Phi).s32().Inputs(1U, 4U);
5527 INST(6U, Opcode::Compare).b().Inputs(2U, 10U).CC(CC_LT);
5528 INST(5U, Opcode::IfImm).SrcType(DataType::BOOL).Inputs(6U).Imm(0U).CC(CC_NE);
5529 }
5530 BASIC_BLOCK(3U, 2U)
5531 {
5532 INST(3U, Opcode::SaveState).Inputs(0U, 1U, 10U).SrcVregs({0U, 1U, 10U});
5533 INST(4U, Opcode::Add).s32().Inputs(2U, 1U);
5534 }
5535 BASIC_BLOCK(4U, 1U)
5536 {
5537 INST(7U, Opcode::Return).s32().Inputs(2U);
5538 }
5539 }
5540 ASSERT_TRUE(GraphComparator().Compare(graph, GetGraph()));
5541 }
5542
TEST_F(ChecksEliminationTest,LoopWithSubOverflowCheck)5543 TEST_F(ChecksEliminationTest, LoopWithSubOverflowCheck)
5544 {
5545 GRAPH(GetGraph())
5546 {
5547 PARAMETER(0U, 0U).s32();
5548 CONSTANT(1U, 1U);
5549 CONSTANT(10U, 10U);
5550
5551 BASIC_BLOCK(2U, 3U, 4U)
5552 {
5553 INST(2U, Opcode::Phi).s32().Inputs(10U, 4U);
5554 INST(6U, Opcode::Compare).b().Inputs(2U, 1U).CC(CC_GT);
5555 INST(5U, Opcode::IfImm).SrcType(DataType::BOOL).Inputs(6U).Imm(0U).CC(CC_NE);
5556 }
5557 BASIC_BLOCK(3U, 2U)
5558 {
5559 INST(3U, Opcode::SaveState).Inputs(0U, 1U, 10U).SrcVregs({0U, 1U, 10U});
5560 INST(4U, Opcode::SubOverflowCheck).s32().Inputs(2U, 1U, 3U); // can't be overflow
5561 }
5562 BASIC_BLOCK(4U, 1U)
5563 {
5564 INST(7U, Opcode::Return).s32().Inputs(2U);
5565 }
5566 }
5567 ASSERT_TRUE(GetGraph()->RunPass<ChecksElimination>());
5568 auto graph = CreateEmptyGraph();
5569 GRAPH(graph)
5570 {
5571 PARAMETER(0U, 0U).s32();
5572 CONSTANT(1U, 1U);
5573 CONSTANT(10U, 10U);
5574
5575 BASIC_BLOCK(2U, 3U, 4U)
5576 {
5577 INST(2U, Opcode::Phi).s32().Inputs(10U, 4U);
5578 INST(6U, Opcode::Compare).b().Inputs(2U, 1U).CC(CC_GT);
5579 INST(5U, Opcode::IfImm).SrcType(DataType::BOOL).Inputs(6U).Imm(0U).CC(CC_NE);
5580 }
5581 BASIC_BLOCK(3U, 2U)
5582 {
5583 INST(3U, Opcode::SaveState).Inputs(0U, 1U, 10U).SrcVregs({0U, 1U, 10U});
5584 INST(4U, Opcode::Sub).s32().Inputs(2U, 1U);
5585 }
5586 BASIC_BLOCK(4U, 1U)
5587 {
5588 INST(7U, Opcode::Return).s32().Inputs(2U);
5589 }
5590 }
5591 ASSERT_TRUE(GraphComparator().Compare(graph, GetGraph()));
5592 }
5593
TEST_F(ChecksEliminationTest,AndWithAddOverFlowCheck)5594 TEST_F(ChecksEliminationTest, AndWithAddOverFlowCheck)
5595 {
5596 GRAPH(GetGraph())
5597 {
5598 PARAMETER(0U, 0U).s64();
5599 PARAMETER(1U, 1U).s64();
5600 CONSTANT(2U, 0x3U);
5601 BASIC_BLOCK(2U, -1L)
5602 {
5603 INST(3U, Opcode::And).s32().Inputs(0U, 2U);
5604 INST(4U, Opcode::And).s32().Inputs(1U, 2U);
5605 INST(5U, Opcode::SaveState).Inputs(0U, 1U).SrcVregs({0U, 1U});
5606 INST(6U, Opcode::AddOverflowCheck).s32().Inputs(3U, 4U, 5U);
5607 INST(7U, Opcode::Return).s32().Inputs(6U);
5608 }
5609 }
5610 ASSERT_TRUE(GetGraph()->RunPass<ChecksElimination>());
5611 auto graph = CreateEmptyGraph();
5612 GRAPH(graph)
5613 {
5614 PARAMETER(0U, 0U).s64();
5615 PARAMETER(1U, 1U).s64();
5616 CONSTANT(2U, 0x3U);
5617 BASIC_BLOCK(2U, -1L)
5618 {
5619 INST(3U, Opcode::And).s32().Inputs(0U, 2U);
5620 INST(4U, Opcode::And).s32().Inputs(1U, 2U);
5621 INST(5U, Opcode::SaveState).Inputs(0U, 1U).SrcVregs({0U, 1U});
5622 INST(6U, Opcode::Add).s32().Inputs(3U, 4U);
5623 INST(7U, Opcode::Return).s32().Inputs(6U);
5624 }
5625 }
5626 ASSERT_TRUE(GraphComparator().Compare(graph, GetGraph()));
5627 }
5628
5629 // Must Deoptimize
TEST_F(ChecksEliminationTest,NegOverflowAndZeroCheck1)5630 TEST_F(ChecksEliminationTest, NegOverflowAndZeroCheck1)
5631 {
5632 for (auto cnst : {0, INT32_MIN}) {
5633 auto graph1 = CreateEmptyGraph();
5634 GRAPH(graph1)
5635 {
5636 CONSTANT(0U, cnst);
5637 BASIC_BLOCK(2U, -1L)
5638 {
5639 INST(5U, Opcode::SaveState).Inputs(0U).SrcVregs({0U});
5640 INST(6U, Opcode::NegOverflowAndZeroCheck).s32().Inputs(0U, 5U);
5641 INST(7U, Opcode::Return).s32().Inputs(6U);
5642 }
5643 }
5644 ASSERT_TRUE(graph1->RunPass<ChecksElimination>());
5645 auto graph2 = CreateEmptyGraph();
5646 GRAPH(graph2)
5647 {
5648 CONSTANT(0U, cnst);
5649 BASIC_BLOCK(2U, -1L)
5650 {
5651 INST(5U, Opcode::SaveState).Inputs(0U).SrcVregs({0U});
5652 INST(6U, Opcode::Deoptimize).DeoptimizeType(DeoptimizeType::OVERFLOW).Inputs(5U);
5653 }
5654 }
5655 ASSERT_TRUE(GraphComparator().Compare(graph1, graph2));
5656 }
5657 }
5658
5659 // Remove dominated check
TEST_F(ChecksEliminationTest,NegOverflowAndZeroCheck2)5660 TEST_F(ChecksEliminationTest, NegOverflowAndZeroCheck2)
5661 {
5662 auto graph1 = CreateEmptyGraph();
5663 GRAPH(graph1)
5664 {
5665 PARAMETER(0U, 0U).u32();
5666 BASIC_BLOCK(2U, -1L)
5667 {
5668 INST(3U, Opcode::SaveState).Inputs(0U).SrcVregs({0U});
5669 INST(4U, Opcode::NegOverflowAndZeroCheck).s32().Inputs(0U, 3U);
5670 INST(5U, Opcode::SaveState).Inputs(0U).SrcVregs({0U});
5671 INST(6U, Opcode::NegOverflowAndZeroCheck).s32().Inputs(0U, 5U);
5672 INST(7U, Opcode::CallStatic).s32().InputsAutoType(4U, 6U, 3U);
5673 INST(8U, Opcode::Return).s32().Inputs(7U);
5674 }
5675 }
5676 ASSERT_TRUE(graph1->RunPass<ChecksElimination>());
5677 ASSERT_TRUE(graph1->RunPass<Cleanup>());
5678 auto graph2 = CreateEmptyGraph();
5679 GRAPH(graph2)
5680 {
5681 PARAMETER(0U, 0U).u32();
5682 BASIC_BLOCK(2U, -1L)
5683 {
5684 INST(3U, Opcode::SaveState).Inputs(0U).SrcVregs({0U});
5685 INST(4U, Opcode::NegOverflowAndZeroCheck).s32().Inputs(0U, 3U);
5686 INST(7U, Opcode::CallStatic).s32().InputsAutoType(4U, 4U, 3U);
5687 INST(8U, Opcode::Return).s32().Inputs(7U);
5688 }
5689 }
5690 ASSERT_TRUE(GraphComparator().Compare(graph1, graph2));
5691 }
5692
5693 // Replace by Neg
TEST_F(ChecksEliminationTest,NegOverflowAndZeroCheck3)5694 TEST_F(ChecksEliminationTest, NegOverflowAndZeroCheck3)
5695 {
5696 auto graph1 = CreateEmptyGraph();
5697 GRAPH(graph1)
5698 {
5699 CONSTANT(0U, 1U);
5700 BASIC_BLOCK(2U, -1L)
5701 {
5702 INST(5U, Opcode::SaveState).Inputs(0U).SrcVregs({0U});
5703 INST(6U, Opcode::NegOverflowAndZeroCheck).s32().Inputs(0U, 5U);
5704 INST(7U, Opcode::Return).s32().Inputs(6U);
5705 }
5706 }
5707 ASSERT_TRUE(graph1->RunPass<ChecksElimination>());
5708 auto graph2 = CreateEmptyGraph();
5709 GRAPH(graph2)
5710 {
5711 CONSTANT(0U, 1U);
5712 BASIC_BLOCK(2U, -1L)
5713 {
5714 INST(5U, Opcode::SaveState).Inputs(0U).SrcVregs({0U});
5715 INST(6U, Opcode::Neg).s32().Inputs(0U);
5716 INST(7U, Opcode::Return).s32().Inputs(6U);
5717 }
5718 }
5719 ASSERT_TRUE(GraphComparator().Compare(graph1, graph2));
5720 }
5721
TEST_F(ChecksEliminationTest,OsrMode)5722 TEST_F(ChecksEliminationTest, OsrMode)
5723 {
5724 auto osrGraph = CreateOsrGraph();
5725 GRAPH(osrGraph)
5726 {
5727 PARAMETER(0U, 0U).u32();
5728 PARAMETER(42U, 1U).ref();
5729 CONSTANT(1U, 0U);
5730 CONSTANT(2U, 1U);
5731
5732 BASIC_BLOCK(2U, 3U)
5733 {
5734 INST(15U, Opcode::SaveState).Inputs(42U).SrcVregs({42U});
5735 INST(16U, Opcode::NullCheck).ref().Inputs(42U, 15U);
5736 INST(17U, Opcode::LenArray).s32().Inputs(16U);
5737 }
5738 BASIC_BLOCK(3U, 5U, 4U)
5739 {
5740 INST(3U, Opcode::Phi).s32().Inputs(0U, 7U);
5741 INST(4U, Opcode::SaveStateOsr).Inputs(3U, 42U).SrcVregs({1U, 42U});
5742 INST(5U, Opcode::Compare).b().SrcType(DataType::Type::INT32).CC(CC_LE).Inputs(3U, 1U);
5743 INST(6U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(5U);
5744 }
5745 BASIC_BLOCK(4U, 3U)
5746 {
5747 INST(7U, Opcode::Sub).s32().Inputs(3U, 2U);
5748 INST(8U, Opcode::SaveState).Inputs(42U).SrcVregs({42U});
5749 INST(9U, Opcode::NullCheck).ref().Inputs(42U, 8U);
5750 INST(10U, Opcode::LenArray).s32().Inputs(9U);
5751 }
5752 BASIC_BLOCK(5U, 6U)
5753 {
5754 INST(11U, Opcode::SaveState).Inputs(42U).SrcVregs({42U});
5755 INST(12U, Opcode::NullCheck).ref().Inputs(42U, 11U);
5756 INST(13U, Opcode::LenArray).s32().Inputs(12U);
5757 }
5758 BASIC_BLOCK(6U, -1L)
5759 {
5760 INST(14U, Opcode::Return).u32().Inputs(3U);
5761 }
5762 }
5763 ASSERT_FALSE(osrGraph->RunPass<ChecksElimination>());
5764 }
5765
5766 // NOLINTEND(readability-magic-numbers)
5767
5768 } // namespace ark::compiler
5769