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