diff options
| author | 2015-03-25 17:19:07 +0000 | |
|---|---|---|
| committer | 2015-03-25 17:19:08 +0000 | |
| commit | 651b4fb337eba9971bb830d9e037c633e29a6eba (patch) | |
| tree | 06cb3f4534205d2a8947e211834806d6343ead15 | |
| parent | 39b4bf99a1167cd9d5e5454059dd360e67f1eac1 (diff) | |
| parent | 9437b78780f9e6ffa5797ebe82de8e8d7f3a5ed6 (diff) | |
Merge "Revert "Revert "Inline across dex files."""
| -rw-r--r-- | compiler/driver/compiler_driver-inl.h | 25 | ||||
| -rw-r--r-- | compiler/driver/compiler_driver.h | 6 | ||||
| -rw-r--r-- | compiler/optimizing/builder.cc | 70 | ||||
| -rw-r--r-- | compiler/optimizing/builder.h | 9 | ||||
| -rw-r--r-- | compiler/optimizing/inliner.cc | 75 | ||||
| -rw-r--r-- | compiler/optimizing/inliner.h | 6 | ||||
| -rw-r--r-- | compiler/optimizing/nodes.h | 6 | ||||
| -rw-r--r-- | compiler/optimizing/optimizing_compiler.cc | 2 | ||||
| -rw-r--r-- | test/462-checker-inlining-across-dex-files/expected.txt | 0 | ||||
| -rw-r--r-- | test/462-checker-inlining-across-dex-files/info.txt | 1 | ||||
| -rw-r--r-- | test/462-checker-inlining-across-dex-files/src-multidex/OtherDex.java | 64 | ||||
| -rw-r--r-- | test/462-checker-inlining-across-dex-files/src/Main.java | 202 | ||||
| -rwxr-xr-x | test/etc/default-build | 13 |
13 files changed, 415 insertions, 64 deletions
diff --git a/compiler/driver/compiler_driver-inl.h b/compiler/driver/compiler_driver-inl.h index 4a35e9fbe7..8babc28db9 100644 --- a/compiler/driver/compiler_driver-inl.h +++ b/compiler/driver/compiler_driver-inl.h @@ -39,6 +39,22 @@ inline mirror::ClassLoader* CompilerDriver::GetClassLoader(ScopedObjectAccess& s return soa.Decode<mirror::ClassLoader*>(mUnit->GetClassLoader()); } +inline mirror::Class* CompilerDriver::ResolveClass( + const ScopedObjectAccess& soa, Handle<mirror::DexCache> dex_cache, + Handle<mirror::ClassLoader> class_loader, uint16_t cls_index, + const DexCompilationUnit* mUnit) { + DCHECK_EQ(dex_cache->GetDexFile(), mUnit->GetDexFile()); + DCHECK_EQ(class_loader.Get(), soa.Decode<mirror::ClassLoader*>(mUnit->GetClassLoader())); + mirror::Class* cls = mUnit->GetClassLinker()->ResolveType( + *mUnit->GetDexFile(), cls_index, dex_cache, class_loader); + DCHECK_EQ(cls == nullptr, soa.Self()->IsExceptionPending()); + if (UNLIKELY(cls == nullptr)) { + // Clean up any exception left by type resolution. + soa.Self()->ClearException(); + } + return cls; +} + inline mirror::Class* CompilerDriver::ResolveCompilingMethodsClass( const ScopedObjectAccess& soa, Handle<mirror::DexCache> dex_cache, Handle<mirror::ClassLoader> class_loader, const DexCompilationUnit* mUnit) { @@ -46,14 +62,7 @@ inline mirror::Class* CompilerDriver::ResolveCompilingMethodsClass( DCHECK_EQ(class_loader.Get(), soa.Decode<mirror::ClassLoader*>(mUnit->GetClassLoader())); const DexFile::MethodId& referrer_method_id = mUnit->GetDexFile()->GetMethodId(mUnit->GetDexMethodIndex()); - mirror::Class* referrer_class = mUnit->GetClassLinker()->ResolveType( - *mUnit->GetDexFile(), referrer_method_id.class_idx_, dex_cache, class_loader); - DCHECK_EQ(referrer_class == nullptr, soa.Self()->IsExceptionPending()); - if (UNLIKELY(referrer_class == nullptr)) { - // Clean up any exception left by type resolution. - soa.Self()->ClearException(); - } - return referrer_class; + return ResolveClass(soa, dex_cache, class_loader, referrer_method_id.class_idx_, mUnit); } inline mirror::ArtField* CompilerDriver::ResolveFieldWithDexFile( diff --git a/compiler/driver/compiler_driver.h b/compiler/driver/compiler_driver.h index 9463c2c9bd..b825293c33 100644 --- a/compiler/driver/compiler_driver.h +++ b/compiler/driver/compiler_driver.h @@ -228,6 +228,12 @@ class CompilerDriver { Handle<mirror::ClassLoader> class_loader, const DexCompilationUnit* mUnit) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + mirror::Class* ResolveClass( + const ScopedObjectAccess& soa, Handle<mirror::DexCache> dex_cache, + Handle<mirror::ClassLoader> class_loader, uint16_t type_index, + const DexCompilationUnit* mUnit) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + // Resolve a field. Returns nullptr on failure, including incompatible class change. // NOTE: Unlike ClassLinker's ResolveField(), this method enforces is_static. mirror::ArtField* ResolveField( diff --git a/compiler/optimizing/builder.cc b/compiler/optimizing/builder.cc index a21c311d90..8786ed419c 100644 --- a/compiler/optimizing/builder.cc +++ b/compiler/optimizing/builder.cc @@ -616,8 +616,8 @@ bool HGraphBuilder::BuildInvoke(const Instruction& instruction, DCHECK((optimized_invoke_type == invoke_type) || (optimized_invoke_type != kDirect) || compiler_driver_->GetCompilerOptions().GetCompilePic()); bool is_recursive = - (target_method.dex_method_index == outer_compilation_unit_->GetDexMethodIndex()); - DCHECK(!is_recursive || (target_method.dex_file == outer_compilation_unit_->GetDexFile())); + (target_method.dex_method_index == dex_compilation_unit_->GetDexMethodIndex()); + DCHECK(!is_recursive || (target_method.dex_file == dex_compilation_unit_->GetDexFile())); invoke = new (arena_) HInvokeStaticOrDirect( arena_, number_of_arguments, return_type, dex_pc, target_method.dex_method_index, is_recursive, optimized_invoke_type); @@ -704,6 +704,34 @@ bool HGraphBuilder::BuildInstanceFieldAccess(const Instruction& instruction, return true; } +mirror::Class* HGraphBuilder::GetOutermostCompilingClass() const { + ScopedObjectAccess soa(Thread::Current()); + StackHandleScope<2> hs(soa.Self()); + const DexFile& outer_dex_file = *outer_compilation_unit_->GetDexFile(); + Handle<mirror::ClassLoader> class_loader(hs.NewHandle( + soa.Decode<mirror::ClassLoader*>(dex_compilation_unit_->GetClassLoader()))); + Handle<mirror::DexCache> outer_dex_cache(hs.NewHandle( + outer_compilation_unit_->GetClassLinker()->FindDexCache(outer_dex_file))); + + return compiler_driver_->ResolveCompilingMethodsClass( + soa, outer_dex_cache, class_loader, outer_compilation_unit_); +} + +bool HGraphBuilder::IsOutermostCompilingClass(uint16_t type_index) const { + 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::Class> cls(hs.NewHandle(compiler_driver_->ResolveClass( + soa, dex_cache, class_loader, type_index, dex_compilation_unit_))); + Handle<mirror::Class> compiling_class(hs.NewHandle(GetOutermostCompilingClass())); + + return compiling_class.Get() == cls.Get(); +} + + bool HGraphBuilder::BuildStaticFieldAccess(const Instruction& instruction, uint32_t dex_pc, bool is_put) { @@ -711,7 +739,7 @@ bool HGraphBuilder::BuildStaticFieldAccess(const Instruction& instruction, uint16_t field_index = instruction.VRegB_21c(); ScopedObjectAccess soa(Thread::Current()); - StackHandleScope<4> hs(soa.Self()); + StackHandleScope<5> 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( @@ -724,23 +752,36 @@ bool HGraphBuilder::BuildStaticFieldAccess(const Instruction& instruction, return false; } - Handle<mirror::Class> referrer_class(hs.NewHandle(compiler_driver_->ResolveCompilingMethodsClass( - soa, dex_cache, class_loader, outer_compilation_unit_))); + const DexFile& outer_dex_file = *outer_compilation_unit_->GetDexFile(); + Handle<mirror::DexCache> outer_dex_cache(hs.NewHandle( + outer_compilation_unit_->GetClassLinker()->FindDexCache(outer_dex_file))); + Handle<mirror::Class> referrer_class(hs.NewHandle(GetOutermostCompilingClass())); // 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) { + bool is_referrer_class = (referrer_class.Get() == resolved_field->GetDeclaringClass()); + if (is_referrer_class) { + storage_index = referrer_class->GetDexTypeIndex(); + } else if (outer_dex_cache.Get() != dex_cache.Get()) { + // The compiler driver cannot currently understand multple dex caches involved. Just bailout. return false; + } else { + std::pair<bool, bool> pair = compiler_driver_->IsFastStaticField( + outer_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); @@ -966,7 +1007,7 @@ bool HGraphBuilder::BuildTypeCheck(const Instruction& instruction, // `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. + // work for inlining, so we use `IsOutermostCompilingClass` instead. bool dont_use_is_referrers_class; bool can_access = compiler_driver_->CanAccessTypeWithoutChecks( dex_compilation_unit_->GetDexMethodIndex(), *dex_file_, type_index, @@ -976,7 +1017,8 @@ bool HGraphBuilder::BuildTypeCheck(const Instruction& instruction, return false; } HInstruction* object = LoadLocal(reference, Primitive::kPrimNot); - HLoadClass* cls = new (arena_) HLoadClass(type_index, IsCompilingClass(type_index), dex_pc); + HLoadClass* cls = new (arena_) HLoadClass( + type_index, IsOutermostCompilingClass(type_index), dex_pc); current_block_->AddInstruction(cls); // The class needs a temporary before being used by the type check. Temporaries temps(graph_); @@ -1971,7 +2013,7 @@ bool HGraphBuilder::AnalyzeDexInstruction(const Instruction& instruction, uint32 // `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. + // work for inlining, so we use `IsOutermostCompilingClass` instead. bool can_access = compiler_driver_->CanAccessTypeWithoutChecks( dex_compilation_unit_->GetDexMethodIndex(), *dex_file_, type_index, &type_known_final, &type_known_abstract, &dont_use_is_referrers_class); @@ -1980,7 +2022,7 @@ bool HGraphBuilder::AnalyzeDexInstruction(const Instruction& instruction, uint32 return false; } current_block_->AddInstruction( - new (arena_) HLoadClass(type_index, IsCompilingClass(type_index), dex_pc)); + new (arena_) HLoadClass(type_index, IsOutermostCompilingClass(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 c70170bb46..b206660fdc 100644 --- a/compiler/optimizing/builder.h +++ b/compiler/optimizing/builder.h @@ -225,13 +225,10 @@ class HGraphBuilder : public ValueObject { void MaybeRecordStat(MethodCompilationStat compilation_stat); + mirror::Class* GetOutermostCompilingClass() const; + // 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; - } + bool IsOutermostCompilingClass(uint16_t type_index) const; ArenaAllocator* const arena_; diff --git a/compiler/optimizing/inliner.cc b/compiler/optimizing/inliner.cc index 968fe3e73c..256e85b6ce 100644 --- a/compiler/optimizing/inliner.cc +++ b/compiler/optimizing/inliner.cc @@ -68,87 +68,87 @@ 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); + const DexFile& caller_dex_file = *caller_compilation_unit_.GetDexFile(); + VLOG(compiler) << "Try inlining " << PrettyMethod(method_index, caller_dex_file); StackHandleScope<3> hs(soa.Self()); Handle<mirror::DexCache> dex_cache( - hs.NewHandle(outer_compilation_unit_.GetClassLinker()->FindDexCache(outer_dex_file))); + hs.NewHandle(caller_compilation_unit_.GetClassLinker()->FindDexCache(caller_dex_file))); Handle<mirror::ClassLoader> class_loader(hs.NewHandle( - soa.Decode<mirror::ClassLoader*>(outer_compilation_unit_.GetClassLoader()))); + soa.Decode<mirror::ClassLoader*>(caller_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))); + soa, dex_cache, class_loader, &caller_compilation_unit_, method_index, invoke_type))); if (resolved_method.Get() == nullptr) { - VLOG(compiler) << "Method cannot be resolved " << PrettyMethod(method_index, outer_dex_file); + VLOG(compiler) << "Method cannot be resolved " << PrettyMethod(method_index, caller_dex_file); return false; } + bool can_use_dex_cache = true; + const DexFile& outer_dex_file = *outer_compilation_unit_.GetDexFile(); 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; + can_use_dex_cache = false; } const DexFile::CodeItem* code_item = resolved_method->GetCodeItem(); if (code_item == nullptr) { - VLOG(compiler) << "Method " << PrettyMethod(method_index, outer_dex_file) + VLOG(compiler) << "Method " << PrettyMethod(method_index, caller_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) + VLOG(compiler) << "Method " << PrettyMethod(method_index, caller_dex_file) << " is too big to inline"; return false; } if (code_item->tries_size_ != 0) { - VLOG(compiler) << "Method " << PrettyMethod(method_index, outer_dex_file) + VLOG(compiler) << "Method " << PrettyMethod(method_index, caller_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) + VLOG(compiler) << "Method " << PrettyMethod(method_index, caller_dex_file) << " is not inlined because its class could not be verified"; return false; } if (resolved_method->ShouldNotInline()) { - VLOG(compiler) << "Method " << PrettyMethod(method_index, outer_dex_file) + VLOG(compiler) << "Method " << PrettyMethod(method_index, caller_dex_file) << " was already flagged as non inlineable"; return false; } - if (!TryBuildAndInline(resolved_method, invoke_instruction, method_index)) { + if (!TryBuildAndInline(resolved_method, invoke_instruction, method_index, can_use_dex_cache)) { resolved_method->SetShouldNotInline(); return false; } - VLOG(compiler) << "Successfully inlined " << PrettyMethod(method_index, outer_dex_file); + VLOG(compiler) << "Successfully inlined " << PrettyMethod(method_index, caller_dex_file); MaybeRecordStat(kInlinedInvoke); return true; } bool HInliner::TryBuildAndInline(Handle<mirror::ArtMethod> resolved_method, HInvoke* invoke_instruction, - uint32_t method_index) const { + uint32_t method_index, + bool can_use_dex_cache) const { ScopedObjectAccess soa(Thread::Current()); const DexFile::CodeItem* code_item = resolved_method->GetCodeItem(); - const DexFile& outer_dex_file = *outer_compilation_unit_.GetDexFile(); + const DexFile& caller_dex_file = *caller_compilation_unit_.GetDexFile(); DexCompilationUnit dex_compilation_unit( nullptr, - outer_compilation_unit_.GetClassLoader(), - outer_compilation_unit_.GetClassLinker(), - outer_dex_file, + caller_compilation_unit_.GetClassLoader(), + caller_compilation_unit_.GetClassLinker(), + *resolved_method->GetDexFile(), code_item, resolved_method->GetDeclaringClass()->GetDexClassDefIndex(), - method_index, + resolved_method->GetDexMethodIndex(), resolved_method->GetAccessFlags(), nullptr); @@ -159,25 +159,25 @@ bool HInliner::TryBuildAndInline(Handle<mirror::ArtMethod> resolved_method, HGraphBuilder builder(callee_graph, &dex_compilation_unit, &outer_compilation_unit_, - &outer_dex_file, + resolved_method->GetDexFile(), compiler_driver_, &inline_stats); if (!builder.BuildGraph(*code_item)) { - VLOG(compiler) << "Method " << PrettyMethod(method_index, outer_dex_file) + VLOG(compiler) << "Method " << PrettyMethod(method_index, caller_dex_file) << " could not be built, so cannot be inlined"; return false; } if (!RegisterAllocator::CanAllocateRegistersFor(*callee_graph, compiler_driver_->GetInstructionSet())) { - VLOG(compiler) << "Method " << PrettyMethod(method_index, outer_dex_file) + VLOG(compiler) << "Method " << PrettyMethod(method_index, caller_dex_file) << " cannot be inlined because of the register allocator"; return false; } if (!callee_graph->TryBuildingSsa()) { - VLOG(compiler) << "Method " << PrettyMethod(method_index, outer_dex_file) + VLOG(compiler) << "Method " << PrettyMethod(method_index, caller_dex_file) << " could not be transformed to SSA"; return false; } @@ -199,8 +199,12 @@ bool HInliner::TryBuildAndInline(Handle<mirror::ArtMethod> resolved_method, } if (depth_ + 1 < kDepthLimit) { - HInliner inliner( - callee_graph, outer_compilation_unit_, compiler_driver_, stats_, depth_ + 1); + HInliner inliner(callee_graph, + outer_compilation_unit_, + dex_compilation_unit, + compiler_driver_, + stats_, + depth_ + 1); inliner.Run(); } @@ -209,7 +213,7 @@ bool HInliner::TryBuildAndInline(Handle<mirror::ArtMethod> resolved_method, for (; !it.Done(); it.Advance()) { HBasicBlock* block = it.Current(); if (block->IsLoopHeader()) { - VLOG(compiler) << "Method " << PrettyMethod(method_index, outer_dex_file) + VLOG(compiler) << "Method " << PrettyMethod(method_index, caller_dex_file) << " could not be inlined because it contains a loop"; return false; } @@ -223,18 +227,25 @@ bool HInliner::TryBuildAndInline(Handle<mirror::ArtMethod> resolved_method, } if (current->CanThrow()) { - VLOG(compiler) << "Method " << PrettyMethod(method_index, outer_dex_file) + VLOG(compiler) << "Method " << PrettyMethod(method_index, caller_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) + VLOG(compiler) << "Method " << PrettyMethod(method_index, caller_dex_file) << " could not be inlined because " << current->DebugName() << " needs an environment"; return false; } + + if (!can_use_dex_cache && current->NeedsDexCache()) { + VLOG(compiler) << "Method " << PrettyMethod(method_index, caller_dex_file) + << " could not be inlined because " << current->DebugName() + << " it is in a different dex file and requires access to the dex cache"; + return false; + } } } diff --git a/compiler/optimizing/inliner.h b/compiler/optimizing/inliner.h index 1251977138..1dbc7d392b 100644 --- a/compiler/optimizing/inliner.h +++ b/compiler/optimizing/inliner.h @@ -32,11 +32,13 @@ class HInliner : public HOptimization { public: HInliner(HGraph* outer_graph, const DexCompilationUnit& outer_compilation_unit, + const DexCompilationUnit& caller_compilation_unit, CompilerDriver* compiler_driver, OptimizingCompilerStats* stats, size_t depth = 0) : HOptimization(outer_graph, true, kInlinerPassName, stats), outer_compilation_unit_(outer_compilation_unit), + caller_compilation_unit_(caller_compilation_unit), compiler_driver_(compiler_driver), depth_(depth) {} @@ -48,9 +50,11 @@ class HInliner : public HOptimization { bool TryInline(HInvoke* invoke_instruction, uint32_t method_index, InvokeType invoke_type) const; bool TryBuildAndInline(Handle<mirror::ArtMethod> resolved_method, HInvoke* invoke_instruction, - uint32_t method_index) const; + uint32_t method_index, + bool can_use_dex_cache) const; const DexCompilationUnit& outer_compilation_unit_; + const DexCompilationUnit& caller_compilation_unit_; CompilerDriver* const compiler_driver_; const size_t depth_; diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h index 664cf18ad7..08b16d99b6 100644 --- a/compiler/optimizing/nodes.h +++ b/compiler/optimizing/nodes.h @@ -1248,6 +1248,8 @@ class HInstruction : public ArenaObject<kArenaAllocMisc> { return NeedsEnvironment() || IsLoadClass() || IsLoadString(); } + virtual bool NeedsDexCache() const { return false; } + protected: virtual const HUserRecord<HInstruction*> InputRecordAt(size_t i) const = 0; virtual void SetRawInputRecordAt(size_t index, const HUserRecord<HInstruction*>& input) = 0; @@ -2142,6 +2144,7 @@ class HInvokeStaticOrDirect : public HInvoke { InvokeType GetInvokeType() const { return invoke_type_; } bool IsRecursive() const { return is_recursive_; } + bool NeedsDexCache() const OVERRIDE { return !IsRecursive(); } DECLARE_INSTRUCTION(InvokeStaticOrDirect); @@ -3024,6 +3027,8 @@ class HLoadClass : public HExpression<0> { return loaded_class_rti_.IsExact(); } + bool NeedsDexCache() const OVERRIDE { return !is_referrers_class_; } + DECLARE_INSTRUCTION(LoadClass); private: @@ -3059,6 +3064,7 @@ class HLoadString : public HExpression<0> { // TODO: Can we deopt or debug when we resolve a string? bool NeedsEnvironment() const OVERRIDE { return false; } + bool NeedsDexCache() const OVERRIDE { return true; } DECLARE_INSTRUCTION(LoadString); diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc index eaa30df4f1..5ce73baef2 100644 --- a/compiler/optimizing/optimizing_compiler.cc +++ b/compiler/optimizing/optimizing_compiler.cc @@ -316,7 +316,7 @@ static void RunOptimizations(HGraph* graph, InstructionSimplifier simplify1(graph, stats); HBooleanSimplifier boolean_not(graph); - HInliner inliner(graph, dex_compilation_unit, driver, stats); + HInliner inliner(graph, dex_compilation_unit, dex_compilation_unit, driver, stats); HConstantFolding fold2(graph); SideEffectsAnalysis side_effects(graph); diff --git a/test/462-checker-inlining-across-dex-files/expected.txt b/test/462-checker-inlining-across-dex-files/expected.txt new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/test/462-checker-inlining-across-dex-files/expected.txt diff --git a/test/462-checker-inlining-across-dex-files/info.txt b/test/462-checker-inlining-across-dex-files/info.txt new file mode 100644 index 0000000000..57008c39e1 --- /dev/null +++ b/test/462-checker-inlining-across-dex-files/info.txt @@ -0,0 +1 @@ +Check our inlining heuristics across dex files in optimizing. diff --git a/test/462-checker-inlining-across-dex-files/src-multidex/OtherDex.java b/test/462-checker-inlining-across-dex-files/src-multidex/OtherDex.java new file mode 100644 index 0000000000..cee8e0fbe7 --- /dev/null +++ b/test/462-checker-inlining-across-dex-files/src-multidex/OtherDex.java @@ -0,0 +1,64 @@ +/* +* Copyright (C) 2015 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. +*/ + +public class OtherDex { + public static void emptyMethod() { + } + + public static int returnIntMethod() { + return 38; + } + + public static int returnOtherDexStatic() { + return myStatic; + } + + public static int returnMainStatic() { + return Main.myStatic; + } + + public static int recursiveCall() { + return recursiveCall(); + } + + public static String returnString() { + return "OtherDex"; + } + + public static Class returnOtherDexClass() { + return OtherDex.class; + } + + public static Class returnMainClass() { + return Main.class; + } + + private static Class returnOtherDexClass2() { + return OtherDex.class; + } + + public static Class returnOtherDexClassStaticCall() { + // Do not call returnOtherDexClass, as it may have been flagged + // as non-inlineable. + return returnOtherDexClass2(); + } + + public static Class returnOtherDexCallingMain() { + return Main.getOtherClass(); + } + + static int myStatic = 1; +} diff --git a/test/462-checker-inlining-across-dex-files/src/Main.java b/test/462-checker-inlining-across-dex-files/src/Main.java new file mode 100644 index 0000000000..d5563b85b3 --- /dev/null +++ b/test/462-checker-inlining-across-dex-files/src/Main.java @@ -0,0 +1,202 @@ +/* +* Copyright (C) 2015 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. +*/ + +// Add a class that will be the first entry in the dex cache, to +// avoid having the OtherDex and Main classes share the same cache index. +class AAA { +} + +public class Main { + + // CHECK-START: void Main.inlineEmptyMethod() inliner (before) + // CHECK-DAG: [[Invoke:v\d+]] InvokeStaticOrDirect + // CHECK-DAG: ReturnVoid + + // CHECK-START: void Main.inlineEmptyMethod() inliner (after) + // CHECK-NOT: InvokeStaticOrDirect + + public static void inlineEmptyMethod() { + OtherDex.emptyMethod(); + } + + // CHECK-START: int Main.inlineReturnIntMethod() inliner (before) + // CHECK-DAG: [[Invoke:i\d+]] InvokeStaticOrDirect + // CHECK-DAG: Return [ [[Invoke]] ] + + // CHECK-START: int Main.inlineReturnIntMethod() inliner (after) + // CHECK-NOT: InvokeStaticOrDirect + + // CHECK-START: int Main.inlineReturnIntMethod() inliner (after) + // CHECK-DAG: [[Const38:i\d+]] IntConstant 38 + // CHECK-DAG: Return [ [[Const38]] ] + + public static int inlineReturnIntMethod() { + return OtherDex.returnIntMethod(); + } + + // CHECK-START: int Main.dontInlineOtherDexStatic() inliner (before) + // CHECK-DAG: [[Invoke:i\d+]] InvokeStaticOrDirect + // CHECK-DAG: Return [ [[Invoke]] ] + + // CHECK-START: int Main.dontInlineOtherDexStatic() inliner (after) + // CHECK-DAG: [[Invoke:i\d+]] InvokeStaticOrDirect + // CHECK-DAG: Return [ [[Invoke]] ] + + public static int dontInlineOtherDexStatic() { + return OtherDex.returnOtherDexStatic(); + } + + // CHECK-START: int Main.inlineMainStatic() inliner (before) + // CHECK-DAG: [[Invoke:i\d+]] InvokeStaticOrDirect + // CHECK-DAG: Return [ [[Invoke]] ] + + // CHECK-START: int Main.inlineMainStatic() inliner (after) + // CHECK-NOT: InvokeStaticOrDirect + + // CHECK-START: int Main.inlineMainStatic() inliner (after) + // CHECK-DAG: [[Static:i\d+]] StaticFieldGet + // CHECK-DAG: Return [ [[Static]] ] + + public static int inlineMainStatic() { + return OtherDex.returnMainStatic(); + } + + // CHECK-START: int Main.dontInlineRecursiveCall() inliner (before) + // CHECK-DAG: [[Invoke:i\d+]] InvokeStaticOrDirect + // CHECK-DAG: Return [ [[Invoke]] ] + + // CHECK-START: int Main.dontInlineRecursiveCall() inliner (after) + // CHECK-DAG: [[Invoke:i\d+]] InvokeStaticOrDirect + // CHECK-DAG: Return [ [[Invoke]] ] + + public static int dontInlineRecursiveCall() { + return OtherDex.recursiveCall(); + } + + // CHECK-START: java.lang.String Main.dontInlineReturnString() inliner (before) + // CHECK-DAG: [[Invoke:l\d+]] InvokeStaticOrDirect + // CHECK-DAG: Return [ [[Invoke]] ] + + // CHECK-START: java.lang.String Main.dontInlineReturnString() inliner (after) + // CHECK-DAG: [[Invoke:l\d+]] InvokeStaticOrDirect + // CHECK-DAG: Return [ [[Invoke]] ] + + public static String dontInlineReturnString() { + return OtherDex.returnString(); + } + + // CHECK-START: java.lang.Class Main.dontInlineOtherDexClass() inliner (before) + // CHECK-DAG: [[Invoke:l\d+]] InvokeStaticOrDirect + // CHECK-DAG: Return [ [[Invoke]] ] + + // CHECK-START: java.lang.Class Main.dontInlineOtherDexClass() inliner (after) + // CHECK-DAG: [[Invoke:l\d+]] InvokeStaticOrDirect + // CHECK-DAG: Return [ [[Invoke]] ] + + public static Class dontInlineOtherDexClass() { + return OtherDex.returnOtherDexClass(); + } + + // CHECK-START: java.lang.Class Main.inlineMainClass() inliner (before) + // CHECK-DAG: [[Invoke:l\d+]] InvokeStaticOrDirect + // CHECK-DAG: Return [ [[Invoke]] ] + + // CHECK-START: java.lang.Class Main.inlineMainClass() inliner (after) + // CHECK-NOT: InvokeStaticOrDirect + + // CHECK-START: java.lang.Class Main.inlineMainClass() inliner (after) + // CHECK-DAG: [[Class:l\d+]] LoadClass + // CHECK-DAG: Return [ [[Class]] ] + + public static Class inlineMainClass() { + return OtherDex.returnMainClass(); + } + + // CHECK-START: java.lang.Class Main.dontInlineOtherDexClassStaticCall() inliner (before) + // CHECK-DAG: [[Invoke:l\d+]] InvokeStaticOrDirect + // CHECK-DAG: Return [ [[Invoke]] ] + + // CHECK-START: java.lang.Class Main.dontInlineOtherDexClassStaticCall() inliner (after) + // CHECK-DAG: [[Invoke:l\d+]] InvokeStaticOrDirect + // CHECK-DAG: Return [ [[Invoke]] ] + + public static Class dontInlineOtherDexClassStaticCall() { + return OtherDex.returnOtherDexClassStaticCall(); + } + + // CHECK-START: java.lang.Class Main.inlineOtherDexCallingMain() inliner (before) + // CHECK-DAG: [[Invoke:l\d+]] InvokeStaticOrDirect + // CHECK-DAG: Return [ [[Invoke]] ] + + // CHECK-START: java.lang.Class Main.inlineOtherDexCallingMain() inliner (after) + // CHECK-NOT: InvokeStaticOrDirect + + // CHECK-START: java.lang.Class Main.inlineOtherDexCallingMain() inliner (after) + // CHECK-DAG: [[Class:l\d+]] LoadClass + // CHECK-DAG: Return [ [[Class]] ] + + public static Class inlineOtherDexCallingMain() { + return OtherDex.returnOtherDexCallingMain(); + } + + public static Class getOtherClass() { + return Main.class; + } + + public static void main(String[] args) { + inlineEmptyMethod(); + if (inlineReturnIntMethod() != 38) { + throw new Error("Expected 38"); + } + + if (dontInlineOtherDexStatic() != 1) { + throw new Error("Expected 1"); + } + + if (inlineMainStatic() != 42) { + throw new Error("Expected 42"); + } + + if (dontInlineReturnString() != "OtherDex") { + throw new Error("Expected OtherDex"); + } + + if (dontInlineOtherDexClass() != OtherDex.class) { + throw new Error("Expected " + OtherDex.class); + } + + if (dontInlineOtherDexClassStaticCall() != OtherDex.class) { + throw new Error("Expected " + OtherDex.class); + } + + if (inlineMainClass() != Main.class) { + throw new Error("Expected " + Main.class); + } + + if (inlineOtherDexCallingMain() != Main.class) { + throw new Error("Expected " + Main.class); + } + } + + // Reference the AAA class to ensure it is in the dex cache. + public static Class<?> cls = AAA.class; + + // Add a field that will be the first entry in the dex cache, to + // avoid having the OtherDex.myStatic and Main.myStatic fields + // share the same cache index. + public static int aaa = 32; + public static int myStatic = 42; +} diff --git a/test/etc/default-build b/test/etc/default-build index 58c9564bab..fbe97f999b 100755 --- a/test/etc/default-build +++ b/test/etc/default-build @@ -39,7 +39,7 @@ if [ -e classes.dex ]; then fi mkdir classes -${JAVAC} -d classes `find src -name '*.java'` +${JAVAC} -implicit:none -classpath src-multidex -d classes `find src -name '*.java'` if [ -d src2 ]; then ${JAVAC} -d classes `find src2 -name '*.java'` @@ -72,6 +72,15 @@ if [ -d src-ex ]; then fi fi -if [ ${NEED_DEX} = "true" ]; then +# Create a single jar with two dex files for multidex. +if [ -d src-multidex ]; then + mkdir classes2 + ${JAVAC} -implicit:none -classpath src -d classes2 `find src-multidex -name '*.java'` + if [ ${NEED_DEX} = "true" ]; then + ${DX} -JXmx256m --debug --dex --dump-to=classes2.lst --output=classes2.dex \ + --dump-width=1000 ${DX_FLAGS} classes2 + zip $TEST_NAME.jar classes.dex classes2.dex + fi +elif [ ${NEED_DEX} = "true" ]; then zip $TEST_NAME.jar classes.dex fi |