codegen_test.cc revision 48dee04f4e4214b0fdd8acd0587ef6b08d3d2456
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 "builder.h" 20#include "code_generator_arm.h" 21#include "code_generator_x86.h" 22#include "code_generator_x86_64.h" 23#include "common_compiler_test.h" 24#include "dex_file.h" 25#include "dex_instruction.h" 26#include "instruction_set.h" 27#include "nodes.h" 28#include "optimizing_unit_test.h" 29#include "prepare_for_register_allocation.h" 30#include "register_allocator.h" 31#include "ssa_liveness_analysis.h" 32 33#include "gtest/gtest.h" 34 35namespace art { 36 37class InternalCodeAllocator : public CodeAllocator { 38 public: 39 InternalCodeAllocator() { } 40 41 virtual uint8_t* Allocate(size_t size) { 42 size_ = size; 43 memory_.reset(new uint8_t[size]); 44 return memory_.get(); 45 } 46 47 size_t GetSize() const { return size_; } 48 uint8_t* GetMemory() const { return memory_.get(); } 49 50 private: 51 size_t size_; 52 std::unique_ptr<uint8_t[]> memory_; 53 54 DISALLOW_COPY_AND_ASSIGN(InternalCodeAllocator); 55}; 56 57static void Run(const InternalCodeAllocator& allocator, 58 const CodeGenerator& codegen, 59 bool has_result, 60 int32_t expected) { 61 typedef int32_t (*fptr)(); 62 CommonCompilerTest::MakeExecutable(allocator.GetMemory(), allocator.GetSize()); 63 fptr f = reinterpret_cast<fptr>(allocator.GetMemory()); 64 if (codegen.GetInstructionSet() == kThumb2) { 65 // For thumb we need the bottom bit set. 66 f = reinterpret_cast<fptr>(reinterpret_cast<uintptr_t>(f) + 1); 67 } 68 int32_t result = f(); 69 if (has_result) { 70 ASSERT_EQ(result, expected); 71 } 72} 73 74static void RunCodeBaseline(HGraph* graph, bool has_result, int32_t expected) { 75 InternalCodeAllocator allocator; 76 77 x86::CodeGeneratorX86 codegenX86(graph); 78 // We avoid doing a stack overflow check that requires the runtime being setup, 79 // by making sure the compiler knows the methods we are running are leaf methods. 80 codegenX86.CompileBaseline(&allocator, true); 81 if (kRuntimeISA == kX86) { 82 Run(allocator, codegenX86, has_result, expected); 83 } 84 85 arm::CodeGeneratorARM codegenARM(graph); 86 codegenARM.CompileBaseline(&allocator, true); 87 if (kRuntimeISA == kArm || kRuntimeISA == kThumb2) { 88 Run(allocator, codegenARM, has_result, expected); 89 } 90 91 x86_64::CodeGeneratorX86_64 codegenX86_64(graph); 92 codegenX86_64.CompileBaseline(&allocator, true); 93 if (kRuntimeISA == kX86_64) { 94 Run(allocator, codegenX86_64, has_result, expected); 95 } 96} 97 98static void RunCodeOptimized(CodeGenerator* codegen, 99 HGraph* graph, 100 std::function<void(HGraph*)> hook_before_codegen, 101 bool has_result, 102 int32_t expected) { 103 SsaLivenessAnalysis liveness(*graph, codegen); 104 liveness.Analyze(); 105 106 RegisterAllocator register_allocator(graph->GetArena(), codegen, liveness); 107 register_allocator.AllocateRegisters(); 108 hook_before_codegen(graph); 109 110 InternalCodeAllocator allocator; 111 codegen->CompileOptimized(&allocator); 112 Run(allocator, *codegen, has_result, expected); 113} 114 115static void RunCodeOptimized(HGraph* graph, 116 std::function<void(HGraph*)> hook_before_codegen, 117 bool has_result, 118 int32_t expected) { 119 if (kRuntimeISA == kX86) { 120 x86::CodeGeneratorX86 codegenX86(graph); 121 RunCodeOptimized(&codegenX86, graph, hook_before_codegen, has_result, expected); 122 } else if (kRuntimeISA == kArm || kRuntimeISA == kThumb2) { 123 arm::CodeGeneratorARM codegenARM(graph); 124 RunCodeOptimized(&codegenARM, graph, hook_before_codegen, has_result, expected); 125 } else if (kRuntimeISA == kX86_64) { 126 x86_64::CodeGeneratorX86_64 codegenX86_64(graph); 127 RunCodeOptimized(&codegenX86_64, graph, hook_before_codegen, has_result, expected); 128 } 129} 130 131static void TestCode(const uint16_t* data, bool has_result = false, int32_t expected = 0) { 132 ArenaPool pool; 133 ArenaAllocator arena(&pool); 134 HGraphBuilder builder(&arena); 135 const DexFile::CodeItem* item = reinterpret_cast<const DexFile::CodeItem*>(data); 136 HGraph* graph = builder.BuildGraph(*item); 137 // Remove suspend checks, they cannot be executed in this context. 138 ASSERT_NE(graph, nullptr); 139 RemoveSuspendChecks(graph); 140 RunCodeBaseline(graph, has_result, expected); 141} 142 143TEST(CodegenTest, ReturnVoid) { 144 const uint16_t data[] = ZERO_REGISTER_CODE_ITEM(Instruction::RETURN_VOID); 145 TestCode(data); 146} 147 148TEST(CodegenTest, CFG1) { 149 const uint16_t data[] = ZERO_REGISTER_CODE_ITEM( 150 Instruction::GOTO | 0x100, 151 Instruction::RETURN_VOID); 152 153 TestCode(data); 154} 155 156TEST(CodegenTest, CFG2) { 157 const uint16_t data[] = ZERO_REGISTER_CODE_ITEM( 158 Instruction::GOTO | 0x100, 159 Instruction::GOTO | 0x100, 160 Instruction::RETURN_VOID); 161 162 TestCode(data); 163} 164 165TEST(CodegenTest, CFG3) { 166 const uint16_t data1[] = ZERO_REGISTER_CODE_ITEM( 167 Instruction::GOTO | 0x200, 168 Instruction::RETURN_VOID, 169 Instruction::GOTO | 0xFF00); 170 171 TestCode(data1); 172 173 const uint16_t data2[] = ZERO_REGISTER_CODE_ITEM( 174 Instruction::GOTO_16, 3, 175 Instruction::RETURN_VOID, 176 Instruction::GOTO_16, 0xFFFF); 177 178 TestCode(data2); 179 180 const uint16_t data3[] = ZERO_REGISTER_CODE_ITEM( 181 Instruction::GOTO_32, 4, 0, 182 Instruction::RETURN_VOID, 183 Instruction::GOTO_32, 0xFFFF, 0xFFFF); 184 185 TestCode(data3); 186} 187 188TEST(CodegenTest, CFG4) { 189 const uint16_t data[] = ZERO_REGISTER_CODE_ITEM( 190 Instruction::RETURN_VOID, 191 Instruction::GOTO | 0x100, 192 Instruction::GOTO | 0xFE00); 193 194 TestCode(data); 195} 196 197TEST(CodegenTest, CFG5) { 198 const uint16_t data[] = ONE_REGISTER_CODE_ITEM( 199 Instruction::CONST_4 | 0 | 0, 200 Instruction::IF_EQ, 3, 201 Instruction::GOTO | 0x100, 202 Instruction::RETURN_VOID); 203 204 TestCode(data); 205} 206 207TEST(CodegenTest, IntConstant) { 208 const uint16_t data[] = ONE_REGISTER_CODE_ITEM( 209 Instruction::CONST_4 | 0 | 0, 210 Instruction::RETURN_VOID); 211 212 TestCode(data); 213} 214 215TEST(CodegenTest, Return1) { 216 const uint16_t data[] = ONE_REGISTER_CODE_ITEM( 217 Instruction::CONST_4 | 0 | 0, 218 Instruction::RETURN | 0); 219 220 TestCode(data, true, 0); 221} 222 223TEST(CodegenTest, Return2) { 224 const uint16_t data[] = TWO_REGISTERS_CODE_ITEM( 225 Instruction::CONST_4 | 0 | 0, 226 Instruction::CONST_4 | 0 | 1 << 8, 227 Instruction::RETURN | 1 << 8); 228 229 TestCode(data, true, 0); 230} 231 232TEST(CodegenTest, Return3) { 233 const uint16_t data[] = TWO_REGISTERS_CODE_ITEM( 234 Instruction::CONST_4 | 0 | 0, 235 Instruction::CONST_4 | 1 << 8 | 1 << 12, 236 Instruction::RETURN | 1 << 8); 237 238 TestCode(data, true, 1); 239} 240 241TEST(CodegenTest, ReturnIf1) { 242 const uint16_t data[] = TWO_REGISTERS_CODE_ITEM( 243 Instruction::CONST_4 | 0 | 0, 244 Instruction::CONST_4 | 1 << 8 | 1 << 12, 245 Instruction::IF_EQ, 3, 246 Instruction::RETURN | 0 << 8, 247 Instruction::RETURN | 1 << 8); 248 249 TestCode(data, true, 1); 250} 251 252TEST(CodegenTest, ReturnIf2) { 253 const uint16_t data[] = TWO_REGISTERS_CODE_ITEM( 254 Instruction::CONST_4 | 0 | 0, 255 Instruction::CONST_4 | 1 << 8 | 1 << 12, 256 Instruction::IF_EQ | 0 << 4 | 1 << 8, 3, 257 Instruction::RETURN | 0 << 8, 258 Instruction::RETURN | 1 << 8); 259 260 TestCode(data, true, 0); 261} 262 263TEST(CodegenTest, ReturnAdd1) { 264 const uint16_t data[] = TWO_REGISTERS_CODE_ITEM( 265 Instruction::CONST_4 | 3 << 12 | 0, 266 Instruction::CONST_4 | 4 << 12 | 1 << 8, 267 Instruction::ADD_INT, 1 << 8 | 0, 268 Instruction::RETURN); 269 270 TestCode(data, true, 7); 271} 272 273TEST(CodegenTest, ReturnAdd2) { 274 const uint16_t data[] = TWO_REGISTERS_CODE_ITEM( 275 Instruction::CONST_4 | 3 << 12 | 0, 276 Instruction::CONST_4 | 4 << 12 | 1 << 8, 277 Instruction::ADD_INT_2ADDR | 1 << 12, 278 Instruction::RETURN); 279 280 TestCode(data, true, 7); 281} 282 283TEST(CodegenTest, ReturnAdd3) { 284 const uint16_t data[] = ONE_REGISTER_CODE_ITEM( 285 Instruction::CONST_4 | 4 << 12 | 0 << 8, 286 Instruction::ADD_INT_LIT8, 3 << 8 | 0, 287 Instruction::RETURN); 288 289 TestCode(data, true, 7); 290} 291 292TEST(CodegenTest, ReturnAdd4) { 293 const uint16_t data[] = ONE_REGISTER_CODE_ITEM( 294 Instruction::CONST_4 | 4 << 12 | 0 << 8, 295 Instruction::ADD_INT_LIT16, 3, 296 Instruction::RETURN); 297 298 TestCode(data, true, 7); 299} 300 301TEST(CodegenTest, NonMaterializedCondition) { 302 ArenaPool pool; 303 ArenaAllocator allocator(&pool); 304 305 HGraph* graph = new (&allocator) HGraph(&allocator); 306 HBasicBlock* entry = new (&allocator) HBasicBlock(graph); 307 graph->AddBlock(entry); 308 graph->SetEntryBlock(entry); 309 entry->AddInstruction(new (&allocator) HGoto()); 310 311 HBasicBlock* first_block = new (&allocator) HBasicBlock(graph); 312 graph->AddBlock(first_block); 313 entry->AddSuccessor(first_block); 314 HIntConstant* constant0 = new (&allocator) HIntConstant(0); 315 entry->AddInstruction(constant0); 316 HIntConstant* constant1 = new (&allocator) HIntConstant(1); 317 entry->AddInstruction(constant1); 318 HEqual* equal = new (&allocator) HEqual(constant0, constant0); 319 first_block->AddInstruction(equal); 320 first_block->AddInstruction(new (&allocator) HIf(equal)); 321 322 HBasicBlock* then = new (&allocator) HBasicBlock(graph); 323 HBasicBlock* else_ = new (&allocator) HBasicBlock(graph); 324 HBasicBlock* exit = new (&allocator) HBasicBlock(graph); 325 326 graph->AddBlock(then); 327 graph->AddBlock(else_); 328 graph->AddBlock(exit); 329 first_block->AddSuccessor(then); 330 first_block->AddSuccessor(else_); 331 then->AddSuccessor(exit); 332 else_->AddSuccessor(exit); 333 334 exit->AddInstruction(new (&allocator) HExit()); 335 then->AddInstruction(new (&allocator) HReturn(constant0)); 336 else_->AddInstruction(new (&allocator) HReturn(constant1)); 337 338 ASSERT_TRUE(equal->NeedsMaterialization()); 339 graph->BuildDominatorTree(); 340 PrepareForRegisterAllocation(graph).Run(); 341 ASSERT_FALSE(equal->NeedsMaterialization()); 342 343 auto hook_before_codegen = [](HGraph* graph) { 344 HBasicBlock* block = graph->GetEntryBlock()->GetSuccessors().Get(0); 345 HParallelMove* move = new (graph->GetArena()) HParallelMove(graph->GetArena()); 346 block->InsertInstructionBefore(move, block->GetLastInstruction()); 347 }; 348 349 RunCodeOptimized(graph, hook_before_codegen, true, 0); 350} 351 352#define MUL_TEST(TYPE, TEST_NAME) \ 353 TEST(CodegenTest, Return ## TEST_NAME) { \ 354 const uint16_t data[] = TWO_REGISTERS_CODE_ITEM( \ 355 Instruction::CONST_4 | 3 << 12 | 0, \ 356 Instruction::CONST_4 | 4 << 12 | 1 << 8, \ 357 Instruction::MUL_ ## TYPE, 1 << 8 | 0, \ 358 Instruction::RETURN); \ 359 \ 360 TestCode(data, true, 12); \ 361 } \ 362 \ 363 TEST(CodegenTest, Return ## TEST_NAME ## 2addr) { \ 364 const uint16_t data[] = TWO_REGISTERS_CODE_ITEM( \ 365 Instruction::CONST_4 | 3 << 12 | 0, \ 366 Instruction::CONST_4 | 4 << 12 | 1 << 8, \ 367 Instruction::MUL_ ## TYPE ## _2ADDR | 1 << 12, \ 368 Instruction::RETURN); \ 369 \ 370 TestCode(data, true, 12); \ 371 } 372 373MUL_TEST(INT, MulInt); 374MUL_TEST(LONG, MulLong); 375// MUL_TEST(FLOAT, Float); 376// MUL_TEST(DOUBLE, Double); 377 378TEST(CodegenTest, ReturnMulIntLit8) { 379 const uint16_t data[] = ONE_REGISTER_CODE_ITEM( 380 Instruction::CONST_4 | 4 << 12 | 0 << 8, 381 Instruction::MUL_INT_LIT8, 3 << 8 | 0, 382 Instruction::RETURN); 383 384 TestCode(data, true, 12); 385} 386 387TEST(CodegenTest, ReturnMulIntLit16) { 388 const uint16_t data[] = ONE_REGISTER_CODE_ITEM( 389 Instruction::CONST_4 | 4 << 12 | 0 << 8, 390 Instruction::MUL_INT_LIT16, 3, 391 Instruction::RETURN); 392 393 TestCode(data, true, 12); 394} 395 396 397} // namespace art 398