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