Created
June 11, 2014 08:49
-
-
Save 3Nigma/eded1949ca36f51c6bc8 to your computer and use it in GitHub Desktop.
Method used to analyze the triggering of the new "return-void-barrier" opcode in Google's Android ART distribution
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
static void ResolveClassFieldsAndMethods(const ParallelCompilationManager* manager, | |
size_t class_def_index) | |
LOCKS_EXCLUDED(Locks::mutator_lock_) { | |
ATRACE_CALL(); | |
Thread* self = Thread::Current(); | |
jobject jclass_loader = manager->GetClassLoader(); | |
const DexFile& dex_file = *manager->GetDexFile(); | |
ClassLinker* class_linker = manager->GetClassLinker(); | |
// If an instance field is final then we need to have a barrier on the return, static final | |
// fields are assigned within the lock held for class initialization. Conservatively assume | |
// constructor barriers are always required. | |
bool requires_constructor_barrier = true; | |
// Method and Field are the worst. We can't resolve without either | |
// context from the code use (to disambiguate virtual vs direct | |
// method and instance vs static field) or from class | |
// definitions. While the compiler will resolve what it can as it | |
// needs it, here we try to resolve fields and methods used in class | |
// definitions, since many of them many never be referenced by | |
// generated code. | |
const DexFile::ClassDef& class_def = dex_file.GetClassDef(class_def_index); | |
if (!SkipClass(class_linker, jclass_loader, dex_file, class_def)) { | |
ScopedObjectAccess soa(self); | |
StackHandleScope<2> hs(soa.Self()); | |
Handle<mirror::ClassLoader> class_loader( | |
hs.NewHandle(soa.Decode<mirror::ClassLoader*>(jclass_loader))); | |
Handle<mirror::DexCache> dex_cache(hs.NewHandle(class_linker->FindDexCache(dex_file))); | |
// Resolve the class. | |
mirror::Class* klass = class_linker->ResolveType(dex_file, class_def.class_idx_, dex_cache, | |
class_loader); | |
bool resolve_fields_and_methods; | |
if (klass == NULL) { | |
// Class couldn't be resolved, for example, super-class is in a different dex file. Don't | |
// attempt to resolve methods and fields when there is no declaring class. | |
CHECK(soa.Self()->IsExceptionPending()); | |
soa.Self()->ClearException(); | |
resolve_fields_and_methods = false; | |
} else { | |
resolve_fields_and_methods = manager->GetCompiler()->IsImage(); | |
} | |
// Note the class_data pointer advances through the headers, | |
// static fields, instance fields, direct methods, and virtual | |
// methods. | |
const byte* class_data = dex_file.GetClassData(class_def); | |
if (class_data == NULL) { | |
// Empty class such as a marker interface. | |
requires_constructor_barrier = false; | |
} else { | |
ClassDataItemIterator it(dex_file, class_data); | |
while (it.HasNextStaticField()) { | |
if (resolve_fields_and_methods) { | |
mirror::ArtField* field = class_linker->ResolveField(dex_file, it.GetMemberIndex(), | |
dex_cache, class_loader, true); | |
if (field == NULL) { | |
CHECK(soa.Self()->IsExceptionPending()); | |
soa.Self()->ClearException(); | |
} | |
} | |
it.Next(); | |
} | |
// We require a constructor barrier if there are final instance fields. | |
requires_constructor_barrier = false; | |
while (it.HasNextInstanceField()) { | |
if ((it.GetMemberAccessFlags() & kAccFinal) != 0) { | |
requires_constructor_barrier = true; | |
} | |
if (resolve_fields_and_methods) { | |
mirror::ArtField* field = class_linker->ResolveField(dex_file, it.GetMemberIndex(), | |
dex_cache, class_loader, false); | |
if (field == NULL) { | |
CHECK(soa.Self()->IsExceptionPending()); | |
soa.Self()->ClearException(); | |
} | |
} | |
it.Next(); | |
} | |
if (resolve_fields_and_methods) { | |
while (it.HasNextDirectMethod()) { | |
mirror::ArtMethod* method = class_linker->ResolveMethod(dex_file, it.GetMemberIndex(), | |
dex_cache, class_loader, | |
NullHandle<mirror::ArtMethod>(), | |
it.GetMethodInvokeType(class_def)); | |
if (method == NULL) { | |
CHECK(soa.Self()->IsExceptionPending()); | |
soa.Self()->ClearException(); | |
} | |
it.Next(); | |
} | |
while (it.HasNextVirtualMethod()) { | |
mirror::ArtMethod* method = class_linker->ResolveMethod(dex_file, it.GetMemberIndex(), | |
dex_cache, class_loader, | |
NullHandle<mirror::ArtMethod>(), | |
it.GetMethodInvokeType(class_def)); | |
if (method == NULL) { | |
CHECK(soa.Self()->IsExceptionPending()); | |
soa.Self()->ClearException(); | |
} | |
it.Next(); | |
} | |
DCHECK(!it.HasNext()); | |
} | |
} | |
} | |
if (requires_constructor_barrier) { | |
manager->GetCompiler()->AddRequiresConstructorBarrier(self, &dex_file, class_def_index); | |
} | |
} |
requires_constructor_barrier gets TRUE only in 2 situations:
- SkipClass(class_linker, jclass_loader, dex_file, class_def) is FALSE
- Class is not empty as it is the case for marker classes AND Class has instance fields AND Member access is final
(it.GetMemberAccessFlags() & kAccFinal) != 0
Running grep -rnw ./ -e "kAccFinal"
inside the android art-platform source tree (v4.4.3) yeilded the following occurances:
./compiler/driver/compiler_driver.cc:1530: if ((it.GetMemberAccessFlags() & kAccFinal) != 0) {
./runtime/class_linker.cc:2195: primitive_class->SetAccessFlags(kAccPublic | kAccFinal | kAccAbstract);
./runtime/class_linker.cc:2324: access_flags |= kAccAbstract | kAccFinal;
./runtime/class_linker.cc:2826: klass->SetAccessFlags(kAccClassIsProxy | kAccPublic | kAccFinal);
./runtime/class_linker.cc:2853: interfaces_sfield->SetAccessFlags(kAccStatic | kAccPublic | kAccFinal);
./runtime/class_linker.cc:2863: throws_sfield->SetAccessFlags(kAccStatic | kAccPublic | kAccFinal);
./runtime/class_linker.cc:3041: method->SetAccessFlags((method->GetAccessFlags() & ~kAccAbstract) | kAccFinal);
./runtime/class_linker_test.cc:93: EXPECT_EQ(kAccPublic | kAccFinal | kAccAbstract, primitive->GetAccessFlags());
./runtime/class_linker_test.cc:107: EXPECT_EQ(kAccFinal | kAccAbstract, (array->GetAccessFlags() & (kAccFinal | kAccAbstract)));
./runtime/dex_file_verifier.cc:342: kAccFinal | kAccVolatile | kAccTransient | kAccSynthetic | kAccEnum;
./runtime/dex_file_verifier.cc:368: kAccFinal | kAccSynchronized | kAccBridge | kAccVarargs | kAccNative | kAccAbstract |
./runtime/mirror/art_field.h:61: return (GetAccessFlags() & kAccFinal) != 0;
./runtime/mirror/art_method.h:107: return (GetAccessFlags() & kAccFinal) != 0;
./runtime/mirror/class.h:206: return (GetAccessFlags() & kAccFinal) != 0;
./runtime/modifiers.h:26:static const uint32_t kAccFinal = 0x0010; // class, field, method, ic
Further analysis of these results concluded:
class_linker.cc:2324
referes to class declarationsclass_linker.cc:2826
referes to classes (resides in a method calledCreateProxyClass
class_linker.cc:2853
resides in the same area as line 2826class_linker.cc:2863
lays still in theCreateProxyClass
methodclass_linker.cc:3041
referes to a method declaration
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
manager->GetCompiler()->AddRequiresConstructorBarrier(self, &dex_file, class_def_index); (line 106) is where the actual substitution from "return-void" to "return-void-barrier" takes place, but the decision weather or not to make the substitution in the first place is taken here.
From the snippet, we can see that the method call is only executed when requires_constructor_barrier == TRUE.