diff options
42 files changed, 749 insertions, 788 deletions
diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc index 8bdff21c70..be82956e76 100644 --- a/compiler/driver/compiler_driver.cc +++ b/compiler/driver/compiler_driver.cc @@ -1283,14 +1283,13 @@ bool CompilerDriver::CanAssumeClassIsLoaded(mirror::Class* klass) { return IsImageClass(descriptor); } -bool CompilerDriver::CanAssumeTypeIsPresentInDexCache(const DexFile& dex_file, uint32_t type_idx) { +bool CompilerDriver::CanAssumeTypeIsPresentInDexCache(Handle<mirror::DexCache> dex_cache, + uint32_t type_idx) { bool result = false; if ((IsBootImage() && - IsImageClass(dex_file.StringDataByIdx(dex_file.GetTypeId(type_idx).descriptor_idx_))) || + IsImageClass(dex_cache->GetDexFile()->StringDataByIdx( + dex_cache->GetDexFile()->GetTypeId(type_idx).descriptor_idx_))) || Runtime::Current()->UseJit()) { - ScopedObjectAccess soa(Thread::Current()); - mirror::DexCache* dex_cache = Runtime::Current()->GetClassLinker()->FindDexCache( - soa.Self(), dex_file, false); mirror::Class* resolved_class = dex_cache->GetResolvedType(type_idx); result = (resolved_class != nullptr); } @@ -1332,32 +1331,16 @@ bool CompilerDriver::CanAssumeStringIsPresentInDexCache(const DexFile& dex_file, return result; } -bool CompilerDriver::CanAccessTypeWithoutChecks(uint32_t referrer_idx, const DexFile& dex_file, - uint32_t type_idx, - bool* type_known_final, bool* type_known_abstract, - bool* equals_referrers_class) { - if (type_known_final != nullptr) { - *type_known_final = false; - } - if (type_known_abstract != nullptr) { - *type_known_abstract = false; - } - if (equals_referrers_class != nullptr) { - *equals_referrers_class = false; - } - ScopedObjectAccess soa(Thread::Current()); - mirror::DexCache* dex_cache = Runtime::Current()->GetClassLinker()->FindDexCache( - soa.Self(), dex_file, false); +bool CompilerDriver::CanAccessTypeWithoutChecks(uint32_t referrer_idx, + Handle<mirror::DexCache> dex_cache, + uint32_t type_idx) { // Get type from dex cache assuming it was populated by the verifier mirror::Class* resolved_class = dex_cache->GetResolvedType(type_idx); if (resolved_class == nullptr) { stats_->TypeNeedsAccessCheck(); return false; // Unknown class needs access checks. } - const DexFile::MethodId& method_id = dex_file.GetMethodId(referrer_idx); - if (equals_referrers_class != nullptr) { - *equals_referrers_class = (method_id.class_idx_ == type_idx); - } + const DexFile::MethodId& method_id = dex_cache->GetDexFile()->GetMethodId(referrer_idx); bool is_accessible = resolved_class->IsPublic(); // Public classes are always accessible. if (!is_accessible) { mirror::Class* referrer_class = dex_cache->GetResolvedType(method_id.class_idx_); @@ -1371,12 +1354,6 @@ bool CompilerDriver::CanAccessTypeWithoutChecks(uint32_t referrer_idx, const Dex } if (is_accessible) { stats_->TypeDoesntNeedAccessCheck(); - if (type_known_final != nullptr) { - *type_known_final = resolved_class->IsFinal() && !resolved_class->IsArrayClass(); - } - if (type_known_abstract != nullptr) { - *type_known_abstract = resolved_class->IsAbstract() && !resolved_class->IsArrayClass(); - } } else { stats_->TypeNeedsAccessCheck(); } @@ -1384,12 +1361,9 @@ bool CompilerDriver::CanAccessTypeWithoutChecks(uint32_t referrer_idx, const Dex } bool CompilerDriver::CanAccessInstantiableTypeWithoutChecks(uint32_t referrer_idx, - const DexFile& dex_file, + Handle<mirror::DexCache> dex_cache, uint32_t type_idx, bool* finalizable) { - ScopedObjectAccess soa(Thread::Current()); - mirror::DexCache* dex_cache = Runtime::Current()->GetClassLinker()->FindDexCache( - soa.Self(), dex_file, false); // Get type from dex cache assuming it was populated by the verifier. mirror::Class* resolved_class = dex_cache->GetResolvedType(type_idx); if (resolved_class == nullptr) { @@ -1399,7 +1373,7 @@ bool CompilerDriver::CanAccessInstantiableTypeWithoutChecks(uint32_t referrer_id return false; // Unknown class needs access checks. } *finalizable = resolved_class->IsFinalizable(); - const DexFile::MethodId& method_id = dex_file.GetMethodId(referrer_idx); + const DexFile::MethodId& method_id = dex_cache->GetDexFile()->GetMethodId(referrer_idx); bool is_accessible = resolved_class->IsPublic(); // Public classes are always accessible. if (!is_accessible) { mirror::Class* referrer_class = dex_cache->GetResolvedType(method_id.class_idx_); @@ -1587,53 +1561,6 @@ bool CompilerDriver::ComputeInstanceFieldInfo(uint32_t field_idx, const DexCompi } } -bool CompilerDriver::ComputeStaticFieldInfo(uint32_t field_idx, const DexCompilationUnit* mUnit, - bool is_put, MemberOffset* field_offset, - uint32_t* storage_index, bool* is_referrers_class, - bool* is_volatile, bool* is_initialized, - Primitive::Type* type) { - ScopedObjectAccess soa(Thread::Current()); - // Try to resolve the field and compiling method's class. - ArtField* resolved_field; - mirror::Class* referrer_class; - Handle<mirror::DexCache> dex_cache(mUnit->GetDexCache()); - { - StackHandleScope<1> hs(soa.Self()); - Handle<mirror::ClassLoader> class_loader_handle( - hs.NewHandle(soa.Decode<mirror::ClassLoader*>(mUnit->GetClassLoader()))); - resolved_field = - ResolveField(soa, dex_cache, class_loader_handle, mUnit, field_idx, true); - referrer_class = resolved_field != nullptr - ? ResolveCompilingMethodsClass(soa, dex_cache, class_loader_handle, mUnit) : nullptr; - } - bool result = false; - if (resolved_field != nullptr && referrer_class != nullptr) { - *is_volatile = IsFieldVolatile(resolved_field); - std::pair<bool, bool> fast_path = IsFastStaticField( - dex_cache.Get(), referrer_class, resolved_field, field_idx, storage_index); - result = is_put ? fast_path.second : fast_path.first; - } - if (result) { - *field_offset = GetFieldOffset(resolved_field); - *is_referrers_class = IsStaticFieldInReferrerClass(referrer_class, resolved_field); - // *is_referrers_class == true implies no worrying about class initialization. - *is_initialized = (*is_referrers_class) || - (IsStaticFieldsClassInitialized(referrer_class, resolved_field) && - CanAssumeTypeIsPresentInDexCache(*mUnit->GetDexFile(), *storage_index)); - *type = resolved_field->GetTypeAsPrimitiveType(); - } else { - // Conservative defaults. - *is_volatile = true; - *field_offset = MemberOffset(static_cast<size_t>(-1)); - *storage_index = -1; - *is_referrers_class = false; - *is_initialized = false; - *type = Primitive::kPrimVoid; - } - ProcessedStaticField(result, *is_referrers_class); - return result; -} - void CompilerDriver::GetCodeAndMethodForDirectCall(InvokeType* type, InvokeType sharp_type, bool no_guarantee_of_dex_cache_entry, const mirror::Class* referrer_class, diff --git a/compiler/driver/compiler_driver.h b/compiler/driver/compiler_driver.h index 4308eac4af..d63dffa49a 100644 --- a/compiler/driver/compiler_driver.h +++ b/compiler/driver/compiler_driver.h @@ -195,25 +195,26 @@ class CompilerDriver { // Callbacks from compiler to see what runtime checks must be generated. - bool CanAssumeTypeIsPresentInDexCache(const DexFile& dex_file, uint32_t type_idx); + bool CanAssumeTypeIsPresentInDexCache(Handle<mirror::DexCache> dex_cache, + uint32_t type_idx) + SHARED_REQUIRES(Locks::mutator_lock_); bool CanAssumeStringIsPresentInDexCache(const DexFile& dex_file, uint32_t string_idx) REQUIRES(!Locks::mutator_lock_); // Are runtime access checks necessary in the compiled code? - bool CanAccessTypeWithoutChecks(uint32_t referrer_idx, const DexFile& dex_file, - uint32_t type_idx, bool* type_known_final = nullptr, - bool* type_known_abstract = nullptr, - bool* equals_referrers_class = nullptr) - REQUIRES(!Locks::mutator_lock_); + bool CanAccessTypeWithoutChecks(uint32_t referrer_idx, + Handle<mirror::DexCache> dex_cache, + uint32_t type_idx) + SHARED_REQUIRES(Locks::mutator_lock_); // Are runtime access and instantiable checks necessary in the code? // out_is_finalizable is set to whether the type is finalizable. bool CanAccessInstantiableTypeWithoutChecks(uint32_t referrer_idx, - const DexFile& dex_file, + Handle<mirror::DexCache> dex_cache, uint32_t type_idx, bool* out_is_finalizable) - REQUIRES(!Locks::mutator_lock_); + SHARED_REQUIRES(Locks::mutator_lock_); bool CanEmbedTypeInCode(const DexFile& dex_file, uint32_t type_idx, bool* is_type_initialized, bool* use_direct_type_ptr, @@ -368,14 +369,6 @@ class CompilerDriver { SHARED_REQUIRES(Locks::mutator_lock_); - // Can we fastpath static field access? Computes field's offset, volatility and whether the - // field is within the referrer (which can avoid checking class initialization). - bool ComputeStaticFieldInfo(uint32_t field_idx, const DexCompilationUnit* mUnit, bool is_put, - MemberOffset* field_offset, uint32_t* storage_index, - bool* is_referrers_class, bool* is_volatile, bool* is_initialized, - Primitive::Type* type) - REQUIRES(!Locks::mutator_lock_); - // Can we fastpath a interface, super class or virtual method call? Computes method's vtable // index. bool ComputeInvokeInfo(const DexCompilationUnit* mUnit, const uint32_t dex_pc, diff --git a/compiler/optimizing/instruction_builder.cc b/compiler/optimizing/instruction_builder.cc index 06b39680b2..f5e49c2235 100644 --- a/compiler/optimizing/instruction_builder.cc +++ b/compiler/optimizing/instruction_builder.cc @@ -889,8 +889,15 @@ bool HInstructionBuilder::BuildInvoke(const Instruction& instruction, } bool HInstructionBuilder::BuildNewInstance(uint16_t type_index, uint32_t dex_pc) { + ScopedObjectAccess soa(Thread::Current()); + StackHandleScope<1> hs(soa.Self()); + Handle<mirror::DexCache> dex_cache = dex_compilation_unit_->GetDexCache(); + Handle<mirror::Class> resolved_class(hs.NewHandle(dex_cache->GetResolvedType(type_index))); + const DexFile& outer_dex_file = *outer_compilation_unit_->GetDexFile(); + Handle<mirror::DexCache> outer_dex_cache = outer_compilation_unit_->GetDexCache(); + bool finalizable; - bool can_throw = NeedsAccessCheck(type_index, &finalizable); + bool can_throw = NeedsAccessCheck(type_index, dex_cache, &finalizable); // Only the non-resolved entrypoint handles the finalizable class case. If we // need access checks, then we haven't resolved the method and the class may @@ -899,16 +906,6 @@ bool HInstructionBuilder::BuildNewInstance(uint16_t type_index, uint32_t dex_pc) ? kQuickAllocObject : kQuickAllocObjectInitialized; - ScopedObjectAccess soa(Thread::Current()); - StackHandleScope<3> hs(soa.Self()); - Handle<mirror::DexCache> dex_cache(hs.NewHandle( - dex_compilation_unit_->GetClassLinker()->FindDexCache( - soa.Self(), *dex_compilation_unit_->GetDexFile()))); - Handle<mirror::Class> resolved_class(hs.NewHandle(dex_cache->GetResolvedType(type_index))); - const DexFile& outer_dex_file = *outer_compilation_unit_->GetDexFile(); - Handle<mirror::DexCache> outer_dex_cache(hs.NewHandle( - outer_compilation_unit_->GetClassLinker()->FindDexCache(soa.Self(), outer_dex_file))); - if (outer_dex_cache.Get() != dex_cache.Get()) { // We currently do not support inlining allocations across dex files. return false; @@ -921,7 +918,7 @@ bool HInstructionBuilder::BuildNewInstance(uint16_t type_index, uint32_t dex_pc) IsOutermostCompilingClass(type_index), dex_pc, /*needs_access_check*/ can_throw, - compiler_driver_->CanAssumeTypeIsPresentInDexCache(outer_dex_file, type_index)); + compiler_driver_->CanAssumeTypeIsPresentInDexCache(outer_dex_cache, type_index)); AppendInstruction(load_class); HInstruction* cls = load_class; @@ -979,13 +976,9 @@ HClinitCheck* HInstructionBuilder::ProcessClinitCheckForInvoke( HInvokeStaticOrDirect::ClinitCheckRequirement* clinit_check_requirement) { const DexFile& outer_dex_file = *outer_compilation_unit_->GetDexFile(); Thread* self = Thread::Current(); - StackHandleScope<4> hs(self); - Handle<mirror::DexCache> dex_cache(hs.NewHandle( - dex_compilation_unit_->GetClassLinker()->FindDexCache( - self, *dex_compilation_unit_->GetDexFile()))); - Handle<mirror::DexCache> outer_dex_cache(hs.NewHandle( - outer_compilation_unit_->GetClassLinker()->FindDexCache( - self, outer_dex_file))); + StackHandleScope<2> hs(self); + Handle<mirror::DexCache> dex_cache = dex_compilation_unit_->GetDexCache(); + Handle<mirror::DexCache> outer_dex_cache = outer_compilation_unit_->GetDexCache(); Handle<mirror::Class> outer_class(hs.NewHandle(GetOutermostCompilingClass())); Handle<mirror::Class> resolved_method_class(hs.NewHandle(resolved_method->GetDeclaringClass())); @@ -1016,7 +1009,7 @@ HClinitCheck* HInstructionBuilder::ProcessClinitCheckForInvoke( is_outer_class, dex_pc, /*needs_access_check*/ false, - compiler_driver_->CanAssumeTypeIsPresentInDexCache(outer_dex_file, storage_index)); + compiler_driver_->CanAssumeTypeIsPresentInDexCache(outer_dex_cache, storage_index)); AppendInstruction(load_class); clinit_check = new (arena_) HClinitCheck(load_class, dex_pc); AppendInstruction(clinit_check); @@ -1261,12 +1254,10 @@ bool HInstructionBuilder::BuildInstanceFieldAccess(const Instruction& instructio static mirror::Class* GetClassFrom(CompilerDriver* driver, const DexCompilationUnit& compilation_unit) { ScopedObjectAccess soa(Thread::Current()); - StackHandleScope<2> hs(soa.Self()); - const DexFile& dex_file = *compilation_unit.GetDexFile(); + StackHandleScope<1> hs(soa.Self()); Handle<mirror::ClassLoader> class_loader(hs.NewHandle( soa.Decode<mirror::ClassLoader*>(compilation_unit.GetClassLoader()))); - Handle<mirror::DexCache> dex_cache(hs.NewHandle( - compilation_unit.GetClassLinker()->FindDexCache(soa.Self(), dex_file))); + Handle<mirror::DexCache> dex_cache = compilation_unit.GetDexCache(); return driver->ResolveCompilingMethodsClass(soa, dex_cache, class_loader, &compilation_unit); } @@ -1281,10 +1272,8 @@ mirror::Class* HInstructionBuilder::GetCompilingClass() const { bool HInstructionBuilder::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( - soa.Self(), *dex_compilation_unit_->GetDexFile()))); + StackHandleScope<3> hs(soa.Self()); + Handle<mirror::DexCache> dex_cache = dex_compilation_unit_->GetDexCache(); Handle<mirror::ClassLoader> class_loader(hs.NewHandle( soa.Decode<mirror::ClassLoader*>(dex_compilation_unit_->GetClassLoader()))); Handle<mirror::Class> cls(hs.NewHandle(compiler_driver_->ResolveClass( @@ -1324,10 +1313,8 @@ bool HInstructionBuilder::BuildStaticFieldAccess(const Instruction& instruction, uint16_t field_index = instruction.VRegB_21c(); ScopedObjectAccess soa(Thread::Current()); - StackHandleScope<5> hs(soa.Self()); - Handle<mirror::DexCache> dex_cache(hs.NewHandle( - dex_compilation_unit_->GetClassLinker()->FindDexCache( - soa.Self(), *dex_compilation_unit_->GetDexFile()))); + StackHandleScope<3> hs(soa.Self()); + Handle<mirror::DexCache> dex_cache = dex_compilation_unit_->GetDexCache(); Handle<mirror::ClassLoader> class_loader(hs.NewHandle( soa.Decode<mirror::ClassLoader*>(dex_compilation_unit_->GetClassLoader()))); ArtField* resolved_field = compiler_driver_->ResolveField( @@ -1342,8 +1329,7 @@ bool HInstructionBuilder::BuildStaticFieldAccess(const Instruction& instruction, Primitive::Type field_type = resolved_field->GetTypeAsPrimitiveType(); const DexFile& outer_dex_file = *outer_compilation_unit_->GetDexFile(); - Handle<mirror::DexCache> outer_dex_cache(hs.NewHandle( - outer_compilation_unit_->GetClassLinker()->FindDexCache(soa.Self(), outer_dex_file))); + Handle<mirror::DexCache> outer_dex_cache = outer_compilation_unit_->GetDexCache(); Handle<mirror::Class> outer_class(hs.NewHandle(GetOutermostCompilingClass())); // The index at which the field's class is stored in the DexCache's type array. @@ -1371,7 +1357,7 @@ bool HInstructionBuilder::BuildStaticFieldAccess(const Instruction& instruction, } bool is_in_cache = - compiler_driver_->CanAssumeTypeIsPresentInDexCache(outer_dex_file, storage_index); + compiler_driver_->CanAssumeTypeIsPresentInDexCache(outer_dex_cache, storage_index); HLoadClass* constant = new (arena_) HLoadClass(graph_->GetCurrentMethod(), storage_index, outer_dex_file, @@ -1634,22 +1620,17 @@ void HInstructionBuilder::BuildTypeCheck(const Instruction& instruction, uint8_t reference, uint16_t type_index, uint32_t dex_pc) { - bool type_known_final, type_known_abstract, use_declaring_class; - bool can_access = compiler_driver_->CanAccessTypeWithoutChecks( - dex_compilation_unit_->GetDexMethodIndex(), - *dex_compilation_unit_->GetDexFile(), - type_index, - &type_known_final, - &type_known_abstract, - &use_declaring_class); - ScopedObjectAccess soa(Thread::Current()); - StackHandleScope<2> hs(soa.Self()); + StackHandleScope<1> hs(soa.Self()); const DexFile& dex_file = *dex_compilation_unit_->GetDexFile(); - Handle<mirror::DexCache> dex_cache(hs.NewHandle( - dex_compilation_unit_->GetClassLinker()->FindDexCache(soa.Self(), dex_file))); + Handle<mirror::DexCache> dex_cache = dex_compilation_unit_->GetDexCache(); Handle<mirror::Class> resolved_class(hs.NewHandle(dex_cache->GetResolvedType(type_index))); + bool can_access = compiler_driver_->CanAccessTypeWithoutChecks( + dex_compilation_unit_->GetDexMethodIndex(), + dex_cache, + type_index); + HInstruction* object = LoadLocal(reference, Primitive::kPrimNot); HLoadClass* cls = new (arena_) HLoadClass( graph_->GetCurrentMethod(), @@ -1658,7 +1639,7 @@ void HInstructionBuilder::BuildTypeCheck(const Instruction& instruction, IsOutermostCompilingClass(type_index), dex_pc, !can_access, - compiler_driver_->CanAssumeTypeIsPresentInDexCache(dex_file, type_index)); + compiler_driver_->CanAssumeTypeIsPresentInDexCache(dex_cache, type_index)); AppendInstruction(cls); TypeCheckKind check_kind = ComputeTypeCheckKind(resolved_class); @@ -1676,9 +1657,17 @@ void HInstructionBuilder::BuildTypeCheck(const Instruction& instruction, } } -bool HInstructionBuilder::NeedsAccessCheck(uint32_t type_index, bool* finalizable) const { +bool HInstructionBuilder::NeedsAccessCheck(uint32_t type_index, + Handle<mirror::DexCache> dex_cache, + bool* finalizable) const { return !compiler_driver_->CanAccessInstantiableTypeWithoutChecks( - dex_compilation_unit_->GetDexMethodIndex(), *dex_file_, type_index, finalizable); + dex_compilation_unit_->GetDexMethodIndex(), dex_cache, type_index, finalizable); +} + +bool HInstructionBuilder::NeedsAccessCheck(uint32_t type_index, bool* finalizable) const { + ScopedObjectAccess soa(Thread::Current()); + Handle<mirror::DexCache> dex_cache = dex_compilation_unit_->GetDexCache(); + return NeedsAccessCheck(type_index, dex_cache, finalizable); } bool HInstructionBuilder::CanDecodeQuickenedInfo() const { @@ -2612,16 +2601,16 @@ bool HInstructionBuilder::ProcessDexInstruction(const Instruction& instruction, case Instruction::CONST_CLASS: { uint16_t type_index = instruction.VRegB_21c(); - bool type_known_final; - bool type_known_abstract; - 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 `IsOutermostCompilingClass` instead. + ScopedObjectAccess soa(Thread::Current()); + Handle<mirror::DexCache> dex_cache = dex_compilation_unit_->GetDexCache(); 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); + dex_compilation_unit_->GetDexMethodIndex(), dex_cache, type_index); + bool is_in_dex_cache = + compiler_driver_->CanAssumeTypeIsPresentInDexCache(dex_cache, type_index); AppendInstruction(new (arena_) HLoadClass( graph_->GetCurrentMethod(), type_index, @@ -2629,7 +2618,7 @@ bool HInstructionBuilder::ProcessDexInstruction(const Instruction& instruction, IsOutermostCompilingClass(type_index), dex_pc, !can_access, - compiler_driver_->CanAssumeTypeIsPresentInDexCache(*dex_file_, type_index))); + is_in_dex_cache)); UpdateLocal(instruction.VRegA_21c(), current_block_->GetLastInstruction()); break; } diff --git a/compiler/optimizing/instruction_builder.h b/compiler/optimizing/instruction_builder.h index f480b70062..070f7da80e 100644 --- a/compiler/optimizing/instruction_builder.h +++ b/compiler/optimizing/instruction_builder.h @@ -97,6 +97,10 @@ class HInstructionBuilder : public ValueObject { // Returns whether the current method needs access check for the type. // Output parameter finalizable is set to whether the type is finalizable. + bool NeedsAccessCheck(uint32_t type_index, + Handle<mirror::DexCache> dex_cache, + /*out*/bool* finalizable) const + SHARED_REQUIRES(Locks::mutator_lock_); bool NeedsAccessCheck(uint32_t type_index, /*out*/bool* finalizable) const; template<typename T> diff --git a/compiler/optimizing/licm.cc b/compiler/optimizing/licm.cc index 7a1e06b951..5a0b89c90a 100644 --- a/compiler/optimizing/licm.cc +++ b/compiler/optimizing/licm.cc @@ -79,8 +79,15 @@ static void UpdateLoopPhisIn(HEnvironment* environment, HLoopInformation* info) void LICM::Run() { DCHECK(side_effects_.HasRun()); + // Only used during debug. - ArenaBitVector visited(graph_->GetArena(), graph_->GetBlocks().size(), false, kArenaAllocLICM); + ArenaBitVector* visited = nullptr; + if (kIsDebugBuild) { + visited = new (graph_->GetArena()) ArenaBitVector(graph_->GetArena(), + graph_->GetBlocks().size(), + false, + kArenaAllocLICM); + } // Post order visit to visit inner loops before outer loops. for (HPostOrderIterator it(*graph_); !it.Done(); it.Advance()) { @@ -109,10 +116,12 @@ void LICM::Run() { DCHECK(inner->IsInLoop()); if (inner->GetLoopInformation() != loop_info) { // Thanks to post order visit, inner loops were already visited. - DCHECK(visited.IsBitSet(inner->GetBlockId())); + DCHECK(visited->IsBitSet(inner->GetBlockId())); continue; } - visited.SetBit(inner->GetBlockId()); + if (kIsDebugBuild) { + visited->SetBit(inner->GetBlockId()); + } if (contains_irreducible_loop) { // We cannot licm in an irreducible loop, or in a natural loop containing an diff --git a/compiler/optimizing/licm_test.cc b/compiler/optimizing/licm_test.cc index 2a62643465..d446539700 100644 --- a/compiler/optimizing/licm_test.cc +++ b/compiler/optimizing/licm_test.cc @@ -169,11 +169,13 @@ TEST_F(LICMTest, ArrayHoisting) { BuildLoop(); // Populate the loop with instructions: set/get array with different types. + // ArrayGet is typed as kPrimByte and ArraySet given a float value in order to + // avoid SsaBuilder's typing of ambiguous array operations from reference type info. HInstruction* get_array = new (&allocator_) HArrayGet( - parameter_, int_constant_, Primitive::kPrimInt, 0); + parameter_, int_constant_, Primitive::kPrimByte, 0); loop_body_->InsertInstructionBefore(get_array, loop_body_->GetLastInstruction()); HInstruction* set_array = new (&allocator_) HArraySet( - parameter_, int_constant_, float_constant_, Primitive::kPrimFloat, 0); + parameter_, int_constant_, float_constant_, Primitive::kPrimShort, 0); loop_body_->InsertInstructionBefore(set_array, loop_body_->GetLastInstruction()); EXPECT_EQ(get_array->GetBlock(), loop_body_); @@ -187,11 +189,13 @@ TEST_F(LICMTest, NoArrayHoisting) { BuildLoop(); // Populate the loop with instructions: set/get array with same types. + // ArrayGet is typed as kPrimByte and ArraySet given a float value in order to + // avoid SsaBuilder's typing of ambiguous array operations from reference type info. HInstruction* get_array = new (&allocator_) HArrayGet( - parameter_, int_constant_, Primitive::kPrimFloat, 0); + parameter_, int_constant_, Primitive::kPrimByte, 0); loop_body_->InsertInstructionBefore(get_array, loop_body_->GetLastInstruction()); HInstruction* set_array = new (&allocator_) HArraySet( - parameter_, get_array, float_constant_, Primitive::kPrimFloat, 0); + parameter_, get_array, float_constant_, Primitive::kPrimByte, 0); loop_body_->InsertInstructionBefore(set_array, loop_body_->GetLastInstruction()); EXPECT_EQ(get_array->GetBlock(), loop_body_); diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h index 6f3e536d05..dc5a8fa9cb 100644 --- a/compiler/optimizing/nodes.h +++ b/compiler/optimizing/nodes.h @@ -1551,21 +1551,21 @@ class SideEffects : public ValueObject { static SideEffects FieldWriteOfType(Primitive::Type type, bool is_volatile) { return is_volatile ? AllWritesAndReads() - : SideEffects(TypeFlag(type, kFieldWriteOffset)); + : SideEffects(TypeFlagWithAlias(type, kFieldWriteOffset)); } static SideEffects ArrayWriteOfType(Primitive::Type type) { - return SideEffects(TypeFlag(type, kArrayWriteOffset)); + return SideEffects(TypeFlagWithAlias(type, kArrayWriteOffset)); } static SideEffects FieldReadOfType(Primitive::Type type, bool is_volatile) { return is_volatile ? AllWritesAndReads() - : SideEffects(TypeFlag(type, kFieldReadOffset)); + : SideEffects(TypeFlagWithAlias(type, kFieldReadOffset)); } static SideEffects ArrayReadOfType(Primitive::Type type) { - return SideEffects(TypeFlag(type, kArrayReadOffset)); + return SideEffects(TypeFlagWithAlias(type, kArrayReadOffset)); } static SideEffects CanTriggerGC() { @@ -1692,6 +1692,23 @@ class SideEffects : public ValueObject { static constexpr uint64_t kAllReads = ((1ULL << (kLastBitForReads + 1 - kFieldReadOffset)) - 1) << kFieldReadOffset; + // Work around the fact that HIR aliases I/F and J/D. + // TODO: remove this interceptor once HIR types are clean + static uint64_t TypeFlagWithAlias(Primitive::Type type, int offset) { + switch (type) { + case Primitive::kPrimInt: + case Primitive::kPrimFloat: + return TypeFlag(Primitive::kPrimInt, offset) | + TypeFlag(Primitive::kPrimFloat, offset); + case Primitive::kPrimLong: + case Primitive::kPrimDouble: + return TypeFlag(Primitive::kPrimLong, offset) | + TypeFlag(Primitive::kPrimDouble, offset); + default: + return TypeFlag(type, offset); + } + } + // Translates type to bit flag. static uint64_t TypeFlag(Primitive::Type type, int offset) { CHECK_NE(type, Primitive::kPrimVoid); @@ -5179,8 +5196,10 @@ class HArraySet : public HTemplateInstruction<3> { uint32_t dex_pc, SideEffects additional_side_effects = SideEffects::None()) : HTemplateInstruction( - SideEffectsForArchRuntimeCalls(value->GetType()).Union(additional_side_effects), - dex_pc) { + SideEffects::ArrayWriteOfType(expected_component_type).Union( + SideEffectsForArchRuntimeCalls(value->GetType())).Union( + additional_side_effects), + dex_pc) { SetPackedField<ExpectedComponentTypeField>(expected_component_type); SetPackedFlag<kFlagNeedsTypeCheck>(value->GetType() == Primitive::kPrimNot); SetPackedFlag<kFlagValueCanBeNull>(true); @@ -5188,8 +5207,6 @@ class HArraySet : public HTemplateInstruction<3> { SetRawInputAt(0, array); SetRawInputAt(1, index); SetRawInputAt(2, value); - // We can now call component type logic to set correct type-based side effects. - AddSideEffects(SideEffects::ArrayWriteOfType(GetComponentType())); } bool NeedsEnvironment() const OVERRIDE { diff --git a/compiler/optimizing/side_effects_test.cc b/compiler/optimizing/side_effects_test.cc index b01bc1ca0d..9bbc354290 100644 --- a/compiler/optimizing/side_effects_test.cc +++ b/compiler/optimizing/side_effects_test.cc @@ -148,19 +148,19 @@ TEST(SideEffectsTest, VolatileDependences) { EXPECT_FALSE(any_write.MayDependOn(volatile_read)); } -TEST(SideEffectsTest, SameWidthTypesNoAlias) { +TEST(SideEffectsTest, SameWidthTypes) { // Type I/F. - testNoWriteAndReadDependence( + testWriteAndReadDependence( SideEffects::FieldWriteOfType(Primitive::kPrimInt, /* is_volatile */ false), SideEffects::FieldReadOfType(Primitive::kPrimFloat, /* is_volatile */ false)); - testNoWriteAndReadDependence( + testWriteAndReadDependence( SideEffects::ArrayWriteOfType(Primitive::kPrimInt), SideEffects::ArrayReadOfType(Primitive::kPrimFloat)); // Type L/D. - testNoWriteAndReadDependence( + testWriteAndReadDependence( SideEffects::FieldWriteOfType(Primitive::kPrimLong, /* is_volatile */ false), SideEffects::FieldReadOfType(Primitive::kPrimDouble, /* is_volatile */ false)); - testNoWriteAndReadDependence( + testWriteAndReadDependence( SideEffects::ArrayWriteOfType(Primitive::kPrimLong), SideEffects::ArrayReadOfType(Primitive::kPrimDouble)); } @@ -216,32 +216,14 @@ TEST(SideEffectsTest, BitStrings) { "||||||L|", SideEffects::FieldWriteOfType(Primitive::kPrimNot, false).ToString().c_str()); EXPECT_STREQ( - "||DFJISCBZL|DFJISCBZL||DFJISCBZL|DFJISCBZL|", - SideEffects::FieldWriteOfType(Primitive::kPrimNot, true).ToString().c_str()); - EXPECT_STREQ( "|||||Z||", SideEffects::ArrayWriteOfType(Primitive::kPrimBoolean).ToString().c_str()); EXPECT_STREQ( - "|||||C||", - SideEffects::ArrayWriteOfType(Primitive::kPrimChar).ToString().c_str()); - EXPECT_STREQ( - "|||||S||", - SideEffects::ArrayWriteOfType(Primitive::kPrimShort).ToString().c_str()); - EXPECT_STREQ( "|||B||||", SideEffects::FieldReadOfType(Primitive::kPrimByte, false).ToString().c_str()); EXPECT_STREQ( - "||D|||||", + "||DJ|||||", // note: DJ alias SideEffects::ArrayReadOfType(Primitive::kPrimDouble).ToString().c_str()); - EXPECT_STREQ( - "||J|||||", - SideEffects::ArrayReadOfType(Primitive::kPrimLong).ToString().c_str()); - EXPECT_STREQ( - "||F|||||", - SideEffects::ArrayReadOfType(Primitive::kPrimFloat).ToString().c_str()); - EXPECT_STREQ( - "||I|||||", - SideEffects::ArrayReadOfType(Primitive::kPrimInt).ToString().c_str()); SideEffects s = SideEffects::None(); s = s.Union(SideEffects::FieldWriteOfType(Primitive::kPrimChar, /* is_volatile */ false)); s = s.Union(SideEffects::FieldWriteOfType(Primitive::kPrimLong, /* is_volatile */ false)); @@ -249,7 +231,9 @@ TEST(SideEffectsTest, BitStrings) { s = s.Union(SideEffects::FieldReadOfType(Primitive::kPrimInt, /* is_volatile */ false)); s = s.Union(SideEffects::ArrayReadOfType(Primitive::kPrimFloat)); s = s.Union(SideEffects::ArrayReadOfType(Primitive::kPrimDouble)); - EXPECT_STREQ("||DF|I||S|JC|", s.ToString().c_str()); + EXPECT_STREQ( + "||DFJI|FI||S|DJC|", // note: DJ/FI alias. + s.ToString().c_str()); } } // namespace art diff --git a/compiler/optimizing/ssa_liveness_analysis.h b/compiler/optimizing/ssa_liveness_analysis.h index 97f2aeeb1e..719feec468 100644 --- a/compiler/optimizing/ssa_liveness_analysis.h +++ b/compiler/optimizing/ssa_liveness_analysis.h @@ -969,6 +969,38 @@ class LiveInterval : public ArenaObject<kArenaAllocSsaLiveness> { return false; } + bool IsLinearOrderWellFormed(const HGraph& graph) { + for (HBasicBlock* header : graph.GetBlocks()) { + if (!header->IsLoopHeader()) { + continue; + } + + HLoopInformation* loop = header->GetLoopInformation(); + size_t num_blocks = loop->GetBlocks().NumSetBits(); + size_t found_blocks = 0u; + + for (HLinearOrderIterator it(graph); !it.Done(); it.Advance()) { + HBasicBlock* current = it.Current(); + if (loop->Contains(*current)) { + found_blocks++; + if (found_blocks == 1u && current != header) { + // First block is not the header. + return false; + } else if (found_blocks == num_blocks && !loop->IsBackEdge(*current)) { + // Last block is not a back edge. + return false; + } + } else if (found_blocks != 0u && found_blocks != num_blocks) { + // Blocks are not adjacent. + return false; + } + } + DCHECK_EQ(found_blocks, num_blocks); + } + + return true; + } + void AddBackEdgeUses(const HBasicBlock& block_at_use) { DCHECK(block_at_use.IsInLoop()); // Add synthesized uses at the back edge of loops to help the register allocator. @@ -995,12 +1027,30 @@ class LiveInterval : public ArenaObject<kArenaAllocSsaLiveness> { if ((first_use_ != nullptr) && (first_use_->GetPosition() <= back_edge_use_position)) { // There was a use already seen in this loop. Therefore the previous call to `AddUse` // already inserted the backedge use. We can stop going outward. - DCHECK(HasSynthesizeUseAt(back_edge_use_position)); + if (kIsDebugBuild) { + if (!HasSynthesizeUseAt(back_edge_use_position)) { + // There exists a use prior to `back_edge_use_position` but there is + // no synthesized use at the back edge. This can happen in the presence + // of irreducible loops, when blocks of the loop are not adjacent in + // linear order, i.e. when there is an out-of-loop block between + // `block_at_use` and `back_edge_position` that uses this interval. + DCHECK(block_at_use.GetGraph()->HasIrreducibleLoops()); + DCHECK(!IsLinearOrderWellFormed(*block_at_use.GetGraph())); + } + } break; } - DCHECK(last_in_new_list == nullptr - || back_edge_use_position > last_in_new_list->GetPosition()); + if (last_in_new_list != nullptr && + back_edge_use_position <= last_in_new_list->GetPosition()) { + // Loops are not properly nested in the linear order, i.e. the back edge + // of an outer loop preceeds blocks of an inner loop. This can happen + // in the presence of irreducible loops. + DCHECK(block_at_use.GetGraph()->HasIrreducibleLoops()); + DCHECK(!IsLinearOrderWellFormed(*block_at_use.GetGraph())); + // We must bail out, otherwise we would generate an unsorted use list. + break; + } UsePosition* new_use = new (allocator_) UsePosition( /* user */ nullptr, diff --git a/runtime/Android.mk b/runtime/Android.mk index c85907987b..aa12c83ceb 100644 --- a/runtime/Android.mk +++ b/runtime/Android.mk @@ -106,7 +106,6 @@ LIBART_COMMON_SRC_FILES := \ jit/debugger_interface.cc \ jit/jit.cc \ jit/jit_code_cache.cc \ - jit/jit_instrumentation.cc \ jit/offline_profiling_info.cc \ jit/profiling_info.cc \ jit/profile_saver.cc \ diff --git a/runtime/arch/arm/quick_entrypoints_arm.S b/runtime/arch/arm/quick_entrypoints_arm.S index da7db1dbb4..e6ff0aa131 100644 --- a/runtime/arch/arm/quick_entrypoints_arm.S +++ b/runtime/arch/arm/quick_entrypoints_arm.S @@ -1832,7 +1832,7 @@ ENTRY art_quick_fmodf add sp, #4 .cfi_adjust_cfa_offset -4 pop {pc} -END art_quick_fmod +END art_quick_fmodf /* int64_t art_d2l(double d) */ .extern art_d2l diff --git a/runtime/asm_support.h b/runtime/asm_support.h index d27d2f6c91..21725d3279 100644 --- a/runtime/asm_support.h +++ b/runtime/asm_support.h @@ -20,7 +20,7 @@ #if defined(__cplusplus) #include "art_method.h" #include "gc/allocator/rosalloc.h" -#include "jit/jit_instrumentation.h" +#include "jit/jit.h" #include "lock_word.h" #include "mirror/class.h" #include "mirror/string.h" diff --git a/runtime/dex_file.h b/runtime/dex_file.h index 3a28422067..ce7f62acb5 100644 --- a/runtime/dex_file.h +++ b/runtime/dex_file.h @@ -57,6 +57,7 @@ class ZipArchive; // TODO: move all of the macro functionality into the DexCache class. class DexFile { public: + static const uint32_t kDefaultMethodsVersion = 37; static const uint8_t kDexMagic[]; static constexpr size_t kNumDexVersions = 2; static constexpr size_t kDexVersionLen = 4; diff --git a/runtime/dex_file_verifier.cc b/runtime/dex_file_verifier.cc index 681c5f977f..3df4e98c84 100644 --- a/runtime/dex_file_verifier.cc +++ b/runtime/dex_file_verifier.cc @@ -2465,7 +2465,7 @@ bool DexFileVerifier::CheckFieldAccessFlags(uint32_t idx, GetFieldDescriptionOrError(begin_, header_, idx).c_str(), field_access_flags, PrettyJavaAccessFlags(field_access_flags).c_str()); - if (header_->GetVersion() >= 37) { + if (header_->GetVersion() >= DexFile::kDefaultMethodsVersion) { return false; } else { // Allow in older versions, but warn. @@ -2480,7 +2480,7 @@ bool DexFileVerifier::CheckFieldAccessFlags(uint32_t idx, GetFieldDescriptionOrError(begin_, header_, idx).c_str(), field_access_flags, PrettyJavaAccessFlags(field_access_flags).c_str()); - if (header_->GetVersion() >= 37) { + if (header_->GetVersion() >= DexFile::kDefaultMethodsVersion) { return false; } else { // Allow in older versions, but warn. @@ -2628,12 +2628,16 @@ bool DexFileVerifier::CheckMethodAccessFlags(uint32_t method_index, // Interfaces are special. if ((class_access_flags & kAccInterface) != 0) { - // Non-static interface methods must be public. - if ((method_access_flags & (kAccPublic | kAccStatic)) == 0) { + // Non-static interface methods must be public or private. + uint32_t desired_flags = (kAccPublic | kAccStatic); + if (dex_file_->GetVersion() >= DexFile::kDefaultMethodsVersion) { + desired_flags |= kAccPrivate; + } + if ((method_access_flags & desired_flags) == 0) { *error_msg = StringPrintf("Interface virtual method %" PRIu32 "(%s) is not public", method_index, GetMethodDescriptionOrError(begin_, header_, method_index).c_str()); - if (header_->GetVersion() >= 37) { + if (header_->GetVersion() >= DexFile::kDefaultMethodsVersion) { return false; } else { // Allow in older versions, but warn. @@ -2686,7 +2690,7 @@ bool DexFileVerifier::CheckMethodAccessFlags(uint32_t method_index, *error_msg = StringPrintf("Interface method %" PRIu32 "(%s) is not public and abstract", method_index, GetMethodDescriptionOrError(begin_, header_, method_index).c_str()); - if (header_->GetVersion() >= 37) { + if (header_->GetVersion() >= DexFile::kDefaultMethodsVersion) { return false; } else { // Allow in older versions, but warn. diff --git a/runtime/interpreter/interpreter.cc b/runtime/interpreter/interpreter.cc index baf4afea18..a43278228c 100644 --- a/runtime/interpreter/interpreter.cc +++ b/runtime/interpreter/interpreter.cc @@ -285,16 +285,19 @@ static inline JValue Execute(Thread* self, const DexFile::CodeItem* code_item, } jit::Jit* jit = Runtime::Current()->GetJit(); - if (jit != nullptr && jit->CanInvokeCompiledCode(method)) { - JValue result; + if (jit != nullptr) { + jit->MethodEntered(self, shadow_frame.GetMethod()); + if (jit->CanInvokeCompiledCode(method)) { + JValue result; - // Pop the shadow frame before calling into compiled code. - self->PopShadowFrame(); - ArtInterpreterToCompiledCodeBridge(self, code_item, &shadow_frame, &result); - // Push the shadow frame back as the caller will expect it. - self->PushShadowFrame(&shadow_frame); + // Pop the shadow frame before calling into compiled code. + self->PopShadowFrame(); + ArtInterpreterToCompiledCodeBridge(self, code_item, &shadow_frame, &result); + // Push the shadow frame back as the caller will expect it. + self->PushShadowFrame(&shadow_frame); - return result; + return result; + } } } diff --git a/runtime/interpreter/interpreter_common.h b/runtime/interpreter/interpreter_common.h index 19d971ead8..fb9817514c 100644 --- a/runtime/interpreter/interpreter_common.h +++ b/runtime/interpreter/interpreter_common.h @@ -34,6 +34,7 @@ #include "dex_instruction-inl.h" #include "entrypoints/entrypoint_utils-inl.h" #include "handle_scope-inl.h" +#include "jit/jit.h" #include "lambda/art_lambda_method.h" #include "lambda/box_table.h" #include "lambda/closure.h" @@ -628,6 +629,15 @@ static inline bool DoInvoke(Thread* self, ShadowFrame& shadow_frame, const Instr result->SetJ(0); return false; } else { + jit::Jit* jit = Runtime::Current()->GetJit(); + if (jit != nullptr) { + if (type == kVirtual || type == kInterface) { + jit->InvokeVirtualOrInterface( + self, receiver, sf_method, shadow_frame.GetDexPC(), called_method); + } + jit->AddSamples(self, sf_method, 1); + } + // TODO: Remove the InvokeVirtualOrInterface instrumentation, as it was only used by the JIT. if (type == kVirtual || type == kInterface) { instrumentation::Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation(); if (UNLIKELY(instrumentation->HasInvokeVirtualOrInterfaceListeners())) { @@ -667,7 +677,14 @@ static inline bool DoInvokeVirtualQuick(Thread* self, ShadowFrame& shadow_frame, result->SetJ(0); return false; } else { + jit::Jit* jit = Runtime::Current()->GetJit(); + if (jit != nullptr) { + jit->InvokeVirtualOrInterface( + self, receiver, shadow_frame.GetMethod(), shadow_frame.GetDexPC(), called_method); + jit->AddSamples(self, shadow_frame.GetMethod(), 1); + } instrumentation::Instrumentation* instrumentation = Runtime::Current()->GetInstrumentation(); + // TODO: Remove the InvokeVirtualOrInterface instrumentation, as it was only used by the JIT. if (UNLIKELY(instrumentation->HasInvokeVirtualOrInterfaceListeners())) { instrumentation->InvokeVirtualOrInterface( self, receiver, shadow_frame.GetMethod(), shadow_frame.GetDexPC(), called_method); diff --git a/runtime/interpreter/interpreter_goto_table_impl.cc b/runtime/interpreter/interpreter_goto_table_impl.cc index ce698fb688..c95af6f0f1 100644 --- a/runtime/interpreter/interpreter_goto_table_impl.cc +++ b/runtime/interpreter/interpreter_goto_table_impl.cc @@ -22,7 +22,6 @@ #include "experimental_flags.h" #include "interpreter_common.h" #include "jit/jit.h" -#include "jit/jit_instrumentation.h" #include "safe_math.h" #include <memory> // std::unique_ptr @@ -67,7 +66,9 @@ namespace interpreter { #define BRANCH_INSTRUMENTATION(offset) \ do { \ - instrumentation->Branch(self, method, dex_pc, offset); \ + if (UNLIKELY(instrumentation->HasBranchListeners())) { \ + instrumentation->Branch(self, method, dex_pc, offset); \ + } \ JValue result; \ if (jit::Jit::MaybeDoOnStackReplacement(self, method, dex_pc, offset, &result)) { \ return result; \ @@ -76,8 +77,8 @@ namespace interpreter { #define HOTNESS_UPDATE() \ do { \ - if (jit_instrumentation_cache != nullptr) { \ - jit_instrumentation_cache->AddSamples(self, method, 1); \ + if (jit != nullptr) { \ + jit->AddSamples(self, method, 1); \ } \ } while (false) @@ -195,10 +196,6 @@ JValue ExecuteGotoImpl(Thread* self, const DexFile::CodeItem* code_item, ShadowF const auto* const instrumentation = Runtime::Current()->GetInstrumentation(); ArtMethod* method = shadow_frame.GetMethod(); jit::Jit* jit = Runtime::Current()->GetJit(); - jit::JitInstrumentationCache* jit_instrumentation_cache = nullptr; - if (jit != nullptr) { - jit_instrumentation_cache = jit->GetInstrumentationCache(); - } // Jump to first instruction. ADVANCE(0); diff --git a/runtime/interpreter/interpreter_switch_impl.cc b/runtime/interpreter/interpreter_switch_impl.cc index 442e1915f8..ca1d635d51 100644 --- a/runtime/interpreter/interpreter_switch_impl.cc +++ b/runtime/interpreter/interpreter_switch_impl.cc @@ -18,7 +18,6 @@ #include "experimental_flags.h" #include "interpreter_common.h" #include "jit/jit.h" -#include "jit/jit_instrumentation.h" #include "safe_math.h" #include <memory> // std::unique_ptr @@ -74,7 +73,9 @@ namespace interpreter { #define BRANCH_INSTRUMENTATION(offset) \ do { \ - instrumentation->Branch(self, method, dex_pc, offset); \ + if (UNLIKELY(instrumentation->HasBranchListeners())) { \ + instrumentation->Branch(self, method, dex_pc, offset); \ + } \ JValue result; \ if (jit::Jit::MaybeDoOnStackReplacement(self, method, dex_pc, offset, &result)) { \ if (interpret_one_instruction) { \ @@ -87,8 +88,8 @@ namespace interpreter { #define HOTNESS_UPDATE() \ do { \ - if (jit_instrumentation_cache != nullptr) { \ - jit_instrumentation_cache->AddSamples(self, method, 1); \ + if (jit != nullptr) { \ + jit->AddSamples(self, method, 1); \ } \ } while (false) @@ -115,10 +116,6 @@ JValue ExecuteSwitchImpl(Thread* self, const DexFile::CodeItem* code_item, uint16_t inst_data; ArtMethod* method = shadow_frame.GetMethod(); jit::Jit* jit = Runtime::Current()->GetJit(); - jit::JitInstrumentationCache* jit_instrumentation_cache = nullptr; - if (jit != nullptr) { - jit_instrumentation_cache = jit->GetInstrumentationCache(); - } // TODO: collapse capture-variable+create-lambda into one opcode, then we won't need // to keep this live for the scope of the entire function call. diff --git a/runtime/interpreter/mterp/mterp.cc b/runtime/interpreter/mterp/mterp.cc index 32c45fca71..f80068354f 100644 --- a/runtime/interpreter/mterp/mterp.cc +++ b/runtime/interpreter/mterp/mterp.cc @@ -20,8 +20,6 @@ #include "interpreter/interpreter_common.h" #include "entrypoints/entrypoint_utils-inl.h" #include "mterp.h" -#include "jit/jit.h" -#include "jit/jit_instrumentation.h" #include "debugger.h" namespace art { @@ -652,10 +650,9 @@ extern "C" int MterpSetUpHotnessCountdown(ArtMethod* method, ShadowFrame* shadow int32_t countdown_value = jit::kJitHotnessDisabled; jit::Jit* jit = Runtime::Current()->GetJit(); if (jit != nullptr) { - jit::JitInstrumentationCache* cache = jit->GetInstrumentationCache(); - int32_t warm_threshold = cache->WarmMethodThreshold(); - int32_t hot_threshold = cache->HotMethodThreshold(); - int32_t osr_threshold = cache->OSRMethodThreshold(); + int32_t warm_threshold = jit->WarmMethodThreshold(); + int32_t hot_threshold = jit->HotMethodThreshold(); + int32_t osr_threshold = jit->OSRMethodThreshold(); if (hotness_count < warm_threshold) { countdown_value = warm_threshold - hotness_count; } else if (hotness_count < hot_threshold) { @@ -666,7 +663,7 @@ extern "C" int MterpSetUpHotnessCountdown(ArtMethod* method, ShadowFrame* shadow countdown_value = jit::kJitCheckForOSR; } if (jit::Jit::ShouldUsePriorityThreadWeight()) { - int32_t priority_thread_weight = cache->PriorityThreadWeight(); + int32_t priority_thread_weight = jit->PriorityThreadWeight(); countdown_value = std::min(countdown_value, countdown_value / priority_thread_weight); } } @@ -692,7 +689,7 @@ extern "C" int16_t MterpAddHotnessBatch(ArtMethod* method, jit::Jit* jit = Runtime::Current()->GetJit(); if (jit != nullptr) { int16_t count = shadow_frame->GetCachedHotnessCountdown() - shadow_frame->GetHotnessCountdown(); - jit->GetInstrumentationCache()->AddSamples(self, method, count); + jit->AddSamples(self, method, count); } return MterpSetUpHotnessCountdown(method, shadow_frame); } @@ -705,7 +702,7 @@ extern "C" bool MterpProfileBranch(Thread* self, ShadowFrame* shadow_frame, int uint32_t dex_pc = shadow_frame->GetDexPC(); jit::Jit* jit = Runtime::Current()->GetJit(); if ((jit != nullptr) && (offset <= 0)) { - jit->GetInstrumentationCache()->AddSamples(self, method, 1); + jit->AddSamples(self, method, 1); } int16_t countdown_value = MterpSetUpHotnessCountdown(method, shadow_frame); if (countdown_value == jit::kJitCheckForOSR) { @@ -725,7 +722,7 @@ extern "C" bool MterpMaybeDoOnStackReplacement(Thread* self, jit::Jit* jit = Runtime::Current()->GetJit(); if (offset <= 0) { // Keep updating hotness in case a compilation request was dropped. Eventually it will retry. - jit->GetInstrumentationCache()->AddSamples(self, method, 1); + jit->AddSamples(self, method, 1); } // Assumes caller has already determined that an OSR check is appropriate. return jit::Jit::MaybeDoOnStackReplacement(self, method, dex_pc, offset, result); diff --git a/runtime/jit/jit.cc b/runtime/jit/jit.cc index 3344346f06..558e4435f0 100644 --- a/runtime/jit/jit.cc +++ b/runtime/jit/jit.cc @@ -23,7 +23,6 @@ #include "entrypoints/runtime_asm_entrypoints.h" #include "interpreter/interpreter.h" #include "jit_code_cache.h" -#include "jit_instrumentation.h" #include "oat_file_manager.h" #include "oat_quick_method_header.h" #include "offline_profiling_info.h" @@ -31,12 +30,15 @@ #include "runtime.h" #include "runtime_options.h" #include "stack_map.h" +#include "thread_list.h" #include "utils.h" namespace art { namespace jit { static constexpr bool kEnableOnStackReplacement = true; +// At what priority to schedule jit threads. 9 is the lowest foreground priority on device. +static constexpr int kJitPoolThreadPthreadPriority = 9; // JIT compiler void* Jit::jit_library_handle_= nullptr; @@ -146,6 +148,17 @@ Jit* Jit::Create(JitOptions* options, std::string* error_msg) { << ", max_capacity=" << PrettySize(options->GetCodeCacheMaxCapacity()) << ", compile_threshold=" << options->GetCompileThreshold() << ", save_profiling_info=" << options->GetSaveProfilingInfo(); + + + jit->hot_method_threshold_ = options->GetCompileThreshold(); + jit->warm_method_threshold_ = options->GetWarmupThreshold(); + jit->osr_method_threshold_ = options->GetOsrThreshold(); + jit->priority_thread_weight_ = options->GetPriorityThreadWeight(); + + jit->CreateThreadPool(); + + // Notify native debugger about the classes already loaded before the creation of the jit. + jit->DumpTypeInfoForLoadedTypes(Runtime::Current()->GetClassLinker()); return jit.release(); } @@ -233,13 +246,31 @@ bool Jit::CompileMethod(ArtMethod* method, Thread* self, bool osr) { } void Jit::CreateThreadPool() { - CHECK(instrumentation_cache_.get() != nullptr); - instrumentation_cache_->CreateThreadPool(); + // There is a DCHECK in the 'AddSamples' method to ensure the tread pool + // is not null when we instrument. + thread_pool_.reset(new ThreadPool("Jit thread pool", 1)); + thread_pool_->SetPthreadPriority(kJitPoolThreadPthreadPriority); + thread_pool_->StartWorkers(Thread::Current()); } void Jit::DeleteThreadPool() { - if (instrumentation_cache_.get() != nullptr) { - instrumentation_cache_->DeleteThreadPool(Thread::Current()); + Thread* self = Thread::Current(); + DCHECK(Runtime::Current()->IsShuttingDown(self)); + if (thread_pool_ != nullptr) { + ThreadPool* cache = nullptr; + { + ScopedSuspendAll ssa(__FUNCTION__); + // Clear thread_pool_ field while the threads are suspended. + // A mutator in the 'AddSamples' method will check against it. + cache = thread_pool_.release(); + } + cache->StopWorkers(self); + cache->RemoveAllTasks(self); + // We could just suspend all threads, but we know those threads + // will finish in a short period, so it's not worth adding a suspend logic + // here. Besides, this is only done for shutdown. + cache->Wait(self, false, false); + delete cache; } } @@ -259,10 +290,7 @@ void Jit::StopProfileSaver() { } bool Jit::JitAtFirstUse() { - if (instrumentation_cache_ != nullptr) { - return instrumentation_cache_->HotMethodThreshold() == 0; - } - return false; + return HotMethodThreshold() == 0; } bool Jit::CanInvokeCompiledCode(ArtMethod* method) { @@ -285,17 +313,6 @@ Jit::~Jit() { } } -void Jit::CreateInstrumentationCache(size_t compile_threshold, - size_t warmup_threshold, - size_t osr_threshold, - uint16_t priority_thread_weight) { - instrumentation_cache_.reset( - new jit::JitInstrumentationCache(compile_threshold, - warmup_threshold, - osr_threshold, - priority_thread_weight)); -} - void Jit::NewTypeLoadedIfUsingJit(mirror::Class* type) { jit::Jit* jit = Runtime::Current()->GetJit(); if (jit != nullptr && jit->generate_debug_info_) { @@ -480,5 +497,164 @@ void Jit::AddMemoryUsage(ArtMethod* method, size_t bytes) { memory_use_.AddValue(bytes); } +class JitCompileTask FINAL : public Task { + public: + enum TaskKind { + kAllocateProfile, + kCompile, + kCompileOsr + }; + + JitCompileTask(ArtMethod* method, TaskKind kind) : method_(method), kind_(kind) { + ScopedObjectAccess soa(Thread::Current()); + // Add a global ref to the class to prevent class unloading until compilation is done. + klass_ = soa.Vm()->AddGlobalRef(soa.Self(), method_->GetDeclaringClass()); + CHECK(klass_ != nullptr); + } + + ~JitCompileTask() { + ScopedObjectAccess soa(Thread::Current()); + soa.Vm()->DeleteGlobalRef(soa.Self(), klass_); + } + + void Run(Thread* self) OVERRIDE { + ScopedObjectAccess soa(self); + if (kind_ == kCompile) { + VLOG(jit) << "JitCompileTask compiling method " << PrettyMethod(method_); + if (!Runtime::Current()->GetJit()->CompileMethod(method_, self, /* osr */ false)) { + VLOG(jit) << "Failed to compile method " << PrettyMethod(method_); + } + } else if (kind_ == kCompileOsr) { + VLOG(jit) << "JitCompileTask compiling method osr " << PrettyMethod(method_); + if (!Runtime::Current()->GetJit()->CompileMethod(method_, self, /* osr */ true)) { + VLOG(jit) << "Failed to compile method osr " << PrettyMethod(method_); + } + } else { + DCHECK(kind_ == kAllocateProfile); + if (ProfilingInfo::Create(self, method_, /* retry_allocation */ true)) { + VLOG(jit) << "Start profiling " << PrettyMethod(method_); + } + } + } + + void Finalize() OVERRIDE { + delete this; + } + + private: + ArtMethod* const method_; + const TaskKind kind_; + jobject klass_; + + DISALLOW_IMPLICIT_CONSTRUCTORS(JitCompileTask); +}; + +void Jit::AddSamples(Thread* self, ArtMethod* method, uint16_t count) { + if (thread_pool_ == nullptr) { + // Should only see this when shutting down. + DCHECK(Runtime::Current()->IsShuttingDown(self)); + return; + } + + if (method->IsClassInitializer() || method->IsNative()) { + // We do not want to compile such methods. + return; + } + DCHECK(thread_pool_ != nullptr); + DCHECK_GT(warm_method_threshold_, 0); + DCHECK_GT(hot_method_threshold_, warm_method_threshold_); + DCHECK_GT(osr_method_threshold_, hot_method_threshold_); + DCHECK_GE(priority_thread_weight_, 1); + DCHECK_LE(priority_thread_weight_, hot_method_threshold_); + + int32_t starting_count = method->GetCounter(); + if (Jit::ShouldUsePriorityThreadWeight()) { + count *= priority_thread_weight_; + } + int32_t new_count = starting_count + count; // int32 here to avoid wrap-around; + if (starting_count < warm_method_threshold_) { + if (new_count >= warm_method_threshold_) { + bool success = ProfilingInfo::Create(self, method, /* retry_allocation */ false); + if (success) { + VLOG(jit) << "Start profiling " << PrettyMethod(method); + } + + if (thread_pool_ == nullptr) { + // Calling ProfilingInfo::Create might put us in a suspended state, which could + // lead to the thread pool being deleted when we are shutting down. + DCHECK(Runtime::Current()->IsShuttingDown(self)); + return; + } + + if (!success) { + // We failed allocating. Instead of doing the collection on the Java thread, we push + // an allocation to a compiler thread, that will do the collection. + thread_pool_->AddTask(self, new JitCompileTask(method, JitCompileTask::kAllocateProfile)); + } + } + // Avoid jumping more than one state at a time. + new_count = std::min(new_count, hot_method_threshold_ - 1); + } else if (starting_count < hot_method_threshold_) { + if (new_count >= hot_method_threshold_) { + DCHECK(thread_pool_ != nullptr); + thread_pool_->AddTask(self, new JitCompileTask(method, JitCompileTask::kCompile)); + } + // Avoid jumping more than one state at a time. + new_count = std::min(new_count, osr_method_threshold_ - 1); + } else if (starting_count < osr_method_threshold_) { + if (new_count >= osr_method_threshold_) { + DCHECK(thread_pool_ != nullptr); + thread_pool_->AddTask(self, new JitCompileTask(method, JitCompileTask::kCompileOsr)); + } + } + // Update hotness counter + method->SetCounter(new_count); +} + +void Jit::MethodEntered(Thread* thread, ArtMethod* method) { + if (UNLIKELY(Runtime::Current()->GetJit()->JitAtFirstUse())) { + // The compiler requires a ProfilingInfo object. + ProfilingInfo::Create(thread, method, /* retry_allocation */ true); + JitCompileTask compile_task(method, JitCompileTask::kCompile); + compile_task.Run(thread); + return; + } + + ProfilingInfo* profiling_info = method->GetProfilingInfo(sizeof(void*)); + // Update the entrypoint if the ProfilingInfo has one. The interpreter will call it + // instead of interpreting the method. + // We avoid doing this if exit stubs are installed to not mess with the instrumentation. + // TODO(ngeoffray): Clean up instrumentation and code cache interactions. + if ((profiling_info != nullptr) && + (profiling_info->GetSavedEntryPoint() != nullptr) && + !Runtime::Current()->GetInstrumentation()->AreExitStubsInstalled()) { + method->SetEntryPointFromQuickCompiledCode(profiling_info->GetSavedEntryPoint()); + } else { + AddSamples(thread, method, 1); + } +} + +void Jit::InvokeVirtualOrInterface(Thread* thread, + mirror::Object* this_object, + ArtMethod* caller, + uint32_t dex_pc, + ArtMethod* callee ATTRIBUTE_UNUSED) { + ScopedAssertNoThreadSuspension ants(thread, __FUNCTION__); + DCHECK(this_object != nullptr); + ProfilingInfo* info = caller->GetProfilingInfo(sizeof(void*)); + if (info != nullptr) { + // Since the instrumentation is marked from the declaring class we need to mark the card so + // that mod-union tables and card rescanning know about the update. + Runtime::Current()->GetHeap()->WriteBarrierEveryFieldOf(caller->GetDeclaringClass()); + info->AddInvokeInfo(dex_pc, this_object->GetClass()); + } +} + +void Jit::WaitForCompilationToFinish(Thread* self) { + if (thread_pool_ != nullptr) { + thread_pool_->Wait(self, false, false); + } +} + } // namespace jit } // namespace art diff --git a/runtime/jit/jit.h b/runtime/jit/jit.h index e2123666f9..96f9608a94 100644 --- a/runtime/jit/jit.h +++ b/runtime/jit/jit.h @@ -34,9 +34,11 @@ struct RuntimeArgumentMap; namespace jit { class JitCodeCache; -class JitInstrumentationCache; class JitOptions; +static constexpr int16_t kJitCheckForOSR = -1; +static constexpr int16_t kJitHotnessDisabled = -2; + class Jit { public: static constexpr bool kStressMode = kIsDebugBuild; @@ -46,17 +48,16 @@ class Jit { static Jit* Create(JitOptions* options, std::string* error_msg); bool CompileMethod(ArtMethod* method, Thread* self, bool osr) SHARED_REQUIRES(Locks::mutator_lock_); - void CreateInstrumentationCache(size_t compile_threshold, - size_t warmup_threshold, - size_t osr_threshold, - uint16_t priority_thread_weight); void CreateThreadPool(); + const JitCodeCache* GetCodeCache() const { return code_cache_.get(); } + JitCodeCache* GetCodeCache() { return code_cache_.get(); } + void DeleteThreadPool(); // Dump interesting info: #methods compiled, code vs data size, compile / verify cumulative // loggers. @@ -68,10 +69,39 @@ class Jit { REQUIRES(!lock_) SHARED_REQUIRES(Locks::mutator_lock_); - JitInstrumentationCache* GetInstrumentationCache() const { - return instrumentation_cache_.get(); + size_t OSRMethodThreshold() const { + return osr_method_threshold_; + } + + size_t HotMethodThreshold() const { + return hot_method_threshold_; + } + + size_t WarmMethodThreshold() const { + return warm_method_threshold_; } + uint16_t PriorityThreadWeight() const { + return priority_thread_weight_; + } + + // Wait until there is no more pending compilation tasks. + void WaitForCompilationToFinish(Thread* self); + + // Profiling methods. + void MethodEntered(Thread* thread, ArtMethod* method) + SHARED_REQUIRES(Locks::mutator_lock_); + + void AddSamples(Thread* self, ArtMethod* method, uint16_t samples) + SHARED_REQUIRES(Locks::mutator_lock_); + + void InvokeVirtualOrInterface(Thread* thread, + mirror::Object* this_object, + ArtMethod* caller, + uint32_t dex_pc, + ArtMethod* callee) + SHARED_REQUIRES(Locks::mutator_lock_); + // Starts the profile saver if the config options allow profile recording. // The profile will be stored in the specified `filename` and will contain // information collected from the given `code_paths` (a set of dex locations). @@ -137,11 +167,15 @@ class Jit { Histogram<uint64_t> memory_use_ GUARDED_BY(lock_); Mutex lock_ DEFAULT_MUTEX_ACQUIRED_AFTER; - std::unique_ptr<jit::JitInstrumentationCache> instrumentation_cache_; std::unique_ptr<jit::JitCodeCache> code_cache_; bool save_profiling_info_; static bool generate_debug_info_; + uint16_t hot_method_threshold_; + uint16_t warm_method_threshold_; + uint16_t osr_method_threshold_; + uint16_t priority_thread_weight_; + std::unique_ptr<ThreadPool> thread_pool_; DISALLOW_COPY_AND_ASSIGN(Jit); }; diff --git a/runtime/jit/jit_instrumentation.cc b/runtime/jit/jit_instrumentation.cc deleted file mode 100644 index b2c0c20237..0000000000 --- a/runtime/jit/jit_instrumentation.cc +++ /dev/null @@ -1,262 +0,0 @@ -/* - * Copyright 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 "jit_instrumentation.h" - -#include "art_method-inl.h" -#include "jit.h" -#include "jit_code_cache.h" -#include "scoped_thread_state_change.h" -#include "thread_list.h" - -namespace art { -namespace jit { - -// At what priority to schedule jit threads. 9 is the lowest foreground priority on device. -static constexpr int kJitPoolThreadPthreadPriority = 9; - -class JitCompileTask FINAL : public Task { - public: - enum TaskKind { - kAllocateProfile, - kCompile, - kCompileOsr - }; - - JitCompileTask(ArtMethod* method, TaskKind kind) : method_(method), kind_(kind) { - ScopedObjectAccess soa(Thread::Current()); - // Add a global ref to the class to prevent class unloading until compilation is done. - klass_ = soa.Vm()->AddGlobalRef(soa.Self(), method_->GetDeclaringClass()); - CHECK(klass_ != nullptr); - } - - ~JitCompileTask() { - ScopedObjectAccess soa(Thread::Current()); - soa.Vm()->DeleteGlobalRef(soa.Self(), klass_); - } - - void Run(Thread* self) OVERRIDE { - ScopedObjectAccess soa(self); - if (kind_ == kCompile) { - VLOG(jit) << "JitCompileTask compiling method " << PrettyMethod(method_); - if (!Runtime::Current()->GetJit()->CompileMethod(method_, self, /* osr */ false)) { - VLOG(jit) << "Failed to compile method " << PrettyMethod(method_); - } - } else if (kind_ == kCompileOsr) { - VLOG(jit) << "JitCompileTask compiling method osr " << PrettyMethod(method_); - if (!Runtime::Current()->GetJit()->CompileMethod(method_, self, /* osr */ true)) { - VLOG(jit) << "Failed to compile method osr " << PrettyMethod(method_); - } - } else { - DCHECK(kind_ == kAllocateProfile); - if (ProfilingInfo::Create(self, method_, /* retry_allocation */ true)) { - VLOG(jit) << "Start profiling " << PrettyMethod(method_); - } - } - } - - void Finalize() OVERRIDE { - delete this; - } - - private: - ArtMethod* const method_; - const TaskKind kind_; - jobject klass_; - - DISALLOW_IMPLICIT_CONSTRUCTORS(JitCompileTask); -}; - -JitInstrumentationCache::JitInstrumentationCache(uint16_t hot_method_threshold, - uint16_t warm_method_threshold, - uint16_t osr_method_threshold, - uint16_t priority_thread_weight) - : hot_method_threshold_(hot_method_threshold), - warm_method_threshold_(warm_method_threshold), - osr_method_threshold_(osr_method_threshold), - priority_thread_weight_(priority_thread_weight), - listener_(this) { -} - -void JitInstrumentationCache::CreateThreadPool() { - // Create the thread pool before setting the instrumentation, so that - // when the threads stopped being suspended, they can use it directly. - // There is a DCHECK in the 'AddSamples' method to ensure the tread pool - // is not null when we instrument. - thread_pool_.reset(new ThreadPool("Jit thread pool", 1)); - thread_pool_->SetPthreadPriority(kJitPoolThreadPthreadPriority); - thread_pool_->StartWorkers(Thread::Current()); - { - // Add Jit interpreter instrumentation, tells the interpreter when - // to notify the jit to compile something. - ScopedSuspendAll ssa(__FUNCTION__); - Runtime::Current()->GetInstrumentation()->AddListener( - &listener_, JitInstrumentationListener::kJitEvents); - } -} - -void JitInstrumentationCache::DeleteThreadPool(Thread* self) { - DCHECK(Runtime::Current()->IsShuttingDown(self)); - if (thread_pool_ != nullptr) { - // First remove the listener, to avoid having mutators enter - // 'AddSamples'. - ThreadPool* cache = nullptr; - { - ScopedSuspendAll ssa(__FUNCTION__); - Runtime::Current()->GetInstrumentation()->RemoveListener( - &listener_, JitInstrumentationListener::kJitEvents); - // Clear thread_pool_ field while the threads are suspended. - // A mutator in the 'AddSamples' method will check against it. - cache = thread_pool_.release(); - } - cache->StopWorkers(self); - cache->RemoveAllTasks(self); - // We could just suspend all threads, but we know those threads - // will finish in a short period, so it's not worth adding a suspend logic - // here. Besides, this is only done for shutdown. - cache->Wait(self, false, false); - delete cache; - } -} - -void JitInstrumentationCache::AddSamples(Thread* self, ArtMethod* method, uint16_t count) { - // Since we don't have on-stack replacement, some methods can remain in the interpreter longer - // than we want resulting in samples even after the method is compiled. Also, if the - // jit is no longer interested in hotness samples because we're shutting down, just return. - if (method->IsClassInitializer() || method->IsNative() || (thread_pool_ == nullptr)) { - if (thread_pool_ == nullptr) { - // Should only see this when shutting down. - DCHECK(Runtime::Current()->IsShuttingDown(self)); - } - return; - } - DCHECK(thread_pool_ != nullptr); - DCHECK_GT(warm_method_threshold_, 0); - DCHECK_GT(hot_method_threshold_, warm_method_threshold_); - DCHECK_GT(osr_method_threshold_, hot_method_threshold_); - DCHECK_GE(priority_thread_weight_, 1); - DCHECK_LE(priority_thread_weight_, hot_method_threshold_); - - int32_t starting_count = method->GetCounter(); - if (Jit::ShouldUsePriorityThreadWeight()) { - count *= priority_thread_weight_; - } - int32_t new_count = starting_count + count; // int32 here to avoid wrap-around; - if (starting_count < warm_method_threshold_) { - if (new_count >= warm_method_threshold_) { - bool success = ProfilingInfo::Create(self, method, /* retry_allocation */ false); - if (success) { - VLOG(jit) << "Start profiling " << PrettyMethod(method); - } - - if (thread_pool_ == nullptr) { - // Calling ProfilingInfo::Create might put us in a suspended state, which could - // lead to the thread pool being deleted when we are shutting down. - DCHECK(Runtime::Current()->IsShuttingDown(self)); - return; - } - - if (!success) { - // We failed allocating. Instead of doing the collection on the Java thread, we push - // an allocation to a compiler thread, that will do the collection. - thread_pool_->AddTask(self, new JitCompileTask(method, JitCompileTask::kAllocateProfile)); - } - } - // Avoid jumping more than one state at a time. - new_count = std::min(new_count, hot_method_threshold_ - 1); - } else if (starting_count < hot_method_threshold_) { - if (new_count >= hot_method_threshold_) { - DCHECK(thread_pool_ != nullptr); - thread_pool_->AddTask(self, new JitCompileTask(method, JitCompileTask::kCompile)); - } - // Avoid jumping more than one state at a time. - new_count = std::min(new_count, osr_method_threshold_ - 1); - } else if (starting_count < osr_method_threshold_) { - if (new_count >= osr_method_threshold_) { - DCHECK(thread_pool_ != nullptr); - thread_pool_->AddTask(self, new JitCompileTask(method, JitCompileTask::kCompileOsr)); - } - } - // Update hotness counter - method->SetCounter(new_count); -} - -JitInstrumentationListener::JitInstrumentationListener(JitInstrumentationCache* cache) - : instrumentation_cache_(cache) { - CHECK(instrumentation_cache_ != nullptr); -} - -void JitInstrumentationListener::MethodEntered(Thread* thread, - mirror::Object* /*this_object*/, - ArtMethod* method, - uint32_t /*dex_pc*/) { - if (UNLIKELY(Runtime::Current()->GetJit()->JitAtFirstUse())) { - // The compiler requires a ProfilingInfo object. - ProfilingInfo::Create(thread, method, /* retry_allocation */ true); - JitCompileTask compile_task(method, JitCompileTask::kCompile); - compile_task.Run(thread); - return; - } - - ProfilingInfo* profiling_info = method->GetProfilingInfo(sizeof(void*)); - // Update the entrypoint if the ProfilingInfo has one. The interpreter will call it - // instead of interpreting the method. - // We avoid doing this if exit stubs are installed to not mess with the instrumentation. - // TODO(ngeoffray): Clean up instrumentation and code cache interactions. - if ((profiling_info != nullptr) && - (profiling_info->GetSavedEntryPoint() != nullptr) && - !Runtime::Current()->GetInstrumentation()->AreExitStubsInstalled()) { - method->SetEntryPointFromQuickCompiledCode(profiling_info->GetSavedEntryPoint()); - } else { - instrumentation_cache_->AddSamples(thread, method, 1); - } -} - -void JitInstrumentationListener::Branch(Thread* thread, - ArtMethod* method, - uint32_t dex_pc ATTRIBUTE_UNUSED, - int32_t dex_pc_offset) { - if (dex_pc_offset < 0) { - // Increment method hotness if it is a backward branch. - instrumentation_cache_->AddSamples(thread, method, 1); - } -} - -void JitInstrumentationListener::InvokeVirtualOrInterface(Thread* thread, - mirror::Object* this_object, - ArtMethod* caller, - uint32_t dex_pc, - ArtMethod* callee ATTRIBUTE_UNUSED) { - // We make sure we cannot be suspended, as the profiling info can be concurrently deleted. - instrumentation_cache_->AddSamples(thread, caller, 1); - DCHECK(this_object != nullptr); - ProfilingInfo* info = caller->GetProfilingInfo(sizeof(void*)); - if (info != nullptr) { - // Since the instrumentation is marked from the declaring class we need to mark the card so - // that mod-union tables and card rescanning know about the update. - Runtime::Current()->GetHeap()->WriteBarrierEveryFieldOf(caller->GetDeclaringClass()); - info->AddInvokeInfo(dex_pc, this_object->GetClass()); - } -} - -void JitInstrumentationCache::WaitForCompilationToFinish(Thread* self) { - if (thread_pool_ != nullptr) { - thread_pool_->Wait(self, false, false); - } -} - -} // namespace jit -} // namespace art diff --git a/runtime/jit/jit_instrumentation.h b/runtime/jit/jit_instrumentation.h deleted file mode 100644 index d0545f8e24..0000000000 --- a/runtime/jit/jit_instrumentation.h +++ /dev/null @@ -1,142 +0,0 @@ -/* - * Copyright 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_RUNTIME_JIT_JIT_INSTRUMENTATION_H_ -#define ART_RUNTIME_JIT_JIT_INSTRUMENTATION_H_ - -#include <unordered_map> - -#include "instrumentation.h" - -#include "atomic.h" -#include "base/macros.h" -#include "base/mutex.h" -#include "gc_root.h" -#include "jni.h" -#include "object_callbacks.h" -#include "thread_pool.h" - -namespace art { -namespace mirror { - class Object; - class Throwable; -} // namespace mirror -class ArtField; -class ArtMethod; -union JValue; -class Thread; - -namespace jit { -static constexpr int16_t kJitCheckForOSR = -1; -static constexpr int16_t kJitHotnessDisabled = -2; - -class JitInstrumentationCache; - -class JitInstrumentationListener : public instrumentation::InstrumentationListener { - public: - explicit JitInstrumentationListener(JitInstrumentationCache* cache); - - void MethodEntered(Thread* thread, mirror::Object* /*this_object*/, - ArtMethod* method, uint32_t /*dex_pc*/) - OVERRIDE SHARED_REQUIRES(Locks::mutator_lock_); - - void MethodExited(Thread* /*thread*/, mirror::Object* /*this_object*/, - ArtMethod* /*method*/, uint32_t /*dex_pc*/, - const JValue& /*return_value*/) - OVERRIDE { } - void MethodUnwind(Thread* /*thread*/, mirror::Object* /*this_object*/, - ArtMethod* /*method*/, uint32_t /*dex_pc*/) OVERRIDE { } - void FieldRead(Thread* /*thread*/, mirror::Object* /*this_object*/, - ArtMethod* /*method*/, uint32_t /*dex_pc*/, - ArtField* /*field*/) OVERRIDE { } - void FieldWritten(Thread* /*thread*/, mirror::Object* /*this_object*/, - ArtMethod* /*method*/, uint32_t /*dex_pc*/, - ArtField* /*field*/, const JValue& /*field_value*/) - OVERRIDE { } - void ExceptionCaught(Thread* /*thread*/, - mirror::Throwable* /*exception_object*/) OVERRIDE { } - - void DexPcMoved(Thread* /*self*/, mirror::Object* /*this_object*/, - ArtMethod* /*method*/, uint32_t /*new_dex_pc*/) OVERRIDE { } - - void Branch(Thread* thread, ArtMethod* method, uint32_t dex_pc, int32_t dex_pc_offset) - OVERRIDE SHARED_REQUIRES(Locks::mutator_lock_); - - void InvokeVirtualOrInterface(Thread* thread, - mirror::Object* this_object, - ArtMethod* caller, - uint32_t dex_pc, - ArtMethod* callee) - OVERRIDE - REQUIRES(Roles::uninterruptible_) - SHARED_REQUIRES(Locks::mutator_lock_); - - static constexpr uint32_t kJitEvents = - instrumentation::Instrumentation::kMethodEntered | - instrumentation::Instrumentation::kInvokeVirtualOrInterface; - - private: - JitInstrumentationCache* const instrumentation_cache_; - - DISALLOW_IMPLICIT_CONSTRUCTORS(JitInstrumentationListener); -}; - -// Keeps track of which methods are hot. -class JitInstrumentationCache { - public: - JitInstrumentationCache(uint16_t hot_method_threshold, - uint16_t warm_method_threshold, - uint16_t osr_method_threshold, - uint16_t priority_thread_weight); - void AddSamples(Thread* self, ArtMethod* method, uint16_t samples) - SHARED_REQUIRES(Locks::mutator_lock_); - void CreateThreadPool(); - void DeleteThreadPool(Thread* self); - - size_t OSRMethodThreshold() const { - return osr_method_threshold_; - } - - size_t HotMethodThreshold() const { - return hot_method_threshold_; - } - - size_t WarmMethodThreshold() const { - return warm_method_threshold_; - } - - size_t PriorityThreadWeight() const { - return priority_thread_weight_; - } - - // Wait until there is no more pending compilation tasks. - void WaitForCompilationToFinish(Thread* self); - - private: - uint16_t hot_method_threshold_; - uint16_t warm_method_threshold_; - uint16_t osr_method_threshold_; - uint16_t priority_thread_weight_; - JitInstrumentationListener listener_; - std::unique_ptr<ThreadPool> thread_pool_; - - DISALLOW_IMPLICIT_CONSTRUCTORS(JitInstrumentationCache); -}; - -} // namespace jit -} // namespace art - -#endif // ART_RUNTIME_JIT_JIT_INSTRUMENTATION_H_ diff --git a/runtime/runtime.cc b/runtime/runtime.cc index 37bb4c1fc1..2489e45e47 100644 --- a/runtime/runtime.cc +++ b/runtime/runtime.cc @@ -1921,16 +1921,7 @@ void Runtime::CreateJit() { } std::string error_msg; jit_.reset(jit::Jit::Create(jit_options_.get(), &error_msg)); - if (jit_.get() != nullptr) { - jit_->CreateInstrumentationCache(jit_options_->GetCompileThreshold(), - jit_options_->GetWarmupThreshold(), - jit_options_->GetOsrThreshold(), - jit_options_->GetPriorityThreadWeight()); - jit_->CreateThreadPool(); - - // Notify native debugger about the classes already loaded before the creation of the jit. - jit_->DumpTypeInfoForLoadedTypes(GetClassLinker()); - } else { + if (jit_.get() == nullptr) { LOG(WARNING) << "Failed to create JIT " << error_msg; } } diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc index 83da6b7b4e..d5319fdb0c 100644 --- a/runtime/verifier/method_verifier.cc +++ b/runtime/verifier/method_verifier.cc @@ -790,9 +790,16 @@ bool MethodVerifier::Verify() { } else if (method_access_flags_ & kAccFinal) { Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "interfaces may not have final methods"; return false; - } else if (!(method_access_flags_ & kAccPublic)) { - Fail(VERIFY_ERROR_BAD_CLASS_HARD) << "interfaces may not have non-public members"; - return false; + } else { + uint32_t access_flag_options = kAccPublic; + if (dex_file_->GetVersion() >= DexFile::kDefaultMethodsVersion) { + access_flag_options |= kAccPrivate; + } + if (!(method_access_flags_ & access_flag_options)) { + Fail(VERIFY_ERROR_BAD_CLASS_HARD) + << "interfaces may not have protected or package-private members"; + return false; + } } } } @@ -3794,9 +3801,12 @@ ArtMethod* MethodVerifier::ResolveMethodAndCheckAccess( // Note: this check must be after the initializer check, as those are required to fail a class, // while this check implies an IncompatibleClassChangeError. if (klass->IsInterface()) { - // methods called on interfaces should be invoke-interface, invoke-super, or invoke-static. + // methods called on interfaces should be invoke-interface, invoke-super, invoke-direct (if + // dex file version is 37 or greater), or invoke-static. if (method_type != METHOD_INTERFACE && method_type != METHOD_STATIC && + ((dex_file_->GetVersion() < DexFile::kDefaultMethodsVersion) || + method_type != METHOD_DIRECT) && method_type != METHOD_SUPER) { Fail(VERIFY_ERROR_CLASS_CHANGE) << "non-interface method " << PrettyMethod(dex_method_idx, *dex_file_) diff --git a/test/141-class-unload/jni_unload.cc b/test/141-class-unload/jni_unload.cc index d913efe53e..bbbb0a6036 100644 --- a/test/141-class-unload/jni_unload.cc +++ b/test/141-class-unload/jni_unload.cc @@ -19,7 +19,6 @@ #include <iostream> #include "jit/jit.h" -#include "jit/jit_instrumentation.h" #include "runtime.h" #include "thread-inl.h" @@ -29,7 +28,7 @@ namespace { extern "C" JNIEXPORT void JNICALL Java_IntHolder_waitForCompilation(JNIEnv*, jclass) { jit::Jit* jit = Runtime::Current()->GetJit(); if (jit != nullptr) { - jit->GetInstrumentationCache()->WaitForCompilationToFinish(Thread::Current()); + jit->WaitForCompilationToFinish(Thread::Current()); } } diff --git a/test/525-checker-arrays-and-fields/expected.txt b/test/525-checker-arrays-and-fields/expected.txt index b0aad4deb5..e69de29bb2 100644 --- a/test/525-checker-arrays-and-fields/expected.txt +++ b/test/525-checker-arrays-and-fields/expected.txt @@ -1 +0,0 @@ -passed diff --git a/test/525-checker-arrays-and-fields/src/Main.java b/test/525-checker-arrays-and-fields/src/Main.java index b3fbf44751..a635a5157f 100644 --- a/test/525-checker-arrays-and-fields/src/Main.java +++ b/test/525-checker-arrays-and-fields/src/Main.java @@ -759,87 +759,12 @@ public class Main { } // - // Misc. tests on false cross-over loops with data types (I/F and J/D) that used - // to be aliased in an older version of the compiler. This alias has been removed, - // however, which enables hoisting the invariant array reference. - // - - /// CHECK-START: void Main.FalseCrossOverLoop1() licm (before) - /// CHECK-DAG: ArraySet loop:none - /// CHECK-DAG: ArrayGet loop:{{B\d+}} - /// CHECK-DAG: ArraySet loop:{{B\d+}} - - /// CHECK-START: void Main.FalseCrossOverLoop1() licm (after) - /// CHECK-DAG: ArraySet loop:none - /// CHECK-DAG: ArrayGet loop:none - /// CHECK-DAG: ArraySet loop:{{B\d+}} - - private static void FalseCrossOverLoop1() { - sArrF[20] = -1; - for (int i = 0; i < sArrI.length; i++) { - sArrI[i] = (int) sArrF[20] - 2; - } - } - - /// CHECK-START: void Main.FalseCrossOverLoop2() licm (before) - /// CHECK-DAG: ArraySet loop:none - /// CHECK-DAG: ArrayGet loop:{{B\d+}} - /// CHECK-DAG: ArraySet loop:{{B\d+}} - - /// CHECK-START: void Main.FalseCrossOverLoop2() licm (after) - /// CHECK-DAG: ArraySet loop:none - /// CHECK-DAG: ArrayGet loop:none - /// CHECK-DAG: ArraySet loop:{{B\d+}} - - private static void FalseCrossOverLoop2() { - sArrI[20] = -2; - for (int i = 0; i < sArrF.length; i++) { - sArrF[i] = sArrI[20] - 2; - } - } - - /// CHECK-START: void Main.FalseCrossOverLoop3() licm (before) - /// CHECK-DAG: ArraySet loop:none - /// CHECK-DAG: ArrayGet loop:{{B\d+}} - /// CHECK-DAG: ArraySet loop:{{B\d+}} - - /// CHECK-START: void Main.FalseCrossOverLoop3() licm (after) - /// CHECK-DAG: ArraySet loop:none - /// CHECK-DAG: ArrayGet loop:none - /// CHECK-DAG: ArraySet loop:{{B\d+}} - - private static void FalseCrossOverLoop3() { - sArrD[20] = -3; - for (int i = 0; i < sArrJ.length; i++) { - sArrJ[i] = (long) sArrD[20] - 2; - } - } - - /// CHECK-START: void Main.FalseCrossOverLoop4() licm (before) - /// CHECK-DAG: ArraySet loop:none - /// CHECK-DAG: ArrayGet loop:{{B\d+}} - /// CHECK-DAG: ArraySet loop:{{B\d+}} - - /// CHECK-START: void Main.FalseCrossOverLoop4() licm (after) - /// CHECK-DAG: ArraySet loop:none - /// CHECK-DAG: ArrayGet loop:none - /// CHECK-DAG: ArraySet loop:{{B\d+}} - - private static void FalseCrossOverLoop4() { - sArrJ[20] = -4; - for (int i = 0; i < sArrD.length; i++) { - sArrD[i] = sArrJ[20] - 2; - } - } - - // // Driver and testers. // public static void main(String[] args) { DoStaticTests(); new Main().DoInstanceTests(); - System.out.println("passed"); } private static void DoStaticTests() { @@ -978,23 +903,6 @@ public class Main { for (int i = 0; i < sArrL.length; i++) { expectEquals(i <= 20 ? anObject : anotherObject, sArrL[i]); } - // Misc. tests. - FalseCrossOverLoop1(); - for (int i = 0; i < sArrI.length; i++) { - expectEquals(-3, sArrI[i]); - } - FalseCrossOverLoop2(); - for (int i = 0; i < sArrF.length; i++) { - expectEquals(-4, sArrF[i]); - } - FalseCrossOverLoop3(); - for (int i = 0; i < sArrJ.length; i++) { - expectEquals(-5, sArrJ[i]); - } - FalseCrossOverLoop4(); - for (int i = 0; i < sArrD.length; i++) { - expectEquals(-6, sArrD[i]); - } } private void DoInstanceTests() { diff --git a/test/594-checker-regression-irreducible-linorder/expected.txt b/test/594-checker-regression-irreducible-linorder/expected.txt new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/test/594-checker-regression-irreducible-linorder/expected.txt diff --git a/test/594-checker-regression-irreducible-linorder/info.txt b/test/594-checker-regression-irreducible-linorder/info.txt new file mode 100644 index 0000000000..a1783f8ec1 --- /dev/null +++ b/test/594-checker-regression-irreducible-linorder/info.txt @@ -0,0 +1,2 @@ +Regression test for a failing DCHECK in SSA liveness analysis in the presence +of irreducible loops. diff --git a/test/594-checker-regression-irreducible-linorder/smali/IrreducibleLoop.smali b/test/594-checker-regression-irreducible-linorder/smali/IrreducibleLoop.smali new file mode 100644 index 0000000000..8e01084841 --- /dev/null +++ b/test/594-checker-regression-irreducible-linorder/smali/IrreducibleLoop.smali @@ -0,0 +1,64 @@ +# Copyright (C) 2016 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. + +.class public LIrreducibleLoop; +.super Ljava/lang/Object; + +# Test case where liveness analysis produces linear order where loop blocks are +# not adjacent. + +## CHECK-START: int IrreducibleLoop.liveness(boolean, boolean, boolean, int) builder (after) +## CHECK-DAG: Add loop:none +## CHECK-DAG: Mul loop:<<Loop:B\d+>> +## CHECK-DAG: Not loop:<<Loop>> + +## CHECK-START: int IrreducibleLoop.liveness(boolean, boolean, boolean, int) liveness (after) +## CHECK-DAG: Add liveness:<<LPreEntry:\d+>> +## CHECK-DAG: Mul liveness:<<LHeader:\d+>> +## CHECK-DAG: Not liveness:<<LBackEdge:\d+>> +## CHECK-EVAL: (<<LHeader>> < <<LPreEntry>>) and (<<LPreEntry>> < <<LBackEdge>>) + +.method public static liveness(ZZZI)I + .registers 10 + const/16 v0, 42 + + if-eqz p0, :header + + :pre_entry + add-int/2addr p3, p3 + invoke-static {v0}, Ljava/lang/System;->exit(I)V + goto :body1 + + :header + mul-int/2addr p3, p3 + if-eqz p1, :body2 + + :body1 + goto :body_merge + + :body2 + invoke-static {v0}, Ljava/lang/System;->exit(I)V + goto :body_merge + + :body_merge + if-eqz p2, :exit + + :back_edge + not-int p3, p3 + goto :header + + :exit + return p3 + +.end method diff --git a/test/594-checker-regression-irreducible-linorder/src/Main.java b/test/594-checker-regression-irreducible-linorder/src/Main.java new file mode 100644 index 0000000000..38b2ab4384 --- /dev/null +++ b/test/594-checker-regression-irreducible-linorder/src/Main.java @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2016 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 Main { + // Workaround for b/18051191. + class InnerClass {} + + public static void main(String[] args) { + // Nothing to run. This regression test merely makes sure the smali test + // case successfully compiles. + } +} diff --git a/test/955-lambda-smali/build b/test/955-lambda-smali/build new file mode 100755 index 0000000000..14230c2e1d --- /dev/null +++ b/test/955-lambda-smali/build @@ -0,0 +1,20 @@ +#!/bin/bash +# +# Copyright 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. + +# make us exit on a failure +set -e + +./default-build "$@" --experimental default-methods diff --git a/test/975-iface-private/build b/test/975-iface-private/build new file mode 100755 index 0000000000..14230c2e1d --- /dev/null +++ b/test/975-iface-private/build @@ -0,0 +1,20 @@ +#!/bin/bash +# +# Copyright 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. + +# make us exit on a failure +set -e + +./default-build "$@" --experimental default-methods diff --git a/test/975-iface-private/expected.txt b/test/975-iface-private/expected.txt new file mode 100644 index 0000000000..908a8f2131 --- /dev/null +++ b/test/975-iface-private/expected.txt @@ -0,0 +1,4 @@ +Saying hi from class +HELLO! +Saying hi from interface +HELLO! diff --git a/test/975-iface-private/info.txt b/test/975-iface-private/info.txt new file mode 100644 index 0000000000..d5a8d3f6d5 --- /dev/null +++ b/test/975-iface-private/info.txt @@ -0,0 +1,5 @@ +Smali-based tests for experimental interface private methods. + +This test cannot be run with --jvm. + +This test checks that synthetic private methods in interfaces work correctly. diff --git a/test/975-iface-private/smali/Iface.smali b/test/975-iface-private/smali/Iface.smali new file mode 100644 index 0000000000..a9a44d1bf4 --- /dev/null +++ b/test/975-iface-private/smali/Iface.smali @@ -0,0 +1,45 @@ + +# /* +# * 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 interface Iface { +# public default void sayHi() { +# System.out.println(getHiWords()); +# } +# +# // Synthetic method +# private String getHiWords() { +# return "HELLO!"; +# } +# } + +.class public abstract interface LIface; +.super Ljava/lang/Object; + +.method public sayHi()V + .locals 2 + sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream; + invoke-direct {p0}, LIface;->getHiWords()Ljava/lang/String; + move-result-object v1 + invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V + return-void +.end method + +.method private synthetic getHiWords()Ljava/lang/String; + .locals 1 + const-string v0, "HELLO!" + return-object v0 +.end method diff --git a/test/975-iface-private/smali/Main.smali b/test/975-iface-private/smali/Main.smali new file mode 100644 index 0000000000..dbde20362a --- /dev/null +++ b/test/975-iface-private/smali/Main.smali @@ -0,0 +1,71 @@ +# /* +# * 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. +# */ +# +# class Main implements Iface { +# public static void main(String[] args) { +# Main m = new Main(); +# sayHiMain(m); +# sayHiIface(m); +# } +# public static void sayHiMain(Main m) { +# System.out.println("Saying hi from class"); +# m.sayHi(); +# } +# public static void sayHiIface(Iface m) { +# System.out.println("Saying hi from interface"); +# m.sayHi(); +# } +# } +.class public LMain; +.super Ljava/lang/Object; +.implements LIface; + +.method public constructor <init>()V + .registers 1 + invoke-direct {p0}, Ljava/lang/Object;-><init>()V + return-void +.end method + +.method public static main([Ljava/lang/String;)V + .locals 2 + new-instance v0, LMain; + invoke-direct {v0}, LMain;-><init>()V + + invoke-static {v0}, LMain;->sayHiMain(LMain;)V + invoke-static {v0}, LMain;->sayHiIface(LIface;)V + + return-void +.end method + +.method public static sayHiMain(LMain;)V + .locals 2 + sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream; + const-string v1, "Saying hi from class" + invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V + + invoke-virtual {p0}, LMain;->sayHi()V + return-void +.end method + +.method public static sayHiIface(LIface;)V + .locals 2 + sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream; + const-string v1, "Saying hi from interface" + invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(Ljava/lang/Object;)V + + invoke-interface {p0}, LIface;->sayHi()V + return-void +.end method diff --git a/test/etc/default-build b/test/etc/default-build index 3d84821bf0..962ae38041 100755 --- a/test/etc/default-build +++ b/test/etc/default-build @@ -69,10 +69,13 @@ declare -A JACK_EXPERIMENTAL_ARGS JACK_EXPERIMENTAL_ARGS["default-methods"]="-D jack.java.source.version=1.8 -D jack.android.min-api-level=24" JACK_EXPERIMENTAL_ARGS["lambdas"]="-D jack.java.source.version=1.8 -D jack.android.min-api-level=24" +declare -A SMALI_EXPERIMENTAL_ARGS +SMALI_EXPERIMENTAL_ARGS["default-methods"]="--api-level 24" + while true; do if [ "x$1" = "x--dx-option" ]; then shift - option="$1" + on="$1" DX_FLAGS="${DX_FLAGS} $option" shift elif [ "x$1" = "x--jvm" ]; then @@ -110,6 +113,7 @@ done # Add args from the experimental mappings. for experiment in ${EXPERIMENTAL}; do JACK_ARGS="${JACK_ARGS} ${JACK_EXPERIMENTAL_ARGS[${experiment}]}" + SMALI_ARGS="${SMALI_ARGS} ${SMALI_EXPERIMENTAL_ARGS[${experiment}]}" done if [ -e classes.dex ]; then diff --git a/test/run-test b/test/run-test index d9e03b0eb5..fc57d0914f 100755 --- a/test/run-test +++ b/test/run-test @@ -46,7 +46,7 @@ export RUN="${progdir}/etc/run-test-jar" export DEX_LOCATION=/data/run-test/${test_dir} export NEED_DEX="true" export USE_JACK="true" -export SMALI_ARGS="--experimental --api-level 23" +export SMALI_ARGS="--experimental" # If dx was not set by the environment variable, assume it is in the path. if [ -z "$DX" ]; then diff --git a/tools/run-jdwp-tests.sh b/tools/run-jdwp-tests.sh index 8422e20823..354fcefe87 100755 --- a/tools/run-jdwp-tests.sh +++ b/tools/run-jdwp-tests.sh @@ -114,9 +114,6 @@ if [[ $verbose == "yes" ]]; then art_debugee="$art_debugee -verbose:jdwp" fi -# Use Jack with "1.8" configuration. -export JACK_VERSION=`basename prebuilts/sdk/tools/jacks/*ALPHA* | sed 's/^jack-//' | sed 's/.jar$//'` - # Run the tests using vogar. vogar $vm_command \ $vm_args \ diff --git a/tools/run-libcore-tests.sh b/tools/run-libcore-tests.sh index 45fb4b4dec..00bb3c5ff0 100755 --- a/tools/run-libcore-tests.sh +++ b/tools/run-libcore-tests.sh @@ -109,7 +109,6 @@ done vogar_args="$vogar_args --timeout 480" # Use Jack with "1.8" configuration. -export JACK_VERSION=`basename prebuilts/sdk/tools/jacks/*ALPHA* | sed 's/^jack-//' | sed 's/.jar$//'` vogar_args="$vogar_args --toolchain jack --language JN" # Run the tests using vogar. |