xref: /aosp_15_r20/art/compiler/optimizing/codegen_test.cc (revision 795d594fd825385562da6b089ea9b2033f3abf5a)
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