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