diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/class_linker.cc | 163 | ||||
| -rw-r--r-- | src/class_linker.h | 12 | ||||
| -rw-r--r-- | src/class_linker_test.cc | 13 | ||||
| -rw-r--r-- | src/dex_instruction.cc | 21 | ||||
| -rw-r--r-- | src/gc/space.cc | 20 | ||||
| -rw-r--r-- | src/gc/space.h | 20 | ||||
| -rw-r--r-- | src/heap.cc | 145 | ||||
| -rw-r--r-- | src/heap.h | 53 | ||||
| -rw-r--r-- | src/oat/runtime/support_invoke.cc | 8 | ||||
| -rw-r--r-- | src/oatdump.cc | 2 | ||||
| -rw-r--r-- | src/object.cc | 26 | ||||
| -rw-r--r-- | src/object.h | 148 | ||||
| -rw-r--r-- | src/object_utils.h | 2 | ||||
| -rw-r--r-- | src/runtime_support.h | 3 | ||||
| -rw-r--r-- | src/timing_logger.h | 9 | ||||
| -rw-r--r-- | src/verifier/method_verifier.cc | 60 | ||||
| -rw-r--r-- | src/verifier/reg_type.cc | 17 | ||||
| -rw-r--r-- | src/verifier/reg_type.h | 15 | ||||
| -rw-r--r-- | src/verifier/reg_type_cache.cc | 73 | ||||
| -rw-r--r-- | src/verifier/reg_type_cache.h | 29 |
20 files changed, 510 insertions, 329 deletions
diff --git a/src/class_linker.cc b/src/class_linker.cc index 1853a59418..83661cbafc 100644 --- a/src/class_linker.cc +++ b/src/class_linker.cc @@ -166,7 +166,6 @@ const char* ClassLinker::class_roots_descriptors_[] = { "Ljava/lang/Object;", "[Ljava/lang/Class;", "[Ljava/lang/Object;", - "[[Ljava/lang/Object;", "Ljava/lang/String;", "Ljava/lang/DexCache;", "Ljava/lang/ref/Reference;", @@ -258,10 +257,6 @@ void ClassLinker::InitFromCompiler(const std::vector<const DexFile*>& boot_class SirtRef<Class> object_array_class(self, AllocClass(self, java_lang_Class.get(), sizeof(Class))); object_array_class->SetComponentType(java_lang_Object.get()); - // Object[][] needed for iftables. - SirtRef<Class> object_array_array_class(self, AllocClass(self, java_lang_Class.get(), sizeof(Class))); - object_array_array_class->SetComponentType(object_array_class.get()); - // Setup the char class to be used for char[]. SirtRef<Class> char_class(self, AllocClass(self, java_lang_Class.get(), sizeof(Class))); @@ -283,7 +278,6 @@ void ClassLinker::InitFromCompiler(const std::vector<const DexFile*>& boot_class SetClassRoot(kJavaLangObject, java_lang_Object.get()); SetClassRoot(kClassArrayClass, class_array_class.get()); SetClassRoot(kObjectArrayClass, object_array_class.get()); - SetClassRoot(kObjectArrayArrayClass, object_array_array_class.get()); SetClassRoot(kCharArrayClass, char_array_class.get()); SetClassRoot(kJavaLangString, java_lang_String.get()); @@ -308,7 +302,7 @@ void ClassLinker::InitFromCompiler(const std::vector<const DexFile*>& boot_class // now that these are registered, we can use AllocClass() and AllocObjectArray - // Setup DexCache. This can not be done later since AppendToBootClassPath calls AllocDexCache. + // Set up DexCache. This cannot be done later since AppendToBootClassPath calls AllocDexCache. SirtRef<Class> java_lang_DexCache(self, AllocClass(self, java_lang_Class.get(), sizeof(DexCacheClass))); SetClassRoot(kJavaLangDexCache, java_lang_DexCache.get()); @@ -428,9 +422,6 @@ void ClassLinker::InitFromCompiler(const std::vector<const DexFile*>& boot_class Class* found_object_array_class = FindSystemClass("[Ljava/lang/Object;"); CHECK_EQ(object_array_class.get(), found_object_array_class); - Class* found_object_array_array_class = FindSystemClass("[[Ljava/lang/Object;"); - CHECK_EQ(object_array_array_class.get(), found_object_array_array_class); - // Setup the single, global copy of "iftable". Class* java_lang_Cloneable = FindSystemClass("Ljava/lang/Cloneable;"); CHECK(java_lang_Cloneable != NULL); @@ -438,8 +429,8 @@ void ClassLinker::InitFromCompiler(const std::vector<const DexFile*>& boot_class CHECK(java_io_Serializable != NULL); // We assume that Cloneable/Serializable don't have superinterfaces -- normally we'd have to // crawl up and explicitly list all of the supers as well. - array_iftable_->Set(0, AllocInterfaceEntry(self, java_lang_Cloneable)); - array_iftable_->Set(1, AllocInterfaceEntry(self, java_io_Serializable)); + array_iftable_->SetInterface(0, java_lang_Cloneable); + array_iftable_->SetInterface(1, java_io_Serializable); // Sanity check Class[] and Object[]'s interfaces. ClassHelper kh(class_array_class.get(), this); @@ -1174,14 +1165,6 @@ DexCache* ClassLinker::AllocDexCache(Thread* self, const DexFile& dex_file) { return dex_cache.get(); } -InterfaceEntry* ClassLinker::AllocInterfaceEntry(Thread* self, Class* interface) { - DCHECK(interface->IsInterface()); - SirtRef<ObjectArray<Object> > array(self, AllocObjectArray<Object>(self, InterfaceEntry::LengthAsArray())); - SirtRef<InterfaceEntry> interface_entry(self, down_cast<InterfaceEntry*>(array.get())); - interface_entry->SetInterface(interface); - return interface_entry.get(); -} - Class* ClassLinker::AllocClass(Thread* self, Class* java_lang_Class, size_t class_size) { DCHECK_GE(class_size, sizeof(Class)); Heap* heap = Runtime::Current()->GetHeap(); @@ -1885,8 +1868,6 @@ Class* ClassLinker::CreateArrayClass(const std::string& descriptor, ClassLoader* new_class.reset(GetClassRoot(kClassArrayClass)); } else if (descriptor == "[Ljava/lang/Object;") { new_class.reset(GetClassRoot(kObjectArrayClass)); - } else if (descriptor == "[[Ljava/lang/Object;") { - new_class.reset(GetClassRoot(kObjectArrayArrayClass)); } else if (descriptor == class_roots_descriptors_[kJavaLangStringArrayClass]) { new_class.reset(GetClassRoot(kJavaLangStringArrayClass)); } else if (descriptor == class_roots_descriptors_[kJavaLangReflectAbstractMethodArrayClass]) { @@ -2683,12 +2664,12 @@ bool ClassLinker::ValidateSuperClassDescriptors(const Class* klass) { } } } + IfTable* iftable = klass->GetIfTable(); for (int32_t i = 0; i < klass->GetIfTableCount(); ++i) { - InterfaceEntry* interface_entry = klass->GetIfTable()->Get(i); - Class* interface = interface_entry->GetInterface(); + Class* interface = iftable->GetInterface(i); if (klass->GetClassLoader() != interface->GetClassLoader()) { for (size_t j = 0; j < interface->NumVirtualMethods(); ++j) { - const AbstractMethod* method = interface_entry->GetMethodArray()->Get(j); + const AbstractMethod* method = iftable->GetMethodArray(i)->Get(j); if (!IsSameMethodSignatureInDifferentClassContexts(method, interface, method->GetDeclaringClass())) { ThrowLinkageError("Class %s method %s resolves differently in interface %s", @@ -2784,7 +2765,7 @@ bool ClassLinker::InitializeSuperClass(Class* klass, bool can_run_clinit, bool c } bool ClassLinker::EnsureInitialized(Class* c, bool can_run_clinit, bool can_init_fields) { - CHECK(c != NULL); + DCHECK(c != NULL); if (c->IsInitialized()) { return true; } @@ -3074,18 +3055,34 @@ bool ClassLinker::LinkInterfaceMethods(SirtRef<Class>& klass, ObjectArray<Class> ifcount += interface->GetIfTableCount(); } if (ifcount == 0) { - // TODO: enable these asserts with klass status validation - // DCHECK_EQ(klass->GetIfTableCount(), 0); - // DCHECK(klass->GetIfTable() == NULL); + // Class implements no interfaces. + DCHECK_EQ(klass->GetIfTableCount(), 0); + DCHECK(klass->GetIfTable() == NULL); return true; } + if (ifcount == super_ifcount) { + // Class implements same interfaces as parent, are any of these not marker interfaces? + bool has_non_marker_interface = false; + IfTable* super_iftable = klass->GetSuperClass()->GetIfTable(); + for (size_t i = 0; i < ifcount; ++i) { + if (super_iftable->GetMethodArrayCount(i) > 0) { + has_non_marker_interface = true; + break; + } + } + if (!has_non_marker_interface) { + // Class just inherits marker interfaces from parent so recycle parent's iftable. + klass->SetIfTable(super_iftable); + return true; + } + } Thread* self = Thread::Current(); - SirtRef<ObjectArray<InterfaceEntry> > iftable(self, AllocIfTable(self, ifcount)); + SirtRef<IfTable> iftable(self, AllocIfTable(self, ifcount)); if (super_ifcount != 0) { - ObjectArray<InterfaceEntry>* super_iftable = klass->GetSuperClass()->GetIfTable(); + IfTable* super_iftable = klass->GetSuperClass()->GetIfTable(); for (size_t i = 0; i < super_ifcount; i++) { - Class* super_interface = super_iftable->Get(i)->GetInterface(); - iftable->Set(i, AllocInterfaceEntry(self, super_interface)); + Class* super_interface = super_iftable->GetInterface(i); + iftable->SetInterface(i, super_interface); } } // Flatten the interface inheritance hierarchy. @@ -3104,7 +3101,7 @@ bool ClassLinker::LinkInterfaceMethods(SirtRef<Class>& klass, ObjectArray<Class> // Check if interface is already in iftable bool duplicate = false; for (size_t j = 0; j < idx; j++) { - Class* existing_interface = iftable->Get(j)->GetInterface(); + Class* existing_interface = iftable->GetInterface(j); if (existing_interface == interface) { duplicate = true; break; @@ -3112,27 +3109,27 @@ bool ClassLinker::LinkInterfaceMethods(SirtRef<Class>& klass, ObjectArray<Class> } if (!duplicate) { // Add this non-duplicate interface. - iftable->Set(idx++, AllocInterfaceEntry(self, interface)); + iftable->SetInterface(idx++, interface); // Add this interface's non-duplicate super-interfaces. for (int32_t j = 0; j < interface->GetIfTableCount(); j++) { - Class* super_interface = interface->GetIfTable()->Get(j)->GetInterface(); + Class* super_interface = interface->GetIfTable()->GetInterface(j); bool super_duplicate = false; for (size_t k = 0; k < idx; k++) { - Class* existing_interface = iftable->Get(k)->GetInterface(); + Class* existing_interface = iftable->GetInterface(k); if (existing_interface == super_interface) { super_duplicate = true; break; } } if (!super_duplicate) { - iftable->Set(idx++, AllocInterfaceEntry(self, super_interface)); + iftable->SetInterface(idx++, super_interface); } } } } // Shrink iftable in case duplicates were found if (idx < ifcount) { - iftable.reset(iftable->CopyOf(self, idx)); + iftable.reset(down_cast<IfTable*>(iftable->CopyOf(self, idx * IfTable::kMax))); ifcount = idx; } else { CHECK_EQ(idx, ifcount); @@ -3140,60 +3137,62 @@ bool ClassLinker::LinkInterfaceMethods(SirtRef<Class>& klass, ObjectArray<Class> klass->SetIfTable(iftable.get()); // If we're an interface, we don't need the vtable pointers, so we're done. - if (klass->IsInterface() /*|| super_ifcount == ifcount*/) { + if (klass->IsInterface()) { return true; } std::vector<AbstractMethod*> miranda_list; MethodHelper vtable_mh(NULL, this); MethodHelper interface_mh(NULL, this); for (size_t i = 0; i < ifcount; ++i) { - InterfaceEntry* interface_entry = iftable->Get(i); - Class* interface = interface_entry->GetInterface(); - ObjectArray<AbstractMethod>* method_array = - AllocMethodArray(self, interface->NumVirtualMethods()); - interface_entry->SetMethodArray(method_array); - ObjectArray<AbstractMethod>* vtable = klass->GetVTableDuringLinking(); - for (size_t j = 0; j < interface->NumVirtualMethods(); ++j) { - AbstractMethod* interface_method = interface->GetVirtualMethod(j); - interface_mh.ChangeMethod(interface_method); - int32_t k; - // For each method listed in the interface's method list, find the - // matching method in our class's method list. We want to favor the - // subclass over the superclass, which just requires walking - // back from the end of the vtable. (This only matters if the - // superclass defines a private method and this class redefines - // it -- otherwise it would use the same vtable slot. In .dex files - // those don't end up in the virtual method table, so it shouldn't - // matter which direction we go. We walk it backward anyway.) - for (k = vtable->GetLength() - 1; k >= 0; --k) { - AbstractMethod* vtable_method = vtable->Get(k); - vtable_mh.ChangeMethod(vtable_method); - if (interface_mh.HasSameNameAndSignature(&vtable_mh)) { - if (!vtable_method->IsPublic()) { - Thread::Current()->ThrowNewExceptionF("Ljava/lang/IllegalAccessError;", - "Implementation not public: %s", PrettyMethod(vtable_method).c_str()); - return false; - } - method_array->Set(j, vtable_method); - break; - } - } - if (k < 0) { - SirtRef<AbstractMethod> miranda_method(self, NULL); - for (size_t mir = 0; mir < miranda_list.size(); mir++) { - AbstractMethod* mir_method = miranda_list[mir]; - vtable_mh.ChangeMethod(mir_method); + Class* interface = iftable->GetInterface(i); + size_t num_methods = interface->NumVirtualMethods(); + if (num_methods > 0) { + ObjectArray<AbstractMethod>* method_array = AllocMethodArray(self, num_methods); + iftable->SetMethodArray(i, method_array); + ObjectArray<AbstractMethod>* vtable = klass->GetVTableDuringLinking(); + for (size_t j = 0; j < interface->NumVirtualMethods(); ++j) { + AbstractMethod* interface_method = interface->GetVirtualMethod(j); + interface_mh.ChangeMethod(interface_method); + int32_t k; + // For each method listed in the interface's method list, find the + // matching method in our class's method list. We want to favor the + // subclass over the superclass, which just requires walking + // back from the end of the vtable. (This only matters if the + // superclass defines a private method and this class redefines + // it -- otherwise it would use the same vtable slot. In .dex files + // those don't end up in the virtual method table, so it shouldn't + // matter which direction we go. We walk it backward anyway.) + for (k = vtable->GetLength() - 1; k >= 0; --k) { + AbstractMethod* vtable_method = vtable->Get(k); + vtable_mh.ChangeMethod(vtable_method); if (interface_mh.HasSameNameAndSignature(&vtable_mh)) { - miranda_method.reset(miranda_list[mir]); + if (!vtable_method->IsPublic()) { + self->ThrowNewExceptionF("Ljava/lang/IllegalAccessError;", + "Implementation not public: %s", + PrettyMethod(vtable_method).c_str()); + return false; + } + method_array->Set(j, vtable_method); break; } } - if (miranda_method.get() == NULL) { - // point the interface table at a phantom slot - miranda_method.reset(down_cast<AbstractMethod*>(interface_method->Clone(self))); - miranda_list.push_back(miranda_method.get()); + if (k < 0) { + SirtRef<AbstractMethod> miranda_method(self, NULL); + for (size_t mir = 0; mir < miranda_list.size(); mir++) { + AbstractMethod* mir_method = miranda_list[mir]; + vtable_mh.ChangeMethod(mir_method); + if (interface_mh.HasSameNameAndSignature(&vtable_mh)) { + miranda_method.reset(miranda_list[mir]); + break; + } + } + if (miranda_method.get() == NULL) { + // point the interface table at a phantom slot + miranda_method.reset(down_cast<AbstractMethod*>(interface_method->Clone(self))); + miranda_list.push_back(miranda_method.get()); + } + method_array->Set(j, miranda_method.get()); } - method_array->Set(j, miranda_method.get()); } } } diff --git a/src/class_linker.h b/src/class_linker.h index fd404c1411..096d602558 100644 --- a/src/class_linker.h +++ b/src/class_linker.h @@ -337,9 +337,9 @@ class ClassLinker { GetClassRoot(kJavaLangReflectMethodArrayClass), length); } - ObjectArray<InterfaceEntry>* AllocIfTable(Thread* self, size_t length) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - return ObjectArray<InterfaceEntry>::Alloc(self, GetClassRoot(kObjectArrayArrayClass), length); + IfTable* AllocIfTable(Thread* self, size_t ifcount) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + return down_cast<IfTable*>( + IfTable::Alloc(self, GetClassRoot(kObjectArrayClass), ifcount * IfTable::kMax)); } ObjectArray<Field>* AllocFieldArray(Thread* self, size_t length) @@ -412,9 +412,6 @@ class ClassLinker { Method* AllocMethod(Thread* self) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); Constructor* AllocConstructor(Thread* self) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - InterfaceEntry* AllocInterfaceEntry(Thread* self, Class* interface) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - Class* CreatePrimitiveClass(Thread* self, Primitive::Type type) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { return InitializePrimitiveClass(AllocClass(self, sizeof(Class)), type); @@ -574,7 +571,6 @@ class ClassLinker { kJavaLangObject, kClassArrayClass, kObjectArrayClass, - kObjectArrayArrayClass, kJavaLangString, kJavaLangDexCache, kJavaLangRefReference, @@ -637,7 +633,7 @@ class ClassLinker { return descriptor; } - ObjectArray<InterfaceEntry>* array_iftable_; + IfTable* array_iftable_; bool init_done_; diff --git a/src/class_linker_test.cc b/src/class_linker_test.cc index 02512eb867..1c41f3bef2 100644 --- a/src/class_linker_test.cc +++ b/src/class_linker_test.cc @@ -127,7 +127,7 @@ class ClassLinkerTest : public CommonTest { EXPECT_EQ(2U, kh.NumDirectInterfaces()); EXPECT_TRUE(array->GetVTable() != NULL); EXPECT_EQ(2, array->GetIfTableCount()); - ObjectArray<InterfaceEntry>* iftable = array->GetIfTable(); + IfTable* iftable = array->GetIfTable(); ASSERT_TRUE(iftable != NULL); kh.ChangeClass(kh.GetDirectInterface(0)); EXPECT_STREQ(kh.GetDescriptor(), "Ljava/lang/Cloneable;"); @@ -202,17 +202,14 @@ class ClassLinkerTest : public CommonTest { } } EXPECT_EQ(klass->IsInterface(), klass->GetVTable() == NULL); + const IfTable* iftable = klass->GetIfTable(); for (int i = 0; i < klass->GetIfTableCount(); i++) { - const InterfaceEntry* interface_entry = klass->GetIfTable()->Get(i); - ASSERT_TRUE(interface_entry != NULL); - Class* interface = interface_entry->GetInterface(); + Class* interface = iftable->GetInterface(i); ASSERT_TRUE(interface != NULL); - EXPECT_TRUE(interface_entry->GetInterface() != NULL); if (klass->IsInterface()) { - EXPECT_EQ(0U, interface_entry->GetMethodArrayCount()); + EXPECT_EQ(0U, iftable->GetMethodArrayCount(i)); } else { - CHECK_EQ(interface->NumVirtualMethods(), interface_entry->GetMethodArrayCount()); - EXPECT_EQ(interface->NumVirtualMethods(), interface_entry->GetMethodArrayCount()); + EXPECT_EQ(interface->NumVirtualMethods(), iftable->GetMethodArrayCount(i)); } } if (klass->IsAbstract()) { diff --git a/src/dex_instruction.cc b/src/dex_instruction.cc index 0de752387e..201a8e6fcb 100644 --- a/src/dex_instruction.cc +++ b/src/dex_instruction.cc @@ -297,7 +297,8 @@ std::string Instruction::DumpString(const DexFile* file) const { case CONST_CLASS: case NEW_INSTANCE: if (file != NULL) { - os << opcode << " " << PrettyType(insn.vB, *file) << " // type@" << insn.vB; + os << opcode << " v" << insn.vA << ", " << PrettyType(insn.vB, *file) + << " // type@" << insn.vB; break; } // else fall-through case SGET: @@ -308,7 +309,8 @@ std::string Instruction::DumpString(const DexFile* file) const { case SGET_CHAR: case SGET_SHORT: if (file != NULL) { - os << opcode << " " << PrettyField(insn.vB, *file, true) << " // field@" << insn.vB; + os << opcode << " v" << insn.vA << ", " << PrettyField(insn.vB, *file, true) + << " // field@" << insn.vB; break; } // else fall-through case SPUT: @@ -319,7 +321,8 @@ std::string Instruction::DumpString(const DexFile* file) const { case SPUT_CHAR: case SPUT_SHORT: if (file != NULL) { - os << opcode << " " << PrettyField(insn.vB, *file, true) << " // field@" << insn.vB; + os << opcode << " v" << insn.vA << ", " << PrettyField(insn.vB, *file, true) + << " // field@" << insn.vB; break; } // else fall-through default: @@ -342,7 +345,8 @@ std::string Instruction::DumpString(const DexFile* file) const { case IGET_CHAR: case IGET_SHORT: if (file != NULL) { - os << PrettyField(insn.vC, *file, true) << " // field@" << insn.vC; + os << opcode << " v" << insn.vA << ", v" << insn.vB << ", " + << PrettyField(insn.vC, *file, true) << " // field@" << insn.vC; break; } // else fall-through case IPUT: @@ -353,17 +357,20 @@ std::string Instruction::DumpString(const DexFile* file) const { case IPUT_CHAR: case IPUT_SHORT: if (file != NULL) { - os << opcode << " " << PrettyField(insn.vC, *file, true) << " // field@" << insn.vB; + os << opcode << " v" << insn.vA << ", v" << insn.vB << ", " + << PrettyField(insn.vC, *file, true) << " // field@" << insn.vC; break; } // else fall-through case INSTANCE_OF: if (file != NULL) { - os << opcode << " " << PrettyType(insn.vC, *file) << " // type@" << insn.vC; + os << opcode << " v" << insn.vA << ", v" << insn.vB << ", " + << PrettyType(insn.vC, *file) << " // type@" << insn.vC; break; } case NEW_ARRAY: if (file != NULL) { - os << opcode << " " << PrettyType(insn.vC, *file) << " // type@" << insn.vC; + os << opcode << " v" << insn.vA << ", v" << insn.vB << ", " + << PrettyType(insn.vC, *file) << " // type@" << insn.vC; break; } // else fall-through default: diff --git a/src/gc/space.cc b/src/gc/space.cc index 9c8819b21e..274d77765e 100644 --- a/src/gc/space.cc +++ b/src/gc/space.cc @@ -73,8 +73,8 @@ size_t AllocSpace::bitmap_index_ = 0; AllocSpace::AllocSpace(const std::string& name, MemMap* mem_map, void* mspace, byte* begin, byte* end, size_t growth_limit) : MemMapSpace(name, mem_map, end - begin, kGcRetentionPolicyAlwaysCollect), - num_bytes_allocated_(0), num_objects_allocated_(0), - lock_("allocation space lock", kAllocSpaceLock), mspace_(mspace), + num_bytes_allocated_(0), num_objects_allocated_(0), total_bytes_allocated_(0), + total_objects_allocated_(0), lock_("allocation space lock", kAllocSpaceLock), mspace_(mspace), growth_limit_(growth_limit) { CHECK(mspace != NULL); @@ -202,7 +202,10 @@ Object* AllocSpace::AllocWithoutGrowthLocked(size_t num_bytes) { *reinterpret_cast<word*>(reinterpret_cast<byte*>(result) + AllocationSize(result) - sizeof(word) - kChunkOverhead) = kPaddingValue; } - num_bytes_allocated_ += AllocationSize(result); + size_t allocation_size = AllocationSize(result); + num_bytes_allocated_ += allocation_size; + total_bytes_allocated_ += allocation_size; + ++total_objects_allocated_; ++num_objects_allocated_; return result; } @@ -552,8 +555,8 @@ DiscontinuousSpace::DiscontinuousSpace(const std::string& name, LargeObjectSpace::LargeObjectSpace(const std::string& name) : DiscontinuousSpace(name, kGcRetentionPolicyAlwaysCollect), - num_bytes_allocated_(0), - num_objects_allocated_(0) { + num_bytes_allocated_(0), num_objects_allocated_(0), total_bytes_allocated_(0), + total_objects_allocated_(0) { live_objects_.reset(new SpaceSetMap("large live objects")); mark_objects_.reset(new SpaceSetMap("large marked objects")); } @@ -583,8 +586,11 @@ Object* LargeObjectMapSpace::Alloc(Thread* self, size_t num_bytes) { Object* obj = reinterpret_cast<Object*>(mem_map->Begin()); large_objects_.push_back(obj); mem_maps_.Put(obj, mem_map); - num_bytes_allocated_ += mem_map->Size(); + size_t allocation_size = mem_map->Size(); + num_bytes_allocated_ += allocation_size; + total_bytes_allocated_ += allocation_size; ++num_objects_allocated_; + ++total_objects_allocated_; return obj; } @@ -757,7 +763,9 @@ Object* FreeListSpace::Alloc(Thread* self, size_t num_bytes) { } num_objects_allocated_++; + total_objects_allocated_++; num_bytes_allocated_ += num_bytes; + total_bytes_allocated_ += num_bytes; return reinterpret_cast<Object*>(addr); } diff --git a/src/gc/space.h b/src/gc/space.h index a543500aa8..ab29582dd4 100644 --- a/src/gc/space.h +++ b/src/gc/space.h @@ -314,6 +314,14 @@ class AllocSpace : public MemMapSpace { return num_objects_allocated_; } + size_t GetTotalBytesAllocated() const { + return total_bytes_allocated_; + } + + size_t GetTotalObjectsAllocated() const { + return total_objects_allocated_; + } + private: Object* AllocWithoutGrowthLocked(size_t num_bytes) EXCLUSIVE_LOCKS_REQUIRED(lock_); @@ -324,6 +332,8 @@ class AllocSpace : public MemMapSpace { // Approximate number of bytes which have been allocated into the space. size_t num_bytes_allocated_; size_t num_objects_allocated_; + size_t total_bytes_allocated_; + size_t total_objects_allocated_; static size_t bitmap_index_; @@ -453,6 +463,14 @@ class LargeObjectSpace : public DiscontinuousSpace { return num_objects_allocated_; } + size_t GetTotalBytesAllocated() const { + return total_bytes_allocated_; + } + + size_t GetTotalObjectsAllocated() const { + return total_objects_allocated_; + } + protected: LargeObjectSpace(const std::string& name); @@ -460,6 +478,8 @@ class LargeObjectSpace : public DiscontinuousSpace { // Approximate number of bytes which have been allocated into the space. size_t num_bytes_allocated_; size_t num_objects_allocated_; + size_t total_bytes_allocated_; + size_t total_objects_allocated_; UniquePtr<SpaceSetMap> live_objects_; UniquePtr<SpaceSetMap> mark_objects_; diff --git a/src/heap.cc b/src/heap.cc index 3989a247e4..d3de6035d3 100644 --- a/src/heap.cc +++ b/src/heap.cc @@ -139,8 +139,9 @@ Heap::Heap(size_t initial_size, size_t growth_limit, size_t capacity, concurrent_start_bytes_(std::numeric_limits<size_t>::max()), concurrent_start_size_(128 * KB), concurrent_min_free_(256 * KB), - concurrent_gc_start_rate_(3 * MB / 2), sticky_gc_count_(0), + total_bytes_freed_(0), + total_objects_freed_(0), large_object_threshold_(3 * kPageSize), num_bytes_allocated_(0), verify_missing_card_marks_(false), @@ -152,7 +153,6 @@ Heap::Heap(size_t initial_size, size_t growth_limit, size_t capacity, min_alloc_space_size_for_sticky_gc_(2 * MB), min_remaining_space_for_sticky_gc_(1 * MB), last_trim_time_(0), - try_running_gc_(false), requesting_gc_(false), max_allocation_stack_size_(MB), reference_referent_offset_(0), @@ -161,6 +161,10 @@ Heap::Heap(size_t initial_size, size_t growth_limit, size_t capacity, reference_pendingNext_offset_(0), finalizer_reference_zombie_offset_(0), target_utilization_(0.5), + total_paused_time_(0), + total_wait_time_(0), + measure_allocation_time_(false), + total_allocation_time_(0), verify_objects_(false) { if (VLOG_IS_ON(heap) || VLOG_IS_ON(startup)) { LOG(INFO) << "Heap() entering"; @@ -320,6 +324,39 @@ void Heap::AddSpace(ContinuousSpace* space) { } } +void Heap::DumpGcPerformanceInfo() { + // Dump cumulative timings. + LOG(INFO) << "Dumping cumulative Gc timings"; + uint64_t total_duration = 0; + for (CumulativeTimings::iterator it = cumulative_timings_.begin(); + it != cumulative_timings_.end(); ++it) { + CumulativeLogger* logger = it->second; + if (logger->GetTotalNs() != 0) { + logger->Dump(); + total_duration += logger->GetTotalNs(); + } + } + uint64_t allocation_time = static_cast<uint64_t>(total_allocation_time_) * kTimeAdjust; + size_t total_objects_allocated = GetTotalObjectsAllocated(); + size_t total_bytes_allocated = GetTotalBytesAllocated(); + if (total_duration != 0) { + const double total_seconds = double(total_duration / 1000) / 1000000.0; + LOG(INFO) << "Total time spent in GC: " << PrettyDuration(total_duration); + LOG(INFO) << "Mean GC size throughput: " + << PrettySize(GetTotalBytesFreed() / total_seconds) << "/s"; + LOG(INFO) << "Mean GC object throughput: " << GetTotalObjectsFreed() / total_seconds << "/s"; + } + LOG(INFO) << "Total number of allocations: " << total_objects_allocated; + LOG(INFO) << "Total bytes allocated " << PrettySize(total_bytes_allocated); + if (measure_allocation_time_) { + LOG(INFO) << "Total time spent allocating: " << PrettyDuration(allocation_time); + LOG(INFO) << "Mean allocation time: " + << PrettyDuration(allocation_time / total_objects_allocated); + } + LOG(INFO) << "Total mutator paused time: " << PrettyDuration(total_paused_time_); + LOG(INFO) << "Total waiting for Gc to complete time: " << PrettyDuration(total_wait_time_); +} + Heap::~Heap() { // If we don't reset then the mark stack complains in it's destructor. allocation_stack_->Reset(); @@ -377,6 +414,10 @@ Object* Heap::AllocObject(Thread* self, Class* c, size_t byte_count) { Object* obj = NULL; size_t size = 0; + uint64_t allocation_start = 0; + if (measure_allocation_time_) { + allocation_start = NanoTime(); + } // We need to have a zygote space or else our newly allocated large object can end up in the // Zygote resulting in it being prematurely freed. @@ -385,21 +426,17 @@ Object* Heap::AllocObject(Thread* self, Class* c, size_t byte_count) { if (byte_count >= large_object_threshold_ && have_zygote_space_ && c->IsPrimitiveArray()) { size = RoundUp(byte_count, kPageSize); obj = Allocate(self, NULL, size); - - if (obj != NULL) { - // Make sure that our large object didn't get placed anywhere within the space interval or else - // it breaks the immune range. - DCHECK(reinterpret_cast<byte*>(obj) < spaces_.front()->Begin() || - reinterpret_cast<byte*>(obj) >= spaces_.back()->End()); - } + // Make sure that our large object didn't get placed anywhere within the space interval or else + // it breaks the immune range. + DCHECK(obj == NULL || + reinterpret_cast<byte*>(obj) < spaces_.front()->Begin() || + reinterpret_cast<byte*>(obj) >= spaces_.back()->End()); } else { obj = Allocate(self, alloc_space_, byte_count); - size = alloc_space_->AllocationSize(obj); - if (obj != NULL) { - // Additional verification to ensure that we did not allocate into a zygote space. - DCHECK(!have_zygote_space_ || !FindSpaceFromObject(obj)->IsZygoteSpace()); - } + // Ensure that we did not allocate into a zygote space. + DCHECK(obj == NULL || !have_zygote_space_ || !FindSpaceFromObject(obj)->IsZygoteSpace()); + size = alloc_space_->AllocationSize(obj); } if (LIKELY(obj != NULL)) { @@ -422,6 +459,10 @@ Object* Heap::AllocObject(Thread* self, Class* c, size_t byte_count) { } VerifyObject(obj); + if (measure_allocation_time_) { + total_allocation_time_ += (NanoTime() - allocation_start) / kTimeAdjust; + } + return obj; } int64_t total_bytes_free = GetFreeMemory(); @@ -566,8 +607,6 @@ void Heap::RecordAllocation(size_t size, Object* obj) { } void Heap::RecordFree(size_t freed_objects, size_t freed_bytes) { - COMPILE_ASSERT(sizeof(size_t) == sizeof(int32_t), - int32_t_must_be_same_size_as_size_t_for_used_atomic_operations); DCHECK_LE(freed_bytes, static_cast<size_t>(num_bytes_allocated_)); num_bytes_allocated_ -= freed_bytes; @@ -688,18 +727,58 @@ Object* Heap::Allocate(Thread* self, AllocSpace* space, size_t alloc_size) { return TryToAllocate(self, space, alloc_size, true); } -int64_t Heap::GetMaxMemory() { +float Heap::GetTargetHeapUtilization() const { + return target_utilization_; +} + +void Heap::SetTargetHeapUtilization(float target) { + DCHECK_GT(target, 0.0f); // asserted in Java code + DCHECK_LT(target, 1.0f); + target_utilization_ = target; +} + +int64_t Heap::GetMaxMemory() const { return growth_limit_; } -int64_t Heap::GetTotalMemory() { +int64_t Heap::GetTotalMemory() const { return GetMaxMemory(); } -int64_t Heap::GetFreeMemory() { +int64_t Heap::GetFreeMemory() const { return GetMaxMemory() - num_bytes_allocated_; } +size_t Heap::GetTotalBytesFreed() const { + return total_bytes_freed_; +} + +size_t Heap::GetTotalObjectsFreed() const { + return total_objects_freed_; +} + +size_t Heap::GetTotalObjectsAllocated() const { + size_t total = large_object_space_->GetTotalObjectsAllocated(); + for (Spaces::const_iterator it = spaces_.begin(); it != spaces_.end(); ++it) { + Space* space = *it; + if (space->IsAllocSpace()) { + total += space->AsAllocSpace()->GetTotalObjectsAllocated(); + } + } + return total; +} + +size_t Heap::GetTotalBytesAllocated() const { + size_t total = large_object_space_->GetTotalBytesAllocated(); + for (Spaces::const_iterator it = spaces_.begin(); it != spaces_.end(); ++it) { + Space* space = *it; + if (space->IsAllocSpace()) { + total += space->AsAllocSpace()->GetTotalBytesAllocated(); + } + } + return total; +} + class InstanceCounter { public: InstanceCounter(Class* c, bool count_assignable, size_t* const count) @@ -1027,6 +1106,8 @@ void Heap::CollectGarbageMarkSweepPlan(Thread* self, GcType gc_type, GcCause gc_ cleared_references = mark_sweep.GetClearedReferences(); bytes_freed = mark_sweep.GetFreedBytes(); + total_bytes_freed_ += bytes_freed; + total_objects_freed_ += mark_sweep.GetFreedObjects(); } if (verify_post_gc_heap_) { @@ -1049,6 +1130,7 @@ void Heap::CollectGarbageMarkSweepPlan(Thread* self, GcType gc_type, GcCause gc_ // If the GC was slow, then print timings in the log. uint64_t duration = (NanoTime() - start_time) / 1000 * 1000; + total_paused_time_ += duration / kTimeAdjust; if (duration > MsToNs(50)) { const size_t percent_free = GetPercentFree(); const size_t current_heap_size = GetUsedMemorySize(); @@ -1586,15 +1668,12 @@ void Heap::CollectGarbageConcurrentMarkSweepPlan(Thread* self, GcType gc_type, G // Make sure everything in the live stack isn't something we unmarked. std::sort(allocation_stack_->Begin(), allocation_stack_->End()); for (Object** it = live_stack_->Begin(); it != live_stack_->End(); ++it) { - if (std::binary_search(allocation_stack_->Begin(), allocation_stack_->End(), *it)) { - LOG(FATAL) << "Unmarked object " << *it << " in the live stack"; - } + DCHECK(!std::binary_search(allocation_stack_->Begin(), allocation_stack_->End(), *it)) + << "Unmarked object " << *it << " in the live stack"; } } else { for (Object** it = allocation_stack_->Begin(); it != allocation_stack_->End(); ++it) { - if (GetLiveBitmap()->Test(*it)) { - LOG(FATAL) << "Object " << *it << " is marked as live"; - } + DCHECK(!GetLiveBitmap()->Test(*it)) << "Object " << *it << " is marked as live"; } } #endif @@ -1662,12 +1741,16 @@ void Heap::CollectGarbageConcurrentMarkSweepPlan(Thread* self, GcType gc_type, G cleared_references = mark_sweep.GetClearedReferences(); bytes_freed = mark_sweep.GetFreedBytes(); + total_bytes_freed_ += bytes_freed; + total_objects_freed_ += mark_sweep.GetFreedObjects(); } GrowForUtilization(); timings.AddSplit("GrowForUtilization"); EnqueueClearedReferences(&cleared_references); + timings.AddSplit("EnqueueClearedReferences"); + RequestHeapTrim(); timings.AddSplit("Finish"); @@ -1675,6 +1758,7 @@ void Heap::CollectGarbageConcurrentMarkSweepPlan(Thread* self, GcType gc_type, G uint64_t pause_roots = (root_end - root_begin) / 1000 * 1000; uint64_t pause_dirty = (dirty_end - dirty_begin) / 1000 * 1000; uint64_t duration = (NanoTime() - root_begin) / 1000 * 1000; + total_paused_time_ += (pause_roots + pause_dirty) / kTimeAdjust; if (pause_roots > MsToNs(5) || pause_dirty > MsToNs(5)) { const size_t percent_free = GetPercentFree(); const size_t current_heap_size = GetUsedMemorySize(); @@ -1706,6 +1790,7 @@ GcType Heap::WaitForConcurrentGcToComplete(Thread* self) { do_wait = is_gc_running_; } if (do_wait) { + uint64_t wait_time; // We must wait, change thread state then sleep on gc_complete_cond_; ScopedThreadStateChange tsc(Thread::Current(), kWaitingForGcToComplete); { @@ -1714,8 +1799,9 @@ GcType Heap::WaitForConcurrentGcToComplete(Thread* self) { gc_complete_cond_->Wait(self, *gc_complete_lock_); } last_gc_type = last_gc_type_; + wait_time = NanoTime() - wait_start;; + total_wait_time_ += wait_time; } - uint64_t wait_time = NanoTime() - wait_start; if (wait_time > MsToNs(5)) { LOG(INFO) << "WaitForConcurrentGcToComplete blocked for " << PrettyDuration(wait_time); } @@ -1727,12 +1813,7 @@ GcType Heap::WaitForConcurrentGcToComplete(Thread* self) { void Heap::DumpForSigQuit(std::ostream& os) { os << "Heap: " << GetPercentFree() << "% free, " << PrettySize(GetUsedMemorySize()) << "/" << PrettySize(GetTotalMemory()) << "; " << GetObjectsAllocated() << " objects\n"; - // Dump cumulative timings. - LOG(INFO) << "Dumping cumulative Gc timings"; - for (CumulativeTimings::iterator it = cumulative_timings_.begin(); - it != cumulative_timings_.end(); ++it) { - it->second->Dump(); - } + DumpGcPerformanceInfo(); } size_t Heap::GetPercentFree() { diff --git a/src/heap.h b/src/heap.h index 209d631cf8..3d49e30776 100644 --- a/src/heap.h +++ b/src/heap.h @@ -82,8 +82,9 @@ std::ostream& operator<<(std::ostream& os, const GcCause& policy); class Heap { public: static const size_t kInitialSize = 2 * MB; - static const size_t kMaximumSize = 32 * MB; + // Used so that we don't overflow the allocation time atomic integer. + static const size_t kTimeAdjust = 1024; typedef void (RootVisitor)(const Object* root, void* arg); typedef bool (IsMarkedTester)(const Object* object, void* arg); @@ -136,11 +137,11 @@ class Heap { void ConcurrentGC(Thread* self) LOCKS_EXCLUDED(Locks::runtime_shutdown_lock_); // Implements java.lang.Runtime.maxMemory. - int64_t GetMaxMemory(); + int64_t GetMaxMemory() const; // Implements java.lang.Runtime.totalMemory. - int64_t GetTotalMemory(); + int64_t GetTotalMemory() const; // Implements java.lang.Runtime.freeMemory. - int64_t GetFreeMemory(); + int64_t GetFreeMemory() const; // Implements VMDebug.countInstancesOfClass. int64_t CountInstances(Class* c, bool count_assignable) @@ -153,16 +154,11 @@ class Heap { // Target ideal heap utilization ratio, implements // dalvik.system.VMRuntime.getTargetHeapUtilization. - float GetTargetHeapUtilization() { - return target_utilization_; - } + float GetTargetHeapUtilization() const; + // Set target ideal heap utilization ratio, implements // dalvik.system.VMRuntime.setTargetHeapUtilization. - void SetTargetHeapUtilization(float target) { - DCHECK_GT(target, 0.0f); // asserted in Java code - DCHECK_LT(target, 1.0f); - target_utilization_ = target; - } + void SetTargetHeapUtilization(float target); // For the alloc space, sets the maximum number of bytes that the heap is allowed to allocate // from the system. Doesn't allow the space to exceed its growth limit. @@ -252,6 +248,18 @@ class Heap { size_t GetConcurrentMinFree() const; size_t GetUsedMemorySize() const; + // Returns the total number of objects allocated since the heap was created. + size_t GetTotalObjectsAllocated() const; + + // Returns the total number of bytes allocated since the heap was created. + size_t GetTotalBytesAllocated() const; + + // Returns the total number of objects freed since the heap was created. + size_t GetTotalObjectsFreed() const; + + // Returns the total number of bytes freed since the heap was created. + size_t GetTotalBytesFreed() const; + // Functions for getting the bitmap which corresponds to an object's address. // This is probably slow, TODO: use better data structure like binary tree . ContinuousSpace* FindSpaceFromObject(const Object*) const; @@ -298,6 +306,9 @@ class Heap { // UnReserve the address range where the oat file will be placed. void UnReserveOatFileAddressRange(); + // GC performance measuring + void DumpGcPerformanceInfo(); + private: // Allocates uninitialized storage. Passing in a null space tries to place the object in the // large object space. @@ -413,10 +424,11 @@ class Heap { size_t concurrent_min_free_; // Number of bytes allocated since the last Gc, we use this to help determine when to schedule concurrent GCs. size_t bytes_since_last_gc_; - // Start a concurrent GC if we have allocated concurrent_gc_start_rate_ bytes and not done a GCs. - size_t concurrent_gc_start_rate_; size_t sticky_gc_count_; + size_t total_bytes_freed_; + size_t total_objects_freed_; + // Primitive objects larger than this size are put in the large object space. size_t large_object_threshold_; @@ -450,11 +462,6 @@ class Heap { UniquePtr<HeapBitmap> live_bitmap_ GUARDED_BY(Locks::heap_bitmap_lock_); UniquePtr<HeapBitmap> mark_bitmap_ GUARDED_BY(Locks::heap_bitmap_lock_); - // True while the garbage collector is trying to signal the GC daemon thread. - // This flag is needed to prevent recursion from occurring when the JNI calls - // allocate memory and request another GC. - bool try_running_gc_; - // Used to ensure that we don't ever recursively request GC. volatile bool requesting_gc_; @@ -487,6 +494,14 @@ class Heap { // Target ideal heap utilization ratio float target_utilization_; + // Total time which mutators are paused or waiting for GC to complete. + uint64_t total_paused_time_; + uint64_t total_wait_time_; + + // Total number of objects allocated in microseconds. + const bool measure_allocation_time_; + AtomicInteger total_allocation_time_; + bool verify_objects_; friend class MarkSweep; diff --git a/src/oat/runtime/support_invoke.cc b/src/oat/runtime/support_invoke.cc index 4656198071..e66749d4a3 100644 --- a/src/oat/runtime/support_invoke.cc +++ b/src/oat/runtime/support_invoke.cc @@ -27,6 +27,12 @@ extern "C" uint64_t artInvokeInterfaceTrampoline(AbstractMethod* interface_metho AbstractMethod* method; if (LIKELY(interface_method->GetDexMethodIndex() != DexFile::kDexNoIndex16)) { method = this_object->GetClass()->FindVirtualMethodForInterface(interface_method); + if (UNLIKELY(method == NULL)) { + FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsAndArgs); + ThrowIncompatibleClassChangeErrorClassForInterfaceDispatch(interface_method, this_object, + caller_method); + return 0; // Failure. + } } else { FinishCalleeSaveFrameSetup(self, sp, Runtime::kRefsAndArgs); DCHECK(interface_method == Runtime::Current()->GetResolutionMethod()); @@ -86,7 +92,7 @@ extern "C" uint64_t artInvokeInterfaceTrampoline(AbstractMethod* interface_metho false, kInterface); if (UNLIKELY(method == NULL)) { CHECK(self->IsExceptionPending()); - return 0; // failure + return 0; // Failure. } } const void* code = method->GetCode(); diff --git a/src/oatdump.cc b/src/oatdump.cc index b40eb1cd4c..a592b9f759 100644 --- a/src/oatdump.cc +++ b/src/oatdump.cc @@ -504,7 +504,7 @@ class OatDumper { } else { os << "\t\t\tcatch entry dex PC: 0x"; } - os << std::hex << raw_table[i + 1] << "\n"; + os << std::hex << raw_table[i + 1] << std::dec << "\n"; return; } } diff --git a/src/object.cc b/src/object.cc index e8381dbf1c..5fdea71e04 100644 --- a/src/object.cc +++ b/src/object.cc @@ -474,10 +474,9 @@ AbstractMethod* AbstractMethod::FindOverriddenMethod() const { } else { MethodHelper mh(this); MethodHelper interface_mh; - ObjectArray<InterfaceEntry>* iftable = GetDeclaringClass()->GetIfTable(); - for (int32_t i = 0; i < iftable->GetLength() && result == NULL; i++) { - InterfaceEntry* entry = iftable->Get(i); - Class* interface = entry->GetInterface(); + IfTable* iftable = GetDeclaringClass()->GetIfTable(); + for (size_t i = 0; i < iftable->Count() && result == NULL; i++) { + Class* interface = iftable->GetInterface(i); for (size_t j = 0; j < interface->NumVirtualMethods(); ++j) { AbstractMethod* interface_method = interface->GetVirtualMethod(j); interface_mh.ChangeMethod(interface_method); @@ -881,9 +880,9 @@ bool Class::Implements(const Class* klass) const { // recursively all super-interfaces of those interfaces, are listed // in iftable_, so we can just do a linear scan through that. int32_t iftable_count = GetIfTableCount(); - ObjectArray<InterfaceEntry>* iftable = GetIfTable(); + IfTable* iftable = GetIfTable(); for (int32_t i = 0; i < iftable_count; i++) { - if (iftable->Get(i)->GetInterface() == klass) { + if (iftable->GetInterface(i) == klass) { return true; } } @@ -1007,11 +1006,10 @@ AbstractMethod* Class::FindVirtualMethodForInterface(AbstractMethod* method) { DCHECK(declaring_class->IsInterface()) << PrettyMethod(method); // TODO cache to improve lookup speed int32_t iftable_count = GetIfTableCount(); - ObjectArray<InterfaceEntry>* iftable = GetIfTable(); + IfTable* iftable = GetIfTable(); for (int32_t i = 0; i < iftable_count; i++) { - InterfaceEntry* interface_entry = iftable->Get(i); - if (interface_entry->GetInterface() == declaring_class) { - return interface_entry->GetMethodArray()->Get(method->GetMethodIndex()); + if (iftable->GetInterface(i) == declaring_class) { + return iftable->GetMethodArray(i)->Get(method->GetMethodIndex()); } } return NULL; @@ -1025,9 +1023,9 @@ AbstractMethod* Class::FindInterfaceMethod(const StringPiece& name, const Strin } int32_t iftable_count = GetIfTableCount(); - ObjectArray<InterfaceEntry>* iftable = GetIfTable(); + IfTable* iftable = GetIfTable(); for (int32_t i = 0; i < iftable_count; i++) { - method = iftable->Get(i)->GetInterface()->FindVirtualMethod(name, signature); + method = iftable->GetInterface(i)->FindVirtualMethod(name, signature); if (method != NULL) { return method; } @@ -1043,9 +1041,9 @@ AbstractMethod* Class::FindInterfaceMethod(const DexCache* dex_cache, uint32_t d } int32_t iftable_count = GetIfTableCount(); - ObjectArray<InterfaceEntry>* iftable = GetIfTable(); + IfTable* iftable = GetIfTable(); for (int32_t i = 0; i < iftable_count; i++) { - method = iftable->Get(i)->GetInterface()->FindVirtualMethod(dex_cache, dex_method_idx); + method = iftable->GetInterface(i)->FindVirtualMethod(dex_cache, dex_method_idx); if (method != NULL) { return method; } diff --git a/src/object.h b/src/object.h index 1af30ea7a0..43aed33183 100644 --- a/src/object.h +++ b/src/object.h @@ -44,7 +44,7 @@ class ClassLoader; class CodeAndDirectMethods; class DexCache; class Field; -class InterfaceEntry; +class IfTable; class Monitor; class Member; class AbstractMethod; @@ -1173,6 +1173,57 @@ ObjectArray<T>* ObjectArray<T>::CopyOf(Thread* self, int32_t new_length) { return new_array; } +class MANAGED IfTable : public ObjectArray<Object> { + public: + Class* GetInterface(int32_t i) const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + Class* interface = Get((i * kMax) + kInterface)->AsClass(); + DCHECK(interface != NULL); + return interface; + } + + void SetInterface(int32_t i, Class* interface) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + + ObjectArray<AbstractMethod>* GetMethodArray(int32_t i) const + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + ObjectArray<AbstractMethod>* method_array = + down_cast<ObjectArray<AbstractMethod>*>(Get((i * kMax) + kMethodArray)); + DCHECK(method_array != NULL); + return method_array; + } + + size_t GetMethodArrayCount(int32_t i) const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + ObjectArray<AbstractMethod>* method_array = + down_cast<ObjectArray<AbstractMethod>*>(Get((i * kMax) + kMethodArray)); + if (method_array == NULL) { + return 0; + } + return method_array->GetLength(); + } + + void SetMethodArray(int32_t i, ObjectArray<AbstractMethod>* new_ma) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + DCHECK(new_ma != NULL); + DCHECK(Get((i * kMax) + kMethodArray) == NULL); + Set((i * kMax) + kMethodArray, new_ma); + } + + size_t Count() const { + return GetLength() / kMax; + } + + enum { + // Points to the interface class. + kInterface = 0, + // Method pointers into the vtable, allow fast map from interface method index to concrete + // instance method. + kMethodArray = 1, + kMax = 2, + }; + + private: + DISALLOW_IMPLICIT_CONSTRUCTORS(IfTable); +}; + // Type for the InitializedStaticStorage table. Currently the Class // provides the static storage. However, this might change to an Array // to improve image sharing, so we use this type to avoid assumptions @@ -1746,20 +1797,18 @@ class MANAGED Class : public StaticStorageBase { SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); int32_t GetIfTableCount() const { - ObjectArray<InterfaceEntry>* iftable = GetIfTable(); + IfTable* iftable = GetIfTable(); if (iftable == NULL) { return 0; } - return iftable->GetLength(); + return iftable->Count(); } - ObjectArray<InterfaceEntry>* GetIfTable() const { - DCHECK(IsResolved() || IsErroneous()); - return GetFieldObject<ObjectArray<InterfaceEntry>*>( - OFFSET_OF_OBJECT_MEMBER(Class, iftable_), false); + IfTable* GetIfTable() const { + return GetFieldObject<IfTable*>(OFFSET_OF_OBJECT_MEMBER(Class, iftable_), false); } - void SetIfTable(ObjectArray<InterfaceEntry>* new_iftable) + void SetIfTable(IfTable* new_iftable) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { SetFieldObject(OFFSET_OF_OBJECT_MEMBER(Class, iftable_), new_iftable, false); } @@ -1970,20 +2019,18 @@ class MANAGED Class : public StaticStorageBase { // specifies the number of reference fields. ObjectArray<Field>* ifields_; - // Interface table (iftable_), one entry per interface supported by - // this class. That means one entry for each interface we support - // directly, indirectly via superclass, or indirectly via - // superinterface. This will be null if neither we nor our - // superclass implement any interfaces. + // The interface table (iftable_) contains pairs of a interface class and an array of the + // interface methods. There is one pair per interface supported by this class. That means one + // pair for each interface we support directly, indirectly via superclass, or indirectly via a + // superinterface. This will be null if neither we nor our superclass implement any interfaces. // - // Why we need this: given "class Foo implements Face", declare - // "Face faceObj = new Foo()". Invoke faceObj.blah(), where "blah" - // is part of the Face interface. We can't easily use a single - // vtable. + // Why we need this: given "class Foo implements Face", declare "Face faceObj = new Foo()". + // Invoke faceObj.blah(), where "blah" is part of the Face interface. We can't easily use a + // single vtable. // - // For every interface a concrete class implements, we create an array - // of the concrete vtable_ methods for the methods in the interface. - ObjectArray<InterfaceEntry>* iftable_; + // For every interface a concrete class implements, we create an array of the concrete vtable_ + // methods for the methods in the interface. + IfTable* iftable_; // descriptor for the class such as "java.lang.Class" or "[C". Lazily initialized by ComputeName String* name_; @@ -2246,6 +2293,13 @@ void ObjectArray<T>::Copy(const ObjectArray<T>* src, int src_pos, } } +inline void IfTable::SetInterface(int32_t i, Class* interface) { + DCHECK(interface != NULL); + DCHECK(interface->IsInterface()); + DCHECK(Get((i * kMax) + kInterface) == NULL); + Set((i * kMax) + kInterface, interface); +} + class MANAGED ClassClass : public Class { private: int32_t padding_; @@ -2648,60 +2702,6 @@ class MANAGED StackTraceElement : public Object { DISALLOW_IMPLICIT_CONSTRUCTORS(StackTraceElement); }; -class MANAGED InterfaceEntry : public ObjectArray<Object> { - public: - Class* GetInterface() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - Class* interface = Get(kInterface)->AsClass(); - DCHECK(interface != NULL); - return interface; - } - - void SetInterface(Class* interface) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - DCHECK(interface != NULL); - DCHECK(interface->IsInterface()); - DCHECK(Get(kInterface) == NULL); - Set(kInterface, interface); - } - - size_t GetMethodArrayCount() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - ObjectArray<AbstractMethod>* method_array = down_cast<ObjectArray<AbstractMethod>*>(Get(kMethodArray)); - if (method_array == NULL) { - return 0; - } - return method_array->GetLength(); - } - - ObjectArray<AbstractMethod>* GetMethodArray() const - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - ObjectArray<AbstractMethod>* method_array = down_cast<ObjectArray<AbstractMethod>*>(Get(kMethodArray)); - DCHECK(method_array != NULL); - return method_array; - } - - void SetMethodArray(ObjectArray<AbstractMethod>* new_ma) - SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - DCHECK(new_ma != NULL); - DCHECK(Get(kMethodArray) == NULL); - Set(kMethodArray, new_ma); - } - - static size_t LengthAsArray() { - return kMax; - } - - private: - enum { - // Points to the interface class. - kInterface = 0, - // Method pointers into the vtable, allow fast map from interface - // method index to concrete instance method. - kMethodArray = 1, - kMax = 2, - }; - - DISALLOW_IMPLICIT_CONSTRUCTORS(InterfaceEntry); -}; - class MANAGED SynthesizedProxyClass : public Class { public: ObjectArray<Class>* GetInterfaces() { diff --git a/src/object_utils.h b/src/object_utils.h index 1bbb7bc187..c6e71c3ae1 100644 --- a/src/object_utils.h +++ b/src/object_utils.h @@ -169,7 +169,7 @@ class ClassHelper { return GetClassLinker()->FindSystemClass("Ljava/io/Serializable;"); } } else if (klass_->IsProxyClass()) { - return klass_->GetIfTable()->Get(idx)->GetInterface(); + return klass_->GetIfTable()->GetInterface(idx); } else { uint16_t type_idx = GetDirectInterfaceTypeIdx(idx); Class* interface = GetDexCache()->GetResolvedType(type_idx); diff --git a/src/runtime_support.h b/src/runtime_support.h index 0d24e488ad..5a5cdcd3d8 100644 --- a/src/runtime_support.h +++ b/src/runtime_support.h @@ -74,7 +74,8 @@ static inline Object* AllocObjectFromCode(uint32_t type_idx, AbstractMethod* met return NULL; // Failure } } - if (!runtime->GetClassLinker()->EnsureInitialized(klass, true, true)) { + if (!klass->IsInitialized() && + !runtime->GetClassLinker()->EnsureInitialized(klass, true, true)) { DCHECK(self->IsExceptionPending()); return NULL; // Failure } diff --git a/src/timing_logger.h b/src/timing_logger.h index 5bc9d71354..7dc267105e 100644 --- a/src/timing_logger.h +++ b/src/timing_logger.h @@ -170,7 +170,7 @@ class CumulativeLogger { << FormatDuration(std_dev * kAdjust, tu) << " " << labels_[i] << "\n"; } uint64_t total_mean_x2 = total_time_squared_; - uint64_t mean_total_ns = GetTotalNs(); + uint64_t mean_total_ns = GetTotalTime(); if (iterations_ != 0) { total_mean_x2 /= iterations_; mean_total_ns /= iterations_; @@ -183,6 +183,12 @@ class CumulativeLogger { } uint64_t GetTotalNs() const { + return GetTotalTime() * kAdjust; + } + + private: + + uint64_t GetTotalTime() const { uint64_t total = 0; for (size_t i = 0; i < times_.size(); ++i) { total += times_[i]; @@ -190,7 +196,6 @@ class CumulativeLogger { return total; } - private: static const uint64_t kAdjust = 1000; std::string name_; bool precise_; diff --git a/src/verifier/method_verifier.cc b/src/verifier/method_verifier.cc index ad238b8310..67507bc3c6 100644 --- a/src/verifier/method_verifier.cc +++ b/src/verifier/method_verifier.cc @@ -1054,7 +1054,8 @@ void MethodVerifier::Dump(std::ostream& os) { os << "Native method\n"; return; } - DCHECK(code_item_ != NULL); + reg_types_.Dump(os); + os << "Dumping instructions and register lines:\n"; const Instruction* inst = Instruction::At(code_item_->insns_); for (size_t dex_pc = 0; dex_pc < code_item_->insns_size_in_code_units_; dex_pc += insn_flags_[dex_pc].GetLengthInCodeUnits()) { @@ -1128,7 +1129,7 @@ bool MethodVerifier::SetTypesFromSignature() { // it's effectively considered initialized the instant we reach here (in the sense that we // can return without doing anything or call virtual methods). { - const RegType& reg_type = reg_types_.FromDescriptor(class_loader_, descriptor); + const RegType& reg_type = reg_types_.FromDescriptor(class_loader_, descriptor, false); reg_line->SetRegisterType(arg_start + cur_arg, reg_type); } break; @@ -1527,7 +1528,8 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { const RegType& res_type = ResolveClassAndCheckAccess(dec_insn.vB); // Register holds class, ie its type is class, on error it will hold Conflict. work_line_->SetRegisterType(dec_insn.vA, - res_type.IsConflict() ? res_type : reg_types_.JavaLangClass()); + res_type.IsConflict() ? res_type + : reg_types_.JavaLangClass(true)); break; } case Instruction::MONITOR_ENTER: @@ -1667,7 +1669,7 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { break; case Instruction::THROW: { const RegType& res_type = work_line_->GetRegisterType(dec_insn.vA); - if (!reg_types_.JavaLangThrowable().IsAssignableFrom(res_type)) { + if (!reg_types_.JavaLangThrowable(false).IsAssignableFrom(res_type)) { Fail(VERIFY_ERROR_BAD_CLASS_SOFT) << "thrown class " << res_type << " not instanceof Throwable"; } break; @@ -1785,7 +1787,7 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { VerifyAGet(dec_insn, reg_types_.Long(), true); break; case Instruction::AGET_OBJECT: - VerifyAGet(dec_insn, reg_types_.JavaLangObject(), false); + VerifyAGet(dec_insn, reg_types_.JavaLangObject(false), false); break; case Instruction::APUT_BOOLEAN: @@ -1807,7 +1809,7 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { VerifyAPut(dec_insn, reg_types_.Long(), true); break; case Instruction::APUT_OBJECT: - VerifyAPut(dec_insn, reg_types_.JavaLangObject(), false); + VerifyAPut(dec_insn, reg_types_.JavaLangObject(false), false); break; case Instruction::IGET_BOOLEAN: @@ -1829,7 +1831,7 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { VerifyISGet(dec_insn, reg_types_.Long(), true, false); break; case Instruction::IGET_OBJECT: - VerifyISGet(dec_insn, reg_types_.JavaLangObject(), false, false); + VerifyISGet(dec_insn, reg_types_.JavaLangObject(false), false, false); break; case Instruction::IPUT_BOOLEAN: @@ -1851,7 +1853,7 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { VerifyISPut(dec_insn, reg_types_.Long(), true, false); break; case Instruction::IPUT_OBJECT: - VerifyISPut(dec_insn, reg_types_.JavaLangObject(), false, false); + VerifyISPut(dec_insn, reg_types_.JavaLangObject(false), false, false); break; case Instruction::SGET_BOOLEAN: @@ -1873,7 +1875,7 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { VerifyISGet(dec_insn, reg_types_.Long(), true, true); break; case Instruction::SGET_OBJECT: - VerifyISGet(dec_insn, reg_types_.JavaLangObject(), false, true); + VerifyISGet(dec_insn, reg_types_.JavaLangObject(false), false, true); break; case Instruction::SPUT_BOOLEAN: @@ -1895,7 +1897,7 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { VerifyISPut(dec_insn, reg_types_.Long(), true, true); break; case Instruction::SPUT_OBJECT: - VerifyISPut(dec_insn, reg_types_.JavaLangObject(), false, true); + VerifyISPut(dec_insn, reg_types_.JavaLangObject(false), false, true); break; case Instruction::INVOKE_VIRTUAL: @@ -1916,7 +1918,7 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { } else { descriptor = MethodHelper(called_method).GetReturnTypeDescriptor(); } - const RegType& return_type = reg_types_.FromDescriptor(class_loader_, descriptor); + const RegType& return_type = reg_types_.FromDescriptor(class_loader_, descriptor, false); work_line_->SetResultRegisterType(return_type); just_set_result = true; break; @@ -1977,7 +1979,8 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { */ work_line_->MarkRefsAsInitialized(this_type); } - const RegType& return_type = reg_types_.FromDescriptor(class_loader_, return_type_descriptor); + const RegType& return_type = reg_types_.FromDescriptor(class_loader_, return_type_descriptor, + false); work_line_->SetResultRegisterType(return_type); just_set_result = true; break; @@ -1995,7 +1998,7 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { } else { descriptor = MethodHelper(called_method).GetReturnTypeDescriptor(); } - const RegType& return_type = reg_types_.FromDescriptor(class_loader_, descriptor); + const RegType& return_type = reg_types_.FromDescriptor(class_loader_, descriptor, false); work_line_->SetResultRegisterType(return_type); just_set_result = true; } @@ -2045,7 +2048,7 @@ bool MethodVerifier::CodeFlowVerifyInstruction(uint32_t* start_guess) { } else { descriptor = MethodHelper(abs_method).GetReturnTypeDescriptor(); } - const RegType& return_type = reg_types_.FromDescriptor(class_loader_, descriptor); + const RegType& return_type = reg_types_.FromDescriptor(class_loader_, descriptor, false); work_line_->SetResultRegisterType(return_type); work_line_->SetResultRegisterType(return_type); just_set_result = true; @@ -2466,8 +2469,8 @@ const RegType& MethodVerifier::ResolveClassAndCheckAccess(uint32_t class_idx) { const RegType& referrer = GetDeclaringClass(); Class* klass = dex_cache_->GetResolvedType(class_idx); const RegType& result = - klass != NULL ? reg_types_.FromClass(klass) - : reg_types_.FromDescriptor(class_loader_, descriptor); + klass != NULL ? reg_types_.FromClass(klass, klass->IsFinal()) + : reg_types_.FromDescriptor(class_loader_, descriptor, false); if (result.IsConflict()) { Fail(VERIFY_ERROR_BAD_CLASS_SOFT) << "accessing broken descriptor '" << descriptor << "' in " << referrer; @@ -2495,14 +2498,14 @@ const RegType& MethodVerifier::GetCaughtExceptionType() { for (; iterator.HasNext(); iterator.Next()) { if (iterator.GetHandlerAddress() == (uint32_t) work_insn_idx_) { if (iterator.GetHandlerTypeIndex() == DexFile::kDexNoIndex16) { - common_super = ®_types_.JavaLangThrowable(); + common_super = ®_types_.JavaLangThrowable(false); } else { const RegType& exception = ResolveClassAndCheckAccess(iterator.GetHandlerTypeIndex()); if (common_super == NULL) { // Unconditionally assign for the first handler. We don't assert this is a Throwable // as that is caught at runtime common_super = &exception; - } else if (!reg_types_.JavaLangThrowable().IsAssignableFrom(exception)) { + } else if (!reg_types_.JavaLangThrowable(false).IsAssignableFrom(exception)) { // We don't know enough about the type and the common path merge will result in // Conflict. Fail here knowing the correct thing can be done at runtime. Fail(VERIFY_ERROR_BAD_CLASS_SOFT) << "unexpected non-exception class " << exception; @@ -2511,7 +2514,7 @@ const RegType& MethodVerifier::GetCaughtExceptionType() { // odd case, but nothing to do } else { common_super = &common_super->Merge(exception, ®_types_); - CHECK(reg_types_.JavaLangThrowable().IsAssignableFrom(*common_super)); + CHECK(reg_types_.JavaLangThrowable(false).IsAssignableFrom(*common_super)); } } } @@ -2677,7 +2680,8 @@ AbstractMethod* MethodVerifier::VerifyInvocationArgs(const DecodedInstruction& d return NULL; } if (method_type != METHOD_INTERFACE && !actual_arg_type.IsZero()) { - const RegType& res_method_class = reg_types_.FromClass(res_method->GetDeclaringClass()); + Class* klass = res_method->GetDeclaringClass(); + const RegType& res_method_class = reg_types_.FromClass(klass, klass->IsFinal()); if (!res_method_class.IsAssignableFrom(actual_arg_type)) { Fail(VERIFY_ERROR_BAD_CLASS_SOFT) << "'this' argument '" << actual_arg_type << "' not instance of '" << res_method_class << "'"; @@ -2707,7 +2711,7 @@ AbstractMethod* MethodVerifier::VerifyInvocationArgs(const DecodedInstruction& d << " missing signature component"; return NULL; } - const RegType& reg_type = reg_types_.FromDescriptor(class_loader_, descriptor); + const RegType& reg_type = reg_types_.FromDescriptor(class_loader_, descriptor, false); uint32_t get_reg = is_range ? dec_insn.vC + actual_args : dec_insn.arg[actual_args]; if (!work_line_->VerifyRegisterType(get_reg, reg_type)) { return res_method; @@ -2904,7 +2908,8 @@ Field* MethodVerifier::GetInstanceField(const RegType& obj_type, int field_idx) // Cannot infer and check type, however, access will cause null pointer exception return field; } else { - const RegType& field_klass = reg_types_.FromClass(field->GetDeclaringClass()); + Class* klass = field->GetDeclaringClass(); + const RegType& field_klass = reg_types_.FromClass(klass, klass->IsFinal()); if (obj_type.IsUninitializedTypes() && (!IsConstructor() || GetDeclaringClass().Equals(obj_type) || !field_klass.Equals(GetDeclaringClass()))) { @@ -2947,7 +2952,7 @@ void MethodVerifier::VerifyISGet(const DecodedInstruction& dec_insn, descriptor = dex_file_->GetFieldTypeDescriptor(field_id); loader = class_loader_; } - const RegType& field_type = reg_types_.FromDescriptor(loader, descriptor); + const RegType& field_type = reg_types_.FromDescriptor(loader, descriptor, false); if (is_primitive) { if (field_type.Equals(insn_type) || (field_type.IsFloat() && insn_type.IsIntegralTypes()) || @@ -2996,7 +3001,7 @@ void MethodVerifier::VerifyISPut(const DecodedInstruction& dec_insn, descriptor = dex_file_->GetFieldTypeDescriptor(field_id); loader = class_loader_; } - const RegType& field_type = reg_types_.FromDescriptor(loader, descriptor); + const RegType& field_type = reg_types_.FromDescriptor(loader, descriptor, false); if (field != NULL) { if (field->IsFinal() && field->GetDeclaringClass() != GetDeclaringClass().GetClass()) { Fail(VERIFY_ERROR_ACCESS_FIELD) << "cannot modify final field " << PrettyField(field) @@ -3104,16 +3109,17 @@ const RegType& MethodVerifier::GetMethodReturnType() { const DexFile::ProtoId& proto_id = dex_file_->GetMethodPrototype(method_id); uint16_t return_type_idx = proto_id.return_type_idx_; const char* descriptor = dex_file_->GetTypeDescriptor(dex_file_->GetTypeId(return_type_idx)); - return reg_types_.FromDescriptor(class_loader_, descriptor); + return reg_types_.FromDescriptor(class_loader_, descriptor, false); } const RegType& MethodVerifier::GetDeclaringClass() { if (foo_method_ != NULL) { - return reg_types_.FromClass(foo_method_->GetDeclaringClass()); + Class* klass = foo_method_->GetDeclaringClass(); + return reg_types_.FromClass(klass, klass->IsFinal()); } else { const DexFile::MethodId& method_id = dex_file_->GetMethodId(method_idx_); const char* descriptor = dex_file_->GetTypeDescriptor(dex_file_->GetTypeId(method_id.class_idx_)); - return reg_types_.FromDescriptor(class_loader_, descriptor); + return reg_types_.FromDescriptor(class_loader_, descriptor, false); } } diff --git a/src/verifier/reg_type.cc b/src/verifier/reg_type.cc index f5552233d0..e02fbf47d0 100644 --- a/src/verifier/reg_type.cc +++ b/src/verifier/reg_type.cc @@ -46,11 +46,12 @@ static const char* type_strings[] = { "Unresolved Merged References", "Unresolved Super Class", "Reference", + "Precise Reference", }; std::string RegType::Dump(const RegTypeCache* reg_types) const { - DCHECK(type_ >= kRegTypeUndefined && type_ <= kRegTypeReference); - DCHECK(arraysize(type_strings) == (kRegTypeReference + 1)); + DCHECK(type_ >= kRegTypeUndefined && type_ <= kRegTypePreciseReference); + DCHECK(arraysize(type_strings) == (kRegTypePreciseReference + 1)); std::string result; if (IsUnresolvedMergedReference()) { if (reg_types == NULL) { @@ -142,7 +143,7 @@ const RegType& RegType::GetSuperClass(RegTypeCache* cache) const { if (!IsUnresolvedTypes()) { Class* super_klass = GetClass()->GetSuperClass(); if (super_klass != NULL) { - return cache->FromClass(super_klass); + return cache->FromClass(super_klass, IsPreciseReference()); } else { return cache->Zero(); } @@ -150,7 +151,7 @@ const RegType& RegType::GetSuperClass(RegTypeCache* cache) const { if (!IsUnresolvedMergedReference() && !IsUnresolvedSuperClass() && GetDescriptor()->CharAt(0) == '[') { // Super class of all arrays is Object. - return cache->JavaLangObject(); + return cache->JavaLangObject(true); } else { return cache->FromUnresolvedSuperClass(*this); } @@ -301,7 +302,7 @@ const RegType& RegType::Merge(const RegType& incoming_type, RegTypeCache* reg_ty if (IsZero() || incoming_type.IsZero()) { return SelectNonConstant(*this, incoming_type); // 0 MERGE ref => ref } else if (IsJavaLangObject() || incoming_type.IsJavaLangObject()) { - return reg_types->JavaLangObject(); // Object MERGE ref => Object + return reg_types->JavaLangObject(false); // Object MERGE ref => Object } else if (IsUnresolvedTypes() || incoming_type.IsUnresolvedTypes()) { // We know how to merge an unresolved type with itself, 0 or Object. In this case we // have two sub-classes and don't know how to merge. Create a new string-based unresolved @@ -319,12 +320,12 @@ const RegType& RegType::Merge(const RegType& incoming_type, RegTypeCache* reg_ty DCHECK(c1 != NULL && !c1->IsPrimitive()); DCHECK(c2 != NULL && !c2->IsPrimitive()); Class* join_class = ClassJoin(c1, c2); - if (c1 == join_class) { + if (c1 == join_class && !IsPreciseReference()) { return *this; - } else if (c2 == join_class) { + } else if (c2 == join_class && !incoming_type.IsPreciseReference()) { return incoming_type; } else { - return reg_types->FromClass(join_class); + return reg_types->FromClass(join_class, false); } } } else { diff --git a/src/verifier/reg_type.h b/src/verifier/reg_type.h index 3064f30ae3..205867d4d1 100644 --- a/src/verifier/reg_type.h +++ b/src/verifier/reg_type.h @@ -65,6 +65,7 @@ class RegType { kRegTypeUnresolvedMergedReference, // Tree of merged references (at least 1 is unresolved). kRegTypeUnresolvedSuperClass, // Super class of an unresolved type. kRegTypeReference, // Reference type. + kRegTypePreciseReference, // Precisely the given type. }; Type GetType() const { @@ -93,6 +94,8 @@ class RegType { bool IsUnresolvedMergedReference() const { return type_ == kRegTypeUnresolvedMergedReference; } bool IsUnresolvedSuperClass() const { return type_ == kRegTypeUnresolvedSuperClass; } bool IsReference() const { return type_ == kRegTypeReference; } + bool IsPreciseReference() const { return type_ == kRegTypePreciseReference; } + bool IsUninitializedTypes() const { return IsUninitializedReference() || IsUninitializedThisReference() || IsUnresolvedAndUninitializedReference() || IsUnresolvedAndUninitializedThisReference(); @@ -154,7 +157,7 @@ class RegType { } bool IsNonZeroReferenceTypes() const { - return IsReference() || IsUnresolvedReference() || + return IsReference() || IsPreciseReference() || IsUnresolvedReference() || IsUninitializedReference() || IsUninitializedThisReference() || IsUnresolvedAndUninitializedReference() || IsUnresolvedAndUninitializedThisReference() || IsUnresolvedMergedReference() || IsUnresolvedSuperClass(); @@ -198,6 +201,10 @@ class RegType { return allocation_pc_or_constant_or_merged_types_; } + bool HasClass() const { + return IsReference() || IsPreciseReference(); + } + Class* GetClass() const { DCHECK(!IsUnresolvedReference()); DCHECK(klass_or_descriptor_ != NULL); @@ -212,7 +219,7 @@ class RegType { bool IsArrayTypes() const SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { if (IsUnresolvedTypes() && !IsUnresolvedMergedReference() && !IsUnresolvedSuperClass()) { return GetDescriptor()->CharAt(0) == '['; - } else if (IsReference()) { + } else if (HasClass()) { return GetClass()->IsArrayClass(); } else { return false; @@ -224,7 +231,7 @@ class RegType { // Primitive arrays will always resolve DCHECK(GetDescriptor()->CharAt(1) == 'L' || GetDescriptor()->CharAt(1) == '['); return GetDescriptor()->CharAt(0) == '['; - } else if (IsReference()) { + } else if (HasClass()) { Class* type = GetClass(); return type->IsArrayClass() && !type->GetComponentType()->IsPrimitive(); } else { @@ -256,7 +263,7 @@ class RegType { } bool IsJavaLangObjectArray() const { - if (IsReference()) { + if (HasClass()) { Class* type = GetClass(); return type->IsArrayClass() && type->GetComponentType()->IsObjectClass(); } diff --git a/src/verifier/reg_type_cache.cc b/src/verifier/reg_type_cache.cc index 37086c9acc..847cde90af 100644 --- a/src/verifier/reg_type_cache.cc +++ b/src/verifier/reg_type_cache.cc @@ -57,11 +57,18 @@ static RegType::Type RegTypeFromDescriptor(const std::string& descriptor) { } } -const RegType& RegTypeCache::FromDescriptor(ClassLoader* loader, const char* descriptor) { - return From(RegTypeFromDescriptor(descriptor), loader, descriptor); +const RegType& RegTypeCache::FromDescriptor(ClassLoader* loader, const char* descriptor, + bool precise) { + return From(RegTypeFromDescriptor(descriptor), loader, descriptor, precise); } -const RegType& RegTypeCache::From(RegType::Type type, ClassLoader* loader, const char* descriptor) { +static bool MatchingPrecisionForClass(RegType* entry, bool precise) + SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + return (entry->IsPreciseReference() == precise) || (entry->GetClass()->IsFinal() && !precise); +} + +const RegType& RegTypeCache::From(RegType::Type type, ClassLoader* loader, const char* descriptor, + bool precise) { if (type <= RegType::kRegTypeLastFixedLocation) { // entries should be sized greater than primitive types DCHECK_GT(entries_.size(), static_cast<size_t>(type)); @@ -76,14 +83,15 @@ const RegType& RegTypeCache::From(RegType::Type type, ClassLoader* loader, const } return *entry; } else { - DCHECK(type == RegType::kRegTypeReference); + DCHECK(type == RegType::kRegTypeReference || type == RegType::kRegTypePreciseReference); ClassHelper kh; for (size_t i = RegType::kRegTypeLastFixedLocation + 1; i < entries_.size(); i++) { RegType* cur_entry = entries_[i]; // check resolved and unresolved references, ignore uninitialized references - if (cur_entry->IsReference()) { + if (cur_entry->HasClass()) { kh.ChangeClass(cur_entry->GetClass()); - if (strcmp(descriptor, kh.GetDescriptor()) == 0) { + if (MatchingPrecisionForClass(cur_entry, precise) && + (strcmp(descriptor, kh.GetDescriptor()) == 0)) { return *cur_entry; } } else if (cur_entry->IsUnresolvedReference() && @@ -93,8 +101,11 @@ const RegType& RegTypeCache::From(RegType::Type type, ClassLoader* loader, const } Class* klass = Runtime::Current()->GetClassLinker()->FindClass(descriptor, loader); if (klass != NULL) { - // Able to resolve so create resolved register type - RegType* entry = new RegType(type, klass, 0, entries_.size()); + // Able to resolve so create resolved register type that is precise if we + // know the type is final. + RegType* entry = new RegType(klass->IsFinal() ? RegType::kRegTypePreciseReference + : RegType::kRegTypeReference, + klass, 0, entries_.size()); entries_.push_back(entry); return *entry; } else { @@ -119,7 +130,7 @@ const RegType& RegTypeCache::From(RegType::Type type, ClassLoader* loader, const } } -const RegType& RegTypeCache::FromClass(Class* klass) { +const RegType& RegTypeCache::FromClass(Class* klass, bool precise) { if (klass->IsPrimitive()) { RegType::Type type = RegTypeFromPrimitiveType(klass->GetPrimitiveType()); // entries should be sized greater than primitive types @@ -133,11 +144,14 @@ const RegType& RegTypeCache::FromClass(Class* klass) { } else { for (size_t i = RegType::kRegTypeLastFixedLocation + 1; i < entries_.size(); i++) { RegType* cur_entry = entries_[i]; - if (cur_entry->IsReference() && cur_entry->GetClass() == klass) { + if ((cur_entry->HasClass()) && + MatchingPrecisionForClass(cur_entry, precise) && cur_entry->GetClass() == klass) { return *cur_entry; } } - RegType* entry = new RegType(RegType::kRegTypeReference, klass, 0, entries_.size()); + RegType* entry = new RegType(precise ? RegType::kRegTypePreciseReference + : RegType::kRegTypeReference, + klass, 0, entries_.size()); entries_.push_back(entry); return *entry; } @@ -243,11 +257,11 @@ const RegType& RegTypeCache::FromUninitialized(const RegType& uninit_type) { Class* klass = uninit_type.GetClass(); for (size_t i = RegType::kRegTypeLastFixedLocation + 1; i < entries_.size(); i++) { RegType* cur_entry = entries_[i]; - if (cur_entry->IsReference() && cur_entry->GetClass() == klass) { + if (cur_entry->IsPreciseReference() && cur_entry->GetClass() == klass) { return *cur_entry; } } - entry = new RegType(RegType::kRegTypeReference, klass, 0, entries_.size()); + entry = new RegType(RegType::kRegTypePreciseReference, klass, 0, entries_.size()); } entries_.push_back(entry); return *entry; @@ -284,17 +298,17 @@ const RegType& RegTypeCache::UninitializedThisArgument(const RegType& type) { const RegType& RegTypeCache::FromType(RegType::Type type) { CHECK(type < RegType::kRegTypeReference); switch (type) { - case RegType::kRegTypeBoolean: return From(type, NULL, "Z"); - case RegType::kRegTypeByte: return From(type, NULL, "B"); - case RegType::kRegTypeShort: return From(type, NULL, "S"); - case RegType::kRegTypeChar: return From(type, NULL, "C"); - case RegType::kRegTypeInteger: return From(type, NULL, "I"); - case RegType::kRegTypeFloat: return From(type, NULL, "F"); + case RegType::kRegTypeBoolean: return From(type, NULL, "Z", true); + case RegType::kRegTypeByte: return From(type, NULL, "B", true); + case RegType::kRegTypeShort: return From(type, NULL, "S", true); + case RegType::kRegTypeChar: return From(type, NULL, "C", true); + case RegType::kRegTypeInteger: return From(type, NULL, "I", true); + case RegType::kRegTypeFloat: return From(type, NULL, "F", true); case RegType::kRegTypeLongLo: - case RegType::kRegTypeLongHi: return From(type, NULL, "J"); + case RegType::kRegTypeLongHi: return From(type, NULL, "J", true); case RegType::kRegTypeDoubleLo: - case RegType::kRegTypeDoubleHi: return From(type, NULL, "D"); - default: return From(type, NULL, ""); + case RegType::kRegTypeDoubleHi: return From(type, NULL, "D", true); + default: return From(type, NULL, "", true); } } @@ -315,9 +329,20 @@ const RegType& RegTypeCache::GetComponentType(const RegType& array, ClassLoader* if (array.IsUnresolvedTypes()) { std::string descriptor(array.GetDescriptor()->ToModifiedUtf8()); std::string component(descriptor.substr(1, descriptor.size() - 1)); - return FromDescriptor(loader, component.c_str()); + return FromDescriptor(loader, component.c_str(), false); } else { - return FromClass(array.GetClass()->GetComponentType()); + Class* klass = array.GetClass()->GetComponentType(); + return FromClass(klass, klass->IsFinal()); + } +} + +void RegTypeCache::Dump(std::ostream& os) { + os << "Register Types:\n"; + for (size_t i = 0; i < entries_.size(); i++) { + RegType* cur_entry = entries_[i]; + if (cur_entry != NULL) { + os << "\t" << i << ": " << cur_entry->Dump() << "\n"; + } } } diff --git a/src/verifier/reg_type_cache.h b/src/verifier/reg_type_cache.h index 5a2c49cc75..36fd2c75bc 100644 --- a/src/verifier/reg_type_cache.h +++ b/src/verifier/reg_type_cache.h @@ -40,12 +40,12 @@ class RegTypeCache { return *result; } - const RegType& From(RegType::Type type, ClassLoader* loader, const char* descriptor) + const RegType& From(RegType::Type type, ClassLoader* loader, const char* descriptor, bool precise) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); - const RegType& FromClass(Class* klass) + const RegType& FromClass(Class* klass, bool precise) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); const RegType& FromCat1Const(int32_t value); - const RegType& FromDescriptor(ClassLoader* loader, const char* descriptor) + const RegType& FromDescriptor(ClassLoader* loader, const char* descriptor, bool precise) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); const RegType& FromType(RegType::Type) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); @@ -77,17 +77,24 @@ class RegTypeCache { return FromType(RegType::kRegTypeDoubleLo); } - const RegType& JavaLangClass() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - return From(RegType::kRegTypeReference, NULL, "Ljava/lang/Class;"); + const RegType& JavaLangClass(bool precise) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + return From(precise ? RegType::kRegTypeReference + : RegType::kRegTypePreciseReference, + NULL, "Ljava/lang/Class;", precise); } - const RegType& JavaLangObject() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - return From(RegType::kRegTypeReference, NULL, "Ljava/lang/Object;"); + const RegType& JavaLangObject(bool precise) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + return From(precise ? RegType::kRegTypeReference + : RegType::kRegTypePreciseReference, + NULL, "Ljava/lang/Object;", precise); } const RegType& JavaLangString() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - return From(RegType::kRegTypeReference, NULL, "Ljava/lang/String;"); + // String is final and therefore always precise. + return From(RegType::kRegTypePreciseReference, NULL, "Ljava/lang/String;", true); } - const RegType& JavaLangThrowable() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - return From(RegType::kRegTypeReference, NULL, "Ljava/lang/Throwable;"); + const RegType& JavaLangThrowable(bool precise) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { + return From(precise ? RegType::kRegTypeReference + : RegType::kRegTypePreciseReference, + NULL, "Ljava/lang/Throwable;", precise); } const RegType& Undefined() SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { @@ -118,6 +125,8 @@ class RegTypeCache { const RegType& GetComponentType(const RegType& array, ClassLoader* loader) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + void Dump(std::ostream& os) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); + private: // The allocated entries std::vector<RegType*> entries_; |