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