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