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 {
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 (CanExecute(test_config.GetInstructionSet())) {
60 v.push_back(test_config);
61 }
62 }
63
64 return v;
65 }
66
67 class CodegenTest : public OptimizingUnitTest {
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 };
77
TestCode(const std::vector<uint16_t> & data,bool has_result,int32_t expected)78 void CodegenTest::TestCode(const std::vector<uint16_t>& data, bool has_result, int32_t expected) {
79 for (const CodegenTargetConfig& target_config : GetTargetConfigs()) {
80 ResetPoolAndAllocator();
81 HGraph* graph = CreateCFG(data);
82 // Remove suspend checks, they cannot be executed in this context.
83 RemoveSuspendChecks(graph);
84 OverrideInstructionSetFeatures(target_config.GetInstructionSet(), "default");
85 RunCode(target_config, *compiler_options_, graph, [](HGraph*) {}, has_result, expected);
86 }
87 }
88
TestCodeLong(const std::vector<uint16_t> & data,bool has_result,int64_t expected)89 void CodegenTest::TestCodeLong(const std::vector<uint16_t>& data,
90 bool has_result, int64_t expected) {
91 for (const CodegenTargetConfig& target_config : GetTargetConfigs()) {
92 ResetPoolAndAllocator();
93 HGraph* graph = CreateCFG(data, DataType::Type::kInt64);
94 // Remove suspend checks, they cannot be executed in this context.
95 RemoveSuspendChecks(graph);
96 OverrideInstructionSetFeatures(target_config.GetInstructionSet(), "default");
97 RunCode(target_config, *compiler_options_, graph, [](HGraph*) {}, has_result, expected);
98 }
99 }
100
TEST_F(CodegenTest,ReturnVoid)101 TEST_F(CodegenTest, ReturnVoid) {
102 const std::vector<uint16_t> data = ZERO_REGISTER_CODE_ITEM(Instruction::RETURN_VOID);
103 TestCode(data);
104 }
105
TEST_F(CodegenTest,CFG1)106 TEST_F(CodegenTest, CFG1) {
107 const std::vector<uint16_t> data = ZERO_REGISTER_CODE_ITEM(
108 Instruction::GOTO | 0x100,
109 Instruction::RETURN_VOID);
110
111 TestCode(data);
112 }
113
TEST_F(CodegenTest,CFG2)114 TEST_F(CodegenTest, CFG2) {
115 const std::vector<uint16_t> data = ZERO_REGISTER_CODE_ITEM(
116 Instruction::GOTO | 0x100,
117 Instruction::GOTO | 0x100,
118 Instruction::RETURN_VOID);
119
120 TestCode(data);
121 }
122
TEST_F(CodegenTest,CFG3)123 TEST_F(CodegenTest, CFG3) {
124 const std::vector<uint16_t> data1 = ZERO_REGISTER_CODE_ITEM(
125 Instruction::GOTO | 0x200,
126 Instruction::RETURN_VOID,
127 Instruction::GOTO | 0xFF00);
128
129 TestCode(data1);
130
131 const std::vector<uint16_t> data2 = ZERO_REGISTER_CODE_ITEM(
132 Instruction::GOTO_16, 3,
133 Instruction::RETURN_VOID,
134 Instruction::GOTO_16, 0xFFFF);
135
136 TestCode(data2);
137
138 const std::vector<uint16_t> data3 = ZERO_REGISTER_CODE_ITEM(
139 Instruction::GOTO_32, 4, 0,
140 Instruction::RETURN_VOID,
141 Instruction::GOTO_32, 0xFFFF, 0xFFFF);
142
143 TestCode(data3);
144 }
145
TEST_F(CodegenTest,CFG4)146 TEST_F(CodegenTest, CFG4) {
147 const std::vector<uint16_t> data = ZERO_REGISTER_CODE_ITEM(
148 Instruction::RETURN_VOID,
149 Instruction::GOTO | 0x100,
150 Instruction::GOTO | 0xFE00);
151
152 TestCode(data);
153 }
154
TEST_F(CodegenTest,CFG5)155 TEST_F(CodegenTest, CFG5) {
156 const std::vector<uint16_t> data = ONE_REGISTER_CODE_ITEM(
157 Instruction::CONST_4 | 0 | 0,
158 Instruction::IF_EQ, 3,
159 Instruction::GOTO | 0x100,
160 Instruction::RETURN_VOID);
161
162 TestCode(data);
163 }
164
TEST_F(CodegenTest,IntConstant)165 TEST_F(CodegenTest, IntConstant) {
166 const std::vector<uint16_t> data = ONE_REGISTER_CODE_ITEM(
167 Instruction::CONST_4 | 0 | 0,
168 Instruction::RETURN_VOID);
169
170 TestCode(data);
171 }
172
TEST_F(CodegenTest,Return1)173 TEST_F(CodegenTest, Return1) {
174 const std::vector<uint16_t> data = ONE_REGISTER_CODE_ITEM(
175 Instruction::CONST_4 | 0 | 0,
176 Instruction::RETURN | 0);
177
178 TestCode(data, true, 0);
179 }
180
TEST_F(CodegenTest,Return2)181 TEST_F(CodegenTest, Return2) {
182 const std::vector<uint16_t> data = TWO_REGISTERS_CODE_ITEM(
183 Instruction::CONST_4 | 0 | 0,
184 Instruction::CONST_4 | 0 | 1 << 8,
185 Instruction::RETURN | 1 << 8);
186
187 TestCode(data, true, 0);
188 }
189
TEST_F(CodegenTest,Return3)190 TEST_F(CodegenTest, Return3) {
191 const std::vector<uint16_t> data = TWO_REGISTERS_CODE_ITEM(
192 Instruction::CONST_4 | 0 | 0,
193 Instruction::CONST_4 | 1 << 8 | 1 << 12,
194 Instruction::RETURN | 1 << 8);
195
196 TestCode(data, true, 1);
197 }
198
TEST_F(CodegenTest,ReturnIf1)199 TEST_F(CodegenTest, ReturnIf1) {
200 const std::vector<uint16_t> data = TWO_REGISTERS_CODE_ITEM(
201 Instruction::CONST_4 | 0 | 0,
202 Instruction::CONST_4 | 1 << 8 | 1 << 12,
203 Instruction::IF_EQ, 3,
204 Instruction::RETURN | 0 << 8,
205 Instruction::RETURN | 1 << 8);
206
207 TestCode(data, true, 1);
208 }
209
TEST_F(CodegenTest,ReturnIf2)210 TEST_F(CodegenTest, ReturnIf2) {
211 const std::vector<uint16_t> data = TWO_REGISTERS_CODE_ITEM(
212 Instruction::CONST_4 | 0 | 0,
213 Instruction::CONST_4 | 1 << 8 | 1 << 12,
214 Instruction::IF_EQ | 0 << 4 | 1 << 8, 3,
215 Instruction::RETURN | 0 << 8,
216 Instruction::RETURN | 1 << 8);
217
218 TestCode(data, true, 0);
219 }
220
221 // Exercise bit-wise (one's complement) not-int instruction.
222 #define NOT_INT_TEST(TEST_NAME, INPUT, EXPECTED_OUTPUT) \
223 TEST_F(CodegenTest, TEST_NAME) { \
224 const int32_t input = INPUT; \
225 const uint16_t input_lo = Low16Bits(input); \
226 const uint16_t input_hi = High16Bits(input); \
227 const std::vector<uint16_t> data = TWO_REGISTERS_CODE_ITEM( \
228 Instruction::CONST | 0 << 8, input_lo, input_hi, \
229 Instruction::NOT_INT | 1 << 8 | 0 << 12 , \
230 Instruction::RETURN | 1 << 8); \
231 \
232 TestCode(data, true, EXPECTED_OUTPUT); \
233 }
234
235 NOT_INT_TEST(ReturnNotIntMinus2, -2, 1)
236 NOT_INT_TEST(ReturnNotIntMinus1, -1, 0)
237 NOT_INT_TEST(ReturnNotInt0, 0, -1)
238 NOT_INT_TEST(ReturnNotInt1, 1, -2)
239 NOT_INT_TEST(ReturnNotIntINT32_MIN, -2147483648, 2147483647) // (2^31) - 1
240 NOT_INT_TEST(ReturnNotIntINT32_MINPlus1, -2147483647, 2147483646) // (2^31) - 2
241 NOT_INT_TEST(ReturnNotIntINT32_MAXMinus1, 2147483646, -2147483647) // -(2^31) - 1
242 NOT_INT_TEST(ReturnNotIntINT32_MAX, 2147483647, -2147483648) // -(2^31)
243
244 #undef NOT_INT_TEST
245
246 // Exercise bit-wise (one's complement) not-long instruction.
247 #define NOT_LONG_TEST(TEST_NAME, INPUT, EXPECTED_OUTPUT) \
248 TEST_F(CodegenTest, TEST_NAME) { \
249 const int64_t input = INPUT; \
250 const uint16_t word0 = Low16Bits(Low32Bits(input)); /* LSW. */ \
251 const uint16_t word1 = High16Bits(Low32Bits(input)); \
252 const uint16_t word2 = Low16Bits(High32Bits(input)); \
253 const uint16_t word3 = High16Bits(High32Bits(input)); /* MSW. */ \
254 const std::vector<uint16_t> data = FOUR_REGISTERS_CODE_ITEM( \
255 Instruction::CONST_WIDE | 0 << 8, word0, word1, word2, word3, \
256 Instruction::NOT_LONG | 2 << 8 | 0 << 12, \
257 Instruction::RETURN_WIDE | 2 << 8); \
258 \
259 TestCodeLong(data, true, EXPECTED_OUTPUT); \
260 }
261
262 NOT_LONG_TEST(ReturnNotLongMinus2, INT64_C(-2), INT64_C(1))
263 NOT_LONG_TEST(ReturnNotLongMinus1, INT64_C(-1), INT64_C(0))
264 NOT_LONG_TEST(ReturnNotLong0, INT64_C(0), INT64_C(-1))
265 NOT_LONG_TEST(ReturnNotLong1, INT64_C(1), INT64_C(-2))
266
267 NOT_LONG_TEST(ReturnNotLongINT32_MIN,
268 INT64_C(-2147483648),
269 INT64_C(2147483647)) // (2^31) - 1
270 NOT_LONG_TEST(ReturnNotLongINT32_MINPlus1,
271 INT64_C(-2147483647),
272 INT64_C(2147483646)) // (2^31) - 2
273 NOT_LONG_TEST(ReturnNotLongINT32_MAXMinus1,
274 INT64_C(2147483646),
275 INT64_C(-2147483647)) // -(2^31) - 1
276 NOT_LONG_TEST(ReturnNotLongINT32_MAX,
277 INT64_C(2147483647),
278 INT64_C(-2147483648)) // -(2^31)
279
280 // Note that the C++ compiler won't accept
281 // INT64_C(-9223372036854775808) (that is, INT64_MIN) as a valid
282 // int64_t literal, so we use INT64_C(-9223372036854775807)-1 instead.
283 NOT_LONG_TEST(ReturnNotINT64_MIN,
284 INT64_C(-9223372036854775807)-1,
285 INT64_C(9223372036854775807)); // (2^63) - 1
286 NOT_LONG_TEST(ReturnNotINT64_MINPlus1,
287 INT64_C(-9223372036854775807),
288 INT64_C(9223372036854775806)); // (2^63) - 2
289 NOT_LONG_TEST(ReturnNotLongINT64_MAXMinus1,
290 INT64_C(9223372036854775806),
291 INT64_C(-9223372036854775807)); // -(2^63) - 1
292 NOT_LONG_TEST(ReturnNotLongINT64_MAX,
293 INT64_C(9223372036854775807),
294 INT64_C(-9223372036854775807)-1); // -(2^63)
295
296 #undef NOT_LONG_TEST
297
TEST_F(CodegenTest,IntToLongOfLongToInt)298 TEST_F(CodegenTest, IntToLongOfLongToInt) {
299 const int64_t input = INT64_C(4294967296); // 2^32
300 const uint16_t word0 = Low16Bits(Low32Bits(input)); // LSW.
301 const uint16_t word1 = High16Bits(Low32Bits(input));
302 const uint16_t word2 = Low16Bits(High32Bits(input));
303 const uint16_t word3 = High16Bits(High32Bits(input)); // MSW.
304 const std::vector<uint16_t> data = FIVE_REGISTERS_CODE_ITEM(
305 Instruction::CONST_WIDE | 0 << 8, word0, word1, word2, word3,
306 Instruction::CONST_WIDE | 2 << 8, 1, 0, 0, 0,
307 Instruction::ADD_LONG | 0, 0 << 8 | 2, // v0 <- 2^32 + 1
308 Instruction::LONG_TO_INT | 4 << 8 | 0 << 12,
309 Instruction::INT_TO_LONG | 2 << 8 | 4 << 12,
310 Instruction::RETURN_WIDE | 2 << 8);
311
312 TestCodeLong(data, true, 1);
313 }
314
TEST_F(CodegenTest,ReturnAdd1)315 TEST_F(CodegenTest, ReturnAdd1) {
316 const std::vector<uint16_t> data = TWO_REGISTERS_CODE_ITEM(
317 Instruction::CONST_4 | 3 << 12 | 0,
318 Instruction::CONST_4 | 4 << 12 | 1 << 8,
319 Instruction::ADD_INT, 1 << 8 | 0,
320 Instruction::RETURN);
321
322 TestCode(data, true, 7);
323 }
324
TEST_F(CodegenTest,ReturnAdd2)325 TEST_F(CodegenTest, ReturnAdd2) {
326 const std::vector<uint16_t> data = TWO_REGISTERS_CODE_ITEM(
327 Instruction::CONST_4 | 3 << 12 | 0,
328 Instruction::CONST_4 | 4 << 12 | 1 << 8,
329 Instruction::ADD_INT_2ADDR | 1 << 12,
330 Instruction::RETURN);
331
332 TestCode(data, true, 7);
333 }
334
TEST_F(CodegenTest,ReturnAdd3)335 TEST_F(CodegenTest, ReturnAdd3) {
336 const std::vector<uint16_t> data = ONE_REGISTER_CODE_ITEM(
337 Instruction::CONST_4 | 4 << 12 | 0 << 8,
338 Instruction::ADD_INT_LIT8, 3 << 8 | 0,
339 Instruction::RETURN);
340
341 TestCode(data, true, 7);
342 }
343
TEST_F(CodegenTest,ReturnAdd4)344 TEST_F(CodegenTest, ReturnAdd4) {
345 const std::vector<uint16_t> data = ONE_REGISTER_CODE_ITEM(
346 Instruction::CONST_4 | 4 << 12 | 0 << 8,
347 Instruction::ADD_INT_LIT16, 3,
348 Instruction::RETURN);
349
350 TestCode(data, true, 7);
351 }
352
TEST_F(CodegenTest,ReturnMulInt)353 TEST_F(CodegenTest, ReturnMulInt) {
354 const std::vector<uint16_t> data = TWO_REGISTERS_CODE_ITEM(
355 Instruction::CONST_4 | 3 << 12 | 0,
356 Instruction::CONST_4 | 4 << 12 | 1 << 8,
357 Instruction::MUL_INT, 1 << 8 | 0,
358 Instruction::RETURN);
359
360 TestCode(data, true, 12);
361 }
362
TEST_F(CodegenTest,ReturnMulInt2addr)363 TEST_F(CodegenTest, ReturnMulInt2addr) {
364 const std::vector<uint16_t> data = TWO_REGISTERS_CODE_ITEM(
365 Instruction::CONST_4 | 3 << 12 | 0,
366 Instruction::CONST_4 | 4 << 12 | 1 << 8,
367 Instruction::MUL_INT_2ADDR | 1 << 12,
368 Instruction::RETURN);
369
370 TestCode(data, true, 12);
371 }
372
TEST_F(CodegenTest,ReturnMulLong)373 TEST_F(CodegenTest, ReturnMulLong) {
374 const std::vector<uint16_t> data = FOUR_REGISTERS_CODE_ITEM(
375 Instruction::CONST_WIDE | 0 << 8, 3, 0, 0, 0,
376 Instruction::CONST_WIDE | 2 << 8, 4, 0, 0, 0,
377 Instruction::MUL_LONG, 2 << 8 | 0,
378 Instruction::RETURN_WIDE);
379
380 TestCodeLong(data, true, 12);
381 }
382
TEST_F(CodegenTest,ReturnMulLong2addr)383 TEST_F(CodegenTest, ReturnMulLong2addr) {
384 const std::vector<uint16_t> data = FOUR_REGISTERS_CODE_ITEM(
385 Instruction::CONST_WIDE | 0 << 8, 3, 0, 0, 0,
386 Instruction::CONST_WIDE | 2 << 8, 4, 0, 0, 0,
387 Instruction::MUL_LONG_2ADDR | 2 << 12,
388 Instruction::RETURN_WIDE);
389
390 TestCodeLong(data, true, 12);
391 }
392
TEST_F(CodegenTest,ReturnMulIntLit8)393 TEST_F(CodegenTest, ReturnMulIntLit8) {
394 const std::vector<uint16_t> data = ONE_REGISTER_CODE_ITEM(
395 Instruction::CONST_4 | 4 << 12 | 0 << 8,
396 Instruction::MUL_INT_LIT8, 3 << 8 | 0,
397 Instruction::RETURN);
398
399 TestCode(data, true, 12);
400 }
401
TEST_F(CodegenTest,ReturnMulIntLit16)402 TEST_F(CodegenTest, ReturnMulIntLit16) {
403 const std::vector<uint16_t> data = ONE_REGISTER_CODE_ITEM(
404 Instruction::CONST_4 | 4 << 12 | 0 << 8,
405 Instruction::MUL_INT_LIT16, 3,
406 Instruction::RETURN);
407
408 TestCode(data, true, 12);
409 }
410
TEST_F(CodegenTest,NonMaterializedCondition)411 TEST_F(CodegenTest, NonMaterializedCondition) {
412 for (CodegenTargetConfig target_config : GetTargetConfigs()) {
413 HGraph* graph = CreateGraph();
414
415 HBasicBlock* entry = new (GetAllocator()) HBasicBlock(graph);
416 graph->AddBlock(entry);
417 graph->SetEntryBlock(entry);
418 entry->AddInstruction(new (GetAllocator()) HGoto());
419
420 HBasicBlock* first_block = new (GetAllocator()) HBasicBlock(graph);
421 graph->AddBlock(first_block);
422 entry->AddSuccessor(first_block);
423 HIntConstant* constant0 = graph->GetIntConstant(0);
424 HIntConstant* constant1 = graph->GetIntConstant(1);
425 HEqual* equal = new (GetAllocator()) HEqual(constant0, constant0);
426 first_block->AddInstruction(equal);
427 first_block->AddInstruction(new (GetAllocator()) HIf(equal));
428
429 HBasicBlock* then_block = new (GetAllocator()) HBasicBlock(graph);
430 HBasicBlock* else_block = new (GetAllocator()) HBasicBlock(graph);
431 HBasicBlock* exit_block = new (GetAllocator()) HBasicBlock(graph);
432 graph->SetExitBlock(exit_block);
433
434 graph->AddBlock(then_block);
435 graph->AddBlock(else_block);
436 graph->AddBlock(exit_block);
437 first_block->AddSuccessor(then_block);
438 first_block->AddSuccessor(else_block);
439 then_block->AddSuccessor(exit_block);
440 else_block->AddSuccessor(exit_block);
441
442 exit_block->AddInstruction(new (GetAllocator()) HExit());
443 then_block->AddInstruction(new (GetAllocator()) HReturn(constant0));
444 else_block->AddInstruction(new (GetAllocator()) HReturn(constant1));
445
446 ASSERT_FALSE(equal->IsEmittedAtUseSite());
447 graph->BuildDominatorTree();
448 PrepareForRegisterAllocation(graph, *compiler_options_).Run();
449 ASSERT_TRUE(equal->IsEmittedAtUseSite());
450
451 auto hook_before_codegen = [](HGraph* graph_in) {
452 HBasicBlock* block = graph_in->GetEntryBlock()->GetSuccessors()[0];
453 HParallelMove* move = new (graph_in->GetAllocator()) HParallelMove(graph_in->GetAllocator());
454 block->InsertInstructionBefore(move, block->GetLastInstruction());
455 };
456
457 OverrideInstructionSetFeatures(target_config.GetInstructionSet(), "default");
458 RunCode(target_config, *compiler_options_, graph, hook_before_codegen, true, 0);
459 }
460 }
461
TEST_F(CodegenTest,MaterializedCondition1)462 TEST_F(CodegenTest, MaterializedCondition1) {
463 for (CodegenTargetConfig target_config : GetTargetConfigs()) {
464 // Check that condition are materialized correctly. A materialized condition
465 // should yield `1` if it evaluated to true, and `0` otherwise.
466 // We force the materialization of comparisons for different combinations of
467
468 // inputs and check the results.
469
470 int lhs[] = {1, 2, -1, 2, 0xabc};
471 int rhs[] = {2, 1, 2, -1, 0xabc};
472
473 for (size_t i = 0; i < arraysize(lhs); i++) {
474 HGraph* graph = CreateGraph();
475
476 HBasicBlock* entry_block = new (GetAllocator()) HBasicBlock(graph);
477 graph->AddBlock(entry_block);
478 graph->SetEntryBlock(entry_block);
479 entry_block->AddInstruction(new (GetAllocator()) HGoto());
480 HBasicBlock* code_block = new (GetAllocator()) HBasicBlock(graph);
481 graph->AddBlock(code_block);
482 HBasicBlock* exit_block = new (GetAllocator()) HBasicBlock(graph);
483 graph->AddBlock(exit_block);
484 exit_block->AddInstruction(new (GetAllocator()) HExit());
485
486 entry_block->AddSuccessor(code_block);
487 code_block->AddSuccessor(exit_block);
488 graph->SetExitBlock(exit_block);
489
490 HIntConstant* cst_lhs = graph->GetIntConstant(lhs[i]);
491 HIntConstant* cst_rhs = graph->GetIntConstant(rhs[i]);
492 HLessThan cmp_lt(cst_lhs, cst_rhs);
493 code_block->AddInstruction(&cmp_lt);
494 HReturn ret(&cmp_lt);
495 code_block->AddInstruction(&ret);
496
497 graph->BuildDominatorTree();
498 auto hook_before_codegen = [](HGraph* graph_in) {
499 HBasicBlock* block = graph_in->GetEntryBlock()->GetSuccessors()[0];
500 HParallelMove* move =
501 new (graph_in->GetAllocator()) HParallelMove(graph_in->GetAllocator());
502 block->InsertInstructionBefore(move, block->GetLastInstruction());
503 };
504 OverrideInstructionSetFeatures(target_config.GetInstructionSet(), "default");
505 RunCode(target_config, *compiler_options_, graph, hook_before_codegen, true, lhs[i] < rhs[i]);
506 }
507 }
508 }
509
TEST_F(CodegenTest,MaterializedCondition2)510 TEST_F(CodegenTest, MaterializedCondition2) {
511 for (CodegenTargetConfig target_config : GetTargetConfigs()) {
512 // Check that HIf correctly interprets a materialized condition.
513 // We force the materialization of comparisons for different combinations of
514 // inputs. An HIf takes the materialized combination as input and returns a
515 // value that we verify.
516
517 int lhs[] = {1, 2, -1, 2, 0xabc};
518 int rhs[] = {2, 1, 2, -1, 0xabc};
519
520
521 for (size_t i = 0; i < arraysize(lhs); i++) {
522 HGraph* graph = CreateGraph();
523
524 HBasicBlock* entry_block = new (GetAllocator()) HBasicBlock(graph);
525 graph->AddBlock(entry_block);
526 graph->SetEntryBlock(entry_block);
527 entry_block->AddInstruction(new (GetAllocator()) HGoto());
528
529 HBasicBlock* if_block = new (GetAllocator()) HBasicBlock(graph);
530 graph->AddBlock(if_block);
531 HBasicBlock* if_true_block = new (GetAllocator()) HBasicBlock(graph);
532 graph->AddBlock(if_true_block);
533 HBasicBlock* if_false_block = new (GetAllocator()) HBasicBlock(graph);
534 graph->AddBlock(if_false_block);
535 HBasicBlock* exit_block = new (GetAllocator()) HBasicBlock(graph);
536 graph->AddBlock(exit_block);
537 exit_block->AddInstruction(new (GetAllocator()) HExit());
538
539 graph->SetEntryBlock(entry_block);
540 entry_block->AddSuccessor(if_block);
541 if_block->AddSuccessor(if_true_block);
542 if_block->AddSuccessor(if_false_block);
543 if_true_block->AddSuccessor(exit_block);
544 if_false_block->AddSuccessor(exit_block);
545 graph->SetExitBlock(exit_block);
546
547 HIntConstant* cst_lhs = graph->GetIntConstant(lhs[i]);
548 HIntConstant* cst_rhs = graph->GetIntConstant(rhs[i]);
549 HLessThan cmp_lt(cst_lhs, cst_rhs);
550 if_block->AddInstruction(&cmp_lt);
551 // We insert a dummy instruction to separate the HIf from the HLessThan
552 // and force the materialization of the condition.
553 HMemoryBarrier force_materialization(MemBarrierKind::kAnyAny, 0);
554 if_block->AddInstruction(&force_materialization);
555 HIf if_lt(&cmp_lt);
556 if_block->AddInstruction(&if_lt);
557
558 HIntConstant* cst_lt = graph->GetIntConstant(1);
559 HReturn ret_lt(cst_lt);
560 if_true_block->AddInstruction(&ret_lt);
561 HIntConstant* cst_ge = graph->GetIntConstant(0);
562 HReturn ret_ge(cst_ge);
563 if_false_block->AddInstruction(&ret_ge);
564
565 graph->BuildDominatorTree();
566 auto hook_before_codegen = [](HGraph* graph_in) {
567 HBasicBlock* block = graph_in->GetEntryBlock()->GetSuccessors()[0];
568 HParallelMove* move =
569 new (graph_in->GetAllocator()) HParallelMove(graph_in->GetAllocator());
570 block->InsertInstructionBefore(move, block->GetLastInstruction());
571 };
572 OverrideInstructionSetFeatures(target_config.GetInstructionSet(), "default");
573 RunCode(target_config, *compiler_options_, graph, hook_before_codegen, true, lhs[i] < rhs[i]);
574 }
575 }
576 }
577
TEST_F(CodegenTest,ReturnDivIntLit8)578 TEST_F(CodegenTest, ReturnDivIntLit8) {
579 const std::vector<uint16_t> data = ONE_REGISTER_CODE_ITEM(
580 Instruction::CONST_4 | 4 << 12 | 0 << 8,
581 Instruction::DIV_INT_LIT8, 3 << 8 | 0,
582 Instruction::RETURN);
583
584 TestCode(data, true, 1);
585 }
586
TEST_F(CodegenTest,ReturnDivInt2Addr)587 TEST_F(CodegenTest, ReturnDivInt2Addr) {
588 const std::vector<uint16_t> data = TWO_REGISTERS_CODE_ITEM(
589 Instruction::CONST_4 | 4 << 12 | 0,
590 Instruction::CONST_4 | 2 << 12 | 1 << 8,
591 Instruction::DIV_INT_2ADDR | 1 << 12,
592 Instruction::RETURN);
593
594 TestCode(data, true, 2);
595 }
596
597 // Helper method.
TestComparison(IfCondition condition,int64_t i,int64_t j,DataType::Type type,const CodegenTargetConfig target_config)598 void CodegenTest::TestComparison(IfCondition condition,
599 int64_t i,
600 int64_t j,
601 DataType::Type type,
602 const CodegenTargetConfig target_config) {
603 HGraph* graph = CreateGraph();
604
605 HBasicBlock* entry_block = new (GetAllocator()) HBasicBlock(graph);
606 graph->AddBlock(entry_block);
607 graph->SetEntryBlock(entry_block);
608 entry_block->AddInstruction(new (GetAllocator()) HGoto());
609
610 HBasicBlock* block = new (GetAllocator()) HBasicBlock(graph);
611 graph->AddBlock(block);
612
613 HBasicBlock* exit_block = new (GetAllocator()) HBasicBlock(graph);
614 graph->AddBlock(exit_block);
615 graph->SetExitBlock(exit_block);
616 exit_block->AddInstruction(new (GetAllocator()) HExit());
617
618 entry_block->AddSuccessor(block);
619 block->AddSuccessor(exit_block);
620
621 HInstruction* op1;
622 HInstruction* op2;
623 if (type == DataType::Type::kInt32) {
624 op1 = graph->GetIntConstant(i);
625 op2 = graph->GetIntConstant(j);
626 } else {
627 DCHECK_EQ(type, DataType::Type::kInt64);
628 op1 = graph->GetLongConstant(i);
629 op2 = graph->GetLongConstant(j);
630 }
631
632 HInstruction* comparison = nullptr;
633 bool expected_result = false;
634 const uint64_t x = i;
635 const uint64_t y = j;
636 switch (condition) {
637 case kCondEQ:
638 comparison = new (GetAllocator()) HEqual(op1, op2);
639 expected_result = (i == j);
640 break;
641 case kCondNE:
642 comparison = new (GetAllocator()) HNotEqual(op1, op2);
643 expected_result = (i != j);
644 break;
645 case kCondLT:
646 comparison = new (GetAllocator()) HLessThan(op1, op2);
647 expected_result = (i < j);
648 break;
649 case kCondLE:
650 comparison = new (GetAllocator()) HLessThanOrEqual(op1, op2);
651 expected_result = (i <= j);
652 break;
653 case kCondGT:
654 comparison = new (GetAllocator()) HGreaterThan(op1, op2);
655 expected_result = (i > j);
656 break;
657 case kCondGE:
658 comparison = new (GetAllocator()) HGreaterThanOrEqual(op1, op2);
659 expected_result = (i >= j);
660 break;
661 case kCondB:
662 comparison = new (GetAllocator()) HBelow(op1, op2);
663 expected_result = (x < y);
664 break;
665 case kCondBE:
666 comparison = new (GetAllocator()) HBelowOrEqual(op1, op2);
667 expected_result = (x <= y);
668 break;
669 case kCondA:
670 comparison = new (GetAllocator()) HAbove(op1, op2);
671 expected_result = (x > y);
672 break;
673 case kCondAE:
674 comparison = new (GetAllocator()) HAboveOrEqual(op1, op2);
675 expected_result = (x >= y);
676 break;
677 }
678 block->AddInstruction(comparison);
679 block->AddInstruction(new (GetAllocator()) HReturn(comparison));
680
681 graph->BuildDominatorTree();
682 OverrideInstructionSetFeatures(target_config.GetInstructionSet(), "default");
683 RunCode(target_config, *compiler_options_, graph, [](HGraph*) {}, true, expected_result);
684 }
685
TEST_F(CodegenTest,ComparisonsInt)686 TEST_F(CodegenTest, ComparisonsInt) {
687 for (CodegenTargetConfig target_config : GetTargetConfigs()) {
688 for (int64_t i = -1; i <= 1; i++) {
689 for (int64_t j = -1; j <= 1; j++) {
690 for (int cond = kCondFirst; cond <= kCondLast; cond++) {
691 TestComparison(
692 static_cast<IfCondition>(cond), i, j, DataType::Type::kInt32, target_config);
693 }
694 }
695 }
696 }
697 }
698
TEST_F(CodegenTest,ComparisonsLong)699 TEST_F(CodegenTest, ComparisonsLong) {
700 for (CodegenTargetConfig target_config : GetTargetConfigs()) {
701 for (int64_t i = -1; i <= 1; i++) {
702 for (int64_t j = -1; j <= 1; j++) {
703 for (int cond = kCondFirst; cond <= kCondLast; cond++) {
704 TestComparison(
705 static_cast<IfCondition>(cond), i, j, DataType::Type::kInt64, target_config);
706 }
707 }
708 }
709 }
710 }
711
712 #ifdef ART_ENABLE_CODEGEN_arm
TEST_F(CodegenTest,ARMVIXLParallelMoveResolver)713 TEST_F(CodegenTest, ARMVIXLParallelMoveResolver) {
714 OverrideInstructionSetFeatures(InstructionSet::kThumb2, "default");
715 HGraph* graph = CreateGraph();
716 arm::CodeGeneratorARMVIXL codegen(graph, *compiler_options_);
717
718 codegen.Initialize();
719
720 // This will result in calling EmitSwap -> void ParallelMoveResolverARMVIXL::Exchange(int mem1,
721 // int mem2) which was faulty (before the fix). So previously GPR and FP scratch registers were
722 // used as temps; however GPR scratch register is required for big stack offsets which don't fit
723 // LDR encoding. So the following code is a regression test for that situation.
724 HParallelMove* move = new (graph->GetAllocator()) HParallelMove(graph->GetAllocator());
725 move->AddMove(Location::StackSlot(0), Location::StackSlot(8192), DataType::Type::kInt32, nullptr);
726 move->AddMove(Location::StackSlot(8192), Location::StackSlot(0), DataType::Type::kInt32, nullptr);
727 codegen.GetMoveResolver()->EmitNativeCode(move);
728
729 InternalCodeAllocator code_allocator;
730 codegen.Finalize(&code_allocator);
731 }
732 #endif
733
734 #ifdef ART_ENABLE_CODEGEN_arm64
735 // Regression test for b/34760542.
TEST_F(CodegenTest,ARM64ParallelMoveResolverB34760542)736 TEST_F(CodegenTest, ARM64ParallelMoveResolverB34760542) {
737 OverrideInstructionSetFeatures(InstructionSet::kArm64, "default");
738 HGraph* graph = CreateGraph();
739 arm64::CodeGeneratorARM64 codegen(graph, *compiler_options_);
740
741 codegen.Initialize();
742
743 // The following ParallelMove used to fail this assertion:
744 //
745 // Assertion failed (!available->IsEmpty())
746 //
747 // in vixl::aarch64::UseScratchRegisterScope::AcquireNextAvailable,
748 // because of the following situation:
749 //
750 // 1. a temp register (IP0) is allocated as a scratch register by
751 // the parallel move resolver to solve a cycle (swap):
752 //
753 // [ source=DS0 destination=DS257 type=PrimDouble instruction=null ]
754 // [ source=DS257 destination=DS0 type=PrimDouble instruction=null ]
755 //
756 // 2. within CodeGeneratorARM64::MoveLocation, another temp
757 // register (IP1) is allocated to generate the swap between two
758 // double stack slots;
759 //
760 // 3. VIXL requires a third temp register to emit the `Ldr` or
761 // `Str` operation from CodeGeneratorARM64::MoveLocation (as
762 // one of the stack slots' offsets cannot be encoded as an
763 // immediate), but the pool of (core) temp registers is now
764 // empty.
765 //
766 // The solution used so far is to use a floating-point temp register
767 // (D31) in step #2, so that IP1 is available for step #3.
768
769 HParallelMove* move = new (graph->GetAllocator()) HParallelMove(graph->GetAllocator());
770 move->AddMove(Location::DoubleStackSlot(0),
771 Location::DoubleStackSlot(257),
772 DataType::Type::kFloat64,
773 nullptr);
774 move->AddMove(Location::DoubleStackSlot(257),
775 Location::DoubleStackSlot(0),
776 DataType::Type::kFloat64,
777 nullptr);
778 codegen.GetMoveResolver()->EmitNativeCode(move);
779
780 InternalCodeAllocator code_allocator;
781 codegen.Finalize(&code_allocator);
782 }
783
784 // Check that ParallelMoveResolver works fine for ARM64 for both cases when SIMD is on and off.
TEST_F(CodegenTest,ARM64ParallelMoveResolverSIMD)785 TEST_F(CodegenTest, ARM64ParallelMoveResolverSIMD) {
786 OverrideInstructionSetFeatures(InstructionSet::kArm64, "default");
787 HGraph* graph = CreateGraph();
788 arm64::CodeGeneratorARM64 codegen(graph, *compiler_options_);
789
790 codegen.Initialize();
791
792 graph->SetHasSIMD(true);
793 for (int i = 0; i < 2; i++) {
794 HParallelMove* move = new (graph->GetAllocator()) HParallelMove(graph->GetAllocator());
795 move->AddMove(Location::SIMDStackSlot(0),
796 Location::SIMDStackSlot(257),
797 DataType::Type::kFloat64,
798 nullptr);
799 move->AddMove(Location::SIMDStackSlot(257),
800 Location::SIMDStackSlot(0),
801 DataType::Type::kFloat64,
802 nullptr);
803 move->AddMove(Location::FpuRegisterLocation(0),
804 Location::FpuRegisterLocation(1),
805 DataType::Type::kFloat64,
806 nullptr);
807 move->AddMove(Location::FpuRegisterLocation(1),
808 Location::FpuRegisterLocation(0),
809 DataType::Type::kFloat64,
810 nullptr);
811 codegen.GetMoveResolver()->EmitNativeCode(move);
812 graph->SetHasSIMD(false);
813 }
814
815 InternalCodeAllocator code_allocator;
816 codegen.Finalize(&code_allocator);
817 }
818
819 // Check that ART ISA Features are propagated to VIXL for arm64 (using cortex-a75 as example).
TEST_F(CodegenTest,ARM64IsaVIXLFeaturesA75)820 TEST_F(CodegenTest, ARM64IsaVIXLFeaturesA75) {
821 OverrideInstructionSetFeatures(InstructionSet::kArm64, "cortex-a75");
822 HGraph* graph = CreateGraph();
823 arm64::CodeGeneratorARM64 codegen(graph, *compiler_options_);
824 vixl::CPUFeatures* features = codegen.GetVIXLAssembler()->GetCPUFeatures();
825
826 EXPECT_TRUE(features->Has(vixl::CPUFeatures::kCRC32));
827 EXPECT_TRUE(features->Has(vixl::CPUFeatures::kDotProduct));
828 EXPECT_TRUE(features->Has(vixl::CPUFeatures::kFPHalf));
829 EXPECT_TRUE(features->Has(vixl::CPUFeatures::kNEONHalf));
830 EXPECT_TRUE(features->Has(vixl::CPUFeatures::kAtomics));
831 }
832
833 // Check that ART ISA Features are propagated to VIXL for arm64 (using cortex-a53 as example).
TEST_F(CodegenTest,ARM64IsaVIXLFeaturesA53)834 TEST_F(CodegenTest, ARM64IsaVIXLFeaturesA53) {
835 OverrideInstructionSetFeatures(InstructionSet::kArm64, "cortex-a53");
836 HGraph* graph = CreateGraph();
837 arm64::CodeGeneratorARM64 codegen(graph, *compiler_options_);
838 vixl::CPUFeatures* features = codegen.GetVIXLAssembler()->GetCPUFeatures();
839
840 EXPECT_TRUE(features->Has(vixl::CPUFeatures::kCRC32));
841 EXPECT_FALSE(features->Has(vixl::CPUFeatures::kDotProduct));
842 EXPECT_FALSE(features->Has(vixl::CPUFeatures::kFPHalf));
843 EXPECT_FALSE(features->Has(vixl::CPUFeatures::kNEONHalf));
844 EXPECT_FALSE(features->Has(vixl::CPUFeatures::kAtomics));
845 }
846
847 constexpr static size_t kExpectedFPSpillSize = 8 * vixl::aarch64::kDRegSizeInBytes;
848
849 // The following two tests check that for both SIMD and non-SIMD graphs exactly 64-bit is
850 // allocated on stack per callee-saved FP register to be preserved in the frame entry as
851 // ABI states.
TEST_F(CodegenTest,ARM64FrameSizeSIMD)852 TEST_F(CodegenTest, ARM64FrameSizeSIMD) {
853 OverrideInstructionSetFeatures(InstructionSet::kArm64, "default");
854 HGraph* graph = CreateGraph();
855 arm64::CodeGeneratorARM64 codegen(graph, *compiler_options_);
856
857 codegen.Initialize();
858 graph->SetHasSIMD(true);
859
860 DCHECK_EQ(arm64::callee_saved_fp_registers.GetCount(), 8);
861 vixl::aarch64::CPURegList reg_list = arm64::callee_saved_fp_registers;
862 while (!reg_list.IsEmpty()) {
863 uint32_t reg_code = reg_list.PopLowestIndex().GetCode();
864 codegen.AddAllocatedRegister(Location::FpuRegisterLocation(reg_code));
865 }
866 codegen.ComputeSpillMask();
867
868 EXPECT_EQ(codegen.GetFpuSpillSize(), kExpectedFPSpillSize);
869 }
870
TEST_F(CodegenTest,ARM64FrameSizeNoSIMD)871 TEST_F(CodegenTest, ARM64FrameSizeNoSIMD) {
872 OverrideInstructionSetFeatures(InstructionSet::kArm64, "default");
873 HGraph* graph = CreateGraph();
874 arm64::CodeGeneratorARM64 codegen(graph, *compiler_options_);
875
876 codegen.Initialize();
877 graph->SetHasSIMD(false);
878
879 DCHECK_EQ(arm64::callee_saved_fp_registers.GetCount(), 8);
880 vixl::aarch64::CPURegList reg_list = arm64::callee_saved_fp_registers;
881 while (!reg_list.IsEmpty()) {
882 uint32_t reg_code = reg_list.PopLowestIndex().GetCode();
883 codegen.AddAllocatedRegister(Location::FpuRegisterLocation(reg_code));
884 }
885 codegen.ComputeSpillMask();
886
887 EXPECT_EQ(codegen.GetFpuSpillSize(), kExpectedFPSpillSize);
888 }
889
890 #endif
891
892 } // namespace art
893