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