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