• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /**
2  * Copyright (c) 2021-2022 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/code_generator/codegen.h"
19 #include "optimizer/optimizations/cleanup.h"
20 #include "optimizer/optimizations/lowering.h"
21 #include "optimizer/optimizations/regalloc/reg_alloc.h"
22 
23 namespace panda::compiler {
24 class LoweringTest : public GraphTest {
25 public:
LoweringTest()26     LoweringTest()
27     {
28 #ifndef NDEBUG
29         graph_->SetLowLevelInstructionsEnabled();
30 #endif
31     }
32     template <class T>
ReturnTest(T val,DataType::Type type)33     void ReturnTest(T val, DataType::Type type)
34     {
35         auto graph = CreateGraphStartEndBlocks();
36 #ifndef NDEBUG
37         graph->SetLowLevelInstructionsEnabled();
38 #endif
39 
40         auto cnst = graph->FindOrCreateConstant(val);
41         auto block = graph->CreateEmptyBlock();
42         graph->GetStartBlock()->AddSucc(block);
43         block->AddSucc(graph->GetEndBlock());
44         auto ret = graph->CreateInstReturn();
45         ret->SetType(type);
46         ret->SetInput(0, cnst);
47         block->AppendInst(ret);
48         graph->RunPass<LoopAnalyzer>();
49         GraphChecker(graph).Check();
50 
51         graph->RunPass<Lowering>();
52         EXPECT_FALSE(cnst->HasUsers());
53         GraphChecker(graph).Check();
54 #ifndef NDEBUG
55         graph->SetRegAllocApplied();
56 #endif
57         EXPECT_TRUE(graph->RunPass<Codegen>());
58     }
59 
CreateEmptyLowLevelGraph()60     Graph *CreateEmptyLowLevelGraph()
61     {
62         auto graph = CreateEmptyGraph();
63 #ifndef NDEBUG
64         graph->SetLowLevelInstructionsEnabled();
65 #endif
66         return graph;
67     }
68 };
69 
TEST_F(LoweringTest,LoweringAddSub)70 TEST_F(LoweringTest, LoweringAddSub)
71 {
72     GRAPH(GetGraph())
73     {
74         PARAMETER(0, 0).u64();
75         PARAMETER(11, 1).f64();
76         PARAMETER(12, 2).f32();
77         CONSTANT(1, 12);
78         CONSTANT(2, -1);
79         CONSTANT(3, 100000000);
80         CONSTANT(21, 1.2);
81         CONSTANT(22, 0.5f);
82 
83         BASIC_BLOCK(2, -1)
84         {
85             INST(4, Opcode::Add).u64().Inputs(0, 1);
86             INST(5, Opcode::Add).u64().Inputs(0, 2);
87             INST(6, Opcode::Add).u64().Inputs(0, 3);
88             INST(7, Opcode::Sub).u64().Inputs(0, 1);
89             INST(8, Opcode::Sub).u64().Inputs(0, 2);
90             INST(9, Opcode::Sub).u64().Inputs(0, 3);
91             INST(13, Opcode::Add).f64().Inputs(11, 21);
92             INST(14, Opcode::Sub).f64().Inputs(11, 21);
93             INST(15, Opcode::Add).f32().Inputs(12, 22);
94             INST(16, Opcode::Sub).f32().Inputs(12, 22);
95             INST(17, Opcode::Add).u64().Inputs(0, 0);
96             INST(18, Opcode::Sub).u64().Inputs(0, 0);
97             INST(19, Opcode::Add).u16().Inputs(0, 1);
98             INST(20, Opcode::Add).u16().Inputs(0, 2);
99             INST(10, Opcode::SafePoint)
100                 .Inputs(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22)
101                 .SrcVregs({0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21});
102             INST(23, Opcode::ReturnVoid);
103         }
104     }
105     GetGraph()->RunPass<Lowering>();
106     ASSERT_FALSE(INS(4).HasUsers());
107     ASSERT_FALSE(INS(5).HasUsers());
108     ASSERT_TRUE(INS(6).HasUsers());
109     ASSERT_FALSE(INS(7).HasUsers());
110     ASSERT_FALSE(INS(8).HasUsers());
111     ASSERT_TRUE(INS(9).HasUsers());
112     ASSERT_TRUE(INS(13).HasUsers());
113     ASSERT_TRUE(INS(14).HasUsers());
114     ASSERT_TRUE(INS(15).HasUsers());
115     ASSERT_TRUE(INS(16).HasUsers());
116     ASSERT_TRUE(INS(17).HasUsers());
117     ASSERT_TRUE(INS(18).HasUsers());
118     ASSERT_TRUE(INS(19).HasUsers());
119     ASSERT_TRUE(INS(20).HasUsers());
120     ASSERT_EQ(INS(4).GetPrev()->GetOpcode(), Opcode::AddI);
121     ASSERT_EQ(INS(5).GetPrev()->GetOpcode(), Opcode::SubI);
122     ASSERT_EQ(INS(6).GetPrev()->GetOpcode(), Opcode::Add);
123     ASSERT_EQ(INS(7).GetPrev()->GetOpcode(), Opcode::SubI);
124     ASSERT_EQ(INS(8).GetPrev()->GetOpcode(), Opcode::AddI);
125     ASSERT_EQ(INS(9).GetPrev()->GetOpcode(), Opcode::Sub);
126 }
127 
TEST_F(LoweringTest,AddSubCornerCase)128 TEST_F(LoweringTest, AddSubCornerCase)
129 {
130     auto graph = CreateEmptyBytecodeGraph();
131     constexpr int MIN = std::numeric_limits<int8_t>::min();
132     ASSERT_TRUE(MIN < 0);
133     GRAPH(graph)
134     {
135         PARAMETER(0, 0).s32();
136         CONSTANT(2, MIN).s32();
137 
138         BASIC_BLOCK(2, -1)
139         {
140             INST(3, Opcode::Add).s32().Inputs(0, 2);
141             INST(4, Opcode::Sub).s32().Inputs(0, 2);
142             INST(20, Opcode::SaveState).NoVregs();
143             INST(5, Opcode::CallStatic).b().InputsAutoType(3, 4, 20);
144             INST(6, Opcode::Return).b().Inputs(5);
145         }
146     }
147 #ifndef NDEBUG
148     graph->SetLowLevelInstructionsEnabled();
149 #endif
150     graph->RunPass<compiler::Lowering>();
151     graph->RunPass<compiler::Cleanup>();
152     auto expected = CreateEmptyBytecodeGraph();
153     GRAPH(expected)
154     {
155         PARAMETER(0, 0).s32();
156 
157         BASIC_BLOCK(2, -1)
158         {
159             // As MIN = -128 and -MIN cannot be encoded with 8-bit, the opcodes will not be reversed.
160             INST(7, Opcode::AddI).s32().Inputs(0).Imm(MIN);
161             INST(8, Opcode::SubI).s32().Inputs(0).Imm(MIN);
162             INST(20, Opcode::SaveState).NoVregs();
163             INST(5, Opcode::CallStatic).b().InputsAutoType(7, 8, 20);
164             INST(6, Opcode::Return).b().Inputs(5);
165         }
166     }
167     EXPECT_TRUE(GraphComparator().Compare(graph, expected));
168 }
169 
TEST_F(LoweringTest,LoweringLogic)170 TEST_F(LoweringTest, LoweringLogic)
171 {
172     GRAPH(GetGraph())
173     {
174         PARAMETER(0, 0).u64();
175         CONSTANT(1, 12);
176         CONSTANT(2, 50);
177 
178         BASIC_BLOCK(2, -1)
179         {
180             INST(3, Opcode::Or).u32().Inputs(0, 1);
181             INST(4, Opcode::Or).u64().Inputs(0, 1);
182             INST(5, Opcode::Or).u64().Inputs(0, 2);
183             INST(6, Opcode::And).u32().Inputs(0, 1);
184             INST(7, Opcode::And).u64().Inputs(0, 1);
185             INST(8, Opcode::And).u64().Inputs(0, 2);
186             INST(9, Opcode::Xor).u32().Inputs(0, 1);
187             INST(10, Opcode::Xor).u64().Inputs(0, 1);
188             INST(11, Opcode::Xor).u64().Inputs(0, 2);
189             INST(12, Opcode::Or).u8().Inputs(0, 1);
190             INST(13, Opcode::And).u64().Inputs(0, 0);
191             INST(14, Opcode::SafePoint)
192                 .Inputs(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13)
193                 .SrcVregs({0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13});
194             INST(23, Opcode::ReturnVoid);
195         }
196     }
197     GetGraph()->RunPass<Lowering>();
198     if (GetGraph()->GetArch() != Arch::AARCH32) {
199         ASSERT_FALSE(INS(3).HasUsers());
200         ASSERT_FALSE(INS(4).HasUsers());
201         ASSERT_TRUE(INS(5).HasUsers());
202         ASSERT_FALSE(INS(6).HasUsers());
203         ASSERT_FALSE(INS(7).HasUsers());
204         ASSERT_TRUE(INS(8).HasUsers());
205         ASSERT_FALSE(INS(9).HasUsers());
206         ASSERT_FALSE(INS(10).HasUsers());
207         ASSERT_TRUE(INS(11).HasUsers());
208         ASSERT_TRUE(INS(12).HasUsers());
209         ASSERT_TRUE(INS(13).HasUsers());
210         ASSERT_EQ(INS(3).GetPrev()->GetOpcode(), Opcode::OrI);
211         ASSERT_EQ(INS(4).GetPrev()->GetOpcode(), Opcode::OrI);
212         ASSERT_EQ(INS(5).GetPrev()->GetOpcode(), Opcode::Or);
213         ASSERT_EQ(INS(6).GetPrev()->GetOpcode(), Opcode::AndI);
214         ASSERT_EQ(INS(7).GetPrev()->GetOpcode(), Opcode::AndI);
215         ASSERT_EQ(INS(8).GetPrev()->GetOpcode(), Opcode::And);
216         ASSERT_EQ(INS(9).GetPrev()->GetOpcode(), Opcode::XorI);
217         ASSERT_EQ(INS(10).GetPrev()->GetOpcode(), Opcode::XorI);
218         ASSERT_EQ(INS(11).GetPrev()->GetOpcode(), Opcode::Xor);
219         return;
220     }
221     // Then check graph only for arm32
222     ASSERT_FALSE(INS(3).HasUsers());
223     ASSERT_FALSE(INS(4).HasUsers());
224     ASSERT_FALSE(INS(5).HasUsers());
225     ASSERT_FALSE(INS(6).HasUsers());
226     ASSERT_FALSE(INS(7).HasUsers());
227     ASSERT_FALSE(INS(8).HasUsers());
228     ASSERT_FALSE(INS(9).HasUsers());
229     ASSERT_FALSE(INS(10).HasUsers());
230     ASSERT_FALSE(INS(11).HasUsers());
231     ASSERT_TRUE(INS(12).HasUsers());
232     ASSERT_TRUE(INS(13).HasUsers());
233     ASSERT_EQ(INS(3).GetPrev()->GetOpcode(), Opcode::OrI);
234     ASSERT_EQ(INS(4).GetPrev()->GetOpcode(), Opcode::OrI);
235     ASSERT_EQ(INS(5).GetPrev()->GetOpcode(), Opcode::OrI);
236     ASSERT_EQ(INS(6).GetPrev()->GetOpcode(), Opcode::AndI);
237     ASSERT_EQ(INS(7).GetPrev()->GetOpcode(), Opcode::AndI);
238     ASSERT_EQ(INS(8).GetPrev()->GetOpcode(), Opcode::AndI);
239     ASSERT_EQ(INS(9).GetPrev()->GetOpcode(), Opcode::XorI);
240     ASSERT_EQ(INS(10).GetPrev()->GetOpcode(), Opcode::XorI);
241     ASSERT_EQ(INS(11).GetPrev()->GetOpcode(), Opcode::XorI);
242 }
243 
TEST_F(LoweringTest,LoweringShift)244 TEST_F(LoweringTest, LoweringShift)
245 {
246     GRAPH(GetGraph())
247     {
248         PARAMETER(0, 0).u64();
249         CONSTANT(1, 12);
250         CONSTANT(2, 64);
251 
252         BASIC_BLOCK(2, -1)
253         {
254             INST(3, Opcode::Shr).u32().Inputs(0, 1);
255             INST(4, Opcode::Shr).u64().Inputs(0, 1);
256             INST(5, Opcode::Shr).u64().Inputs(0, 2);
257             INST(6, Opcode::AShr).u32().Inputs(0, 1);
258             INST(7, Opcode::AShr).u64().Inputs(0, 1);
259             INST(8, Opcode::AShr).u64().Inputs(0, 2);
260             INST(9, Opcode::Shl).u32().Inputs(0, 1);
261             INST(10, Opcode::Shl).u64().Inputs(0, 1);
262             INST(11, Opcode::Shl).u64().Inputs(0, 2);
263             INST(12, Opcode::Shl).u8().Inputs(0, 1);
264             INST(13, Opcode::Shr).u64().Inputs(0, 0);
265             INST(14, Opcode::SafePoint)
266                 .Inputs(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13)
267                 .SrcVregs({0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13});
268             INST(23, Opcode::ReturnVoid);
269         }
270     }
271     GetGraph()->RunPass<Lowering>();
272     ASSERT_FALSE(INS(3).HasUsers());
273     ASSERT_FALSE(INS(4).HasUsers());
274     ASSERT_TRUE(INS(5).HasUsers());
275     ASSERT_FALSE(INS(6).HasUsers());
276     ASSERT_FALSE(INS(7).HasUsers());
277     ASSERT_TRUE(INS(8).HasUsers());
278     ASSERT_FALSE(INS(9).HasUsers());
279     ASSERT_FALSE(INS(10).HasUsers());
280     ASSERT_TRUE(INS(11).HasUsers());
281     ASSERT_TRUE(INS(12).HasUsers());
282     ASSERT_TRUE(INS(13).HasUsers());
283     ASSERT_EQ(INS(3).GetPrev()->GetOpcode(), Opcode::ShrI);
284     ASSERT_EQ(INS(4).GetPrev()->GetOpcode(), Opcode::ShrI);
285     ASSERT_EQ(INS(5).GetPrev()->GetOpcode(), Opcode::Shr);
286     ASSERT_EQ(INS(6).GetPrev()->GetOpcode(), Opcode::AShrI);
287     ASSERT_EQ(INS(7).GetPrev()->GetOpcode(), Opcode::AShrI);
288     ASSERT_EQ(INS(8).GetPrev()->GetOpcode(), Opcode::AShr);
289     ASSERT_EQ(INS(9).GetPrev()->GetOpcode(), Opcode::ShlI);
290     ASSERT_EQ(INS(10).GetPrev()->GetOpcode(), Opcode::ShlI);
291     ASSERT_EQ(INS(11).GetPrev()->GetOpcode(), Opcode::Shl);
292 }
293 
TEST_F(LoweringTest,SaveStateTest)294 TEST_F(LoweringTest, SaveStateTest)
295 {
296     if (GetGraph()->GetArch() == Arch::AARCH32) {
297         GTEST_SKIP() << "The optimization for float isn't supported on Aarch32";
298     }
299     GRAPH(GetGraph())
300     {
301         PARAMETER(0, 0).u64();
302         CONSTANT(1, 0);
303         CONSTANT(2, 1);
304         CONSTANT(3, 50);
305         CONSTANT(4, 0.5);
306         PARAMETER(5, 1).u64();
307 
308         BASIC_BLOCK(2, -1)
309         {
310             INST(8, Opcode::SaveState).Inputs(0, 1, 2, 3, 4, 5).SrcVregs({10, 11, 12, 13, 14, 15});
311             INST(11, Opcode::SafePoint).Inputs(0, 1, 2, 3, 4, 5).SrcVregs({10, 11, 12, 13, 14, 15});
312             INST(9, Opcode::CallStatic).u64().InputsAutoType(0, 1, 8);
313             INST(10, Opcode::Return).u64().Inputs(9);
314         }
315     }
316 
317     GetGraph()->RunPass<Lowering>();
318     GraphChecker(GetGraph()).Check();
319     EXPECT_TRUE(INS(0).HasUsers());
320     EXPECT_TRUE(INS(1).HasUsers());
321     EXPECT_FALSE(INS(2).HasUsers());
322     EXPECT_FALSE(INS(3).HasUsers());
323     EXPECT_FALSE(INS(4).HasUsers());
324     EXPECT_TRUE(INS(5).HasUsers());
325 
326     auto save_state = INS(8).CastToSaveState();
327     auto safe_point = INS(11).CastToSafePoint();
328 
329     EXPECT_EQ(save_state->GetInputsCount(), 2U);
330     EXPECT_EQ(save_state->GetImmediatesCount(), 4U);
331     EXPECT_EQ((*save_state->GetImmediates())[0].value, (bit_cast<uint64_t, uint64_t>(0)));
332     EXPECT_EQ((*save_state->GetImmediates())[1].value, (bit_cast<uint64_t, uint64_t>(1)));
333     EXPECT_EQ((*save_state->GetImmediates())[2].value, (bit_cast<uint64_t, double>(0.5)));
334     EXPECT_EQ((*save_state->GetImmediates())[3].value, (bit_cast<uint64_t, uint64_t>(50)));
335     EXPECT_EQ(save_state->GetInput(0).GetInst(), &INS(0));
336     EXPECT_EQ(save_state->GetInput(1).GetInst(), &INS(5));
337     EXPECT_EQ(save_state->GetVirtualRegister(0).Value(), 10);
338     EXPECT_EQ(save_state->GetVirtualRegister(1).Value(), 15);
339 
340     EXPECT_EQ(safe_point->GetInputsCount(), 2U);
341     EXPECT_EQ(safe_point->GetImmediatesCount(), 4U);
342     EXPECT_EQ(safe_point->GetInput(0).GetInst(), &INS(0));
343     EXPECT_EQ(safe_point->GetInput(1).GetInst(), &INS(5));
344     EXPECT_EQ(safe_point->GetVirtualRegister(0).Value(), 10);
345     EXPECT_EQ(safe_point->GetVirtualRegister(1).Value(), 15);
346 
347     GetGraph()->RunPass<LoopAnalyzer>();
348     RegAlloc(GetGraph());
349     SetNumVirtRegs(GetGraph()->GetVRegsCount());
350     EXPECT_TRUE(GetGraph()->RunPass<Codegen>());
351 }
352 
TEST_F(LoweringTest,BoundCheck)353 TEST_F(LoweringTest, BoundCheck)
354 {
355     GRAPH(GetGraph())
356     {
357         PARAMETER(0, 0).ref();  // array
358         CONSTANT(1, 10);        // index
359         BASIC_BLOCK(2, 3)
360         {
361             INST(2, Opcode::SaveState).Inputs(0, 1).SrcVregs({0, 1});
362             INST(3, Opcode::NullCheck).ref().Inputs(0, 2);
363             INST(4, Opcode::LenArray).s32().Inputs(3);
364             INST(5, Opcode::BoundsCheck).s32().Inputs(4, 1, 2);
365             INST(6, Opcode::LoadArray).u64().Inputs(3, 5);
366             INST(7, Opcode::Add).u64().Inputs(6, 6);
367             INST(8, Opcode::StoreArray).u64().Inputs(3, 5, 7);
368         }
369         BASIC_BLOCK(3, -1)
370         {
371             INST(10, Opcode::Add).u64().Inputs(7, 7);
372             INST(13, Opcode::SaveState).Inputs(0, 10).SrcVregs({0, 1});
373             INST(11, Opcode::CallStatic).u64().InputsAutoType(0, 10, 13);
374             INST(12, Opcode::Return).u64().Inputs(11);
375         }
376     }
377 
378     GetGraph()->RunPass<Lowering>();
379     EXPECT_TRUE(INS(0).HasUsers());
380     EXPECT_FALSE(INS(1).HasUsers());
381     EXPECT_TRUE(INS(2).HasUsers());
382     EXPECT_TRUE(INS(3).HasUsers());
383     EXPECT_TRUE(INS(4).HasUsers());
384     EXPECT_FALSE(INS(5).HasUsers());
385     EXPECT_FALSE(INS(6).HasUsers());
386     EXPECT_TRUE(INS(7).HasUsers());
387     EXPECT_FALSE(INS(8).HasUsers());
388     GraphChecker(GetGraph()).Check();
389     // Run codegen
390     GetGraph()->RunPass<LoopAnalyzer>();
391     RegAlloc(GetGraph());
392     SetNumVirtRegs(GetGraph()->GetVRegsCount());
393     EXPECT_TRUE(GetGraph()->RunPass<Codegen>());
394 }
395 
TEST_F(LoweringTest,LoadStoreArray)396 TEST_F(LoweringTest, LoadStoreArray)
397 {
398     GRAPH(GetGraph())
399     {
400         PARAMETER(0, 0).ref();  // array
401         CONSTANT(1, 10);        // index
402         BASIC_BLOCK(2, 3)
403         {
404             INST(2, Opcode::SaveState).Inputs(0, 1).SrcVregs({0, 1});
405             INST(3, Opcode::LoadArray).u64().Inputs(0, 1);
406             INST(4, Opcode::Add).u64().Inputs(3, 3);
407             INST(5, Opcode::StoreArray).u64().Inputs(0, 1, 4);
408         }
409         BASIC_BLOCK(3, -1)
410         {
411             INST(9, Opcode::SaveState).Inputs(0, 4).SrcVregs({0, 1});
412             INST(7, Opcode::CallStatic).u64().InputsAutoType(0, 4, 9);
413             INST(8, Opcode::Return).u64().Inputs(7);
414         }
415     }
416 
417     GetGraph()->RunPass<Lowering>();
418     EXPECT_TRUE(INS(0).HasUsers());
419     EXPECT_FALSE(INS(1).HasUsers());
420     EXPECT_FALSE(INS(2).HasUsers());
421     EXPECT_FALSE(INS(3).HasUsers());
422     EXPECT_TRUE(INS(4).HasUsers());
423     EXPECT_FALSE(INS(5).HasUsers());
424     GraphChecker(GetGraph()).Check();
425     // Run codegen
426     GetGraph()->RunPass<LoopAnalyzer>();
427     RegAlloc(GetGraph());
428     SetNumVirtRegs(GetGraph()->GetVRegsCount());
429     EXPECT_TRUE(GetGraph()->RunPass<Codegen>());
430 }
431 
TEST_F(LoweringTest,Return)432 TEST_F(LoweringTest, Return)
433 {
434     ReturnTest<int64_t>(10, DataType::INT64);
435     ReturnTest<float>(10.0F, DataType::FLOAT32);
436     ReturnTest<double>(10.0, DataType::FLOAT64);
437     ReturnTest<int64_t>(0, DataType::INT64);
438     ReturnTest<float>(0.0F, DataType::FLOAT32);
439     ReturnTest<double>(0.0, DataType::FLOAT64);
440 }
441 
TEST_F(LoweringTest,If)442 TEST_F(LoweringTest, If)
443 {
444     for (int ccint = ConditionCode::CC_FIRST; ccint <= ConditionCode::CC_LAST; ccint++) {
445         ConditionCode cc = static_cast<ConditionCode>(ccint);
446         auto graph = CreateEmptyLowLevelGraph();
447         GRAPH(graph)
448         {
449             PARAMETER(0, 0).u64();
450             PARAMETER(1, 1).u64();
451             CONSTANT(2, 0);
452             CONSTANT(3, 1);
453             BASIC_BLOCK(2, 3, 4)
454             {
455                 INST(4, Opcode::Compare).b().CC(cc).Inputs(0, 1);
456                 INST(5, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0).Inputs(4);
457             }
458             BASIC_BLOCK(3, -1)
459             {
460                 INST(6, Opcode::Return).b().Inputs(3);
461             }
462             BASIC_BLOCK(4, -1)
463             {
464                 INST(7, Opcode::Return).b().Inputs(2);
465             }
466         }
467 
468         EXPECT_TRUE(graph->RunPass<Lowering>());
469         EXPECT_TRUE(graph->RunPass<Cleanup>());
470 
471         auto graph_if = CreateEmptyGraph();
472         GRAPH(graph_if)
473         {
474             PARAMETER(0, 0).u64();
475             PARAMETER(1, 1).u64();
476             BASIC_BLOCK(2, 3, 4)
477             {
478                 INST(2, Opcode::If).SrcType(DataType::UINT64).CC(cc).Inputs(0, 1);
479             }
480             BASIC_BLOCK(3, -1)
481             {
482                 INST(3, Opcode::ReturnI).b().Imm(1);
483             }
484             BASIC_BLOCK(4, -1)
485             {
486                 INST(4, Opcode::ReturnI).b().Imm(0);
487             }
488         }
489         ASSERT_TRUE(GraphComparator().Compare(graph, graph_if));
490     }
491 }
492 
TEST_F(LoweringTest,If1)493 TEST_F(LoweringTest, If1)
494 {
495     // Applied
496     // Compare have several IfImm users.
497     for (int ccint = ConditionCode::CC_FIRST; ccint <= ConditionCode::CC_LAST; ccint++) {
498         ConditionCode cc = static_cast<ConditionCode>(ccint);
499         auto graph = CreateEmptyLowLevelGraph();
500         GRAPH(graph)
501         {
502             PARAMETER(0, 0).u64();
503             PARAMETER(1, 1).u64();
504             CONSTANT(2, 0);
505             CONSTANT(3, 1);
506             CONSTANT(10, 2);
507             BASIC_BLOCK(2, 3, 4)
508             {
509                 INST(4, Opcode::Compare).b().CC(cc).Inputs(0, 1);
510                 INST(5, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0).Inputs(4);
511             }
512             BASIC_BLOCK(3, 5, 6)
513             {
514                 INST(6, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0).Inputs(4);
515             }
516             BASIC_BLOCK(4, -1)
517             {
518                 INST(7, Opcode::Return).s32().Inputs(2);
519             }
520             BASIC_BLOCK(5, -1)
521             {
522                 INST(8, Opcode::Return).s32().Inputs(3);
523             }
524             BASIC_BLOCK(6, -1)
525             {
526                 INST(9, Opcode::Return).s32().Inputs(10);
527             }
528         }
529 
530         EXPECT_TRUE(graph->RunPass<Lowering>());
531         EXPECT_TRUE(graph->RunPass<Cleanup>());
532 
533         auto graph_if = CreateEmptyGraph();
534         GRAPH(graph_if)
535         {
536             PARAMETER(0, 0).u64();
537             PARAMETER(1, 1).u64();
538             BASIC_BLOCK(2, 3, 4)
539             {
540                 INST(2, Opcode::If).SrcType(DataType::UINT64).CC(cc).Inputs(0, 1);
541             }
542             BASIC_BLOCK(3, 5, 6)
543             {
544                 INST(3, Opcode::If).SrcType(DataType::UINT64).CC(cc).Inputs(0, 1);
545             }
546             BASIC_BLOCK(4, -1)
547             {
548                 INST(4, Opcode::ReturnI).s32().Imm(0);
549             }
550             BASIC_BLOCK(5, -1)
551             {
552                 INST(5, Opcode::ReturnI).s32().Imm(1);
553             }
554             BASIC_BLOCK(6, -1)
555             {
556                 INST(6, Opcode::ReturnI).s32().Imm(2);
557             }
558         }
559         ASSERT_TRUE(GraphComparator().Compare(graph, graph_if));
560     }
561 }
562 
TEST_F(LoweringTest,If2)563 TEST_F(LoweringTest, If2)
564 {
565     // Not applied
566     // Compare have several users, not only IfImm.
567     for (int ccint = ConditionCode::CC_FIRST; ccint <= ConditionCode::CC_LAST; ccint++) {
568         ConditionCode cc = static_cast<ConditionCode>(ccint);
569         auto graph = CreateEmptyLowLevelGraph();
570         GRAPH(graph)
571         {
572             PARAMETER(0, 0).u64();
573             PARAMETER(1, 1).u64();
574             CONSTANT(2, 0);
575             CONSTANT(3, 1);
576             CONSTANT(10, 2);
577             BASIC_BLOCK(2, 3, 4)
578             {
579                 INST(4, Opcode::Compare).b().CC(cc).Inputs(0, 1);
580                 INST(5, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0).Inputs(4);
581             }
582             BASIC_BLOCK(3, 5, 6)
583             {
584                 INST(6, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0).Inputs(4);
585             }
586             BASIC_BLOCK(4, -1)
587             {
588                 INST(20, Opcode::SaveState).NoVregs();
589                 INST(11, Opcode::CallStatic).b().InputsAutoType(4, 20);
590                 INST(7, Opcode::Return).s32().Inputs(2);
591             }
592             BASIC_BLOCK(5, -1)
593             {
594                 INST(8, Opcode::Return).s32().Inputs(3);
595             }
596             BASIC_BLOCK(6, -1)
597             {
598                 INST(9, Opcode::Return).s32().Inputs(10);
599             }
600         }
601 
602         EXPECT_TRUE(graph->RunPass<Lowering>());
603         EXPECT_TRUE(graph->RunPass<Cleanup>());
604 
605         auto graph_if = CreateEmptyGraph();
606         GRAPH(graph_if)
607         {
608             PARAMETER(0, 0).u64();
609             PARAMETER(1, 1).u64();
610             BASIC_BLOCK(2, 3, 4)
611             {
612                 INST(4, Opcode::Compare).b().CC(cc).Inputs(0, 1);
613                 INST(5, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0).Inputs(4);
614             }
615             BASIC_BLOCK(3, 5, 6)
616             {
617                 INST(6, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0).Inputs(4);
618             }
619             BASIC_BLOCK(4, -1)
620             {
621                 INST(20, Opcode::SaveState).NoVregs();
622                 INST(11, Opcode::CallStatic).b().InputsAutoType(4, 20);
623                 INST(7, Opcode::ReturnI).s32().Imm(0);
624             }
625             BASIC_BLOCK(5, -1)
626             {
627                 INST(8, Opcode::ReturnI).s32().Imm(1);
628             }
629             BASIC_BLOCK(6, -1)
630             {
631                 INST(9, Opcode::ReturnI).s32().Imm(2);
632             }
633         }
634         ASSERT_TRUE(GraphComparator().Compare(graph, graph_if));
635     }
636 }
637 
TEST_F(LoweringTest,If_Fcmpl)638 TEST_F(LoweringTest, If_Fcmpl)
639 {
640     for (int ccint = ConditionCode::CC_FIRST; ccint <= ConditionCode::CC_GE; ++ccint) {
641         ConditionCode cc = static_cast<ConditionCode>(ccint);
642         auto graph = CreateEmptyLowLevelGraph();
643         GRAPH(graph)
644         {
645             PARAMETER(0, 0).f64();
646             PARAMETER(1, 1).f64();
647             CONSTANT(2, 0);
648             CONSTANT(3, 1);
649 
650             BASIC_BLOCK(2, 3, 4)
651             {
652                 INST(4, Opcode::Cmp).s32().SrcType(DataType::FLOAT64).Fcmpg(false).Inputs(0, 1);
653                 INST(5, Opcode::Compare).b().CC(cc).Inputs(4, 2);
654                 INST(6, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0).Inputs(5);
655             }
656             BASIC_BLOCK(3, -1)
657             {
658                 INST(7, Opcode::Return).b().Inputs(3);
659             }
660             BASIC_BLOCK(4, -1)
661             {
662                 INST(8, Opcode::Return).b().Inputs(2);
663             }
664         }
665 
666         EXPECT_TRUE(graph->RunPass<Lowering>());
667         EXPECT_TRUE(graph->RunPass<Cleanup>());
668 
669         auto graph_if = CreateEmptyGraph();
670         GRAPH(graph_if)
671         {
672             PARAMETER(0, 0).f64();
673             PARAMETER(1, 1).f64();
674 
675             BASIC_BLOCK(2, 3, 4)
676             {
677                 INST(2, Opcode::If).SrcType(DataType::FLOAT64).CC(cc).Inputs(0, 1);
678             }
679             BASIC_BLOCK(3, -1)
680             {
681                 INST(3, Opcode::ReturnI).b().Imm(1);
682             }
683             BASIC_BLOCK(4, -1)
684             {
685                 INST(4, Opcode::ReturnI).b().Imm(0);
686             }
687         }
688 
689         ASSERT_TRUE(GraphComparator().Compare(graph, graph_if));
690     }
691 }
692 
TEST_F(LoweringTest,If_Fcmpl_NoJoin)693 TEST_F(LoweringTest, If_Fcmpl_NoJoin)
694 {
695     for (int ccint = ConditionCode::CC_FIRST; ccint <= ConditionCode::CC_GE; ++ccint) {
696         ConditionCode cc = static_cast<ConditionCode>(ccint);
697         auto graph = CreateEmptyLowLevelGraph();
698         GRAPH(graph)
699         {
700             PARAMETER(0, 0).f64();
701             PARAMETER(1, 1).f64();
702             CONSTANT(2, 0);
703             CONSTANT(3, 1);
704 
705             BASIC_BLOCK(2, 3, 4)
706             {
707                 INST(4, Opcode::Cmp).s32().SrcType(DataType::FLOAT64).Fcmpg(false).Inputs(0, 1);
708                 INST(5, Opcode::Compare).b().CC(cc).Inputs(4, 3);
709                 INST(6, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0).Inputs(5);
710             }
711             BASIC_BLOCK(3, -1)
712             {
713                 INST(7, Opcode::Return).b().Inputs(3);
714             }
715             BASIC_BLOCK(4, -1)
716             {
717                 INST(8, Opcode::Return).b().Inputs(2);
718             }
719         }
720 
721         EXPECT_TRUE(graph->RunPass<Lowering>());
722         EXPECT_TRUE(graph->RunPass<Cleanup>());
723 
724         auto graph_if = CreateEmptyGraph();
725         GRAPH(graph_if)
726         {
727             PARAMETER(0, 0).f64();
728             PARAMETER(1, 1).f64();
729 
730             BASIC_BLOCK(2, 3, 4)
731             {
732                 INST(4, Opcode::Cmp).s32().SrcType(DataType::FLOAT64).Fcmpg(false).Inputs(0, 1);
733                 INST(5, Opcode::IfImm).SrcType(DataType::INT32).CC(cc).Imm(1).Inputs(4);
734             }
735             BASIC_BLOCK(3, -1)
736             {
737                 INST(7, Opcode::ReturnI).b().Imm(1);
738             }
739             BASIC_BLOCK(4, -1)
740             {
741                 INST(8, Opcode::ReturnI).b().Imm(0);
742             }
743         }
744 
745         ASSERT_TRUE(GraphComparator().Compare(graph, graph_if));
746     }
747 }
748 
TEST_F(LoweringTest,If_Fcmpl_NoJoin2)749 TEST_F(LoweringTest, If_Fcmpl_NoJoin2)
750 {
751     for (int ccint = ConditionCode::CC_FIRST; ccint <= ConditionCode::CC_GE; ++ccint) {
752         ConditionCode cc = static_cast<ConditionCode>(ccint);
753         auto graph = CreateEmptyLowLevelGraph();
754         GRAPH(graph)
755         {
756             PARAMETER(0, 0).f64();
757             PARAMETER(1, 1).f64();
758             CONSTANT(2, 0);
759             CONSTANT(3, 1);
760 
761             BASIC_BLOCK(2, 3, 4)
762             {
763                 INST(4, Opcode::Cmp).s32().SrcType(DataType::FLOAT64).Fcmpg(false).Inputs(0, 1);
764                 INST(10, Opcode::Add).s32().Inputs(4, 3);
765                 INST(5, Opcode::Compare).b().CC(cc).Inputs(4, 2);
766                 INST(6, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0).Inputs(5);
767             }
768             BASIC_BLOCK(3, -1)
769             {
770                 INST(7, Opcode::Return).b().Inputs(3);
771             }
772             BASIC_BLOCK(4, -1)
773             {
774                 INST(8, Opcode::Return).b().Inputs(10);
775             }
776         }
777 
778         EXPECT_TRUE(graph->RunPass<Lowering>());
779         EXPECT_TRUE(graph->RunPass<Cleanup>());
780 
781         auto graph_if = CreateEmptyGraph();
782         GRAPH(graph_if)
783         {
784             PARAMETER(0, 0).f64();
785             PARAMETER(1, 1).f64();
786 
787             BASIC_BLOCK(2, 3, 4)
788             {
789                 INST(4, Opcode::Cmp).s32().SrcType(DataType::FLOAT64).Fcmpg(false).Inputs(0, 1);
790                 INST(10, Opcode::AddI).s32().Imm(1).Inputs(4);
791                 INST(5, Opcode::IfImm).SrcType(DataType::INT32).CC(cc).Imm(0).Inputs(4);
792             }
793             BASIC_BLOCK(3, -1)
794             {
795                 INST(7, Opcode::ReturnI).b().Imm(1);
796             }
797             BASIC_BLOCK(4, -1)
798             {
799                 INST(8, Opcode::Return).b().Inputs(10);
800             }
801         }
802 
803         ASSERT_TRUE(GraphComparator().Compare(graph, graph_if));
804     }
805 }
806 
TEST_F(LoweringTest,If_Fcmpg)807 TEST_F(LoweringTest, If_Fcmpg)
808 {
809     for (int ccint = ConditionCode::CC_FIRST; ccint <= ConditionCode::CC_GE; ++ccint) {
810         ConditionCode cc = static_cast<ConditionCode>(ccint);
811         auto graph = CreateEmptyLowLevelGraph();
812         GRAPH(graph)
813         {
814             PARAMETER(0, 0).f64();
815             PARAMETER(1, 1).f64();
816             CONSTANT(2, 0);
817             CONSTANT(3, 1);
818 
819             BASIC_BLOCK(2, 3, 4)
820             {
821                 INST(4, Opcode::Cmp).s32().SrcType(DataType::FLOAT64).Fcmpg(true).Inputs(0, 1);
822                 INST(5, Opcode::Compare).b().CC(cc).Inputs(4, 2);
823                 INST(6, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0).Inputs(5);
824             }
825             BASIC_BLOCK(3, -1)
826             {
827                 INST(7, Opcode::Return).b().Inputs(3);
828             }
829             BASIC_BLOCK(4, -1)
830             {
831                 INST(8, Opcode::Return).b().Inputs(2);
832             }
833         }
834 
835         EXPECT_TRUE(graph->RunPass<Lowering>());
836         EXPECT_TRUE(graph->RunPass<Cleanup>());
837 
838         auto graph_if = CreateEmptyGraph();
839         GRAPH(graph_if)
840         {
841             PARAMETER(0, 0).f64();
842             PARAMETER(1, 1).f64();
843 
844             BASIC_BLOCK(2, 3, 4)
845             {
846                 INST(2, Opcode::If).SrcType(DataType::FLOAT64).CC(InverseSignednessConditionCode(cc)).Inputs(0, 1);
847             }
848             BASIC_BLOCK(3, -1)
849             {
850                 INST(3, Opcode::ReturnI).b().Imm(1);
851             }
852             BASIC_BLOCK(4, -1)
853             {
854                 INST(4, Opcode::ReturnI).b().Imm(0);
855             }
856         }
857 
858         ASSERT_TRUE(GraphComparator().Compare(graph, graph_if));
859     }
860 }
861 
TEST_F(LoweringTest,MultiplyAddInteger)862 TEST_F(LoweringTest, MultiplyAddInteger)
863 {
864     if (GetGraph()->GetArch() != Arch::AARCH64) {
865         GTEST_SKIP() << "multiply-add instruction is only supported on Aarch64";
866     }
867 
868     std::initializer_list<std::array<DataType::Type, 4>> type_combinations = {
869         {DataType::UINT32, DataType::UINT32, DataType::UINT32, DataType::UINT32},
870         {DataType::INT32, DataType::INT32, DataType::INT32, DataType::INT32},
871         {DataType::INT32, DataType::INT16, DataType::INT8, DataType::INT16},
872         {DataType::UINT64, DataType::UINT64, DataType::UINT64, DataType::UINT64},
873         {DataType::INT64, DataType::INT64, DataType::INT64, DataType::INT64}};
874 
875     for (auto &types : type_combinations) {
876         auto type = types[0];
877         auto graph = CreateEmptyLowLevelGraph();
878         GRAPH(graph)
879         {
880             PARAMETER(0, 0).type(types[1]);
881             PARAMETER(1, 1).type(types[2]);
882             PARAMETER(2, 2).type(types[3]);
883 
884             BASIC_BLOCK(2, -1)
885             {
886                 // a * b + c
887                 INST(3, Opcode::Mul).type(type).Inputs(0, 1);
888                 INST(4, Opcode::Add).type(type).Inputs(3, 2);
889 
890                 // c + a * b
891                 INST(5, Opcode::Mul).type(type).Inputs(1, 2);
892                 INST(6, Opcode::Add).type(type).Inputs(0, 5);
893 
894                 // a * b + c, but a * b is reused
895                 INST(7, Opcode::Mul).type(type).Inputs(0, 1);
896                 INST(8, Opcode::Add).type(type).Inputs(7, 2);
897 
898                 INST(9, Opcode::Add).type(type).Inputs(4, 6);
899                 INST(10, Opcode::Add).type(type).Inputs(9, 8);
900                 INST(11, Opcode::Add).type(type).Inputs(10, 7);
901                 INST(12, Opcode::Return).type(type).Inputs(11);
902             }
903         }
904 
905         EXPECT_TRUE(graph->RunPass<Lowering>());
906         EXPECT_TRUE(graph->RunPass<Cleanup>());
907 
908         auto graph_madd = CreateEmptyGraph();
909         GRAPH(graph_madd)
910         {
911             PARAMETER(0, 0).type(types[1]);
912             PARAMETER(1, 1).type(types[2]);
913             PARAMETER(2, 2).type(types[3]);
914 
915             BASIC_BLOCK(2, -1)
916             {
917                 INST(3, Opcode::MAdd).type(type).Inputs(0, 1, 2);
918                 INST(4, Opcode::MAdd).type(type).Inputs(1, 2, 0);
919 
920                 INST(5, Opcode::Mul).type(type).Inputs(0, 1);
921                 INST(6, Opcode::Add).type(type).Inputs(5, 2);
922 
923                 INST(7, Opcode::Add).type(type).Inputs(3, 4);
924                 INST(8, Opcode::Add).type(type).Inputs(7, 6);
925                 INST(9, Opcode::Add).type(type).Inputs(8, 5);
926                 INST(10, Opcode::Return).type(type).Inputs(9);
927             }
928         }
929 
930         ASSERT_TRUE(GraphComparator().Compare(graph, graph_madd));
931     }
932 }
933 
TEST_F(LoweringTest,MultiplyAddWithIncompatibleInstructionTypes)934 TEST_F(LoweringTest, MultiplyAddWithIncompatibleInstructionTypes)
935 {
936     if (GetGraph()->GetArch() != Arch::AARCH64) {
937         GTEST_SKIP() << "multiply-add instruction is only supported on Aarch64";
938     }
939     auto graph = CreateEmptyLowLevelGraph();
940     GRAPH(graph)
941     {
942         PARAMETER(0, 0).u32();
943         PARAMETER(1, 1).u16();
944         PARAMETER(2, 2).u16();
945 
946         BASIC_BLOCK(2, -1)
947         {
948             // a + b * c
949             INST(3, Opcode::Mul).u16().Inputs(1, 2);
950             INST(4, Opcode::Add).u32().Inputs(3, 0);
951             INST(5, Opcode::Return).u32().Inputs(4);
952         }
953     }
954     auto clone = GraphCloner(graph, GetGraph()->GetAllocator(), GetGraph()->GetLocalAllocator()).CloneGraph();
955     ASSERT_TRUE(graph->RunPass<Lowering>());
956     ASSERT_TRUE(GraphComparator().Compare(graph, clone));
957 }
958 
TEST_F(LoweringTest,MultiplySubInteger)959 TEST_F(LoweringTest, MultiplySubInteger)
960 {
961     if (GetGraph()->GetArch() != Arch::AARCH64) {
962         GTEST_SKIP() << "multiply-subtract instruction is only supported on Aarch64";
963     }
964 
965     std::initializer_list<std::array<DataType::Type, 4>> type_combinations = {
966         {DataType::INT32, DataType::INT32, DataType::INT32, DataType::INT32},
967         {DataType::INT32, DataType::INT16, DataType::INT8, DataType::INT16},
968         {DataType::INT64, DataType::INT64, DataType::INT64, DataType::INT64}};
969 
970     for (auto &types : type_combinations) {
971         auto type = types[0];
972         auto graph = CreateEmptyLowLevelGraph();
973         GRAPH(graph)
974         {
975             PARAMETER(0, 0).type(types[1]);
976             PARAMETER(1, 1).type(types[2]);
977             PARAMETER(2, 2).type(types[3]);
978 
979             BASIC_BLOCK(2, -1)
980             {
981                 // c - a * b
982                 INST(3, Opcode::Mul).type(type).Inputs(0, 1);
983                 INST(4, Opcode::Sub).type(type).Inputs(2, 3);
984 
985                 // (- a * b) + c
986                 INST(5, Opcode::Mul).type(type).Inputs(0, 1);
987                 INST(6, Opcode::Neg).type(type).Inputs(5);
988                 INST(7, Opcode::Add).type(type).Inputs(6, 2);
989 
990                 // c + (-a) * b
991                 INST(8, Opcode::Neg).type(type).Inputs(0);
992                 INST(9, Opcode::Mul).type(type).Inputs(8, 1);
993                 INST(10, Opcode::Add).type(type).Inputs(9, 2);
994 
995                 // c + a * (-b)
996                 INST(11, Opcode::Neg).type(type).Inputs(1);
997                 INST(12, Opcode::Mul).type(type).Inputs(0, 11);
998                 INST(13, Opcode::Add).type(type).Inputs(12, 2);
999 
1000                 // c - a * b, but a * b is reused
1001                 INST(14, Opcode::Mul).type(type).Inputs(0, 1);
1002                 INST(15, Opcode::Sub).type(type).Inputs(2, 14);
1003 
1004                 INST(16, Opcode::Add).type(type).Inputs(4, 7);
1005                 INST(17, Opcode::Add).type(type).Inputs(16, 10);
1006                 INST(18, Opcode::Add).type(type).Inputs(17, 13);
1007                 INST(19, Opcode::Add).type(type).Inputs(18, 15);
1008                 INST(20, Opcode::Add).type(type).Inputs(19, 14);
1009                 INST(21, Opcode::Return).type(type).Inputs(20);
1010             }
1011         }
1012         EXPECT_TRUE(graph->RunPass<Lowering>());
1013         EXPECT_TRUE(graph->RunPass<Cleanup>());
1014 
1015         auto graph_msub = CreateEmptyGraph();
1016         GRAPH(graph_msub)
1017         {
1018             PARAMETER(0, 0).type(types[1]);
1019             PARAMETER(1, 1).type(types[2]);
1020             PARAMETER(2, 2).type(types[3]);
1021 
1022             BASIC_BLOCK(2, -1)
1023             {
1024                 INST(3, Opcode::MSub).type(type).Inputs(0, 1, 2);
1025                 INST(4, Opcode::MSub).type(type).Inputs(0, 1, 2);
1026                 INST(5, Opcode::MSub).type(type).Inputs(0, 1, 2);
1027                 // add(mul(0, neg(1)), 2) -> add(mneg(1, 0), 2)
1028                 INST(6, Opcode::MSub).type(type).Inputs(1, 0, 2);
1029 
1030                 INST(7, Opcode::Mul).type(type).Inputs(0, 1);
1031                 INST(8, Opcode::Sub).type(type).Inputs(2, 7);
1032 
1033                 INST(9, Opcode::Add).type(type).Inputs(3, 4);
1034                 INST(10, Opcode::Add).type(type).Inputs(9, 5);
1035                 INST(11, Opcode::Add).type(type).Inputs(10, 6);
1036                 INST(12, Opcode::Add).type(type).Inputs(11, 8);
1037                 INST(13, Opcode::Add).type(type).Inputs(12, 7);
1038                 INST(14, Opcode::Return).type(type).Inputs(13);
1039             }
1040         }
1041 
1042         ASSERT_TRUE(GraphComparator().Compare(graph, graph_msub));
1043     }
1044 }
1045 
TEST_F(LoweringTest,MultiplySubIntegerWithIncompatibleInstructionTypes)1046 TEST_F(LoweringTest, MultiplySubIntegerWithIncompatibleInstructionTypes)
1047 {
1048     if (GetGraph()->GetArch() != Arch::AARCH64) {
1049         GTEST_SKIP() << "multiply-subtract instruction is only supported on Aarch64";
1050     }
1051 
1052     auto graph = CreateEmptyLowLevelGraph();
1053     GRAPH(graph)
1054     {
1055         PARAMETER(0, 0).u8();
1056         PARAMETER(1, 1).u8();
1057         PARAMETER(2, 2).u32();
1058 
1059         BASIC_BLOCK(2, -1)
1060         {
1061             // c - a * b
1062             INST(3, Opcode::Mul).u16().Inputs(0, 1);
1063             INST(4, Opcode::Sub).u32().Inputs(2, 3);
1064 
1065             // (- a * b) + c
1066             INST(5, Opcode::Mul).u16().Inputs(0, 1);
1067             INST(6, Opcode::Neg).u32().Inputs(5);
1068             INST(7, Opcode::Add).u32().Inputs(6, 2);
1069 
1070             // c + (-a) * b
1071             INST(8, Opcode::Neg).u8().Inputs(0);
1072             INST(9, Opcode::Mul).u16().Inputs(8, 1);
1073             INST(10, Opcode::Add).u32().Inputs(9, 2);
1074 
1075             // c + a * (-b)
1076             INST(11, Opcode::Neg).u8().Inputs(1);
1077             INST(12, Opcode::Mul).u16().Inputs(0, 11);
1078             INST(13, Opcode::Add).u32().Inputs(12, 2);
1079 
1080             // c - a * b, but a * b is reused
1081             INST(14, Opcode::Mul).u16().Inputs(0, 1);
1082             INST(15, Opcode::Sub).u32().Inputs(2, 14);
1083 
1084             INST(16, Opcode::Add).u32().Inputs(4, 7);
1085             INST(17, Opcode::Add).u32().Inputs(16, 10);
1086             INST(18, Opcode::Add).u32().Inputs(17, 13);
1087             INST(19, Opcode::Add).u32().Inputs(18, 15);
1088             INST(20, Opcode::Add).u32().Inputs(19, 14);
1089             INST(21, Opcode::Return).u32().Inputs(20);
1090         }
1091     }
1092 
1093     auto clone = GraphCloner(graph, GetGraph()->GetAllocator(), GetGraph()->GetLocalAllocator()).CloneGraph();
1094     EXPECT_TRUE(graph->RunPass<Lowering>());
1095     ASSERT_TRUE(GraphComparator().Compare(graph, clone));
1096 }
1097 
TEST_F(LoweringTest,MultiplyAddSubFloat)1098 TEST_F(LoweringTest, MultiplyAddSubFloat)
1099 {
1100     std::array<Graph *, 2> graphs {CreateEmptyLowLevelGraph(), CreateEmptyLowLevelGraph()};
1101     for (auto &graph : graphs) {
1102         GRAPH(graph)
1103         {
1104             PARAMETER(0, 0).f64();
1105             PARAMETER(1, 1).f64();
1106             PARAMETER(2, 2).f64();
1107 
1108             BASIC_BLOCK(2, -1)
1109             {
1110                 INST(3, Opcode::Mul).f64().Inputs(0, 1);
1111                 INST(4, Opcode::Add).f64().Inputs(3, 2);
1112 
1113                 INST(5, Opcode::Mul).f64().Inputs(0, 1);
1114                 INST(6, Opcode::Sub).f64().Inputs(2, 5);
1115 
1116                 INST(7, Opcode::Add).f64().Inputs(4, 6);
1117                 INST(8, Opcode::Return).f64().Inputs(7);
1118             }
1119         }
1120     }
1121 
1122     EXPECT_TRUE(graphs[0]->RunPass<Lowering>());
1123     ASSERT_TRUE(GraphComparator().Compare(graphs[0], graphs[1]));
1124 }
1125 
TEST_F(LoweringTest,MultiplyNegate)1126 TEST_F(LoweringTest, MultiplyNegate)
1127 {
1128     if (GetGraph()->GetArch() != Arch::AARCH64) {
1129         GTEST_SKIP() << "multiply-negate instruction is only supported on Aarch64";
1130     }
1131 
1132     std::initializer_list<std::array<DataType::Type, 3>> type_combinations = {
1133         {DataType::FLOAT32, DataType::FLOAT32, DataType::FLOAT32},
1134         {DataType::FLOAT64, DataType::FLOAT64, DataType::FLOAT64},
1135         {DataType::INT32, DataType::INT32, DataType::INT32},
1136         {DataType::INT32, DataType::INT16, DataType::INT8},
1137         {DataType::INT64, DataType::INT64, DataType::INT64}};
1138 
1139     for (auto &types : type_combinations) {
1140         auto type = types[0];
1141         auto graph = CreateEmptyLowLevelGraph();
1142         GRAPH(graph)
1143         {
1144             PARAMETER(0, 0).type(types[1]);
1145             PARAMETER(1, 1).type(types[2]);
1146 
1147             BASIC_BLOCK(2, -1)
1148             {
1149                 // - (a * b)
1150                 INST(3, Opcode::Mul).type(type).Inputs(0, 1);
1151                 INST(4, Opcode::Neg).type(type).Inputs(3);
1152 
1153                 // a * (-b)
1154                 INST(5, Opcode::Neg).type(type).Inputs(1);
1155                 INST(6, Opcode::Mul).type(type).Inputs(0, 5);
1156 
1157                 // (-a) * b
1158                 INST(7, Opcode::Neg).type(type).Inputs(0);
1159                 INST(8, Opcode::Mul).type(type).Inputs(7, 1);
1160 
1161                 // - (a * b), but mul result is reused
1162                 INST(9, Opcode::Mul).type(type).Inputs(0, 1);
1163                 INST(10, Opcode::Neg).type(type).Inputs(9);
1164 
1165                 // (-a) * b, but neg result is reused
1166                 INST(11, Opcode::Neg).type(type).Inputs(0);
1167                 INST(12, Opcode::Mul).type(type).Inputs(11, 1);
1168 
1169                 INST(13, Opcode::Max).type(type).Inputs(4, 6);
1170                 INST(14, Opcode::Max).type(type).Inputs(13, 8);
1171                 INST(15, Opcode::Max).type(type).Inputs(14, 10);
1172                 INST(16, Opcode::Max).type(type).Inputs(15, 12);
1173                 INST(17, Opcode::Max).type(type).Inputs(16, 9);
1174                 INST(18, Opcode::Max).type(type).Inputs(17, 11);
1175                 INST(19, Opcode::Return).type(type).Inputs(18);
1176             }
1177         }
1178 
1179         EXPECT_TRUE(graph->RunPass<Lowering>());
1180         EXPECT_TRUE(graph->RunPass<Cleanup>());
1181 
1182         auto graph_expected = CreateEmptyGraph();
1183         GRAPH(graph_expected)
1184         {
1185             PARAMETER(0, 0).type(types[1]);
1186             PARAMETER(1, 1).type(types[2]);
1187 
1188             BASIC_BLOCK(2, -1)
1189             {
1190                 INST(3, Opcode::MNeg).type(type).Inputs(0, 1);
1191                 INST(4, Opcode::MNeg).type(type).Inputs(1, 0);
1192                 INST(5, Opcode::MNeg).type(type).Inputs(0, 1);
1193                 INST(6, Opcode::Mul).type(type).Inputs(0, 1);
1194                 INST(7, Opcode::Neg).type(type).Inputs(6);
1195                 INST(8, Opcode::Neg).type(type).Inputs(0);
1196                 INST(9, Opcode::Mul).type(type).Inputs(8, 1);
1197 
1198                 INST(10, Opcode::Max).type(type).Inputs(3, 4);
1199                 INST(11, Opcode::Max).type(type).Inputs(10, 5);
1200                 INST(12, Opcode::Max).type(type).Inputs(11, 7);
1201                 INST(13, Opcode::Max).type(type).Inputs(12, 9);
1202                 INST(14, Opcode::Max).type(type).Inputs(13, 6);
1203                 INST(15, Opcode::Max).type(type).Inputs(14, 8);
1204                 INST(16, Opcode::Return).type(type).Inputs(15);
1205             }
1206         }
1207 
1208         ASSERT_TRUE(GraphComparator().Compare(graph, graph_expected));
1209     }
1210 }
1211 
TEST_F(LoweringTest,MultiplyNegateWithIncompatibleInstructionTypes)1212 TEST_F(LoweringTest, MultiplyNegateWithIncompatibleInstructionTypes)
1213 {
1214     if (GetGraph()->GetArch() != Arch::AARCH64) {
1215         GTEST_SKIP() << "multiply-negate instruction is only supported on Aarch64";
1216     }
1217 
1218     auto graph = CreateEmptyLowLevelGraph();
1219     GRAPH(graph)
1220     {
1221         PARAMETER(0, 0).s16();
1222         PARAMETER(1, 1).s16();
1223 
1224         BASIC_BLOCK(2, -1)
1225         {
1226             // - (a * b)
1227             INST(3, Opcode::Mul).s16().Inputs(0, 1);
1228             INST(4, Opcode::Neg).s32().Inputs(3);
1229 
1230             // a * (-b)
1231             INST(5, Opcode::Neg).s16().Inputs(1);
1232             INST(6, Opcode::Mul).s32().Inputs(0, 5);
1233 
1234             // (-a) * b
1235             INST(7, Opcode::Neg).s16().Inputs(0);
1236             INST(8, Opcode::Mul).s32().Inputs(7, 1);
1237 
1238             // - (a * b), but mul result is reused
1239             INST(9, Opcode::Mul).s16().Inputs(0, 1);
1240             INST(10, Opcode::Neg).s32().Inputs(9);
1241 
1242             // (-a) * b, but neg result is reused
1243             INST(11, Opcode::Neg).s16().Inputs(0);
1244             INST(12, Opcode::Mul).s32().Inputs(11, 1);
1245 
1246             INST(13, Opcode::Max).s32().Inputs(4, 6);
1247             INST(14, Opcode::Max).s32().Inputs(13, 8);
1248             INST(15, Opcode::Max).s32().Inputs(14, 10);
1249             INST(16, Opcode::Max).s32().Inputs(15, 12);
1250             INST(17, Opcode::Max).s32().Inputs(16, 9);
1251             INST(18, Opcode::Max).s32().Inputs(17, 11);
1252             INST(19, Opcode::Return).s32().Inputs(18);
1253         }
1254     }
1255 
1256     auto clone = GraphCloner(graph, GetGraph()->GetAllocator(), GetGraph()->GetLocalAllocator()).CloneGraph();
1257     EXPECT_TRUE(graph->RunPass<Lowering>());
1258     ASSERT_TRUE(GraphComparator().Compare(graph, clone));
1259 }
1260 
TEST_F(LoweringTest,BitwiseBinaryOpWithInvertedOperand)1261 TEST_F(LoweringTest, BitwiseBinaryOpWithInvertedOperand)
1262 {
1263     if (GetGraph()->GetArch() != Arch::AARCH64) {
1264         GTEST_SKIP() << "xor-not, or-not and and-not instructions are only supported on Aarch64";
1265     }
1266 
1267     std::initializer_list<std::pair<Opcode, Opcode>> opcodes = {
1268         {Opcode::And, Opcode::AndNot}, {Opcode::Or, Opcode::OrNot}, {Opcode::Xor, Opcode::XorNot}};
1269     std::initializer_list<std::array<DataType::Type, 3>> type_combinations = {
1270         {DataType::INT32, DataType::INT32, DataType::INT32},
1271         {DataType::UINT32, DataType::UINT32, DataType::UINT32},
1272         {DataType::UINT32, DataType::UINT16, DataType::UINT8},
1273         {DataType::INT64, DataType::INT64, DataType::INT64},
1274         {DataType::UINT64, DataType::UINT64, DataType::UINT64}};
1275     for (auto &types : type_combinations) {
1276         for (auto &ops : opcodes) {
1277             auto type = types[0];
1278             auto graph = CreateEmptyLowLevelGraph();
1279             GRAPH(graph)
1280             {
1281                 PARAMETER(0, 0).type(types[1]);
1282                 PARAMETER(1, 1).type(types[2]);
1283 
1284                 BASIC_BLOCK(2, -1)
1285                 {
1286                     // ~a op b
1287                     INST(3, Opcode::Not).type(type).Inputs(0);
1288                     INST(4, ops.first).type(type).Inputs(3, 1);
1289                     // a op ~b
1290                     INST(5, Opcode::Not).type(type).Inputs(1);
1291                     INST(6, ops.first).type(type).Inputs(0, 5);
1292                     // ~a op b, but ~a is reused
1293                     INST(7, Opcode::Not).type(type).Inputs(0);
1294                     INST(8, ops.first).type(type).Inputs(7, 1);
1295 
1296                     INST(9, Opcode::Add).type(type).Inputs(4, 6);
1297                     INST(10, Opcode::Add).type(type).Inputs(9, 8);
1298                     INST(11, Opcode::Add).type(type).Inputs(10, 7);
1299                     INST(12, Opcode::Return).type(type).Inputs(11);
1300                 }
1301             }
1302 
1303             EXPECT_TRUE(graph->RunPass<Lowering>());
1304             EXPECT_TRUE(graph->RunPass<Cleanup>());
1305 
1306             auto graph_expected = CreateEmptyGraph();
1307             GRAPH(graph_expected)
1308             {
1309                 PARAMETER(0, 0).type(types[1]);
1310                 PARAMETER(1, 1).type(types[2]);
1311 
1312                 BASIC_BLOCK(2, -1)
1313                 {
1314                     INST(3, ops.second).type(type).Inputs(1, 0);
1315                     INST(4, ops.second).type(type).Inputs(0, 1);
1316                     INST(5, Opcode::Not).type(type).Inputs(0);
1317                     INST(6, ops.first).type(type).Inputs(5, 1);
1318 
1319                     INST(7, Opcode::Add).type(type).Inputs(3, 4);
1320                     INST(8, Opcode::Add).type(type).Inputs(7, 6);
1321                     INST(9, Opcode::Add).type(type).Inputs(8, 5);
1322                     INST(10, Opcode::Return).type(type).Inputs(9);
1323                 }
1324             }
1325 
1326             ASSERT_TRUE(GraphComparator().Compare(graph, graph_expected));
1327         }
1328     }
1329 }
1330 
TEST_F(LoweringTest,BitwiseBinaryOpWithInvertedOperandWitnIncompatibleInstructionTypes)1331 TEST_F(LoweringTest, BitwiseBinaryOpWithInvertedOperandWitnIncompatibleInstructionTypes)
1332 {
1333     if (GetGraph()->GetArch() != Arch::AARCH64) {
1334         GTEST_SKIP() << "xor-not, or-not and and-not instructions are only supported on Aarch64";
1335     }
1336 
1337     std::initializer_list<Opcode> opcodes = {Opcode::And, Opcode::Or, Opcode::Xor};
1338     for (auto &ops : opcodes) {
1339         auto graph = CreateEmptyLowLevelGraph();
1340         GRAPH(graph)
1341         {
1342             PARAMETER(0, 0).s16();
1343             PARAMETER(1, 1).s16();
1344 
1345             BASIC_BLOCK(2, -1)
1346             {
1347                 // ~a op b
1348                 INST(3, Opcode::Not).s16().Inputs(0);
1349                 INST(4, ops).s32().Inputs(3, 1);
1350                 // a op ~b
1351                 INST(5, Opcode::Not).s16().Inputs(1);
1352                 INST(6, ops).s32().Inputs(0, 5);
1353                 // ~a op b, but ~a is reused
1354                 INST(7, Opcode::Not).s16().Inputs(0);
1355                 INST(8, ops).s32().Inputs(7, 1);
1356 
1357                 INST(9, Opcode::Add).s32().Inputs(4, 6);
1358                 INST(10, Opcode::Add).s32().Inputs(9, 8);
1359                 INST(11, Opcode::Add).s32().Inputs(10, 7);
1360                 INST(12, Opcode::Return).s32().Inputs(11);
1361             }
1362         }
1363 
1364         auto clone = GraphCloner(graph, GetGraph()->GetAllocator(), GetGraph()->GetLocalAllocator()).CloneGraph();
1365         EXPECT_TRUE(graph->RunPass<Lowering>());
1366         ASSERT_TRUE(GraphComparator().Compare(graph, clone));
1367     }
1368 }
1369 
TEST_F(LoweringTest,CommutativeBinaryOpWithShiftedOperand)1370 TEST_F(LoweringTest, CommutativeBinaryOpWithShiftedOperand)
1371 {
1372     if (GetGraph()->GetArch() != Arch::AARCH64) {
1373         GTEST_SKIP() << "instructions with shifted operands are only supported on Aarch64";
1374     }
1375 
1376     std::initializer_list<std::pair<Opcode, Opcode>> opcodes = {{Opcode::Add, Opcode::AddSR},
1377                                                                 {Opcode::And, Opcode::AndSR},
1378                                                                 {Opcode::Or, Opcode::OrSR},
1379                                                                 {Opcode::Xor, Opcode::XorSR}};
1380 
1381     std::initializer_list<std::tuple<Opcode, Opcode, ShiftType>> shift_ops = {
1382         {Opcode::Shl, Opcode::ShlI, ShiftType::LSL},
1383         {Opcode::Shr, Opcode::ShrI, ShiftType::LSR},
1384         {Opcode::AShr, Opcode::AShrI, ShiftType::ASR}};
1385     std::initializer_list<std::array<DataType::Type, 3>> type_combinations = {
1386         {DataType::INT32, DataType::INT32, DataType::INT32},
1387         {DataType::UINT32, DataType::UINT32, DataType::UINT32},
1388         {DataType::UINT32, DataType::UINT16, DataType::UINT8},
1389         {DataType::INT64, DataType::INT64, DataType::INT64},
1390         {DataType::UINT64, DataType::UINT64, DataType::UINT64}};
1391 
1392     for (auto &types : type_combinations) {
1393         for (auto &shift_op : shift_ops) {
1394             for (auto &ops : opcodes) {
1395                 auto type = types[0];
1396                 auto graph = CreateEmptyLowLevelGraph();
1397                 GRAPH(graph)
1398                 {
1399                     PARAMETER(0, 0).type(types[1]);
1400                     PARAMETER(1, 1).type(types[2]);
1401                     CONSTANT(2, 5);
1402                     CONSTANT(3, 3);
1403 
1404                     BASIC_BLOCK(2, -1)
1405                     {
1406                         INST(4, std::get<0>(shift_op)).type(type).Inputs(0, 2);
1407                         INST(5, ops.first).type(type).Inputs(1, 4);
1408 
1409                         INST(6, std::get<0>(shift_op)).type(type).Inputs(1, 2);
1410                         INST(7, ops.first).type(type).Inputs(6, 1);
1411 
1412                         INST(8, std::get<0>(shift_op)).type(type).Inputs(1, 2);
1413                         INST(9, ops.first).type(type).Inputs(8, 3);
1414 
1415                         INST(10, std::get<0>(shift_op)).type(type).Inputs(0, 2);
1416                         INST(11, ops.first).type(type).Inputs(1, 10);
1417 
1418                         INST(12, Opcode::Max).type(type).Inputs(5, 7);
1419                         INST(13, Opcode::Max).type(type).Inputs(12, 9);
1420                         INST(14, Opcode::Max).type(type).Inputs(13, 11);
1421                         INST(15, Opcode::Max).type(type).Inputs(14, 10);
1422                         INST(16, Opcode::Return).type(type).Inputs(15);
1423                     }
1424                 }
1425 
1426                 EXPECT_TRUE(graph->RunPass<Lowering>());
1427                 EXPECT_TRUE(graph->RunPass<Cleanup>());
1428 
1429                 auto graph_expected = CreateEmptyGraph();
1430                 GRAPH(graph_expected)
1431                 {
1432                     PARAMETER(0, 0).type(types[1]);
1433                     PARAMETER(1, 1).type(types[2]);
1434                     CONSTANT(2, 3);
1435 
1436                     BASIC_BLOCK(2, -1)
1437                     {
1438                         INST(3, ops.second).Shift(std::get<2>(shift_op), 5).type(type).Inputs(1, 0);
1439                         INST(4, ops.second).Shift(std::get<2>(shift_op), 5).type(type).Inputs(1, 1);
1440                         INST(5, ops.second).Shift(std::get<2>(shift_op), 5).type(type).Inputs(2, 1);
1441 
1442                         INST(6, std::get<1>(shift_op)).Imm(5).type(type).Inputs(0);
1443                         INST(7, ops.first).type(type).Inputs(1, 6);
1444 
1445                         INST(8, Opcode::Max).type(type).Inputs(3, 4);
1446                         INST(9, Opcode::Max).type(type).Inputs(8, 5);
1447                         INST(10, Opcode::Max).type(type).Inputs(9, 7);
1448                         INST(11, Opcode::Max).type(type).Inputs(10, 6);
1449                         INST(12, Opcode::Return).type(type).Inputs(11);
1450                     }
1451                 }
1452 
1453                 ASSERT_TRUE(GraphComparator().Compare(graph, graph_expected));
1454             }
1455         }
1456     }
1457 }
1458 
TEST_F(LoweringTest,CommutativeBinaryOpWithShiftedOperandWithIncompatibleInstructionTypes)1459 TEST_F(LoweringTest, CommutativeBinaryOpWithShiftedOperandWithIncompatibleInstructionTypes)
1460 {
1461     if (GetGraph()->GetArch() != Arch::AARCH64) {
1462         GTEST_SKIP() << "instructions with shifted operands are only supported on Aarch64";
1463     }
1464 
1465     std::initializer_list<Opcode> opcodes = {Opcode::Add, Opcode::And, Opcode::Or, Opcode::Xor};
1466 
1467     std::initializer_list<std::tuple<Opcode, Opcode, ShiftType>> shift_ops = {
1468         {Opcode::Shl, Opcode::ShlI, ShiftType::LSL},
1469         {Opcode::Shr, Opcode::ShrI, ShiftType::LSR},
1470         {Opcode::AShr, Opcode::AShrI, ShiftType::ASR}};
1471     for (auto &shift_op : shift_ops) {
1472         for (auto &ops : opcodes) {
1473             auto graph = CreateEmptyLowLevelGraph();
1474             GRAPH(graph)
1475             {
1476                 PARAMETER(0, 0).s16();
1477                 PARAMETER(1, 1).s16();
1478                 CONSTANT(2, 5);
1479                 PARAMETER(3, 2).s16();
1480 
1481                 BASIC_BLOCK(2, -1)
1482                 {
1483                     INST(4, std::get<0>(shift_op)).s16().Inputs(0, 2);
1484                     INST(5, ops).s32().Inputs(1, 4);
1485 
1486                     INST(6, std::get<0>(shift_op)).s16().Inputs(1, 2);
1487                     INST(7, ops).s32().Inputs(6, 1);
1488 
1489                     INST(8, std::get<0>(shift_op)).s16().Inputs(1, 2);
1490                     INST(9, ops).s32().Inputs(8, 3);
1491 
1492                     INST(10, std::get<0>(shift_op)).s16().Inputs(0, 2);
1493                     INST(11, ops).s32().Inputs(1, 10);
1494 
1495                     INST(12, Opcode::Max).s32().Inputs(5, 7);
1496                     INST(13, Opcode::Max).s32().Inputs(12, 9);
1497                     INST(14, Opcode::Max).s32().Inputs(13, 11);
1498                     INST(15, Opcode::Max).s32().Inputs(14, 10);
1499                     INST(16, Opcode::Return).s32().Inputs(15);
1500                 }
1501             }
1502 
1503             auto clone = GraphCloner(graph, GetGraph()->GetAllocator(), GetGraph()->GetLocalAllocator()).CloneGraph();
1504             EXPECT_TRUE(graph->RunPass<Lowering>());
1505             ASSERT_TRUE(GraphComparator().Compare(graph, clone));
1506         }
1507     }
1508 }
1509 
TEST_F(LoweringTest,SubWithShiftedOperand)1510 TEST_F(LoweringTest, SubWithShiftedOperand)
1511 {
1512     if (GetGraph()->GetArch() != Arch::AARCH64) {
1513         GTEST_SKIP() << "instructions with shifted operands are only supported on Aarch64";
1514     }
1515 
1516     std::initializer_list<std::tuple<Opcode, Opcode, ShiftType>> shift_ops = {
1517         {Opcode::Shl, Opcode::ShlI, ShiftType::LSL},
1518         {Opcode::Shr, Opcode::ShrI, ShiftType::LSR},
1519         {Opcode::AShr, Opcode::AShrI, ShiftType::ASR}};
1520     std::initializer_list<std::array<DataType::Type, 3>> type_combinations = {
1521         {DataType::INT32, DataType::INT32, DataType::INT32},
1522         {DataType::UINT32, DataType::UINT32, DataType::UINT32},
1523         {DataType::UINT32, DataType::UINT16, DataType::UINT8},
1524         {DataType::INT64, DataType::INT64, DataType::INT64},
1525         {DataType::UINT64, DataType::UINT64, DataType::UINT64}};
1526 
1527     for (auto &types : type_combinations) {
1528         for (auto &shift_op : shift_ops) {
1529             auto type = types[0];
1530             auto graph = CreateEmptyLowLevelGraph();
1531             GRAPH(graph)
1532             {
1533                 PARAMETER(0, 0).type(types[1]);
1534                 PARAMETER(1, 1).type(types[2]);
1535                 CONSTANT(2, 5);
1536                 CONSTANT(3, 2);
1537 
1538                 BASIC_BLOCK(2, -1)
1539                 {
1540                     INST(4, std::get<0>(shift_op)).type(type).Inputs(0, 2);
1541                     INST(5, Opcode::Sub).type(type).Inputs(1, 4);
1542 
1543                     INST(6, std::get<0>(shift_op)).type(type).Inputs(1, 2);
1544                     INST(7, Opcode::Sub).type(type).Inputs(6, 1);
1545 
1546                     INST(8, std::get<0>(shift_op)).type(type).Inputs(0, 2);
1547                     INST(9, Opcode::Sub).type(type).Inputs(1, 8);
1548 
1549                     INST(10, std::get<0>(shift_op)).type(type).Inputs(0, 2);
1550                     INST(11, Opcode::Sub).type(type).Inputs(3, 10);
1551 
1552                     INST(12, Opcode::Max).type(type).Inputs(5, 7);
1553                     INST(13, Opcode::Max).type(type).Inputs(12, 8);
1554                     INST(14, Opcode::Max).type(type).Inputs(13, 9);
1555                     INST(15, Opcode::Max).type(type).Inputs(14, 11);
1556                     INST(16, Opcode::Return).type(type).Inputs(15);
1557                 }
1558             }
1559 
1560             EXPECT_TRUE(graph->RunPass<Lowering>());
1561             EXPECT_TRUE(graph->RunPass<Cleanup>());
1562 
1563             auto graph_expected = CreateEmptyGraph();
1564             GRAPH(graph_expected)
1565             {
1566                 PARAMETER(0, 0).type(types[1]);
1567                 PARAMETER(1, 1).type(types[2]);
1568                 CONSTANT(2, 2);
1569 
1570                 BASIC_BLOCK(2, -1)
1571                 {
1572                     INST(3, Opcode::SubSR).Shift(std::get<2>(shift_op), 5).type(type).Inputs(1, 0);
1573                     INST(4, std::get<1>(shift_op)).Imm(5).type(type).Inputs(1);
1574                     INST(5, Opcode::Sub).type(type).Inputs(4, 1);
1575                     INST(6, std::get<1>(shift_op)).Imm(5).type(type).Inputs(0);
1576                     INST(7, Opcode::Sub).type(type).Inputs(1, 6);
1577                     INST(8, Opcode::SubSR).Shift(std::get<2>(shift_op), 5).type(type).Inputs(2, 0);
1578 
1579                     INST(9, Opcode::Max).type(type).Inputs(3, 5);
1580                     INST(10, Opcode::Max).type(type).Inputs(9, 6);
1581                     INST(11, Opcode::Max).type(type).Inputs(10, 7);
1582                     INST(12, Opcode::Max).type(type).Inputs(11, 8);
1583                     INST(13, Opcode::Return).type(type).Inputs(12);
1584                 }
1585             }
1586 
1587             ASSERT_TRUE(GraphComparator().Compare(graph, graph_expected));
1588         }
1589     }
1590 }
1591 
TEST_F(LoweringTest,SubWithShiftedOperandWithIncompatibleTypes)1592 TEST_F(LoweringTest, SubWithShiftedOperandWithIncompatibleTypes)
1593 {
1594     if (GetGraph()->GetArch() != Arch::AARCH64) {
1595         GTEST_SKIP() << "instructions with shifted operands are only supported on Aarch64";
1596     }
1597 
1598     std::initializer_list<std::tuple<Opcode, Opcode, ShiftType>> shift_ops = {
1599         {Opcode::Shl, Opcode::ShlI, ShiftType::LSL},
1600         {Opcode::Shr, Opcode::ShrI, ShiftType::LSR},
1601         {Opcode::AShr, Opcode::AShrI, ShiftType::ASR}};
1602 
1603     for (auto &shift_op : shift_ops) {
1604         auto graph = CreateEmptyLowLevelGraph();
1605         GRAPH(graph)
1606         {
1607             PARAMETER(0, 0).s16();
1608             PARAMETER(1, 1).s16();
1609             CONSTANT(2, 5);
1610             CONSTANT(3, 2);
1611 
1612             BASIC_BLOCK(2, -1)
1613             {
1614                 INST(4, std::get<0>(shift_op)).s16().Inputs(0, 2);
1615                 INST(5, Opcode::Sub).s32().Inputs(1, 4);
1616 
1617                 INST(6, std::get<0>(shift_op)).s16().Inputs(1, 2);
1618                 INST(7, Opcode::Sub).s32().Inputs(6, 1);
1619 
1620                 INST(8, std::get<0>(shift_op)).s16().Inputs(0, 2);
1621                 INST(9, Opcode::Sub).s32().Inputs(1, 8);
1622 
1623                 INST(10, std::get<0>(shift_op)).s16().Inputs(0, 2);
1624                 INST(11, Opcode::Sub).s32().Inputs(3, 10);
1625 
1626                 INST(12, Opcode::Max).s32().Inputs(5, 7);
1627                 INST(13, Opcode::Max).s32().Inputs(12, 8);
1628                 INST(14, Opcode::Max).s32().Inputs(13, 9);
1629                 INST(15, Opcode::Max).s32().Inputs(14, 11);
1630                 INST(16, Opcode::Return).s32().Inputs(15);
1631             }
1632         }
1633 
1634         auto clone = GraphCloner(graph, GetGraph()->GetAllocator(), GetGraph()->GetLocalAllocator()).CloneGraph();
1635         EXPECT_TRUE(graph->RunPass<Lowering>());
1636         ASSERT_TRUE(GraphComparator().Compare(graph, clone));
1637     }
1638 }
1639 
TEST_F(LoweringTest,NonCommutativeBinaryOpWithShiftedOperand)1640 TEST_F(LoweringTest, NonCommutativeBinaryOpWithShiftedOperand)
1641 {
1642     if (GetGraph()->GetArch() != Arch::AARCH64) {
1643         GTEST_SKIP() << "instructions with shifted operands are only supported on Aarch64";
1644     }
1645 
1646     std::initializer_list<std::pair<Opcode, Opcode>> opcodes = {{Opcode::Sub, Opcode::SubSR},
1647                                                                 {Opcode::AndNot, Opcode::AndNotSR},
1648                                                                 {Opcode::OrNot, Opcode::OrNotSR},
1649                                                                 {Opcode::XorNot, Opcode::XorNotSR}};
1650 
1651     std::initializer_list<std::tuple<Opcode, Opcode, ShiftType>> shift_ops = {
1652         {Opcode::Shl, Opcode::ShlI, ShiftType::LSL},
1653         {Opcode::Shr, Opcode::ShrI, ShiftType::LSR},
1654         {Opcode::AShr, Opcode::AShrI, ShiftType::ASR}};
1655     std::initializer_list<std::array<DataType::Type, 3>> type_combinations = {
1656         {DataType::INT32, DataType::INT32, DataType::INT32},
1657         {DataType::UINT32, DataType::UINT32, DataType::UINT32},
1658         {DataType::UINT32, DataType::UINT16, DataType::UINT8},
1659         {DataType::INT64, DataType::INT64, DataType::INT64},
1660         {DataType::UINT64, DataType::UINT64, DataType::UINT64}};
1661 
1662     for (auto &types : type_combinations) {
1663         for (auto &shift_op : shift_ops) {
1664             auto type = types[0];
1665             for (auto &ops : opcodes) {
1666                 auto graph = CreateEmptyLowLevelGraph();
1667                 GRAPH(graph)
1668                 {
1669                     PARAMETER(0, 0).type(types[1]);
1670                     PARAMETER(1, 1).type(types[2]);
1671                     CONSTANT(2, 5);
1672 
1673                     BASIC_BLOCK(2, -1)
1674                     {
1675                         INST(3, std::get<0>(shift_op)).type(type).Inputs(0, 2);
1676                         INST(4, ops.first).type(type).Inputs(1, 3);
1677 
1678                         INST(5, std::get<0>(shift_op)).type(type).Inputs(1, 2);
1679                         INST(6, ops.first).type(type).Inputs(5, 1);
1680 
1681                         INST(7, std::get<0>(shift_op)).type(type).Inputs(0, 2);
1682                         INST(8, ops.first).type(type).Inputs(1, 7);
1683 
1684                         INST(9, Opcode::Max).type(type).Inputs(4, 6);
1685                         INST(10, Opcode::Max).type(type).Inputs(9, 8);
1686                         INST(11, Opcode::Max).type(type).Inputs(10, 7);
1687                         INST(12, Opcode::Return).type(type).Inputs(11);
1688                     }
1689                 }
1690 
1691                 EXPECT_TRUE(graph->RunPass<Lowering>());
1692                 EXPECT_TRUE(graph->RunPass<Cleanup>());
1693 
1694                 auto graph_expected = CreateEmptyGraph();
1695                 GRAPH(graph_expected)
1696                 {
1697                     PARAMETER(0, 0).type(types[1]);
1698                     PARAMETER(1, 1).type(types[2]);
1699 
1700                     BASIC_BLOCK(2, -1)
1701                     {
1702                         INST(2, ops.second).Shift(std::get<2>(shift_op), 5).type(type).Inputs(1, 0);
1703                         INST(3, std::get<1>(shift_op)).Imm(5).type(type).Inputs(1);
1704                         INST(4, ops.first).type(type).Inputs(3, 1);
1705                         INST(5, std::get<1>(shift_op)).Imm(5).type(type).Inputs(0);
1706                         INST(6, ops.first).type(type).Inputs(1, 5);
1707 
1708                         INST(7, Opcode::Max).type(type).Inputs(2, 4);
1709                         INST(8, Opcode::Max).type(type).Inputs(7, 6);
1710                         INST(9, Opcode::Max).type(type).Inputs(8, 5);
1711                         INST(10, Opcode::Return).type(type).Inputs(9);
1712                     }
1713                 }
1714 
1715                 ASSERT_TRUE(GraphComparator().Compare(graph, graph_expected));
1716             }
1717         }
1718     }
1719 }
1720 
TEST_F(LoweringTest,NonCommutativeBinaryOpWithShiftedOperandWithIncompatibleInstructionTypes)1721 TEST_F(LoweringTest, NonCommutativeBinaryOpWithShiftedOperandWithIncompatibleInstructionTypes)
1722 {
1723     if (GetGraph()->GetArch() != Arch::AARCH64) {
1724         GTEST_SKIP() << "instructions with shifted operands are only supported on Aarch64";
1725     }
1726 
1727     std::initializer_list<Opcode> opcodes = {Opcode::Sub, Opcode::AndNot, Opcode::OrNot, Opcode::XorNot};
1728 
1729     std::initializer_list<std::tuple<Opcode, Opcode, ShiftType>> shift_ops = {
1730         {Opcode::Shl, Opcode::ShlI, ShiftType::LSL},
1731         {Opcode::Shr, Opcode::ShrI, ShiftType::LSR},
1732         {Opcode::AShr, Opcode::AShrI, ShiftType::ASR}};
1733 
1734     for (auto &shift_op : shift_ops) {
1735         for (auto &ops : opcodes) {
1736             auto graph = CreateEmptyLowLevelGraph();
1737             GRAPH(graph)
1738             {
1739                 PARAMETER(0, 0).s16();
1740                 PARAMETER(1, 1).s16();
1741                 CONSTANT(2, 5);
1742 
1743                 BASIC_BLOCK(2, -1)
1744                 {
1745                     INST(3, std::get<0>(shift_op)).s16().Inputs(0, 2);
1746                     INST(4, ops).s32().Inputs(1, 3);
1747 
1748                     INST(5, std::get<0>(shift_op)).s16().Inputs(1, 2);
1749                     INST(6, ops).s32().Inputs(5, 1);
1750 
1751                     INST(7, std::get<0>(shift_op)).s16().Inputs(0, 2);
1752                     INST(8, ops).s32().Inputs(1, 7);
1753 
1754                     INST(9, Opcode::Max).s32().Inputs(4, 6);
1755                     INST(10, Opcode::Max).s32().Inputs(9, 8);
1756                     INST(11, Opcode::Max).s32().Inputs(10, 7);
1757                     INST(12, Opcode::Return).s32().Inputs(11);
1758                 }
1759             }
1760 
1761             auto clone = GraphCloner(graph, GetGraph()->GetAllocator(), GetGraph()->GetLocalAllocator()).CloneGraph();
1762             EXPECT_TRUE(graph->RunPass<Lowering>());
1763             ASSERT_TRUE(GraphComparator().Compare(graph, clone));
1764         }
1765     }
1766 }
1767 
TEST_F(LoweringTest,BitwiseInstructionsWithInvertedShiftedOperand)1768 TEST_F(LoweringTest, BitwiseInstructionsWithInvertedShiftedOperand)
1769 {
1770     if (GetGraph()->GetArch() != Arch::AARCH64) {
1771         GTEST_SKIP() << "instructions with shifted operands are only supported on Aarch64";
1772     }
1773 
1774     std::initializer_list<std::pair<Opcode, Opcode>> opcodes = {
1775         {Opcode::And, Opcode::AndNotSR}, {Opcode::Or, Opcode::OrNotSR}, {Opcode::Xor, Opcode::XorNotSR}};
1776 
1777     std::initializer_list<std::pair<Opcode, ShiftType>> shift_ops = {
1778         {Opcode::Shl, ShiftType::LSL}, {Opcode::Shr, ShiftType::LSR}, {Opcode::AShr, ShiftType::ASR}};
1779 
1780     std::initializer_list<std::array<DataType::Type, 3>> type_combinations = {
1781         {DataType::INT32, DataType::INT32, DataType::INT32},
1782         {DataType::UINT32, DataType::UINT32, DataType::UINT32},
1783         {DataType::UINT32, DataType::UINT16, DataType::UINT8},
1784         {DataType::INT64, DataType::INT64, DataType::INT64},
1785         {DataType::UINT64, DataType::UINT64, DataType::UINT64}};
1786 
1787     for (auto &types : type_combinations) {
1788         for (auto &shift_op : shift_ops) {
1789             auto type = types[0];
1790             for (auto &ops : opcodes) {
1791                 auto graph = CreateEmptyLowLevelGraph();
1792                 GRAPH(graph)
1793                 {
1794                     PARAMETER(0, 0).type(types[1]);
1795                     PARAMETER(1, 1).type(types[2]);
1796                     CONSTANT(2, 5);
1797 
1798                     BASIC_BLOCK(2, -1)
1799                     {
1800                         INST(3, shift_op.first).type(type).Inputs(0, 2);
1801                         INST(4, Opcode::Not).type(type).Inputs(3);
1802                         INST(5, ops.first).type(type).Inputs(1, 4);
1803                         INST(6, Opcode::Return).type(type).Inputs(5);
1804                     }
1805                 }
1806 
1807                 EXPECT_TRUE(graph->RunPass<Lowering>());
1808                 EXPECT_TRUE(graph->RunPass<Cleanup>());
1809 
1810                 auto graph_expected = CreateEmptyGraph();
1811                 GRAPH(graph_expected)
1812                 {
1813                     PARAMETER(0, 0).type(types[1]);
1814                     PARAMETER(1, 1).type(types[2]);
1815 
1816                     BASIC_BLOCK(2, -1)
1817                     {
1818                         INST(2, ops.second).Shift(shift_op.second, 5).type(type).Inputs(1, 0);
1819                         INST(3, Opcode::Return).type(type).Inputs(2);
1820                     }
1821                 }
1822 
1823                 ASSERT_TRUE(GraphComparator().Compare(graph, graph_expected));
1824             }
1825         }
1826     }
1827 }
1828 
TEST_F(LoweringTest,BitwiseInstructionsWithInvertedShiftedOperandWithIncompatibleInstructionTypes)1829 TEST_F(LoweringTest, BitwiseInstructionsWithInvertedShiftedOperandWithIncompatibleInstructionTypes)
1830 {
1831     if (GetGraph()->GetArch() != Arch::AARCH64) {
1832         GTEST_SKIP() << "instructions with shifted operands are only supported on Aarch64";
1833     }
1834 
1835     std::initializer_list<Opcode> opcodes = {Opcode::And, Opcode::Or, Opcode::Xor};
1836 
1837     std::initializer_list<std::pair<Opcode, ShiftType>> shift_ops = {
1838         {Opcode::Shl, ShiftType::LSL}, {Opcode::Shr, ShiftType::LSR}, {Opcode::AShr, ShiftType::ASR}};
1839 
1840     for (auto &shift_op : shift_ops) {
1841         for (auto &ops : opcodes) {
1842             auto graph = CreateEmptyLowLevelGraph();
1843             GRAPH(graph)
1844             {
1845                 PARAMETER(0, 0).s16();
1846                 PARAMETER(1, 1).s16();
1847                 CONSTANT(2, 5);
1848 
1849                 BASIC_BLOCK(2, -1)
1850                 {
1851                     INST(3, shift_op.first).s16().Inputs(0, 2);
1852                     INST(4, Opcode::Not).s16().Inputs(3);
1853                     INST(5, ops).s32().Inputs(1, 4);
1854                     INST(6, Opcode::Return).s32().Inputs(5);
1855                 }
1856             }
1857 
1858             auto clone = GraphCloner(graph, GetGraph()->GetAllocator(), GetGraph()->GetLocalAllocator()).CloneGraph();
1859             EXPECT_TRUE(graph->RunPass<Lowering>());
1860             ASSERT_TRUE(GraphComparator().Compare(graph, clone));
1861         }
1862     }
1863 }
1864 
TEST_F(LoweringTest,NegWithShiftedOperand)1865 TEST_F(LoweringTest, NegWithShiftedOperand)
1866 {
1867     if (GetGraph()->GetArch() != Arch::AARCH64) {
1868         GTEST_SKIP() << "instructions with shifted operand are only supported on Aarch64";
1869     }
1870 
1871     std::initializer_list<std::pair<Opcode, ShiftType>> shift_ops = {
1872         {Opcode::Shl, ShiftType::LSL}, {Opcode::Shr, ShiftType::LSR}, {Opcode::AShr, ShiftType::ASR}};
1873     std::initializer_list<std::pair<DataType::Type, DataType::Type>> type_combinations = {
1874         {DataType::INT32, DataType::INT32},
1875         {DataType::UINT32, DataType::UINT32},
1876         {DataType::UINT32, DataType::UINT16},
1877         {DataType::INT64, DataType::INT64},
1878         {DataType::UINT64, DataType::UINT64}};
1879     for (auto &types : type_combinations) {
1880         for (auto &shift_op : shift_ops) {
1881             auto type = types.first;
1882             auto graph = CreateEmptyLowLevelGraph();
1883             GRAPH(graph)
1884             {
1885                 PARAMETER(0, 0).type(types.second);
1886                 CONSTANT(1, 5);
1887 
1888                 BASIC_BLOCK(2, -1)
1889                 {
1890                     INST(2, shift_op.first).type(type).Inputs(0, 1);
1891                     INST(3, Opcode::Neg).type(type).Inputs(2);
1892                     INST(4, Opcode::Return).type(type).Inputs(3);
1893                 }
1894             }
1895 
1896             EXPECT_TRUE(graph->RunPass<Lowering>());
1897             EXPECT_TRUE(graph->RunPass<Cleanup>());
1898 
1899             auto graph_expected = CreateEmptyGraph();
1900             GRAPH(graph_expected)
1901             {
1902                 PARAMETER(0, 0).type(types.second);
1903 
1904                 BASIC_BLOCK(2, -1)
1905                 {
1906                     INST(1, Opcode::NegSR).Shift(shift_op.second, 5).type(type).Inputs(0);
1907                     INST(2, Opcode::Return).type(type).Inputs(1);
1908                 }
1909             }
1910 
1911             ASSERT_TRUE(GraphComparator().Compare(graph, graph_expected));
1912         }
1913     }
1914 }
1915 
TEST_F(LoweringTest,NegWithShiftedOperandWithIncompatibleInstructionTypes)1916 TEST_F(LoweringTest, NegWithShiftedOperandWithIncompatibleInstructionTypes)
1917 {
1918     if (GetGraph()->GetArch() != Arch::AARCH64) {
1919         GTEST_SKIP() << "instructions with shifted operand are only supported on Aarch64";
1920     }
1921 
1922     std::initializer_list<std::pair<Opcode, ShiftType>> shift_ops = {
1923         {Opcode::Shl, ShiftType::LSL}, {Opcode::Shr, ShiftType::LSR}, {Opcode::AShr, ShiftType::ASR}};
1924 
1925     for (auto &shift_op : shift_ops) {
1926         auto graph = CreateEmptyLowLevelGraph();
1927         GRAPH(graph)
1928         {
1929             PARAMETER(0, 0).s16();
1930             CONSTANT(1, 5);
1931 
1932             BASIC_BLOCK(2, -1)
1933             {
1934                 INST(2, shift_op.first).s16().Inputs(0, 1);
1935                 INST(3, Opcode::Neg).s32().Inputs(2);
1936                 INST(4, Opcode::Return).s32().Inputs(3);
1937             }
1938         }
1939 
1940         auto clone = GraphCloner(graph, GetGraph()->GetAllocator(), GetGraph()->GetLocalAllocator()).CloneGraph();
1941         EXPECT_TRUE(graph->RunPass<Lowering>());
1942         ASSERT_TRUE(GraphComparator().Compare(graph, clone));
1943     }
1944 }
1945 
TEST_F(LoweringTest,AddSwapInputs)1946 TEST_F(LoweringTest, AddSwapInputs)
1947 {
1948     GRAPH(GetGraph())
1949     {
1950         PARAMETER(0, 0).s64();
1951         CONSTANT(1, 5);
1952 
1953         BASIC_BLOCK(2, -1)
1954         {
1955             INST(2, Opcode::Add).s64().Inputs(1, 0);
1956             INST(3, Opcode::Return).s64().Inputs(2);
1957         }
1958     }
1959     EXPECT_TRUE(GetGraph()->RunPass<Lowering>());
1960     EXPECT_TRUE(GetGraph()->RunPass<Cleanup>());
1961 
1962     auto graph_expected = CreateEmptyGraph();
1963     GRAPH(graph_expected)
1964     {
1965         PARAMETER(0, 0).s64();
1966         BASIC_BLOCK(2, -1)
1967         {
1968             INST(2, Opcode::AddI).s64().Inputs(0).Imm(5);
1969             INST(3, Opcode::Return).s64().Inputs(2);
1970         }
1971     }
1972 
1973     ASSERT_TRUE(GraphComparator().Compare(GetGraph(), graph_expected));
1974 }
1975 
1976 // Not applied, sub isn't commutative inst
TEST_F(LoweringTest,SubSwapInputs)1977 TEST_F(LoweringTest, SubSwapInputs)
1978 {
1979     GRAPH(GetGraph())
1980     {
1981         PARAMETER(0, 0).s64();
1982         CONSTANT(1, 5);
1983 
1984         BASIC_BLOCK(2, -1)
1985         {
1986             INST(2, Opcode::Sub).s64().Inputs(1, 0);
1987             INST(3, Opcode::Return).s64().Inputs(2);
1988         }
1989     }
1990     auto clone = GraphCloner(GetGraph(), GetGraph()->GetAllocator(), GetGraph()->GetLocalAllocator()).CloneGraph();
1991     ASSERT_TRUE(GetGraph()->RunPass<Lowering>());
1992     ASSERT_TRUE(GraphComparator().Compare(GetGraph(), clone));
1993 }
1994 
TEST_F(LoweringTest,DeoptimizeCompare)1995 TEST_F(LoweringTest, DeoptimizeCompare)
1996 {
1997     // Check if Compare + DeoptimizeIf ===> DeoptimizeCompare/DeoptimizeCompareImm transformations are applied.
1998     GRAPH(GetGraph())
1999     {
2000         PARAMETER(0, 0).ref();
2001         PARAMETER(1, 1).s32();
2002         CONSTANT(2, 10).s32();
2003         CONSTANT(3, nullptr).ref();
2004         BASIC_BLOCK(2, 1)
2005         {
2006             INST(5, Opcode::LoadArray).ref().Inputs(0, 1);
2007             INST(7, Opcode::SaveStateDeoptimize).Inputs(0, 1).SrcVregs({0, 1});
2008             INST(8, Opcode::Compare).b().CC(ConditionCode::CC_EQ).Inputs(5, 3);
2009             INST(9, Opcode::DeoptimizeIf).Inputs(8, 7);
2010             INST(10, Opcode::LenArray).s32().Inputs(5);
2011             INST(11, Opcode::Compare).b().CC(ConditionCode::CC_GT).Inputs(2, 10);
2012             INST(12, Opcode::DeoptimizeIf).Inputs(11, 7);
2013             INST(13, Opcode::Return).ref().Inputs(5);
2014         }
2015     }
2016     ASSERT_TRUE(GetGraph()->RunPass<Lowering>());
2017     ASSERT_TRUE(GetGraph()->RunPass<Cleanup>());
2018     auto graph = CreateEmptyGraph();
2019     GRAPH(graph)
2020     {
2021         PARAMETER(0, 0).ref();
2022         PARAMETER(1, 1).s32();
2023         CONSTANT(2, 10).s32();
2024         BASIC_BLOCK(2, 1)
2025         {
2026             INST(5, Opcode::LoadArray).ref().Inputs(0, 1);
2027             INST(7, Opcode::SaveStateDeoptimize).Inputs(0, 1).SrcVregs({0, 1});
2028             INST(10, Opcode::DeoptimizeCompareImm).CC(ConditionCode::CC_EQ).Imm(0).Inputs(5, 7);
2029             INST(11, Opcode::LenArray).s32().Inputs(5);
2030             INST(14, Opcode::DeoptimizeCompare).CC(ConditionCode::CC_GT).Inputs(2, 11, 7);
2031             INST(15, Opcode::Return).ref().Inputs(5);
2032         }
2033     }
2034     ASSERT_TRUE(GraphComparator().Compare(GetGraph(), graph));
2035 }
2036 
TEST_F(LoweringTest,DeoptimizeCompareImmDoesNotFit)2037 TEST_F(LoweringTest, DeoptimizeCompareImmDoesNotFit)
2038 {
2039     auto graph = CreateEmptyGraph(RUNTIME_ARCH);
2040 #ifndef NDEBUG
2041     graph->SetLowLevelInstructionsEnabled();
2042 #endif
2043     if (graph->GetArch() == Arch::AARCH32 || graph->GetArch() == Arch::AARCH64) {
2044         GRAPH(graph)
2045         {
2046             PARAMETER(0, 0).ref();
2047             PARAMETER(1, 1).s32();
2048             CONSTANT(2, INT_MAX).s32();
2049             BASIC_BLOCK(2, 1)
2050             {
2051                 INST(5, Opcode::LoadArray).ref().Inputs(0, 1);
2052                 INST(6, Opcode::SaveStateDeoptimize).Inputs(0, 1).SrcVregs({0, 1});
2053                 INST(7, Opcode::LenArray).s32().Inputs(5);
2054                 INST(8, Opcode::Compare).b().CC(ConditionCode::CC_GT).Inputs(7, 2);
2055                 INST(9, Opcode::DeoptimizeIf).Inputs(8, 6);
2056                 INST(10, Opcode::Return).ref().Inputs(5);
2057             }
2058         }
2059     } else if (graph->GetArch() == Arch::X86_64) {
2060         GRAPH(graph)
2061         {
2062             PARAMETER(0, 0).ref();
2063             PARAMETER(1, 1).s32();
2064             CONSTANT(2, LONG_MAX).s64();
2065             BASIC_BLOCK(2, 1)
2066             {
2067                 INST(5, Opcode::LoadArray).ref().Inputs(0, 1);
2068                 INST(6, Opcode::SaveStateDeoptimize).Inputs(0, 1).SrcVregs({0, 1});
2069                 INST(7, Opcode::LenArray).s32().Inputs(5);
2070                 INST(8, Opcode::Cast).s64().SrcType(compiler::DataType::INT32).Inputs(7);
2071                 INST(9, Opcode::Compare).b().CC(ConditionCode::CC_GT).Inputs(8, 2);
2072                 INST(10, Opcode::DeoptimizeIf).Inputs(9, 6);
2073                 INST(11, Opcode::Return).ref().Inputs(5);
2074             }
2075         }
2076     } else {
2077         UNREACHABLE();
2078     }
2079     ASSERT_TRUE(graph->RunPass<Lowering>());
2080     ASSERT_TRUE(graph->RunPass<Cleanup>());
2081 
2082     auto graphExpected = CreateEmptyGraph(graph->GetArch());
2083     if (graphExpected->GetArch() == Arch::AARCH32 || graphExpected->GetArch() == Arch::AARCH64) {
2084         GRAPH(graphExpected)
2085         {
2086             PARAMETER(0, 0).ref();
2087             PARAMETER(1, 1).s32();
2088             CONSTANT(2, INT_MAX).s32();
2089             BASIC_BLOCK(2, 1)
2090             {
2091                 INST(5, Opcode::LoadArray).ref().Inputs(0, 1);
2092                 INST(6, Opcode::SaveStateDeoptimize).Inputs(0, 1).SrcVregs({0, 1});
2093                 INST(7, Opcode::LenArray).s32().Inputs(5);
2094                 INST(9, Opcode::DeoptimizeCompare).CC(ConditionCode::CC_GT).Inputs(7, 2, 6);
2095                 INST(10, Opcode::Return).ref().Inputs(5);
2096             }
2097         }
2098     } else if (graphExpected->GetArch() == Arch::X86_64) {
2099         GRAPH(graphExpected)
2100         {
2101             PARAMETER(0, 0).ref();
2102             PARAMETER(1, 1).s32();
2103             CONSTANT(2, LONG_MAX).s64();
2104             BASIC_BLOCK(2, 1)
2105             {
2106                 INST(5, Opcode::LoadArray).ref().Inputs(0, 1);
2107                 INST(6, Opcode::SaveStateDeoptimize).Inputs(0, 1).SrcVregs({0, 1});
2108                 INST(7, Opcode::LenArray).s32().Inputs(5);
2109                 INST(8, Opcode::Cast).s64().SrcType(compiler::DataType::INT32).Inputs(7);
2110                 INST(10, Opcode::DeoptimizeCompare).CC(ConditionCode::CC_GT).Inputs(8, 2, 6);
2111                 INST(11, Opcode::Return).ref().Inputs(5);
2112             }
2113         }
2114     } else {
2115         UNREACHABLE();
2116     }
2117     ASSERT_TRUE(GraphComparator().Compare(graph, graphExpected));
2118 }
2119 
TEST_F(LoweringTest,LowerMoveScaleInLoadStore)2120 TEST_F(LoweringTest, LowerMoveScaleInLoadStore)
2121 {
2122     auto graph = CreateEmptyFastpathGraph(RUNTIME_ARCH);
2123     if (graph->GetArch() == Arch::AARCH32) {
2124         GTEST_SKIP() << "The optimization isn't supported on Aarch32";
2125     }
2126     GRAPH(graph)
2127     {
2128         PARAMETER(0, 0).ptr();
2129         PARAMETER(1, 1).ptr();
2130         PARAMETER(2, 2).u64();
2131         PARAMETER(80, 3).u64();
2132         PARAMETER(3, 4).u32();
2133         CONSTANT(4, 1);
2134         CONSTANT(5, 2);
2135         CONSTANT(6, 3);
2136         CONSTANT(7, 4);
2137 
2138         BASIC_BLOCK(2, -1)
2139         {
2140             INST(8, Opcode::Shl).u64().Inputs(2, 4);
2141             INST(9, Opcode::Load).u64().Inputs(0, 8);
2142             INST(10, Opcode::Store).u64().Inputs(1, 8, 9);
2143             INST(11, Opcode::Shl).u64().Inputs(2, 5);
2144             INST(12, Opcode::Load).u64().Inputs(0, 11);
2145             INST(13, Opcode::Store).u64().Inputs(1, 11, 12);
2146             INST(14, Opcode::Shl).u64().Inputs(2, 6);
2147             INST(15, Opcode::Load).u64().Inputs(0, 14);
2148             INST(16, Opcode::Store).u64().Inputs(1, 14, 15);
2149             INST(17, Opcode::Shl).u64().Inputs(2, 7);
2150             INST(18, Opcode::Load).u64().Inputs(0, 17);
2151             INST(19, Opcode::Store).u64().Inputs(1, 17, 18);
2152             INST(20, Opcode::Shl).u64().Inputs(80, 4);
2153             INST(21, Opcode::Load).u16().Inputs(0, 20);
2154             INST(22, Opcode::Store).u16().Inputs(1, 20, 21);
2155             INST(23, Opcode::Shl).u64().Inputs(80, 5);
2156             INST(24, Opcode::Load).u32().Inputs(0, 23);
2157             INST(25, Opcode::Store).u32().Inputs(1, 23, 24);
2158             INST(26, Opcode::Shl).u32().Inputs(3, 6);
2159             INST(27, Opcode::Load).u64().Inputs(0, 26);
2160             INST(28, Opcode::Store).u64().Inputs(1, 26, 27);
2161             INST(90, Opcode::ReturnVoid).v0id();
2162         }
2163     }
2164     EXPECT_TRUE(graph->RunPass<Lowering>());
2165     EXPECT_TRUE(graph->RunPass<Cleanup>());
2166 
2167     auto graph_expected = CreateEmptyFastpathGraph(RUNTIME_ARCH);
2168     if (graph->GetArch() == Arch::AARCH64) {
2169         GRAPH(graph_expected)
2170         {
2171             PARAMETER(0, 0).ptr();
2172             PARAMETER(1, 1).ptr();
2173             PARAMETER(2, 2).u64();
2174             PARAMETER(80, 3).u64();
2175             PARAMETER(3, 4).u32();
2176 
2177             BASIC_BLOCK(2, -1)
2178             {
2179                 INST(8, Opcode::ShlI).u64().Inputs(2).Imm(1);
2180                 INST(9, Opcode::Load).u64().Inputs(0, 8);
2181                 INST(10, Opcode::Store).u64().Inputs(1, 8, 9);
2182                 INST(11, Opcode::ShlI).u64().Inputs(2).Imm(2);
2183                 INST(12, Opcode::Load).u64().Inputs(0, 11);
2184                 INST(13, Opcode::Store).u64().Inputs(1, 11, 12);
2185                 INST(15, Opcode::Load).u64().Inputs(0, 2).Scale(3);
2186                 INST(16, Opcode::Store).u64().Inputs(1, 2, 15).Scale(3);
2187                 INST(17, Opcode::ShlI).u64().Inputs(2).Imm(4);
2188                 INST(18, Opcode::Load).u64().Inputs(0, 17);
2189                 INST(19, Opcode::Store).u64().Inputs(1, 17, 18);
2190                 INST(21, Opcode::Load).u16().Inputs(0, 80).Scale(1);
2191                 INST(22, Opcode::Store).u16().Inputs(1, 80, 21).Scale(1);
2192                 INST(24, Opcode::Load).u32().Inputs(0, 80).Scale(2);
2193                 INST(25, Opcode::Store).u32().Inputs(1, 80, 24).Scale(2);
2194                 INST(26, Opcode::ShlI).u32().Inputs(3).Imm(3);
2195                 INST(27, Opcode::Load).u64().Inputs(0, 26);
2196                 INST(28, Opcode::Store).u64().Inputs(1, 26, 27);
2197                 INST(90, Opcode::ReturnVoid).v0id();
2198             }
2199         }
2200     } else {
2201         ASSERT(graph->GetArch() == Arch::X86_64);
2202         GRAPH(graph_expected)
2203         {
2204             PARAMETER(0, 0).ptr();
2205             PARAMETER(1, 1).ptr();
2206             PARAMETER(2, 2).u64();
2207             PARAMETER(80, 3).u64();
2208             PARAMETER(3, 4).u32();
2209 
2210             BASIC_BLOCK(2, -1)
2211             {
2212                 INST(9, Opcode::Load).u64().Inputs(0, 2).Scale(1);
2213                 INST(10, Opcode::Store).u64().Inputs(1, 2, 9).Scale(1);
2214                 INST(12, Opcode::Load).u64().Inputs(0, 2).Scale(2);
2215                 INST(13, Opcode::Store).u64().Inputs(1, 2, 12).Scale(2);
2216                 INST(15, Opcode::Load).u64().Inputs(0, 2).Scale(3);
2217                 INST(16, Opcode::Store).u64().Inputs(1, 2, 15).Scale(3);
2218                 INST(17, Opcode::ShlI).u64().Inputs(2).Imm(4);
2219                 INST(18, Opcode::Load).u64().Inputs(0, 17);
2220                 INST(19, Opcode::Store).u64().Inputs(1, 17, 18);
2221                 INST(21, Opcode::Load).u16().Inputs(0, 80).Scale(1);
2222                 INST(22, Opcode::Store).u16().Inputs(1, 80, 21).Scale(1);
2223                 INST(24, Opcode::Load).u32().Inputs(0, 80).Scale(2);
2224                 INST(25, Opcode::Store).u32().Inputs(1, 80, 24).Scale(2);
2225                 INST(26, Opcode::ShlI).u32().Inputs(3).Imm(3);
2226                 INST(27, Opcode::Load).u64().Inputs(0, 26);
2227                 INST(28, Opcode::Store).u64().Inputs(1, 26, 27);
2228                 INST(90, Opcode::ReturnVoid).v0id();
2229             }
2230         }
2231     }
2232     ASSERT_TRUE(GraphComparator().Compare(graph, graph_expected));
2233 
2234     EXPECT_TRUE(RegAlloc(graph));
2235     EXPECT_TRUE(graph->RunPass<Codegen>());
2236 }
2237 
TEST_F(LoweringTest,LowerUnsignedCast)2238 TEST_F(LoweringTest, LowerUnsignedCast)
2239 {
2240     auto graph = CreateEmptyFastpathGraph(RUNTIME_ARCH);
2241     GRAPH(graph)
2242     {
2243         PARAMETER(0, 0).ptr();
2244         PARAMETER(1, 1).u64();
2245 
2246         BASIC_BLOCK(2, -1)
2247         {
2248             INST(2, Opcode::Load).u8().Inputs(0, 1);
2249             INST(3, Opcode::Cast).u64().SrcType(DataType::UINT8).Inputs(2);
2250             INST(4, Opcode::Load).u16().Inputs(0, 3);
2251             INST(5, Opcode::Cast).u64().SrcType(DataType::UINT16).Inputs(4);
2252             INST(6, Opcode::Load).u32().Inputs(0, 5);
2253             INST(7, Opcode::Cast).u64().SrcType(DataType::UINT32).Inputs(6);
2254             INST(8, Opcode::Load).f32().Inputs(0, 7);
2255             INST(9, Opcode::Cast).u64().SrcType(DataType::FLOAT32).Inputs(8);
2256             INST(10, Opcode::Load).s8().Inputs(0, 9);
2257             INST(11, Opcode::Cast).u64().SrcType(DataType::INT8).Inputs(10);
2258             INST(12, Opcode::Load).u8().Inputs(0, 11);
2259             INST(13, Opcode::Cast).u32().SrcType(DataType::UINT8).Inputs(12);
2260             INST(14, Opcode::Load).u64().Inputs(0, 13);
2261             INST(15, Opcode::Cast).u32().SrcType(DataType::UINT64).Inputs(14);
2262             INST(16, Opcode::Mul).u8().Inputs(15, 15);
2263             INST(17, Opcode::Cast).u64().SrcType(DataType::UINT8).Inputs(16);
2264             INST(18, Opcode::Return).u64().Inputs(17);
2265         }
2266     }
2267 
2268     auto graph_expected = CreateEmptyFastpathGraph(RUNTIME_ARCH);
2269     if (graph->GetArch() == Arch::AARCH64) {
2270         GRAPH(graph_expected)
2271         {
2272             PARAMETER(0, 0).ptr();
2273             PARAMETER(1, 1).u64();
2274 
2275             BASIC_BLOCK(2, -1)
2276             {
2277                 INST(2, Opcode::Load).u8().Inputs(0, 1);
2278                 INST(4, Opcode::Load).u16().Inputs(0, 2);
2279                 INST(6, Opcode::Load).u32().Inputs(0, 4);
2280                 INST(8, Opcode::Load).f32().Inputs(0, 6);
2281                 INST(9, Opcode::Cast).u64().SrcType(DataType::FLOAT32).Inputs(8);
2282                 INST(10, Opcode::Load).s8().Inputs(0, 9);
2283                 INST(11, Opcode::Cast).u64().SrcType(DataType::INT8).Inputs(10);
2284                 INST(12, Opcode::Load).u8().Inputs(0, 11);
2285                 INST(14, Opcode::Load).u64().Inputs(0, 12);
2286                 INST(15, Opcode::Cast).u32().SrcType(DataType::UINT64).Inputs(14);
2287                 INST(16, Opcode::Mul).u8().Inputs(15, 15);
2288                 INST(17, Opcode::Cast).u64().SrcType(DataType::UINT8).Inputs(16);
2289                 INST(18, Opcode::Return).u64().Inputs(17);
2290             }
2291         }
2292     } else {
2293         graph_expected = GraphCloner(graph, graph->GetAllocator(), graph->GetLocalAllocator()).CloneGraph();
2294     }
2295     EXPECT_TRUE(graph->RunPass<Lowering>());
2296     if (graph->GetArch() == Arch::AARCH64) {
2297         EXPECT_TRUE(graph->RunPass<Cleanup>());
2298     } else {
2299         EXPECT_FALSE(graph->RunPass<Cleanup>());
2300     }
2301     ASSERT_TRUE(GraphComparator().Compare(graph, graph_expected));
2302 
2303     EXPECT_TRUE(RegAlloc(graph));
2304     EXPECT_TRUE(graph->RunPass<Codegen>());
2305 }
2306 }  // namespace panda::compiler
2307