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