diff options
author | 2017-01-31 11:49:33 +0300 | |
---|---|---|
committer | 2017-03-13 14:38:08 +0300 | |
commit | b3f2b5cfeac75cce1babfc61db46d457fb7c4d70 (patch) | |
tree | eced81fc8fcc7c6a474369ea1d5b4abb6c8923bf | |
parent | 8f48f4cb1760cd5d1b193e87ba6b56043bb08f35 (diff) |
Assign pointer to quick code for copied methods
This optimizes out interpretation of copied
Java 8 default methods which have been compiled.
Test: make test-art-target-image_test
Change-Id: I4e9e593fb3d8f9a4e26a821440473982b39698b3
-rw-r--r-- | build/Android.gtest.mk | 3 | ||||
-rw-r--r-- | compiler/image_test.cc | 91 | ||||
-rw-r--r-- | compiler/oat_writer.cc | 98 | ||||
-rw-r--r-- | test/DefaultMethods/IterableBase.java | 27 |
4 files changed, 198 insertions, 21 deletions
diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk index c27f8dbe4a..ed34a8df5f 100644 --- a/build/Android.gtest.mk +++ b/build/Android.gtest.mk @@ -25,6 +25,7 @@ include art/build/Android.common_build.mk GTEST_DEX_DIRECTORIES := \ AbstractMethod \ AllFields \ + DefaultMethods \ DexToDexDecompiler \ ErroneousA \ ErroneousB \ @@ -104,7 +105,7 @@ ART_GTEST_dex_cache_test_DEX_DEPS := Main Packages MethodTypes ART_GTEST_dex_file_test_DEX_DEPS := GetMethodSignature Main Nested MultiDex ART_GTEST_dex2oat_test_DEX_DEPS := $(ART_GTEST_dex2oat_environment_tests_DEX_DEPS) Statics VerifierDeps ART_GTEST_exception_test_DEX_DEPS := ExceptionHandle -ART_GTEST_image_test_DEX_DEPS := ImageLayoutA ImageLayoutB +ART_GTEST_image_test_DEX_DEPS := ImageLayoutA ImageLayoutB DefaultMethods ART_GTEST_imtable_test_DEX_DEPS := IMTA IMTB ART_GTEST_instrumentation_test_DEX_DEPS := Instrumentation ART_GTEST_jni_compiler_test_DEX_DEPS := MyClassNatives diff --git a/compiler/image_test.cc b/compiler/image_test.cc index 89e8a678b1..7ee494a131 100644 --- a/compiler/image_test.cc +++ b/compiler/image_test.cc @@ -76,7 +76,7 @@ class ImageTest : public CommonCompilerTest { void Compile(ImageHeader::StorageMode storage_mode, CompilationHelper& out_helper, const std::string& extra_dex = "", - const std::string& image_class = ""); + const std::initializer_list<std::string>& image_classes = {}); void SetUpRuntimeOptions(RuntimeOptions* options) OVERRIDE { CommonCompilerTest::SetUpRuntimeOptions(options); @@ -90,6 +90,18 @@ class ImageTest : public CommonCompilerTest { return new std::unordered_set<std::string>(image_classes_); } + ArtMethod* FindCopiedMethod(ArtMethod* origin, mirror::Class* klass) + REQUIRES_SHARED(Locks::mutator_lock_) { + PointerSize pointer_size = class_linker_->GetImagePointerSize(); + for (ArtMethod& m : klass->GetCopiedMethods(pointer_size)) { + if (strcmp(origin->GetName(), m.GetName()) == 0 && + origin->GetSignature() == m.GetSignature()) { + return &m; + } + } + return nullptr; + } + private: std::unordered_set<std::string> image_classes_; }; @@ -345,8 +357,8 @@ void CompilationHelper::Compile(CompilerDriver* driver, void ImageTest::Compile(ImageHeader::StorageMode storage_mode, CompilationHelper& helper, const std::string& extra_dex, - const std::string& image_class) { - if (!image_class.empty()) { + const std::initializer_list<std::string>& image_classes) { + for (const std::string& image_class : image_classes) { image_classes_.insert(image_class); } CreateCompilerDriver(Compiler::kOptimizing, kRuntimeISA, kIsTargetBuild ? 2U : 16U); @@ -358,13 +370,15 @@ void ImageTest::Compile(ImageHeader::StorageMode storage_mode, helper.extra_dex_files = OpenTestDexFiles(extra_dex.c_str()); } helper.Compile(compiler_driver_.get(), storage_mode); - if (!image_class.empty()) { + if (image_classes.begin() != image_classes.end()) { // Make sure the class got initialized. ScopedObjectAccess soa(Thread::Current()); ClassLinker* const class_linker = Runtime::Current()->GetClassLinker(); - mirror::Class* klass = class_linker->FindSystemClass(Thread::Current(), image_class.c_str()); - EXPECT_TRUE(klass != nullptr); - EXPECT_TRUE(klass->IsInitialized()); + for (const std::string& image_class : image_classes) { + mirror::Class* klass = class_linker->FindSystemClass(Thread::Current(), image_class.c_str()); + EXPECT_TRUE(klass != nullptr); + EXPECT_TRUE(klass->IsInitialized()); + } } } @@ -492,7 +506,7 @@ TEST_F(ImageTest, TestImageLayout) { // Compile multi-image with ImageLayoutA being the last image. { CompilationHelper helper; - Compile(ImageHeader::kStorageModeUncompressed, helper, "ImageLayoutA", "LMyClass;"); + Compile(ImageHeader::kStorageModeUncompressed, helper, "ImageLayoutA", {"LMyClass;"}); image_sizes = helper.GetImageObjectSectionSizes(); } TearDown(); @@ -501,7 +515,7 @@ TEST_F(ImageTest, TestImageLayout) { // Compile multi-image with ImageLayoutB being the last image. { CompilationHelper helper; - Compile(ImageHeader::kStorageModeUncompressed, helper, "ImageLayoutB", "LMyClass;"); + Compile(ImageHeader::kStorageModeUncompressed, helper, "ImageLayoutB", {"LMyClass;"}); image_sizes_extra = helper.GetImageObjectSectionSizes(); } // Make sure that the new stuff in the clinit in ImageLayoutB is in the last image and not in the @@ -553,4 +567,63 @@ TEST_F(ImageTest, ImageHeaderIsValid) { ASSERT_FALSE(image_header.IsValid()); } +// Test that pointer to quick code is the same in +// a default method of an interface and in a copied method +// of a class which implements the interface. This should be true +// only if the copied method and the origin method are located in the +// same oat file. +TEST_F(ImageTest, TestDefaultMethods) { + CompilationHelper helper; + Compile(ImageHeader::kStorageModeUncompressed, + helper, + "DefaultMethods", + {"LIface;", "LImpl;", "LIterableBase;"}); + + PointerSize pointer_size = class_linker_->GetImagePointerSize(); + Thread* self = Thread::Current(); + ScopedObjectAccess soa(self); + + // Test the pointer to quick code is the same in origin method + // and in the copied method form the same oat file. + mirror::Class* iface_klass = class_linker_->LookupClass( + self, "LIface;", ObjPtr<mirror::ClassLoader>()); + ASSERT_NE(nullptr, iface_klass); + ArtMethod* origin = iface_klass->FindDeclaredVirtualMethod( + "defaultMethod", "()V", pointer_size); + ASSERT_NE(nullptr, origin); + const void* code = origin->GetEntryPointFromQuickCompiledCodePtrSize(pointer_size); + // The origin method should have a pointer to quick code + ASSERT_NE(nullptr, code); + ASSERT_FALSE(class_linker_->IsQuickToInterpreterBridge(code)); + mirror::Class* impl_klass = class_linker_->LookupClass( + self, "LImpl;", ObjPtr<mirror::ClassLoader>()); + ASSERT_NE(nullptr, impl_klass); + ArtMethod* copied = FindCopiedMethod(origin, impl_klass); + ASSERT_NE(nullptr, copied); + // the copied method should have pointer to the same quick code as the origin method + ASSERT_EQ(code, copied->GetEntryPointFromQuickCompiledCodePtrSize(pointer_size)); + + // Test the origin method has pointer to quick code + // but the copied method has pointer to interpreter + // because these methods are in different oat files. + mirror::Class* iterable_klass = class_linker_->LookupClass( + self, "Ljava/lang/Iterable;", ObjPtr<mirror::ClassLoader>()); + ASSERT_NE(nullptr, iterable_klass); + origin = iterable_klass->FindDeclaredVirtualMethod( + "forEach", "(Ljava/util/function/Consumer;)V", pointer_size); + ASSERT_NE(nullptr, origin); + code = origin->GetEntryPointFromQuickCompiledCodePtrSize(pointer_size); + // the origin method should have a pointer to quick code + ASSERT_NE(nullptr, code); + ASSERT_FALSE(class_linker_->IsQuickToInterpreterBridge(code)); + mirror::Class* iterablebase_klass = class_linker_->LookupClass( + self, "LIterableBase;", ObjPtr<mirror::ClassLoader>()); + ASSERT_NE(nullptr, iterablebase_klass); + copied = FindCopiedMethod(origin, iterablebase_klass); + ASSERT_NE(nullptr, copied); + code = copied->GetEntryPointFromQuickCompiledCodePtrSize(pointer_size); + // the copied method should have a pointer to interpreter + ASSERT_TRUE(class_linker_->IsQuickToInterpreterBridge(code)); +} + } // namespace art diff --git a/compiler/oat_writer.cc b/compiler/oat_writer.cc index afcdf5ea17..5812bd75a5 100644 --- a/compiler/oat_writer.cc +++ b/compiler/oat_writer.cc @@ -985,18 +985,63 @@ class OatWriter::InitMapMethodVisitor : public OatDexMethodVisitor { class OatWriter::InitImageMethodVisitor : public OatDexMethodVisitor { public: - InitImageMethodVisitor(OatWriter* writer, size_t offset) + InitImageMethodVisitor(OatWriter* writer, + size_t offset, + const std::vector<const DexFile*>* dex_files) : OatDexMethodVisitor(writer, offset), - pointer_size_(GetInstructionSetPointerSize(writer_->compiler_driver_->GetInstructionSet())) { + pointer_size_(GetInstructionSetPointerSize(writer_->compiler_driver_->GetInstructionSet())), + dex_files_(dex_files), + class_linker_(Runtime::Current()->GetClassLinker()) { + } + + // Handle copied methods here. Copy pointer to quick code from + // an origin method to a copied method only if they are + // in the same oat file. If the origin and the copied methods are + // in different oat files don't touch the copied method. + // References to other oat files are not supported yet. + bool StartClass(const DexFile* dex_file, size_t class_def_index) + REQUIRES_SHARED(Locks::mutator_lock_) { + OatDexMethodVisitor::StartClass(dex_file, class_def_index); + // Skip classes that are not in the image. + if (!IsImageClass()) { + return true; + } + ScopedObjectAccessUnchecked soa(Thread::Current()); + StackHandleScope<1> hs(soa.Self()); + Handle<mirror::DexCache> dex_cache = hs.NewHandle( + class_linker_->FindDexCache(Thread::Current(), *dex_file)); + const DexFile::ClassDef& class_def = dex_file->GetClassDef(class_def_index); + mirror::Class* klass = dex_cache->GetResolvedType(class_def.class_idx_); + if (klass != nullptr) { + for (ArtMethod& method : klass->GetCopiedMethods(pointer_size_)) { + // Find origin method. Declaring class and dex_method_idx + // in the copied method should be the same as in the origin + // method. + mirror::Class* declaring_class = method.GetDeclaringClass(); + ArtMethod* origin = declaring_class->FindDeclaredVirtualMethod( + declaring_class->GetDexCache(), + method.GetDexMethodIndex(), + pointer_size_); + CHECK(origin != nullptr); + if (IsInOatFile(&declaring_class->GetDexFile())) { + const void* code_ptr = + origin->GetEntryPointFromQuickCompiledCodePtrSize(pointer_size_); + if (code_ptr == nullptr) { + methods_to_process_.push_back(std::make_pair(&method, origin)); + } else { + method.SetEntryPointFromQuickCompiledCodePtrSize( + code_ptr, pointer_size_); + } + } + } + } + return true; } bool VisitMethod(size_t class_def_method_index, const ClassDataItemIterator& it) REQUIRES_SHARED(Locks::mutator_lock_) { - const DexFile::TypeId& type_id = - dex_file_->GetTypeId(dex_file_->GetClassDef(class_def_index_).class_idx_); - const char* class_descriptor = dex_file_->GetTypeDescriptor(type_id); // Skip methods that are not in the image. - if (!writer_->GetCompilerDriver()->IsImageClass(class_descriptor)) { + if (!IsImageClass()) { return true; } @@ -1010,17 +1055,16 @@ class OatWriter::InitImageMethodVisitor : public OatDexMethodVisitor { ++method_offsets_index_; } - ClassLinker* linker = Runtime::Current()->GetClassLinker(); // Unchecked as we hold mutator_lock_ on entry. ScopedObjectAccessUnchecked soa(Thread::Current()); StackHandleScope<1> hs(soa.Self()); - Handle<mirror::DexCache> dex_cache(hs.NewHandle(linker->FindDexCache( + Handle<mirror::DexCache> dex_cache(hs.NewHandle(class_linker_->FindDexCache( Thread::Current(), *dex_file_))); ArtMethod* method; if (writer_->HasBootImage()) { const InvokeType invoke_type = it.GetMethodInvokeType( dex_file_->GetClassDef(class_def_index_)); - method = linker->ResolveMethod<ClassLinker::kNoICCECheckForCache>( + method = class_linker_->ResolveMethod<ClassLinker::kNoICCECheckForCache>( *dex_file_, it.GetMemberIndex(), dex_cache, @@ -1040,7 +1084,8 @@ class OatWriter::InitImageMethodVisitor : public OatDexMethodVisitor { // Should already have been resolved by the compiler, just peek into the dex cache. // It may not be resolved if the class failed to verify, in this case, don't set the // entrypoint. This is not fatal since the dex cache will contain a resolution method. - method = dex_cache->GetResolvedMethod(it.GetMemberIndex(), linker->GetImagePointerSize()); + method = dex_cache->GetResolvedMethod(it.GetMemberIndex(), + class_linker_->GetImagePointerSize()); } if (method != nullptr && compiled_method != nullptr && @@ -1052,8 +1097,38 @@ class OatWriter::InitImageMethodVisitor : public OatDexMethodVisitor { return true; } + // Check whether current class is image class + bool IsImageClass() { + const DexFile::TypeId& type_id = + dex_file_->GetTypeId(dex_file_->GetClassDef(class_def_index_).class_idx_); + const char* class_descriptor = dex_file_->GetTypeDescriptor(type_id); + return writer_->GetCompilerDriver()->IsImageClass(class_descriptor); + } + + // Check whether specified dex file is in the compiled oat file. + bool IsInOatFile(const DexFile* dex_file) { + return ContainsElement(*dex_files_, dex_file); + } + + // Assign a pointer to quick code for copied methods + // not handled in the method StartClass + void Postprocess() { + for (std::pair<ArtMethod*, ArtMethod*>& p : methods_to_process_) { + ArtMethod* method = p.first; + ArtMethod* origin = p.second; + const void* code_ptr = + origin->GetEntryPointFromQuickCompiledCodePtrSize(pointer_size_); + if (code_ptr != nullptr) { + method->SetEntryPointFromQuickCompiledCodePtrSize(code_ptr, pointer_size_); + } + } + } + protected: const PointerSize pointer_size_; + const std::vector<const DexFile*>* dex_files_; + ClassLinker* const class_linker_; + std::vector<std::pair<ArtMethod*, ArtMethod*>> methods_to_process_; }; class OatWriter::WriteCodeMethodVisitor : public OatDexMethodVisitor { @@ -1631,8 +1706,9 @@ size_t OatWriter::InitOatCodeDexFiles(size_t offset) { offset = code_visitor.GetOffset(); if (HasImage()) { - InitImageMethodVisitor image_visitor(this, offset); + InitImageMethodVisitor image_visitor(this, offset, dex_files_); success = VisitDexMethods(&image_visitor); + image_visitor.Postprocess(); DCHECK(success); offset = image_visitor.GetOffset(); } diff --git a/test/DefaultMethods/IterableBase.java b/test/DefaultMethods/IterableBase.java new file mode 100644 index 0000000000..4cefdefb67 --- /dev/null +++ b/test/DefaultMethods/IterableBase.java @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2017 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. + */ + +interface Iface { + default void defaultMethod() { + } +} + +class Impl implements Iface { +} + +abstract class IterableBase implements Iterable { +} + |