• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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