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 "macros.h"
17 #include "unit_test.h"
18 #include "optimizer/ir/graph_cloner.h"
19 #include "optimizer/optimizations/deoptimize_elimination.h"
20 #include "optimizer/optimizations/cleanup.h"
21 #include "optimizer/ir/runtime_interface.h"
22
23 namespace ark::compiler {
24 class DeoptimizeEliminationTest : public CommonTest {
25 public:
DeoptimizeEliminationTest()26 DeoptimizeEliminationTest()
27 : graph_(CreateGraphStartEndBlocks()),
28 defaultCompilerSafePointsRequireRegMap_(g_options.IsCompilerSafePointsRequireRegMap())
29 {
30 }
31
~DeoptimizeEliminationTest()32 ~DeoptimizeEliminationTest() override
33 {
34 g_options.SetCompilerSafePointsRequireRegMap(defaultCompilerSafePointsRequireRegMap_);
35 }
36
37 NO_COPY_SEMANTIC(DeoptimizeEliminationTest);
38 NO_MOVE_SEMANTIC(DeoptimizeEliminationTest);
39
GetGraph()40 Graph *GetGraph()
41 {
42 return graph_;
43 }
44
45 private:
46 Graph *graph_ {nullptr};
47 bool defaultCompilerSafePointsRequireRegMap_;
48 };
49
50 // NOLINTBEGIN(readability-magic-numbers)
TEST_F(DeoptimizeEliminationTest,DeoptimizeIfTest)51 TEST_F(DeoptimizeEliminationTest, DeoptimizeIfTest)
52 {
53 GRAPH(GetGraph())
54 {
55 PARAMETER(0U, 0U).s32();
56 CONSTANT(1U, 0U);
57 BASIC_BLOCK(2U, 1U)
58 {
59 // cleanup delete this
60 INST(10U, Opcode::SaveState).Inputs(1U).SrcVregs({1U});
61
62 INST(3U, Opcode::SaveStateDeoptimize).Inputs(1U).SrcVregs({1U});
63 INST(4U, Opcode::Compare).b().Inputs(0U, 1U).CC(CC_GT);
64 INST(5U, Opcode::DeoptimizeIf).Inputs(4U, 3U);
65 // 5 is dominate by 7
66 INST(6U, Opcode::SaveStateDeoptimize).Inputs(1U).SrcVregs({1U});
67 INST(7U, Opcode::DeoptimizeIf).Inputs(4U, 6U);
68
69 // redundant
70 INST(8U, Opcode::DeoptimizeIf).Inputs(1U, 6U);
71
72 INST(9U, Opcode::ReturnVoid).v0id();
73 }
74 }
75 ASSERT_TRUE(GetGraph()->RunPass<DeoptimizeElimination>());
76 ASSERT_TRUE(GetGraph()->RunPass<Cleanup>());
77 auto graph = CreateEmptyGraph();
78 GRAPH(graph)
79 {
80 PARAMETER(0U, 0U).s32();
81 CONSTANT(1U, 0U);
82 BASIC_BLOCK(2U, 1U)
83 {
84 INST(3U, Opcode::SaveStateDeoptimize).Inputs(1U).SrcVregs({1U});
85 INST(4U, Opcode::Compare).b().Inputs(0U, 1U).CC(CC_GT);
86 INST(5U, Opcode::DeoptimizeIf).Inputs(4U, 3U);
87
88 INST(9U, Opcode::ReturnVoid).v0id();
89 }
90 }
91 ASSERT_TRUE(GraphComparator().Compare(GetGraph(), graph));
92 }
93
TEST_F(DeoptimizeEliminationTest,ChaGuardOneBlockTest)94 TEST_F(DeoptimizeEliminationTest, ChaGuardOneBlockTest)
95 {
96 GRAPH(GetGraph())
97 {
98 CONSTANT(10U, 0U);
99 BASIC_BLOCK(2U, 1U)
100 {
101 INST(0U, Opcode::SaveStateDeoptimize).Inputs(10U).SrcVregs({10U});
102 INST(1U, Opcode::IsMustDeoptimize).b();
103 INST(2U, Opcode::DeoptimizeIf).Inputs(1U, 0U);
104
105 INST(3U, Opcode::SaveStateDeoptimize).Inputs(10U).SrcVregs({10U});
106 INST(4U, Opcode::IsMustDeoptimize).b();
107 INST(5U, Opcode::DeoptimizeIf).Inputs(4U, 3U);
108
109 INST(9U, Opcode::ReturnVoid).v0id();
110 }
111 }
112 ASSERT_TRUE(GetGraph()->RunPass<DeoptimizeElimination>());
113 auto graph = CreateEmptyGraph();
114 GRAPH(graph)
115 {
116 CONSTANT(10U, 0U);
117 BASIC_BLOCK(2U, 1U)
118 {
119 INST(0U, Opcode::SaveStateDeoptimize).Inputs(10U).SrcVregs({10U});
120 INST(1U, Opcode::IsMustDeoptimize).b();
121 INST(2U, Opcode::DeoptimizeIf).Inputs(1U, 0U);
122
123 INST(3U, Opcode::NOP);
124 INST(4U, Opcode::NOP);
125 INST(5U, Opcode::NOP);
126
127 INST(9U, Opcode::ReturnVoid).v0id();
128 }
129 }
130 ASSERT_TRUE(GraphComparator().Compare(GetGraph(), graph));
131 }
132
TEST_F(DeoptimizeEliminationTest,ChaGuardOneBlockCallTest)133 TEST_F(DeoptimizeEliminationTest, ChaGuardOneBlockCallTest)
134 {
135 // Not applied. Call to runtime between guards.
136 GRAPH(GetGraph())
137 {
138 CONSTANT(10U, 0U);
139 BASIC_BLOCK(2U, 1U)
140 {
141 INST(0U, Opcode::SaveStateDeoptimize).Inputs(10U).SrcVregs({10U});
142 INST(1U, Opcode::IsMustDeoptimize).b();
143 INST(2U, Opcode::DeoptimizeIf).Inputs(1U, 0U);
144 INST(20U, Opcode::SaveState).NoVregs();
145 INST(6U, Opcode::CallStatic).v0id().InputsAutoType(20U);
146
147 INST(3U, Opcode::SaveStateDeoptimize).Inputs(10U).SrcVregs({10U});
148 INST(4U, Opcode::IsMustDeoptimize).b();
149 INST(5U, Opcode::DeoptimizeIf).Inputs(4U, 3U);
150
151 INST(9U, Opcode::ReturnVoid).v0id();
152 }
153 }
154 auto clone = GraphCloner(GetGraph(), GetGraph()->GetAllocator(), GetGraph()->GetLocalAllocator()).CloneGraph();
155 ASSERT_FALSE(GetGraph()->RunPass<DeoptimizeElimination>());
156 ASSERT_TRUE(GraphComparator().Compare(GetGraph(), clone));
157 }
158
TEST_F(DeoptimizeEliminationTest,ChaGuardOneBlockSeveralGuardsTest)159 TEST_F(DeoptimizeEliminationTest, ChaGuardOneBlockSeveralGuardsTest)
160 {
161 GRAPH(GetGraph())
162 {
163 CONSTANT(10U, 0U);
164 BASIC_BLOCK(2U, 1U)
165 {
166 INST(0U, Opcode::SaveStateDeoptimize).Inputs(10U).SrcVregs({10U});
167 INST(1U, Opcode::IsMustDeoptimize).b();
168 INST(2U, Opcode::DeoptimizeIf).Inputs(1U, 0U);
169 INST(20U, Opcode::SaveState).NoVregs();
170 INST(6U, Opcode::CallStatic).v0id().InputsAutoType(20U);
171
172 INST(3U, Opcode::SaveStateDeoptimize).Inputs(10U).SrcVregs({10U});
173 INST(4U, Opcode::IsMustDeoptimize).b();
174 INST(5U, Opcode::DeoptimizeIf).Inputs(4U, 3U);
175
176 INST(11U, Opcode::SaveStateDeoptimize).Inputs(10U).SrcVregs({10U});
177 INST(12U, Opcode::IsMustDeoptimize).b();
178 INST(13U, Opcode::DeoptimizeIf).Inputs(12U, 11U);
179
180 INST(9U, Opcode::ReturnVoid).v0id();
181 }
182 }
183 ASSERT_TRUE(GetGraph()->RunPass<DeoptimizeElimination>());
184 auto graph = CreateEmptyGraph();
185 GRAPH(graph)
186 {
187 CONSTANT(10U, 0U);
188 BASIC_BLOCK(2U, 1U)
189 {
190 INST(0U, Opcode::SaveStateDeoptimize).Inputs(10U).SrcVregs({10U});
191 INST(1U, Opcode::IsMustDeoptimize).b();
192 INST(2U, Opcode::DeoptimizeIf).Inputs(1U, 0U);
193 INST(20U, Opcode::SaveState).NoVregs();
194 INST(6U, Opcode::CallStatic).v0id().InputsAutoType(20U);
195
196 INST(3U, Opcode::SaveStateDeoptimize).Inputs(10U).SrcVregs({10U});
197 INST(4U, Opcode::IsMustDeoptimize).b();
198 INST(5U, Opcode::DeoptimizeIf).Inputs(4U, 3U);
199
200 INST(11U, Opcode::NOP);
201 INST(12U, Opcode::NOP);
202 INST(13U, Opcode::NOP);
203
204 INST(9U, Opcode::ReturnVoid).v0id();
205 }
206 }
207 ASSERT_TRUE(GraphComparator().Compare(GetGraph(), graph));
208 }
209
TEST_F(DeoptimizeEliminationTest,ChaGuardOneBlockCallInlinedTest)210 TEST_F(DeoptimizeEliminationTest, ChaGuardOneBlockCallInlinedTest)
211 {
212 GRAPH(GetGraph())
213 {
214 CONSTANT(10U, 0U);
215 BASIC_BLOCK(2U, 1U)
216 {
217 INST(0U, Opcode::SaveStateDeoptimize).Inputs(10U).SrcVregs({10U});
218 INST(1U, Opcode::IsMustDeoptimize).b();
219 INST(2U, Opcode::DeoptimizeIf).Inputs(1U, 0U);
220 INST(20U, Opcode::SaveState).NoVregs();
221 INST(6U, Opcode::CallStatic).v0id().Inlined().InputsAutoType(20U);
222 INST(7U, Opcode::ReturnInlined).Inputs(20U);
223
224 INST(3U, Opcode::SaveStateDeoptimize).Inputs(10U).SrcVregs({10U});
225 INST(4U, Opcode::IsMustDeoptimize).b();
226 INST(5U, Opcode::DeoptimizeIf).Inputs(4U, 3U);
227
228 INST(9U, Opcode::ReturnVoid).v0id();
229 }
230 }
231 ASSERT_TRUE(GetGraph()->RunPass<DeoptimizeElimination>());
232 auto graph = CreateEmptyGraph();
233 GRAPH(graph)
234 {
235 CONSTANT(10U, 0U);
236 BASIC_BLOCK(2U, 1U)
237 {
238 INST(0U, Opcode::SaveStateDeoptimize).Inputs(10U).SrcVregs({10U});
239 INST(1U, Opcode::IsMustDeoptimize).b();
240 INST(2U, Opcode::DeoptimizeIf).Inputs(1U, 0U);
241 INST(20U, Opcode::SaveState).NoVregs();
242 INST(6U, Opcode::CallStatic).v0id().Inlined().InputsAutoType(20U);
243 INST(7U, Opcode::ReturnInlined).Inputs(20U);
244
245 INST(3U, Opcode::NOP);
246 INST(4U, Opcode::NOP);
247 INST(5U, Opcode::NOP);
248
249 INST(9U, Opcode::ReturnVoid).v0id();
250 }
251 }
252 ASSERT_TRUE(GraphComparator().Compare(GetGraph(), graph));
253 }
254
TEST_F(DeoptimizeEliminationTest,ChaGuardIfTest)255 TEST_F(DeoptimizeEliminationTest, ChaGuardIfTest)
256 {
257 /*
258 * Not applied.
259 * bb1
260 * guard
261 * | \
262 * | bb2
263 * | runtime call
264 * | /
265 * bb3
266 * guard
267 */
268 GRAPH(GetGraph())
269 {
270 PARAMETER(10U, 0U).s32();
271 CONSTANT(11U, 1U);
272 BASIC_BLOCK(2U, 4U, 5U)
273 {
274 INST(0U, Opcode::SaveStateDeoptimize).Inputs(10U, 11U).SrcVregs({0U, 1U});
275 INST(1U, Opcode::IsMustDeoptimize).b();
276 INST(2U, Opcode::DeoptimizeIf).Inputs(1U, 0U);
277 INST(3U, Opcode::Compare).b().CC(CC_LT).Inputs(10U, 11U);
278 INST(4U, Opcode::IfImm).CC(CC_NE).Inputs(3U).Imm(0U);
279 }
280 BASIC_BLOCK(4U, 5U)
281 {
282 INST(20U, Opcode::SaveState).NoVregs();
283 INST(5U, Opcode::CallStatic).v0id().InputsAutoType(20U);
284 }
285 BASIC_BLOCK(5U, 1U)
286 {
287 INST(6U, Opcode::SaveStateDeoptimize).Inputs(10U, 11U).SrcVregs({0U, 1U});
288 INST(7U, Opcode::IsMustDeoptimize).b();
289 INST(8U, Opcode::DeoptimizeIf).Inputs(7U, 6U);
290 INST(9U, Opcode::ReturnVoid).v0id();
291 }
292 }
293 auto clone = GraphCloner(GetGraph(), GetGraph()->GetAllocator(), GetGraph()->GetLocalAllocator()).CloneGraph();
294 ASSERT_FALSE(GetGraph()->RunPass<DeoptimizeElimination>());
295 ASSERT_TRUE(GraphComparator().Compare(GetGraph(), clone));
296 }
297
SRC_GRAPH(ChaGuardIfSeveralGuardsTest,Graph * graph)298 SRC_GRAPH(ChaGuardIfSeveralGuardsTest, Graph *graph)
299 {
300 GRAPH(graph)
301 {
302 PARAMETER(10U, 0U).s32();
303 CONSTANT(11U, 1U);
304 BASIC_BLOCK(2U, 4U, 5U)
305 {
306 INST(0U, Opcode::SaveStateDeoptimize).Inputs(10U, 11U).SrcVregs({0U, 1U});
307 INST(1U, Opcode::IsMustDeoptimize).b();
308 INST(2U, Opcode::DeoptimizeIf).Inputs(1U, 0U);
309 INST(20U, Opcode::SaveState).NoVregs();
310 INST(12U, Opcode::CallStatic).v0id().InputsAutoType(20U);
311 INST(13U, Opcode::SaveStateDeoptimize).Inputs(10U, 11U).SrcVregs({0U, 1U});
312 INST(14U, Opcode::IsMustDeoptimize).b();
313 INST(15U, Opcode::DeoptimizeIf).Inputs(14U, 13U);
314 INST(3U, Opcode::Compare).b().CC(CC_LT).Inputs(10U, 11U);
315 INST(4U, Opcode::IfImm).CC(CC_NE).Inputs(3U).Imm(0U);
316 }
317 BASIC_BLOCK(4U, 5U)
318 {
319 INST(21U, Opcode::SaveState).NoVregs();
320 INST(5U, Opcode::CallStatic).v0id().Inlined().InputsAutoType(21U);
321 INST(16U, Opcode::ReturnInlined).Inputs(21U);
322 }
323 BASIC_BLOCK(5U, 1U)
324 {
325 INST(6U, Opcode::SaveStateDeoptimize).Inputs(10U, 11U).SrcVregs({0U, 1U});
326 INST(7U, Opcode::IsMustDeoptimize).b();
327 INST(8U, Opcode::DeoptimizeIf).Inputs(7U, 6U);
328 INST(9U, Opcode::ReturnVoid).v0id();
329 }
330 }
331 }
332
OUT_GRAPH(ChaGuardIfSeveralGuardsTest,Graph * graph)333 OUT_GRAPH(ChaGuardIfSeveralGuardsTest, Graph *graph)
334 {
335 GRAPH(graph)
336 {
337 PARAMETER(10U, 0U).s32();
338 CONSTANT(11U, 1U);
339 BASIC_BLOCK(2U, 4U, 5U)
340 {
341 INST(0U, Opcode::SaveStateDeoptimize).Inputs(10U, 11U).SrcVregs({0U, 1U});
342 INST(1U, Opcode::IsMustDeoptimize).b();
343 INST(2U, Opcode::DeoptimizeIf).Inputs(1U, 0U);
344 INST(20U, Opcode::SaveState).NoVregs();
345 INST(12U, Opcode::CallStatic).v0id().InputsAutoType(20U);
346 INST(13U, Opcode::SaveStateDeoptimize).Inputs(10U, 11U).SrcVregs({0U, 1U});
347 INST(14U, Opcode::IsMustDeoptimize).b();
348 INST(15U, Opcode::DeoptimizeIf).Inputs(14U, 13U);
349 INST(3U, Opcode::Compare).b().CC(CC_LT).Inputs(10U, 11U);
350 INST(4U, Opcode::IfImm).CC(CC_NE).Inputs(3U).Imm(0U);
351 }
352 BASIC_BLOCK(4U, 5U)
353 {
354 INST(21U, Opcode::SaveState).NoVregs();
355 INST(5U, Opcode::CallStatic).v0id().Inlined().InputsAutoType(21U);
356 INST(16U, Opcode::ReturnInlined).Inputs(21U);
357 }
358 BASIC_BLOCK(5U, 1U)
359 {
360 INST(6U, Opcode::NOP);
361 INST(7U, Opcode::NOP);
362 INST(8U, Opcode::NOP);
363 INST(9U, Opcode::ReturnVoid).v0id();
364 }
365 }
366 }
367
TEST_F(DeoptimizeEliminationTest,ChaGuardIfSeveralGuardsTest)368 TEST_F(DeoptimizeEliminationTest, ChaGuardIfSeveralGuardsTest)
369 {
370 /*
371 * bb1
372 * guard
373 * call
374 * guard
375 * | \
376 * | bb2
377 * | some inst
378 * | /
379 * bb3
380 * guard
381 */
382 src_graph::ChaGuardIfSeveralGuardsTest::CREATE(GetGraph());
383 ASSERT_TRUE(GetGraph()->RunPass<DeoptimizeElimination>());
384 auto graph = CreateEmptyGraph();
385 out_graph::ChaGuardIfSeveralGuardsTest::CREATE(graph);
386 ASSERT_TRUE(GraphComparator().Compare(GetGraph(), graph));
387 }
388
SRC_GRAPH(ChaGuardLoopTest,Graph * graph)389 SRC_GRAPH(ChaGuardLoopTest, Graph *graph)
390 {
391 GRAPH(graph)
392 {
393 CONSTANT(10U, 0U);
394 CONSTANT(11U, 1U);
395 CONSTANT(12U, 10U);
396 BASIC_BLOCK(2U, 3U)
397 {
398 INST(0U, Opcode::SaveStateDeoptimize).Inputs(10U, 11U).SrcVregs({0U, 1U});
399 INST(1U, Opcode::IsMustDeoptimize).b();
400 INST(2U, Opcode::DeoptimizeIf).Inputs(1U, 0U);
401 }
402 BASIC_BLOCK(3U, 4U, 5U)
403 {
404 INST(3U, Opcode::Phi).s32().Inputs(10U, 7U);
405 INST(8U, Opcode::Compare).b().CC(CC_LT).Inputs(3U, 12U);
406 INST(9U, Opcode::IfImm).CC(CC_NE).Inputs(8U).Imm(0U);
407 }
408 BASIC_BLOCK(4U, 3U)
409 {
410 INST(4U, Opcode::SaveStateDeoptimize).Inputs(10U, 11U).SrcVregs({0U, 1U});
411 INST(5U, Opcode::IsMustDeoptimize).b();
412 INST(6U, Opcode::DeoptimizeIf).Inputs(5U, 4U);
413 INST(7U, Opcode::Add).s32().Inputs(3U, 11U);
414 }
415 BASIC_BLOCK(5U, 1U)
416 {
417 INST(13U, Opcode::Return).s32().Inputs(3U);
418 }
419 }
420 }
421
OUT_GRAPH(ChaGuardLoopTest,Graph * graph)422 OUT_GRAPH(ChaGuardLoopTest, Graph *graph)
423 {
424 GRAPH(graph)
425 {
426 CONSTANT(10U, 0U);
427 CONSTANT(11U, 1U);
428 CONSTANT(12U, 10U);
429 BASIC_BLOCK(2U, 3U)
430 {
431 INST(0U, Opcode::SaveStateDeoptimize).Inputs(10U, 11U).SrcVregs({0U, 1U});
432 INST(1U, Opcode::IsMustDeoptimize).b();
433 INST(2U, Opcode::DeoptimizeIf).Inputs(1U, 0U);
434 }
435 BASIC_BLOCK(3U, 4U, 5U)
436 {
437 INST(3U, Opcode::Phi).s32().Inputs(10U, 7U);
438 INST(8U, Opcode::Compare).b().CC(CC_LT).Inputs(3U, 12U);
439 INST(9U, Opcode::IfImm).CC(CC_NE).Inputs(8U).Imm(0U);
440 }
441 BASIC_BLOCK(4U, 3U)
442 {
443 INST(4U, Opcode::NOP);
444 INST(5U, Opcode::NOP);
445 INST(6U, Opcode::NOP);
446 INST(7U, Opcode::Add).s32().Inputs(3U, 11U);
447 }
448 BASIC_BLOCK(5U, 1U)
449 {
450 INST(13U, Opcode::Return).s32().Inputs(3U);
451 }
452 }
453 }
454
TEST_F(DeoptimizeEliminationTest,ChaGuardLoopTest)455 TEST_F(DeoptimizeEliminationTest, ChaGuardLoopTest)
456 {
457 src_graph::ChaGuardLoopTest::CREATE(GetGraph());
458 ASSERT_TRUE(GetGraph()->RunPass<DeoptimizeElimination>());
459 auto graph = CreateEmptyGraph();
460 out_graph::ChaGuardLoopTest::CREATE(graph);
461 ASSERT_TRUE(GraphComparator().Compare(GetGraph(), graph));
462 }
463
TEST_F(DeoptimizeEliminationTest,ChaGuardLoopWithCallAfterGuardTest)464 TEST_F(DeoptimizeEliminationTest, ChaGuardLoopWithCallAfterGuardTest)
465 {
466 GRAPH(GetGraph())
467 {
468 CONSTANT(10U, 0U);
469 CONSTANT(11U, 1U);
470 CONSTANT(12U, 10U);
471 BASIC_BLOCK(2U, 3U)
472 {
473 INST(0U, Opcode::SaveStateDeoptimize).Inputs(10U, 11U).SrcVregs({0U, 1U});
474 INST(1U, Opcode::IsMustDeoptimize).b();
475 INST(2U, Opcode::DeoptimizeIf).Inputs(1U, 0U);
476 }
477 BASIC_BLOCK(3U, 4U, 5U)
478 {
479 INST(3U, Opcode::Phi).s32().Inputs(10U, 7U);
480 INST(8U, Opcode::Compare).b().CC(CC_LT).Inputs(3U, 12U);
481 INST(9U, Opcode::IfImm).CC(CC_NE).Inputs(8U).Imm(0U);
482 }
483 BASIC_BLOCK(4U, 3U)
484 {
485 INST(4U, Opcode::SaveStateDeoptimize).Inputs(10U, 11U).SrcVregs({0U, 1U});
486 INST(5U, Opcode::IsMustDeoptimize).b();
487 INST(6U, Opcode::DeoptimizeIf).Inputs(5U, 4U);
488 INST(20U, Opcode::SaveState).NoVregs();
489 INST(14U, Opcode::CallStatic).v0id().InputsAutoType(20U);
490 INST(7U, Opcode::Add).s32().Inputs(3U, 11U);
491 }
492 BASIC_BLOCK(5U, 1U)
493 {
494 INST(13U, Opcode::Return).s32().Inputs(3U);
495 }
496 }
497 auto clone = GraphCloner(GetGraph(), GetGraph()->GetAllocator(), GetGraph()->GetLocalAllocator()).CloneGraph();
498 ASSERT_FALSE(GetGraph()->RunPass<DeoptimizeElimination>());
499 ASSERT_TRUE(GraphComparator().Compare(GetGraph(), clone));
500 }
501
TEST_F(DeoptimizeEliminationTest,ChaGuardLoopWithCallBeforeGuardTest)502 TEST_F(DeoptimizeEliminationTest, ChaGuardLoopWithCallBeforeGuardTest)
503 {
504 GRAPH(GetGraph())
505 {
506 CONSTANT(10U, 0U);
507 CONSTANT(11U, 1U);
508 CONSTANT(12U, 10U);
509 BASIC_BLOCK(2U, 3U)
510 {
511 INST(0U, Opcode::SaveStateDeoptimize).Inputs(10U, 11U).SrcVregs({0U, 1U});
512 INST(1U, Opcode::IsMustDeoptimize).b();
513 INST(2U, Opcode::DeoptimizeIf).Inputs(1U, 0U);
514 }
515 BASIC_BLOCK(3U, 4U, 5U)
516 {
517 INST(3U, Opcode::Phi).s32().Inputs(10U, 7U);
518 INST(8U, Opcode::Compare).b().CC(CC_LT).Inputs(3U, 12U);
519 INST(9U, Opcode::IfImm).CC(CC_NE).Inputs(8U).Imm(0U);
520 }
521 BASIC_BLOCK(4U, 3U)
522 {
523 INST(20U, Opcode::SaveState).NoVregs();
524 INST(14U, Opcode::CallStatic).v0id().InputsAutoType(20U);
525 INST(4U, Opcode::SaveStateDeoptimize).Inputs(10U, 11U).SrcVregs({0U, 1U});
526 INST(5U, Opcode::IsMustDeoptimize).b();
527 INST(6U, Opcode::DeoptimizeIf).Inputs(5U, 4U);
528 INST(7U, Opcode::Add).s32().Inputs(3U, 11U);
529 }
530 BASIC_BLOCK(5U, 1U)
531 {
532 INST(13U, Opcode::Return).s32().Inputs(3U);
533 }
534 }
535 auto clone = GraphCloner(GetGraph(), GetGraph()->GetAllocator(), GetGraph()->GetLocalAllocator()).CloneGraph();
536 ASSERT_FALSE(GetGraph()->RunPass<DeoptimizeElimination>());
537 ASSERT_TRUE(GraphComparator().Compare(GetGraph(), clone));
538 }
539
SRC_GRAPH(ChaGuardLoopWithCallBetweenGuardsWithDomGuardTest,Graph * graph)540 SRC_GRAPH(ChaGuardLoopWithCallBetweenGuardsWithDomGuardTest, Graph *graph)
541 {
542 GRAPH(graph)
543 {
544 CONSTANT(10U, 0U);
545 CONSTANT(11U, 1U);
546 CONSTANT(12U, 10U);
547 BASIC_BLOCK(2U, 3U)
548 {
549 INST(0U, Opcode::SaveStateDeoptimize).Inputs(10U, 11U).SrcVregs({0U, 1U});
550 INST(1U, Opcode::IsMustDeoptimize).b();
551 INST(2U, Opcode::DeoptimizeIf).Inputs(1U, 0U);
552 }
553 BASIC_BLOCK(3U, 4U, 5U)
554 {
555 INST(3U, Opcode::Phi).s32().Inputs(10U, 7U);
556 INST(8U, Opcode::Compare).b().CC(CC_LT).Inputs(3U, 12U);
557 INST(9U, Opcode::IfImm).CC(CC_NE).Inputs(8U).Imm(0U);
558 }
559 BASIC_BLOCK(4U, 3U)
560 {
561 INST(15U, Opcode::SaveStateDeoptimize).Inputs(10U, 11U).SrcVregs({0U, 1U});
562 INST(16U, Opcode::IsMustDeoptimize).b();
563 INST(17U, Opcode::DeoptimizeIf).Inputs(16U, 15U);
564 INST(20U, Opcode::SaveState).NoVregs();
565 INST(14U, Opcode::CallStatic).v0id().InputsAutoType(20U);
566 INST(4U, Opcode::SaveStateDeoptimize).Inputs(10U, 11U).SrcVregs({0U, 1U});
567 INST(5U, Opcode::IsMustDeoptimize).b();
568 INST(6U, Opcode::DeoptimizeIf).Inputs(5U, 4U);
569 INST(7U, Opcode::Add).s32().Inputs(3U, 11U);
570 }
571 BASIC_BLOCK(5U, 1U)
572 {
573 INST(13U, Opcode::Return).s32().Inputs(3U);
574 }
575 }
576 }
577
OUT_GRAPH(ChaGuardLoopWithCallBetweenGuardsWithDomGuardTest,Graph * graph)578 OUT_GRAPH(ChaGuardLoopWithCallBetweenGuardsWithDomGuardTest, Graph *graph)
579 {
580 GRAPH(graph)
581 {
582 CONSTANT(10U, 0U);
583 CONSTANT(11U, 1U);
584 CONSTANT(12U, 10U);
585 BASIC_BLOCK(2U, 3U)
586 {
587 INST(0U, Opcode::SaveStateDeoptimize).Inputs(10U, 11U).SrcVregs({0U, 1U});
588 INST(1U, Opcode::IsMustDeoptimize).b();
589 INST(2U, Opcode::DeoptimizeIf).Inputs(1U, 0U);
590 }
591 BASIC_BLOCK(3U, 4U, 5U)
592 {
593 INST(3U, Opcode::Phi).s32().Inputs(10U, 7U);
594 INST(8U, Opcode::Compare).b().CC(CC_LT).Inputs(3U, 12U);
595 INST(9U, Opcode::IfImm).CC(CC_NE).Inputs(8U).Imm(0U);
596 }
597 BASIC_BLOCK(4U, 3U)
598 {
599 INST(15U, Opcode::NOP);
600 INST(16U, Opcode::NOP);
601 INST(17U, Opcode::NOP);
602 INST(20U, Opcode::SaveState).NoVregs();
603 INST(14U, Opcode::CallStatic).v0id().InputsAutoType(20U);
604 INST(4U, Opcode::SaveStateDeoptimize).Inputs(10U, 11U).SrcVregs({0U, 1U});
605 INST(5U, Opcode::IsMustDeoptimize).b();
606 INST(6U, Opcode::DeoptimizeIf).Inputs(5U, 4U);
607 INST(7U, Opcode::Add).s32().Inputs(3U, 11U);
608 }
609 BASIC_BLOCK(5U, 1U)
610 {
611 INST(13U, Opcode::Return).s32().Inputs(3U);
612 }
613 }
614 }
615
TEST_F(DeoptimizeEliminationTest,ChaGuardLoopWithCallBetweenGuardsWithDomGuardTest)616 TEST_F(DeoptimizeEliminationTest, ChaGuardLoopWithCallBetweenGuardsWithDomGuardTest)
617 {
618 src_graph::ChaGuardLoopWithCallBetweenGuardsWithDomGuardTest::CREATE(GetGraph());
619 ASSERT_TRUE(GetGraph()->RunPass<DeoptimizeElimination>());
620 auto graph = CreateEmptyGraph();
621 out_graph::ChaGuardLoopWithCallBetweenGuardsWithDomGuardTest::CREATE(graph);
622 ASSERT_TRUE(GraphComparator().Compare(GetGraph(), graph));
623 }
624
SRC_GRAPH(ChaGuardLoopWithDuplicatedGuardsTest,Graph * graph)625 SRC_GRAPH(ChaGuardLoopWithDuplicatedGuardsTest, Graph *graph)
626 {
627 GRAPH(graph)
628 {
629 CONSTANT(0U, 0U);
630 CONSTANT(1U, 1U);
631 PARAMETER(2U, 0U).s32();
632 BASIC_BLOCK(2U, 3U, 10U)
633 {
634 INST(3U, Opcode::Compare).b().CC(CC_LT).Inputs(0U, 2U);
635 INST(4U, Opcode::IfImm).CC(CC_NE).Inputs(3U).Imm(0U);
636 }
637 BASIC_BLOCK(3U, 4U, 5U)
638 {
639 INST(19U, Opcode::Phi).s32().Inputs(0U, 14U);
640 INST(5U, Opcode::SaveStateDeoptimize).Inputs(0U, 1U, 2U).SrcVregs({0U, 1U, 2U});
641 INST(6U, Opcode::IsMustDeoptimize).b();
642 INST(7U, Opcode::DeoptimizeIf).Inputs(6U, 5U);
643 INST(8U, Opcode::Compare).b().CC(CC_LT).Inputs(0U, 2U);
644 INST(9U, Opcode::IfImm).CC(CC_NE).Inputs(8U).Imm(0U);
645 }
646 BASIC_BLOCK(4U, 5U)
647 {
648 INST(10U, Opcode::SaveStateDeoptimize).Inputs(0U, 1U, 2U).SrcVregs({0U, 1U, 2U});
649 INST(11U, Opcode::IsMustDeoptimize).b();
650 INST(12U, Opcode::DeoptimizeIf).Inputs(11U, 10U);
651 }
652 BASIC_BLOCK(5U, 3U, 10U)
653 {
654 INST(20U, Opcode::SaveState).NoVregs();
655 INST(13U, Opcode::CallStatic).v0id().InputsAutoType(20U);
656 INST(14U, Opcode::Add).s32().Inputs(19U, 1U);
657 INST(15U, Opcode::Compare).b().CC(CC_LT).Inputs(14U, 2U);
658 INST(16U, Opcode::IfImm).CC(CC_NE).Inputs(15U).Imm(0U);
659 }
660
661 BASIC_BLOCK(10U, 1U)
662 {
663 INST(17U, Opcode::Phi).s32().Inputs(0U, 14U);
664 INST(18U, Opcode::Return).s32().Inputs(17U);
665 }
666 }
667 }
668
OUT_GRAPH(ChaGuardLoopWithDuplicatedGuardsTest,Graph * graph)669 OUT_GRAPH(ChaGuardLoopWithDuplicatedGuardsTest, Graph *graph)
670 {
671 GRAPH(graph)
672 {
673 CONSTANT(0U, 0U);
674 CONSTANT(1U, 1U);
675 PARAMETER(2U, 0U).s32();
676 BASIC_BLOCK(2U, 3U, 10U)
677 {
678 INST(3U, Opcode::Compare).b().CC(CC_LT).Inputs(0U, 2U);
679 INST(4U, Opcode::IfImm).CC(CC_NE).Inputs(3U).Imm(0U);
680 }
681 BASIC_BLOCK(3U, 4U, 5U)
682 {
683 INST(19U, Opcode::Phi).s32().Inputs(0U, 14U);
684 INST(5U, Opcode::SaveStateDeoptimize).Inputs(0U, 1U, 2U).SrcVregs({0U, 1U, 2U});
685 INST(6U, Opcode::IsMustDeoptimize).b();
686 INST(7U, Opcode::DeoptimizeIf).Inputs(6U, 5U);
687 INST(8U, Opcode::Compare).b().CC(CC_LT).Inputs(0U, 2U);
688 INST(9U, Opcode::IfImm).CC(CC_NE).Inputs(8U).Imm(0U);
689 }
690 BASIC_BLOCK(4U, 5U)
691 {
692 INST(10U, Opcode::NOP);
693 INST(11U, Opcode::NOP);
694 INST(12U, Opcode::NOP);
695 }
696 BASIC_BLOCK(5U, 3U, 10U)
697 {
698 INST(20U, Opcode::SaveState).NoVregs();
699 INST(13U, Opcode::CallStatic).v0id().InputsAutoType(20U);
700 INST(14U, Opcode::Add).s32().Inputs(19U, 1U);
701 INST(15U, Opcode::Compare).b().CC(CC_LT).Inputs(14U, 2U);
702 INST(16U, Opcode::IfImm).CC(CC_NE).Inputs(15U).Imm(0U);
703 }
704
705 BASIC_BLOCK(10U, 1U)
706 {
707 INST(17U, Opcode::Phi).s32().Inputs(0U, 14U);
708 INST(18U, Opcode::Return).s32().Inputs(17U);
709 }
710 }
711 }
712
TEST_F(DeoptimizeEliminationTest,ChaGuardLoopWithDuplicatedGuardsTest)713 TEST_F(DeoptimizeEliminationTest, ChaGuardLoopWithDuplicatedGuardsTest)
714 {
715 src_graph::ChaGuardLoopWithDuplicatedGuardsTest::CREATE(GetGraph());
716 ASSERT_TRUE(GetGraph()->RunPass<DeoptimizeElimination>());
717 auto graph = CreateEmptyGraph();
718 out_graph::ChaGuardLoopWithDuplicatedGuardsTest::CREATE(graph);
719 ASSERT_TRUE(GraphComparator().Compare(GetGraph(), graph));
720 }
721
TEST_F(DeoptimizeEliminationTest,ChaGuardNotDominateTest)722 TEST_F(DeoptimizeEliminationTest, ChaGuardNotDominateTest)
723 {
724 GRAPH(GetGraph())
725 {
726 PARAMETER(0U, 0U).s32();
727 PARAMETER(1U, 1U).s32();
728 BASIC_BLOCK(2U, 3U, 4U)
729 {
730 INST(2U, Opcode::Compare).b().CC(CC_EQ).Inputs(0U, 1U);
731 INST(3U, Opcode::IfImm).CC(CC_NE).Inputs(2U).Imm(0U);
732 }
733 BASIC_BLOCK(3U, 4U)
734 {
735 INST(4U, Opcode::SaveStateDeoptimize).Inputs(0U, 1U).SrcVregs({0U, 1U});
736 INST(5U, Opcode::IsMustDeoptimize).b();
737 INST(6U, Opcode::DeoptimizeIf).Inputs(5U, 4U);
738 }
739 BASIC_BLOCK(4U, 1U)
740 {
741 INST(7U, Opcode::SaveStateDeoptimize).Inputs(0U, 1U).SrcVregs({0U, 1U});
742 INST(8U, Opcode::IsMustDeoptimize).b();
743 INST(9U, Opcode::DeoptimizeIf).Inputs(8U, 7U);
744 INST(10U, Opcode::ReturnVoid).v0id();
745 }
746 }
747 auto clone = GraphCloner(GetGraph(), GetGraph()->GetAllocator(), GetGraph()->GetLocalAllocator()).CloneGraph();
748 ASSERT_FALSE(GetGraph()->RunPass<DeoptimizeElimination>());
749 ASSERT_TRUE(GraphComparator().Compare(GetGraph(), clone));
750 }
751
SRC_GRAPH(ChaGuardIfWithGuardsTest,Graph * graph)752 SRC_GRAPH(ChaGuardIfWithGuardsTest, Graph *graph)
753 {
754 GRAPH(graph)
755 {
756 PARAMETER(0U, 0U).s32();
757 PARAMETER(1U, 1U).s32();
758 BASIC_BLOCK(2U, 3U, 4U)
759 {
760 INST(5U, Opcode::Compare).b().CC(CC_EQ).Inputs(0U, 1U);
761 INST(6U, Opcode::IfImm).CC(CC_NE).Inputs(5U).Imm(0U);
762 }
763 BASIC_BLOCK(3U, 5U)
764 {
765 INST(20U, Opcode::SaveState).NoVregs();
766 INST(7U, Opcode::CallStatic).v0id().InputsAutoType(20U);
767 INST(8U, Opcode::SaveStateDeoptimize).Inputs(0U, 1U).SrcVregs({0U, 1U});
768 INST(9U, Opcode::IsMustDeoptimize).b();
769 INST(10U, Opcode::DeoptimizeIf).Inputs(9U, 8U);
770 }
771 BASIC_BLOCK(4U, 5U)
772 {
773 INST(21U, Opcode::SaveState).NoVregs();
774 INST(11U, Opcode::CallStatic).v0id().InputsAutoType(21U);
775 INST(12U, Opcode::SaveStateDeoptimize).Inputs(0U, 1U).SrcVregs({0U, 1U});
776 INST(13U, Opcode::IsMustDeoptimize).b();
777 INST(14U, Opcode::DeoptimizeIf).Inputs(13U, 12U);
778 }
779 BASIC_BLOCK(5U, 1U)
780 {
781 INST(15U, Opcode::SaveStateDeoptimize).Inputs(0U, 1U).SrcVregs({0U, 1U});
782 INST(16U, Opcode::IsMustDeoptimize).b();
783 INST(17U, Opcode::DeoptimizeIf).Inputs(16U, 15U);
784 INST(18U, Opcode::ReturnVoid).v0id();
785 }
786 }
787 }
788
OUT_GRAPH(ChaGuardIfWithGuardsTest,Graph * graph)789 OUT_GRAPH(ChaGuardIfWithGuardsTest, Graph *graph)
790 {
791 GRAPH(graph)
792 {
793 PARAMETER(0U, 0U).s32();
794 PARAMETER(1U, 1U).s32();
795 BASIC_BLOCK(2U, 3U, 4U)
796 {
797 INST(5U, Opcode::Compare).b().CC(CC_EQ).Inputs(0U, 1U);
798 INST(6U, Opcode::IfImm).CC(CC_NE).Inputs(5U).Imm(0U);
799 }
800 BASIC_BLOCK(3U, 5U)
801 {
802 INST(20U, Opcode::SaveState).NoVregs();
803 INST(7U, Opcode::CallStatic).v0id().InputsAutoType(20U);
804 INST(8U, Opcode::SaveStateDeoptimize).Inputs(0U, 1U).SrcVregs({0U, 1U});
805 INST(9U, Opcode::IsMustDeoptimize).b();
806 INST(10U, Opcode::DeoptimizeIf).Inputs(9U, 8U);
807 }
808 BASIC_BLOCK(4U, 5U)
809 {
810 INST(21U, Opcode::SaveState).NoVregs();
811 INST(11U, Opcode::CallStatic).v0id().InputsAutoType(21U);
812 INST(12U, Opcode::SaveStateDeoptimize).Inputs(0U, 1U).SrcVregs({0U, 1U});
813 INST(13U, Opcode::IsMustDeoptimize).b();
814 INST(14U, Opcode::DeoptimizeIf).Inputs(13U, 12U);
815 }
816 BASIC_BLOCK(5U, 1U)
817 {
818 INST(15U, Opcode::NOP);
819 INST(16U, Opcode::NOP);
820 INST(17U, Opcode::NOP);
821 INST(18U, Opcode::ReturnVoid).v0id();
822 }
823 }
824 }
825
TEST_F(DeoptimizeEliminationTest,ChaGuardIfWithGuardsTest)826 TEST_F(DeoptimizeEliminationTest, ChaGuardIfWithGuardsTest)
827 {
828 /*
829 * some code
830 * / \
831 * call call
832 * guard guard
833 * \ /
834 * \ /
835 * guard(*, deleted)
836 */
837 src_graph::ChaGuardIfWithGuardsTest::CREATE(GetGraph());
838 ASSERT_TRUE(GetGraph()->RunPass<DeoptimizeElimination>());
839 auto graph = CreateEmptyGraph();
840 out_graph::ChaGuardIfWithGuardsTest::CREATE(graph);
841 ASSERT_TRUE(GraphComparator().Compare(GetGraph(), graph));
842 }
843
TEST_F(DeoptimizeEliminationTest,ReplaceByDeoptimizeTest)844 TEST_F(DeoptimizeEliminationTest, ReplaceByDeoptimizeTest)
845 {
846 GRAPH(GetGraph())
847 {
848 CONSTANT(0U, 1U);
849 BASIC_BLOCK(2U, 1U)
850 {
851 INST(2U, Opcode::SaveState).Inputs(0U).SrcVregs({0U});
852 INST(3U, Opcode::DeoptimizeIf).Inputs(0U, 2U);
853 INST(4U, Opcode::ReturnVoid).v0id();
854 }
855 }
856 ASSERT_TRUE(GetGraph()->RunPass<DeoptimizeElimination>());
857 auto graph = CreateEmptyGraph();
858 GRAPH(graph)
859 {
860 CONSTANT(0U, 1U);
861 BASIC_BLOCK(2U, 1U)
862 {
863 INST(2U, Opcode::SaveState).Inputs(0U).SrcVregs({0U});
864 INST(3U, Opcode::Deoptimize).DeoptimizeType(DeoptimizeType::INVALID).Inputs(2U);
865 }
866 }
867 ASSERT_TRUE(GraphComparator().Compare(GetGraph(), graph));
868 }
869
TEST_F(DeoptimizeEliminationTest,ReplaceByDeoptimizeInliningTest)870 TEST_F(DeoptimizeEliminationTest, ReplaceByDeoptimizeInliningTest)
871 {
872 GRAPH(GetGraph())
873 {
874 CONSTANT(0U, 1U);
875 BASIC_BLOCK(2U, 1U)
876 {
877 INST(2U, Opcode::SaveState).Inputs(0U).SrcVregs({0U});
878 INST(3U, Opcode::CallStatic).v0id().InputsAutoType(2U).Inlined();
879 INST(4U, Opcode::SaveState).Inputs(0U).SrcVregs({0U});
880 INST(7U, Opcode::DeoptimizeIf).Inputs(0U, 4U);
881 INST(5U, Opcode::ReturnInlined).Inputs(2U);
882 INST(6U, Opcode::ReturnVoid).v0id();
883 }
884 }
885 INS(4U).CastToSaveState()->SetCallerInst(static_cast<CallInst *>(&INS(3U)));
886 INS(4U).CastToSaveState()->SetInliningDepth(1U);
887
888 ASSERT_TRUE(GetGraph()->RunPass<DeoptimizeElimination>());
889
890 auto graph = CreateEmptyGraph();
891 GRAPH(graph)
892 {
893 CONSTANT(0U, 1U);
894 BASIC_BLOCK(2U, 1U)
895 {
896 INST(2U, Opcode::SaveState).Inputs(0U).SrcVregs({0U});
897 INST(3U, Opcode::CallStatic).v0id().InputsAutoType(2U).Inlined();
898 INST(4U, Opcode::SaveState).Inputs(0U).SrcVregs({0U});
899 INST(5U, Opcode::ReturnInlined).Inputs(2U);
900 INST(8U, Opcode::Deoptimize).Inputs(4U);
901 }
902 }
903 ASSERT_TRUE(GraphComparator().Compare(GetGraph(), graph));
904 }
905
906 // NOLINTEND(readability-magic-numbers)
907
908 } // namespace ark::compiler
909