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