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