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