codegen_test.cc revision 9983e302384c12a975c8d2d5ae239f79fd8e1996
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 "builder.h"
22#include "codegen_test_utils.h"
23#include "dex_file.h"
24#include "dex_instruction.h"
25#include "driver/compiler_options.h"
26#include "nodes.h"
27#include "optimizing_unit_test.h"
28#include "register_allocator_linear_scan.h"
29#include "utils.h"
30#include "utils/arm/assembler_arm_vixl.h"
31#include "utils/arm/managed_register_arm.h"
32#include "utils/mips/managed_register_mips.h"
33#include "utils/mips64/managed_register_mips64.h"
34#include "utils/x86/managed_register_x86.h"
35
36#include "gtest/gtest.h"
37
38namespace art {
39
40// Return all combinations of ISA and code generator that are executable on
41// hardware, or on simulator, and that we'd like to test.
42static ::std::vector<CodegenTargetConfig> GetTargetConfigs() {
43  ::std::vector<CodegenTargetConfig> v;
44  ::std::vector<CodegenTargetConfig> test_config_candidates = {
45#ifdef ART_ENABLE_CODEGEN_arm
46    // TODO: Should't this be `kThumb2` instead of `kArm` here?
47    CodegenTargetConfig(kArm, create_codegen_arm_vixl32),
48#endif
49#ifdef ART_ENABLE_CODEGEN_arm64
50    CodegenTargetConfig(kArm64, create_codegen_arm64),
51#endif
52#ifdef ART_ENABLE_CODEGEN_x86
53    CodegenTargetConfig(kX86, create_codegen_x86),
54#endif
55#ifdef ART_ENABLE_CODEGEN_x86_64
56    CodegenTargetConfig(kX86_64, create_codegen_x86_64),
57#endif
58#ifdef ART_ENABLE_CODEGEN_mips
59    CodegenTargetConfig(kMips, create_codegen_mips),
60#endif
61#ifdef ART_ENABLE_CODEGEN_mips64
62    CodegenTargetConfig(kMips64, create_codegen_mips64)
63#endif
64  };
65
66  for (const CodegenTargetConfig& test_config : test_config_candidates) {
67    if (CanExecute(test_config.GetInstructionSet())) {
68      v.push_back(test_config);
69    }
70  }
71
72  return v;
73}
74
75static void TestCode(const uint16_t* data,
76                     bool has_result = false,
77                     int32_t expected = 0) {
78  for (const CodegenTargetConfig& target_config : GetTargetConfigs()) {
79    ArenaPool pool;
80    ArenaAllocator arena(&pool);
81    HGraph* graph = CreateCFG(&arena, data);
82    // Remove suspend checks, they cannot be executed in this context.
83    RemoveSuspendChecks(graph);
84    RunCode(target_config, graph, [](HGraph*) {}, has_result, expected);
85  }
86}
87
88static void TestCodeLong(const uint16_t* data,
89                         bool has_result,
90                         int64_t expected) {
91  for (const CodegenTargetConfig& target_config : GetTargetConfigs()) {
92    ArenaPool pool;
93    ArenaAllocator arena(&pool);
94    HGraph* graph = CreateCFG(&arena, data, Primitive::kPrimLong);
95    // Remove suspend checks, they cannot be executed in this context.
96    RemoveSuspendChecks(graph);
97    RunCode(target_config, graph, [](HGraph*) {}, has_result, expected);
98  }
99}
100
101class CodegenTest : public CommonCompilerTest {};
102
103TEST_F(CodegenTest, ReturnVoid) {
104  const uint16_t data[] = ZERO_REGISTER_CODE_ITEM(Instruction::RETURN_VOID);
105  TestCode(data);
106}
107
108TEST_F(CodegenTest, CFG1) {
109  const uint16_t data[] = ZERO_REGISTER_CODE_ITEM(
110    Instruction::GOTO | 0x100,
111    Instruction::RETURN_VOID);
112
113  TestCode(data);
114}
115
116TEST_F(CodegenTest, CFG2) {
117  const 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
125TEST_F(CodegenTest, CFG3) {
126  const 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 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 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
148TEST_F(CodegenTest, CFG4) {
149  const 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
157TEST_F(CodegenTest, CFG5) {
158  const 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
167TEST_F(CodegenTest, IntConstant) {
168  const uint16_t data[] = ONE_REGISTER_CODE_ITEM(
169    Instruction::CONST_4 | 0 | 0,
170    Instruction::RETURN_VOID);
171
172  TestCode(data);
173}
174
175TEST_F(CodegenTest, Return1) {
176  const 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
183TEST_F(CodegenTest, Return2) {
184  const 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
192TEST_F(CodegenTest, Return3) {
193  const 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
201TEST_F(CodegenTest, ReturnIf1) {
202  const 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
212TEST_F(CodegenTest, ReturnIf2) {
213  const 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) \
225TEST_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 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
237NOT_INT_TEST(ReturnNotIntMinus2, -2, 1)
238NOT_INT_TEST(ReturnNotIntMinus1, -1, 0)
239NOT_INT_TEST(ReturnNotInt0, 0, -1)
240NOT_INT_TEST(ReturnNotInt1, 1, -2)
241NOT_INT_TEST(ReturnNotIntINT32_MIN, -2147483648, 2147483647)  // (2^31) - 1
242NOT_INT_TEST(ReturnNotIntINT32_MINPlus1, -2147483647, 2147483646)  // (2^31) - 2
243NOT_INT_TEST(ReturnNotIntINT32_MAXMinus1, 2147483646, -2147483647)  // -(2^31) - 1
244NOT_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)                 \
250TEST_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 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
264NOT_LONG_TEST(ReturnNotLongMinus2, INT64_C(-2), INT64_C(1))
265NOT_LONG_TEST(ReturnNotLongMinus1, INT64_C(-1), INT64_C(0))
266NOT_LONG_TEST(ReturnNotLong0, INT64_C(0), INT64_C(-1))
267NOT_LONG_TEST(ReturnNotLong1, INT64_C(1), INT64_C(-2))
268
269NOT_LONG_TEST(ReturnNotLongINT32_MIN,
270              INT64_C(-2147483648),
271              INT64_C(2147483647))  // (2^31) - 1
272NOT_LONG_TEST(ReturnNotLongINT32_MINPlus1,
273              INT64_C(-2147483647),
274              INT64_C(2147483646))  // (2^31) - 2
275NOT_LONG_TEST(ReturnNotLongINT32_MAXMinus1,
276              INT64_C(2147483646),
277              INT64_C(-2147483647))  // -(2^31) - 1
278NOT_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.
285NOT_LONG_TEST(ReturnNotINT64_MIN,
286              INT64_C(-9223372036854775807)-1,
287              INT64_C(9223372036854775807));  // (2^63) - 1
288NOT_LONG_TEST(ReturnNotINT64_MINPlus1,
289              INT64_C(-9223372036854775807),
290              INT64_C(9223372036854775806));  // (2^63) - 2
291NOT_LONG_TEST(ReturnNotLongINT64_MAXMinus1,
292              INT64_C(9223372036854775806),
293              INT64_C(-9223372036854775807));  // -(2^63) - 1
294NOT_LONG_TEST(ReturnNotLongINT64_MAX,
295              INT64_C(9223372036854775807),
296              INT64_C(-9223372036854775807)-1);  // -(2^63)
297
298#undef NOT_LONG_TEST
299
300TEST_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 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
317TEST_F(CodegenTest, ReturnAdd1) {
318  const 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
327TEST_F(CodegenTest, ReturnAdd2) {
328  const 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
337TEST_F(CodegenTest, ReturnAdd3) {
338  const 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
346TEST_F(CodegenTest, ReturnAdd4) {
347  const 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
355TEST_F(CodegenTest, ReturnMulInt) {
356  const 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
365TEST_F(CodegenTest, ReturnMulInt2addr) {
366  const 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
375TEST_F(CodegenTest, ReturnMulLong) {
376  const 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
385TEST_F(CodegenTest, ReturnMulLong2addr) {
386  const 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
395TEST_F(CodegenTest, ReturnMulIntLit8) {
396  const 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
404TEST_F(CodegenTest, ReturnMulIntLit16) {
405  const 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
413TEST_F(CodegenTest, NonMaterializedCondition) {
414  for (CodegenTargetConfig target_config : GetTargetConfigs()) {
415    ArenaPool pool;
416    ArenaAllocator allocator(&pool);
417
418    HGraph* graph = CreateGraph(&allocator);
419
420    HBasicBlock* entry = new (&allocator) HBasicBlock(graph);
421    graph->AddBlock(entry);
422    graph->SetEntryBlock(entry);
423    entry->AddInstruction(new (&allocator) HGoto());
424
425    HBasicBlock* first_block = new (&allocator) HBasicBlock(graph);
426    graph->AddBlock(first_block);
427    entry->AddSuccessor(first_block);
428    HIntConstant* constant0 = graph->GetIntConstant(0);
429    HIntConstant* constant1 = graph->GetIntConstant(1);
430    HEqual* equal = new (&allocator) HEqual(constant0, constant0);
431    first_block->AddInstruction(equal);
432    first_block->AddInstruction(new (&allocator) HIf(equal));
433
434    HBasicBlock* then_block = new (&allocator) HBasicBlock(graph);
435    HBasicBlock* else_block = new (&allocator) HBasicBlock(graph);
436    HBasicBlock* exit_block = new (&allocator) HBasicBlock(graph);
437    graph->SetExitBlock(exit_block);
438
439    graph->AddBlock(then_block);
440    graph->AddBlock(else_block);
441    graph->AddBlock(exit_block);
442    first_block->AddSuccessor(then_block);
443    first_block->AddSuccessor(else_block);
444    then_block->AddSuccessor(exit_block);
445    else_block->AddSuccessor(exit_block);
446
447    exit_block->AddInstruction(new (&allocator) HExit());
448    then_block->AddInstruction(new (&allocator) HReturn(constant0));
449    else_block->AddInstruction(new (&allocator) HReturn(constant1));
450
451    ASSERT_FALSE(equal->IsEmittedAtUseSite());
452    graph->BuildDominatorTree();
453    PrepareForRegisterAllocation(graph).Run();
454    ASSERT_TRUE(equal->IsEmittedAtUseSite());
455
456    auto hook_before_codegen = [](HGraph* graph_in) {
457      HBasicBlock* block = graph_in->GetEntryBlock()->GetSuccessors()[0];
458      HParallelMove* move = new (graph_in->GetArena()) HParallelMove(graph_in->GetArena());
459      block->InsertInstructionBefore(move, block->GetLastInstruction());
460    };
461
462    RunCode(target_config, graph, hook_before_codegen, true, 0);
463  }
464}
465
466TEST_F(CodegenTest, MaterializedCondition1) {
467  for (CodegenTargetConfig target_config : GetTargetConfigs()) {
468    // Check that condition are materialized correctly. A materialized condition
469    // should yield `1` if it evaluated to true, and `0` otherwise.
470    // We force the materialization of comparisons for different combinations of
471
472    // inputs and check the results.
473
474    int lhs[] = {1, 2, -1, 2, 0xabc};
475    int rhs[] = {2, 1, 2, -1, 0xabc};
476
477    for (size_t i = 0; i < arraysize(lhs); i++) {
478      ArenaPool pool;
479      ArenaAllocator allocator(&pool);
480      HGraph* graph = CreateGraph(&allocator);
481
482      HBasicBlock* entry_block = new (&allocator) HBasicBlock(graph);
483      graph->AddBlock(entry_block);
484      graph->SetEntryBlock(entry_block);
485      entry_block->AddInstruction(new (&allocator) HGoto());
486      HBasicBlock* code_block = new (&allocator) HBasicBlock(graph);
487      graph->AddBlock(code_block);
488      HBasicBlock* exit_block = new (&allocator) HBasicBlock(graph);
489      graph->AddBlock(exit_block);
490      exit_block->AddInstruction(new (&allocator) HExit());
491
492      entry_block->AddSuccessor(code_block);
493      code_block->AddSuccessor(exit_block);
494      graph->SetExitBlock(exit_block);
495
496      HIntConstant* cst_lhs = graph->GetIntConstant(lhs[i]);
497      HIntConstant* cst_rhs = graph->GetIntConstant(rhs[i]);
498      HLessThan cmp_lt(cst_lhs, cst_rhs);
499      code_block->AddInstruction(&cmp_lt);
500      HReturn ret(&cmp_lt);
501      code_block->AddInstruction(&ret);
502
503      graph->BuildDominatorTree();
504      auto hook_before_codegen = [](HGraph* graph_in) {
505        HBasicBlock* block = graph_in->GetEntryBlock()->GetSuccessors()[0];
506        HParallelMove* move = new (graph_in->GetArena()) HParallelMove(graph_in->GetArena());
507        block->InsertInstructionBefore(move, block->GetLastInstruction());
508      };
509      RunCode(target_config, graph, hook_before_codegen, true, lhs[i] < rhs[i]);
510    }
511  }
512}
513
514TEST_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      ArenaPool pool;
527      ArenaAllocator allocator(&pool);
528      HGraph* graph = CreateGraph(&allocator);
529
530      HBasicBlock* entry_block = new (&allocator) HBasicBlock(graph);
531      graph->AddBlock(entry_block);
532      graph->SetEntryBlock(entry_block);
533      entry_block->AddInstruction(new (&allocator) HGoto());
534
535      HBasicBlock* if_block = new (&allocator) HBasicBlock(graph);
536      graph->AddBlock(if_block);
537      HBasicBlock* if_true_block = new (&allocator) HBasicBlock(graph);
538      graph->AddBlock(if_true_block);
539      HBasicBlock* if_false_block = new (&allocator) HBasicBlock(graph);
540      graph->AddBlock(if_false_block);
541      HBasicBlock* exit_block = new (&allocator) HBasicBlock(graph);
542      graph->AddBlock(exit_block);
543      exit_block->AddInstruction(new (&allocator) HExit());
544
545      graph->SetEntryBlock(entry_block);
546      entry_block->AddSuccessor(if_block);
547      if_block->AddSuccessor(if_true_block);
548      if_block->AddSuccessor(if_false_block);
549      if_true_block->AddSuccessor(exit_block);
550      if_false_block->AddSuccessor(exit_block);
551      graph->SetExitBlock(exit_block);
552
553      HIntConstant* cst_lhs = graph->GetIntConstant(lhs[i]);
554      HIntConstant* cst_rhs = graph->GetIntConstant(rhs[i]);
555      HLessThan cmp_lt(cst_lhs, cst_rhs);
556      if_block->AddInstruction(&cmp_lt);
557      // We insert a dummy instruction to separate the HIf from the HLessThan
558      // and force the materialization of the condition.
559      HMemoryBarrier force_materialization(MemBarrierKind::kAnyAny, 0);
560      if_block->AddInstruction(&force_materialization);
561      HIf if_lt(&cmp_lt);
562      if_block->AddInstruction(&if_lt);
563
564      HIntConstant* cst_lt = graph->GetIntConstant(1);
565      HReturn ret_lt(cst_lt);
566      if_true_block->AddInstruction(&ret_lt);
567      HIntConstant* cst_ge = graph->GetIntConstant(0);
568      HReturn ret_ge(cst_ge);
569      if_false_block->AddInstruction(&ret_ge);
570
571      graph->BuildDominatorTree();
572      auto hook_before_codegen = [](HGraph* graph_in) {
573        HBasicBlock* block = graph_in->GetEntryBlock()->GetSuccessors()[0];
574        HParallelMove* move = new (graph_in->GetArena()) HParallelMove(graph_in->GetArena());
575        block->InsertInstructionBefore(move, block->GetLastInstruction());
576      };
577      RunCode(target_config, graph, hook_before_codegen, true, lhs[i] < rhs[i]);
578    }
579  }
580}
581
582TEST_F(CodegenTest, ReturnDivIntLit8) {
583  const uint16_t data[] = ONE_REGISTER_CODE_ITEM(
584    Instruction::CONST_4 | 4 << 12 | 0 << 8,
585    Instruction::DIV_INT_LIT8, 3 << 8 | 0,
586    Instruction::RETURN);
587
588  TestCode(data, true, 1);
589}
590
591TEST_F(CodegenTest, ReturnDivInt2Addr) {
592  const uint16_t data[] = TWO_REGISTERS_CODE_ITEM(
593    Instruction::CONST_4 | 4 << 12 | 0,
594    Instruction::CONST_4 | 2 << 12 | 1 << 8,
595    Instruction::DIV_INT_2ADDR | 1 << 12,
596    Instruction::RETURN);
597
598  TestCode(data, true, 2);
599}
600
601// Helper method.
602static void TestComparison(IfCondition condition,
603                           int64_t i,
604                           int64_t j,
605                           Primitive::Type type,
606                           const CodegenTargetConfig target_config) {
607  ArenaPool pool;
608  ArenaAllocator allocator(&pool);
609  HGraph* graph = CreateGraph(&allocator);
610
611  HBasicBlock* entry_block = new (&allocator) HBasicBlock(graph);
612  graph->AddBlock(entry_block);
613  graph->SetEntryBlock(entry_block);
614  entry_block->AddInstruction(new (&allocator) HGoto());
615
616  HBasicBlock* block = new (&allocator) HBasicBlock(graph);
617  graph->AddBlock(block);
618
619  HBasicBlock* exit_block = new (&allocator) HBasicBlock(graph);
620  graph->AddBlock(exit_block);
621  graph->SetExitBlock(exit_block);
622  exit_block->AddInstruction(new (&allocator) HExit());
623
624  entry_block->AddSuccessor(block);
625  block->AddSuccessor(exit_block);
626
627  HInstruction* op1;
628  HInstruction* op2;
629  if (type == Primitive::kPrimInt) {
630    op1 = graph->GetIntConstant(i);
631    op2 = graph->GetIntConstant(j);
632  } else {
633    DCHECK_EQ(type, Primitive::kPrimLong);
634    op1 = graph->GetLongConstant(i);
635    op2 = graph->GetLongConstant(j);
636  }
637
638  HInstruction* comparison = nullptr;
639  bool expected_result = false;
640  const uint64_t x = i;
641  const uint64_t y = j;
642  switch (condition) {
643    case kCondEQ:
644      comparison = new (&allocator) HEqual(op1, op2);
645      expected_result = (i == j);
646      break;
647    case kCondNE:
648      comparison = new (&allocator) HNotEqual(op1, op2);
649      expected_result = (i != j);
650      break;
651    case kCondLT:
652      comparison = new (&allocator) HLessThan(op1, op2);
653      expected_result = (i < j);
654      break;
655    case kCondLE:
656      comparison = new (&allocator) HLessThanOrEqual(op1, op2);
657      expected_result = (i <= j);
658      break;
659    case kCondGT:
660      comparison = new (&allocator) HGreaterThan(op1, op2);
661      expected_result = (i > j);
662      break;
663    case kCondGE:
664      comparison = new (&allocator) HGreaterThanOrEqual(op1, op2);
665      expected_result = (i >= j);
666      break;
667    case kCondB:
668      comparison = new (&allocator) HBelow(op1, op2);
669      expected_result = (x < y);
670      break;
671    case kCondBE:
672      comparison = new (&allocator) HBelowOrEqual(op1, op2);
673      expected_result = (x <= y);
674      break;
675    case kCondA:
676      comparison = new (&allocator) HAbove(op1, op2);
677      expected_result = (x > y);
678      break;
679    case kCondAE:
680      comparison = new (&allocator) HAboveOrEqual(op1, op2);
681      expected_result = (x >= y);
682      break;
683  }
684  block->AddInstruction(comparison);
685  block->AddInstruction(new (&allocator) HReturn(comparison));
686
687  graph->BuildDominatorTree();
688  RunCode(target_config, graph, [](HGraph*) {}, true, expected_result);
689}
690
691TEST_F(CodegenTest, ComparisonsInt) {
692  for (CodegenTargetConfig target_config : GetTargetConfigs()) {
693    for (int64_t i = -1; i <= 1; i++) {
694      for (int64_t j = -1; j <= 1; j++) {
695        for (int cond = kCondFirst; cond <= kCondLast; cond++) {
696          TestComparison(static_cast<IfCondition>(cond), i, j, Primitive::kPrimInt, target_config);
697        }
698      }
699    }
700  }
701}
702
703TEST_F(CodegenTest, ComparisonsLong) {
704  for (CodegenTargetConfig target_config : GetTargetConfigs()) {
705    for (int64_t i = -1; i <= 1; i++) {
706      for (int64_t j = -1; j <= 1; j++) {
707        for (int cond = kCondFirst; cond <= kCondLast; cond++) {
708          TestComparison(static_cast<IfCondition>(cond), i, j, Primitive::kPrimLong, target_config);
709        }
710      }
711    }
712  }
713}
714
715#ifdef ART_ENABLE_CODEGEN_arm
716TEST_F(CodegenTest, ARMVIXLParallelMoveResolver) {
717  std::unique_ptr<const ArmInstructionSetFeatures> features(
718      ArmInstructionSetFeatures::FromCppDefines());
719  ArenaPool pool;
720  ArenaAllocator allocator(&pool);
721  HGraph* graph = CreateGraph(&allocator);
722  arm::CodeGeneratorARMVIXL codegen(graph, *features.get(), CompilerOptions());
723
724  codegen.Initialize();
725
726  // This will result in calling EmitSwap -> void ParallelMoveResolverARMVIXL::Exchange(int mem1,
727  // int mem2) which was faulty (before the fix). So previously GPR and FP scratch registers were
728  // used as temps; however GPR scratch register is required for big stack offsets which don't fit
729  // LDR encoding. So the following code is a regression test for that situation.
730  HParallelMove* move = new (graph->GetArena()) HParallelMove(graph->GetArena());
731  move->AddMove(Location::StackSlot(0), Location::StackSlot(8192), Primitive::kPrimInt, nullptr);
732  move->AddMove(Location::StackSlot(8192), Location::StackSlot(0), Primitive::kPrimInt, nullptr);
733  codegen.GetMoveResolver()->EmitNativeCode(move);
734
735  InternalCodeAllocator code_allocator;
736  codegen.Finalize(&code_allocator);
737}
738#endif
739
740#ifdef ART_ENABLE_CODEGEN_arm64
741// Regression test for b/34760542.
742TEST_F(CodegenTest, ARM64ParallelMoveResolverB34760542) {
743  std::unique_ptr<const Arm64InstructionSetFeatures> features(
744      Arm64InstructionSetFeatures::FromCppDefines());
745  ArenaPool pool;
746  ArenaAllocator allocator(&pool);
747  HGraph* graph = CreateGraph(&allocator);
748  arm64::CodeGeneratorARM64 codegen(graph, *features.get(), CompilerOptions());
749
750  codegen.Initialize();
751
752  // The following ParallelMove used to fail this assertion:
753  //
754  //   Assertion failed (!available->IsEmpty())
755  //
756  // in vixl::aarch64::UseScratchRegisterScope::AcquireNextAvailable,
757  // because of the following situation:
758  //
759  //   1. a temp register (IP0) is allocated as a scratch register by
760  //      the parallel move resolver to solve a cycle (swap):
761  //
762  //        [ source=DS0 destination=DS257 type=PrimDouble instruction=null ]
763  //        [ source=DS257 destination=DS0 type=PrimDouble instruction=null ]
764  //
765  //   2. within CodeGeneratorARM64::MoveLocation, another temp
766  //      register (IP1) is allocated to generate the swap between two
767  //      double stack slots;
768  //
769  //   3. VIXL requires a third temp register to emit the `Ldr` or
770  //      `Str` operation from CodeGeneratorARM64::MoveLocation (as
771  //      one of the stack slots' offsets cannot be encoded as an
772  //      immediate), but the pool of (core) temp registers is now
773  //      empty.
774  //
775  // The solution used so far is to use a floating-point temp register
776  // (D31) in step #2, so that IP1 is available for step #3.
777
778  HParallelMove* move = new (graph->GetArena()) HParallelMove(graph->GetArena());
779  move->AddMove(Location::DoubleStackSlot(0),
780                Location::DoubleStackSlot(257),
781                Primitive::kPrimDouble,
782                nullptr);
783  move->AddMove(Location::DoubleStackSlot(257),
784                Location::DoubleStackSlot(0),
785                Primitive::kPrimDouble,
786                nullptr);
787  codegen.GetMoveResolver()->EmitNativeCode(move);
788
789  InternalCodeAllocator code_allocator;
790  codegen.Finalize(&code_allocator);
791}
792
793// Check that ParallelMoveResolver works fine for ARM64 for both cases when SIMD is on and off.
794TEST_F(CodegenTest, ARM64ParallelMoveResolverSIMD) {
795  std::unique_ptr<const Arm64InstructionSetFeatures> features(
796      Arm64InstructionSetFeatures::FromCppDefines());
797  ArenaPool pool;
798  ArenaAllocator allocator(&pool);
799  HGraph* graph = CreateGraph(&allocator);
800  arm64::CodeGeneratorARM64 codegen(graph, *features.get(), CompilerOptions());
801
802  codegen.Initialize();
803
804  graph->SetHasSIMD(true);
805  for (int i = 0; i < 2; i++) {
806    HParallelMove* move = new (graph->GetArena()) HParallelMove(graph->GetArena());
807    move->AddMove(Location::SIMDStackSlot(0),
808                  Location::SIMDStackSlot(257),
809                  Primitive::kPrimDouble,
810                  nullptr);
811    move->AddMove(Location::SIMDStackSlot(257),
812                  Location::SIMDStackSlot(0),
813                  Primitive::kPrimDouble,
814                  nullptr);
815    move->AddMove(Location::FpuRegisterLocation(0),
816                  Location::FpuRegisterLocation(1),
817                  Primitive::kPrimDouble,
818                  nullptr);
819    move->AddMove(Location::FpuRegisterLocation(1),
820                  Location::FpuRegisterLocation(0),
821                  Primitive::kPrimDouble,
822                  nullptr);
823    codegen.GetMoveResolver()->EmitNativeCode(move);
824    graph->SetHasSIMD(false);
825  }
826
827  InternalCodeAllocator code_allocator;
828  codegen.Finalize(&code_allocator);
829}
830#endif
831
832#ifdef ART_ENABLE_CODEGEN_mips
833TEST_F(CodegenTest, MipsClobberRA) {
834  std::unique_ptr<const MipsInstructionSetFeatures> features_mips(
835      MipsInstructionSetFeatures::FromCppDefines());
836  if (!CanExecute(kMips) || features_mips->IsR6()) {
837    // HMipsComputeBaseMethodAddress and the NAL instruction behind it
838    // should only be generated on non-R6.
839    return;
840  }
841
842  ArenaPool pool;
843  ArenaAllocator allocator(&pool);
844  HGraph* graph = CreateGraph(&allocator);
845
846  HBasicBlock* entry_block = new (&allocator) HBasicBlock(graph);
847  graph->AddBlock(entry_block);
848  graph->SetEntryBlock(entry_block);
849  entry_block->AddInstruction(new (&allocator) HGoto());
850
851  HBasicBlock* block = new (&allocator) HBasicBlock(graph);
852  graph->AddBlock(block);
853
854  HBasicBlock* exit_block = new (&allocator) HBasicBlock(graph);
855  graph->AddBlock(exit_block);
856  graph->SetExitBlock(exit_block);
857  exit_block->AddInstruction(new (&allocator) HExit());
858
859  entry_block->AddSuccessor(block);
860  block->AddSuccessor(exit_block);
861
862  // To simplify matters, don't create PC-relative HLoadClass or HLoadString.
863  // Instead, generate HMipsComputeBaseMethodAddress directly.
864  HMipsComputeBaseMethodAddress* base = new (&allocator) HMipsComputeBaseMethodAddress();
865  block->AddInstruction(base);
866  // HMipsComputeBaseMethodAddress is defined as int, so just make the
867  // compiled method return it.
868  block->AddInstruction(new (&allocator) HReturn(base));
869
870  graph->BuildDominatorTree();
871
872  mips::CodeGeneratorMIPS codegenMIPS(graph, *features_mips.get(), CompilerOptions());
873  // Since there isn't HLoadClass or HLoadString, we need to manually indicate
874  // that RA is clobbered and the method entry code should generate a stack frame
875  // and preserve RA in it. And this is what we're testing here.
876  codegenMIPS.ClobberRA();
877  // Without ClobberRA() the code would be:
878  //   nal              # Sets RA to point to the jr instruction below
879  //   move  v0, ra     # and the CPU falls into an infinite loop.
880  //   jr    ra
881  //   nop
882  // The expected code is:
883  //   addiu sp, sp, -16
884  //   sw    ra, 12(sp)
885  //   sw    a0, 0(sp)
886  //   nal              # Sets RA to point to the lw instruction below.
887  //   move  v0, ra
888  //   lw    ra, 12(sp)
889  //   jr    ra
890  //   addiu sp, sp, 16
891  RunCode(&codegenMIPS, graph, [](HGraph*) {}, false, 0);
892}
893#endif
894
895}  // namespace art
896