diff options
author | 2017-03-06 14:38:52 +0000 | |
---|---|---|
committer | 2017-03-07 14:32:54 +0000 | |
commit | c4aa82c5b0aa921c51eaf6f6bbaff36501ea2cee (patch) | |
tree | 0da77f4e4a0fca7577f4c4c709353465d46c8581 /compiler | |
parent | 3a791cee90451dc32d46f1b0a85fecc6f22f62bf (diff) |
Invoke typed arraycopy for primitive arrays.
Apps will always call the Object version of arraycopy. When
we can infer the types of the passed arrays, replace the method
being called to be the typed System.arraycopy one.
10% improvement on ExoPlayerBench.
Test: 641-checker-arraycopy
bug: 7103825
Change-Id: I872d7a6e163a4614510ef04ae582eb90ec48b5fa
Diffstat (limited to 'compiler')
-rw-r--r-- | compiler/optimizing/bounds_check_elimination_test.cc | 2 | ||||
-rw-r--r-- | compiler/optimizing/inliner.cc | 4 | ||||
-rw-r--r-- | compiler/optimizing/instruction_builder.cc | 8 | ||||
-rw-r--r-- | compiler/optimizing/instruction_simplifier.cc | 58 | ||||
-rw-r--r-- | compiler/optimizing/instruction_simplifier.h | 8 | ||||
-rw-r--r-- | compiler/optimizing/nodes.h | 3 | ||||
-rw-r--r-- | compiler/optimizing/optimizing_compiler.cc | 10 | ||||
-rw-r--r-- | compiler/optimizing/sharpening.cc | 30 | ||||
-rw-r--r-- | compiler/optimizing/sharpening.h | 12 |
9 files changed, 102 insertions, 33 deletions
diff --git a/compiler/optimizing/bounds_check_elimination_test.cc b/compiler/optimizing/bounds_check_elimination_test.cc index 5d58207511..cb6e14b2bd 100644 --- a/compiler/optimizing/bounds_check_elimination_test.cc +++ b/compiler/optimizing/bounds_check_elimination_test.cc @@ -43,7 +43,7 @@ class BoundsCheckEliminationTest : public testing::Test { void RunBCE() { graph_->BuildDominatorTree(); - InstructionSimplifier(graph_).Run(); + InstructionSimplifier(graph_, /* codegen */ nullptr).Run(); SideEffectsAnalysis side_effects(graph_); side_effects.Run(); diff --git a/compiler/optimizing/inliner.cc b/compiler/optimizing/inliner.cc index 3e340908bf..e3926c58c4 100644 --- a/compiler/optimizing/inliner.cc +++ b/compiler/optimizing/inliner.cc @@ -549,7 +549,7 @@ HInstruction* HInliner::AddTypeGuard(HInstruction* receiver, is_referrer, invoke_instruction->GetDexPc(), /* needs_access_check */ false); - HLoadClass::LoadKind kind = HSharpening::SharpenClass( + HLoadClass::LoadKind kind = HSharpening::ComputeLoadClassKind( load_class, codegen_, compiler_driver_, caller_compilation_unit_); DCHECK(kind != HLoadClass::LoadKind::kInvalid) << "We should always be able to reference a class for inline caches"; @@ -1491,7 +1491,7 @@ size_t HInliner::RunOptimizations(HGraph* callee_graph, HDeadCodeElimination dce(callee_graph, inline_stats_, "dead_code_elimination$inliner"); HConstantFolding fold(callee_graph, "constant_folding$inliner"); HSharpening sharpening(callee_graph, codegen_, dex_compilation_unit, compiler_driver_, handles_); - InstructionSimplifier simplify(callee_graph, inline_stats_); + InstructionSimplifier simplify(callee_graph, codegen_, inline_stats_); IntrinsicsRecognizer intrinsics(callee_graph, inline_stats_); HOptimization* optimizations[] = { diff --git a/compiler/optimizing/instruction_builder.cc b/compiler/optimizing/instruction_builder.cc index c60f6e5393..fe11433ae0 100644 --- a/compiler/optimizing/instruction_builder.cc +++ b/compiler/optimizing/instruction_builder.cc @@ -1676,10 +1676,10 @@ HLoadClass* HInstructionBuilder::BuildLoadClass(dex::TypeIndex type_index, dex_pc, needs_access_check); - HLoadClass::LoadKind load_kind = HSharpening::SharpenClass(load_class, - code_generator_, - compiler_driver_, - *dex_compilation_unit_); + HLoadClass::LoadKind load_kind = HSharpening::ComputeLoadClassKind(load_class, + code_generator_, + compiler_driver_, + *dex_compilation_unit_); if (load_kind == HLoadClass::LoadKind::kInvalid) { // We actually cannot reference this class, we're forced to bail. diff --git a/compiler/optimizing/instruction_simplifier.cc b/compiler/optimizing/instruction_simplifier.cc index 35f59cb4a4..17421fc364 100644 --- a/compiler/optimizing/instruction_simplifier.cc +++ b/compiler/optimizing/instruction_simplifier.cc @@ -19,14 +19,18 @@ #include "escape.h" #include "intrinsics.h" #include "mirror/class-inl.h" +#include "sharpening.h" #include "scoped_thread_state_change-inl.h" namespace art { class InstructionSimplifierVisitor : public HGraphDelegateVisitor { public: - InstructionSimplifierVisitor(HGraph* graph, OptimizingCompilerStats* stats) + InstructionSimplifierVisitor(HGraph* graph, + CodeGenerator* codegen, + OptimizingCompilerStats* stats) : HGraphDelegateVisitor(graph), + codegen_(codegen), stats_(stats) {} void Run(); @@ -112,6 +116,7 @@ class InstructionSimplifierVisitor : public HGraphDelegateVisitor { void SimplifyAllocationIntrinsic(HInvoke* invoke); void SimplifyMemBarrier(HInvoke* invoke, MemBarrierKind barrier_kind); + CodeGenerator* codegen_; OptimizingCompilerStats* stats_; bool simplification_occurred_ = false; int simplifications_at_current_position_ = 0; @@ -123,7 +128,7 @@ class InstructionSimplifierVisitor : public HGraphDelegateVisitor { }; void InstructionSimplifier::Run() { - InstructionSimplifierVisitor visitor(graph_, stats_); + InstructionSimplifierVisitor visitor(graph_, codegen_, stats_); visitor.Run(); } @@ -1805,6 +1810,8 @@ void InstructionSimplifierVisitor::SimplifySystemArrayCopy(HInvoke* instruction) { ScopedObjectAccess soa(Thread::Current()); + Primitive::Type source_component_type = Primitive::kPrimVoid; + Primitive::Type destination_component_type = Primitive::kPrimVoid; ReferenceTypeInfo destination_rti = destination->GetReferenceTypeInfo(); if (destination_rti.IsValid()) { if (destination_rti.IsObjectArray()) { @@ -1814,6 +1821,8 @@ void InstructionSimplifierVisitor::SimplifySystemArrayCopy(HInvoke* instruction) optimizations.SetDestinationIsTypedObjectArray(); } if (destination_rti.IsPrimitiveArrayClass()) { + destination_component_type = + destination_rti.GetTypeHandle()->GetComponentType()->GetPrimitiveType(); optimizations.SetDestinationIsPrimitiveArray(); } else if (destination_rti.IsNonPrimitiveArrayClass()) { optimizations.SetDestinationIsNonPrimitiveArray(); @@ -1826,10 +1835,55 @@ void InstructionSimplifierVisitor::SimplifySystemArrayCopy(HInvoke* instruction) } if (source_rti.IsPrimitiveArrayClass()) { optimizations.SetSourceIsPrimitiveArray(); + source_component_type = source_rti.GetTypeHandle()->GetComponentType()->GetPrimitiveType(); } else if (source_rti.IsNonPrimitiveArrayClass()) { optimizations.SetSourceIsNonPrimitiveArray(); } } + // For primitive arrays, use their optimized ArtMethod implementations. + if ((source_component_type != Primitive::kPrimVoid) && + (source_component_type == destination_component_type)) { + ClassLinker* class_linker = Runtime::Current()->GetClassLinker(); + PointerSize image_size = class_linker->GetImagePointerSize(); + HInvokeStaticOrDirect* invoke = instruction->AsInvokeStaticOrDirect(); + mirror::Class* system = invoke->GetResolvedMethod()->GetDeclaringClass(); + ArtMethod* method = nullptr; + switch (source_component_type) { + case Primitive::kPrimBoolean: + method = system->FindDeclaredDirectMethod("arraycopy", "([ZI[ZII)V", image_size); + break; + case Primitive::kPrimByte: + method = system->FindDeclaredDirectMethod("arraycopy", "([BI[BII)V", image_size); + break; + case Primitive::kPrimChar: + method = system->FindDeclaredDirectMethod("arraycopy", "([CI[CII)V", image_size); + break; + case Primitive::kPrimShort: + method = system->FindDeclaredDirectMethod("arraycopy", "([SI[SII)V", image_size); + break; + case Primitive::kPrimInt: + method = system->FindDeclaredDirectMethod("arraycopy", "([II[III)V", image_size); + break; + case Primitive::kPrimFloat: + method = system->FindDeclaredDirectMethod("arraycopy", "([FI[FII)V", image_size); + break; + case Primitive::kPrimLong: + method = system->FindDeclaredDirectMethod("arraycopy", "([JI[JII)V", image_size); + break; + case Primitive::kPrimDouble: + method = system->FindDeclaredDirectMethod("arraycopy", "([DI[DII)V", image_size); + break; + default: + LOG(FATAL) << "Unreachable"; + } + DCHECK(method != nullptr); + invoke->SetResolvedMethod(method); + // Sharpen the new invoke. Note that we do not update the dex method index of + // the invoke, as we would need to look it up in the current dex file, and it + // is unlikely that it exists. The most usual situation for such typed + // arraycopy methods is a direct pointer to the boot image. + HSharpening::SharpenInvokeStaticOrDirect(invoke, codegen_); + } } } diff --git a/compiler/optimizing/instruction_simplifier.h b/compiler/optimizing/instruction_simplifier.h index 7fe1067aa9..f7329a4a1f 100644 --- a/compiler/optimizing/instruction_simplifier.h +++ b/compiler/optimizing/instruction_simplifier.h @@ -23,6 +23,8 @@ namespace art { +class CodeGenerator; + /** * Implements optimizations specific to each instruction. * @@ -36,15 +38,19 @@ namespace art { class InstructionSimplifier : public HOptimization { public: explicit InstructionSimplifier(HGraph* graph, + CodeGenerator* codegen, OptimizingCompilerStats* stats = nullptr, const char* name = kInstructionSimplifierPassName) - : HOptimization(graph, name, stats) {} + : HOptimization(graph, name, stats), + codegen_(codegen) {} static constexpr const char* kInstructionSimplifierPassName = "instruction_simplifier"; void Run() OVERRIDE; private: + CodeGenerator* codegen_; + DISALLOW_COPY_AND_ASSIGN(InstructionSimplifier); }; diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h index c39aed2c6a..69ef9e178c 100644 --- a/compiler/optimizing/nodes.h +++ b/compiler/optimizing/nodes.h @@ -3915,6 +3915,7 @@ class HInvoke : public HVariableInputSizeInstruction { bool IsIntrinsic() const { return intrinsic_ != Intrinsics::kNone; } ArtMethod* GetResolvedMethod() const { return resolved_method_; } + void SetResolvedMethod(ArtMethod* method) { resolved_method_ = method; } DECLARE_ABSTRACT_INSTRUCTION(Invoke); @@ -3957,7 +3958,7 @@ class HInvoke : public HVariableInputSizeInstruction { } uint32_t number_of_arguments_; - ArtMethod* const resolved_method_; + ArtMethod* resolved_method_; const uint32_t dex_method_index_; Intrinsics intrinsic_; diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc index 3842ef98da..4fe846f37f 100644 --- a/compiler/optimizing/optimizing_compiler.cc +++ b/compiler/optimizing/optimizing_compiler.cc @@ -507,7 +507,7 @@ static HOptimization* BuildOptimization( } else if (opt_name == HInductionVarAnalysis::kInductionPassName) { return new (arena) HInductionVarAnalysis(graph); } else if (opt_name == InstructionSimplifier::kInstructionSimplifierPassName) { - return new (arena) InstructionSimplifier(graph, stats, pass_name.c_str()); + return new (arena) InstructionSimplifier(graph, codegen, stats, pass_name.c_str()); } else if (opt_name == IntrinsicsRecognizer::kIntrinsicsRecognizerPassName) { return new (arena) IntrinsicsRecognizer(graph, stats); } else if (opt_name == LICM::kLoopInvariantCodeMotionPassName) { @@ -768,7 +768,7 @@ void OptimizingCompiler::RunOptimizations(HGraph* graph, HDeadCodeElimination* dce3 = new (arena) HDeadCodeElimination( graph, stats, "dead_code_elimination$final"); HConstantFolding* fold1 = new (arena) HConstantFolding(graph, "constant_folding"); - InstructionSimplifier* simplify1 = new (arena) InstructionSimplifier(graph, stats); + InstructionSimplifier* simplify1 = new (arena) InstructionSimplifier(graph, codegen, stats); HSelectGenerator* select_generator = new (arena) HSelectGenerator(graph, stats); HConstantFolding* fold2 = new (arena) HConstantFolding( graph, "constant_folding$after_inlining"); @@ -783,11 +783,11 @@ void OptimizingCompiler::RunOptimizations(HGraph* graph, HSharpening* sharpening = new (arena) HSharpening( graph, codegen, dex_compilation_unit, driver, handles); InstructionSimplifier* simplify2 = new (arena) InstructionSimplifier( - graph, stats, "instruction_simplifier$after_inlining"); + graph, codegen, stats, "instruction_simplifier$after_inlining"); InstructionSimplifier* simplify3 = new (arena) InstructionSimplifier( - graph, stats, "instruction_simplifier$after_bce"); + graph, codegen, stats, "instruction_simplifier$after_bce"); InstructionSimplifier* simplify4 = new (arena) InstructionSimplifier( - graph, stats, "instruction_simplifier$before_codegen"); + graph, codegen, stats, "instruction_simplifier$before_codegen"); IntrinsicsRecognizer* intrinsics = new (arena) IntrinsicsRecognizer(graph, stats); CHAGuardOptimization* cha_guard = new (arena) CHAGuardOptimization(graph); CodeSinking* code_sinking = new (arena) CodeSinking(graph, stats); diff --git a/compiler/optimizing/sharpening.cc b/compiler/optimizing/sharpening.cc index be400925d5..8f1827be69 100644 --- a/compiler/optimizing/sharpening.cc +++ b/compiler/optimizing/sharpening.cc @@ -41,7 +41,7 @@ void HSharpening::Run() { for (HInstructionIterator it(block->GetInstructions()); !it.Done(); it.Advance()) { HInstruction* instruction = it.Current(); if (instruction->IsInvokeStaticOrDirect()) { - ProcessInvokeStaticOrDirect(instruction->AsInvokeStaticOrDirect()); + SharpenInvokeStaticOrDirect(instruction->AsInvokeStaticOrDirect(), codegen_); } else if (instruction->IsLoadString()) { ProcessLoadString(instruction->AsLoadString()); } @@ -70,7 +70,9 @@ static bool AOTCanEmbedMethod(ArtMethod* method, const CompilerOptions& options) return IsInBootImage(method) && !options.GetCompilePic() && !options.GetIncludePatchInformation(); } -void HSharpening::ProcessInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) { + +void HSharpening::SharpenInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke, + CodeGenerator* codegen) { if (invoke->IsStringInit()) { // Not using the dex cache arrays. But we could still try to use a better dispatch... // TODO: Use direct_method and direct_code for the appropriate StringFactory method. @@ -97,12 +99,12 @@ void HSharpening::ProcessInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) { // We don't optimize for debuggable as it would prevent us from obsoleting the method in some // situations. - if (callee == codegen_->GetGraph()->GetArtMethod() && !codegen_->GetGraph()->IsDebuggable()) { + if (callee == codegen->GetGraph()->GetArtMethod() && !codegen->GetGraph()->IsDebuggable()) { // Recursive call. method_load_kind = HInvokeStaticOrDirect::MethodLoadKind::kRecursive; code_ptr_location = HInvokeStaticOrDirect::CodePtrLocation::kCallSelf; } else if (Runtime::Current()->UseJitCompilation() || - AOTCanEmbedMethod(callee, codegen_->GetCompilerOptions())) { + AOTCanEmbedMethod(callee, codegen->GetCompilerOptions())) { // JIT or on-device AOT compilation referencing a boot image method. // Use the method address directly. method_load_kind = HInvokeStaticOrDirect::MethodLoadKind::kDirectAddress; @@ -111,13 +113,17 @@ void HSharpening::ProcessInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) { } else { // Use PC-relative access to the dex cache arrays. method_load_kind = HInvokeStaticOrDirect::MethodLoadKind::kDexCachePcRelative; - DexCacheArraysLayout layout(GetInstructionSetPointerSize(codegen_->GetInstructionSet()), - &graph_->GetDexFile()); + // Note: we use the invoke's graph instead of the codegen graph, which are + // different when inlining (the codegen graph is the most outer graph). The + // invoke's dex method index is relative to the dex file where the invoke's graph + // was built from. + DexCacheArraysLayout layout(GetInstructionSetPointerSize(codegen->GetInstructionSet()), + &invoke->GetBlock()->GetGraph()->GetDexFile()); method_load_data = layout.MethodOffset(invoke->GetDexMethodIndex()); code_ptr_location = HInvokeStaticOrDirect::CodePtrLocation::kCallArtMethod; } - if (graph_->IsDebuggable()) { + if (codegen->GetGraph()->IsDebuggable()) { // For debuggable apps always use the code pointer from ArtMethod // so that we don't circumvent instrumentation stubs if installed. code_ptr_location = HInvokeStaticOrDirect::CodePtrLocation::kCallArtMethod; @@ -127,14 +133,14 @@ void HSharpening::ProcessInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) { method_load_kind, code_ptr_location, method_load_data }; HInvokeStaticOrDirect::DispatchInfo dispatch_info = - codegen_->GetSupportedInvokeStaticOrDirectDispatch(desired_dispatch_info, invoke); + codegen->GetSupportedInvokeStaticOrDirectDispatch(desired_dispatch_info, invoke); invoke->SetDispatchInfo(dispatch_info); } -HLoadClass::LoadKind HSharpening::SharpenClass(HLoadClass* load_class, - CodeGenerator* codegen, - CompilerDriver* compiler_driver, - const DexCompilationUnit& dex_compilation_unit) { +HLoadClass::LoadKind HSharpening::ComputeLoadClassKind(HLoadClass* load_class, + CodeGenerator* codegen, + CompilerDriver* compiler_driver, + const DexCompilationUnit& dex_compilation_unit) { Handle<mirror::Class> klass = load_class->GetClass(); DCHECK(load_class->GetLoadKind() == HLoadClass::LoadKind::kDexCacheViaMethod || load_class->GetLoadKind() == HLoadClass::LoadKind::kReferrersClass) diff --git a/compiler/optimizing/sharpening.h b/compiler/optimizing/sharpening.h index 4240b2f339..10707c796f 100644 --- a/compiler/optimizing/sharpening.h +++ b/compiler/optimizing/sharpening.h @@ -48,14 +48,16 @@ class HSharpening : public HOptimization { static constexpr const char* kSharpeningPassName = "sharpening"; // Used by the builder and the inliner. - static HLoadClass::LoadKind SharpenClass(HLoadClass* load_class, - CodeGenerator* codegen, - CompilerDriver* compiler_driver, - const DexCompilationUnit& dex_compilation_unit) + static HLoadClass::LoadKind ComputeLoadClassKind(HLoadClass* load_class, + CodeGenerator* codegen, + CompilerDriver* compiler_driver, + const DexCompilationUnit& dex_compilation_unit) REQUIRES_SHARED(Locks::mutator_lock_); + // Used by Sharpening and InstructionSimplifier. + static void SharpenInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke, CodeGenerator* codegen); + private: - void ProcessInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke); void ProcessLoadString(HLoadString* load_string); CodeGenerator* codegen_; |