Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save 3Nigma/eded1949ca36f51c6bc8 to your computer and use it in GitHub Desktop.
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
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);
}
}
@3Nigma
Copy link
Author

3Nigma commented Jun 11, 2014

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.

@3Nigma
Copy link
Author

3Nigma commented Jun 11, 2014

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

@3Nigma
Copy link
Author

3Nigma commented Jun 11, 2014

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 declarations
  • class_linker.cc:2826 referes to classes (resides in a method called CreateProxyClass
  • class_linker.cc:2853 resides in the same area as line 2826
  • class_linker.cc:2863 lays still in the CreateProxyClass method
  • class_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