diff options
Diffstat (limited to 'compiler/optimizing')
24 files changed, 555 insertions, 175 deletions
diff --git a/compiler/optimizing/builder.cc b/compiler/optimizing/builder.cc index d39f1c7d49..f9054e0133 100644 --- a/compiler/optimizing/builder.cc +++ b/compiler/optimizing/builder.cc @@ -259,13 +259,13 @@ bool HGraphBuilder::SkipCompilation(size_t number_of_dex_instructions, return false; } -HGraph* HGraphBuilder::BuildGraph(const DexFile::CodeItem& code_item) { +HGraph* HGraphBuilder::BuildGraph(const DexFile::CodeItem& code_item, int start_instruction_id) { const uint16_t* code_ptr = code_item.insns_; const uint16_t* code_end = code_item.insns_ + code_item.insns_size_in_code_units_; code_start_ = code_ptr; // Setup the graph with the entry block and exit block. - graph_ = new (arena_) HGraph(arena_); + graph_ = new (arena_) HGraph(arena_, start_instruction_id); entry_block_ = new (arena_) HBasicBlock(graph_, 0); graph_->AddBlock(entry_block_); exit_block_ = new (arena_) HBasicBlock(graph_, kNoDexPc); @@ -273,7 +273,7 @@ HGraph* HGraphBuilder::BuildGraph(const DexFile::CodeItem& code_item) { graph_->SetExitBlock(exit_block_); InitializeLocals(code_item.registers_size_); - graph_->UpdateMaximumNumberOfOutVRegs(code_item.outs_size_); + graph_->SetMaximumNumberOfOutVRegs(code_item.outs_size_); // Compute the number of dex instructions, blocks, and branches. We will // check these values against limits given to the compiler. @@ -613,9 +613,9 @@ bool HGraphBuilder::BuildInvoke(const Instruction& instruction, // Sharpening to kDirect only works if we compile PIC. DCHECK((optimized_invoke_type == invoke_type) || (optimized_invoke_type != kDirect) || compiler_driver_->GetCompilerOptions().GetCompilePic()); - // Treat invoke-direct like static calls for now. - invoke = new (arena_) HInvokeStatic( - arena_, number_of_arguments, return_type, dex_pc, target_method.dex_method_index); + invoke = new (arena_) HInvokeStaticOrDirect( + arena_, number_of_arguments, return_type, dex_pc, target_method.dex_method_index, + optimized_invoke_type); } size_t start_index = 0; @@ -709,42 +709,53 @@ bool HGraphBuilder::BuildStaticFieldAccess(const Instruction& instruction, uint32_t source_or_dest_reg = instruction.VRegA_21c(); uint16_t field_index = instruction.VRegB_21c(); - uint32_t storage_index; - bool is_referrers_class; - bool is_initialized; - bool is_volatile; - MemberOffset field_offset(0u); - Primitive::Type field_type; - - bool fast_path = compiler_driver_->ComputeStaticFieldInfo(field_index, - dex_compilation_unit_, - is_put, - &field_offset, - &storage_index, - &is_referrers_class, - &is_volatile, - &is_initialized, - &field_type); - if (!fast_path) { + ScopedObjectAccess soa(Thread::Current()); + StackHandleScope<4> hs(soa.Self()); + Handle<mirror::DexCache> dex_cache(hs.NewHandle( + dex_compilation_unit_->GetClassLinker()->FindDexCache(*dex_compilation_unit_->GetDexFile()))); + Handle<mirror::ClassLoader> class_loader(hs.NewHandle( + soa.Decode<mirror::ClassLoader*>(dex_compilation_unit_->GetClassLoader()))); + Handle<mirror::ArtField> resolved_field(hs.NewHandle(compiler_driver_->ResolveField( + soa, dex_cache, class_loader, dex_compilation_unit_, field_index, true))); + + if (resolved_field.Get() == nullptr) { MaybeRecordStat(MethodCompilationStat::kNotCompiledUnresolvedField); return false; } - if (is_volatile) { + if (resolved_field->IsVolatile()) { MaybeRecordStat(MethodCompilationStat::kNotCompiledVolatile); return false; } - HLoadClass* constant = new (arena_) HLoadClass( - storage_index, is_referrers_class, dex_pc); + Handle<mirror::Class> referrer_class(hs.NewHandle(compiler_driver_->ResolveCompilingMethodsClass( + soa, dex_cache, class_loader, outer_compilation_unit_))); + + // The index at which the field's class is stored in the DexCache's type array. + uint32_t storage_index; + std::pair<bool, bool> pair = compiler_driver_->IsFastStaticField( + dex_cache.Get(), referrer_class.Get(), resolved_field.Get(), field_index, &storage_index); + bool can_easily_access = is_put ? pair.second : pair.first; + if (!can_easily_access) { + return false; + } + + // TODO: find out why this check is needed. + bool is_in_dex_cache = compiler_driver_->CanAssumeTypeIsPresentInDexCache( + *outer_compilation_unit_->GetDexFile(), storage_index); + bool is_initialized = resolved_field->GetDeclaringClass()->IsInitialized() && is_in_dex_cache; + bool is_referrer_class = (referrer_class.Get() == resolved_field->GetDeclaringClass()); + + HLoadClass* constant = new (arena_) HLoadClass(storage_index, is_referrer_class, dex_pc); current_block_->AddInstruction(constant); HInstruction* cls = constant; - if (!is_initialized) { + if (!is_initialized && !is_referrer_class) { cls = new (arena_) HClinitCheck(constant, dex_pc); current_block_->AddInstruction(cls); } + Primitive::Type field_type = resolved_field->GetTypeAsPrimitiveType(); if (is_put) { // We need to keep the class alive before loading the value. Temporaries temps(graph_); @@ -752,9 +763,10 @@ bool HGraphBuilder::BuildStaticFieldAccess(const Instruction& instruction, HInstruction* value = LoadLocal(source_or_dest_reg, field_type); DCHECK_EQ(value->GetType(), field_type); current_block_->AddInstruction( - new (arena_) HStaticFieldSet(cls, value, field_type, field_offset)); + new (arena_) HStaticFieldSet(cls, value, field_type, resolved_field->GetOffset())); } else { - current_block_->AddInstruction(new (arena_) HStaticFieldGet(cls, field_type, field_offset)); + current_block_->AddInstruction( + new (arena_) HStaticFieldGet(cls, field_type, resolved_field->GetOffset())); UpdateLocal(source_or_dest_reg, current_block_->GetLastInstruction()); } return true; @@ -949,16 +961,20 @@ bool HGraphBuilder::BuildTypeCheck(const Instruction& instruction, uint32_t dex_pc) { bool type_known_final; bool type_known_abstract; - bool is_referrers_class; + // `CanAccessTypeWithoutChecks` will tell whether the method being + // built is trying to access its own class, so that the generated + // code can optimize for this case. However, the optimization does not + // work for inlining, so we use `IsCompilingClass` instead. + bool dont_use_is_referrers_class; bool can_access = compiler_driver_->CanAccessTypeWithoutChecks( dex_compilation_unit_->GetDexMethodIndex(), *dex_file_, type_index, - &type_known_final, &type_known_abstract, &is_referrers_class); + &type_known_final, &type_known_abstract, &dont_use_is_referrers_class); if (!can_access) { MaybeRecordStat(MethodCompilationStat::kNotCompiledCantAccesType); return false; } HInstruction* object = LoadLocal(reference, Primitive::kPrimNot); - HLoadClass* cls = new (arena_) HLoadClass(type_index, is_referrers_class, dex_pc); + HLoadClass* cls = new (arena_) HLoadClass(type_index, IsCompilingClass(type_index), dex_pc); current_block_->AddInstruction(cls); // The class needs a temporary before being used by the type check. Temporaries temps(graph_); @@ -1929,16 +1945,20 @@ bool HGraphBuilder::AnalyzeDexInstruction(const Instruction& instruction, uint32 uint16_t type_index = instruction.VRegB_21c(); bool type_known_final; bool type_known_abstract; - bool is_referrers_class; + bool dont_use_is_referrers_class; + // `CanAccessTypeWithoutChecks` will tell whether the method being + // built is trying to access its own class, so that the generated + // code can optimize for this case. However, the optimization does not + // work for inlining, so we use `IsCompilingClass` instead. bool can_access = compiler_driver_->CanAccessTypeWithoutChecks( dex_compilation_unit_->GetDexMethodIndex(), *dex_file_, type_index, - &type_known_final, &type_known_abstract, &is_referrers_class); + &type_known_final, &type_known_abstract, &dont_use_is_referrers_class); if (!can_access) { MaybeRecordStat(MethodCompilationStat::kNotCompiledCantAccesType); return false; } current_block_->AddInstruction( - new (arena_) HLoadClass(type_index, is_referrers_class, dex_pc)); + new (arena_) HLoadClass(type_index, IsCompilingClass(type_index), dex_pc)); UpdateLocal(instruction.VRegA_21c(), current_block_->GetLastInstruction()); break; } diff --git a/compiler/optimizing/builder.h b/compiler/optimizing/builder.h index 75c8634ea3..cc5f6a04dc 100644 --- a/compiler/optimizing/builder.h +++ b/compiler/optimizing/builder.h @@ -36,6 +36,7 @@ class HGraphBuilder : public ValueObject { public: HGraphBuilder(ArenaAllocator* arena, DexCompilationUnit* dex_compilation_unit, + const DexCompilationUnit* const outer_compilation_unit, const DexFile* dex_file, CompilerDriver* driver, OptimizingCompilerStats* compiler_stats) @@ -51,6 +52,7 @@ class HGraphBuilder : public ValueObject { dex_file_(dex_file), dex_compilation_unit_(dex_compilation_unit), compiler_driver_(driver), + outer_compilation_unit_(outer_compilation_unit), return_type_(Primitive::GetType(dex_compilation_unit_->GetShorty()[0])), code_start_(nullptr), latest_result_(nullptr), @@ -70,12 +72,13 @@ class HGraphBuilder : public ValueObject { dex_file_(nullptr), dex_compilation_unit_(nullptr), compiler_driver_(nullptr), + outer_compilation_unit_(nullptr), return_type_(return_type), code_start_(nullptr), latest_result_(nullptr), compilation_stats_(nullptr) {} - HGraph* BuildGraph(const DexFile::CodeItem& code); + HGraph* BuildGraph(const DexFile::CodeItem& code, int start_instruction_id = 0); private: // Analyzes the dex instruction and adds HInstruction to the graph @@ -225,6 +228,14 @@ class HGraphBuilder : public ValueObject { void MaybeRecordStat(MethodCompilationStat compilation_stat); + // Returns whether `type_index` points to the outer-most compiling method's class. + bool IsCompilingClass(uint16_t type_index) const { + uint32_t referrer_index = outer_compilation_unit_->GetDexMethodIndex(); + const DexFile::MethodId& method_id = + outer_compilation_unit_->GetDexFile()->GetMethodId(referrer_index); + return method_id.class_idx_ == type_index; + } + ArenaAllocator* const arena_; // A list of the size of the dex code holding block information for @@ -242,9 +253,21 @@ class HGraphBuilder : public ValueObject { HIntConstant* constant0_; HIntConstant* constant1_; + // The dex file where the method being compiled is. const DexFile* const dex_file_; + + // The compilation unit of the current method being compiled. Note that + // it can be an inlined method. DexCompilationUnit* const dex_compilation_unit_; + CompilerDriver* const compiler_driver_; + + // The compilation unit of the outermost method being compiled. That is the + // method being compiled (and not inlined), and potentially inlining other + // methods. + const DexCompilationUnit* const outer_compilation_unit_; + + // The return type of the method being compiled. const Primitive::Type return_type_; // The pointer in the dex file where the instructions of the code item diff --git a/compiler/optimizing/code_generator.cc b/compiler/optimizing/code_generator.cc index 461409ddca..6f424ce11d 100644 --- a/compiler/optimizing/code_generator.cc +++ b/compiler/optimizing/code_generator.cc @@ -565,10 +565,19 @@ void CodeGenerator::RecordPcInfo(HInstruction* instruction, uint32_t dex_pc) { stack_map_stream_.AddDexRegisterEntry(DexRegisterMap::kConstant, High32Bits(value)); ++i; DCHECK_LT(i, environment_size); - } else { - DCHECK(current->IsIntConstant()); + } else if (current->IsDoubleConstant()) { + int64_t value = bit_cast<double, int64_t>(current->AsDoubleConstant()->GetValue()); + stack_map_stream_.AddDexRegisterEntry(DexRegisterMap::kConstant, Low32Bits(value)); + stack_map_stream_.AddDexRegisterEntry(DexRegisterMap::kConstant, High32Bits(value)); + ++i; + DCHECK_LT(i, environment_size); + } else if (current->IsIntConstant()) { int32_t value = current->AsIntConstant()->GetValue(); stack_map_stream_.AddDexRegisterEntry(DexRegisterMap::kConstant, value); + } else { + DCHECK(current->IsFloatConstant()); + int32_t value = bit_cast<float, int32_t>(current->AsFloatConstant()->GetValue()); + stack_map_stream_.AddDexRegisterEntry(DexRegisterMap::kConstant, value); } break; } diff --git a/compiler/optimizing/code_generator_arm.cc b/compiler/optimizing/code_generator_arm.cc index cbe5f0cc6e..002d9d4449 100644 --- a/compiler/optimizing/code_generator_arm.cc +++ b/compiler/optimizing/code_generator_arm.cc @@ -1171,7 +1171,7 @@ void InstructionCodeGeneratorARM::VisitReturn(HReturn* ret) { codegen_->GenerateFrameExit(); } -void LocationsBuilderARM::VisitInvokeStatic(HInvokeStatic* invoke) { +void LocationsBuilderARM::VisitInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) { HandleInvoke(invoke); } @@ -1179,7 +1179,7 @@ void CodeGeneratorARM::LoadCurrentMethod(Register reg) { __ LoadFromOffset(kLoadWord, reg, SP, kCurrentMethodStackOffset); } -void InstructionCodeGeneratorARM::VisitInvokeStatic(HInvokeStatic* invoke) { +void InstructionCodeGeneratorARM::VisitInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) { Register temp = invoke->GetLocations()->GetTemp(0).AsRegister<Register>(); // TODO: Implement all kinds of calls: diff --git a/compiler/optimizing/code_generator_arm64.cc b/compiler/optimizing/code_generator_arm64.cc index b048c07b4c..c7517d3abc 100644 --- a/compiler/optimizing/code_generator_arm64.cc +++ b/compiler/optimizing/code_generator_arm64.cc @@ -1796,11 +1796,11 @@ void LocationsBuilderARM64::VisitInvokeVirtual(HInvokeVirtual* invoke) { HandleInvoke(invoke); } -void LocationsBuilderARM64::VisitInvokeStatic(HInvokeStatic* invoke) { +void LocationsBuilderARM64::VisitInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) { HandleInvoke(invoke); } -void InstructionCodeGeneratorARM64::VisitInvokeStatic(HInvokeStatic* invoke) { +void InstructionCodeGeneratorARM64::VisitInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) { Register temp = WRegisterFrom(invoke->GetLocations()->GetTemp(0)); // Make sure that ArtMethod* is passed in W0 as per the calling convention DCHECK(temp.Is(w0)); diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc index 47572350b4..e7edd8a805 100644 --- a/compiler/optimizing/code_generator_x86.cc +++ b/compiler/optimizing/code_generator_x86.cc @@ -1115,11 +1115,11 @@ void InstructionCodeGeneratorX86::VisitReturn(HReturn* ret) { __ ret(); } -void LocationsBuilderX86::VisitInvokeStatic(HInvokeStatic* invoke) { +void LocationsBuilderX86::VisitInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) { HandleInvoke(invoke); } -void InstructionCodeGeneratorX86::VisitInvokeStatic(HInvokeStatic* invoke) { +void InstructionCodeGeneratorX86::VisitInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) { Register temp = invoke->GetLocations()->GetTemp(0).AsRegister<Register>(); // TODO: Implement all kinds of calls: diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc index f8651f605f..ff7fcdcbac 100644 --- a/compiler/optimizing/code_generator_x86_64.cc +++ b/compiler/optimizing/code_generator_x86_64.cc @@ -1122,11 +1122,11 @@ Location InvokeDexCallingConventionVisitor::GetNextLocation(Primitive::Type type return Location(); } -void LocationsBuilderX86_64::VisitInvokeStatic(HInvokeStatic* invoke) { +void LocationsBuilderX86_64::VisitInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) { HandleInvoke(invoke); } -void InstructionCodeGeneratorX86_64::VisitInvokeStatic(HInvokeStatic* invoke) { +void InstructionCodeGeneratorX86_64::VisitInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) { CpuRegister temp = invoke->GetLocations()->GetTemp(0).AsRegister<CpuRegister>(); // TODO: Implement all kinds of calls: // 1) boot -> boot diff --git a/compiler/optimizing/constant_folding_test.cc b/compiler/optimizing/constant_folding_test.cc index a56b9d9a12..cad6683577 100644 --- a/compiler/optimizing/constant_folding_test.cc +++ b/compiler/optimizing/constant_folding_test.cc @@ -38,8 +38,7 @@ static void TestCode(const uint16_t* data, HGraph* graph = CreateCFG(&allocator, data, return_type); ASSERT_NE(graph, nullptr); - graph->BuildDominatorTree(); - graph->TransformToSSA(); + graph->TryBuildingSsa(); StringPrettyPrinter printer_before(graph); printer_before.VisitInsertionOrder(); diff --git a/compiler/optimizing/dead_code_elimination_test.cc b/compiler/optimizing/dead_code_elimination_test.cc index 5d4b9cb024..3dbd04e250 100644 --- a/compiler/optimizing/dead_code_elimination_test.cc +++ b/compiler/optimizing/dead_code_elimination_test.cc @@ -32,8 +32,7 @@ static void TestCode(const uint16_t* data, HGraph* graph = CreateCFG(&allocator, data); ASSERT_NE(graph, nullptr); - graph->BuildDominatorTree(); - graph->TransformToSSA(); + graph->TryBuildingSsa(); StringPrettyPrinter printer_before(graph); printer_before.VisitInsertionOrder(); diff --git a/compiler/optimizing/graph_checker_test.cc b/compiler/optimizing/graph_checker_test.cc index 39def82007..923468ff16 100644 --- a/compiler/optimizing/graph_checker_test.cc +++ b/compiler/optimizing/graph_checker_test.cc @@ -62,7 +62,7 @@ static void TestCodeSSA(const uint16_t* data) { ASSERT_NE(graph, nullptr); graph->BuildDominatorTree(); - graph->TransformToSSA(); + graph->TransformToSsa(); SSAChecker ssa_checker(&allocator, graph); ssa_checker.Run(); diff --git a/compiler/optimizing/graph_visualizer.cc b/compiler/optimizing/graph_visualizer.cc index 4ed2156241..5d1703e237 100644 --- a/compiler/optimizing/graph_visualizer.cc +++ b/compiler/optimizing/graph_visualizer.cc @@ -17,7 +17,6 @@ #include "graph_visualizer.h" #include "code_generator.h" -#include "driver/dex_compilation_unit.h" #include "nodes.h" #include "ssa_liveness_analysis.h" @@ -270,39 +269,20 @@ HGraphVisualizer::HGraphVisualizer(std::ostream* output, HGraph* graph, const char* string_filter, const CodeGenerator& codegen, - const DexCompilationUnit& cu) + const char* method_name) : output_(output), graph_(graph), codegen_(codegen), is_enabled_(false) { if (output == nullptr) { return; } - std::string pretty_name = PrettyMethod(cu.GetDexMethodIndex(), *cu.GetDexFile()); - if (pretty_name.find(string_filter) == std::string::npos) { + if (strstr(method_name, string_filter) == nullptr) { return; } is_enabled_ = true; HGraphVisualizerPrinter printer(graph, *output_, "", codegen_); printer.StartTag("compilation"); - printer.PrintProperty("name", pretty_name.c_str()); - printer.PrintProperty("method", pretty_name.c_str()); - printer.PrintTime("date"); - printer.EndTag("compilation"); -} - -HGraphVisualizer::HGraphVisualizer(std::ostream* output, - HGraph* graph, - const CodeGenerator& codegen, - const char* name) - : output_(output), graph_(graph), codegen_(codegen), is_enabled_(false) { - if (output == nullptr) { - return; - } - - is_enabled_ = true; - HGraphVisualizerPrinter printer(graph, *output_, "", codegen_); - printer.StartTag("compilation"); - printer.PrintProperty("name", name); - printer.PrintProperty("method", name); + printer.PrintProperty("name", method_name); + printer.PrintProperty("method", method_name); printer.PrintTime("date"); printer.EndTag("compilation"); } diff --git a/compiler/optimizing/graph_visualizer.h b/compiler/optimizing/graph_visualizer.h index 60d996ba88..b5baed9c99 100644 --- a/compiler/optimizing/graph_visualizer.h +++ b/compiler/optimizing/graph_visualizer.h @@ -47,16 +47,7 @@ class HGraphVisualizer : public ValueObject { HGraph* graph, const char* string_filter, const CodeGenerator& codegen, - const DexCompilationUnit& cu); - - /** - * Version of `HGraphVisualizer` for unit testing, that is when a - * `DexCompilationUnit` is not available. - */ - HGraphVisualizer(std::ostream* output, - HGraph* graph, - const CodeGenerator& codegen, - const char* name); + const char* method_name); /** * If this visualizer is enabled, emit the compilation information diff --git a/compiler/optimizing/gvn_test.cc b/compiler/optimizing/gvn_test.cc index a6a68ca59d..94ff192264 100644 --- a/compiler/optimizing/gvn_test.cc +++ b/compiler/optimizing/gvn_test.cc @@ -59,8 +59,7 @@ TEST(GVNTest, LocalFieldElimination) { ASSERT_EQ(different_offset->GetBlock(), block); ASSERT_EQ(use_after_kill->GetBlock(), block); - graph->BuildDominatorTree(); - graph->TransformToSSA(); + graph->TryBuildingSsa(); GlobalValueNumberer(&allocator, graph).Run(); ASSERT_TRUE(to_remove->GetBlock() == nullptr); @@ -108,8 +107,7 @@ TEST(GVNTest, GlobalFieldElimination) { new (&allocator) HInstanceFieldGet(parameter, Primitive::kPrimBoolean, MemberOffset(42))); join->AddInstruction(new (&allocator) HExit()); - graph->BuildDominatorTree(); - graph->TransformToSSA(); + graph->TryBuildingSsa(); GlobalValueNumberer(&allocator, graph).Run(); // Check that all field get instructions have been GVN'ed. @@ -173,9 +171,7 @@ TEST(GVNTest, LoopFieldElimination) { ASSERT_EQ(field_get_in_loop_body->GetBlock(), loop_body); ASSERT_EQ(field_get_in_exit->GetBlock(), exit); - graph->BuildDominatorTree(); - graph->TransformToSSA(); - graph->AnalyzeNaturalLoops(); + graph->TryBuildingSsa(); GlobalValueNumberer(&allocator, graph).Run(); // Check that all field get instructions are still there. @@ -237,9 +233,7 @@ TEST(GVNTest, LoopSideEffects) { inner_loop_exit->AddInstruction(new (&allocator) HGoto()); outer_loop_exit->AddInstruction(new (&allocator) HExit()); - graph->BuildDominatorTree(); - graph->TransformToSSA(); - graph->AnalyzeNaturalLoops(); + graph->TryBuildingSsa(); ASSERT_TRUE(inner_loop_header->GetLoopInformation()->IsIn( *outer_loop_header->GetLoopInformation())); diff --git a/compiler/optimizing/inliner.cc b/compiler/optimizing/inliner.cc new file mode 100644 index 0000000000..1de5b78121 --- /dev/null +++ b/compiler/optimizing/inliner.cc @@ -0,0 +1,208 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "inliner.h" + +#include "builder.h" +#include "class_linker.h" +#include "constant_folding.h" +#include "dead_code_elimination.h" +#include "driver/compiler_driver-inl.h" +#include "driver/dex_compilation_unit.h" +#include "instruction_simplifier.h" +#include "mirror/art_method-inl.h" +#include "mirror/class_loader.h" +#include "mirror/dex_cache.h" +#include "nodes.h" +#include "ssa_phi_elimination.h" +#include "scoped_thread_state_change.h" +#include "thread.h" + +namespace art { + +static constexpr int kMaxInlineCodeUnits = 100; +static constexpr int kMaxInlineNumberOfBlocks = 3; + +void HInliner::Run() { + for (HReversePostOrderIterator it(*graph_); !it.Done(); it.Advance()) { + for (HInstructionIterator instr_it(it.Current()->GetInstructions()); + !instr_it.Done(); + instr_it.Advance()) { + HInvokeStaticOrDirect* current = instr_it.Current()->AsInvokeStaticOrDirect(); + if (current != nullptr) { + if (!TryInline(current, current->GetIndexInDexCache(), current->GetInvokeType())) { + if (kIsDebugBuild) { + std::string callee_name = + PrettyMethod(current->GetIndexInDexCache(), *outer_compilation_unit_.GetDexFile()); + bool should_inline = callee_name.find("$inline$") != std::string::npos; + CHECK(!should_inline) << "Could not inline " << callee_name; + } + } + } + } + } +} + +bool HInliner::TryInline(HInvoke* invoke_instruction, + uint32_t method_index, + InvokeType invoke_type) const { + ScopedObjectAccess soa(Thread::Current()); + const DexFile& outer_dex_file = *outer_compilation_unit_.GetDexFile(); + VLOG(compiler) << "Try inlining " << PrettyMethod(method_index, outer_dex_file); + + StackHandleScope<3> hs(soa.Self()); + Handle<mirror::DexCache> dex_cache( + hs.NewHandle(outer_compilation_unit_.GetClassLinker()->FindDexCache(outer_dex_file))); + Handle<mirror::ClassLoader> class_loader(hs.NewHandle( + soa.Decode<mirror::ClassLoader*>(outer_compilation_unit_.GetClassLoader()))); + Handle<mirror::ArtMethod> resolved_method(hs.NewHandle( + compiler_driver_->ResolveMethod( + soa, dex_cache, class_loader, &outer_compilation_unit_, method_index, invoke_type))); + + if (resolved_method.Get() == nullptr) { + VLOG(compiler) << "Method cannot be resolved " << PrettyMethod(method_index, outer_dex_file); + return false; + } + + if (resolved_method->GetDexFile()->GetLocation().compare(outer_dex_file.GetLocation()) != 0) { + VLOG(compiler) << "Did not inline " + << PrettyMethod(method_index, outer_dex_file) + << " because it is in a different dex file"; + return false; + } + + const DexFile::CodeItem* code_item = resolved_method->GetCodeItem(); + + if (code_item == nullptr) { + VLOG(compiler) << "Method " << PrettyMethod(method_index, outer_dex_file) + << " is not inlined because it is native"; + return false; + } + + if (code_item->insns_size_in_code_units_ > kMaxInlineCodeUnits) { + VLOG(compiler) << "Method " << PrettyMethod(method_index, outer_dex_file) + << " is too big to inline"; + return false; + } + + if (code_item->tries_size_ != 0) { + VLOG(compiler) << "Method " << PrettyMethod(method_index, outer_dex_file) + << " is not inlined because of try block"; + return false; + } + + if (!resolved_method->GetDeclaringClass()->IsVerified()) { + VLOG(compiler) << "Method " << PrettyMethod(method_index, outer_dex_file) + << " is not inlined because its class could not be verified"; + return false; + } + + DexCompilationUnit dex_compilation_unit( + nullptr, + outer_compilation_unit_.GetClassLoader(), + outer_compilation_unit_.GetClassLinker(), + outer_dex_file, + code_item, + resolved_method->GetDeclaringClass()->GetDexClassDefIndex(), + method_index, + resolved_method->GetAccessFlags(), + nullptr); + + OptimizingCompilerStats inline_stats; + HGraphBuilder builder(graph_->GetArena(), + &dex_compilation_unit, + &outer_compilation_unit_, + &outer_dex_file, + compiler_driver_, + &inline_stats); + HGraph* callee_graph = builder.BuildGraph(*code_item, graph_->GetCurrentInstructionId()); + + if (callee_graph == nullptr) { + VLOG(compiler) << "Method " << PrettyMethod(method_index, outer_dex_file) + << " could not be built, so cannot be inlined"; + return false; + } + + if (callee_graph->GetBlocks().Size() > kMaxInlineNumberOfBlocks) { + VLOG(compiler) << "Method " << PrettyMethod(method_index, outer_dex_file) + << " has too many blocks to be inlined: " + << callee_graph->GetBlocks().Size(); + return false; + } + + if (!callee_graph->TryBuildingSsa()) { + VLOG(compiler) << "Method " << PrettyMethod(method_index, outer_dex_file) + << " could not be transformed to SSA"; + return false; + } + + HReversePostOrderIterator it(*callee_graph); + it.Advance(); // Past the entry block to avoid seeing the suspend check. + for (; !it.Done(); it.Advance()) { + HBasicBlock* block = it.Current(); + if (block->IsLoopHeader()) { + VLOG(compiler) << "Method " << PrettyMethod(method_index, outer_dex_file) + << " could not be inlined because it contains a loop"; + return false; + } + + for (HInstructionIterator instr_it(block->GetInstructions()); + !instr_it.Done(); + instr_it.Advance()) { + HInstruction* current = instr_it.Current(); + if (current->CanThrow()) { + VLOG(compiler) << "Method " << PrettyMethod(method_index, outer_dex_file) + << " could not be inlined because " << current->DebugName() + << " can throw"; + return false; + } + + if (current->NeedsEnvironment()) { + VLOG(compiler) << "Method " << PrettyMethod(method_index, outer_dex_file) + << " could not be inlined because " << current->DebugName() + << " needs an environment"; + return false; + } + } + } + + // Run simple optimizations on the graph. + SsaRedundantPhiElimination redundant_phi(callee_graph); + SsaDeadPhiElimination dead_phi(callee_graph); + HDeadCodeElimination dce(callee_graph); + HConstantFolding fold(callee_graph); + InstructionSimplifier simplify(callee_graph); + + HOptimization* optimizations[] = { + &redundant_phi, + &dead_phi, + &dce, + &fold, + &simplify, + }; + + for (size_t i = 0; i < arraysize(optimizations); ++i) { + HOptimization* optimization = optimizations[i]; + optimization->Run(); + } + + callee_graph->InlineInto(graph_, invoke_instruction); + VLOG(compiler) << "Successfully inlined " << PrettyMethod(method_index, outer_dex_file); + outer_stats_->RecordStat(kInlinedInvoke); + return true; +} + +} // namespace art diff --git a/compiler/optimizing/inliner.h b/compiler/optimizing/inliner.h new file mode 100644 index 0000000000..370e33c2a1 --- /dev/null +++ b/compiler/optimizing/inliner.h @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ART_COMPILER_OPTIMIZING_INLINER_H_ +#define ART_COMPILER_OPTIMIZING_INLINER_H_ + +#include "invoke_type.h" +#include "optimization.h" + +namespace art { + +class CompilerDriver; +class DexCompilationUnit; +class HGraph; +class HInvoke; +class OptimizingCompilerStats; + +class HInliner : public HOptimization { + public: + HInliner(HGraph* outer_graph, + const DexCompilationUnit& outer_compilation_unit, + CompilerDriver* compiler_driver, + OptimizingCompilerStats* stats) + : HOptimization(outer_graph, true, "inliner"), + outer_compilation_unit_(outer_compilation_unit), + compiler_driver_(compiler_driver), + outer_stats_(stats) {} + + void Run() OVERRIDE; + + private: + bool TryInline(HInvoke* invoke_instruction, uint32_t method_index, InvokeType invoke_type) const; + + const DexCompilationUnit& outer_compilation_unit_; + CompilerDriver* const compiler_driver_; + OptimizingCompilerStats* const outer_stats_; + + DISALLOW_COPY_AND_ASSIGN(HInliner); +}; + +} // namespace art + +#endif // ART_COMPILER_OPTIMIZING_INLINER_H_ diff --git a/compiler/optimizing/linearize_test.cc b/compiler/optimizing/linearize_test.cc index 28ca5e81e6..59404dcb14 100644 --- a/compiler/optimizing/linearize_test.cc +++ b/compiler/optimizing/linearize_test.cc @@ -42,9 +42,7 @@ static void TestCode(const uint16_t* data, const int* expected_order, size_t num HGraph* graph = builder.BuildGraph(*item); ASSERT_NE(graph, nullptr); - graph->BuildDominatorTree(); - graph->TransformToSSA(); - graph->AnalyzeNaturalLoops(); + graph->TryBuildingSsa(); x86::CodeGeneratorX86 codegen(graph); SsaLivenessAnalysis liveness(*graph, &codegen); diff --git a/compiler/optimizing/live_ranges_test.cc b/compiler/optimizing/live_ranges_test.cc index 5c7e6f0325..007c43e218 100644 --- a/compiler/optimizing/live_ranges_test.cc +++ b/compiler/optimizing/live_ranges_test.cc @@ -36,9 +36,7 @@ static HGraph* BuildGraph(const uint16_t* data, ArenaAllocator* allocator) { // Suspend checks implementation may change in the future, and this test relies // on how instructions are ordered. RemoveSuspendChecks(graph); - graph->BuildDominatorTree(); - graph->TransformToSSA(); - graph->AnalyzeNaturalLoops(); + graph->TryBuildingSsa(); // `Inline` conditions into ifs. PrepareForRegisterAllocation(graph).Run(); return graph; diff --git a/compiler/optimizing/liveness_test.cc b/compiler/optimizing/liveness_test.cc index 4b69e57960..6f706c391d 100644 --- a/compiler/optimizing/liveness_test.cc +++ b/compiler/optimizing/liveness_test.cc @@ -48,9 +48,7 @@ static void TestCode(const uint16_t* data, const char* expected) { const DexFile::CodeItem* item = reinterpret_cast<const DexFile::CodeItem*>(data); HGraph* graph = builder.BuildGraph(*item); ASSERT_NE(graph, nullptr); - graph->BuildDominatorTree(); - graph->TransformToSSA(); - graph->AnalyzeNaturalLoops(); + graph->TryBuildingSsa(); // `Inline` conditions into ifs. PrepareForRegisterAllocation(graph).Run(); x86::CodeGeneratorX86 codegen(graph); diff --git a/compiler/optimizing/nodes.cc b/compiler/optimizing/nodes.cc index ba4dccf598..fb941b542f 100644 --- a/compiler/optimizing/nodes.cc +++ b/compiler/optimizing/nodes.cc @@ -167,7 +167,7 @@ void HGraph::VisitBlockForDominatorTree(HBasicBlock* block, } } -void HGraph::TransformToSSA() { +void HGraph::TransformToSsa() { DCHECK(!reverse_post_order_.IsEmpty()); SsaBuilder ssa_builder(this); ssa_builder.BuildSsa(); @@ -682,4 +682,81 @@ std::ostream& operator<<(std::ostream& os, const HInstruction::InstructionKind& return os; } +void HInstruction::InsertBefore(HInstruction* cursor) { + next_->previous_ = previous_; + if (previous_ != nullptr) { + previous_->next_ = next_; + } + if (block_->instructions_.first_instruction_ == this) { + block_->instructions_.first_instruction_ = next_; + } + + previous_ = cursor->previous_; + if (previous_ != nullptr) { + previous_->next_ = this; + } + next_ = cursor; + cursor->previous_ = this; + block_ = cursor->block_; +} + +void HGraph::InlineInto(HGraph* outer_graph, HInvoke* invoke) { + // We currently only support graphs with one entry block, one body block, and one exit block. + DCHECK_EQ(GetBlocks().Size(), 3u); + + // Walk over the entry block and: + // - Move constants from the entry block to the outer_graph's entry block, + // - Replace HParameterValue instructions with their real value. + // - Remove suspend checks, that hold an environment. + int parameter_index = 0; + for (HInstructionIterator it(entry_block_->GetInstructions()); !it.Done(); it.Advance()) { + HInstruction* current = it.Current(); + if (current->IsConstant()) { + current->InsertBefore(outer_graph->GetEntryBlock()->GetLastInstruction()); + } else if (current->IsParameterValue()) { + current->ReplaceWith(invoke->InputAt(parameter_index++)); + } else { + DCHECK(current->IsGoto() || current->IsSuspendCheck()); + entry_block_->RemoveInstruction(current); + } + } + + // Insert the body's instructions except the last, just after the `invoke` + // instruction. + HBasicBlock* body = GetBlocks().Get(1); + DCHECK(!body->IsExitBlock()); + HInstruction* last = body->GetLastInstruction(); + HInstruction* first = body->GetFirstInstruction(); + + if (first != last) { + HInstruction* antelast = last->GetPrevious(); + + // Update the instruction list of the body to only contain the last + // instruction. + last->previous_ = nullptr; + body->instructions_.first_instruction_ = last; + body->instructions_.last_instruction_ = last; + + // Update the instruction list of the `invoke`'s block to now contain the + // body's instructions. + antelast->next_ = invoke->GetNext(); + antelast->next_->previous_ = antelast; + first->previous_ = invoke; + invoke->next_ = first; + + // Update the block pointer of all instructions. + for (HInstruction* current = antelast; current != invoke; current = current->GetPrevious()) { + current->SetBlock(invoke->GetBlock()); + } + } + + // Finally, replace the invoke with the return value of the inlined graph. + if (last->IsReturn()) { + invoke->ReplaceWith(last->InputAt(0)); + body->RemoveInstruction(last); + } else { + DCHECK(last->IsReturnVoid()); + } +} + } // namespace art diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h index 8a25de19d9..c963b70492 100644 --- a/compiler/optimizing/nodes.h +++ b/compiler/optimizing/nodes.h @@ -17,6 +17,7 @@ #ifndef ART_COMPILER_OPTIMIZING_NODES_H_ #define ART_COMPILER_OPTIMIZING_NODES_H_ +#include "invoke_type.h" #include "locations.h" #include "offsets.h" #include "primitive.h" @@ -30,6 +31,7 @@ class HBasicBlock; class HEnvironment; class HInstruction; class HIntConstant; +class HInvoke; class HGraphVisitor; class HPhi; class HSuspendCheck; @@ -75,6 +77,8 @@ class HInstructionList { HInstruction* last_instruction_; friend class HBasicBlock; + friend class HGraph; + friend class HInstruction; friend class HInstructionIterator; friend class HBackwardInstructionIterator; @@ -84,7 +88,7 @@ class HInstructionList { // Control-flow graph of a method. Contains a list of basic blocks. class HGraph : public ArenaObject<kArenaAllocMisc> { public: - explicit HGraph(ArenaAllocator* arena) + HGraph(ArenaAllocator* arena, int start_instruction_id = 0) : arena_(arena), blocks_(arena, kDefaultNumberOfBlocks), reverse_post_order_(arena, kDefaultNumberOfBlocks), @@ -94,7 +98,7 @@ class HGraph : public ArenaObject<kArenaAllocMisc> { number_of_vregs_(0), number_of_in_vregs_(0), temporaries_vreg_slots_(0), - current_instruction_id_(0) {} + current_instruction_id_(start_instruction_id) {} ArenaAllocator* GetArena() const { return arena_; } const GrowableArray<HBasicBlock*>& GetBlocks() const { return blocks_; } @@ -108,8 +112,16 @@ class HGraph : public ArenaObject<kArenaAllocMisc> { void AddBlock(HBasicBlock* block); + // Try building the SSA form of this graph, with dominance computation and loop + // recognition. Returns whether it was successful in doing all these steps. + bool TryBuildingSsa() { + BuildDominatorTree(); + TransformToSsa(); + return AnalyzeNaturalLoops(); + } + void BuildDominatorTree(); - void TransformToSSA(); + void TransformToSsa(); void SimplifyCFG(); // Analyze all natural loops in this graph. Returns false if one @@ -117,19 +129,31 @@ class HGraph : public ArenaObject<kArenaAllocMisc> { // back edge. bool AnalyzeNaturalLoops() const; + // Inline this graph in `outer_graph`, replacing the given `invoke` instruction. + void InlineInto(HGraph* outer_graph, HInvoke* invoke); + void SplitCriticalEdge(HBasicBlock* block, HBasicBlock* successor); void SimplifyLoop(HBasicBlock* header); - int GetNextInstructionId() { + int32_t GetNextInstructionId() { + DCHECK_NE(current_instruction_id_, INT32_MAX); return current_instruction_id_++; } + int32_t GetCurrentInstructionId() const { + return current_instruction_id_; + } + + void SetCurrentInstructionId(int32_t id) { + current_instruction_id_ = id; + } + uint16_t GetMaximumNumberOfOutVRegs() const { return maximum_number_of_out_vregs_; } - void UpdateMaximumNumberOfOutVRegs(uint16_t new_value) { - maximum_number_of_out_vregs_ = std::max(new_value, maximum_number_of_out_vregs_); + void SetMaximumNumberOfOutVRegs(uint16_t new_value) { + maximum_number_of_out_vregs_ = new_value; } void UpdateTemporariesVRegSlots(size_t slots) { @@ -152,10 +176,6 @@ class HGraph : public ArenaObject<kArenaAllocMisc> { number_of_in_vregs_ = value; } - uint16_t GetNumberOfInVRegs() const { - return number_of_in_vregs_; - } - uint16_t GetNumberOfLocalVRegs() const { return number_of_vregs_ - number_of_in_vregs_; } @@ -200,8 +220,9 @@ class HGraph : public ArenaObject<kArenaAllocMisc> { size_t temporaries_vreg_slots_; // The current id to assign to a newly added instruction. See HInstruction.id_. - int current_instruction_id_; + int32_t current_instruction_id_; + ART_FRIEND_TEST(GraphTest, IfSuccessorSimpleJoinBlock1); DISALLOW_COPY_AND_ASSIGN(HGraph); }; @@ -474,6 +495,9 @@ class HBasicBlock : public ArenaObject<kArenaAllocMisc> { size_t lifetime_end_; bool is_catch_block_; + friend class HGraph; + friend class HInstruction; + DISALLOW_COPY_AND_ASSIGN(HBasicBlock); }; @@ -503,7 +527,7 @@ class HBasicBlock : public ArenaObject<kArenaAllocMisc> { M(InstanceOf, Instruction) \ M(IntConstant, Constant) \ M(InvokeInterface, Invoke) \ - M(InvokeStatic, Invoke) \ + M(InvokeStaticOrDirect, Invoke) \ M(InvokeVirtual, Invoke) \ M(LessThan, Condition) \ M(LessThanOrEqual, Condition) \ @@ -748,6 +772,9 @@ class HInstruction : public ArenaObject<kArenaAllocMisc> { void ReplaceWith(HInstruction* instruction); void ReplaceInput(HInstruction* replacement, size_t index); + // Insert `this` instruction in `cursor`'s graph, just before `cursor`. + void InsertBefore(HInstruction* cursor); + bool HasOnlyOneUse() const { return uses_ != nullptr && uses_->GetTail() == nullptr; } @@ -836,6 +863,7 @@ class HInstruction : public ArenaObject<kArenaAllocMisc> { const SideEffects side_effects_; friend class HBasicBlock; + friend class HGraph; friend class HInstructionList; DISALLOW_COPY_AND_ASSIGN(HInstruction); @@ -1595,24 +1623,28 @@ class HInvoke : public HInstruction { DISALLOW_COPY_AND_ASSIGN(HInvoke); }; -class HInvokeStatic : public HInvoke { +class HInvokeStaticOrDirect : public HInvoke { public: - HInvokeStatic(ArenaAllocator* arena, - uint32_t number_of_arguments, - Primitive::Type return_type, - uint32_t dex_pc, - uint32_t index_in_dex_cache) + HInvokeStaticOrDirect(ArenaAllocator* arena, + uint32_t number_of_arguments, + Primitive::Type return_type, + uint32_t dex_pc, + uint32_t index_in_dex_cache, + InvokeType invoke_type) : HInvoke(arena, number_of_arguments, return_type, dex_pc), - index_in_dex_cache_(index_in_dex_cache) {} + index_in_dex_cache_(index_in_dex_cache), + invoke_type_(invoke_type) {} uint32_t GetIndexInDexCache() const { return index_in_dex_cache_; } + InvokeType GetInvokeType() const { return invoke_type_; } - DECLARE_INSTRUCTION(InvokeStatic); + DECLARE_INSTRUCTION(InvokeStaticOrDirect); private: const uint32_t index_in_dex_cache_; + const InvokeType invoke_type_; - DISALLOW_COPY_AND_ASSIGN(HInvokeStatic); + DISALLOW_COPY_AND_ASSIGN(HInvokeStaticOrDirect); }; class HInvokeVirtual : public HInvoke { @@ -2425,7 +2457,7 @@ class HLoadString : public HExpression<0> { DISALLOW_COPY_AND_ASSIGN(HLoadString); }; -// TODO: Pass this check to HInvokeStatic nodes. +// TODO: Pass this check to HInvokeStaticOrDirect nodes. /** * Performs an initialization check on its Class object input. */ diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc index d47217fa3f..deebaf7414 100644 --- a/compiler/optimizing/optimizing_compiler.cc +++ b/compiler/optimizing/optimizing_compiler.cc @@ -30,6 +30,7 @@ #include "elf_writer_quick.h" #include "graph_visualizer.h" #include "gvn.h" +#include "inliner.h" #include "instruction_simplifier.h" #include "jni/quick/jni_compiler.h" #include "mirror/art_method-inl.h" @@ -181,25 +182,33 @@ static bool CanOptimize(const DexFile::CodeItem& code_item) { return code_item.tries_size_ == 0; } -static void RunOptimizations(HGraph* graph, const HGraphVisualizer& visualizer) { - HDeadCodeElimination opt1(graph); - HConstantFolding opt2(graph); - SsaRedundantPhiElimination opt3(graph); - SsaDeadPhiElimination opt4(graph); - InstructionSimplifier opt5(graph); - GVNOptimization opt6(graph); +static void RunOptimizations(HGraph* graph, + CompilerDriver* driver, + OptimizingCompilerStats* stats, + const DexCompilationUnit& dex_compilation_unit, + const HGraphVisualizer& visualizer) { + SsaRedundantPhiElimination redundant_phi(graph); + SsaDeadPhiElimination dead_phi(graph); + HDeadCodeElimination dce(graph); + HConstantFolding fold(graph); + InstructionSimplifier simplify1(graph); + + HInliner inliner(graph, dex_compilation_unit, driver, stats); + + GVNOptimization gvn(graph); BoundsCheckElimination bce(graph); - InstructionSimplifier opt8(graph); + InstructionSimplifier simplify2(graph); HOptimization* optimizations[] = { - &opt1, - &opt2, - &opt3, - &opt4, - &opt5, - &opt6, + &redundant_phi, + &dead_phi, + &dce, + &fold, + &simplify1, + &inliner, + &gvn, &bce, - &opt8 + &simplify2 }; for (size_t i = 0; i < arraysize(optimizations); ++i) { @@ -210,23 +219,6 @@ static void RunOptimizations(HGraph* graph, const HGraphVisualizer& visualizer) } } -static bool TryBuildingSsa(HGraph* graph, - const DexCompilationUnit& dex_compilation_unit, - const HGraphVisualizer& visualizer) { - graph->BuildDominatorTree(); - graph->TransformToSSA(); - - if (!graph->AnalyzeNaturalLoops()) { - LOG(INFO) << "Skipping compilation of " - << PrettyMethod(dex_compilation_unit.GetDexMethodIndex(), - *dex_compilation_unit.GetDexFile()) - << ": it contains a non natural loop"; - return false; - } - visualizer.DumpGraph("ssa transform"); - return true; -} - // The stack map we generate must be 4-byte aligned on ARM. Since existing // maps are generated alongside these stack maps, we must also align them. static std::vector<uint8_t>& AlignVectorSize(std::vector<uint8_t>& vector) { @@ -270,19 +262,23 @@ CompiledMethod* OptimizingCompiler::Compile(const DexFile::CodeItem* code_item, class_def_idx, method_idx, access_flags, GetCompilerDriver()->GetVerifiedMethod(&dex_file, method_idx)); + std::string method_name = PrettyMethod(method_idx, dex_file); + // For testing purposes, we put a special marker on method names that should be compiled // with this compiler. This makes sure we're not regressing. - bool shouldCompile = dex_compilation_unit.GetSymbol().find("00024opt_00024") != std::string::npos; - bool shouldOptimize = - dex_compilation_unit.GetSymbol().find("00024reg_00024") != std::string::npos; + bool shouldCompile = method_name.find("$opt$") != std::string::npos; + bool shouldOptimize = method_name.find("$opt$reg$") != std::string::npos; ArenaPool pool; ArenaAllocator arena(&pool); HGraphBuilder builder(&arena, &dex_compilation_unit, - &dex_file, GetCompilerDriver(), + &dex_compilation_unit, + &dex_file, + GetCompilerDriver(), &compilation_stats_); + VLOG(compiler) << "Building " << PrettyMethod(method_idx, dex_file); HGraph* graph = builder.BuildGraph(*code_item); if (graph == nullptr) { CHECK(!shouldCompile) << "Could not build graph in optimizing compiler"; @@ -297,7 +293,7 @@ CompiledMethod* OptimizingCompiler::Compile(const DexFile::CodeItem* code_item, } HGraphVisualizer visualizer( - visualizer_output_.get(), graph, kStringFilter, *codegen, dex_compilation_unit); + visualizer_output_.get(), graph, kStringFilter, *codegen, method_name.c_str()); visualizer.DumpGraph("builder"); CodeVectorAllocator allocator; @@ -306,12 +302,16 @@ CompiledMethod* OptimizingCompiler::Compile(const DexFile::CodeItem* code_item, bool can_allocate_registers = RegisterAllocator::CanAllocateRegistersFor(*graph, instruction_set); if (run_optimizations_ && can_optimize && can_allocate_registers) { VLOG(compiler) << "Optimizing " << PrettyMethod(method_idx, dex_file); - if (!TryBuildingSsa(graph, dex_compilation_unit, visualizer)) { + if (!graph->TryBuildingSsa()) { + LOG(INFO) << "Skipping compilation of " + << PrettyMethod(method_idx, dex_file) + << ": it contains a non natural loop"; // We could not transform the graph to SSA, bailout. compilation_stats_.RecordStat(MethodCompilationStat::kNotCompiledCannotBuildSSA); return nullptr; } - RunOptimizations(graph, visualizer); + RunOptimizations( + graph, GetCompilerDriver(), &compilation_stats_, dex_compilation_unit, visualizer); PrepareForRegisterAllocation(graph).Run(); SsaLivenessAnalysis liveness(*graph, codegen); diff --git a/compiler/optimizing/optimizing_compiler_stats.h b/compiler/optimizing/optimizing_compiler_stats.h index 829982e792..7993b19850 100644 --- a/compiler/optimizing/optimizing_compiler_stats.h +++ b/compiler/optimizing/optimizing_compiler_stats.h @@ -28,6 +28,7 @@ enum MethodCompilationStat { kAttemptCompilation = 0, kCompiledBaseline, kCompiledOptimized, + kInlinedInvoke, kNotCompiledUnsupportedIsa, kNotCompiledPathological, kNotCompiledHugeMethod, @@ -65,10 +66,10 @@ class OptimizingCompilerStats { std::ostringstream oss; oss << "Attempted compilation of " << compile_stats_[kAttemptCompilation] << " methods: " << unoptimized_percent << "% (" << compile_stats_[kCompiledBaseline] << ") unoptimized, " - << optimized_percent << "% (" << compile_stats_[kCompiledOptimized] << ") optimized.\n"; + << optimized_percent << "% (" << compile_stats_[kCompiledOptimized] << ") optimized."; for (int i = 0; i < kLastStat; i++) { if (compile_stats_[i] != 0) { - oss << PrintMethodCompilationStat(i) << ": " << compile_stats_[i] << "\n"; + oss << "\n" << PrintMethodCompilationStat(i) << ": " << compile_stats_[i]; } } LOG(INFO) << oss.str(); @@ -81,6 +82,7 @@ class OptimizingCompilerStats { case kAttemptCompilation : return "kAttemptCompilation"; case kCompiledBaseline : return "kCompiledBaseline"; case kCompiledOptimized : return "kCompiledOptimized"; + case kInlinedInvoke : return "kInlinedInvoke"; case kNotCompiledUnsupportedIsa : return "kNotCompiledUnsupportedIsa"; case kNotCompiledPathological : return "kNotCompiledPathological"; case kNotCompiledHugeMethod : return "kNotCompiledHugeMethod"; diff --git a/compiler/optimizing/register_allocator_test.cc b/compiler/optimizing/register_allocator_test.cc index 8d75db91d2..f677e840ef 100644 --- a/compiler/optimizing/register_allocator_test.cc +++ b/compiler/optimizing/register_allocator_test.cc @@ -39,9 +39,7 @@ static bool Check(const uint16_t* data) { HGraphBuilder builder(&allocator); const DexFile::CodeItem* item = reinterpret_cast<const DexFile::CodeItem*>(data); HGraph* graph = builder.BuildGraph(*item); - graph->BuildDominatorTree(); - graph->TransformToSSA(); - graph->AnalyzeNaturalLoops(); + graph->TryBuildingSsa(); x86::CodeGeneratorX86 codegen(graph); SsaLivenessAnalysis liveness(*graph, &codegen); liveness.Analyze(); @@ -253,9 +251,7 @@ static HGraph* BuildSSAGraph(const uint16_t* data, ArenaAllocator* allocator) { HGraphBuilder builder(allocator); const DexFile::CodeItem* item = reinterpret_cast<const DexFile::CodeItem*>(data); HGraph* graph = builder.BuildGraph(*item); - graph->BuildDominatorTree(); - graph->TransformToSSA(); - graph->AnalyzeNaturalLoops(); + graph->TryBuildingSsa(); return graph; } diff --git a/compiler/optimizing/ssa_test.cc b/compiler/optimizing/ssa_test.cc index 6174dd49a1..6b6bf05053 100644 --- a/compiler/optimizing/ssa_test.cc +++ b/compiler/optimizing/ssa_test.cc @@ -87,7 +87,7 @@ static void TestCode(const uint16_t* data, const char* expected) { // Suspend checks implementation may change in the future, and this test relies // on how instructions are ordered. RemoveSuspendChecks(graph); - graph->TransformToSSA(); + graph->TransformToSsa(); ReNumberInstructions(graph); // Test that phis had their type set. |