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