1 /*
2 * Copyright (C) 2014 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include <functional>
18 #include <memory>
19
20 #include "base/macros.h"
21 #include "base/utils.h"
22 #include "builder.h"
23 #include "codegen_test_utils.h"
24 #include "dex/dex_file.h"
25 #include "dex/dex_instruction.h"
26 #include "driver/compiler_options.h"
27 #include "nodes.h"
28 #include "optimizing_unit_test.h"
29 #include "register_allocator_linear_scan.h"
30 #include "utils/arm/assembler_arm_vixl.h"
31 #include "utils/arm/managed_register_arm.h"
32 #include "utils/x86/managed_register_x86.h"
33
34 #include "gtest/gtest.h"
35
36 namespace art HIDDEN {
37
38 // Return all combinations of ISA and code generator that are executable on
39 // hardware, or on simulator, and that we'd like to test.
GetTargetConfigs()40 static ::std::vector<CodegenTargetConfig> GetTargetConfigs() {
41 ::std::vector<CodegenTargetConfig> v;
42 ::std::vector<CodegenTargetConfig> test_config_candidates = {
43 #ifdef ART_ENABLE_CODEGEN_arm
44 // TODO: Should't this be `kThumb2` instead of `kArm` here?
45 CodegenTargetConfig(InstructionSet::kArm, create_codegen_arm_vixl32),
46 #endif
47 #ifdef ART_ENABLE_CODEGEN_arm64
48 CodegenTargetConfig(InstructionSet::kArm64, create_codegen_arm64),
49 #endif
50 #ifdef ART_ENABLE_CODEGEN_x86
51 CodegenTargetConfig(InstructionSet::kX86, create_codegen_x86),
52 #endif
53 #ifdef ART_ENABLE_CODEGEN_x86_64
54 CodegenTargetConfig(InstructionSet::kX86_64, create_codegen_x86_64),
55 #endif
56 };
57
58 for (const CodegenTargetConfig& test_config : test_config_candidates) {
59 if (CanExecuteISA(test_config.GetInstructionSet())) {
60 v.push_back(test_config);
61 }
62 }
63
64 return v;
65 }
66
67 class CodegenTest : public CommonCompilerTest, public OptimizingUnitTestHelper {
68 protected:
69 void TestCode(const std::vector<uint16_t>& data, bool has_result = false, int32_t expected = 0);
70 void TestCodeLong(const std::vector<uint16_t>& data, bool has_result, int64_t expected);
71 void TestComparison(IfCondition condition,
72 int64_t i,
73 int64_t j,
74 DataType::Type type,
75 const CodegenTargetConfig target_config);
76 void TestPackedSwitch(const CodegenTargetConfig target_config);
77 void TestVectorComparison(IfCondition condition,
78 int64_t lhs_value,
79 int64_t rhs_value,
80 DataType::Type type,
81 CodeGenerator* codegen);
82 };
83
TestCode(const std::vector<uint16_t> & data,bool has_result,int32_t expected)84 void CodegenTest::TestCode(const std::vector<uint16_t>& data, bool has_result, int32_t expected) {
85 for (const CodegenTargetConfig& target_config : GetTargetConfigs()) {
86 ResetPoolAndAllocator();
87 HGraph* graph = CreateCFG(data);
88 // Remove suspend checks, they cannot be executed in this context.
89 RemoveSuspendChecks(graph);
90 std::unique_ptr<CompilerOptions> compiler_options =
91 CommonCompilerTest::CreateCompilerOptions(target_config.GetInstructionSet(), "default");
92 RunCode(target_config, *compiler_options, graph, [](HGraph*) {}, has_result, expected);
93 }
94 }
95
TestCodeLong(const std::vector<uint16_t> & data,bool has_result,int64_t expected)96 void CodegenTest::TestCodeLong(const std::vector<uint16_t>& data,
97 bool has_result, int64_t expected) {
98 for (const CodegenTargetConfig& target_config : GetTargetConfigs()) {
99 ResetPoolAndAllocator();
100 HGraph* graph = CreateCFG(data, DataType::Type::kInt64);
101 // Remove suspend checks, they cannot be executed in this context.
102 RemoveSuspendChecks(graph);
103 std::unique_ptr<CompilerOptions> compiler_options =
104 CommonCompilerTest::CreateCompilerOptions(target_config.GetInstructionSet(), "default");
105 RunCode(target_config, *compiler_options, graph, [](HGraph*) {}, has_result, expected);
106 }
107 }
108
TEST_F(CodegenTest,ReturnVoid)109 TEST_F(CodegenTest, ReturnVoid) {
110 const std::vector<uint16_t> data = ZERO_REGISTER_CODE_ITEM(Instruction::RETURN_VOID);
111 TestCode(data);
112 }
113
TEST_F(CodegenTest,CFG1)114 TEST_F(CodegenTest, CFG1) {
115 const std::vector<uint16_t> data = ZERO_REGISTER_CODE_ITEM(
116 Instruction::GOTO | 0x100,
117 Instruction::RETURN_VOID);
118
119 TestCode(data);
120 }
121
TEST_F(CodegenTest,CFG2)122 TEST_F(CodegenTest, CFG2) {
123 const std::vector<uint16_t> data = ZERO_REGISTER_CODE_ITEM(
124 Instruction::GOTO | 0x100,
125 Instruction::GOTO | 0x100,
126 Instruction::RETURN_VOID);
127
128 TestCode(data);
129 }
130
TEST_F(CodegenTest,CFG3)131 TEST_F(CodegenTest, CFG3) {
132 const std::vector<uint16_t> data1 = ZERO_REGISTER_CODE_ITEM(
133 Instruction::GOTO | 0x200,
134 Instruction::RETURN_VOID,
135 Instruction::GOTO | 0xFF00);
136
137 TestCode(data1);
138
139 const std::vector<uint16_t> data2 = ZERO_REGISTER_CODE_ITEM(
140 Instruction::GOTO_16, 3,
141 Instruction::RETURN_VOID,
142 Instruction::GOTO_16, 0xFFFF);
143
144 TestCode(data2);
145
146 const std::vector<uint16_t> data3 = ZERO_REGISTER_CODE_ITEM(
147 Instruction::GOTO_32, 4, 0,
148 Instruction::RETURN_VOID,
149 Instruction::GOTO_32, 0xFFFF, 0xFFFF);
150
151 TestCode(data3);
152 }
153
TEST_F(CodegenTest,CFG4)154 TEST_F(CodegenTest, CFG4) {
155 const std::vector<uint16_t> data = ZERO_REGISTER_CODE_ITEM(
156 Instruction::RETURN_VOID,
157 Instruction::GOTO | 0x100,
158 Instruction::GOTO | 0xFE00);
159
160 TestCode(data);
161 }
162
TEST_F(CodegenTest,CFG5)163 TEST_F(CodegenTest, CFG5) {
164 const std::vector<uint16_t> data = ONE_REGISTER_CODE_ITEM(
165 Instruction::CONST_4 | 0 | 0,
166 Instruction::IF_EQ, 3,
167 Instruction::GOTO | 0x100,
168 Instruction::RETURN_VOID);
169
170 TestCode(data);
171 }
172
TEST_F(CodegenTest,IntConstant)173 TEST_F(CodegenTest, IntConstant) {
174 const std::vector<uint16_t> data = ONE_REGISTER_CODE_ITEM(
175 Instruction::CONST_4 | 0 | 0,
176 Instruction::RETURN_VOID);
177
178 TestCode(data);
179 }
180
TEST_F(CodegenTest,Return1)181 TEST_F(CodegenTest, Return1) {
182 const std::vector<uint16_t> data = ONE_REGISTER_CODE_ITEM(
183 Instruction::CONST_4 | 0 | 0,
184 Instruction::RETURN | 0);
185
186 TestCode(data, true, 0);
187 }
188
TEST_F(CodegenTest,Return2)189 TEST_F(CodegenTest, Return2) {
190 const std::vector<uint16_t> data = TWO_REGISTERS_CODE_ITEM(
191 Instruction::CONST_4 | 0 | 0,
192 Instruction::CONST_4 | 0 | 1 << 8,
193 Instruction::RETURN | 1 << 8);
194
195 TestCode(data, true, 0);
196 }
197
TEST_F(CodegenTest,Return3)198 TEST_F(CodegenTest, Return3) {
199 const std::vector<uint16_t> data = TWO_REGISTERS_CODE_ITEM(
200 Instruction::CONST_4 | 0 | 0,
201 Instruction::CONST_4 | 1 << 8 | 1 << 12,
202 Instruction::RETURN | 1 << 8);
203
204 TestCode(data, true, 1);
205 }
206
TEST_F(CodegenTest,ReturnIf1)207 TEST_F(CodegenTest, ReturnIf1) {
208 const std::vector<uint16_t> data = TWO_REGISTERS_CODE_ITEM(
209 Instruction::CONST_4 | 0 | 0,
210 Instruction::CONST_4 | 1 << 8 | 1 << 12,
211 Instruction::IF_EQ, 3,
212 Instruction::RETURN | 0 << 8,
213 Instruction::RETURN | 1 << 8);
214
215 TestCode(data, true, 1);
216 }
217
TEST_F(CodegenTest,ReturnIf2)218 TEST_F(CodegenTest, ReturnIf2) {
219 const std::vector<uint16_t> data = TWO_REGISTERS_CODE_ITEM(
220 Instruction::CONST_4 | 0 | 0,
221 Instruction::CONST_4 | 1 << 8 | 1 << 12,
222 Instruction::IF_EQ | 0 << 4 | 1 << 8, 3,
223 Instruction::RETURN | 0 << 8,
224 Instruction::RETURN | 1 << 8);
225
226 TestCode(data, true, 0);
227 }
228
229 // Exercise bit-wise (one's complement) not-int instruction.
230 #define NOT_INT_TEST(TEST_NAME, INPUT, EXPECTED_OUTPUT) \
231 TEST_F(CodegenTest, TEST_NAME) { \
232 const int32_t input = INPUT; \
233 const uint16_t input_lo = Low16Bits(input); \
234 const uint16_t input_hi = High16Bits(input); \
235 const std::vector<uint16_t> data = TWO_REGISTERS_CODE_ITEM( \
236 Instruction::CONST | 0 << 8, input_lo, input_hi, \
237 Instruction::NOT_INT | 1 << 8 | 0 << 12 , \
238 Instruction::RETURN | 1 << 8); \
239 \
240 TestCode(data, true, EXPECTED_OUTPUT); \
241 }
242
243 NOT_INT_TEST(ReturnNotIntMinus2, -2, 1)
244 NOT_INT_TEST(ReturnNotIntMinus1, -1, 0)
245 NOT_INT_TEST(ReturnNotInt0, 0, -1)
246 NOT_INT_TEST(ReturnNotInt1, 1, -2)
247 NOT_INT_TEST(ReturnNotIntINT32_MIN, -2147483648, 2147483647) // (2^31) - 1
248 NOT_INT_TEST(ReturnNotIntINT32_MINPlus1, -2147483647, 2147483646) // (2^31) - 2
249 NOT_INT_TEST(ReturnNotIntINT32_MAXMinus1, 2147483646, -2147483647) // -(2^31) - 1
250 NOT_INT_TEST(ReturnNotIntINT32_MAX, 2147483647, -2147483648) // -(2^31)
251
252 #undef NOT_INT_TEST
253
254 // Exercise bit-wise (one's complement) not-long instruction.
255 #define NOT_LONG_TEST(TEST_NAME, INPUT, EXPECTED_OUTPUT) \
256 TEST_F(CodegenTest, TEST_NAME) { \
257 const int64_t input = INPUT; \
258 const uint16_t word0 = Low16Bits(Low32Bits(input)); /* LSW. */ \
259 const uint16_t word1 = High16Bits(Low32Bits(input)); \
260 const uint16_t word2 = Low16Bits(High32Bits(input)); \
261 const uint16_t word3 = High16Bits(High32Bits(input)); /* MSW. */ \
262 const std::vector<uint16_t> data = FOUR_REGISTERS_CODE_ITEM( \
263 Instruction::CONST_WIDE | 0 << 8, word0, word1, word2, word3, \
264 Instruction::NOT_LONG | 2 << 8 | 0 << 12, \
265 Instruction::RETURN_WIDE | 2 << 8); \
266 \
267 TestCodeLong(data, true, EXPECTED_OUTPUT); \
268 }
269
270 NOT_LONG_TEST(ReturnNotLongMinus2, INT64_C(-2), INT64_C(1))
271 NOT_LONG_TEST(ReturnNotLongMinus1, INT64_C(-1), INT64_C(0))
272 NOT_LONG_TEST(ReturnNotLong0, INT64_C(0), INT64_C(-1))
273 NOT_LONG_TEST(ReturnNotLong1, INT64_C(1), INT64_C(-2))
274
275 NOT_LONG_TEST(ReturnNotLongINT32_MIN,
276 INT64_C(-2147483648),
277 INT64_C(2147483647)) // (2^31) - 1
278 NOT_LONG_TEST(ReturnNotLongINT32_MINPlus1,
279 INT64_C(-2147483647),
280 INT64_C(2147483646)) // (2^31) - 2
281 NOT_LONG_TEST(ReturnNotLongINT32_MAXMinus1,
282 INT64_C(2147483646),
283 INT64_C(-2147483647)) // -(2^31) - 1
284 NOT_LONG_TEST(ReturnNotLongINT32_MAX,
285 INT64_C(2147483647),
286 INT64_C(-2147483648)) // -(2^31)
287
288 // Note that the C++ compiler won't accept
289 // INT64_C(-9223372036854775808) (that is, INT64_MIN) as a valid
290 // int64_t literal, so we use INT64_C(-9223372036854775807)-1 instead.
291 NOT_LONG_TEST(ReturnNotINT64_MIN,
292 INT64_C(-9223372036854775807)-1,
293 INT64_C(9223372036854775807)); // (2^63) - 1
294 NOT_LONG_TEST(ReturnNotINT64_MINPlus1,
295 INT64_C(-9223372036854775807),
296 INT64_C(9223372036854775806)); // (2^63) - 2
297 NOT_LONG_TEST(ReturnNotLongINT64_MAXMinus1,
298 INT64_C(9223372036854775806),
299 INT64_C(-9223372036854775807)); // -(2^63) - 1
300 NOT_LONG_TEST(ReturnNotLongINT64_MAX,
301 INT64_C(9223372036854775807),
302 INT64_C(-9223372036854775807)-1); // -(2^63)
303
304 #undef NOT_LONG_TEST
305
TEST_F(CodegenTest,IntToLongOfLongToInt)306 TEST_F(CodegenTest, IntToLongOfLongToInt) {
307 const int64_t input = INT64_C(4294967296); // 2^32
308 const uint16_t word0 = Low16Bits(Low32Bits(input)); // LSW.
309 const uint16_t word1 = High16Bits(Low32Bits(input));
310 const uint16_t word2 = Low16Bits(High32Bits(input));
311 const uint16_t word3 = High16Bits(High32Bits(input)); // MSW.
312 const std::vector<uint16_t> data = FIVE_REGISTERS_CODE_ITEM(
313 Instruction::CONST_WIDE | 0 << 8, word0, word1, word2, word3,
314 Instruction::CONST_WIDE | 2 << 8, 1, 0, 0, 0,
315 Instruction::ADD_LONG | 0, 0 << 8 | 2, // v0 <- 2^32 + 1
316 Instruction::LONG_TO_INT | 4 << 8 | 0 << 12,
317 Instruction::INT_TO_LONG | 2 << 8 | 4 << 12,
318 Instruction::RETURN_WIDE | 2 << 8);
319
320 TestCodeLong(data, true, 1);
321 }
322
TEST_F(CodegenTest,ReturnAdd1)323 TEST_F(CodegenTest, ReturnAdd1) {
324 const std::vector<uint16_t> data = TWO_REGISTERS_CODE_ITEM(
325 Instruction::CONST_4 | 3 << 12 | 0,
326 Instruction::CONST_4 | 4 << 12 | 1 << 8,
327 Instruction::ADD_INT, 1 << 8 | 0,
328 Instruction::RETURN);
329
330 TestCode(data, true, 7);
331 }
332
TEST_F(CodegenTest,ReturnAdd2)333 TEST_F(CodegenTest, ReturnAdd2) {
334 const std::vector<uint16_t> data = TWO_REGISTERS_CODE_ITEM(
335 Instruction::CONST_4 | 3 << 12 | 0,
336 Instruction::CONST_4 | 4 << 12 | 1 << 8,
337 Instruction::ADD_INT_2ADDR | 1 << 12,
338 Instruction::RETURN);
339
340 TestCode(data, true, 7);
341 }
342
TEST_F(CodegenTest,ReturnAdd3)343 TEST_F(CodegenTest, ReturnAdd3) {
344 const std::vector<uint16_t> data = ONE_REGISTER_CODE_ITEM(
345 Instruction::CONST_4 | 4 << 12 | 0 << 8,
346 Instruction::ADD_INT_LIT8, 3 << 8 | 0,
347 Instruction::RETURN);
348
349 TestCode(data, true, 7);
350 }
351
TEST_F(CodegenTest,ReturnAdd4)352 TEST_F(CodegenTest, ReturnAdd4) {
353 const std::vector<uint16_t> data = ONE_REGISTER_CODE_ITEM(
354 Instruction::CONST_4 | 4 << 12 | 0 << 8,
355 Instruction::ADD_INT_LIT16, 3,
356 Instruction::RETURN);
357
358 TestCode(data, true, 7);
359 }
360
TEST_F(CodegenTest,ReturnMulInt)361 TEST_F(CodegenTest, ReturnMulInt) {
362 const std::vector<uint16_t> data = TWO_REGISTERS_CODE_ITEM(
363 Instruction::CONST_4 | 3 << 12 | 0,
364 Instruction::CONST_4 | 4 << 12 | 1 << 8,
365 Instruction::MUL_INT, 1 << 8 | 0,
366 Instruction::RETURN);
367
368 TestCode(data, true, 12);
369 }
370
TEST_F(CodegenTest,ReturnMulInt2addr)371 TEST_F(CodegenTest, ReturnMulInt2addr) {
372 const std::vector<uint16_t> data = TWO_REGISTERS_CODE_ITEM(
373 Instruction::CONST_4 | 3 << 12 | 0,
374 Instruction::CONST_4 | 4 << 12 | 1 << 8,
375 Instruction::MUL_INT_2ADDR | 1 << 12,
376 Instruction::RETURN);
377
378 TestCode(data, true, 12);
379 }
380
TEST_F(CodegenTest,ReturnMulLong)381 TEST_F(CodegenTest, ReturnMulLong) {
382 const std::vector<uint16_t> data = FOUR_REGISTERS_CODE_ITEM(
383 Instruction::CONST_WIDE | 0 << 8, 3, 0, 0, 0,
384 Instruction::CONST_WIDE | 2 << 8, 4, 0, 0, 0,
385 Instruction::MUL_LONG, 2 << 8 | 0,
386 Instruction::RETURN_WIDE);
387
388 TestCodeLong(data, true, 12);
389 }
390
TEST_F(CodegenTest,ReturnMulLong2addr)391 TEST_F(CodegenTest, ReturnMulLong2addr) {
392 const std::vector<uint16_t> data = FOUR_REGISTERS_CODE_ITEM(
393 Instruction::CONST_WIDE | 0 << 8, 3, 0, 0, 0,
394 Instruction::CONST_WIDE | 2 << 8, 4, 0, 0, 0,
395 Instruction::MUL_LONG_2ADDR | 2 << 12,
396 Instruction::RETURN_WIDE);
397
398 TestCodeLong(data, true, 12);
399 }
400
TEST_F(CodegenTest,ReturnMulIntLit8)401 TEST_F(CodegenTest, ReturnMulIntLit8) {
402 const std::vector<uint16_t> data = ONE_REGISTER_CODE_ITEM(
403 Instruction::CONST_4 | 4 << 12 | 0 << 8,
404 Instruction::MUL_INT_LIT8, 3 << 8 | 0,
405 Instruction::RETURN);
406
407 TestCode(data, true, 12);
408 }
409
TEST_F(CodegenTest,ReturnMulIntLit16)410 TEST_F(CodegenTest, ReturnMulIntLit16) {
411 const std::vector<uint16_t> data = ONE_REGISTER_CODE_ITEM(
412 Instruction::CONST_4 | 4 << 12 | 0 << 8,
413 Instruction::MUL_INT_LIT16, 3,
414 Instruction::RETURN);
415
416 TestCode(data, true, 12);
417 }
418
TEST_F(CodegenTest,NonMaterializedCondition)419 TEST_F(CodegenTest, NonMaterializedCondition) {
420 for (CodegenTargetConfig target_config : GetTargetConfigs()) {
421 HGraph* graph = CreateGraph();
422
423 HBasicBlock* entry = new (GetAllocator()) HBasicBlock(graph);
424 graph->AddBlock(entry);
425 graph->SetEntryBlock(entry);
426 MakeGoto(entry);
427
428 HBasicBlock* first_block = new (GetAllocator()) HBasicBlock(graph);
429 graph->AddBlock(first_block);
430 entry->AddSuccessor(first_block);
431 HIntConstant* constant0 = graph->GetIntConstant(0);
432 HIntConstant* constant1 = graph->GetIntConstant(1);
433 HInstruction* equal = MakeCondition(first_block, kCondEQ, constant0, constant0);
434 MakeIf(first_block, equal);
435
436 HBasicBlock* then_block = new (GetAllocator()) HBasicBlock(graph);
437 HBasicBlock* else_block = new (GetAllocator()) HBasicBlock(graph);
438 HBasicBlock* exit_block = new (GetAllocator()) HBasicBlock(graph);
439 graph->SetExitBlock(exit_block);
440
441 graph->AddBlock(then_block);
442 graph->AddBlock(else_block);
443 graph->AddBlock(exit_block);
444 first_block->AddSuccessor(then_block);
445 first_block->AddSuccessor(else_block);
446 then_block->AddSuccessor(exit_block);
447 else_block->AddSuccessor(exit_block);
448
449 MakeExit(exit_block);
450 MakeReturn(then_block, constant0);
451 MakeReturn(else_block, constant1);
452
453 ASSERT_FALSE(equal->IsEmittedAtUseSite());
454 graph->BuildDominatorTree();
455 std::unique_ptr<CompilerOptions> compiler_options =
456 CommonCompilerTest::CreateCompilerOptions(target_config.GetInstructionSet(), "default");
457 PrepareForRegisterAllocation(graph, *compiler_options).Run();
458 ASSERT_TRUE(equal->IsEmittedAtUseSite());
459
460 auto hook_before_codegen = [](HGraph* graph_in) {
461 HBasicBlock* block = graph_in->GetEntryBlock()->GetSuccessors()[0];
462 HParallelMove* move = new (graph_in->GetAllocator()) HParallelMove(graph_in->GetAllocator());
463 block->InsertInstructionBefore(move, block->GetLastInstruction());
464 };
465
466 RunCode(target_config, *compiler_options, graph, hook_before_codegen, true, 0);
467 }
468 }
469
TEST_F(CodegenTest,MaterializedCondition1)470 TEST_F(CodegenTest, MaterializedCondition1) {
471 for (CodegenTargetConfig target_config : GetTargetConfigs()) {
472 // Check that condition are materialized correctly. A materialized condition
473 // should yield `1` if it evaluated to true, and `0` otherwise.
474 // We force the materialization of comparisons for different combinations of
475
476 // inputs and check the results.
477
478 int lhs[] = {1, 2, -1, 2, 0xabc};
479 int rhs[] = {2, 1, 2, -1, 0xabc};
480
481 for (size_t i = 0; i < arraysize(lhs); i++) {
482 HGraph* graph = CreateGraph();
483
484 HBasicBlock* entry_block = new (GetAllocator()) HBasicBlock(graph);
485 graph->AddBlock(entry_block);
486 graph->SetEntryBlock(entry_block);
487 MakeGoto(entry_block);
488 HBasicBlock* code_block = new (GetAllocator()) HBasicBlock(graph);
489 graph->AddBlock(code_block);
490 HBasicBlock* exit_block = new (GetAllocator()) HBasicBlock(graph);
491 graph->AddBlock(exit_block);
492 MakeExit(exit_block);
493
494 entry_block->AddSuccessor(code_block);
495 code_block->AddSuccessor(exit_block);
496 graph->SetExitBlock(exit_block);
497
498 HIntConstant* cst_lhs = graph->GetIntConstant(lhs[i]);
499 HIntConstant* cst_rhs = graph->GetIntConstant(rhs[i]);
500 HInstruction* cmp_lt = MakeCondition(code_block, kCondLT, cst_lhs, cst_rhs);
501 MakeReturn(code_block, cmp_lt);
502
503 graph->BuildDominatorTree();
504 auto hook_before_codegen = [](HGraph* graph_in) {
505 HBasicBlock* block = graph_in->GetEntryBlock()->GetSuccessors()[0];
506 HParallelMove* move =
507 new (graph_in->GetAllocator()) HParallelMove(graph_in->GetAllocator());
508 block->InsertInstructionBefore(move, block->GetLastInstruction());
509 };
510 std::unique_ptr<CompilerOptions> compiler_options =
511 CommonCompilerTest::CreateCompilerOptions(target_config.GetInstructionSet(), "default");
512 RunCode(target_config, *compiler_options, graph, hook_before_codegen, true, lhs[i] < rhs[i]);
513 }
514 }
515 }
516
TEST_F(CodegenTest,MaterializedCondition2)517 TEST_F(CodegenTest, MaterializedCondition2) {
518 for (CodegenTargetConfig target_config : GetTargetConfigs()) {
519 // Check that HIf correctly interprets a materialized condition.
520 // We force the materialization of comparisons for different combinations of
521 // inputs. An HIf takes the materialized combination as input and returns a
522 // value that we verify.
523
524 int lhs[] = {1, 2, -1, 2, 0xabc};
525 int rhs[] = {2, 1, 2, -1, 0xabc};
526
527
528 for (size_t i = 0; i < arraysize(lhs); i++) {
529 HGraph* graph = CreateGraph();
530
531 HBasicBlock* entry_block = new (GetAllocator()) HBasicBlock(graph);
532 graph->AddBlock(entry_block);
533 graph->SetEntryBlock(entry_block);
534 MakeGoto(entry_block);
535
536 HBasicBlock* if_block = new (GetAllocator()) HBasicBlock(graph);
537 graph->AddBlock(if_block);
538 HBasicBlock* if_true_block = new (GetAllocator()) HBasicBlock(graph);
539 graph->AddBlock(if_true_block);
540 HBasicBlock* if_false_block = new (GetAllocator()) HBasicBlock(graph);
541 graph->AddBlock(if_false_block);
542 HBasicBlock* exit_block = new (GetAllocator()) HBasicBlock(graph);
543 graph->AddBlock(exit_block);
544 MakeExit(exit_block);
545
546 graph->SetEntryBlock(entry_block);
547 entry_block->AddSuccessor(if_block);
548 if_block->AddSuccessor(if_true_block);
549 if_block->AddSuccessor(if_false_block);
550 if_true_block->AddSuccessor(exit_block);
551 if_false_block->AddSuccessor(exit_block);
552 graph->SetExitBlock(exit_block);
553
554 HIntConstant* cst_lhs = graph->GetIntConstant(lhs[i]);
555 HIntConstant* cst_rhs = graph->GetIntConstant(rhs[i]);
556 HInstruction* cmp_lt = MakeCondition(if_block, kCondLT, cst_lhs, cst_rhs);
557 // We insert a fake instruction to separate the HIf from the HLessThan
558 // and force the materialization of the condition.
559 HInstruction* force_materialization =
560 new (GetAllocator()) HMemoryBarrier(MemBarrierKind::kAnyAny, 0);
561 if_block->AddInstruction(force_materialization);
562 MakeIf(if_block, cmp_lt);
563
564 HIntConstant* cst_lt = graph->GetIntConstant(1);
565 MakeReturn(if_true_block, cst_lt);
566 HIntConstant* cst_ge = graph->GetIntConstant(0);
567 MakeReturn(if_false_block, cst_ge);
568
569 graph->BuildDominatorTree();
570 auto hook_before_codegen = [](HGraph* graph_in) {
571 HBasicBlock* block = graph_in->GetEntryBlock()->GetSuccessors()[0];
572 HParallelMove* move =
573 new (graph_in->GetAllocator()) HParallelMove(graph_in->GetAllocator());
574 block->InsertInstructionBefore(move, block->GetLastInstruction());
575 };
576 std::unique_ptr<CompilerOptions> compiler_options =
577 CommonCompilerTest::CreateCompilerOptions(target_config.GetInstructionSet(), "default");
578 RunCode(target_config, *compiler_options, graph, hook_before_codegen, true, lhs[i] < rhs[i]);
579 }
580 }
581 }
582
TEST_F(CodegenTest,ReturnDivIntLit8)583 TEST_F(CodegenTest, ReturnDivIntLit8) {
584 const std::vector<uint16_t> data = ONE_REGISTER_CODE_ITEM(
585 Instruction::CONST_4 | 4 << 12 | 0 << 8,
586 Instruction::DIV_INT_LIT8, 3 << 8 | 0,
587 Instruction::RETURN);
588
589 TestCode(data, true, 1);
590 }
591
TEST_F(CodegenTest,ReturnDivInt2Addr)592 TEST_F(CodegenTest, ReturnDivInt2Addr) {
593 const std::vector<uint16_t> data = TWO_REGISTERS_CODE_ITEM(
594 Instruction::CONST_4 | 4 << 12 | 0,
595 Instruction::CONST_4 | 2 << 12 | 1 << 8,
596 Instruction::DIV_INT_2ADDR | 1 << 12,
597 Instruction::RETURN);
598
599 TestCode(data, true, 2);
600 }
601
GetExpectedResultFromComparison(IfCondition condition,int64_t lhs,int64_t rhs)602 static bool GetExpectedResultFromComparison(IfCondition condition, int64_t lhs, int64_t rhs) {
603 const uint64_t unsigned_lhs = lhs;
604 const uint64_t unsigned_rhs = rhs;
605 switch (condition) {
606 case kCondEQ:
607 return lhs == rhs;
608 case kCondNE:
609 return lhs != rhs;
610 case kCondLT:
611 return lhs < rhs;
612 case kCondLE:
613 return lhs <= rhs;
614 case kCondGT:
615 return lhs > rhs;
616 case kCondGE:
617 return lhs >= rhs;
618 case kCondB:
619 return unsigned_lhs < unsigned_rhs;
620 case kCondBE:
621 return unsigned_lhs <= unsigned_rhs;
622 case kCondA:
623 return unsigned_lhs > unsigned_rhs;
624 case kCondAE:
625 return unsigned_lhs >= unsigned_rhs;
626 }
627 LOG(FATAL) << "Condition '" << enum_cast<uint32_t>(condition) << "' not supported: ";
628 UNREACHABLE();
629 }
630
631 // Helper method.
TestComparison(IfCondition condition,int64_t i,int64_t j,DataType::Type type,const CodegenTargetConfig target_config)632 void CodegenTest::TestComparison(IfCondition condition,
633 int64_t i,
634 int64_t j,
635 DataType::Type type,
636 const CodegenTargetConfig target_config) {
637 HBasicBlock* block = InitEntryMainExitGraph();
638
639 HInstruction* op1;
640 HInstruction* op2;
641 if (type == DataType::Type::kInt32) {
642 op1 = graph_->GetIntConstant(i);
643 op2 = graph_->GetIntConstant(j);
644 } else {
645 DCHECK_EQ(type, DataType::Type::kInt64);
646 op1 = graph_->GetLongConstant(i);
647 op2 = graph_->GetLongConstant(j);
648 }
649
650 HInstruction* comparison = MakeCondition(block, condition, op1, op2);
651 MakeReturn(block, comparison);
652
653 graph_->BuildDominatorTree();
654 std::unique_ptr<CompilerOptions> compiler_options =
655 CommonCompilerTest::CreateCompilerOptions(target_config.GetInstructionSet(), "default");
656 bool expected_result = GetExpectedResultFromComparison(condition, i, j);
657 RunCode(target_config, *compiler_options, graph_, [](HGraph*) {}, true, expected_result);
658 }
659
TEST_F(CodegenTest,ComparisonsInt)660 TEST_F(CodegenTest, ComparisonsInt) {
661 for (CodegenTargetConfig target_config : GetTargetConfigs()) {
662 for (int64_t i = -1; i <= 1; i++) {
663 for (int64_t j = -1; j <= 1; j++) {
664 for (int cond = kCondFirst; cond <= kCondLast; cond++) {
665 TestComparison(
666 static_cast<IfCondition>(cond), i, j, DataType::Type::kInt32, target_config);
667 }
668 }
669 }
670 }
671 }
672
TEST_F(CodegenTest,ComparisonsLong)673 TEST_F(CodegenTest, ComparisonsLong) {
674 for (CodegenTargetConfig target_config : GetTargetConfigs()) {
675 for (int64_t i = -1; i <= 1; i++) {
676 for (int64_t j = -1; j <= 1; j++) {
677 for (int cond = kCondFirst; cond <= kCondLast; cond++) {
678 TestComparison(
679 static_cast<IfCondition>(cond), i, j, DataType::Type::kInt64, target_config);
680 }
681 }
682 }
683 }
684 }
685
686 // Tests a PackedSwitch in a very large HGraph; validates that the switch jump table is in
687 // range for the PC-relative load in the codegen visitor.
TestPackedSwitch(const CodegenTargetConfig target_config)688 void CodegenTest::TestPackedSwitch(const CodegenTargetConfig target_config) {
689 HBasicBlock* return_block = InitEntryMainExitGraph();
690 constexpr DataType::Type data_type = DataType::Type::kInt32;
691
692 // A number of entries - we are interested to test jump table implementation.
693 constexpr size_t kNumSwitchEntries = 10;
694
695 // Number of jump targets (including a 'default' case).
696 constexpr size_t kNumBB = kNumSwitchEntries + 1;
697 // Some arbitrary value to be used as input.
698 constexpr int kInputValue = kNumBB - 4;
699
700 HInstruction* input = graph_->GetIntConstant(kInputValue);
701 HIntConstant* constant_1 = graph_->GetIntConstant(1);
702
703 HBasicBlock* switch_block = AddNewBlock();
704 entry_block_->ReplaceSuccessor(return_block, switch_block);
705
706 HPackedSwitch* hswitch = new (GetAllocator()) HPackedSwitch(0, kNumSwitchEntries, input);
707 switch_block->AddInstruction(hswitch);
708
709 std::vector<HInstruction*> phi_inputs {};
710
711 // Add switch jump target blocks.
712 for (int i = 0; i < kNumBB; i++) {
713 HBasicBlock* case_block = AddNewBlock();
714 case_block->AddPredecessor(switch_block);
715 case_block->AddSuccessor(return_block);
716
717 HIntConstant* case_value = graph_->GetIntConstant(i);
718 HAdd* add = MakeBinOp<HAdd>(case_block, data_type, input, case_value);
719 phi_inputs.emplace_back(add);
720
721 MakeGoto(case_block);
722 }
723
724 HPhi* phi = MakePhi(return_block, phi_inputs);
725 HInstruction* return_val = phi;
726
727 // Emit a huge number of HAdds - to simulate a very large HGraph.
728 constexpr int kNumOfAdds = 2 * 1024 * 1024;
729 for (int i = 0; i < kNumOfAdds; i++) {
730 return_val = MakeBinOp<HAdd>(return_block, data_type, return_val, constant_1);
731 }
732
733 MakeReturn(return_block, return_val);
734
735 graph_->BuildDominatorTree();
736 EXPECT_TRUE(CheckGraph());
737
738 std::unique_ptr<CompilerOptions> compiler_options =
739 CommonCompilerTest::CreateCompilerOptions(target_config.GetInstructionSet(), "default");
740 RunCode(target_config,
741 *compiler_options,
742 graph_,
743 [](HGraph*) {}, true, kNumOfAdds + 2 * kInputValue);
744 }
745
TEST_F(CodegenTest,PackedSwitchInHugeMethod)746 TEST_F(CodegenTest, PackedSwitchInHugeMethod) {
747 for (CodegenTargetConfig target_config : GetTargetConfigs()) {
748 TestPackedSwitch(target_config);
749 }
750 }
751
752 #ifdef ART_ENABLE_CODEGEN_arm
TEST_F(CodegenTest,ARMVIXLParallelMoveResolver)753 TEST_F(CodegenTest, ARMVIXLParallelMoveResolver) {
754 std::unique_ptr<CompilerOptions> compiler_options =
755 CommonCompilerTest::CreateCompilerOptions(InstructionSet::kThumb2, "default");
756 HGraph* graph = CreateGraph();
757 arm::CodeGeneratorARMVIXL codegen(graph, *compiler_options);
758
759 codegen.Initialize();
760
761 // This will result in calling EmitSwap -> void ParallelMoveResolverARMVIXL::Exchange(int mem1,
762 // int mem2) which was faulty (before the fix). So previously GPR and FP scratch registers were
763 // used as temps; however GPR scratch register is required for big stack offsets which don't fit
764 // LDR encoding. So the following code is a regression test for that situation.
765 HParallelMove* move = new (graph->GetAllocator()) HParallelMove(graph->GetAllocator());
766 move->AddMove(Location::StackSlot(0), Location::StackSlot(8192), DataType::Type::kInt32, nullptr);
767 move->AddMove(Location::StackSlot(8192), Location::StackSlot(0), DataType::Type::kInt32, nullptr);
768 codegen.GetMoveResolver()->EmitNativeCode(move);
769
770 codegen.Finalize();
771 }
772 #endif
773
774 #ifdef ART_ENABLE_CODEGEN_arm64
775 // Regression test for b/34760542.
TEST_F(CodegenTest,ARM64ParallelMoveResolverB34760542)776 TEST_F(CodegenTest, ARM64ParallelMoveResolverB34760542) {
777 std::unique_ptr<CompilerOptions> compiler_options =
778 CommonCompilerTest::CreateCompilerOptions(InstructionSet::kArm64, "default");
779 HGraph* graph = CreateGraph();
780 arm64::CodeGeneratorARM64 codegen(graph, *compiler_options);
781
782 codegen.Initialize();
783
784 // The following ParallelMove used to fail this assertion:
785 //
786 // Assertion failed (!available->IsEmpty())
787 //
788 // in vixl::aarch64::UseScratchRegisterScope::AcquireNextAvailable,
789 // because of the following situation:
790 //
791 // 1. a temp register (IP0) is allocated as a scratch register by
792 // the parallel move resolver to solve a cycle (swap):
793 //
794 // [ source=DS0 destination=DS257 type=PrimDouble instruction=null ]
795 // [ source=DS257 destination=DS0 type=PrimDouble instruction=null ]
796 //
797 // 2. within CodeGeneratorARM64::MoveLocation, another temp
798 // register (IP1) is allocated to generate the swap between two
799 // double stack slots;
800 //
801 // 3. VIXL requires a third temp register to emit the `Ldr` or
802 // `Str` operation from CodeGeneratorARM64::MoveLocation (as
803 // one of the stack slots' offsets cannot be encoded as an
804 // immediate), but the pool of (core) temp registers is now
805 // empty.
806 //
807 // The solution used so far is to use a floating-point temp register
808 // (D31) in step #2, so that IP1 is available for step #3.
809
810 HParallelMove* move = new (graph->GetAllocator()) HParallelMove(graph->GetAllocator());
811 move->AddMove(Location::DoubleStackSlot(0),
812 Location::DoubleStackSlot(257),
813 DataType::Type::kFloat64,
814 nullptr);
815 move->AddMove(Location::DoubleStackSlot(257),
816 Location::DoubleStackSlot(0),
817 DataType::Type::kFloat64,
818 nullptr);
819 codegen.GetMoveResolver()->EmitNativeCode(move);
820
821 codegen.Finalize();
822 }
823
824 // Check that ParallelMoveResolver works fine for ARM64 for both cases when SIMD is on and off.
TEST_F(CodegenTest,ARM64ParallelMoveResolverSIMD)825 TEST_F(CodegenTest, ARM64ParallelMoveResolverSIMD) {
826 std::unique_ptr<CompilerOptions> compiler_options =
827 CommonCompilerTest::CreateCompilerOptions(InstructionSet::kArm64, "default");
828 HGraph* graph = CreateGraph();
829 arm64::CodeGeneratorARM64 codegen(graph, *compiler_options);
830
831 codegen.Initialize();
832
833 graph->SetHasTraditionalSIMD(true);
834 for (int i = 0; i < 2; i++) {
835 HParallelMove* move = new (graph->GetAllocator()) HParallelMove(graph->GetAllocator());
836 move->AddMove(Location::SIMDStackSlot(0),
837 Location::SIMDStackSlot(257),
838 DataType::Type::kFloat64,
839 nullptr);
840 move->AddMove(Location::SIMDStackSlot(257),
841 Location::SIMDStackSlot(0),
842 DataType::Type::kFloat64,
843 nullptr);
844 move->AddMove(Location::FpuRegisterLocation(0),
845 Location::FpuRegisterLocation(1),
846 DataType::Type::kFloat64,
847 nullptr);
848 move->AddMove(Location::FpuRegisterLocation(1),
849 Location::FpuRegisterLocation(0),
850 DataType::Type::kFloat64,
851 nullptr);
852 codegen.GetMoveResolver()->EmitNativeCode(move);
853 graph->SetHasTraditionalSIMD(false);
854 }
855
856 codegen.Finalize();
857 }
858
859 // Check that ART ISA Features are propagated to VIXL for arm64 (using cortex-a75 as example).
TEST_F(CodegenTest,ARM64IsaVIXLFeaturesA75)860 TEST_F(CodegenTest, ARM64IsaVIXLFeaturesA75) {
861 std::unique_ptr<CompilerOptions> compiler_options =
862 CommonCompilerTest::CreateCompilerOptions(InstructionSet::kArm64, "cortex-a75");
863 HGraph* graph = CreateGraph();
864 arm64::CodeGeneratorARM64 codegen(graph, *compiler_options);
865 vixl::CPUFeatures* features = codegen.GetVIXLAssembler()->GetCPUFeatures();
866
867 EXPECT_TRUE(features->Has(vixl::CPUFeatures::kCRC32));
868 EXPECT_TRUE(features->Has(vixl::CPUFeatures::kDotProduct));
869 EXPECT_TRUE(features->Has(vixl::CPUFeatures::kFPHalf));
870 EXPECT_TRUE(features->Has(vixl::CPUFeatures::kNEONHalf));
871 EXPECT_TRUE(features->Has(vixl::CPUFeatures::kAtomics));
872 }
873
874 // Check that ART ISA Features are propagated to VIXL for arm64 (using cortex-a53 as example).
TEST_F(CodegenTest,ARM64IsaVIXLFeaturesA53)875 TEST_F(CodegenTest, ARM64IsaVIXLFeaturesA53) {
876 std::unique_ptr<CompilerOptions> compiler_options =
877 CommonCompilerTest::CreateCompilerOptions(InstructionSet::kArm64, "cortex-a53");
878 HGraph* graph = CreateGraph();
879 arm64::CodeGeneratorARM64 codegen(graph, *compiler_options);
880 vixl::CPUFeatures* features = codegen.GetVIXLAssembler()->GetCPUFeatures();
881
882 EXPECT_TRUE(features->Has(vixl::CPUFeatures::kCRC32));
883 EXPECT_FALSE(features->Has(vixl::CPUFeatures::kDotProduct));
884 EXPECT_FALSE(features->Has(vixl::CPUFeatures::kFPHalf));
885 EXPECT_FALSE(features->Has(vixl::CPUFeatures::kNEONHalf));
886 EXPECT_FALSE(features->Has(vixl::CPUFeatures::kAtomics));
887 }
888
889 constexpr static size_t kExpectedFPSpillSize = 8 * vixl::aarch64::kDRegSizeInBytes;
890
891 // The following two tests check that for both SIMD and non-SIMD graphs exactly 64-bit is
892 // allocated on stack per callee-saved FP register to be preserved in the frame entry as
893 // ABI states.
TEST_F(CodegenTest,ARM64FrameSizeSIMD)894 TEST_F(CodegenTest, ARM64FrameSizeSIMD) {
895 std::unique_ptr<CompilerOptions> compiler_options =
896 CommonCompilerTest::CreateCompilerOptions(InstructionSet::kArm64, "default");
897 HGraph* graph = CreateGraph();
898 arm64::CodeGeneratorARM64 codegen(graph, *compiler_options);
899
900 codegen.Initialize();
901 graph->SetHasTraditionalSIMD(true);
902
903 DCHECK_EQ(arm64::callee_saved_fp_registers.GetCount(), 8);
904 vixl::aarch64::CPURegList reg_list = arm64::callee_saved_fp_registers;
905 while (!reg_list.IsEmpty()) {
906 uint32_t reg_code = reg_list.PopLowestIndex().GetCode();
907 codegen.AddAllocatedRegister(Location::FpuRegisterLocation(reg_code));
908 }
909 codegen.ComputeSpillMask();
910
911 EXPECT_EQ(codegen.GetFpuSpillSize(), kExpectedFPSpillSize);
912 }
913
TEST_F(CodegenTest,ARM64FrameSizeNoSIMD)914 TEST_F(CodegenTest, ARM64FrameSizeNoSIMD) {
915 std::unique_ptr<CompilerOptions> compiler_options =
916 CommonCompilerTest::CreateCompilerOptions(InstructionSet::kArm64, "default");
917 HGraph* graph = CreateGraph();
918 arm64::CodeGeneratorARM64 codegen(graph, *compiler_options);
919
920 codegen.Initialize();
921 graph->SetHasTraditionalSIMD(false);
922 graph->SetHasPredicatedSIMD(false);
923
924 DCHECK_EQ(arm64::callee_saved_fp_registers.GetCount(), 8);
925 vixl::aarch64::CPURegList reg_list = arm64::callee_saved_fp_registers;
926 while (!reg_list.IsEmpty()) {
927 uint32_t reg_code = reg_list.PopLowestIndex().GetCode();
928 codegen.AddAllocatedRegister(Location::FpuRegisterLocation(reg_code));
929 }
930 codegen.ComputeSpillMask();
931
932 EXPECT_EQ(codegen.GetFpuSpillSize(), kExpectedFPSpillSize);
933 }
934
935 // This test checks that the result of the VecPredToBoolean instruction doesn't depend on
936 // conditional flags that can be updated by other instructions. For example:
937 //
938 // VecPredWhile p0, opa, opb
939 // Below opb, opa
940 // VecPredToBoolean p0
941 //
942 // where Below updates conditions flags after VecPredWhile.
TEST_F(CodegenTest,ARM64SvePredicateToBoolean)943 TEST_F(CodegenTest, ARM64SvePredicateToBoolean) {
944 std::unique_ptr<CompilerOptions> compiler_options =
945 CommonCompilerTest::CreateCompilerOptions(InstructionSet::kArm64, "default", "sve");
946 for (int i = 0; i < 2; i++) {
947 for (int j = 0; j < 2; j++) {
948 HBasicBlock* block = InitEntryMainExitGraph();
949 TestCodeGeneratorARM64 codegen(graph_, *compiler_options);
950 if (!codegen.SupportsPredicatedSIMD()) {
951 GTEST_SKIP() << "Predicated SIMD is not supported.";
952 }
953
954 HInstruction *opa = graph_->GetIntConstant(i);
955 HInstruction *opb = graph_->GetIntConstant(j);
956 HVecPredWhile *pred_while = MakeVecPredWhile(block,
957 opa,
958 opb,
959 HVecPredWhile::CondKind::kLO,
960 DataType::Type::kInt32);
961 // Update condition flags by using Below instruction.
962 MakeCondition(block, IfCondition::kCondB, opb, opa);
963 HVecPredToBoolean *boolean = MakeVecPredToBoolean(block,
964 pred_while,
965 HVecPredToBoolean::PCondKind::kNFirst,
966 DataType::Type::kInt32);
967 MakeReturn(block, boolean);
968
969 graph_->SetHasPredicatedSIMD(true);
970 graph_->BuildDominatorTree();
971
972 if (CanExecute(codegen)) {
973 RunCode(&codegen, graph_, [](HGraph*) {}, true, i >= j);
974 }
975 }
976 }
977 }
978
TestVectorComparison(IfCondition condition,int64_t lhs_value,int64_t rhs_value,DataType::Type type,CodeGenerator * codegen)979 void CodegenTest::TestVectorComparison(IfCondition condition,
980 int64_t lhs_value,
981 int64_t rhs_value,
982 DataType::Type type,
983 CodeGenerator* codegen) {
984 HBasicBlock* block = entry_block_->GetSingleSuccessor();
985
986 size_t vector_size_in_bytes = codegen->GetSIMDRegisterWidth();
987
988 HVecPredSetAll* predicate = MakeVecPredSetAll(block,
989 graph_->GetIntConstant(1),
990 type,
991 vector_size_in_bytes);
992 HVecReplicateScalar* op1 = MakeVecReplicateScalar(block,
993 graph_->GetConstant(type, lhs_value),
994 type,
995 vector_size_in_bytes,
996 predicate);
997 HVecReplicateScalar* op2 = MakeVecReplicateScalar(block,
998 graph_->GetConstant(type, rhs_value),
999 type,
1000 vector_size_in_bytes,
1001 predicate);
1002 HVecCondition* comparison = MakeVecCondition(block,
1003 condition,
1004 op1,
1005 op2,
1006 type,
1007 vector_size_in_bytes,
1008 predicate);
1009 HInstruction* boolean_return = MakeVecPredToBoolean(block,
1010 comparison,
1011 HVecPredToBoolean::PCondKind::kFirst,
1012 type,
1013 vector_size_in_bytes);
1014 MakeReturn(block, boolean_return);
1015
1016 graph_->SetHasPredicatedSIMD(true);
1017 graph_->BuildDominatorTree();
1018
1019 if (CanExecute(*codegen)) {
1020 bool expected_result = GetExpectedResultFromComparison(condition, lhs_value, rhs_value);
1021 RunCode(codegen, graph_, [](HGraph*) {}, true, expected_result);
1022 }
1023 }
1024
1025 // Define tests ensuring that all types of conditions can be generated correctly and return the
1026 // expected result.
1027 #define DEFINE_CONDITION_TESTS(CondType) \
1028 TEST_F(CodegenTest, ComparisonsVector##CondType) { \
1029 std::unique_ptr<CompilerOptions> compiler_options = \
1030 CommonCompilerTest::CreateCompilerOptions(InstructionSet::kArm64, "default", "sve"); \
1031 for (int64_t i = -1; i <= 1; i++) { \
1032 for (int64_t j = -1; j <= 1; j++) { \
1033 for (int cond = kCondFirst; cond <= kCondLast; cond++) { \
1034 InitEntryMainExitGraph(); \
1035 TestCodeGeneratorARM64 codegen(graph_, *compiler_options); \
1036 if (!codegen.SupportsPredicatedSIMD()) { \
1037 GTEST_SKIP() << "Predicated SIMD is not supported."; \
1038 } \
1039 TestVectorComparison( \
1040 static_cast<IfCondition>(cond), i, j, DataType::Type::k##CondType, &codegen); \
1041 } \
1042 } \
1043 } \
1044 }
1045 DEFINE_CONDITION_TESTS(Uint8)
1046 DEFINE_CONDITION_TESTS(Int8)
1047 DEFINE_CONDITION_TESTS(Uint16)
1048 DEFINE_CONDITION_TESTS(Int16)
1049 DEFINE_CONDITION_TESTS(Int32)
1050 #undef DEFINE_CONDITION_TESTS
1051
1052 #endif
1053
1054 } // namespace art
1055