optimizing_compiler.cc revision e2dc6fa7aa319ee892d6ed407c986c5a3ec3dcd7
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 "optimizing_compiler.h"
18
19#include <fstream>
20#include <stdint.h>
21
22#include "builder.h"
23#include "code_generator.h"
24#include "compiler.h"
25#include "constant_folding.h"
26#include "dead_code_elimination.h"
27#include "driver/compiler_driver.h"
28#include "driver/dex_compilation_unit.h"
29#include "elf_writer_quick.h"
30#include "graph_visualizer.h"
31#include "gvn.h"
32#include "instruction_simplifier.h"
33#include "jni/quick/jni_compiler.h"
34#include "mirror/art_method-inl.h"
35#include "nodes.h"
36#include "prepare_for_register_allocation.h"
37#include "register_allocator.h"
38#include "ssa_phi_elimination.h"
39#include "ssa_liveness_analysis.h"
40#include "utils/arena_allocator.h"
41
42namespace art {
43
44/**
45 * Used by the code generator, to allocate the code in a vector.
46 */
47class CodeVectorAllocator FINAL : public CodeAllocator {
48 public:
49  CodeVectorAllocator() {}
50
51  virtual uint8_t* Allocate(size_t size) {
52    size_ = size;
53    memory_.resize(size);
54    return &memory_[0];
55  }
56
57  size_t GetSize() const { return size_; }
58  const std::vector<uint8_t>& GetMemory() const { return memory_; }
59
60 private:
61  std::vector<uint8_t> memory_;
62  size_t size_;
63
64  DISALLOW_COPY_AND_ASSIGN(CodeVectorAllocator);
65};
66
67/**
68 * If set to true, generates a file suitable for the c1visualizer tool and IRHydra.
69 */
70static bool kIsVisualizerEnabled = false;
71
72/**
73 * Filter to apply to the visualizer. Methods whose name contain that filter will
74 * be in the file.
75 */
76static const char* kStringFilter = "";
77
78class OptimizingCompiler FINAL : public Compiler {
79 public:
80  explicit OptimizingCompiler(CompilerDriver* driver);
81  ~OptimizingCompiler();
82
83  bool CanCompileMethod(uint32_t method_idx, const DexFile& dex_file, CompilationUnit* cu) const
84      OVERRIDE;
85
86  CompiledMethod* Compile(const DexFile::CodeItem* code_item,
87                          uint32_t access_flags,
88                          InvokeType invoke_type,
89                          uint16_t class_def_idx,
90                          uint32_t method_idx,
91                          jobject class_loader,
92                          const DexFile& dex_file) const OVERRIDE;
93
94  CompiledMethod* JniCompile(uint32_t access_flags,
95                             uint32_t method_idx,
96                             const DexFile& dex_file) const OVERRIDE;
97
98  uintptr_t GetEntryPointOf(mirror::ArtMethod* method) const OVERRIDE
99      SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
100
101  bool WriteElf(art::File* file,
102                OatWriter* oat_writer,
103                const std::vector<const art::DexFile*>& dex_files,
104                const std::string& android_root,
105                bool is_host) const OVERRIDE SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
106
107  Backend* GetCodeGenerator(CompilationUnit* cu ATTRIBUTE_UNUSED,
108                            void* compilation_unit ATTRIBUTE_UNUSED) const OVERRIDE {
109    return nullptr;
110  }
111
112  void InitCompilationUnit(CompilationUnit& cu ATTRIBUTE_UNUSED) const OVERRIDE {}
113
114  void Init() const OVERRIDE {}
115
116  void UnInit() const OVERRIDE {}
117
118 private:
119  // Whether we should run any optimization or register allocation. If false, will
120  // just run the code generation after the graph was built.
121  const bool run_optimizations_;
122  mutable AtomicInteger total_compiled_methods_;
123  mutable AtomicInteger unoptimized_compiled_methods_;
124  mutable AtomicInteger optimized_compiled_methods_;
125
126  std::unique_ptr<std::ostream> visualizer_output_;
127
128  DISALLOW_COPY_AND_ASSIGN(OptimizingCompiler);
129};
130
131static const int kMaximumCompilationTimeBeforeWarning = 100; /* ms */
132
133OptimizingCompiler::OptimizingCompiler(CompilerDriver* driver)
134    : Compiler(driver, kMaximumCompilationTimeBeforeWarning),
135      run_optimizations_(
136          driver->GetCompilerOptions().GetCompilerFilter() != CompilerOptions::kTime),
137      total_compiled_methods_(0),
138      unoptimized_compiled_methods_(0),
139      optimized_compiled_methods_(0) {
140  if (kIsVisualizerEnabled) {
141    visualizer_output_.reset(new std::ofstream("art.cfg"));
142  }
143}
144
145OptimizingCompiler::~OptimizingCompiler() {
146  if (total_compiled_methods_ == 0) {
147    LOG(INFO) << "Did not compile any method.";
148  } else {
149    size_t unoptimized_percent = (unoptimized_compiled_methods_ * 100 / total_compiled_methods_);
150    size_t optimized_percent = (optimized_compiled_methods_ * 100 / total_compiled_methods_);
151    LOG(INFO) << "Compiled " << total_compiled_methods_ << " methods: "
152              << unoptimized_percent << "% (" << unoptimized_compiled_methods_ << ") unoptimized, "
153              << optimized_percent << "% (" << optimized_compiled_methods_ << ") optimized.";
154  }
155}
156
157bool OptimizingCompiler::CanCompileMethod(uint32_t method_idx ATTRIBUTE_UNUSED,
158                                          const DexFile& dex_file ATTRIBUTE_UNUSED,
159                                          CompilationUnit* cu ATTRIBUTE_UNUSED) const {
160  return true;
161}
162
163CompiledMethod* OptimizingCompiler::JniCompile(uint32_t access_flags,
164                                               uint32_t method_idx,
165                                               const DexFile& dex_file) const {
166  return ArtQuickJniCompileMethod(GetCompilerDriver(), access_flags, method_idx, dex_file);
167}
168
169uintptr_t OptimizingCompiler::GetEntryPointOf(mirror::ArtMethod* method) const {
170  return reinterpret_cast<uintptr_t>(method->GetEntryPointFromQuickCompiledCode());
171}
172
173bool OptimizingCompiler::WriteElf(art::File* file, OatWriter* oat_writer,
174                                  const std::vector<const art::DexFile*>& dex_files,
175                                  const std::string& android_root, bool is_host) const {
176  return art::ElfWriterQuick32::Create(file, oat_writer, dex_files, android_root, is_host,
177                                       *GetCompilerDriver());
178}
179
180static bool IsInstructionSetSupported(InstructionSet instruction_set) {
181  return instruction_set == kArm64
182      || (instruction_set == kThumb2 && !kArm32QuickCodeUseSoftFloat)
183      || instruction_set == kX86
184      || instruction_set == kX86_64;
185}
186
187static bool CanOptimize(const DexFile::CodeItem& code_item) {
188  // TODO: We currently cannot optimize methods with try/catch.
189  return code_item.tries_size_ == 0;
190}
191
192CompiledMethod* OptimizingCompiler::Compile(const DexFile::CodeItem* code_item,
193                                            uint32_t access_flags,
194                                            InvokeType invoke_type,
195                                            uint16_t class_def_idx,
196                                            uint32_t method_idx,
197                                            jobject class_loader,
198                                            const DexFile& dex_file) const {
199  UNUSED(invoke_type);
200  total_compiled_methods_++;
201  InstructionSet instruction_set = GetCompilerDriver()->GetInstructionSet();
202  // Always use the thumb2 assembler: some runtime functionality (like implicit stack
203  // overflow checks) assume thumb2.
204  if (instruction_set == kArm) {
205    instruction_set = kThumb2;
206  }
207
208  // Do not attempt to compile on architectures we do not support.
209  if (!IsInstructionSetSupported(instruction_set)) {
210    return nullptr;
211  }
212
213  if (Compiler::IsPathologicalCase(*code_item, method_idx, dex_file)) {
214    return nullptr;
215  }
216
217  DexCompilationUnit dex_compilation_unit(
218    nullptr, class_loader, art::Runtime::Current()->GetClassLinker(), dex_file, code_item,
219    class_def_idx, method_idx, access_flags,
220    GetCompilerDriver()->GetVerifiedMethod(&dex_file, method_idx));
221
222  // For testing purposes, we put a special marker on method names that should be compiled
223  // with this compiler. This makes sure we're not regressing.
224  bool shouldCompile = dex_compilation_unit.GetSymbol().find("00024opt_00024") != std::string::npos;
225  bool shouldOptimize =
226      dex_compilation_unit.GetSymbol().find("00024reg_00024") != std::string::npos;
227
228  ArenaPool pool;
229  ArenaAllocator arena(&pool);
230  HGraphBuilder builder(&arena, &dex_compilation_unit, &dex_file, GetCompilerDriver());
231
232  HGraph* graph = builder.BuildGraph(*code_item);
233  if (graph == nullptr) {
234    CHECK(!shouldCompile) << "Could not build graph in optimizing compiler";
235    return nullptr;
236  }
237
238  CodeGenerator* codegen = CodeGenerator::Create(&arena, graph, instruction_set);
239  if (codegen == nullptr) {
240    CHECK(!shouldCompile) << "Could not find code generator for optimizing compiler";
241    return nullptr;
242  }
243
244  HGraphVisualizer visualizer(
245      visualizer_output_.get(), graph, kStringFilter, *codegen, dex_compilation_unit);
246  visualizer.DumpGraph("builder");
247
248  CodeVectorAllocator allocator;
249
250  if (run_optimizations_
251      && CanOptimize(*code_item)
252      && RegisterAllocator::CanAllocateRegistersFor(*graph, instruction_set)) {
253    optimized_compiled_methods_++;
254    graph->BuildDominatorTree();
255    graph->TransformToSSA();
256    visualizer.DumpGraph("ssa");
257    graph->FindNaturalLoops();
258
259    HDeadCodeElimination(graph, visualizer).Execute();
260    HConstantFolding(graph, visualizer).Execute();
261
262    SsaRedundantPhiElimination(graph).Run();
263    SsaDeadPhiElimination(graph).Run();
264    InstructionSimplifier(graph).Run();
265    GlobalValueNumberer(graph->GetArena(), graph).Run();
266    visualizer.DumpGraph(kGVNPassName);
267    InstructionSimplifier(graph).Run();
268    PrepareForRegisterAllocation(graph).Run();
269
270    SsaLivenessAnalysis liveness(*graph, codegen);
271    liveness.Analyze();
272    visualizer.DumpGraph(kLivenessPassName);
273
274    RegisterAllocator register_allocator(graph->GetArena(), codegen, liveness);
275    register_allocator.AllocateRegisters();
276
277    visualizer.DumpGraph(kRegisterAllocatorPassName);
278    codegen->CompileOptimized(&allocator);
279
280    std::vector<uint8_t> mapping_table;
281    SrcMap src_mapping_table;
282    codegen->BuildMappingTable(&mapping_table,
283            GetCompilerDriver()->GetCompilerOptions().GetIncludeDebugSymbols() ?
284                 &src_mapping_table : nullptr);
285
286    std::vector<uint8_t> stack_map;
287    codegen->BuildStackMaps(&stack_map);
288
289    return new CompiledMethod(GetCompilerDriver(),
290                              instruction_set,
291                              allocator.GetMemory(),
292                              codegen->GetFrameSize(),
293                              codegen->GetCoreSpillMask(),
294                              0, /* FPR spill mask, unused */
295                              mapping_table,
296                              stack_map);
297  } else if (shouldOptimize && RegisterAllocator::Supports(instruction_set)) {
298    LOG(FATAL) << "Could not allocate registers in optimizing compiler";
299    UNREACHABLE();
300  } else {
301    unoptimized_compiled_methods_++;
302    codegen->CompileBaseline(&allocator);
303
304    if (CanOptimize(*code_item)) {
305      // Run these phases to get some test coverage.
306      graph->BuildDominatorTree();
307      graph->TransformToSSA();
308      visualizer.DumpGraph("ssa");
309      graph->FindNaturalLoops();
310      SsaRedundantPhiElimination(graph).Run();
311      SsaDeadPhiElimination(graph).Run();
312      GlobalValueNumberer(graph->GetArena(), graph).Run();
313      SsaLivenessAnalysis liveness(*graph, codegen);
314      liveness.Analyze();
315      visualizer.DumpGraph(kLivenessPassName);
316    }
317
318    std::vector<uint8_t> mapping_table;
319    SrcMap src_mapping_table;
320    codegen->BuildMappingTable(&mapping_table,
321            GetCompilerDriver()->GetCompilerOptions().GetIncludeDebugSymbols() ?
322                 &src_mapping_table : nullptr);
323    std::vector<uint8_t> vmap_table;
324    codegen->BuildVMapTable(&vmap_table);
325    std::vector<uint8_t> gc_map;
326    codegen->BuildNativeGCMap(&gc_map, dex_compilation_unit);
327
328    return new CompiledMethod(GetCompilerDriver(),
329                              instruction_set,
330                              allocator.GetMemory(),
331                              codegen->GetFrameSize(),
332                              codegen->GetCoreSpillMask(),
333                              0, /* FPR spill mask, unused */
334                              &src_mapping_table,
335                              mapping_table,
336                              vmap_table,
337                              gc_map,
338                              nullptr);
339  }
340}
341
342Compiler* CreateOptimizingCompiler(CompilerDriver* driver) {
343  return new OptimizingCompiler(driver);
344}
345
346}  // namespace art
347