Faster access to unresolved classes from compiled code.
Add two new load kinds to LoadClass, similar to kBssEntry
but using the access-checking entrypoint on the slow-path.
One is used for classes that are in the literal package and
the other for classes outside the literal package of the
compiling class. Associate new .bss entries with these load
kinds and update them from entrypoints based on the resolved
class properties. If the resolved class is public, both
types of entries can be updated, otherwise only the package
local entry can be updated and only if the defining class
loader of the class is the same as the caller's defining
class loader (which is identical for all code in an oat
file) because the run time access check for same package
requires both class loader and literal package name match.
Test: Additional tests in 727-checker-unresolved-class.
Test: m test-art-host-gtest
Test: testrunner.py --host --optimizing
Test: aosp_blueline-userdebug boots.
Test: run-gtests.sh
Test: testrunner.py --target --optimizing
Bug: 161898207
Change-Id: I281e06ac2825caf81c6d7ee3128af833abd39992
diff --git a/compiler/optimizing/sharpening.cc b/compiler/optimizing/sharpening.cc
index 393369d..f570c60 100644
--- a/compiler/optimizing/sharpening.cc
+++ b/compiler/optimizing/sharpening.cc
@@ -158,24 +158,55 @@
load_class->GetLoadKind() == HLoadClass::LoadKind::kReferrersClass)
<< load_class->GetLoadKind();
DCHECK(!load_class->IsInBootImage()) << "HLoadClass should not be optimized before sharpening.";
+ const DexFile& dex_file = load_class->GetDexFile();
+ dex::TypeIndex type_index = load_class->GetTypeIndex();
+ const CompilerOptions& compiler_options = codegen->GetCompilerOptions();
- HLoadClass::LoadKind load_kind = load_class->GetLoadKind();
+ bool is_in_boot_image = false;
+ HLoadClass::LoadKind desired_load_kind = HLoadClass::LoadKind::kInvalid;
- if (load_class->NeedsAccessCheck()) {
- // We need to call the runtime anyway, so we simply get the class as that call's return value.
- } else if (load_kind == HLoadClass::LoadKind::kReferrersClass) {
+ if (load_class->GetLoadKind() == HLoadClass::LoadKind::kReferrersClass) {
+ DCHECK(!load_class->NeedsAccessCheck());
// Loading from the ArtMethod* is the most efficient retrieval in code size.
// TODO: This may not actually be true for all architectures and
// locations of target classes. The additional register pressure
// for using the ArtMethod* should be considered.
+ desired_load_kind = HLoadClass::LoadKind::kReferrersClass;
+ } else if (load_class->NeedsAccessCheck()) {
+ DCHECK_EQ(load_class->GetLoadKind(), HLoadClass::LoadKind::kRuntimeCall);
+ if (klass != nullptr) {
+ // Resolved class that needs access check must be really inaccessible
+ // and the access check is bound to fail. Just emit the runtime call.
+ desired_load_kind = HLoadClass::LoadKind::kRuntimeCall;
+ } else if (compiler_options.IsJitCompiler()) {
+ // Unresolved class while JITting means that either we never hit this
+ // instruction or it failed. Either way, just emit the runtime call.
+ // (Though we could consider emitting Deoptimize instead and
+ // recompile if the instruction succeeds in interpreter.)
+ desired_load_kind = HLoadClass::LoadKind::kRuntimeCall;
+ } else {
+ // For AOT, check if the class is in the same literal package as the
+ // compiling class and pick an appropriate .bss entry.
+ auto package_length = [](const char* descriptor) {
+ const char* slash_pos = strrchr(descriptor, '/');
+ return (slash_pos != nullptr) ? static_cast<size_t>(slash_pos - descriptor) : 0u;
+ };
+ const char* klass_descriptor = dex_file.StringByTypeIdx(type_index);
+ const uint32_t klass_package_length = package_length(klass_descriptor);
+ const DexFile* referrer_dex_file = dex_compilation_unit.GetDexFile();
+ const dex::TypeIndex referrer_type_index =
+ referrer_dex_file->GetClassDef(dex_compilation_unit.GetClassDefIndex()).class_idx_;
+ const char* referrer_descriptor = referrer_dex_file->StringByTypeIdx(referrer_type_index);
+ const uint32_t referrer_package_length = package_length(referrer_descriptor);
+ bool same_package =
+ (referrer_package_length == klass_package_length) &&
+ memcmp(referrer_descriptor, klass_descriptor, referrer_package_length) == 0;
+ desired_load_kind = same_package
+ ? HLoadClass::LoadKind::kBssEntryPackage
+ : HLoadClass::LoadKind::kBssEntryPublic;
+ }
} else {
- const DexFile& dex_file = load_class->GetDexFile();
- dex::TypeIndex type_index = load_class->GetTypeIndex();
-
- bool is_in_boot_image = false;
- HLoadClass::LoadKind desired_load_kind = HLoadClass::LoadKind::kInvalid;
Runtime* runtime = Runtime::Current();
- const CompilerOptions& compiler_options = codegen->GetCompilerOptions();
if (compiler_options.IsBootImage() || compiler_options.IsBootImageExtension()) {
// Compiling boot image or boot image extension. Check if the class is a boot image class.
DCHECK(!compiler_options.IsJitCompiler());
@@ -227,17 +258,19 @@
desired_load_kind = HLoadClass::LoadKind::kBssEntry;
}
}
- DCHECK_NE(desired_load_kind, HLoadClass::LoadKind::kInvalid);
-
- if (is_in_boot_image) {
- load_class->MarkInBootImage();
- }
- load_kind = codegen->GetSupportedLoadClassKind(desired_load_kind);
}
+ DCHECK_NE(desired_load_kind, HLoadClass::LoadKind::kInvalid);
+
+ if (is_in_boot_image) {
+ load_class->MarkInBootImage();
+ }
+ HLoadClass::LoadKind load_kind = codegen->GetSupportedLoadClassKind(desired_load_kind);
if (!IsSameDexFile(load_class->GetDexFile(), *dex_compilation_unit.GetDexFile())) {
- if ((load_kind == HLoadClass::LoadKind::kRuntimeCall) ||
- (load_kind == HLoadClass::LoadKind::kBssEntry)) {
+ if (load_kind == HLoadClass::LoadKind::kRuntimeCall ||
+ load_kind == HLoadClass::LoadKind::kBssEntry ||
+ load_kind == HLoadClass::LoadKind::kBssEntryPublic ||
+ load_kind == HLoadClass::LoadKind::kBssEntryPackage) {
// We actually cannot reference this class, we're forced to bail.
// We cannot reference this class with Bss, as the entrypoint will lookup the class
// in the caller's dex file, but that dex file does not reference the class.