Improve checking of multidex dex checksums.
* Fall back to odex file for all multidex entries if the apk is stripped,
not just for the main multidex entry.
* Verify the number of multidex entries has not changed.
* Improve performance by getting all checksums from the apk in one go
and cache the results instead of repeatedly opening the apk.
* Stop referring to non-main multidex entries as "secondary" dex files.
Bug: 34604632
Test: added tests to dex_file_test and oat_file_assistant_test
Test: m test-art-host
Change-Id: I58b17ecfbc9165a5bfeffd5281ee21d108f64479
diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk
index 5c49f19..2c2c1b5 100644
--- a/build/Android.gtest.mk
+++ b/build/Android.gtest.mk
@@ -92,7 +92,7 @@
ART_GTEST_class_table_test_DEX_DEPS := XandY
ART_GTEST_compiler_driver_test_DEX_DEPS := AbstractMethod StaticLeafMethods ProfileTestMultiDex
ART_GTEST_dex_cache_test_DEX_DEPS := Main Packages MethodTypes
-ART_GTEST_dex_file_test_DEX_DEPS := GetMethodSignature Main Nested
+ART_GTEST_dex_file_test_DEX_DEPS := GetMethodSignature Main Nested MultiDex
ART_GTEST_dex2oat_test_DEX_DEPS := $(ART_GTEST_dex2oat_environment_tests_DEX_DEPS) Statics VerifierDeps
ART_GTEST_exception_test_DEX_DEPS := ExceptionHandle
ART_GTEST_image_test_DEX_DEPS := ImageLayoutA ImageLayoutB
diff --git a/runtime/dex_file.cc b/runtime/dex_file.cc
index 02ba33c..48db7e5 100644
--- a/runtime/dex_file.cc
+++ b/runtime/dex_file.cc
@@ -74,7 +74,6 @@
bool DexFile::GetChecksum(const char* filename, uint32_t* checksum, std::string* error_msg) {
CHECK(checksum != nullptr);
- uint32_t magic;
// Strip ":...", which is the location
const char* zip_entry_name = kClassesDex;
@@ -88,7 +87,29 @@
DCHECK_EQ(zip_entry_name[-1], kMultiDexSeparator);
}
- File fd = OpenAndReadMagic(file_part, &magic, error_msg);
+ std::vector<uint32_t> checksums;
+ if (!GetMultiDexChecksums(file_part, &checksums, error_msg)) {
+ return false;
+ }
+
+ for (size_t i = 0; i < checksums.size(); i++) {
+ if (GetMultiDexClassesDexName(i) == std::string(zip_entry_name)) {
+ *checksum = checksums[i];
+ return true;
+ }
+ }
+
+ *error_msg = StringPrintf("Failed to find entry '%s' in '%s'", zip_entry_name, file_part);
+ return false;
+}
+
+bool DexFile::GetMultiDexChecksums(const char* filename,
+ std::vector<uint32_t>* checksums,
+ std::string* error_msg) {
+ CHECK(checksums != nullptr);
+ uint32_t magic;
+
+ File fd = OpenAndReadMagic(filename, &magic, error_msg);
if (fd.Fd() == -1) {
DCHECK(!error_msg->empty());
return false;
@@ -97,17 +118,25 @@
std::unique_ptr<ZipArchive> zip_archive(
ZipArchive::OpenFromFd(fd.Release(), filename, error_msg));
if (zip_archive.get() == nullptr) {
- *error_msg = StringPrintf("Failed to open zip archive '%s' (error msg: %s)", file_part,
+ *error_msg = StringPrintf("Failed to open zip archive '%s' (error msg: %s)", filename,
error_msg->c_str());
return false;
}
- std::unique_ptr<ZipEntry> zip_entry(zip_archive->Find(zip_entry_name, error_msg));
+
+ uint32_t i = 0;
+ std::string zip_entry_name = GetMultiDexClassesDexName(i++);
+ std::unique_ptr<ZipEntry> zip_entry(zip_archive->Find(zip_entry_name.c_str(), error_msg));
if (zip_entry.get() == nullptr) {
- *error_msg = StringPrintf("Zip archive '%s' doesn't contain %s (error msg: %s)", file_part,
- zip_entry_name, error_msg->c_str());
+ *error_msg = StringPrintf("Zip archive '%s' doesn't contain %s (error msg: %s)", filename,
+ zip_entry_name.c_str(), error_msg->c_str());
return false;
}
- *checksum = zip_entry->GetCrc32();
+
+ do {
+ checksums->push_back(zip_entry->GetCrc32());
+ zip_entry_name = DexFile::GetMultiDexClassesDexName(i++);
+ zip_entry.reset(zip_archive->Find(zip_entry_name.c_str(), error_msg));
+ } while (zip_entry.get() != nullptr);
return true;
}
if (IsDexMagic(magic)) {
@@ -116,7 +145,7 @@
if (dex_file.get() == nullptr) {
return false;
}
- *checksum = dex_file->GetHeader().checksum_;
+ checksums->push_back(dex_file->GetHeader().checksum_);
return true;
}
*error_msg = StringPrintf("Expected valid zip or dex file: '%s'", filename);
diff --git a/runtime/dex_file.h b/runtime/dex_file.h
index cf90bca..8ad61aa 100644
--- a/runtime/dex_file.h
+++ b/runtime/dex_file.h
@@ -430,6 +430,15 @@
// Return true if the checksum could be found, false otherwise.
static bool GetChecksum(const char* filename, uint32_t* checksum, std::string* error_msg);
+ // Returns the checksums of a file for comparison with GetLocationChecksum().
+ // For .dex files, this is the single header checksum.
+ // For zip files, this is the zip entry CRC32 checksum for classes.dex and
+ // each additional multidex entry classes2.dex, classes3.dex, etc.
+ // Return true if the checksums could be found, false otherwise.
+ static bool GetMultiDexChecksums(const char* filename,
+ std::vector<uint32_t>* checksums,
+ std::string* error_msg);
+
// Opens .dex file, backed by existing memory
static std::unique_ptr<const DexFile> Open(const uint8_t* base,
size_t size,
diff --git a/runtime/dex_file_test.cc b/runtime/dex_file_test.cc
index 9dca4c0..b6e010c 100644
--- a/runtime/dex_file_test.cc
+++ b/runtime/dex_file_test.cc
@@ -334,6 +334,29 @@
EXPECT_EQ(java_lang_dex_file_->GetLocationChecksum(), checksum);
}
+TEST_F(DexFileTest, GetMultiDexChecksums) {
+ std::string error_msg;
+ std::vector<uint32_t> checksums;
+ std::string multidex_file = GetTestDexFileName("MultiDex");
+ EXPECT_TRUE(DexFile::GetMultiDexChecksums(multidex_file.c_str(),
+ &checksums,
+ &error_msg)) << error_msg;
+
+ uint32_t checksum0 = 0;
+ EXPECT_TRUE(DexFile::GetChecksum(DexFile::GetMultiDexLocation(0, multidex_file.c_str()).c_str(),
+ &checksum0,
+ &error_msg)) << error_msg;
+
+ uint32_t checksum1 = 0;
+ EXPECT_TRUE(DexFile::GetChecksum(DexFile::GetMultiDexLocation(1, multidex_file.c_str()).c_str(),
+ &checksum1,
+ &error_msg)) << error_msg;
+
+ ASSERT_EQ(2u, checksums.size());
+ EXPECT_EQ(checksums[0], checksum0);
+ EXPECT_EQ(checksums[1], checksum1);
+}
+
TEST_F(DexFileTest, ClassDefs) {
ScopedObjectAccess soa(Thread::Current());
std::unique_ptr<const DexFile> raw(OpenTestDexFile("Nested"));
diff --git a/runtime/oat_file_assistant.cc b/runtime/oat_file_assistant.cc
index 77cdd28..5ae2fc5 100644
--- a/runtime/oat_file_assistant.cc
+++ b/runtime/oat_file_assistant.cc
@@ -38,6 +38,8 @@
namespace art {
+using android::base::StringPrintf;
+
std::ostream& operator << (std::ostream& stream, const OatFileAssistant::OatStatus status) {
switch (status) {
case OatFileAssistant::kOatCannotOpen:
@@ -264,7 +266,7 @@
const OatFile& oat_file, const char* dex_location) {
std::vector<std::unique_ptr<const DexFile>> dex_files;
- // Load the primary dex file.
+ // Load the main dex file.
std::string error_msg;
const OatFile::OatDexFile* oat_dex_file = oat_file.GetOatDexFile(
dex_location, nullptr, &error_msg);
@@ -280,12 +282,12 @@
}
dex_files.push_back(std::move(dex_file));
- // Load secondary multidex files
+ // Load the rest of the multidex entries
for (size_t i = 1; ; i++) {
- std::string secondary_dex_location = DexFile::GetMultiDexLocation(i, dex_location);
- oat_dex_file = oat_file.GetOatDexFile(secondary_dex_location.c_str(), nullptr);
+ std::string multidex_dex_location = DexFile::GetMultiDexLocation(i, dex_location);
+ oat_dex_file = oat_file.GetOatDexFile(multidex_dex_location.c_str(), nullptr);
if (oat_dex_file == nullptr) {
- // There are no more secondary dex files to load.
+ // There are no more multidex entries to load.
break;
}
@@ -300,10 +302,10 @@
}
bool OatFileAssistant::HasOriginalDexFiles() {
- // Ensure GetRequiredDexChecksum has been run so that
+ // Ensure GetRequiredDexChecksums has been run so that
// has_original_dex_files_ is initialized. We don't care about the result of
- // GetRequiredDexChecksum.
- GetRequiredDexChecksum();
+ // GetRequiredDexChecksums.
+ GetRequiredDexChecksums();
return has_original_dex_files_;
}
@@ -316,88 +318,66 @@
}
bool OatFileAssistant::DexChecksumUpToDate(const VdexFile& file, std::string* error_msg) {
- if (file.GetHeader().GetNumberOfDexFiles() <= 0) {
- VLOG(oat) << "Vdex does not contain any dex files";
+ const std::vector<uint32_t>* required_dex_checksums = GetRequiredDexChecksums();
+ if (required_dex_checksums == nullptr) {
+ LOG(WARNING) << "Required dex checksums not found. Assuming dex checksums are up to date.";
+ return true;
+ }
+
+ uint32_t number_of_dex_files = file.GetHeader().GetNumberOfDexFiles();
+ if (required_dex_checksums->size() != number_of_dex_files) {
+ *error_msg = StringPrintf("expected %zu dex files but found %u",
+ required_dex_checksums->size(),
+ number_of_dex_files);
return false;
}
- // TODO: Use GetRequiredDexChecksum to get secondary checksums as well, not
- // just the primary. Because otherwise we may fail to see a secondary
- // checksum failure in the case when the original (multidex) files are
- // stripped but we have a newer odex file.
- const uint32_t* dex_checksum_pointer = GetRequiredDexChecksum();
- if (dex_checksum_pointer != nullptr) {
- uint32_t actual_checksum = file.GetLocationChecksum(0);
- if (*dex_checksum_pointer != actual_checksum) {
- VLOG(oat) << "Dex checksum does not match for primary dex: " << dex_location_
- << ". Expected: " << *dex_checksum_pointer
- << ", Actual: " << actual_checksum;
+ for (uint32_t i = 0; i < number_of_dex_files; i++) {
+ uint32_t expected_checksum = (*required_dex_checksums)[i];
+ uint32_t actual_checksum = file.GetLocationChecksum(i);
+ if (expected_checksum != actual_checksum) {
+ std::string dex = DexFile::GetMultiDexLocation(i, dex_location_.c_str());
+ *error_msg = StringPrintf("Dex checksum does not match for dex: %s."
+ "Expected: %u, actual: %u",
+ dex.c_str(),
+ expected_checksum,
+ actual_checksum);
return false;
}
}
- // Verify the dex checksums for any secondary multidex files
- for (uint32_t i = 1; i < file.GetHeader().GetNumberOfDexFiles(); i++) {
- std::string secondary_dex_location = DexFile::GetMultiDexLocation(i, dex_location_.c_str());
- uint32_t expected_secondary_checksum = 0;
- if (DexFile::GetChecksum(secondary_dex_location.c_str(),
- &expected_secondary_checksum,
- error_msg)) {
- uint32_t actual_secondary_checksum = file.GetLocationChecksum(i);
- if (expected_secondary_checksum != actual_secondary_checksum) {
- VLOG(oat) << "Dex checksum does not match for secondary dex: "
- << secondary_dex_location
- << ". Expected: " << expected_secondary_checksum
- << ", Actual: " << actual_secondary_checksum;
- return false;
- }
- } else {
- // If we can't get the checksum for the secondary location, we assume
- // the dex checksum is up to date for this and all other secondary dex
- // files.
- break;
- }
- }
return true;
}
bool OatFileAssistant::DexChecksumUpToDate(const OatFile& file, std::string* error_msg) {
- // Note: GetOatDexFile will return null if the dex checksum doesn't match
- // what we provide, which verifies the primary dex checksum for us.
- const uint32_t* dex_checksum_pointer = GetRequiredDexChecksum();
- const OatFile::OatDexFile* oat_dex_file = file.GetOatDexFile(
- dex_location_.c_str(), dex_checksum_pointer, error_msg);
- if (oat_dex_file == nullptr) {
+ const std::vector<uint32_t>* required_dex_checksums = GetRequiredDexChecksums();
+ if (required_dex_checksums == nullptr) {
+ LOG(WARNING) << "Required dex checksums not found. Assuming dex checksums are up to date.";
+ return true;
+ }
+
+ uint32_t number_of_dex_files = file.GetOatHeader().GetDexFileCount();
+ if (required_dex_checksums->size() != number_of_dex_files) {
+ *error_msg = StringPrintf("expected %zu dex files but found %u",
+ required_dex_checksums->size(),
+ number_of_dex_files);
return false;
}
- // Verify the dex checksums for any secondary multidex files
- for (size_t i = 1; ; i++) {
- std::string secondary_dex_location = DexFile::GetMultiDexLocation(i, dex_location_.c_str());
- const OatFile::OatDexFile* secondary_oat_dex_file
- = file.GetOatDexFile(secondary_dex_location.c_str(), nullptr);
- if (secondary_oat_dex_file == nullptr) {
- // There are no more secondary dex files to check.
- break;
+ for (uint32_t i = 0; i < number_of_dex_files; i++) {
+ std::string dex = DexFile::GetMultiDexLocation(i, dex_location_.c_str());
+ uint32_t expected_checksum = (*required_dex_checksums)[i];
+ const OatFile::OatDexFile* oat_dex_file = file.GetOatDexFile(dex.c_str(), nullptr);
+ if (oat_dex_file == nullptr) {
+ *error_msg = StringPrintf("failed to find %s in %s", dex.c_str(), file.GetLocation().c_str());
+ return false;
}
-
- uint32_t expected_secondary_checksum = 0;
- if (DexFile::GetChecksum(secondary_dex_location.c_str(),
- &expected_secondary_checksum, error_msg)) {
- uint32_t actual_secondary_checksum
- = secondary_oat_dex_file->GetDexFileLocationChecksum();
- if (expected_secondary_checksum != actual_secondary_checksum) {
- VLOG(oat) << "Dex checksum does not match for secondary dex: "
- << secondary_dex_location
- << ". Expected: " << expected_secondary_checksum
- << ", Actual: " << actual_secondary_checksum;
- return false;
- }
- } else {
- // If we can't get the checksum for the secondary location, we assume
- // the dex checksum is up to date for this and all other secondary dex
- // files.
- break;
+ uint32_t actual_checksum = oat_dex_file->GetDexFileLocationChecksum();
+ if (expected_checksum != actual_checksum) {
+ VLOG(oat) << "Dex checksum does not match for dex: " << dex
+ << ". Expected: " << expected_checksum
+ << ", Actual: " << actual_checksum;
+ return false;
}
}
return true;
@@ -710,13 +690,16 @@
return image_spaces[0]->GetImageLocation();
}
-const uint32_t* OatFileAssistant::GetRequiredDexChecksum() {
- if (!required_dex_checksum_attempted_) {
- required_dex_checksum_attempted_ = true;
- required_dex_checksum_found_ = false;
+const std::vector<uint32_t>* OatFileAssistant::GetRequiredDexChecksums() {
+ if (!required_dex_checksums_attempted_) {
+ required_dex_checksums_attempted_ = true;
+ required_dex_checksums_found_ = false;
+ cached_required_dex_checksums_.clear();
std::string error_msg;
- if (DexFile::GetChecksum(dex_location_.c_str(), &cached_required_dex_checksum_, &error_msg)) {
- required_dex_checksum_found_ = true;
+ if (DexFile::GetMultiDexChecksums(dex_location_.c_str(),
+ &cached_required_dex_checksums_,
+ &error_msg)) {
+ required_dex_checksums_found_ = true;
has_original_dex_files_ = true;
} else {
// This can happen if the original dex file has been stripped from the
@@ -724,19 +707,23 @@
VLOG(oat) << "OatFileAssistant: " << error_msg;
has_original_dex_files_ = false;
- // Get the checksum from the odex if we can.
+ // Get the checksums from the odex if we can.
const OatFile* odex_file = odex_.GetFile();
if (odex_file != nullptr) {
- const OatFile::OatDexFile* odex_dex_file
- = odex_file->GetOatDexFile(dex_location_.c_str(), nullptr);
- if (odex_dex_file != nullptr) {
- cached_required_dex_checksum_ = odex_dex_file->GetDexFileLocationChecksum();
- required_dex_checksum_found_ = true;
+ required_dex_checksums_found_ = true;
+ for (size_t i = 0; i < odex_file->GetOatHeader().GetDexFileCount(); i++) {
+ std::string dex = DexFile::GetMultiDexLocation(i, dex_location_.c_str());
+ const OatFile::OatDexFile* odex_dex_file = odex_file->GetOatDexFile(dex.c_str(), nullptr);
+ if (odex_dex_file == nullptr) {
+ required_dex_checksums_found_ = false;
+ break;
+ }
+ cached_required_dex_checksums_.push_back(odex_dex_file->GetDexFileLocationChecksum());
}
}
}
}
- return required_dex_checksum_found_ ? &cached_required_dex_checksum_ : nullptr;
+ return required_dex_checksums_found_ ? &cached_required_dex_checksums_ : nullptr;
}
const OatFileAssistant::ImageInfo* OatFileAssistant::GetImageInfo() {
diff --git a/runtime/oat_file_assistant.h b/runtime/oat_file_assistant.h
index 6d47ad2..3ede29f 100644
--- a/runtime/oat_file_assistant.h
+++ b/runtime/oat_file_assistant.h
@@ -400,13 +400,13 @@
// the oat file assistant.
static std::string ImageLocation();
- // Gets the dex checksum required for an up-to-date oat file.
- // Returns dex_checksum if a required checksum was located. Returns
- // null if the required checksum was not found.
- // The caller shouldn't clean up or free the returned pointer.
- // This sets the has_original_dex_files_ field to true if a checksum was
- // found for the dex_location_ dex file.
- const uint32_t* GetRequiredDexChecksum();
+ // Gets the dex checksums required for an up-to-date oat file.
+ // Returns cached_required_dex_checksums if the required checksums were
+ // located. Returns null if the required checksums were not found. The
+ // caller shouldn't clean up or free the returned pointer. This sets the
+ // has_original_dex_files_ field to true if the checksums were found for the
+ // dex_location_ dex file.
+ const std::vector<uint32_t>* GetRequiredDexChecksums();
// Returns the loaded image info.
// Loads the image info if needed. Returns null if the image info failed
@@ -430,11 +430,11 @@
// Whether we will attempt to load oat files executable.
bool load_executable_ = false;
- // Cached value of the required dex checksum.
- // This should be accessed only by the GetRequiredDexChecksum() method.
- uint32_t cached_required_dex_checksum_;
- bool required_dex_checksum_attempted_ = false;
- bool required_dex_checksum_found_;
+ // Cached value of the required dex checksums.
+ // This should be accessed only by the GetRequiredDexChecksums() method.
+ std::vector<uint32_t> cached_required_dex_checksums_;
+ bool required_dex_checksums_attempted_ = false;
+ bool required_dex_checksums_found_;
bool has_original_dex_files_;
OatFileInfo odex_;
diff --git a/runtime/oat_file_assistant_test.cc b/runtime/oat_file_assistant_test.cc
index f777340..6cfe3d1 100644
--- a/runtime/oat_file_assistant_test.cc
+++ b/runtime/oat_file_assistant_test.cc
@@ -237,16 +237,16 @@
EXPECT_EQ(2u, dex_files.size());
}
-// Case: We have a MultiDEX file where the secondary dex file is out of date.
+// Case: We have a MultiDEX file where the non-main multdex entry is out of date.
// Expect: The status is kDex2OatNeeded.
-TEST_F(OatFileAssistantTest, MultiDexSecondaryOutOfDate) {
- std::string dex_location = GetScratchDir() + "/MultiDexSecondaryOutOfDate.jar";
+TEST_F(OatFileAssistantTest, MultiDexNonMainOutOfDate) {
+ std::string dex_location = GetScratchDir() + "/MultiDexNonMainOutOfDate.jar";
// Compile code for GetMultiDexSrc1.
Copy(GetMultiDexSrc1(), dex_location);
GenerateOatForTest(dex_location.c_str(), CompilerFilter::kSpeed);
- // Now overwrite the dex file with GetMultiDexSrc2 so the secondary checksum
+ // Now overwrite the dex file with GetMultiDexSrc2 so the non-main checksum
// is out of date.
Copy(GetMultiDexSrc2(), dex_location);
@@ -256,6 +256,37 @@
EXPECT_TRUE(oat_file_assistant.HasOriginalDexFiles());
}
+// Case: We have a stripped MultiDEX file where the non-main multidex entry is
+// out of date with respect to the odex file.
+TEST_F(OatFileAssistantTest, StrippedMultiDexNonMainOutOfDate) {
+ std::string dex_location = GetScratchDir() + "/StrippedMultiDexNonMainOutOfDate.jar";
+ std::string odex_location = GetOdexDir() + "/StrippedMultiDexNonMainOutOfDate.odex";
+
+ // Compile the oat from GetMultiDexSrc1.
+ Copy(GetMultiDexSrc1(), dex_location);
+ GenerateOatForTest(dex_location.c_str(), CompilerFilter::kSpeed);
+
+ // Compile the odex from GetMultiDexSrc2, which has a different non-main
+ // dex checksum.
+ Copy(GetMultiDexSrc2(), dex_location);
+ GenerateOdexForTest(dex_location, odex_location, CompilerFilter::kInterpretOnly);
+
+ // Strip the dex file.
+ Copy(GetStrippedDexSrc1(), dex_location);
+
+ OatFileAssistant oat_file_assistant(dex_location.c_str(), kRuntimeISA, /*load_executable*/false);
+
+ // Because the dex file is stripped, the odex file is considered the source
+ // of truth for the dex checksums. The oat file should be considered
+ // unusable.
+ std::unique_ptr<OatFile> best_file = oat_file_assistant.GetBestOatFile();
+ ASSERT_TRUE(best_file.get() != nullptr);
+ EXPECT_EQ(best_file->GetLocation(), odex_location);
+ EXPECT_FALSE(oat_file_assistant.HasOriginalDexFiles());
+ EXPECT_EQ(OatFileAssistant::kOatUpToDate, oat_file_assistant.OdexFileStatus());
+ EXPECT_EQ(OatFileAssistant::kOatDexOutOfDate, oat_file_assistant.OatFileStatus());
+}
+
// Case: We have a MultiDEX file and up-to-date OAT file for it with relative
// encoded dex locations.
// Expect: The oat file status is kNoDexOptNeeded.
@@ -336,16 +367,16 @@
oat_file_assistant.GetDexOptNeeded(CompilerFilter::kSpeed));
}
-// Case: We have a MultiDEX (ODEX) VDEX file where the secondary dex file is
-// out of date and there is no corresponding ODEX file.
-TEST_F(OatFileAssistantTest, VdexMultiDexSecondaryOutOfDate) {
+// Case: We have a MultiDEX (ODEX) VDEX file where the non-main multidex entry
+// is out of date and there is no corresponding ODEX file.
+TEST_F(OatFileAssistantTest, VdexMultiDexNonMainOutOfDate) {
// This test case is only meaningful if vdex is enabled.
if (!kIsVdexEnabled) {
return;
}
- std::string dex_location = GetScratchDir() + "/VdexMultiDexSecondaryOutOfDate.jar";
- std::string oat_location = GetOdexDir() + "/VdexMultiDexSecondaryOutOfDate.oat";
+ std::string dex_location = GetScratchDir() + "/VdexMultiDexNonMainOutOfDate.jar";
+ std::string oat_location = GetOdexDir() + "/VdexMultiDexNonMainOutOfDate.oat";
Copy(GetMultiDexSrc1(), dex_location);
GenerateOdexForTest(dex_location, oat_location, CompilerFilter::kSpeed);