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