codegen_test.cc revision 360231a056e796c36ffe62348507e904dc9efb9b
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  RemoveSuspendChecks(graph);
139  ASSERT_NE(graph, nullptr);
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}  // namespace art
353