ART: Use the bitstring type check for AOT app compilation.
For boot image target classes that have their bitstring
already assigned in the boot image.
The size of the services.odex for aosp_taimen-userdebug:
- before:
- arm64: 20988640
- after:
- arm64: 20968016 (-20KiB, -0.1%)
(There is no arm version, only arm64.)
Test: New test case in 552-checker-sharpening.
Test: m test-art-host-gtest
Test: testrunner.py --host --optimizing
Test: Pixel 2 XL boots.
Test: testrunner.py --target --optimizing
Bug: 64692057
Change-Id: I9585efca8ba0df15400e7536e5e2cc76aca13e8d
diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc
index 70cbb01..273bd50 100644
--- a/compiler/driver/compiler_driver.cc
+++ b/compiler/driver/compiler_driver.cc
@@ -872,6 +872,14 @@
TimingLogger* timings) {
CheckThreadPools();
+ if (kUseBitstringTypeCheck &&
+ !compiler_options_->IsBootImage() &&
+ compiler_options_->IsAotCompilationEnabled()) {
+ RecordBootImageClassesWithAssignedBitstring();
+ VLOG(compiler) << "RecordBootImageClassesWithAssignedBitstring: "
+ << GetMemoryUsageString(false);
+ }
+
LoadImageClasses(timings);
VLOG(compiler) << "LoadImageClasses: " << GetMemoryUsageString(false);
@@ -940,6 +948,43 @@
}
}
+void CompilerDriver::RecordBootImageClassesWithAssignedBitstring() {
+ if (boot_image_classes_with_assigned_bitstring_ != nullptr) {
+ return; // Already recorded. (Happens because of class unloading between dex files.)
+ }
+
+ class Visitor : public ClassVisitor {
+ public:
+ explicit Visitor(std::unordered_set<mirror::Class*>* recorded_classes)
+ : recorded_classes_(recorded_classes) {}
+
+ bool operator()(ObjPtr<mirror::Class> klass) OVERRIDE
+ REQUIRES(Locks::subtype_check_lock_) REQUIRES_SHARED(Locks::mutator_lock_) {
+ DCHECK(klass != nullptr);
+ SubtypeCheckInfo::State state = SubtypeCheck<ObjPtr<mirror::Class>>::GetState(klass);
+ if (state == SubtypeCheckInfo::kAssigned) {
+ recorded_classes_->insert(klass.Ptr());
+ }
+ return true;
+ }
+
+ private:
+ std::unordered_set<mirror::Class*>* const recorded_classes_;
+ };
+
+ boot_image_classes_with_assigned_bitstring_.reset(new std::unordered_set<mirror::Class*>());
+ Visitor visitor(boot_image_classes_with_assigned_bitstring_.get());
+ ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
+ ScopedObjectAccess soa(Thread::Current());
+ MutexLock subtype_check_lock(soa.Self(), *Locks::subtype_check_lock_);
+ class_linker->VisitClasses(&visitor);
+}
+
+bool CompilerDriver::IsBootImageClassWithAssignedBitstring(ObjPtr<mirror::Class> klass) {
+ DCHECK(boot_image_classes_with_assigned_bitstring_ != nullptr);
+ return boot_image_classes_with_assigned_bitstring_->count(klass.Ptr()) != 0u;
+}
+
bool CompilerDriver::IsImageClass(const char* descriptor) const {
if (image_classes_ != nullptr) {
// If we have a set of image classes, use those.
diff --git a/compiler/driver/compiler_driver.h b/compiler/driver/compiler_driver.h
index 4b5916d..3004275 100644
--- a/compiler/driver/compiler_driver.h
+++ b/compiler/driver/compiler_driver.h
@@ -385,12 +385,17 @@
return dex_to_dex_compiler_;
}
+ bool IsBootImageClassWithAssignedBitstring(ObjPtr<mirror::Class> klass)
+ REQUIRES_SHARED(Locks::mutator_lock_);
+
private:
void PreCompile(jobject class_loader,
const std::vector<const DexFile*>& dex_files,
TimingLogger* timings)
REQUIRES(!Locks::mutator_lock_);
+ void RecordBootImageClassesWithAssignedBitstring() REQUIRES(!Locks::mutator_lock_);
+
void LoadImageClasses(TimingLogger* timings) REQUIRES(!Locks::mutator_lock_);
// Attempt to resolve all type, methods, fields, and strings
@@ -513,6 +518,12 @@
// This option may be restricted to the boot image, depending on a flag in the implementation.
std::unique_ptr<std::unordered_set<std::string>> methods_to_compile_;
+ // For AOT app compilation, we keep the set of boot image classes with assigned type check
+ // bitstring. We need to retrieve this set before we initialize app image classes as the
+ // initialization can cause more boot image bitstrings to be assigned.
+ // Note that boot image classes are non-moveable, so it's OK to keep raw pointers.
+ std::unique_ptr<std::unordered_set<mirror::Class*>> boot_image_classes_with_assigned_bitstring_;
+
std::atomic<uint32_t> number_of_soft_verifier_failures_;
bool had_hard_verifier_failure_;
diff --git a/compiler/optimizing/sharpening.cc b/compiler/optimizing/sharpening.cc
index dffef17..12319df 100644
--- a/compiler/optimizing/sharpening.cc
+++ b/compiler/optimizing/sharpening.cc
@@ -253,9 +253,9 @@
// If the target is a boot image class, try to assign a type check bitstring (fall through).
// (If --force-determinism, this was already done; repeating is OK and yields the same result.)
} else {
- // TODO: Use the bitstring also for AOT app compilation if the target class has a bitstring
- // already assigned in the boot image.
- return false;
+ // For AOT app compilation we can use the bitstring iff the target class is
+ // a boot image class with a bitstring already assigned in the boot image.
+ return compiler_driver->IsBootImageClassWithAssignedBitstring(klass);
}
// Try to assign a type check bitstring.
diff --git a/runtime/entrypoints/quick/quick_throw_entrypoints.cc b/runtime/entrypoints/quick/quick_throw_entrypoints.cc
index 4b26bee..db4891e 100644
--- a/runtime/entrypoints/quick/quick_throw_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_throw_entrypoints.cc
@@ -126,6 +126,14 @@
dex::TypeIndex type_index(check_cast.VRegB_21c());
ClassLinker* linker = Runtime::Current()->GetClassLinker();
dest_type = linker->LookupResolvedType(type_index, visitor.caller).Ptr();
+ if (UNLIKELY(dest_type == nullptr)) {
+ // This class must have been resolved to the boot image at AOT compile time
+ // but it's not yet resolved in the app's class loader. Just look it up in
+ // the boot class path loader.
+ DCHECK(visitor.caller->GetClassLoader() != nullptr);
+ dest_type = linker->LookupResolvedType(
+ type_index, visitor.caller->GetDexCache(), /* class_loader */ nullptr).Ptr();
+ }
CHECK(dest_type != nullptr) << "Target class should have been previously resolved: "
<< visitor.caller->GetDexFile()->PrettyType(type_index);
}
diff --git a/test/552-checker-sharpening/src/Main.java b/test/552-checker-sharpening/src/Main.java
index 3173afd..0623a22 100644
--- a/test/552-checker-sharpening/src/Main.java
+++ b/test/552-checker-sharpening/src/Main.java
@@ -14,8 +14,17 @@
* limitations under the License.
*/
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+
public class Main {
+ public static void assertBooleanEquals(boolean expected, boolean result) {
+ if (expected != result) {
+ throw new Error("Expected: " + expected + ", found: " + result);
+ }
+ }
+
public static void assertIntEquals(int expected, int result) {
if (expected != result) {
throw new Error("Expected: " + expected + ", found: " + result);
@@ -195,6 +204,14 @@
return Other.class;
}
+ /// CHECK-START: boolean Main.$noinline$instanceOfInputStream(java.lang.Object) builder (after)
+ /// CHECK-NOT: LoadClass
+ /// CHECK: InstanceOf check_kind:bitstring_check
+ public static boolean $noinline$instanceOfInputStream(Object o) {
+ // InputStream is known to be in the core image with an initialized type check bitstring.
+ return o instanceof InputStream;
+ }
+
public static void main(String[] args) {
assertIntEquals(1, testSimple(1));
assertIntEquals(1, testDiamond(false, 1));
@@ -208,6 +225,10 @@
assertStringEquals("non-boot-image-string", $noinline$getNonBootImageString());
assertClassEquals(String.class, $noinline$getStringClass());
assertClassEquals(Other.class, $noinline$getOtherClass());
+ assertBooleanEquals(false, $noinline$instanceOfInputStream(null));
+ assertBooleanEquals(false, $noinline$instanceOfInputStream(new Integer(1)));
+ assertBooleanEquals(true,
+ $noinline$instanceOfInputStream(new ByteArrayInputStream(new byte[10])));
}
}