1 /*
2 * Copyright (c) 2021-2023 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/ir/graph_cloner.h"
18 #include "optimizer/optimizations/licm.h"
19
20 namespace panda::compiler {
21 class LicmTest : public GraphTest {
22 public:
23 static constexpr uint32_t HOST_LIMIT = 8;
24 };
25
26 // NOLINTBEGIN(readability-magic-numbers)
27 /*
28 * Test Graph:
29 * [0]
30 * |
31 * v
32 * /-----[2]--------\
33 * | ^ |
34 * | | |
35 * | [6] |
36 * | ^ |
37 * | | |
38 * | [5]------->|
39 * | ^ |
40 * | | |
41 * | [4] |
42 * | ^ |
43 * | | |
44 * \---->[3] |
45 * | |
46 * v |
47 * [exit]<-----/
48 */
49
TEST_F(LicmTest,LoopExits)50 TEST_F(LicmTest, LoopExits)
51 {
52 GRAPH(GetGraph())
53 {
54 PARAMETER(0U, 0U).u64();
55 PARAMETER(1U, 1U).u64();
56 BASIC_BLOCK(2U, 3U, 7U)
57 {
58 INST(2U, Opcode::Compare).b().Inputs(0U, 1U);
59 INST(3U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(2U);
60 }
61 BASIC_BLOCK(3U, 4U, 7U)
62 {
63 INST(4U, Opcode::Compare).b().Inputs(0U, 1U);
64 INST(5U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(4U);
65 }
66 BASIC_BLOCK(4U, 5U) {}
67 BASIC_BLOCK(5U, 6U, 7U)
68 {
69 INST(6U, Opcode::Compare).b().Inputs(0U, 1U);
70 INST(7U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(6U);
71 }
72 BASIC_BLOCK(6U, 2U) {}
73 BASIC_BLOCK(7U, -1L)
74 {
75 INST(8U, Opcode::ReturnVoid);
76 }
77 }
78
79 auto licm = Licm(GetGraph(), HOST_LIMIT);
80 licm.RunImpl();
81
82 ASSERT_TRUE(licm.IsBlockLoopExit(&BB(3U)));
83 ASSERT_TRUE(licm.IsBlockLoopExit(&BB(5U)));
84
85 ASSERT_FALSE(licm.IsBlockLoopExit(&BB(0U)));
86 ASSERT_FALSE(licm.IsBlockLoopExit(&BB(1U)));
87 ASSERT_FALSE(licm.IsBlockLoopExit(&BB(2U)));
88 ASSERT_FALSE(licm.IsBlockLoopExit(&BB(4U)));
89 ASSERT_FALSE(licm.IsBlockLoopExit(&BB(6U)));
90 ASSERT_FALSE(licm.IsBlockLoopExit(&BB(7U)));
91 }
92
93 /*
94 * Test Graph:
95 * [0]
96 * |
97 * v
98 * /-----[2]<----\
99 * | | |
100 * | v |
101 * | [3]-----/
102 * |
103 * \---->[4]
104 * |
105 * v
106 * [exit]
107 */
TEST_F(LicmTest,OneLoop)108 TEST_F(LicmTest, OneLoop)
109 {
110 GRAPH(GetGraph())
111 {
112 CONSTANT(0U, 1U);
113 CONSTANT(1U, 10U);
114 CONSTANT(2U, 20U);
115 CONSTANT(12U, 1U);
116
117 BASIC_BLOCK(2U, 3U, 4U)
118 {
119 INST(3U, Opcode::Phi).u64().Inputs({{0U, 0U}, {3U, 7U}});
120 INST(4U, Opcode::Phi).u64().Inputs({{0U, 1U}, {3U, 8U}});
121 INST(5U, Opcode::Compare).b().Inputs(4U, 0U);
122 INST(6U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(5U);
123 }
124
125 BASIC_BLOCK(3U, 2U)
126 {
127 INST(7U, Opcode::Mul).u64().Inputs(3U, 4U);
128 INST(13U, Opcode::Mul).u64().Inputs(12U, 0U);
129 INST(8U, Opcode::Sub).u64().Inputs(4U, 13U);
130 }
131
132 BASIC_BLOCK(4U, -1L)
133 {
134 INST(10U, Opcode::Add).u64().Inputs(2U, 3U);
135 INST(20U, Opcode::SaveState).NoVregs();
136 INST(15U, Opcode::CallStatic).u64().InputsAutoType(3U, 4U, 20U);
137 INST(11U, Opcode::ReturnVoid);
138 }
139 }
140
141 GetGraph()->RunPass<Licm>(HOST_LIMIT);
142 ASSERT_EQ(INS(13U).GetBasicBlock(), BB(3U).GetLoop()->GetPreHeader());
143 ASSERT_EQ(INS(7U).GetBasicBlock(), &BB(3U));
144 ASSERT_EQ(INS(8U).GetBasicBlock(), &BB(3U));
145
146 GraphChecker(GetGraph()).Check();
147 }
148
149 /*
150 * NOTE (a.popov) Improve Licm to support this test with updated DF: `INST(19, Opcode::Phi).u64().Inputs(1, 18)`
151 *
152 * Test Graph:
153 * [0]
154 * |
155 * v
156 * [2]<----------\
157 * | |
158 * v |
159 * /-----[3]<----\ |
160 * | | | |
161 * | v | |
162 * | [4]-----/ [6]
163 * | |
164 * \---->[5]-----------/
165 * |
166 * v
167 * [exit]
168 */
TEST_F(LicmTest,DISABLED_TwoLoops)169 TEST_F(LicmTest, DISABLED_TwoLoops)
170 {
171 GRAPH(GetGraph())
172 {
173 PARAMETER(0U, 0U).u64();
174 PARAMETER(1U, 1U).u64();
175 PARAMETER(2U, 10U).u64();
176 PARAMETER(3U, 100U).u64();
177 BASIC_BLOCK(2U, 3U) {}
178 BASIC_BLOCK(3U, 4U, 5U)
179 {
180 INST(5U, Opcode::Phi).u64().Inputs(2U, 9U);
181 INST(6U, Opcode::Phi).u64().Inputs(3U, 11U);
182 INST(19U, Opcode::Phi).u64().Inputs(1U, 18U);
183 INST(7U, Opcode::Compare).b().Inputs(5U, 6U);
184 INST(8U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(7U);
185 }
186 BASIC_BLOCK(4U, 3U)
187 {
188 INST(9U, Opcode::Mul).u64().Inputs(5U, 1U);
189 INST(10U, Opcode::Mul).u64().Inputs(1U, 2U);
190 INST(18U, Opcode::Mul).u64().Inputs(10U, 10U);
191 INST(11U, Opcode::Sub).u64().Inputs(6U, 1U);
192 }
193
194 BASIC_BLOCK(5U, 6U, 7U)
195 {
196 INST(13U, Opcode::Compare).b().Inputs(6U, 1U);
197 INST(14U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(13U);
198 }
199 BASIC_BLOCK(6U, 2U) {}
200 BASIC_BLOCK(7U, -1L)
201 {
202 INST(16U, Opcode::Add).u64().Inputs(19U, 0U);
203 INST(17U, Opcode::ReturnVoid);
204 }
205 }
206
207 GetGraph()->RunPass<Licm>(HOST_LIMIT);
208 ASSERT_EQ(INS(10U).GetBasicBlock(), BB(2U).GetLoop()->GetPreHeader());
209 ASSERT_EQ(INS(18U).GetBasicBlock(), BB(2U).GetLoop()->GetPreHeader());
210 ASSERT_EQ(INS(9U).GetBasicBlock(), &BB(4U));
211 ASSERT_EQ(INS(11U).GetBasicBlock(), &BB(4U));
212 GraphChecker(GetGraph()).Check();
213 }
214
TEST_F(LicmTest,EmptyPreHeaderWithPhi)215 TEST_F(LicmTest, EmptyPreHeaderWithPhi)
216 {
217 GRAPH(GetGraph())
218 {
219 PARAMETER(0U, 0U).u64();
220 CONSTANT(1U, 1U);
221
222 BASIC_BLOCK(2U, 3U, 4U)
223 {
224 INST(5U, Opcode::IfImm).SrcType(DataType::UINT64).CC(CC_NE).Imm(0U).Inputs(0U);
225 }
226 BASIC_BLOCK(3U, 4U) {}
227 BASIC_BLOCK(4U, 5U)
228 {
229 INST(6U, Opcode::Phi).u64().Inputs(0U, 1U);
230 }
231 BASIC_BLOCK(5U, 5U, 6U)
232 {
233 INST(7U, Opcode::Phi).u64().Inputs(0U, 8U);
234 INST(8U, Opcode::Add).u64().Inputs(7U, 1U);
235 INST(9U, Opcode::Mul).u64().Inputs(6U, 6U);
236 INST(10U, Opcode::Compare).b().SrcType(DataType::Type::UINT64).CC(CC_LT).Inputs(8U, 9U);
237 INST(11U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(10U);
238 }
239 BASIC_BLOCK(6U, -1L)
240 {
241 INST(12U, Opcode::Return).u64().Inputs(8U);
242 }
243 }
244
245 GetGraph()->RunPass<Licm>(HOST_LIMIT);
246 ASSERT_EQ(INS(9U).GetBasicBlock(), BB(5U).GetLoop()->GetPreHeader());
247 }
248
TEST_F(LicmTest,PreHeaderWithIf)249 TEST_F(LicmTest, PreHeaderWithIf)
250 {
251 GRAPH(GetGraph())
252 {
253 PARAMETER(0U, 0U).u64();
254 CONSTANT(1U, 1U);
255
256 BASIC_BLOCK(2U, 3U, 4U)
257 {
258 INST(5U, Opcode::IfImm).SrcType(DataType::UINT64).CC(CC_NE).Imm(0U).Inputs(0U);
259 }
260 BASIC_BLOCK(3U, 3U, 4U)
261 {
262 INST(7U, Opcode::Phi).u64().Inputs(0U, 8U);
263 INST(8U, Opcode::Add).u64().Inputs(7U, 1U);
264 INST(9U, Opcode::Mul).u64().Inputs(0U, 0U);
265 INST(10U, Opcode::Compare).b().SrcType(DataType::Type::UINT64).CC(CC_LT).Inputs(8U, 9U);
266 INST(11U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(10U);
267 }
268 BASIC_BLOCK(4U, -1L)
269 {
270 INST(12U, Opcode::Phi).u64().Inputs(0U, 8U);
271 INST(13U, Opcode::Return).u64().Inputs(12U);
272 }
273 }
274 GetGraph()->RunPass<Licm>(HOST_LIMIT);
275
276 auto graph = CreateEmptyGraph();
277 GRAPH(graph)
278 {
279 PARAMETER(0U, 0U).u64();
280 CONSTANT(1U, 1U);
281
282 BASIC_BLOCK(2U, 5U, 4U)
283 {
284 INST(5U, Opcode::IfImm).SrcType(DataType::UINT64).CC(CC_NE).Imm(0U).Inputs(0U);
285 }
286 BASIC_BLOCK(5U, 3U)
287 {
288 INST(9U, Opcode::Mul).u64().Inputs(0U, 0U);
289 }
290 BASIC_BLOCK(3U, 3U, 4U)
291 {
292 INST(7U, Opcode::Phi).u64().Inputs(0U, 8U);
293 INST(8U, Opcode::Add).u64().Inputs(7U, 1U);
294 INST(10U, Opcode::Compare).b().SrcType(DataType::Type::UINT64).CC(CC_LT).Inputs(8U, 9U);
295 INST(11U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(10U);
296 }
297 BASIC_BLOCK(4U, -1L)
298 {
299 INST(12U, Opcode::Phi).u64().Inputs(0U, 8U);
300 INST(13U, Opcode::Return).u64().Inputs(12U);
301 }
302 }
303 ASSERT_TRUE(GraphComparator().Compare(GetGraph(), graph));
304 }
305
TEST_F(LicmTest,LicmResolver)306 TEST_F(LicmTest, LicmResolver)
307 {
308 GRAPH(GetGraph())
309 {
310 PARAMETER(0U, 0U).ref();
311 CONSTANT(20U, 0xaU);
312
313 BASIC_BLOCK(5U, 2U)
314 {
315 INST(2U, Opcode::SaveState).NoVregs();
316 INST(3U, Opcode::UnresolvedLoadAndInitClass).ref().Inputs(2U).TypeId(0U);
317 INST(4U, Opcode::NewObject).ref().Inputs(3U, 2U);
318 INST(5U, Opcode::SaveState).NoVregs();
319 INST(6U, Opcode::UnresolvedLoadAndInitClass).ref().Inputs(2U).TypeId(0U);
320 INST(1U, Opcode::SaveStateDeoptimize).NoVregs();
321 // We can safely hoist ResolveVirtual (INST[8]) into BLOCK[5] and link it to SaveState (INST[5])
322 // as INST[6] produces a reference, which is never moved by GC (note that ResolveVirtual calls
323 // runtime and may trigger GC).
324 }
325 BASIC_BLOCK(2U, 2U, 3U)
326 {
327 INST(7U, Opcode::SaveState).NoVregs();
328 INST(8U, Opcode::ResolveVirtual).ptr().Inputs(4U, 7U);
329 INST(9U, Opcode::CallResolvedVirtual)
330 .v0id()
331 .Inputs({{DataType::POINTER, 8U}, {DataType::REFERENCE, 4U}, {DataType::NO_TYPE, 7U}});
332 INST(10U, Opcode::SaveState).NoVregs();
333 INST(11U, Opcode::NullCheck).ref().Inputs(0U, 10U);
334 INST(12U, Opcode::LoadObject).s32().Inputs(11U).TypeId(122U);
335 INST(13U, Opcode::Compare).b().SrcType(DataType::INT32).CC(CC_GE).Inputs(12U, 20U);
336 INST(14U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(13U);
337 }
338 BASIC_BLOCK(3U, -1L)
339 {
340 INST(15U, Opcode::ReturnVoid).v0id();
341 }
342 }
343
344 ASSERT_TRUE(GetGraph()->RunPass<Licm>(HOST_LIMIT));
345 GetGraph()->RunPass<Cleanup>(HOST_LIMIT);
346
347 auto graph = CreateEmptyGraph();
348 GRAPH(graph)
349 {
350 PARAMETER(0U, 0U).ref();
351 CONSTANT(20U, 0xaU);
352
353 BASIC_BLOCK(5U, 2U)
354 {
355 INST(2U, Opcode::SaveState).NoVregs();
356 INST(3U, Opcode::UnresolvedLoadAndInitClass).ref().Inputs(2U).TypeId(0U);
357 INST(4U, Opcode::NewObject).ref().Inputs(3U, 2U);
358 INST(5U, Opcode::SaveState).NoVregs();
359 INST(6U, Opcode::UnresolvedLoadAndInitClass).ref().Inputs(2U).TypeId(0U);
360 INST(8U, Opcode::ResolveVirtual).ptr().Inputs(4U, 5U);
361 INST(1U, Opcode::SaveStateDeoptimize).NoVregs();
362 }
363 BASIC_BLOCK(2U, 2U, 3U)
364 {
365 INST(7U, Opcode::SaveState).NoVregs();
366 INST(9U, Opcode::CallResolvedVirtual)
367 .v0id()
368 .Inputs({{DataType::POINTER, 8U}, {DataType::REFERENCE, 4U}, {DataType::NO_TYPE, 7U}});
369 INST(10U, Opcode::SaveState).NoVregs();
370 INST(11U, Opcode::NullCheck).ref().Inputs(0U, 10U);
371 INST(12U, Opcode::LoadObject).s32().Inputs(11U).TypeId(122U);
372 INST(13U, Opcode::Compare).b().SrcType(DataType::INT32).CC(CC_GE).Inputs(12U, 20U);
373 INST(14U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(13U);
374 }
375 BASIC_BLOCK(3U, -1L)
376 {
377 INST(15U, Opcode::ReturnVoid).v0id();
378 }
379 }
380 ASSERT_TRUE(GraphComparator().Compare(GetGraph(), graph));
381 }
382
TEST_F(LicmTest,LicmResolverIfHeaderIsNotExit)383 TEST_F(LicmTest, LicmResolverIfHeaderIsNotExit)
384 {
385 GRAPH(GetGraph())
386 {
387 PARAMETER(0U, 0U).ref();
388 CONSTANT(20U, 0xaU);
389
390 BASIC_BLOCK(5U, 2U)
391 {
392 INST(2U, Opcode::SaveState).NoVregs();
393 INST(3U, Opcode::UnresolvedLoadAndInitClass).ref().Inputs(2U).TypeId(0U);
394 INST(4U, Opcode::NewObject).ref().Inputs(3U, 2U);
395 INST(5U, Opcode::SaveState).NoVregs();
396 INST(6U, Opcode::UnresolvedLoadAndInitClass).ref().Inputs(2U).TypeId(0U);
397 INST(1U, Opcode::SaveStateDeoptimize).NoVregs();
398 }
399 BASIC_BLOCK(2U, 3U)
400 {
401 INST(10U, Opcode::SaveState).NoVregs();
402 INST(11U, Opcode::NullCheck).ref().Inputs(0U, 10U);
403 INST(12U, Opcode::LoadObject).s32().Inputs(11U).TypeId(122U);
404 }
405 BASIC_BLOCK(3U, 2U, 4U)
406 {
407 INST(7U, Opcode::SaveState).NoVregs();
408 INST(8U, Opcode::ResolveVirtual).ptr().Inputs(4U, 7U);
409 INST(9U, Opcode::CallResolvedVirtual)
410 .v0id()
411 .Inputs({{DataType::POINTER, 8U}, {DataType::REFERENCE, 4U}, {DataType::NO_TYPE, 7U}});
412 INST(13U, Opcode::Compare).b().SrcType(DataType::INT32).CC(CC_GE).Inputs(12U, 20U);
413 INST(14U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(13U);
414 }
415 BASIC_BLOCK(4U, -1L)
416 {
417 INST(15U, Opcode::ReturnVoid).v0id();
418 }
419 }
420 ASSERT_TRUE(GetGraph()->RunPass<Licm>(HOST_LIMIT));
421
422 auto graph = CreateEmptyGraph();
423 GRAPH(graph)
424 {
425 PARAMETER(0U, 0U).ref();
426 CONSTANT(20U, 0xaU);
427
428 BASIC_BLOCK(5U, 2U)
429 {
430 INST(2U, Opcode::SaveState).NoVregs();
431 INST(3U, Opcode::UnresolvedLoadAndInitClass).ref().Inputs(2U).TypeId(0U);
432 INST(4U, Opcode::NewObject).ref().Inputs(3U, 2U);
433 INST(5U, Opcode::SaveState).NoVregs();
434 INST(6U, Opcode::UnresolvedLoadAndInitClass).ref().Inputs(2U).TypeId(0U);
435 INST(8U, Opcode::ResolveVirtual).ptr().Inputs(4U, 5U);
436 INST(1U, Opcode::SaveStateDeoptimize).NoVregs();
437 }
438 BASIC_BLOCK(2U, 3U)
439 {
440 INST(10U, Opcode::SaveState).NoVregs();
441 INST(11U, Opcode::NullCheck).ref().Inputs(0U, 10U);
442 INST(12U, Opcode::LoadObject).s32().Inputs(11U).TypeId(122U);
443 }
444 BASIC_BLOCK(3U, 2U, 4U)
445 {
446 INST(7U, Opcode::SaveState).NoVregs();
447 INST(9U, Opcode::CallResolvedVirtual)
448 .v0id()
449 .Inputs({{DataType::POINTER, 8U}, {DataType::REFERENCE, 4U}, {DataType::NO_TYPE, 7U}});
450 INST(13U, Opcode::Compare).b().SrcType(DataType::INT32).CC(CC_GE).Inputs(12U, 20U);
451 INST(14U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(13U);
452 }
453 BASIC_BLOCK(4U, -1L)
454 {
455 INST(15U, Opcode::ReturnVoid).v0id();
456 }
457 }
458 ASSERT_TRUE(GraphComparator().Compare(GetGraph(), graph));
459 }
460
TEST_F(LicmTest,DontLicmResolverIfHeaderIsExit)461 TEST_F(LicmTest, DontLicmResolverIfHeaderIsExit)
462 {
463 GRAPH(GetGraph())
464 {
465 PARAMETER(0U, 0U).ref();
466 CONSTANT(20U, 0xaU);
467
468 BASIC_BLOCK(5U, 2U)
469 {
470 INST(2U, Opcode::SaveState).NoVregs();
471 INST(3U, Opcode::UnresolvedLoadAndInitClass).ref().Inputs(2U).TypeId(0U);
472 INST(4U, Opcode::NewObject).ref().Inputs(3U, 2U);
473 INST(5U, Opcode::SaveState).NoVregs();
474 INST(6U, Opcode::UnresolvedLoadAndInitClass).ref().Inputs(2U).TypeId(0U);
475 INST(1U, Opcode::SaveStateDeoptimize).NoVregs();
476 }
477 BASIC_BLOCK(2U, 3U, 4U)
478 {
479 INST(10U, Opcode::SaveState).NoVregs();
480 INST(11U, Opcode::NullCheck).ref().Inputs(0U, 10U);
481 INST(12U, Opcode::LoadObject).s32().Inputs(11U).TypeId(122U);
482 INST(13U, Opcode::Compare).b().SrcType(DataType::INT32).CC(CC_GE).Inputs(12U, 20U);
483 INST(14U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(13U);
484 }
485 BASIC_BLOCK(3U, 2U)
486 {
487 INST(7U, Opcode::SaveState).NoVregs();
488 INST(8U, Opcode::ResolveVirtual).ptr().Inputs(4U, 7U);
489 INST(9U, Opcode::CallResolvedVirtual)
490 .v0id()
491 .Inputs({{DataType::POINTER, 8U}, {DataType::REFERENCE, 4U}, {DataType::NO_TYPE, 7U}});
492 }
493 BASIC_BLOCK(4U, -1L)
494 {
495 INST(15U, Opcode::ReturnVoid).v0id();
496 }
497 }
498 ASSERT_FALSE(GetGraph()->RunPass<Licm>(HOST_LIMIT));
499 }
500
TEST_F(LicmTest,DontLicmResolverIfNoAppropriateSaveState)501 TEST_F(LicmTest, DontLicmResolverIfNoAppropriateSaveState)
502 {
503 GRAPH(GetGraph())
504 {
505 PARAMETER(0U, 0U).ref();
506 CONSTANT(20U, 0xaU);
507
508 BASIC_BLOCK(5U, 2U)
509 {
510 INST(2U, Opcode::SaveState).NoVregs();
511 INST(3U, Opcode::UnresolvedLoadAndInitClass).ref().Inputs(2U).TypeId(0U);
512 INST(4U, Opcode::NewObject).ref().Inputs(3U, 2U);
513 // We must not hoist ResolveVirtual (INST[8]) into BLOCK[5] and link it to SaveState (INST[2]).
514 // Otherwise a reference produced by INST[4] might be moved by GC as ResolveVirtual calls runtime.
515 INST(1U, Opcode::SaveStateDeoptimize).NoVregs();
516 }
517 BASIC_BLOCK(2U, 2U, 3U)
518 {
519 INST(7U, Opcode::SaveState).NoVregs();
520 INST(8U, Opcode::ResolveVirtual).ptr().Inputs(4U, 7U);
521 INST(9U, Opcode::CallResolvedVirtual)
522 .v0id()
523 .Inputs({{DataType::POINTER, 8U}, {DataType::REFERENCE, 4U}, {DataType::NO_TYPE, 7U}});
524 INST(10U, Opcode::SaveState).NoVregs();
525 INST(11U, Opcode::NullCheck).ref().Inputs(0U, 10U);
526 INST(12U, Opcode::LoadObject).s32().Inputs(11U).TypeId(122U);
527 INST(13U, Opcode::Compare).b().SrcType(DataType::INT32).CC(CC_GE).Inputs(12U, 20U);
528 INST(14U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(13U);
529 }
530 BASIC_BLOCK(3U, -1L)
531 {
532 INST(15U, Opcode::ReturnVoid).v0id();
533 }
534 }
535 ASSERT_FALSE(GetGraph()->RunPass<Licm>(HOST_LIMIT));
536 }
537
TEST_F(LicmTest,DontLicmResolverThroughTry)538 TEST_F(LicmTest, DontLicmResolverThroughTry)
539 {
540 GRAPH(GetGraph())
541 {
542 PARAMETER(0U, 0U).ref();
543 CONSTANT(20U, 0xaU);
544
545 BASIC_BLOCK(5U, 2U)
546 {
547 INST(2U, Opcode::SaveState).NoVregs();
548 INST(3U, Opcode::UnresolvedLoadAndInitClass).ref().Inputs(2U).TypeId(0U);
549 INST(4U, Opcode::NewObject).ref().Inputs(3U, 2U);
550 INST(5U, Opcode::SaveState).NoVregs();
551 INST(6U, Opcode::UnresolvedLoadAndInitClass).ref().Inputs(2U).TypeId(0U);
552 INST(1U, Opcode::SaveStateDeoptimize).NoVregs();
553 }
554 BASIC_BLOCK(2U, 2U, 3U)
555 {
556 INST(7U, Opcode::SaveState).NoVregs();
557 INST(8U, Opcode::ResolveVirtual).ptr().Inputs(4U, 7U);
558 INST(9U, Opcode::CallResolvedVirtual)
559 .v0id()
560 .Inputs({{DataType::POINTER, 8U}, {DataType::REFERENCE, 4U}, {DataType::NO_TYPE, 7U}});
561 INST(10U, Opcode::SaveState).NoVregs();
562 INST(11U, Opcode::NullCheck).ref().Inputs(0U, 10U);
563 INST(12U, Opcode::LoadObject).s32().Inputs(11U).TypeId(122U);
564 INST(13U, Opcode::Compare).b().SrcType(DataType::INT32).CC(CC_GE).Inputs(12U, 20U);
565 INST(14U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(13U);
566 }
567 BASIC_BLOCK(3U, -1L)
568 {
569 INST(15U, Opcode::ReturnVoid).v0id();
570 }
571 }
572 BB(2U).SetTry(true);
573 ASSERT_FALSE(GetGraph()->RunPass<Licm>(HOST_LIMIT));
574 }
575
TEST_F(LicmTest,DontLicmResolverThroughInitClass)576 TEST_F(LicmTest, DontLicmResolverThroughInitClass)
577 {
578 GRAPH(GetGraph())
579 {
580 PARAMETER(0U, 0U).ref();
581 CONSTANT(20U, 0xaU);
582
583 BASIC_BLOCK(5U, 2U)
584 {
585 INST(2U, Opcode::SaveState).NoVregs();
586 INST(3U, Opcode::UnresolvedLoadAndInitClass).ref().Inputs(2U).TypeId(0U);
587 INST(4U, Opcode::NewObject).ref().Inputs(3U, 2U);
588 INST(5U, Opcode::SaveState).NoVregs();
589 INST(6U, Opcode::UnresolvedLoadAndInitClass).ref().Inputs(2U).TypeId(0U);
590 INST(1U, Opcode::SaveStateDeoptimize).NoVregs();
591 }
592 BASIC_BLOCK(2U, 2U, 3U)
593 {
594 INST(100U, Opcode::SaveState).NoVregs();
595 INST(101U, Opcode::UnresolvedLoadAndInitClass).ref().Inputs(100U).TypeId(0U);
596 INST(7U, Opcode::SaveState).NoVregs();
597 INST(8U, Opcode::ResolveVirtual).ptr().Inputs(4U, 7U);
598 INST(9U, Opcode::CallResolvedVirtual)
599 .v0id()
600 .Inputs({{DataType::POINTER, 8U}, {DataType::REFERENCE, 4U}, {DataType::NO_TYPE, 7U}});
601 INST(10U, Opcode::SaveState).NoVregs();
602 INST(11U, Opcode::NullCheck).ref().Inputs(0U, 10U);
603 INST(12U, Opcode::LoadObject).s32().Inputs(11U).TypeId(122U);
604 INST(13U, Opcode::Compare).b().SrcType(DataType::INT32).CC(CC_GE).Inputs(12U, 20U);
605 INST(14U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(13U);
606 }
607 BASIC_BLOCK(3U, -1L)
608 {
609 INST(15U, Opcode::ReturnVoid).v0id();
610 }
611 }
612 ASSERT_FALSE(GetGraph()->RunPass<Licm>(HOST_LIMIT));
613 }
614
TEST_F(LicmTest,DontHoistLenArray)615 TEST_F(LicmTest, DontHoistLenArray)
616 {
617 // If ChecksElimination removed NullCheck based on BoundsAnalysis, we can hoist LenArray
618 // only if the array cannot be null in the loop preheader
619 GRAPH(GetGraph())
620 {
621 PARAMETER(0U, 0U).ref();
622 CONSTANT(1U, 0U);
623 CONSTANT(2U, nullptr).ref();
624
625 BASIC_BLOCK(2U, 3U)
626 {
627 INST(7U, Opcode::Compare).b().SrcType(DataType::REFERENCE).CC(CC_NE).Inputs(0U, 2U);
628 INST(3U, Opcode::SaveStateDeoptimize).NoVregs();
629 }
630
631 BASIC_BLOCK(3U, 4U, 6U)
632 {
633 INST(4U, Opcode::SaveState).NoVregs();
634 INST(5U, Opcode::CallStatic).b().InputsAutoType(4U);
635 INST(6U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(5U);
636 }
637
638 BASIC_BLOCK(4U, 3U, 5U)
639 {
640 INST(8U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(7U);
641 }
642
643 BASIC_BLOCK(5U, 3U)
644 {
645 INST(9U, Opcode::SaveState).NoVregs();
646 INST(10U, Opcode::LenArray).s32().Inputs(0U);
647 INST(11U, Opcode::BoundsCheck).s32().Inputs(10U, 1U, 9U);
648 INST(12U, Opcode::StoreArray).s32().Inputs(0U, 11U, 1U);
649 }
650
651 BASIC_BLOCK(6U, -1L)
652 {
653 INST(13U, Opcode::ReturnVoid).v0id();
654 }
655 }
656
657 auto clone = GraphCloner(GetGraph(), GetGraph()->GetAllocator(), GetGraph()->GetLocalAllocator()).CloneGraph();
658 ASSERT_FALSE(GetGraph()->RunPass<Licm>(HOST_LIMIT));
659 ASSERT_TRUE(GraphComparator().Compare(GetGraph(), clone));
660 }
661
TEST_F(LicmTest,HoistLenArray)662 TEST_F(LicmTest, HoistLenArray)
663 {
664 GRAPH(GetGraph())
665 {
666 PARAMETER(0U, 0U).ref();
667 CONSTANT(1U, 0U);
668
669 BASIC_BLOCK(2U, 3U)
670 {
671 INST(3U, Opcode::SaveStateDeoptimize).NoVregs();
672 INST(4U, Opcode::NullCheck).ref().Inputs(0U, 3U).SetFlag(inst_flags::CAN_DEOPTIMIZE);
673 }
674
675 BASIC_BLOCK(3U, 3U, 4U)
676 {
677 INST(6U, Opcode::SaveState).NoVregs();
678 INST(7U, Opcode::LenArray).s32().Inputs(4U);
679 INST(8U, Opcode::BoundsCheck).s32().Inputs(7U, 1U, 6U);
680 INST(9U, Opcode::StoreArray).s32().Inputs(0U, 8U, 1U);
681 INST(10U, Opcode::CallStatic).b().InputsAutoType(6U);
682 INST(11U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(10U);
683 }
684
685 BASIC_BLOCK(4U, -1L)
686 {
687 INST(12U, Opcode::ReturnVoid).v0id();
688 }
689 }
690
691 ASSERT_TRUE(GetGraph()->RunPass<Licm>(HOST_LIMIT));
692 auto graph = CreateEmptyGraph();
693
694 GRAPH(graph)
695 {
696 PARAMETER(0U, 0U).ref();
697 CONSTANT(1U, 0U);
698
699 BASIC_BLOCK(2U, 3U)
700 {
701 INST(3U, Opcode::SaveStateDeoptimize).NoVregs();
702 INST(4U, Opcode::NullCheck).ref().Inputs(0U, 3U).SetFlag(inst_flags::CAN_DEOPTIMIZE);
703 INST(7U, Opcode::LenArray).s32().Inputs(4U);
704 }
705
706 BASIC_BLOCK(3U, 3U, 4U)
707 {
708 INST(6U, Opcode::SaveState).NoVregs();
709 INST(8U, Opcode::BoundsCheck).s32().Inputs(7U, 1U, 6U);
710 INST(9U, Opcode::StoreArray).s32().Inputs(0U, 8U, 1U);
711 INST(10U, Opcode::CallStatic).b().InputsAutoType(6U);
712 INST(11U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(10U);
713 }
714
715 BASIC_BLOCK(4U, -1L)
716 {
717 INST(12U, Opcode::ReturnVoid).v0id();
718 }
719 }
720 ASSERT_TRUE(GraphComparator().Compare(GetGraph(), graph));
721 }
722
TEST_F(LicmTest,LoadImmediate)723 TEST_F(LicmTest, LoadImmediate)
724 {
725 auto class1 = reinterpret_cast<RuntimeInterface::ClassPtr>(1U);
726 auto graph = CreateEmptyGraph();
727 GRAPH(graph)
728 {
729 CONSTANT(40U, 0xaU);
730 CONSTANT(41U, 0xbU);
731 BASIC_BLOCK(2U, 5U) {}
732 BASIC_BLOCK(5U, 4U, 3U)
733 {
734 INST(13U, Opcode::Compare).b().SrcType(DataType::INT32).CC(CC_GE).Inputs(40U, 41U);
735 INST(14U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(13U);
736 }
737 BASIC_BLOCK(4U, 5U)
738 {
739 INST(2U, Opcode::LoadImmediate).ref().Class(class1);
740 INST(20U, Opcode::SaveState).NoVregs();
741 INST(15U, Opcode::CallStatic).b().InputsAutoType(2U, 20U);
742 }
743 BASIC_BLOCK(3U, -1L)
744 {
745 INST(17U, Opcode::ReturnVoid);
746 }
747 }
748 auto graph1 = CreateEmptyGraph();
749 GRAPH(graph1)
750 {
751 CONSTANT(40U, 0xaU);
752 CONSTANT(41U, 0xbU);
753 BASIC_BLOCK(2U, 5U)
754 {
755 INST(13U, Opcode::Compare).b().SrcType(DataType::INT32).CC(CC_GE).Inputs(40U, 41U);
756 INST(2U, Opcode::LoadImmediate).ref().Class(class1);
757 }
758 BASIC_BLOCK(5U, 4U, 3U)
759 {
760 INST(14U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(13U);
761 }
762 BASIC_BLOCK(4U, 5U)
763 {
764 INST(20U, Opcode::SaveState).NoVregs();
765 INST(15U, Opcode::CallStatic).b().InputsAutoType(2U, 20U);
766 }
767 BASIC_BLOCK(3U, -1L)
768 {
769 INST(17U, Opcode::ReturnVoid);
770 }
771 }
772 GraphChecker(graph).Check();
773 ASSERT_TRUE(graph->RunPass<Licm>(HOST_LIMIT));
774 ASSERT_TRUE(GraphComparator().Compare(graph, graph1));
775 }
776
TEST_F(LicmTest,LoadObjFromConst)777 TEST_F(LicmTest, LoadObjFromConst)
778 {
779 auto obj1 = static_cast<uintptr_t>(1U);
780 auto graph = CreateEmptyGraph();
781 GRAPH(graph)
782 {
783 CONSTANT(40U, 0xaU);
784 CONSTANT(41U, 0xbU);
785 BASIC_BLOCK(2U, 5U) {}
786 BASIC_BLOCK(5U, 4U, 3U)
787 {
788 INST(13U, Opcode::Compare).b().SrcType(DataType::INT32).CC(CC_GE).Inputs(40U, 41U);
789 INST(14U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(13U);
790 }
791 BASIC_BLOCK(4U, 5U)
792 {
793 INST(2U, Opcode::LoadObjFromConst).ref().SetPtr(obj1);
794 INST(20U, Opcode::SaveState).NoVregs();
795 INST(15U, Opcode::CallStatic).b().InputsAutoType(2U, 20U);
796 }
797 BASIC_BLOCK(3U, -1L)
798 {
799 INST(17U, Opcode::ReturnVoid);
800 }
801 }
802 auto graph1 = CreateEmptyGraph();
803 GRAPH(graph1)
804 {
805 CONSTANT(40U, 0xaU);
806 CONSTANT(41U, 0xbU);
807 BASIC_BLOCK(2U, 5U)
808 {
809 INST(13U, Opcode::Compare).b().SrcType(DataType::INT32).CC(CC_GE).Inputs(40U, 41U);
810 INST(2U, Opcode::LoadObjFromConst).ref().SetPtr(obj1);
811 }
812 BASIC_BLOCK(5U, 4U, 3U)
813 {
814 INST(14U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(13U);
815 }
816 BASIC_BLOCK(4U, 5U)
817 {
818 INST(20U, Opcode::SaveState).Inputs(2U).SrcVregs({VirtualRegister::BRIDGE});
819 INST(15U, Opcode::CallStatic).b().InputsAutoType(2U, 20U);
820 }
821 BASIC_BLOCK(3U, -1L)
822 {
823 INST(17U, Opcode::ReturnVoid);
824 }
825 }
826 GraphChecker(graph).Check();
827 ASSERT_TRUE(graph->RunPass<Licm>(HOST_LIMIT));
828 ASSERT_TRUE(GraphComparator().Compare(graph, graph1));
829 }
830
TEST_F(LicmTest,FunctionImmediate)831 TEST_F(LicmTest, FunctionImmediate)
832 {
833 auto fptr = static_cast<uintptr_t>(1U);
834 auto graph = CreateEmptyGraph();
835 graph->SetDynamicMethod();
836 #ifndef NDEBUG
837 graph->SetDynUnitTestFlag();
838 #endif
839 GRAPH(graph)
840 {
841 CONSTANT(40U, 0xaU);
842 CONSTANT(41U, 0xbU);
843 BASIC_BLOCK(2U, 5U) {}
844 BASIC_BLOCK(5U, 4U, 3U)
845 {
846 INST(13U, Opcode::Compare).b().SrcType(DataType::INT32).CC(CC_GE).Inputs(40U, 41U);
847 INST(14U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(13U);
848 }
849 BASIC_BLOCK(4U, 5U)
850 {
851 INST(2U, Opcode::FunctionImmediate).any().SetPtr(fptr);
852 INST(20U, Opcode::SaveState).NoVregs();
853 INST(15U, Opcode::CallStatic).b().InputsAutoType(2U, 20U);
854 }
855 BASIC_BLOCK(3U, -1L)
856 {
857 INST(17U, Opcode::ReturnVoid);
858 }
859 }
860 auto graph1 = CreateEmptyGraph();
861 graph1->SetDynamicMethod();
862 #ifndef NDEBUG
863 graph1->SetDynUnitTestFlag();
864 #endif
865 GRAPH(graph1)
866 {
867 CONSTANT(40U, 0xaU);
868 CONSTANT(41U, 0xbU);
869 BASIC_BLOCK(2U, 5U)
870 {
871 INST(13U, Opcode::Compare).b().SrcType(DataType::INT32).CC(CC_GE).Inputs(40U, 41U);
872 INST(2U, Opcode::FunctionImmediate).any().SetPtr(fptr);
873 }
874 BASIC_BLOCK(5U, 4U, 3U)
875 {
876 INST(14U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(13U);
877 }
878 BASIC_BLOCK(4U, 5U)
879 {
880 INST(20U, Opcode::SaveState).Inputs(2U).SrcVregs({VirtualRegister::BRIDGE});
881 INST(15U, Opcode::CallStatic).b().InputsAutoType(2U, 20U);
882 }
883 BASIC_BLOCK(3U, -1L)
884 {
885 INST(17U, Opcode::ReturnVoid);
886 }
887 }
888 GraphChecker(graph).Check();
889 ASSERT_TRUE(graph->RunPass<Licm>(HOST_LIMIT));
890 ASSERT_TRUE(GraphComparator().Compare(graph, graph1));
891 }
892
TEST_F(LicmTest,LoadFromConstantPool)893 TEST_F(LicmTest, LoadFromConstantPool)
894 {
895 auto graph = CreateEmptyGraph();
896 graph->SetDynamicMethod();
897 #ifndef NDEBUG
898 graph->SetDynUnitTestFlag();
899 #endif
900 GRAPH(graph)
901 {
902 PARAMETER(0U, 0U).any();
903 PARAMETER(1U, 1U).any();
904 BASIC_BLOCK(2U, 3U)
905 {
906 INST(2U, Opcode::LoadConstantPool).any().Inputs(0U);
907 }
908 BASIC_BLOCK(3U, 3U, 4U)
909 {
910 INST(3U, Opcode::SaveState).Inputs(0U, 1U, 2U).SrcVregs({0U, 1U, 2U});
911 INST(9U, Opcode::Intrinsic).any().Inputs({{DataType::ANY, 1U}, {DataType::NO_TYPE, 3U}});
912 INST(4U, Opcode::LoadFromConstantPool).any().Inputs(2U).TypeId(1U);
913 INST(5U, Opcode::SaveState).Inputs(0U, 1U, 2U, 4U, 9U).SrcVregs({0U, 1U, 2U, 3U, 4U});
914 INST(6U, Opcode::Intrinsic).b().Inputs({{DataType::ANY, 4U}, {DataType::ANY, 9U}, {DataType::NO_TYPE, 5U}});
915 INST(7U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(6U);
916 }
917 BASIC_BLOCK(4U, -1L)
918 {
919 INST(8U, Opcode::ReturnVoid).v0id();
920 }
921 }
922
923 ASSERT_TRUE(graph->RunPass<Licm>(HOST_LIMIT));
924
925 auto graph1 = CreateEmptyGraph();
926 graph1->SetDynamicMethod();
927 #ifndef NDEBUG
928 graph1->SetDynUnitTestFlag();
929 #endif
930 GRAPH(graph1)
931 {
932 PARAMETER(0U, 0U).any();
933 PARAMETER(1U, 1U).any();
934 BASIC_BLOCK(2U, 3U)
935 {
936 INST(2U, Opcode::LoadConstantPool).any().Inputs(0U);
937 INST(4U, Opcode::LoadFromConstantPool).any().Inputs(2U).TypeId(1U);
938 }
939 BASIC_BLOCK(3U, 3U, 4U)
940 {
941 INST(3U, Opcode::SaveState).Inputs(0U, 1U, 2U, 4U).SrcVregs({0U, 1U, 2U, VirtualRegister::BRIDGE});
942 INST(9U, Opcode::Intrinsic).any().Inputs({{DataType::ANY, 1U}, {DataType::NO_TYPE, 3U}});
943 INST(5U, Opcode::SaveState).Inputs(0U, 1U, 2U, 4U, 9U).SrcVregs({0U, 1U, 2U, 3U, 4U});
944 INST(6U, Opcode::Intrinsic).b().Inputs({{DataType::ANY, 4U}, {DataType::ANY, 9U}, {DataType::NO_TYPE, 5U}});
945 INST(7U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(6U);
946 }
947 BASIC_BLOCK(4U, -1L)
948 {
949 INST(8U, Opcode::ReturnVoid).v0id();
950 }
951 }
952 ASSERT_TRUE(GraphComparator().Compare(graph, graph1));
953 }
954 // NOLINTEND(readability-magic-numbers)
955
956 } // namespace panda::compiler
957