codegen_test.cc revision 4cd27d64b0bbdde61fa3f6674ceb24221853ac2c
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 19#include "arch/instruction_set.h" 20#include "arch/arm/instruction_set_features_arm.h" 21#include "arch/arm/registers_arm.h" 22#include "arch/arm64/instruction_set_features_arm64.h" 23#include "arch/mips64/instruction_set_features_mips64.h" 24#include "arch/mips64/registers_mips64.h" 25#include "arch/x86/instruction_set_features_x86.h" 26#include "arch/x86/registers_x86.h" 27#include "arch/x86_64/instruction_set_features_x86_64.h" 28#include "base/macros.h" 29#include "builder.h" 30#include "code_generator_arm.h" 31#include "code_generator_arm64.h" 32#include "code_generator_mips64.h" 33#include "code_generator_x86.h" 34#include "code_generator_x86_64.h" 35#include "common_compiler_test.h" 36#include "dex_file.h" 37#include "dex_instruction.h" 38#include "driver/compiler_options.h" 39#include "nodes.h" 40#include "optimizing_unit_test.h" 41#include "prepare_for_register_allocation.h" 42#include "register_allocator.h" 43#include "ssa_liveness_analysis.h" 44#include "utils.h" 45#include "utils/arm/managed_register_arm.h" 46#include "utils/mips64/managed_register_mips64.h" 47#include "utils/x86/managed_register_x86.h" 48 49#include "gtest/gtest.h" 50 51namespace art { 52 53// Provide our own codegen, that ensures the C calling conventions 54// are preserved. Currently, ART and C do not match as R4 is caller-save 55// in ART, and callee-save in C. Alternatively, we could use or write 56// the stub that saves and restores all registers, but it is easier 57// to just overwrite the code generator. 58class TestCodeGeneratorARM : public arm::CodeGeneratorARM { 59 public: 60 TestCodeGeneratorARM(HGraph* graph, 61 const ArmInstructionSetFeatures& isa_features, 62 const CompilerOptions& compiler_options) 63 : arm::CodeGeneratorARM(graph, isa_features, compiler_options) { 64 AddAllocatedRegister(Location::RegisterLocation(arm::R6)); 65 AddAllocatedRegister(Location::RegisterLocation(arm::R7)); 66 } 67 68 void SetupBlockedRegisters(bool is_baseline) const OVERRIDE { 69 arm::CodeGeneratorARM::SetupBlockedRegisters(is_baseline); 70 blocked_core_registers_[arm::R4] = true; 71 blocked_core_registers_[arm::R6] = false; 72 blocked_core_registers_[arm::R7] = false; 73 // Makes pair R6-R7 available. 74 blocked_register_pairs_[arm::R6_R7] = false; 75 } 76}; 77 78class TestCodeGeneratorX86 : public x86::CodeGeneratorX86 { 79 public: 80 TestCodeGeneratorX86(HGraph* graph, 81 const X86InstructionSetFeatures& isa_features, 82 const CompilerOptions& compiler_options) 83 : x86::CodeGeneratorX86(graph, isa_features, compiler_options) { 84 // Save edi, we need it for getting enough registers for long multiplication. 85 AddAllocatedRegister(Location::RegisterLocation(x86::EDI)); 86 } 87 88 void SetupBlockedRegisters(bool is_baseline) const OVERRIDE { 89 x86::CodeGeneratorX86::SetupBlockedRegisters(is_baseline); 90 // ebx is a callee-save register in C, but caller-save for ART. 91 blocked_core_registers_[x86::EBX] = true; 92 blocked_register_pairs_[x86::EAX_EBX] = true; 93 blocked_register_pairs_[x86::EDX_EBX] = true; 94 blocked_register_pairs_[x86::ECX_EBX] = true; 95 blocked_register_pairs_[x86::EBX_EDI] = true; 96 97 // Make edi available. 98 blocked_core_registers_[x86::EDI] = false; 99 blocked_register_pairs_[x86::ECX_EDI] = false; 100 } 101}; 102 103class InternalCodeAllocator : public CodeAllocator { 104 public: 105 InternalCodeAllocator() : size_(0) { } 106 107 virtual uint8_t* Allocate(size_t size) { 108 size_ = size; 109 memory_.reset(new uint8_t[size]); 110 return memory_.get(); 111 } 112 113 size_t GetSize() const { return size_; } 114 uint8_t* GetMemory() const { return memory_.get(); } 115 116 private: 117 size_t size_; 118 std::unique_ptr<uint8_t[]> memory_; 119 120 DISALLOW_COPY_AND_ASSIGN(InternalCodeAllocator); 121}; 122 123template <typename Expected> 124static void Run(const InternalCodeAllocator& allocator, 125 const CodeGenerator& codegen, 126 bool has_result, 127 Expected expected) { 128 typedef Expected (*fptr)(); 129 CommonCompilerTest::MakeExecutable(allocator.GetMemory(), allocator.GetSize()); 130 fptr f = reinterpret_cast<fptr>(allocator.GetMemory()); 131 if (codegen.GetInstructionSet() == kThumb2) { 132 // For thumb we need the bottom bit set. 133 f = reinterpret_cast<fptr>(reinterpret_cast<uintptr_t>(f) + 1); 134 } 135 Expected result = f(); 136 if (has_result) { 137 ASSERT_EQ(expected, result); 138 } 139} 140 141template <typename Expected> 142static void RunCodeBaseline(HGraph* graph, bool has_result, Expected expected) { 143 InternalCodeAllocator allocator; 144 145 CompilerOptions compiler_options; 146 std::unique_ptr<const X86InstructionSetFeatures> features_x86( 147 X86InstructionSetFeatures::FromCppDefines()); 148 TestCodeGeneratorX86 codegenX86(graph, *features_x86.get(), compiler_options); 149 // We avoid doing a stack overflow check that requires the runtime being setup, 150 // by making sure the compiler knows the methods we are running are leaf methods. 151 codegenX86.CompileBaseline(&allocator, true); 152 if (kRuntimeISA == kX86) { 153 Run(allocator, codegenX86, has_result, expected); 154 } 155 156 std::unique_ptr<const ArmInstructionSetFeatures> features_arm( 157 ArmInstructionSetFeatures::FromCppDefines()); 158 TestCodeGeneratorARM codegenARM(graph, *features_arm.get(), compiler_options); 159 codegenARM.CompileBaseline(&allocator, true); 160 if (kRuntimeISA == kArm || kRuntimeISA == kThumb2) { 161 Run(allocator, codegenARM, has_result, expected); 162 } 163 164 std::unique_ptr<const X86_64InstructionSetFeatures> features_x86_64( 165 X86_64InstructionSetFeatures::FromCppDefines()); 166 x86_64::CodeGeneratorX86_64 codegenX86_64(graph, *features_x86_64.get(), compiler_options); 167 codegenX86_64.CompileBaseline(&allocator, true); 168 if (kRuntimeISA == kX86_64) { 169 Run(allocator, codegenX86_64, has_result, expected); 170 } 171 172 std::unique_ptr<const Arm64InstructionSetFeatures> features_arm64( 173 Arm64InstructionSetFeatures::FromCppDefines()); 174 arm64::CodeGeneratorARM64 codegenARM64(graph, *features_arm64.get(), compiler_options); 175 codegenARM64.CompileBaseline(&allocator, true); 176 if (kRuntimeISA == kArm64) { 177 Run(allocator, codegenARM64, has_result, expected); 178 } 179 180 std::unique_ptr<const Mips64InstructionSetFeatures> features_mips64( 181 Mips64InstructionSetFeatures::FromCppDefines()); 182 mips64::CodeGeneratorMIPS64 codegenMIPS64(graph, *features_mips64.get(), compiler_options); 183 codegenMIPS64.CompileBaseline(&allocator, true); 184 if (kRuntimeISA == kMips64) { 185 Run(allocator, codegenMIPS64, has_result, expected); 186 } 187} 188 189template <typename Expected> 190static void RunCodeOptimized(CodeGenerator* codegen, 191 HGraph* graph, 192 std::function<void(HGraph*)> hook_before_codegen, 193 bool has_result, 194 Expected expected) { 195 // Tests may have already computed it. 196 if (graph->GetReversePostOrder().IsEmpty()) { 197 graph->BuildDominatorTree(); 198 } 199 SsaLivenessAnalysis liveness(graph, codegen); 200 liveness.Analyze(); 201 202 RegisterAllocator register_allocator(graph->GetArena(), codegen, liveness); 203 register_allocator.AllocateRegisters(); 204 hook_before_codegen(graph); 205 206 InternalCodeAllocator allocator; 207 codegen->CompileOptimized(&allocator); 208 Run(allocator, *codegen, has_result, expected); 209} 210 211template <typename Expected> 212static void RunCodeOptimized(HGraph* graph, 213 std::function<void(HGraph*)> hook_before_codegen, 214 bool has_result, 215 Expected expected) { 216 CompilerOptions compiler_options; 217 if (kRuntimeISA == kArm || kRuntimeISA == kThumb2) { 218 TestCodeGeneratorARM codegenARM(graph, 219 *ArmInstructionSetFeatures::FromCppDefines(), 220 compiler_options); 221 RunCodeOptimized(&codegenARM, graph, hook_before_codegen, has_result, expected); 222 } else if (kRuntimeISA == kArm64) { 223 arm64::CodeGeneratorARM64 codegenARM64(graph, 224 *Arm64InstructionSetFeatures::FromCppDefines(), 225 compiler_options); 226 RunCodeOptimized(&codegenARM64, graph, hook_before_codegen, has_result, expected); 227 } else if (kRuntimeISA == kX86) { 228 std::unique_ptr<const X86InstructionSetFeatures> features_x86( 229 X86InstructionSetFeatures::FromCppDefines()); 230 x86::CodeGeneratorX86 codegenX86(graph, *features_x86.get(), compiler_options); 231 RunCodeOptimized(&codegenX86, graph, hook_before_codegen, has_result, expected); 232 } else if (kRuntimeISA == kX86_64) { 233 std::unique_ptr<const X86_64InstructionSetFeatures> features_x86_64( 234 X86_64InstructionSetFeatures::FromCppDefines()); 235 x86_64::CodeGeneratorX86_64 codegenX86_64(graph, *features_x86_64.get(), compiler_options); 236 RunCodeOptimized(&codegenX86_64, graph, hook_before_codegen, has_result, expected); 237 } else if (kRuntimeISA == kMips64) { 238 std::unique_ptr<const Mips64InstructionSetFeatures> features_mips64( 239 Mips64InstructionSetFeatures::FromCppDefines()); 240 mips64::CodeGeneratorMIPS64 codegenMIPS64(graph, *features_mips64.get(), compiler_options); 241 RunCodeOptimized(&codegenMIPS64, graph, hook_before_codegen, has_result, expected); 242 } 243} 244 245static void TestCode(const uint16_t* data, bool has_result = false, int32_t expected = 0) { 246 ArenaPool pool; 247 ArenaAllocator arena(&pool); 248 HGraph* graph = CreateGraph(&arena); 249 HGraphBuilder builder(graph); 250 const DexFile::CodeItem* item = reinterpret_cast<const DexFile::CodeItem*>(data); 251 bool graph_built = builder.BuildGraph(*item); 252 ASSERT_TRUE(graph_built); 253 // Remove suspend checks, they cannot be executed in this context. 254 RemoveSuspendChecks(graph); 255 RunCodeBaseline(graph, has_result, expected); 256} 257 258static void TestCodeLong(const uint16_t* data, bool has_result, int64_t expected) { 259 ArenaPool pool; 260 ArenaAllocator arena(&pool); 261 HGraph* graph = CreateGraph(&arena); 262 HGraphBuilder builder(graph, Primitive::kPrimLong); 263 const DexFile::CodeItem* item = reinterpret_cast<const DexFile::CodeItem*>(data); 264 bool graph_built = builder.BuildGraph(*item); 265 ASSERT_TRUE(graph_built); 266 // Remove suspend checks, they cannot be executed in this context. 267 RemoveSuspendChecks(graph); 268 RunCodeBaseline(graph, has_result, expected); 269} 270 271TEST(CodegenTest, ReturnVoid) { 272 const uint16_t data[] = ZERO_REGISTER_CODE_ITEM(Instruction::RETURN_VOID); 273 TestCode(data); 274} 275 276TEST(CodegenTest, CFG1) { 277 const uint16_t data[] = ZERO_REGISTER_CODE_ITEM( 278 Instruction::GOTO | 0x100, 279 Instruction::RETURN_VOID); 280 281 TestCode(data); 282} 283 284TEST(CodegenTest, CFG2) { 285 const uint16_t data[] = ZERO_REGISTER_CODE_ITEM( 286 Instruction::GOTO | 0x100, 287 Instruction::GOTO | 0x100, 288 Instruction::RETURN_VOID); 289 290 TestCode(data); 291} 292 293TEST(CodegenTest, CFG3) { 294 const uint16_t data1[] = ZERO_REGISTER_CODE_ITEM( 295 Instruction::GOTO | 0x200, 296 Instruction::RETURN_VOID, 297 Instruction::GOTO | 0xFF00); 298 299 TestCode(data1); 300 301 const uint16_t data2[] = ZERO_REGISTER_CODE_ITEM( 302 Instruction::GOTO_16, 3, 303 Instruction::RETURN_VOID, 304 Instruction::GOTO_16, 0xFFFF); 305 306 TestCode(data2); 307 308 const uint16_t data3[] = ZERO_REGISTER_CODE_ITEM( 309 Instruction::GOTO_32, 4, 0, 310 Instruction::RETURN_VOID, 311 Instruction::GOTO_32, 0xFFFF, 0xFFFF); 312 313 TestCode(data3); 314} 315 316TEST(CodegenTest, CFG4) { 317 const uint16_t data[] = ZERO_REGISTER_CODE_ITEM( 318 Instruction::RETURN_VOID, 319 Instruction::GOTO | 0x100, 320 Instruction::GOTO | 0xFE00); 321 322 TestCode(data); 323} 324 325TEST(CodegenTest, CFG5) { 326 const uint16_t data[] = ONE_REGISTER_CODE_ITEM( 327 Instruction::CONST_4 | 0 | 0, 328 Instruction::IF_EQ, 3, 329 Instruction::GOTO | 0x100, 330 Instruction::RETURN_VOID); 331 332 TestCode(data); 333} 334 335TEST(CodegenTest, IntConstant) { 336 const uint16_t data[] = ONE_REGISTER_CODE_ITEM( 337 Instruction::CONST_4 | 0 | 0, 338 Instruction::RETURN_VOID); 339 340 TestCode(data); 341} 342 343TEST(CodegenTest, Return1) { 344 const uint16_t data[] = ONE_REGISTER_CODE_ITEM( 345 Instruction::CONST_4 | 0 | 0, 346 Instruction::RETURN | 0); 347 348 TestCode(data, true, 0); 349} 350 351TEST(CodegenTest, Return2) { 352 const uint16_t data[] = TWO_REGISTERS_CODE_ITEM( 353 Instruction::CONST_4 | 0 | 0, 354 Instruction::CONST_4 | 0 | 1 << 8, 355 Instruction::RETURN | 1 << 8); 356 357 TestCode(data, true, 0); 358} 359 360TEST(CodegenTest, Return3) { 361 const uint16_t data[] = TWO_REGISTERS_CODE_ITEM( 362 Instruction::CONST_4 | 0 | 0, 363 Instruction::CONST_4 | 1 << 8 | 1 << 12, 364 Instruction::RETURN | 1 << 8); 365 366 TestCode(data, true, 1); 367} 368 369TEST(CodegenTest, ReturnIf1) { 370 const uint16_t data[] = TWO_REGISTERS_CODE_ITEM( 371 Instruction::CONST_4 | 0 | 0, 372 Instruction::CONST_4 | 1 << 8 | 1 << 12, 373 Instruction::IF_EQ, 3, 374 Instruction::RETURN | 0 << 8, 375 Instruction::RETURN | 1 << 8); 376 377 TestCode(data, true, 1); 378} 379 380TEST(CodegenTest, ReturnIf2) { 381 const uint16_t data[] = TWO_REGISTERS_CODE_ITEM( 382 Instruction::CONST_4 | 0 | 0, 383 Instruction::CONST_4 | 1 << 8 | 1 << 12, 384 Instruction::IF_EQ | 0 << 4 | 1 << 8, 3, 385 Instruction::RETURN | 0 << 8, 386 Instruction::RETURN | 1 << 8); 387 388 TestCode(data, true, 0); 389} 390 391// Exercise bit-wise (one's complement) not-int instruction. 392#define NOT_INT_TEST(TEST_NAME, INPUT, EXPECTED_OUTPUT) \ 393TEST(CodegenTest, TEST_NAME) { \ 394 const int32_t input = INPUT; \ 395 const uint16_t input_lo = Low16Bits(input); \ 396 const uint16_t input_hi = High16Bits(input); \ 397 const uint16_t data[] = TWO_REGISTERS_CODE_ITEM( \ 398 Instruction::CONST | 0 << 8, input_lo, input_hi, \ 399 Instruction::NOT_INT | 1 << 8 | 0 << 12 , \ 400 Instruction::RETURN | 1 << 8); \ 401 \ 402 TestCode(data, true, EXPECTED_OUTPUT); \ 403} 404 405NOT_INT_TEST(ReturnNotIntMinus2, -2, 1) 406NOT_INT_TEST(ReturnNotIntMinus1, -1, 0) 407NOT_INT_TEST(ReturnNotInt0, 0, -1) 408NOT_INT_TEST(ReturnNotInt1, 1, -2) 409NOT_INT_TEST(ReturnNotIntINT32_MIN, -2147483648, 2147483647) // (2^31) - 1 410NOT_INT_TEST(ReturnNotIntINT32_MINPlus1, -2147483647, 2147483646) // (2^31) - 2 411NOT_INT_TEST(ReturnNotIntINT32_MAXMinus1, 2147483646, -2147483647) // -(2^31) - 1 412NOT_INT_TEST(ReturnNotIntINT32_MAX, 2147483647, -2147483648) // -(2^31) 413 414#undef NOT_INT_TEST 415 416// Exercise bit-wise (one's complement) not-long instruction. 417#define NOT_LONG_TEST(TEST_NAME, INPUT, EXPECTED_OUTPUT) \ 418TEST(CodegenTest, TEST_NAME) { \ 419 const int64_t input = INPUT; \ 420 const uint16_t word0 = Low16Bits(Low32Bits(input)); /* LSW. */ \ 421 const uint16_t word1 = High16Bits(Low32Bits(input)); \ 422 const uint16_t word2 = Low16Bits(High32Bits(input)); \ 423 const uint16_t word3 = High16Bits(High32Bits(input)); /* MSW. */ \ 424 const uint16_t data[] = FOUR_REGISTERS_CODE_ITEM( \ 425 Instruction::CONST_WIDE | 0 << 8, word0, word1, word2, word3, \ 426 Instruction::NOT_LONG | 2 << 8 | 0 << 12, \ 427 Instruction::RETURN_WIDE | 2 << 8); \ 428 \ 429 TestCodeLong(data, true, EXPECTED_OUTPUT); \ 430} 431 432NOT_LONG_TEST(ReturnNotLongMinus2, INT64_C(-2), INT64_C(1)) 433NOT_LONG_TEST(ReturnNotLongMinus1, INT64_C(-1), INT64_C(0)) 434NOT_LONG_TEST(ReturnNotLong0, INT64_C(0), INT64_C(-1)) 435NOT_LONG_TEST(ReturnNotLong1, INT64_C(1), INT64_C(-2)) 436 437NOT_LONG_TEST(ReturnNotLongINT32_MIN, 438 INT64_C(-2147483648), 439 INT64_C(2147483647)) // (2^31) - 1 440NOT_LONG_TEST(ReturnNotLongINT32_MINPlus1, 441 INT64_C(-2147483647), 442 INT64_C(2147483646)) // (2^31) - 2 443NOT_LONG_TEST(ReturnNotLongINT32_MAXMinus1, 444 INT64_C(2147483646), 445 INT64_C(-2147483647)) // -(2^31) - 1 446NOT_LONG_TEST(ReturnNotLongINT32_MAX, 447 INT64_C(2147483647), 448 INT64_C(-2147483648)) // -(2^31) 449 450// Note that the C++ compiler won't accept 451// INT64_C(-9223372036854775808) (that is, INT64_MIN) as a valid 452// int64_t literal, so we use INT64_C(-9223372036854775807)-1 instead. 453NOT_LONG_TEST(ReturnNotINT64_MIN, 454 INT64_C(-9223372036854775807)-1, 455 INT64_C(9223372036854775807)); // (2^63) - 1 456NOT_LONG_TEST(ReturnNotINT64_MINPlus1, 457 INT64_C(-9223372036854775807), 458 INT64_C(9223372036854775806)); // (2^63) - 2 459NOT_LONG_TEST(ReturnNotLongINT64_MAXMinus1, 460 INT64_C(9223372036854775806), 461 INT64_C(-9223372036854775807)); // -(2^63) - 1 462NOT_LONG_TEST(ReturnNotLongINT64_MAX, 463 INT64_C(9223372036854775807), 464 INT64_C(-9223372036854775807)-1); // -(2^63) 465 466#undef NOT_LONG_TEST 467 468TEST(CodegenTest, IntToLongOfLongToInt) { 469 const int64_t input = INT64_C(4294967296); // 2^32 470 const uint16_t word0 = Low16Bits(Low32Bits(input)); // LSW. 471 const uint16_t word1 = High16Bits(Low32Bits(input)); 472 const uint16_t word2 = Low16Bits(High32Bits(input)); 473 const uint16_t word3 = High16Bits(High32Bits(input)); // MSW. 474 const uint16_t data[] = FIVE_REGISTERS_CODE_ITEM( 475 Instruction::CONST_WIDE | 0 << 8, word0, word1, word2, word3, 476 Instruction::CONST_WIDE | 2 << 8, 1, 0, 0, 0, 477 Instruction::ADD_LONG | 0, 0 << 8 | 2, // v0 <- 2^32 + 1 478 Instruction::LONG_TO_INT | 4 << 8 | 0 << 12, 479 Instruction::INT_TO_LONG | 2 << 8 | 4 << 12, 480 Instruction::RETURN_WIDE | 2 << 8); 481 482 TestCodeLong(data, true, 1); 483} 484 485TEST(CodegenTest, ReturnAdd1) { 486 const uint16_t data[] = TWO_REGISTERS_CODE_ITEM( 487 Instruction::CONST_4 | 3 << 12 | 0, 488 Instruction::CONST_4 | 4 << 12 | 1 << 8, 489 Instruction::ADD_INT, 1 << 8 | 0, 490 Instruction::RETURN); 491 492 TestCode(data, true, 7); 493} 494 495TEST(CodegenTest, ReturnAdd2) { 496 const uint16_t data[] = TWO_REGISTERS_CODE_ITEM( 497 Instruction::CONST_4 | 3 << 12 | 0, 498 Instruction::CONST_4 | 4 << 12 | 1 << 8, 499 Instruction::ADD_INT_2ADDR | 1 << 12, 500 Instruction::RETURN); 501 502 TestCode(data, true, 7); 503} 504 505TEST(CodegenTest, ReturnAdd3) { 506 const uint16_t data[] = ONE_REGISTER_CODE_ITEM( 507 Instruction::CONST_4 | 4 << 12 | 0 << 8, 508 Instruction::ADD_INT_LIT8, 3 << 8 | 0, 509 Instruction::RETURN); 510 511 TestCode(data, true, 7); 512} 513 514TEST(CodegenTest, ReturnAdd4) { 515 const uint16_t data[] = ONE_REGISTER_CODE_ITEM( 516 Instruction::CONST_4 | 4 << 12 | 0 << 8, 517 Instruction::ADD_INT_LIT16, 3, 518 Instruction::RETURN); 519 520 TestCode(data, true, 7); 521} 522 523TEST(CodegenTest, NonMaterializedCondition) { 524 ArenaPool pool; 525 ArenaAllocator allocator(&pool); 526 527 HGraph* graph = CreateGraph(&allocator); 528 HBasicBlock* entry = new (&allocator) HBasicBlock(graph); 529 graph->AddBlock(entry); 530 graph->SetEntryBlock(entry); 531 entry->AddInstruction(new (&allocator) HGoto()); 532 533 HBasicBlock* first_block = new (&allocator) HBasicBlock(graph); 534 graph->AddBlock(first_block); 535 entry->AddSuccessor(first_block); 536 HIntConstant* constant0 = graph->GetIntConstant(0); 537 HIntConstant* constant1 = graph->GetIntConstant(1); 538 HEqual* equal = new (&allocator) HEqual(constant0, constant0); 539 first_block->AddInstruction(equal); 540 first_block->AddInstruction(new (&allocator) HIf(equal)); 541 542 HBasicBlock* then = new (&allocator) HBasicBlock(graph); 543 HBasicBlock* else_ = new (&allocator) HBasicBlock(graph); 544 HBasicBlock* exit = new (&allocator) HBasicBlock(graph); 545 546 graph->AddBlock(then); 547 graph->AddBlock(else_); 548 graph->AddBlock(exit); 549 first_block->AddSuccessor(then); 550 first_block->AddSuccessor(else_); 551 then->AddSuccessor(exit); 552 else_->AddSuccessor(exit); 553 554 exit->AddInstruction(new (&allocator) HExit()); 555 then->AddInstruction(new (&allocator) HReturn(constant0)); 556 else_->AddInstruction(new (&allocator) HReturn(constant1)); 557 558 ASSERT_TRUE(equal->NeedsMaterialization()); 559 graph->BuildDominatorTree(); 560 PrepareForRegisterAllocation(graph).Run(); 561 ASSERT_FALSE(equal->NeedsMaterialization()); 562 563 auto hook_before_codegen = [](HGraph* graph_in) { 564 HBasicBlock* block = graph_in->GetEntryBlock()->GetSuccessors().Get(0); 565 HParallelMove* move = new (graph_in->GetArena()) HParallelMove(graph_in->GetArena()); 566 block->InsertInstructionBefore(move, block->GetLastInstruction()); 567 }; 568 569 RunCodeOptimized(graph, hook_before_codegen, true, 0); 570} 571 572TEST(CodegenTest, ReturnMulInt) { 573 const uint16_t data[] = TWO_REGISTERS_CODE_ITEM( 574 Instruction::CONST_4 | 3 << 12 | 0, 575 Instruction::CONST_4 | 4 << 12 | 1 << 8, 576 Instruction::MUL_INT, 1 << 8 | 0, 577 Instruction::RETURN); 578 579 TestCode(data, true, 12); 580} 581 582TEST(CodegenTest, ReturnMulInt2addr) { 583 const uint16_t data[] = TWO_REGISTERS_CODE_ITEM( 584 Instruction::CONST_4 | 3 << 12 | 0, 585 Instruction::CONST_4 | 4 << 12 | 1 << 8, 586 Instruction::MUL_INT_2ADDR | 1 << 12, 587 Instruction::RETURN); 588 589 TestCode(data, true, 12); 590} 591 592TEST(CodegenTest, ReturnMulLong) { 593 const uint16_t data[] = FOUR_REGISTERS_CODE_ITEM( 594 Instruction::CONST_4 | 3 << 12 | 0, 595 Instruction::CONST_4 | 0 << 12 | 1 << 8, 596 Instruction::CONST_4 | 4 << 12 | 2 << 8, 597 Instruction::CONST_4 | 0 << 12 | 3 << 8, 598 Instruction::MUL_LONG, 2 << 8 | 0, 599 Instruction::RETURN_WIDE); 600 601 TestCodeLong(data, true, 12); 602} 603 604TEST(CodegenTest, ReturnMulLong2addr) { 605 const uint16_t data[] = FOUR_REGISTERS_CODE_ITEM( 606 Instruction::CONST_4 | 3 << 12 | 0 << 8, 607 Instruction::CONST_4 | 0 << 12 | 1 << 8, 608 Instruction::CONST_4 | 4 << 12 | 2 << 8, 609 Instruction::CONST_4 | 0 << 12 | 3 << 8, 610 Instruction::MUL_LONG_2ADDR | 2 << 12, 611 Instruction::RETURN_WIDE); 612 613 TestCodeLong(data, true, 12); 614} 615 616TEST(CodegenTest, ReturnMulIntLit8) { 617 const uint16_t data[] = ONE_REGISTER_CODE_ITEM( 618 Instruction::CONST_4 | 4 << 12 | 0 << 8, 619 Instruction::MUL_INT_LIT8, 3 << 8 | 0, 620 Instruction::RETURN); 621 622 TestCode(data, true, 12); 623} 624 625TEST(CodegenTest, ReturnMulIntLit16) { 626 const uint16_t data[] = ONE_REGISTER_CODE_ITEM( 627 Instruction::CONST_4 | 4 << 12 | 0 << 8, 628 Instruction::MUL_INT_LIT16, 3, 629 Instruction::RETURN); 630 631 TestCode(data, true, 12); 632} 633 634TEST(CodegenTest, MaterializedCondition1) { 635 // Check that condition are materialized correctly. A materialized condition 636 // should yield `1` if it evaluated to true, and `0` otherwise. 637 // We force the materialization of comparisons for different combinations of 638 // inputs and check the results. 639 640 int lhs[] = {1, 2, -1, 2, 0xabc}; 641 int rhs[] = {2, 1, 2, -1, 0xabc}; 642 643 for (size_t i = 0; i < arraysize(lhs); i++) { 644 ArenaPool pool; 645 ArenaAllocator allocator(&pool); 646 HGraph* graph = CreateGraph(&allocator); 647 648 HBasicBlock* entry_block = new (&allocator) HBasicBlock(graph); 649 graph->AddBlock(entry_block); 650 graph->SetEntryBlock(entry_block); 651 entry_block->AddInstruction(new (&allocator) HGoto()); 652 HBasicBlock* code_block = new (&allocator) HBasicBlock(graph); 653 graph->AddBlock(code_block); 654 HBasicBlock* exit_block = new (&allocator) HBasicBlock(graph); 655 graph->AddBlock(exit_block); 656 exit_block->AddInstruction(new (&allocator) HExit()); 657 658 entry_block->AddSuccessor(code_block); 659 code_block->AddSuccessor(exit_block); 660 graph->SetExitBlock(exit_block); 661 662 HIntConstant* cst_lhs = graph->GetIntConstant(lhs[i]); 663 HIntConstant* cst_rhs = graph->GetIntConstant(rhs[i]); 664 HLessThan cmp_lt(cst_lhs, cst_rhs); 665 code_block->AddInstruction(&cmp_lt); 666 HReturn ret(&cmp_lt); 667 code_block->AddInstruction(&ret); 668 669 auto hook_before_codegen = [](HGraph* graph_in) { 670 HBasicBlock* block = graph_in->GetEntryBlock()->GetSuccessors().Get(0); 671 HParallelMove* move = new (graph_in->GetArena()) HParallelMove(graph_in->GetArena()); 672 block->InsertInstructionBefore(move, block->GetLastInstruction()); 673 }; 674 675 RunCodeOptimized(graph, hook_before_codegen, true, lhs[i] < rhs[i]); 676 } 677} 678 679TEST(CodegenTest, MaterializedCondition2) { 680 // Check that HIf correctly interprets a materialized condition. 681 // We force the materialization of comparisons for different combinations of 682 // inputs. An HIf takes the materialized combination as input and returns a 683 // value that we verify. 684 685 int lhs[] = {1, 2, -1, 2, 0xabc}; 686 int rhs[] = {2, 1, 2, -1, 0xabc}; 687 688 689 for (size_t i = 0; i < arraysize(lhs); i++) { 690 ArenaPool pool; 691 ArenaAllocator allocator(&pool); 692 HGraph* graph = CreateGraph(&allocator); 693 694 HBasicBlock* entry_block = new (&allocator) HBasicBlock(graph); 695 graph->AddBlock(entry_block); 696 graph->SetEntryBlock(entry_block); 697 entry_block->AddInstruction(new (&allocator) HGoto()); 698 699 HBasicBlock* if_block = new (&allocator) HBasicBlock(graph); 700 graph->AddBlock(if_block); 701 HBasicBlock* if_true_block = new (&allocator) HBasicBlock(graph); 702 graph->AddBlock(if_true_block); 703 HBasicBlock* if_false_block = new (&allocator) HBasicBlock(graph); 704 graph->AddBlock(if_false_block); 705 HBasicBlock* exit_block = new (&allocator) HBasicBlock(graph); 706 graph->AddBlock(exit_block); 707 exit_block->AddInstruction(new (&allocator) HExit()); 708 709 graph->SetEntryBlock(entry_block); 710 entry_block->AddSuccessor(if_block); 711 if_block->AddSuccessor(if_true_block); 712 if_block->AddSuccessor(if_false_block); 713 if_true_block->AddSuccessor(exit_block); 714 if_false_block->AddSuccessor(exit_block); 715 graph->SetExitBlock(exit_block); 716 717 HIntConstant* cst_lhs = graph->GetIntConstant(lhs[i]); 718 HIntConstant* cst_rhs = graph->GetIntConstant(rhs[i]); 719 HLessThan cmp_lt(cst_lhs, cst_rhs); 720 if_block->AddInstruction(&cmp_lt); 721 // We insert a temporary to separate the HIf from the HLessThan and force 722 // the materialization of the condition. 723 HTemporary force_materialization(0); 724 if_block->AddInstruction(&force_materialization); 725 HIf if_lt(&cmp_lt); 726 if_block->AddInstruction(&if_lt); 727 728 HIntConstant* cst_lt = graph->GetIntConstant(1); 729 HReturn ret_lt(cst_lt); 730 if_true_block->AddInstruction(&ret_lt); 731 HIntConstant* cst_ge = graph->GetIntConstant(0); 732 HReturn ret_ge(cst_ge); 733 if_false_block->AddInstruction(&ret_ge); 734 735 auto hook_before_codegen = [](HGraph* graph_in) { 736 HBasicBlock* block = graph_in->GetEntryBlock()->GetSuccessors().Get(0); 737 HParallelMove* move = new (graph_in->GetArena()) HParallelMove(graph_in->GetArena()); 738 block->InsertInstructionBefore(move, block->GetLastInstruction()); 739 }; 740 741 RunCodeOptimized(graph, hook_before_codegen, true, lhs[i] < rhs[i]); 742 } 743} 744 745TEST(CodegenTest, ReturnDivIntLit8) { 746 const uint16_t data[] = ONE_REGISTER_CODE_ITEM( 747 Instruction::CONST_4 | 4 << 12 | 0 << 8, 748 Instruction::DIV_INT_LIT8, 3 << 8 | 0, 749 Instruction::RETURN); 750 751 TestCode(data, true, 1); 752} 753 754TEST(CodegenTest, ReturnDivInt2Addr) { 755 const uint16_t data[] = TWO_REGISTERS_CODE_ITEM( 756 Instruction::CONST_4 | 4 << 12 | 0, 757 Instruction::CONST_4 | 2 << 12 | 1 << 8, 758 Instruction::DIV_INT_2ADDR | 1 << 12, 759 Instruction::RETURN); 760 761 TestCode(data, true, 2); 762} 763 764} // namespace art 765