Merge "ARM: VIXL32: Use 16-bit encoding for B(cond,...) where possible."
diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk
index d59d8f6..bd7f900 100644
--- a/build/Android.gtest.mk
+++ b/build/Android.gtest.mk
@@ -26,6 +26,8 @@
AbstractMethod \
AllFields \
DexToDexDecompiler \
+ ErroneousA \
+ ErroneousB \
ExceptionHandle \
GetMethodSignature \
ImageLayoutA \
@@ -85,7 +87,7 @@
ART_GTEST_dex2oat_environment_tests_DEX_DEPS := Main MainStripped MultiDex MultiDexModifiedSecondary Nested
ART_GTEST_atomic_method_ref_map_test_DEX_DEPS := Interfaces
-ART_GTEST_class_linker_test_DEX_DEPS := Interfaces MethodTypes MultiDex MyClass Nested Statics StaticsFromCode
+ART_GTEST_class_linker_test_DEX_DEPS := ErroneousA ErroneousB Interfaces MethodTypes MultiDex MyClass Nested Statics StaticsFromCode
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
diff --git a/build/art.go b/build/art.go
index ccaa11d..0af1767 100644
--- a/build/art.go
+++ b/build/art.go
@@ -259,7 +259,7 @@
}
func artLibrary() (blueprint.Module, []interface{}) {
- library, _ := cc.NewLibrary(android.HostAndDeviceSupported, true, true)
+ library, _ := cc.NewLibrary(android.HostAndDeviceSupported)
module, props := library.Init()
props = installCodegenCustomizer(module, props, true)
diff --git a/cmdline/cmdline.h b/cmdline/cmdline.h
index 6e042c3..f4540ff 100644
--- a/cmdline/cmdline.h
+++ b/cmdline/cmdline.h
@@ -24,10 +24,12 @@
#include <iostream>
#include <string>
-#include "runtime.h"
+#include "android-base/stringprintf.h"
+
+#include "base/logging.h"
#include "base/stringpiece.h"
#include "noop_compiler_callbacks.h"
-#include "base/logging.h"
+#include "runtime.h"
#if !defined(NDEBUG)
#define DBG_LOG LOG(INFO)
@@ -197,7 +199,7 @@
" Example: --boot-image=/system/framework/boot.art\n"
" (specifies /system/framework/<arch>/boot.art as the image file)\n"
"\n";
- usage += StringPrintf( // Optional.
+ usage += android::base::StringPrintf( // Optional.
" --instruction-set=(arm|arm64|mips|mips64|x86|x86_64): for locating the image\n"
" file based on the image location set.\n"
" Example: --instruction-set=x86\n"
@@ -264,8 +266,8 @@
// Check that the boot image location points to a valid file name.
std::string file_name;
if (!LocationToFilename(boot_image_location, instruction_set_, &file_name)) {
- *error_msg = StringPrintf("No corresponding file for location '%s' exists",
- file_name.c_str());
+ *error_msg = android::base::StringPrintf("No corresponding file for location '%s' exists",
+ file_name.c_str());
return false;
}
diff --git a/compiler/Android.bp b/compiler/Android.bp
index 2eb6fba..46f3358 100644
--- a/compiler/Android.bp
+++ b/compiler/Android.bp
@@ -49,6 +49,7 @@
"optimizing/block_builder.cc",
"optimizing/bounds_check_elimination.cc",
"optimizing/builder.cc",
+ "optimizing/cha_guard_optimization.cc",
"optimizing/code_generator.cc",
"optimizing/code_generator_utils.cc",
"optimizing/constant_folding.cc",
diff --git a/compiler/compiled_class.h b/compiler/compiled_class.h
index b88d613..06ce946 100644
--- a/compiler/compiled_class.h
+++ b/compiler/compiled_class.h
@@ -28,8 +28,11 @@
mirror::Class::Status GetStatus() const {
return status_;
}
+ void SetStatus(mirror::Class::Status status) {
+ status_ = status;
+ }
private:
- const mirror::Class::Status status_;
+ mirror::Class::Status status_;
};
} // namespace art
diff --git a/compiler/dex/dex_to_dex_compiler.cc b/compiler/dex/dex_to_dex_compiler.cc
index cf69f46..d4f6545 100644
--- a/compiler/dex/dex_to_dex_compiler.cc
+++ b/compiler/dex/dex_to_dex_compiler.cc
@@ -16,6 +16,8 @@
#include "dex_to_dex_compiler.h"
+#include "android-base/stringprintf.h"
+
#include "art_field-inl.h"
#include "art_method-inl.h"
#include "base/logging.h"
@@ -32,6 +34,8 @@
namespace art {
namespace optimizer {
+using android::base::StringPrintf;
+
// Controls quickening activation.
const bool kEnableQuickening = true;
// Control check-cast elision.
diff --git a/compiler/dex/dex_to_dex_decompiler.cc b/compiler/dex/dex_to_dex_decompiler.cc
index 051125e..bfd485d 100644
--- a/compiler/dex/dex_to_dex_decompiler.cc
+++ b/compiler/dex/dex_to_dex_decompiler.cc
@@ -27,10 +27,13 @@
class DexDecompiler {
public:
- DexDecompiler(const DexFile::CodeItem& code_item, const ArrayRef<const uint8_t>& quickened_info)
+ DexDecompiler(const DexFile::CodeItem& code_item,
+ const ArrayRef<const uint8_t>& quickened_info,
+ bool decompile_return_instruction)
: code_item_(code_item),
quickened_info_ptr_(quickened_info.data()),
- quickened_info_end_(quickened_info.data() + quickened_info.size()) {}
+ quickened_info_end_(quickened_info.data() + quickened_info.size()),
+ decompile_return_instruction_(decompile_return_instruction) {}
bool Decompile();
@@ -87,6 +90,7 @@
const DexFile::CodeItem& code_item_;
const uint8_t* quickened_info_ptr_;
const uint8_t* const quickened_info_end_;
+ const bool decompile_return_instruction_;
DISALLOW_COPY_AND_ASSIGN(DexDecompiler);
};
@@ -102,7 +106,9 @@
switch (inst->Opcode()) {
case Instruction::RETURN_VOID_NO_BARRIER:
- inst->SetOpcode(Instruction::RETURN_VOID);
+ if (decompile_return_instruction_) {
+ inst->SetOpcode(Instruction::RETURN_VOID);
+ }
break;
case Instruction::NOP:
@@ -189,8 +195,12 @@
}
bool ArtDecompileDEX(const DexFile::CodeItem& code_item,
- const ArrayRef<const uint8_t>& quickened_info) {
- DexDecompiler decompiler(code_item, quickened_info);
+ const ArrayRef<const uint8_t>& quickened_info,
+ bool decompile_return_instruction) {
+ if (quickened_info.size() == 0 && !decompile_return_instruction) {
+ return true;
+ }
+ DexDecompiler decompiler(code_item, quickened_info, decompile_return_instruction);
return decompiler.Decompile();
}
diff --git a/compiler/dex/dex_to_dex_decompiler.h b/compiler/dex/dex_to_dex_decompiler.h
index 5502ca2..b5d5b91 100644
--- a/compiler/dex/dex_to_dex_decompiler.h
+++ b/compiler/dex/dex_to_dex_decompiler.h
@@ -30,7 +30,8 @@
// consistent with DexToDexCompiler, but we should really change it to
// DexFile::CodeItem*.
bool ArtDecompileDEX(const DexFile::CodeItem& code_item,
- const ArrayRef<const uint8_t>& quickened_data);
+ const ArrayRef<const uint8_t>& quickened_data,
+ bool decompile_return_instruction);
} // namespace optimizer
} // namespace art
diff --git a/compiler/dex/dex_to_dex_decompiler_test.cc b/compiler/dex/dex_to_dex_decompiler_test.cc
index ea6c7a2..9a8d27c 100644
--- a/compiler/dex/dex_to_dex_decompiler_test.cc
+++ b/compiler/dex/dex_to_dex_decompiler_test.cc
@@ -102,7 +102,8 @@
if (compiled_method != nullptr) {
table = compiled_method->GetVmapTable();
}
- optimizer::ArtDecompileDEX(*it.GetMethodCodeItem(), table);
+ optimizer::ArtDecompileDEX(
+ *it.GetMethodCodeItem(), table, /* decompile_return_instruction */ true);
it.Next();
}
while (it.HasNextVirtualMethod()) {
@@ -113,7 +114,8 @@
if (compiled_method != nullptr) {
table = compiled_method->GetVmapTable();
}
- optimizer::ArtDecompileDEX(*it.GetMethodCodeItem(), table);
+ optimizer::ArtDecompileDEX(
+ *it.GetMethodCodeItem(), table, /* decompile_return_instruction */ true);
it.Next();
}
DCHECK(!it.HasNext());
diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc
index 1b1de78..ec1642e 100644
--- a/compiler/driver/compiler_driver.cc
+++ b/compiler/driver/compiler_driver.cc
@@ -423,7 +423,7 @@
// Compile:
// 1) Compile all classes and methods enabled for compilation. May fall back to dex-to-dex
// compilation.
- if (!GetCompilerOptions().VerifyAtRuntime()) {
+ if (!GetCompilerOptions().VerifyAtRuntime() && !GetCompilerOptions().VerifyOnlyProfile()) {
Compile(class_loader, dex_files, timings);
}
if (dump_stats_) {
@@ -435,7 +435,8 @@
// In-place unquicken the given `dex_files` based on `quickening_info`.
static void Unquicken(const std::vector<const DexFile*>& dex_files,
- const ArrayRef<const uint8_t>& quickening_info) {
+ const ArrayRef<const uint8_t>& quickening_info,
+ bool decompile_return_instruction) {
const uint8_t* quickening_info_ptr = quickening_info.data();
const uint8_t* const quickening_info_end = quickening_info.data() + quickening_info.size();
for (const DexFile* dex_file : dex_files) {
@@ -454,14 +455,14 @@
it.Next();
}
- // Unquicken each method.
while (it.HasNextDirectMethod()) {
const DexFile::CodeItem* code_item = it.GetMethodCodeItem();
if (code_item != nullptr) {
uint32_t quickening_size = *reinterpret_cast<const uint32_t*>(quickening_info_ptr);
quickening_info_ptr += sizeof(uint32_t);
- optimizer::ArtDecompileDEX(
- *code_item, ArrayRef<const uint8_t>(quickening_info_ptr, quickening_size));
+ optimizer::ArtDecompileDEX(*code_item,
+ ArrayRef<const uint8_t>(quickening_info_ptr, quickening_size),
+ decompile_return_instruction);
quickening_info_ptr += quickening_size;
}
it.Next();
@@ -472,8 +473,9 @@
if (code_item != nullptr) {
uint32_t quickening_size = *reinterpret_cast<const uint32_t*>(quickening_info_ptr);
quickening_info_ptr += sizeof(uint32_t);
- optimizer::ArtDecompileDEX(
- *code_item, ArrayRef<const uint8_t>(quickening_info_ptr, quickening_size));
+ optimizer::ArtDecompileDEX(*code_item,
+ ArrayRef<const uint8_t>(quickening_info_ptr, quickening_size),
+ decompile_return_instruction);
quickening_info_ptr += quickening_size;
}
it.Next();
@@ -492,7 +494,11 @@
// TODO: we unquicken unconditionnally, as we don't know
// if the boot image has changed. How exactly we'll know is under
// experimentation.
- Unquicken(dex_files, vdex_file->GetQuickeningInfo());
+ TimingLogger::ScopedTiming t("Unquicken", timings);
+ // We do not decompile a RETURN_VOID_NO_BARRIER into a RETURN_VOID, as the quickening
+ // optimization does not depend on the boot image (the optimization relies on not
+ // having final fields in a class, which does not change for an app).
+ Unquicken(dex_files, vdex_file->GetQuickeningInfo(), /* decompile_return_instruction */ false);
Runtime::Current()->GetCompilerCallbacks()->SetVerifierDeps(
new verifier::VerifierDeps(dex_files, vdex_file->GetVerifierDepsData()));
}
@@ -983,8 +989,10 @@
<< "situations. Please check the log.";
}
- InitializeClasses(class_loader, dex_files, timings);
- VLOG(compiler) << "InitializeClasses: " << GetMemoryUsageString(false);
+ if (!verify_only_profile) {
+ InitializeClasses(class_loader, dex_files, timings);
+ VLOG(compiler) << "InitializeClasses: " << GetMemoryUsageString(false);
+ }
UpdateImageClasses(timings);
VLOG(compiler) << "UpdateImageClasses: " << GetMemoryUsageString(false);
@@ -2060,21 +2068,32 @@
std::set<dex::TypeIndex> set(unverified_classes.begin(), unverified_classes.end());
for (uint32_t i = 0; i < dex_file->NumClassDefs(); ++i) {
const DexFile::ClassDef& class_def = dex_file->GetClassDef(i);
- const char* descriptor = dex_file->GetClassDescriptor(class_def);
- cls.Assign(class_linker->FindClass(soa.Self(), descriptor, class_loader));
- if (cls.Get() == nullptr) {
- CHECK(soa.Self()->IsExceptionPending());
- soa.Self()->ClearException();
- } else if (set.find(class_def.class_idx_) == set.end()) {
- ObjectLock<mirror::Class> lock(soa.Self(), cls);
- mirror::Class::SetStatus(cls, mirror::Class::kStatusVerified, soa.Self());
- // Create `VerifiedMethod`s for each methods, the compiler expects one for
- // quickening or compiling.
- // Note that this means:
- // - We're only going to compile methods that did verify.
- // - Quickening will not do checkcast ellision.
- // TODO(ngeoffray): Reconsider this once we refactor compiler filters.
- PopulateVerifiedMethods(*dex_file, i, verification_results_);
+ if (set.find(class_def.class_idx_) == set.end()) {
+ if (GetCompilerOptions().VerifyOnlyProfile()) {
+ // Just update the compiled_classes_ map. The compiler doesn't need to resolve
+ // the type.
+ compiled_classes_.Overwrite(
+ ClassReference(dex_file, i), new CompiledClass(mirror::Class::kStatusVerified));
+ } else {
+ // Resolve the type, so later compilation stages know they don't need to verify
+ // the class.
+ const char* descriptor = dex_file->GetClassDescriptor(class_def);
+ cls.Assign(class_linker->FindClass(soa.Self(), descriptor, class_loader));
+ if (cls.Get() != nullptr) {
+ ObjectLock<mirror::Class> lock(soa.Self(), cls);
+ mirror::Class::SetStatus(cls, mirror::Class::kStatusVerified, soa.Self());
+ } else {
+ DCHECK(soa.Self()->IsExceptionPending());
+ soa.Self()->ClearException();
+ }
+ // Create `VerifiedMethod`s for each methods, the compiler expects one for
+ // quickening or compiling.
+ // Note that this means:
+ // - We're only going to compile methods that did verify.
+ // - Quickening will not do checkcast ellision.
+ // TODO(ngeoffray): Reconsider this once we refactor compiler filters.
+ PopulateVerifiedMethods(*dex_file, i, verification_results_);
+ }
}
}
}
@@ -2675,29 +2694,29 @@
}
void CompilerDriver::RecordClassStatus(ClassReference ref, mirror::Class::Status status) {
+ switch (status) {
+ case mirror::Class::kStatusNotReady:
+ case mirror::Class::kStatusError:
+ case mirror::Class::kStatusRetryVerificationAtRuntime:
+ case mirror::Class::kStatusVerified:
+ case mirror::Class::kStatusInitialized:
+ case mirror::Class::kStatusResolved:
+ break; // Expected states.
+ default:
+ LOG(FATAL) << "Unexpected class status for class "
+ << PrettyDescriptor(ref.first->GetClassDescriptor(ref.first->GetClassDef(ref.second)))
+ << " of " << status;
+ }
+
MutexLock mu(Thread::Current(), compiled_classes_lock_);
auto it = compiled_classes_.find(ref);
- if (it == compiled_classes_.end() || it->second->GetStatus() != status) {
- // An entry doesn't exist or the status is lower than the new status.
- if (it != compiled_classes_.end()) {
- CHECK_GT(status, it->second->GetStatus());
- delete it->second;
- }
- switch (status) {
- case mirror::Class::kStatusNotReady:
- case mirror::Class::kStatusError:
- case mirror::Class::kStatusRetryVerificationAtRuntime:
- case mirror::Class::kStatusVerified:
- case mirror::Class::kStatusInitialized:
- case mirror::Class::kStatusResolved:
- break; // Expected states.
- default:
- LOG(FATAL) << "Unexpected class status for class "
- << PrettyDescriptor(ref.first->GetClassDescriptor(ref.first->GetClassDef(ref.second)))
- << " of " << status;
- }
+ if (it == compiled_classes_.end()) {
CompiledClass* compiled_class = new CompiledClass(status);
compiled_classes_.Overwrite(ref, compiled_class);
+ } else if (status > it->second->GetStatus()) {
+ // Update the status if we now have a greater one. This happens with vdex,
+ // which records a class is verified, but does not resolve it.
+ it->second->SetStatus(status);
}
}
diff --git a/compiler/driver/compiler_driver.h b/compiler/driver/compiler_driver.h
index cc50197..eb69931 100644
--- a/compiler/driver/compiler_driver.h
+++ b/compiler/driver/compiler_driver.h
@@ -440,9 +440,12 @@
TimingLogger* timings)
REQUIRES(!Locks::mutator_lock_);
+ // NO_THREAD_SAFETY_ANALYSIS as the method accesses a guarded value in a
+ // single-threaded way.
void Verify(jobject class_loader,
const std::vector<const DexFile*>& dex_files,
- TimingLogger* timings);
+ TimingLogger* timings)
+ NO_THREAD_SAFETY_ANALYSIS;
void VerifyDexFile(jobject class_loader,
const DexFile& dex_file,
diff --git a/compiler/driver/dex_compilation_unit.cc b/compiler/driver/dex_compilation_unit.cc
index 64fd9e7..47b1929 100644
--- a/compiler/driver/dex_compilation_unit.cc
+++ b/compiler/driver/dex_compilation_unit.cc
@@ -16,7 +16,6 @@
#include "dex_compilation_unit.h"
-#include "base/stringprintf.h"
#include "mirror/dex_cache.h"
#include "utils.h"
diff --git a/compiler/elf_writer_test.cc b/compiler/elf_writer_test.cc
index 6f48779..9669c4a 100644
--- a/compiler/elf_writer_test.cc
+++ b/compiler/elf_writer_test.cc
@@ -16,7 +16,6 @@
#include "elf_file.h"
-#include "base/stringprintf.h"
#include "base/unix_file/fd_file.h"
#include "common_compiler_test.h"
#include "elf_file.h"
diff --git a/compiler/image_test.cc b/compiler/image_test.cc
index 9bbe595..1290379 100644
--- a/compiler/image_test.cc
+++ b/compiler/image_test.cc
@@ -20,6 +20,8 @@
#include <string>
#include <vector>
+#include "android-base/stringprintf.h"
+
#include "base/unix_file/fd_file.h"
#include "class_linker-inl.h"
#include "common_compiler_test.h"
@@ -134,7 +136,8 @@
// Create a generic tmp file, to be the base of the .art and .oat temporary files.
ScratchFile location;
for (int i = 0; i < static_cast<int>(class_path.size()); ++i) {
- std::string cur_location(StringPrintf("%s-%d.art", location.GetFilename().c_str(), i));
+ std::string cur_location =
+ android::base::StringPrintf("%s-%d.art", location.GetFilename().c_str(), i);
image_locations.push_back(ScratchFile(cur_location));
}
}
diff --git a/compiler/image_writer.cc b/compiler/image_writer.cc
index b22ca47..9c38445 100644
--- a/compiler/image_writer.cc
+++ b/compiler/image_writer.cc
@@ -1113,11 +1113,15 @@
}
// build an Object[] of the roots needed to restore the runtime
+ int32_t image_roots_size = ImageHeader::NumberOfImageRoots(compile_app_image_);
auto image_roots(hs.NewHandle(
- ObjectArray<Object>::Alloc(self, object_array_class.Get(), ImageHeader::kImageRootsMax)));
+ ObjectArray<Object>::Alloc(self, object_array_class.Get(), image_roots_size)));
image_roots->Set<false>(ImageHeader::kDexCaches, dex_caches.Get());
image_roots->Set<false>(ImageHeader::kClassRoots, class_linker->GetClassRoots());
- for (int i = 0; i < ImageHeader::kImageRootsMax; i++) {
+ // image_roots[ImageHeader::kClassLoader] will be set later for app image.
+ static_assert(ImageHeader::kClassLoader + 1u == ImageHeader::kImageRootsMax,
+ "Class loader should be the last image root.");
+ for (int32_t i = 0; i < ImageHeader::kImageRootsMax - 1; ++i) {
CHECK(image_roots->Get(i) != nullptr);
}
return image_roots.Get();
@@ -1539,6 +1543,12 @@
}
// Process the work stack in case anything was added by TryAssignBinSlot.
ProcessWorkStack(&work_stack);
+
+ // Store the class loader in the class roots.
+ CHECK_EQ(class_loaders_.size(), 1u);
+ CHECK_EQ(image_roots.size(), 1u);
+ CHECK(*class_loaders_.begin() != nullptr);
+ image_roots[0]->Set<false>(ImageHeader::kClassLoader, *class_loaders_.begin());
}
// Verify that all objects have assigned image bin slots.
diff --git a/compiler/jit/jit_compiler.cc b/compiler/jit/jit_compiler.cc
index 9dfb434..148ce4f 100644
--- a/compiler/jit/jit_compiler.cc
+++ b/compiler/jit/jit_compiler.cc
@@ -16,6 +16,8 @@
#include "jit_compiler.h"
+#include "android-base/stringprintf.h"
+
#include "arch/instruction_set.h"
#include "arch/instruction_set_features.h"
#include "art_method-inl.h"
@@ -81,7 +83,7 @@
va_list ap;
va_start(ap, fmt);
std::string error;
- StringAppendV(&error, fmt, ap);
+ android::base::StringAppendV(&error, fmt, ap);
LOG(FATAL) << error;
va_end(ap);
exit(EXIT_FAILURE);
diff --git a/compiler/linker/mips64/relative_patcher_mips64_test.cc b/compiler/linker/mips64/relative_patcher_mips64_test.cc
index 9e37f6b..c317058 100644
--- a/compiler/linker/mips64/relative_patcher_mips64_test.cc
+++ b/compiler/linker/mips64/relative_patcher_mips64_test.cc
@@ -45,7 +45,7 @@
const uint8_t Mips64RelativePatcherTest::kUnpatchedPcRelativeRawCode[] = {
0x34, 0x12, 0x5E, 0xEE, // auipc s2, high(diff); placeholder = 0x1234
- 0x78, 0x56, 0x52, 0x26, // addiu s2, s2, low(diff); placeholder = 0x5678
+ 0x78, 0x56, 0x52, 0x66, // daddiu s2, s2, low(diff); placeholder = 0x5678
};
const uint8_t Mips64RelativePatcherTest::kUnpatchedPcRelativeCallRawCode[] = {
0x34, 0x12, 0x3E, 0xEC, // auipc at, high(diff); placeholder = 0x1234
@@ -71,7 +71,7 @@
const uint8_t expected_code[] = {
static_cast<uint8_t>(diff >> 16), static_cast<uint8_t>(diff >> 24), 0x5E, 0xEE,
- static_cast<uint8_t>(diff), static_cast<uint8_t>(diff >> 8), 0x52, 0x26,
+ static_cast<uint8_t>(diff), static_cast<uint8_t>(diff >> 8), 0x52, 0x66,
};
EXPECT_TRUE(CheckLinkedMethod(MethodRef(1u), ArrayRef<const uint8_t>(expected_code)));
}
diff --git a/compiler/oat_test.cc b/compiler/oat_test.cc
index 0a778b0..edc93ab 100644
--- a/compiler/oat_test.cc
+++ b/compiler/oat_test.cc
@@ -14,6 +14,8 @@
* limitations under the License.
*/
+#include "android-base/stringprintf.h"
+
#include "arch/instruction_set_features.h"
#include "art_method-inl.h"
#include "base/enums.h"
@@ -48,7 +50,7 @@
va_list ap;
va_start(ap, fmt);
std::string error;
- StringAppendV(&error, fmt, ap);
+ android::base::StringAppendV(&error, fmt, ap);
LOG(FATAL) << error;
va_end(ap);
UNREACHABLE();
diff --git a/compiler/optimizing/cha_guard_optimization.cc b/compiler/optimizing/cha_guard_optimization.cc
new file mode 100644
index 0000000..fe42301
--- /dev/null
+++ b/compiler/optimizing/cha_guard_optimization.cc
@@ -0,0 +1,253 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "cha_guard_optimization.h"
+
+namespace art {
+
+// Note we can only do CHA guard elimination/motion in a single pass, since
+// if a guard is not removed, another guard might be removed due to
+// the existence of the first guard. The first guard should not be further
+// removed in another pass. For example, due to further optimizations,
+// a receiver of a guard might turn out to be a parameter value, or defined at
+// a different site, which makes the guard removable as a result. However
+// it's not safe to remove the guard in another pass since another guard might
+// have been removed due to the existence of this guard.
+//
+// As a consequence, we decided not to rely on other passes to remove them
+// (such as GVN or instruction simplifier).
+
+class CHAGuardVisitor : HGraphVisitor {
+ public:
+ explicit CHAGuardVisitor(HGraph* graph)
+ : HGraphVisitor(graph),
+ block_has_cha_guard_(GetGraph()->GetBlocks().size(),
+ 0,
+ graph->GetArena()->Adapter(kArenaAllocCHA)) {
+ number_of_guards_to_visit_ = GetGraph()->GetNumberOfCHAGuards();
+ DCHECK_NE(number_of_guards_to_visit_, 0u);
+ // Will recount number of guards during guard optimization.
+ GetGraph()->SetNumberOfCHAGuards(0);
+ }
+
+ void VisitShouldDeoptimizeFlag(HShouldDeoptimizeFlag* flag) OVERRIDE;
+
+ void VisitBasicBlock(HBasicBlock* block) OVERRIDE;
+
+ private:
+ void RemoveGuard(HShouldDeoptimizeFlag* flag);
+ // Return true if `flag` is removed.
+ bool OptimizeForParameter(HShouldDeoptimizeFlag* flag, HInstruction* receiver);
+ // Return true if `flag` is removed.
+ bool OptimizeWithDominatingGuard(HShouldDeoptimizeFlag* flag, HInstruction* receiver);
+ // Return true if `flag` is hoisted.
+ bool HoistGuard(HShouldDeoptimizeFlag* flag, HInstruction* receiver);
+
+ // Record if each block has any CHA guard. It's updated during the
+ // reverse post order visit. Use int instead of bool since ArenaVector
+ // does not support bool.
+ ArenaVector<int> block_has_cha_guard_;
+
+ // The iterator that's being used for this visitor. Need it to manually
+ // advance the iterator due to removing/moving more than one instruction.
+ HInstructionIterator* instruction_iterator_;
+
+ // Used to short-circuit the pass when there is no more guards left to visit.
+ uint32_t number_of_guards_to_visit_;
+
+ DISALLOW_COPY_AND_ASSIGN(CHAGuardVisitor);
+};
+
+void CHAGuardVisitor::VisitBasicBlock(HBasicBlock* block) {
+ if (number_of_guards_to_visit_ == 0) {
+ return;
+ }
+ // Skip phis, just iterate through instructions.
+ HInstructionIterator it(block->GetInstructions());
+ instruction_iterator_ = ⁢
+ for (; !it.Done(); it.Advance()) {
+ DCHECK(it.Current()->IsInBlock());
+ it.Current()->Accept(this);
+ }
+}
+
+void CHAGuardVisitor::RemoveGuard(HShouldDeoptimizeFlag* flag) {
+ HBasicBlock* block = flag->GetBlock();
+ HInstruction* compare = flag->GetNext();
+ DCHECK(compare->IsNotEqual());
+ HInstruction* deopt = compare->GetNext();
+ DCHECK(deopt->IsDeoptimize());
+
+ // Advance instruction iterator first before we remove the guard.
+ // We need to do it twice since we remove three instructions and the
+ // visitor is responsible for advancing it once.
+ instruction_iterator_->Advance();
+ instruction_iterator_->Advance();
+ block->RemoveInstruction(deopt);
+ block->RemoveInstruction(compare);
+ block->RemoveInstruction(flag);
+}
+
+bool CHAGuardVisitor::OptimizeForParameter(HShouldDeoptimizeFlag* flag,
+ HInstruction* receiver) {
+ // If some compiled code is invalidated by CHA due to class loading, the
+ // compiled code will not be entered anymore. So the very fact that the
+ // compiled code is invoked guarantees that a parameter receiver conforms
+ // to all the CHA devirtualization assumptions made by the compiled code,
+ // since all parameter receivers pre-exist any (potential) invalidation of
+ // the compiled code.
+ //
+ // TODO: allow more cases such as a phi whose inputs are all parameters.
+ if (receiver->IsParameterValue()) {
+ RemoveGuard(flag);
+ return true;
+ }
+ return false;
+}
+
+bool CHAGuardVisitor::OptimizeWithDominatingGuard(HShouldDeoptimizeFlag* flag,
+ HInstruction* receiver) {
+ // If there is another guard that dominates the current guard, and
+ // that guard is dominated by receiver's definition, then the current
+ // guard can be eliminated, since receiver must pre-exist that other
+ // guard, and passing that guard guarantees that receiver conforms to
+ // all the CHA devirtualization assumptions.
+ HBasicBlock* dominator = flag->GetBlock();
+ HBasicBlock* receiver_def_block = receiver->GetBlock();
+
+ // Complexity of the following algorithm:
+ // We potentially need to traverse the full dominator chain to receiver_def_block,
+ // plus a (partial) linear search within one block for each guard.
+ // So the worst case for each guard is bounded by the size of the
+ // biggest block plus the depth of the dominating tree.
+
+ while (dominator != receiver_def_block) {
+ if (block_has_cha_guard_[dominator->GetBlockId()] == 1) {
+ RemoveGuard(flag);
+ return true;
+ }
+ dominator = dominator->GetDominator();
+ }
+
+ // At this point dominator is the block where receiver is defined.
+ // We do a linear search within dominator to see if there is a guard after
+ // receiver's definition.
+ HInstruction* instruction;
+ if (dominator == flag->GetBlock()) {
+ // Flag and receiver are defined in the same block. Search backward from
+ // the current guard.
+ instruction = flag->GetPrevious();
+ } else {
+ // Search backward from the last instruction of that dominator.
+ instruction = dominator->GetLastInstruction();
+ }
+ while (instruction != receiver) {
+ if (instruction == nullptr) {
+ // receiver must be defined in this block, we didn't find it
+ // in the instruction list, so it must be a Phi.
+ DCHECK(receiver->IsPhi());
+ break;
+ }
+ if (instruction->IsShouldDeoptimizeFlag()) {
+ RemoveGuard(flag);
+ return true;
+ }
+ instruction = instruction->GetPrevious();
+ }
+ return false;
+}
+
+bool CHAGuardVisitor::HoistGuard(HShouldDeoptimizeFlag* flag,
+ HInstruction* receiver) {
+ // If receiver is loop invariant, we can hoist the guard out of the
+ // loop since passing a guard before entering the loop guarantees that
+ // receiver conforms to all the CHA devirtualization assumptions.
+ // We only hoist guards out of the inner loop since that offers most of the
+ // benefit and it might help remove other guards in the inner loop.
+ HBasicBlock* block = flag->GetBlock();
+ HLoopInformation* loop_info = block->GetLoopInformation();
+ if (loop_info != nullptr &&
+ !loop_info->IsIrreducible() &&
+ loop_info->IsDefinedOutOfTheLoop(receiver)) {
+ HInstruction* compare = flag->GetNext();
+ DCHECK(compare->IsNotEqual());
+ HInstruction* deopt = compare->GetNext();
+ DCHECK(deopt->IsDeoptimize());
+
+ // Advance instruction iterator first before we move the guard.
+ // We need to do it twice since we move three instructions and the
+ // visitor is responsible for advancing it once.
+ instruction_iterator_->Advance();
+ instruction_iterator_->Advance();
+
+ HBasicBlock* pre_header = loop_info->GetPreHeader();
+ flag->MoveBefore(pre_header->GetLastInstruction());
+ compare->MoveBefore(pre_header->GetLastInstruction());
+
+ block->RemoveInstruction(deopt);
+ HInstruction* suspend = loop_info->GetSuspendCheck();
+ // Need a new deoptimize instruction that copies the environment
+ // of the suspend instruction for the loop.
+ HDeoptimize* deoptimize =
+ new (GetGraph()->GetArena()) HDeoptimize(compare, suspend->GetDexPc());
+ pre_header->InsertInstructionBefore(deoptimize, pre_header->GetLastInstruction());
+ deoptimize->CopyEnvironmentFromWithLoopPhiAdjustment(
+ suspend->GetEnvironment(), loop_info->GetHeader());
+ block_has_cha_guard_[pre_header->GetBlockId()] = 1;
+ GetGraph()->IncrementNumberOfCHAGuards();
+ return true;
+ }
+ return false;
+}
+
+void CHAGuardVisitor::VisitShouldDeoptimizeFlag(HShouldDeoptimizeFlag* flag) {
+ number_of_guards_to_visit_--;
+ HInstruction* receiver = flag->InputAt(0);
+ // Don't need the receiver anymore.
+ flag->RemoveInputAt(0);
+ if (receiver->IsNullCheck()) {
+ receiver = receiver->InputAt(0);
+ }
+
+ if (OptimizeForParameter(flag, receiver)) {
+ DCHECK(!flag->IsInBlock());
+ return;
+ }
+ if (OptimizeWithDominatingGuard(flag, receiver)) {
+ DCHECK(!flag->IsInBlock());
+ return;
+ }
+ if (HoistGuard(flag, receiver)) {
+ DCHECK(flag->IsInBlock());
+ return;
+ }
+
+ // Need to keep the CHA guard in place.
+ block_has_cha_guard_[flag->GetBlock()->GetBlockId()] = 1;
+ GetGraph()->IncrementNumberOfCHAGuards();
+}
+
+void CHAGuardOptimization::Run() {
+ if (graph_->GetNumberOfCHAGuards() == 0) {
+ return;
+ }
+ CHAGuardVisitor visitor(graph_);
+ for (HBasicBlock* block : graph_->GetReversePostOrder()) {
+ visitor.VisitBasicBlock(block);
+ }
+}
+
+} // namespace art
diff --git a/compiler/optimizing/cha_guard_optimization.h b/compiler/optimizing/cha_guard_optimization.h
new file mode 100644
index 0000000..ba0cdb8
--- /dev/null
+++ b/compiler/optimizing/cha_guard_optimization.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_COMPILER_OPTIMIZING_CHA_GUARD_OPTIMIZATION_H_
+#define ART_COMPILER_OPTIMIZING_CHA_GUARD_OPTIMIZATION_H_
+
+#include "optimization.h"
+
+namespace art {
+
+/**
+ * Optimize CHA guards by removing/moving them.
+ */
+class CHAGuardOptimization : public HOptimization {
+ public:
+ explicit CHAGuardOptimization(HGraph* graph)
+ : HOptimization(graph, kCHAGuardOptimizationPassName) {}
+
+ void Run() OVERRIDE;
+
+ static constexpr const char* kCHAGuardOptimizationPassName = "cha_guard_optimization";
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(CHAGuardOptimization);
+};
+
+} // namespace art
+
+#endif // ART_COMPILER_OPTIMIZING_CHA_GUARD_OPTIMIZATION_H_
diff --git a/compiler/optimizing/code_generator_arm_vixl.cc b/compiler/optimizing/code_generator_arm_vixl.cc
index 2167f4b..517bdbf 100644
--- a/compiler/optimizing/code_generator_arm_vixl.cc
+++ b/compiler/optimizing/code_generator_arm_vixl.cc
@@ -46,8 +46,10 @@
using helpers::InputRegister;
using helpers::InputRegisterAt;
using helpers::InputSRegisterAt;
+using helpers::InputVRegister;
using helpers::InputVRegisterAt;
using helpers::Int32ConstantFrom;
+using helpers::Int64ConstantFrom;
using helpers::LocationFrom;
using helpers::LowRegisterFrom;
using helpers::LowSRegisterFrom;
@@ -56,6 +58,7 @@
using helpers::OutputVRegister;
using helpers::RegisterFrom;
using helpers::SRegisterFrom;
+using helpers::Uint64ConstantFrom;
using vixl::ExactAssemblyScope;
using vixl::CodeBufferCheckScope;
@@ -1394,7 +1397,7 @@
if (!skip_overflow_check) {
UseScratchRegisterScope temps(GetVIXLAssembler());
vixl32::Register temp = temps.Acquire();
- __ Sub(temp, sp, static_cast<int32_t>(GetStackOverflowReservedBytes(kArm)));
+ __ Sub(temp, sp, Operand::From(GetStackOverflowReservedBytes(kArm)));
// The load must immediately precede RecordPcInfo.
ExactAssemblyScope aas(GetVIXLAssembler(),
vixl32::kMaxInstructionSizeInBytes,
@@ -1811,7 +1814,7 @@
break;
}
if (right.IsConstant()) {
- int64_t value = right.GetConstant()->AsLongConstant()->GetValue();
+ int64_t value = Int64ConstantFrom(right);
int32_t val_low = Low32Bits(value);
int32_t val_high = High32Bits(value);
@@ -1896,7 +1899,7 @@
__ B(true_target);
}
} else {
- DCHECK(cond->AsIntConstant()->IsFalse()) << cond->AsIntConstant()->GetValue();
+ DCHECK(cond->AsIntConstant()->IsFalse()) << Int32ConstantFrom(cond);
if (false_target != nullptr) {
__ B(false_target);
}
@@ -2498,9 +2501,7 @@
case Primitive::kPrimFloat:
case Primitive::kPrimDouble:
- // TODO(VIXL): Consider introducing an InputVRegister()
- // helper function (equivalent to InputRegister()).
- __ Vneg(OutputVRegister(neg), InputVRegisterAt(neg, 0));
+ __ Vneg(OutputVRegister(neg), InputVRegister(neg));
break;
default:
@@ -2790,8 +2791,8 @@
} else {
DCHECK(in.IsConstant());
DCHECK(in.GetConstant()->IsLongConstant());
- int64_t value = in.GetConstant()->AsLongConstant()->GetValue();
- __ Mov(OutputRegister(conversion), static_cast<int32_t>(value));
+ int32_t value = Int32ConstantFrom(in);
+ __ Mov(OutputRegister(conversion), value);
}
break;
@@ -3130,8 +3131,8 @@
// Extra checks to protect caused by the existence of R1_R2.
// The algorithm is wrong if out.hi is either in1.lo or in2.lo:
// (e.g. in1=r0_r1, in2=r2_r3 and out=r1_r2);
- DCHECK_NE(out_hi.GetCode(), in1_lo.GetCode());
- DCHECK_NE(out_hi.GetCode(), in2_lo.GetCode());
+ DCHECK(!out_hi.Is(in1_lo));
+ DCHECK(!out_hi.Is(in2_lo));
// input: in1 - 64 bits, in2 - 64 bits
// output: out
@@ -3171,7 +3172,7 @@
vixl32::Register out = OutputRegister(instruction);
vixl32::Register dividend = InputRegisterAt(instruction, 0);
- int32_t imm = second.GetConstant()->AsIntConstant()->GetValue();
+ int32_t imm = Int32ConstantFrom(second);
DCHECK(imm == 1 || imm == -1);
if (instruction->IsRem()) {
@@ -3196,7 +3197,7 @@
vixl32::Register out = OutputRegister(instruction);
vixl32::Register dividend = InputRegisterAt(instruction, 0);
vixl32::Register temp = RegisterFrom(locations->GetTemp(0));
- int32_t imm = second.GetConstant()->AsIntConstant()->GetValue();
+ int32_t imm = Int32ConstantFrom(second);
uint32_t abs_imm = static_cast<uint32_t>(AbsOrMin(imm));
int ctz_imm = CTZ(abs_imm);
@@ -3269,7 +3270,7 @@
Location second = instruction->GetLocations()->InAt(1);
DCHECK(second.IsConstant());
- int32_t imm = second.GetConstant()->AsIntConstant()->GetValue();
+ int32_t imm = Int32ConstantFrom(second);
if (imm == 0) {
// Do not generate anything. DivZeroCheck would prevent any code to be executed.
} else if (imm == 1 || imm == -1) {
@@ -3303,7 +3304,7 @@
locations->SetInAt(0, Location::RequiresRegister());
locations->SetInAt(1, Location::ConstantLocation(div->InputAt(1)->AsConstant()));
locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
- int32_t value = div->InputAt(1)->AsIntConstant()->GetValue();
+ int32_t value = Int32ConstantFrom(div->InputAt(1));
if (value == 1 || value == 0 || value == -1) {
// No temp register required.
} else {
@@ -3416,7 +3417,7 @@
locations->SetInAt(0, Location::RequiresRegister());
locations->SetInAt(1, Location::ConstantLocation(rem->InputAt(1)->AsConstant()));
locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
- int32_t value = rem->InputAt(1)->AsIntConstant()->GetValue();
+ int32_t value = Int32ConstantFrom(rem->InputAt(1));
if (value == 1 || value == 0 || value == -1) {
// No temp register required.
} else {
@@ -3551,7 +3552,7 @@
__ CompareAndBranchIfZero(InputRegisterAt(instruction, 0), slow_path->GetEntryLabel());
} else {
DCHECK(value.IsConstant()) << value;
- if (value.GetConstant()->AsIntConstant()->GetValue() == 0) {
+ if (Int32ConstantFrom(value) == 0) {
__ B(slow_path->GetEntryLabel());
}
}
@@ -3565,7 +3566,7 @@
__ B(eq, slow_path->GetEntryLabel());
} else {
DCHECK(value.IsConstant()) << value;
- if (value.GetConstant()->AsLongConstant()->GetValue() == 0) {
+ if (Int64ConstantFrom(value) == 0) {
__ B(slow_path->GetEntryLabel());
}
}
@@ -3775,7 +3776,7 @@
__ Lsr(out_reg, first_reg, out_reg);
}
} else {
- int32_t cst = second.GetConstant()->AsIntConstant()->GetValue();
+ int32_t cst = Int32ConstantFrom(second);
uint32_t shift_value = cst & kMaxIntShiftDistance;
if (shift_value == 0) { // ARM does not support shifting with 0 immediate.
__ Mov(out_reg, first_reg);
@@ -3860,7 +3861,7 @@
// Register allocator doesn't create partial overlap.
DCHECK(!o_l.Is(high));
DCHECK(!o_h.Is(low));
- int32_t cst = second.GetConstant()->AsIntConstant()->GetValue();
+ int32_t cst = Int32ConstantFrom(second);
uint32_t shift_value = cst & kMaxLongShiftDistance;
if (shift_value > 32) {
if (op->IsShl()) {
@@ -4927,7 +4928,7 @@
codegen_->MaybeRecordImplicitNullCheck(instruction);
}
if (index.IsConstant()) {
- int32_t const_index = index.GetConstant()->AsIntConstant()->GetValue();
+ int32_t const_index = Int32ConstantFrom(index);
if (maybe_compressed_char_at) {
vixl32::Label uncompressed_load, done;
__ Lsrs(length, length, 1u); // LSRS has a 16-bit encoding, TST (immediate) does not.
@@ -4961,7 +4962,7 @@
// `TryExtractArrayAccessAddress()`.
if (kIsDebugBuild) {
HIntermediateAddress* tmp = array_instr->AsIntermediateAddress();
- DCHECK_EQ(tmp->GetOffset()->AsIntConstant()->GetValueAsUint64(), data_offset);
+ DCHECK_EQ(Uint64ConstantFrom(tmp->GetOffset()), data_offset);
}
temp = obj;
} else {
@@ -5006,7 +5007,7 @@
vixl32::Register out = OutputRegister(instruction);
if (index.IsConstant()) {
size_t offset =
- (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset;
+ (Int32ConstantFrom(index) << TIMES_4) + data_offset;
GetAssembler()->LoadFromOffset(kLoadWord, out, obj, offset);
// TODO(VIXL): Here and for other calls to `MaybeRecordImplicitNullCheck` in this method,
// we should use a scope and the assembler to emit the load instruction to guarantee that
@@ -5028,7 +5029,7 @@
// `TryExtractArrayAccessAddress()`.
if (kIsDebugBuild) {
HIntermediateAddress* tmp = array_instr->AsIntermediateAddress();
- DCHECK_EQ(tmp->GetOffset()->AsIntConstant()->GetValueAsUint64(), data_offset);
+ DCHECK_EQ(Uint64ConstantFrom(tmp->GetOffset()), data_offset);
}
temp = obj;
} else {
@@ -5053,7 +5054,7 @@
case Primitive::kPrimLong: {
if (index.IsConstant()) {
size_t offset =
- (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_8) + data_offset;
+ (Int32ConstantFrom(index) << TIMES_8) + data_offset;
GetAssembler()->LoadFromOffset(kLoadWordPair, LowRegisterFrom(out_loc), obj, offset);
} else {
UseScratchRegisterScope temps(GetVIXLAssembler());
@@ -5067,7 +5068,7 @@
case Primitive::kPrimFloat: {
vixl32::SRegister out = SRegisterFrom(out_loc);
if (index.IsConstant()) {
- size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset;
+ size_t offset = (Int32ConstantFrom(index) << TIMES_4) + data_offset;
GetAssembler()->LoadSFromOffset(out, obj, offset);
} else {
UseScratchRegisterScope temps(GetVIXLAssembler());
@@ -5080,7 +5081,7 @@
case Primitive::kPrimDouble: {
if (index.IsConstant()) {
- size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_8) + data_offset;
+ size_t offset = (Int32ConstantFrom(index) << TIMES_8) + data_offset;
GetAssembler()->LoadDFromOffset(DRegisterFrom(out_loc), obj, offset);
} else {
UseScratchRegisterScope temps(GetVIXLAssembler());
@@ -5154,7 +5155,7 @@
case Primitive::kPrimChar:
case Primitive::kPrimInt: {
if (index.IsConstant()) {
- int32_t const_index = index.GetConstant()->AsIntConstant()->GetValue();
+ int32_t const_index = Int32ConstantFrom(index);
uint32_t full_offset =
data_offset + (const_index << Primitive::ComponentSizeShift(value_type));
StoreOperandType store_type = GetStoreOperandType(value_type);
@@ -5169,7 +5170,7 @@
// `TryExtractArrayAccessAddress()`.
if (kIsDebugBuild) {
HIntermediateAddress* tmp = array_instr->AsIntermediateAddress();
- DCHECK(tmp->GetOffset()->AsIntConstant()->GetValueAsUint64() == data_offset);
+ DCHECK_EQ(Uint64ConstantFrom(tmp->GetOffset()), data_offset);
}
temp = array;
} else {
@@ -5190,7 +5191,7 @@
// Just setting null.
if (index.IsConstant()) {
size_t offset =
- (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset;
+ (Int32ConstantFrom(index) << TIMES_4) + data_offset;
GetAssembler()->StoreToOffset(kStoreWord, value, array, offset);
} else {
DCHECK(index.IsRegister()) << index;
@@ -5226,7 +5227,7 @@
__ CompareAndBranchIfNonZero(value, &non_zero);
if (index.IsConstant()) {
size_t offset =
- (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset;
+ (Int32ConstantFrom(index) << TIMES_4) + data_offset;
GetAssembler()->StoreToOffset(kStoreWord, value, array, offset);
} else {
DCHECK(index.IsRegister()) << index;
@@ -5300,7 +5301,7 @@
if (index.IsConstant()) {
size_t offset =
- (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset;
+ (Int32ConstantFrom(index) << TIMES_4) + data_offset;
GetAssembler()->StoreToOffset(kStoreWord, source, array, offset);
} else {
DCHECK(index.IsRegister()) << index;
@@ -5337,7 +5338,7 @@
Location value = locations->InAt(2);
if (index.IsConstant()) {
size_t offset =
- (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_8) + data_offset;
+ (Int32ConstantFrom(index) << TIMES_8) + data_offset;
GetAssembler()->StoreToOffset(kStoreWordPair, LowRegisterFrom(value), array, offset);
} else {
UseScratchRegisterScope temps(GetVIXLAssembler());
@@ -5352,7 +5353,7 @@
Location value = locations->InAt(2);
DCHECK(value.IsFpuRegister());
if (index.IsConstant()) {
- size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_4) + data_offset;
+ size_t offset = (Int32ConstantFrom(index) << TIMES_4) + data_offset;
GetAssembler()->StoreSToOffset(SRegisterFrom(value), array, offset);
} else {
UseScratchRegisterScope temps(GetVIXLAssembler());
@@ -5367,7 +5368,7 @@
Location value = locations->InAt(2);
DCHECK(value.IsFpuRegisterPair());
if (index.IsConstant()) {
- size_t offset = (index.GetConstant()->AsIntConstant()->GetValue() << TIMES_8) + data_offset;
+ size_t offset = (Int32ConstantFrom(index) << TIMES_8) + data_offset;
GetAssembler()->StoreDToOffset(DRegisterFrom(value), array, offset);
} else {
UseScratchRegisterScope temps(GetVIXLAssembler());
@@ -5432,7 +5433,7 @@
if (second.IsRegister()) {
__ Add(out, first, RegisterFrom(second));
} else {
- __ Add(out, first, second.GetConstant()->AsIntConstant()->GetValue());
+ __ Add(out, first, Int32ConstantFrom(second));
}
}
@@ -5628,7 +5629,7 @@
GetAssembler()->StoreToOffset(kStoreWord, temp, sp, destination.GetStackIndex());
}
} else if (constant->IsLongConstant()) {
- int64_t value = constant->AsLongConstant()->GetValue();
+ int64_t value = Int64ConstantFrom(source);
if (destination.IsRegisterPair()) {
__ Mov(LowRegisterFrom(destination), Low32Bits(value));
__ Mov(HighRegisterFrom(destination), High32Bits(value));
diff --git a/compiler/optimizing/code_generator_mips.cc b/compiler/optimizing/code_generator_mips.cc
index 456c5c6..897b719 100644
--- a/compiler/optimizing/code_generator_mips.cc
+++ b/compiler/optimizing/code_generator_mips.cc
@@ -5689,11 +5689,7 @@
}
void LocationsBuilderMIPS::VisitLoadString(HLoadString* load) {
- LocationSummary::CallKind call_kind = (load->NeedsEnvironment() || kEmitCompilerReadBarrier)
- ? ((load->GetLoadKind() == HLoadString::LoadKind::kDexCacheViaMethod)
- ? LocationSummary::kCallOnMainOnly
- : LocationSummary::kCallOnSlowPath)
- : LocationSummary::kNoCall;
+ LocationSummary::CallKind call_kind = CodeGenerator::GetLoadStringCallKind(load);
LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(load, call_kind);
HLoadString::LoadKind load_kind = load->GetLoadKind();
switch (load_kind) {
@@ -5743,14 +5739,12 @@
switch (load_kind) {
case HLoadString::LoadKind::kBootImageLinkTimeAddress:
- DCHECK(!kEmitCompilerReadBarrier);
__ LoadLiteral(out,
base_or_current_method_reg,
codegen_->DeduplicateBootImageStringLiteral(load->GetDexFile(),
load->GetStringIndex()));
return; // No dex cache slow path.
case HLoadString::LoadKind::kBootImageLinkTimePcRelative: {
- DCHECK(!kEmitCompilerReadBarrier);
DCHECK(codegen_->GetCompilerOptions().IsBootImage());
CodeGeneratorMIPS::PcRelativePatchInfo* info =
codegen_->NewPcRelativeStringPatch(load->GetDexFile(), load->GetStringIndex().index_);
@@ -5758,7 +5752,6 @@
return; // No dex cache slow path.
}
case HLoadString::LoadKind::kBootImageAddress: {
- DCHECK(!kEmitCompilerReadBarrier);
DCHECK_NE(load->GetAddress(), 0u);
uint32_t address = dchecked_integral_cast<uint32_t>(load->GetAddress());
__ LoadLiteral(out,
diff --git a/compiler/optimizing/code_generator_mips64.cc b/compiler/optimizing/code_generator_mips64.cc
index 44d3759..cf9f9d4 100644
--- a/compiler/optimizing/code_generator_mips64.cc
+++ b/compiler/optimizing/code_generator_mips64.cc
@@ -91,8 +91,6 @@
// Space on the stack is reserved for all arguments.
stack_index_ += Primitive::Is64BitType(type) ? 2 : 1;
- // TODO: review
-
// TODO: shouldn't we use a whole machine word per argument on the stack?
// Implicit 4-byte method pointer (and such) will cause misalignment.
@@ -235,6 +233,7 @@
SaveLiveRegisters(codegen, locations);
InvokeRuntimeCallingConvention calling_convention;
+ HLoadString* load = instruction_->AsLoadString();
const uint32_t string_index = instruction_->AsLoadString()->GetStringIndex().index_;
__ LoadConst32(calling_convention.GetRegisterAt(0), string_index);
mips64_codegen->InvokeRuntime(kQuickResolveString,
@@ -248,6 +247,17 @@
type);
RestoreLiveRegisters(codegen, locations);
+
+ // Store the resolved String to the BSS entry.
+ // TODO: Change art_quick_resolve_string to kSaveEverything and use a temporary for the
+ // .bss entry address in the fast path, so that we can avoid another calculation here.
+ GpuRegister out = locations->Out().AsRegister<GpuRegister>();
+ DCHECK_NE(out, AT);
+ CodeGeneratorMIPS64::PcRelativePatchInfo* info =
+ mips64_codegen->NewPcRelativeStringPatch(load->GetDexFile(), string_index);
+ mips64_codegen->EmitPcRelativeAddressPlaceholderHigh(info, AT);
+ __ Sw(out, AT, /* placeholder */ 0x5678);
+
__ Bc(GetExitLabel());
}
@@ -401,6 +411,8 @@
move_resolver_(graph->GetArena(), this),
assembler_(graph->GetArena()),
isa_features_(isa_features),
+ uint32_literals_(std::less<uint32_t>(),
+ graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
uint64_literals_(std::less<uint64_t>(),
graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
method_patches_(MethodReferenceComparator(),
@@ -408,7 +420,15 @@
call_patches_(MethodReferenceComparator(),
graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
pc_relative_dex_cache_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
- relative_call_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)) {
+ relative_call_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
+ boot_image_string_patches_(StringReferenceValueComparator(),
+ graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
+ pc_relative_string_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
+ boot_image_type_patches_(TypeReferenceValueComparator(),
+ graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
+ pc_relative_type_patches_(graph->GetArena()->Adapter(kArenaAllocCodeGenerator)),
+ boot_image_address_patches_(std::less<uint32_t>(),
+ graph->GetArena()->Adapter(kArenaAllocCodeGenerator)) {
// Save RA (containing the return address) to mimic Quick.
AddAllocatedRegister(Location::RegisterLocation(RA));
}
@@ -907,7 +927,12 @@
method_patches_.size() +
call_patches_.size() +
pc_relative_dex_cache_patches_.size() +
- relative_call_patches_.size();
+ relative_call_patches_.size() +
+ pc_relative_string_patches_.size() +
+ pc_relative_type_patches_.size() +
+ boot_image_string_patches_.size() +
+ boot_image_type_patches_.size() +
+ boot_image_address_patches_.size();
linker_patches->reserve(size);
for (const auto& entry : method_patches_) {
const MethodReference& target_method = entry.first;
@@ -937,6 +962,50 @@
linker_patches->push_back(
LinkerPatch::RelativeCodePatch(pc_rel_offset, &dex_file, method_index));
}
+ if (!GetCompilerOptions().IsBootImage()) {
+ EmitPcRelativeLinkerPatches<LinkerPatch::StringBssEntryPatch>(pc_relative_string_patches_,
+ linker_patches);
+ } else {
+ EmitPcRelativeLinkerPatches<LinkerPatch::RelativeStringPatch>(pc_relative_string_patches_,
+ linker_patches);
+ }
+ EmitPcRelativeLinkerPatches<LinkerPatch::RelativeTypePatch>(pc_relative_type_patches_,
+ linker_patches);
+ for (const auto& entry : boot_image_string_patches_) {
+ const StringReference& target_string = entry.first;
+ Literal* literal = entry.second;
+ DCHECK(literal->GetLabel()->IsBound());
+ uint32_t literal_offset = __ GetLabelLocation(literal->GetLabel());
+ linker_patches->push_back(LinkerPatch::StringPatch(literal_offset,
+ target_string.dex_file,
+ target_string.string_index.index_));
+ }
+ for (const auto& entry : boot_image_type_patches_) {
+ const TypeReference& target_type = entry.first;
+ Literal* literal = entry.second;
+ DCHECK(literal->GetLabel()->IsBound());
+ uint32_t literal_offset = __ GetLabelLocation(literal->GetLabel());
+ linker_patches->push_back(LinkerPatch::TypePatch(literal_offset,
+ target_type.dex_file,
+ target_type.type_index.index_));
+ }
+ for (const auto& entry : boot_image_address_patches_) {
+ DCHECK(GetCompilerOptions().GetIncludePatchInformation());
+ Literal* literal = entry.second;
+ DCHECK(literal->GetLabel()->IsBound());
+ uint32_t literal_offset = __ GetLabelLocation(literal->GetLabel());
+ linker_patches->push_back(LinkerPatch::RecordPosition(literal_offset));
+ }
+}
+
+CodeGeneratorMIPS64::PcRelativePatchInfo* CodeGeneratorMIPS64::NewPcRelativeStringPatch(
+ const DexFile& dex_file, uint32_t string_index) {
+ return NewPcRelativePatch(dex_file, string_index, &pc_relative_string_patches_);
+}
+
+CodeGeneratorMIPS64::PcRelativePatchInfo* CodeGeneratorMIPS64::NewPcRelativeTypePatch(
+ const DexFile& dex_file, dex::TypeIndex type_index) {
+ return NewPcRelativePatch(dex_file, type_index.index_, &pc_relative_type_patches_);
}
CodeGeneratorMIPS64::PcRelativePatchInfo* CodeGeneratorMIPS64::NewPcRelativeDexCacheArrayPatch(
@@ -955,6 +1024,12 @@
return &patches->back();
}
+Literal* CodeGeneratorMIPS64::DeduplicateUint32Literal(uint32_t value, Uint32ToLiteralMap* map) {
+ return map->GetOrCreate(
+ value,
+ [this, value]() { return __ NewLiteral<uint32_t>(value); });
+}
+
Literal* CodeGeneratorMIPS64::DeduplicateUint64Literal(uint64_t value) {
return uint64_literals_.GetOrCreate(
value,
@@ -976,13 +1051,33 @@
return DeduplicateMethodLiteral(target_method, &call_patches_);
}
+Literal* CodeGeneratorMIPS64::DeduplicateBootImageStringLiteral(const DexFile& dex_file,
+ dex::StringIndex string_index) {
+ return boot_image_string_patches_.GetOrCreate(
+ StringReference(&dex_file, string_index),
+ [this]() { return __ NewLiteral<uint32_t>(/* placeholder */ 0u); });
+}
+
+Literal* CodeGeneratorMIPS64::DeduplicateBootImageTypeLiteral(const DexFile& dex_file,
+ dex::TypeIndex type_index) {
+ return boot_image_type_patches_.GetOrCreate(
+ TypeReference(&dex_file, type_index),
+ [this]() { return __ NewLiteral<uint32_t>(/* placeholder */ 0u); });
+}
+
+Literal* CodeGeneratorMIPS64::DeduplicateBootImageAddressLiteral(uint64_t address) {
+ bool needs_patch = GetCompilerOptions().GetIncludePatchInformation();
+ Uint32ToLiteralMap* map = needs_patch ? &boot_image_address_patches_ : &uint32_literals_;
+ return DeduplicateUint32Literal(dchecked_integral_cast<uint32_t>(address), map);
+}
+
void CodeGeneratorMIPS64::EmitPcRelativeAddressPlaceholderHigh(PcRelativePatchInfo* info,
GpuRegister out) {
__ Bind(&info->pc_rel_label);
// Add the high half of a 32-bit offset to PC.
__ Auipc(out, /* placeholder */ 0x1234);
// The immediately following instruction will add the sign-extended low half of the 32-bit
- // offset to `out` (e.g. ld, jialc, addiu).
+ // offset to `out` (e.g. ld, jialc, daddiu).
}
void CodeGeneratorMIPS64::SetupBlockedRegisters() const {
@@ -1008,8 +1103,6 @@
// Reserve T9 for function calls
blocked_core_registers_[T9] = true;
- // TODO: review; anything else?
-
if (GetGraph()->IsDebuggable()) {
// Stubs do not save callee-save floating point registers. If the graph
// is debuggable, we need to deal with these registers differently. For
@@ -2929,6 +3022,31 @@
HandleFieldSet(instruction, instruction->GetFieldInfo(), instruction->GetValueCanBeNull());
}
+void InstructionCodeGeneratorMIPS64::GenerateGcRootFieldLoad(
+ HInstruction* instruction ATTRIBUTE_UNUSED,
+ Location root,
+ GpuRegister obj,
+ uint32_t offset) {
+ // When handling HLoadClass::LoadKind::kDexCachePcRelative, the caller calls
+ // EmitPcRelativeAddressPlaceholderHigh() and then GenerateGcRootFieldLoad().
+ // The relative patcher expects the two methods to emit the following patchable
+ // sequence of instructions in this case:
+ // auipc reg1, 0x1234 // 0x1234 is a placeholder for offset_high.
+ // lwu reg2, 0x5678(reg1) // 0x5678 is a placeholder for offset_low.
+ // TODO: Adjust GenerateGcRootFieldLoad() and its caller when this method is
+ // extended (e.g. for read barriers) so as not to break the relative patcher.
+ GpuRegister root_reg = root.AsRegister<GpuRegister>();
+ if (kEmitCompilerReadBarrier) {
+ UNIMPLEMENTED(FATAL) << "for read barrier";
+ } else {
+ // Plain GC root load with no read barrier.
+ // /* GcRoot<mirror::Object> */ root = *(obj + offset)
+ __ LoadFromOffset(kLoadUnsignedWord, root_reg, obj, offset);
+ // Note that GC roots are not affected by heap poisoning, thus we
+ // do not have to unpoison `root_reg` here.
+ }
+}
+
void LocationsBuilderMIPS64::VisitInstanceOf(HInstanceOf* instruction) {
LocationSummary::CallKind call_kind =
instruction->IsExactCheck() ? LocationSummary::kNoCall : LocationSummary::kCallOnSlowPath;
@@ -3080,16 +3198,69 @@
}
HLoadString::LoadKind CodeGeneratorMIPS64::GetSupportedLoadStringKind(
- HLoadString::LoadKind desired_string_load_kind ATTRIBUTE_UNUSED) {
- // TODO: Implement other kinds.
- return HLoadString::LoadKind::kDexCacheViaMethod;
+ HLoadString::LoadKind desired_string_load_kind) {
+ if (kEmitCompilerReadBarrier) {
+ UNIMPLEMENTED(FATAL) << "for read barrier";
+ }
+ bool fallback_load = false;
+ switch (desired_string_load_kind) {
+ case HLoadString::LoadKind::kBootImageLinkTimeAddress:
+ DCHECK(!GetCompilerOptions().GetCompilePic());
+ break;
+ case HLoadString::LoadKind::kBootImageLinkTimePcRelative:
+ DCHECK(GetCompilerOptions().GetCompilePic());
+ break;
+ case HLoadString::LoadKind::kBootImageAddress:
+ break;
+ case HLoadString::LoadKind::kBssEntry:
+ DCHECK(!Runtime::Current()->UseJitCompilation());
+ break;
+ case HLoadString::LoadKind::kDexCacheViaMethod:
+ break;
+ case HLoadString::LoadKind::kJitTableAddress:
+ DCHECK(Runtime::Current()->UseJitCompilation());
+ // TODO: implement.
+ fallback_load = true;
+ break;
+ }
+ if (fallback_load) {
+ desired_string_load_kind = HLoadString::LoadKind::kDexCacheViaMethod;
+ }
+ return desired_string_load_kind;
}
HLoadClass::LoadKind CodeGeneratorMIPS64::GetSupportedLoadClassKind(
HLoadClass::LoadKind desired_class_load_kind) {
- DCHECK_NE(desired_class_load_kind, HLoadClass::LoadKind::kReferrersClass);
- // TODO: Implement other kinds.
- return HLoadClass::LoadKind::kDexCacheViaMethod;
+ if (kEmitCompilerReadBarrier) {
+ UNIMPLEMENTED(FATAL) << "for read barrier";
+ }
+ bool fallback_load = false;
+ switch (desired_class_load_kind) {
+ case HLoadClass::LoadKind::kReferrersClass:
+ break;
+ case HLoadClass::LoadKind::kBootImageLinkTimeAddress:
+ DCHECK(!GetCompilerOptions().GetCompilePic());
+ break;
+ case HLoadClass::LoadKind::kBootImageLinkTimePcRelative:
+ DCHECK(GetCompilerOptions().GetCompilePic());
+ break;
+ case HLoadClass::LoadKind::kBootImageAddress:
+ break;
+ case HLoadClass::LoadKind::kJitTableAddress:
+ DCHECK(Runtime::Current()->UseJitCompilation());
+ // TODO: implement.
+ fallback_load = true;
+ break;
+ case HLoadClass::LoadKind::kDexCachePcRelative:
+ DCHECK(!Runtime::Current()->UseJitCompilation());
+ break;
+ case HLoadClass::LoadKind::kDexCacheViaMethod:
+ break;
+ }
+ if (fallback_load) {
+ desired_class_load_kind = HLoadClass::LoadKind::kDexCacheViaMethod;
+ }
+ return desired_class_load_kind;
}
HInvokeStaticOrDirect::DispatchInfo CodeGeneratorMIPS64::GetSupportedInvokeStaticOrDirectDispatch(
@@ -3271,11 +3442,26 @@
}
void LocationsBuilderMIPS64::VisitLoadClass(HLoadClass* cls) {
- InvokeRuntimeCallingConvention calling_convention;
- CodeGenerator::CreateLoadClassLocationSummary(
- cls,
- Location::RegisterLocation(calling_convention.GetRegisterAt(0)),
- calling_convention.GetReturnLocation(cls->GetType()));
+ if (cls->NeedsAccessCheck()) {
+ InvokeRuntimeCallingConvention calling_convention;
+ CodeGenerator::CreateLoadClassLocationSummary(
+ cls,
+ Location::RegisterLocation(calling_convention.GetRegisterAt(0)),
+ calling_convention.GetReturnLocation(Primitive::kPrimNot),
+ /* code_generator_supports_read_barrier */ false);
+ return;
+ }
+
+ LocationSummary::CallKind call_kind = (cls->NeedsEnvironment() || kEmitCompilerReadBarrier)
+ ? LocationSummary::kCallOnSlowPath
+ : LocationSummary::kNoCall;
+ LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(cls, call_kind);
+ HLoadClass::LoadKind load_kind = cls->GetLoadKind();
+ if (load_kind == HLoadClass::LoadKind::kReferrersClass ||
+ load_kind == HLoadClass::LoadKind::kDexCacheViaMethod) {
+ locations->SetInAt(0, Location::RequiresRegister());
+ }
+ locations->SetOut(Location::RequiresRegister());
}
void InstructionCodeGeneratorMIPS64::VisitLoadClass(HLoadClass* cls) {
@@ -3287,35 +3473,90 @@
return;
}
- GpuRegister out = locations->Out().AsRegister<GpuRegister>();
- GpuRegister current_method = locations->InAt(0).AsRegister<GpuRegister>();
- if (cls->IsReferrersClass()) {
- DCHECK(!cls->CanCallRuntime());
- DCHECK(!cls->MustGenerateClinitCheck());
- __ LoadFromOffset(kLoadUnsignedWord, out, current_method,
- ArtMethod::DeclaringClassOffset().Int32Value());
- } else {
- __ LoadFromOffset(kLoadDoubleword, out, current_method,
- ArtMethod::DexCacheResolvedTypesOffset(kMips64PointerSize).Int32Value());
- __ LoadFromOffset(
- kLoadUnsignedWord, out, out, CodeGenerator::GetCacheOffset(cls->GetTypeIndex().index_));
- // TODO: We will need a read barrier here.
- if (!cls->IsInDexCache() || cls->MustGenerateClinitCheck()) {
- DCHECK(cls->CanCallRuntime());
- SlowPathCodeMIPS64* slow_path = new (GetGraph()->GetArena()) LoadClassSlowPathMIPS64(
- cls,
- cls,
- cls->GetDexPc(),
- cls->MustGenerateClinitCheck());
- codegen_->AddSlowPath(slow_path);
- if (!cls->IsInDexCache()) {
- __ Beqzc(out, slow_path->GetEntryLabel());
- }
- if (cls->MustGenerateClinitCheck()) {
- GenerateClassInitializationCheck(slow_path, out);
- } else {
- __ Bind(slow_path->GetExitLabel());
- }
+ HLoadClass::LoadKind load_kind = cls->GetLoadKind();
+ Location out_loc = locations->Out();
+ GpuRegister out = out_loc.AsRegister<GpuRegister>();
+ GpuRegister current_method_reg = ZERO;
+ if (load_kind == HLoadClass::LoadKind::kReferrersClass ||
+ load_kind == HLoadClass::LoadKind::kDexCacheViaMethod) {
+ current_method_reg = locations->InAt(0).AsRegister<GpuRegister>();
+ }
+
+ bool generate_null_check = false;
+ switch (load_kind) {
+ case HLoadClass::LoadKind::kReferrersClass:
+ DCHECK(!cls->CanCallRuntime());
+ DCHECK(!cls->MustGenerateClinitCheck());
+ // /* GcRoot<mirror::Class> */ out = current_method->declaring_class_
+ GenerateGcRootFieldLoad(cls,
+ out_loc,
+ current_method_reg,
+ ArtMethod::DeclaringClassOffset().Int32Value());
+ break;
+ case HLoadClass::LoadKind::kBootImageLinkTimeAddress:
+ DCHECK(!kEmitCompilerReadBarrier);
+ __ LoadLiteral(out,
+ kLoadUnsignedWord,
+ codegen_->DeduplicateBootImageTypeLiteral(cls->GetDexFile(),
+ cls->GetTypeIndex()));
+ break;
+ case HLoadClass::LoadKind::kBootImageLinkTimePcRelative: {
+ DCHECK(!kEmitCompilerReadBarrier);
+ CodeGeneratorMIPS64::PcRelativePatchInfo* info =
+ codegen_->NewPcRelativeTypePatch(cls->GetDexFile(), cls->GetTypeIndex());
+ codegen_->EmitPcRelativeAddressPlaceholderHigh(info, AT);
+ __ Daddiu(out, AT, /* placeholder */ 0x5678);
+ break;
+ }
+ case HLoadClass::LoadKind::kBootImageAddress: {
+ DCHECK(!kEmitCompilerReadBarrier);
+ DCHECK_NE(cls->GetAddress(), 0u);
+ uint32_t address = dchecked_integral_cast<uint32_t>(cls->GetAddress());
+ __ LoadLiteral(out,
+ kLoadUnsignedWord,
+ codegen_->DeduplicateBootImageAddressLiteral(address));
+ break;
+ }
+ case HLoadClass::LoadKind::kJitTableAddress: {
+ LOG(FATAL) << "Unimplemented";
+ break;
+ }
+ case HLoadClass::LoadKind::kDexCachePcRelative: {
+ uint32_t element_offset = cls->GetDexCacheElementOffset();
+ CodeGeneratorMIPS64::PcRelativePatchInfo* info =
+ codegen_->NewPcRelativeDexCacheArrayPatch(cls->GetDexFile(), element_offset);
+ codegen_->EmitPcRelativeAddressPlaceholderHigh(info, AT);
+ // /* GcRoot<mirror::Class> */ out = *address /* PC-relative */
+ GenerateGcRootFieldLoad(cls, out_loc, AT, /* placeholder */ 0x5678);
+ generate_null_check = !cls->IsInDexCache();
+ break;
+ }
+ case HLoadClass::LoadKind::kDexCacheViaMethod: {
+ // /* GcRoot<mirror::Class>[] */ out =
+ // current_method.ptr_sized_fields_->dex_cache_resolved_types_
+ __ LoadFromOffset(kLoadDoubleword,
+ out,
+ current_method_reg,
+ ArtMethod::DexCacheResolvedTypesOffset(kMips64PointerSize).Int32Value());
+ // /* GcRoot<mirror::Class> */ out = out[type_index]
+ size_t offset = CodeGenerator::GetCacheOffset(cls->GetTypeIndex().index_);
+ GenerateGcRootFieldLoad(cls, out_loc, out, offset);
+ generate_null_check = !cls->IsInDexCache();
+ }
+ }
+
+ if (generate_null_check || cls->MustGenerateClinitCheck()) {
+ DCHECK(cls->CanCallRuntime());
+ SlowPathCodeMIPS64* slow_path = new (GetGraph()->GetArena()) LoadClassSlowPathMIPS64(
+ cls, cls, cls->GetDexPc(), cls->MustGenerateClinitCheck());
+ codegen_->AddSlowPath(slow_path);
+ if (generate_null_check) {
+ __ Beqzc(out, slow_path->GetEntryLabel());
+ }
+ if (cls->MustGenerateClinitCheck()) {
+ GenerateClassInitializationCheck(slow_path, out);
+ } else {
+ __ Bind(slow_path->GetExitLabel());
}
}
}
@@ -3344,20 +3585,68 @@
}
void LocationsBuilderMIPS64::VisitLoadString(HLoadString* load) {
- LocationSummary::CallKind call_kind = load->NeedsEnvironment()
- ? LocationSummary::kCallOnSlowPath
- : LocationSummary::kNoCall;
+ HLoadString::LoadKind load_kind = load->GetLoadKind();
+ LocationSummary::CallKind call_kind = CodeGenerator::GetLoadStringCallKind(load);
LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(load, call_kind);
- locations->SetInAt(0, Location::RequiresRegister());
- locations->SetOut(Location::RequiresRegister());
+ if (load_kind == HLoadString::LoadKind::kDexCacheViaMethod) {
+ InvokeRuntimeCallingConvention calling_convention;
+ locations->SetOut(calling_convention.GetReturnLocation(load->GetType()));
+ } else {
+ locations->SetOut(Location::RequiresRegister());
+ }
}
void InstructionCodeGeneratorMIPS64::VisitLoadString(HLoadString* load) {
+ HLoadString::LoadKind load_kind = load->GetLoadKind();
+ LocationSummary* locations = load->GetLocations();
+ Location out_loc = locations->Out();
+ GpuRegister out = out_loc.AsRegister<GpuRegister>();
+
+ switch (load_kind) {
+ case HLoadString::LoadKind::kBootImageLinkTimeAddress:
+ __ LoadLiteral(out,
+ kLoadUnsignedWord,
+ codegen_->DeduplicateBootImageStringLiteral(load->GetDexFile(),
+ load->GetStringIndex()));
+ return; // No dex cache slow path.
+ case HLoadString::LoadKind::kBootImageLinkTimePcRelative: {
+ DCHECK(codegen_->GetCompilerOptions().IsBootImage());
+ CodeGeneratorMIPS64::PcRelativePatchInfo* info =
+ codegen_->NewPcRelativeStringPatch(load->GetDexFile(), load->GetStringIndex().index_);
+ codegen_->EmitPcRelativeAddressPlaceholderHigh(info, AT);
+ __ Daddiu(out, AT, /* placeholder */ 0x5678);
+ return; // No dex cache slow path.
+ }
+ case HLoadString::LoadKind::kBootImageAddress: {
+ DCHECK_NE(load->GetAddress(), 0u);
+ uint32_t address = dchecked_integral_cast<uint32_t>(load->GetAddress());
+ __ LoadLiteral(out,
+ kLoadUnsignedWord,
+ codegen_->DeduplicateBootImageAddressLiteral(address));
+ return; // No dex cache slow path.
+ }
+ case HLoadString::LoadKind::kBssEntry: {
+ DCHECK(!codegen_->GetCompilerOptions().IsBootImage());
+ CodeGeneratorMIPS64::PcRelativePatchInfo* info =
+ codegen_->NewPcRelativeStringPatch(load->GetDexFile(), load->GetStringIndex().index_);
+ codegen_->EmitPcRelativeAddressPlaceholderHigh(info, AT);
+ __ Lwu(out, AT, /* placeholder */ 0x5678);
+ SlowPathCodeMIPS64* slow_path = new (GetGraph()->GetArena()) LoadStringSlowPathMIPS64(load);
+ codegen_->AddSlowPath(slow_path);
+ __ Beqzc(out, slow_path->GetEntryLabel());
+ __ Bind(slow_path->GetExitLabel());
+ return;
+ }
+ default:
+ break;
+ }
+
// TODO: Re-add the compiler code to do string dex cache lookup again.
- SlowPathCodeMIPS64* slow_path = new (GetGraph()->GetArena()) LoadStringSlowPathMIPS64(load);
- codegen_->AddSlowPath(slow_path);
- __ Bc(slow_path->GetEntryLabel());
- __ Bind(slow_path->GetExitLabel());
+ DCHECK(load_kind == HLoadString::LoadKind::kDexCacheViaMethod);
+ InvokeRuntimeCallingConvention calling_convention;
+ __ LoadConst32(calling_convention.GetRegisterAt(0), load->GetStringIndex().index_);
+ codegen_->InvokeRuntime(kQuickResolveString, load, load->GetDexPc());
+ CheckEntrypointTypes<kQuickResolveString, void*, uint32_t>();
}
void LocationsBuilderMIPS64::VisitLongConstant(HLongConstant* constant) {
diff --git a/compiler/optimizing/code_generator_mips64.h b/compiler/optimizing/code_generator_mips64.h
index 067c1f9..cbd4957 100644
--- a/compiler/optimizing/code_generator_mips64.h
+++ b/compiler/optimizing/code_generator_mips64.h
@@ -22,6 +22,7 @@
#include "nodes.h"
#include "parallel_move_resolver.h"
#include "utils/mips64/assembler_mips64.h"
+#include "utils/type_reference.h"
namespace art {
namespace mips64 {
@@ -227,6 +228,15 @@
const FieldInfo& field_info,
bool value_can_be_null);
void HandleFieldGet(HInstruction* instruction, const FieldInfo& field_info);
+ // Generate a GC root reference load:
+ //
+ // root <- *(obj + offset)
+ //
+ // while honoring read barriers (if any).
+ void GenerateGcRootFieldLoad(HInstruction* instruction,
+ Location root,
+ GpuRegister obj,
+ uint32_t offset);
void GenerateTestAndBranch(HInstruction* instruction,
size_t condition_input_index,
Mips64Label* true_target,
@@ -375,16 +385,31 @@
Mips64Label pc_rel_label;
};
+ PcRelativePatchInfo* NewPcRelativeStringPatch(const DexFile& dex_file, uint32_t string_index);
+ PcRelativePatchInfo* NewPcRelativeTypePatch(const DexFile& dex_file, dex::TypeIndex type_index);
PcRelativePatchInfo* NewPcRelativeDexCacheArrayPatch(const DexFile& dex_file,
uint32_t element_offset);
PcRelativePatchInfo* NewPcRelativeCallPatch(const DexFile& dex_file,
uint32_t method_index);
+ Literal* DeduplicateBootImageStringLiteral(const DexFile& dex_file,
+ dex::StringIndex string_index);
+ Literal* DeduplicateBootImageTypeLiteral(const DexFile& dex_file, dex::TypeIndex type_index);
+ Literal* DeduplicateBootImageAddressLiteral(uint64_t address);
void EmitPcRelativeAddressPlaceholderHigh(PcRelativePatchInfo* info, GpuRegister out);
private:
+ using Uint32ToLiteralMap = ArenaSafeMap<uint32_t, Literal*>;
using Uint64ToLiteralMap = ArenaSafeMap<uint64_t, Literal*>;
using MethodToLiteralMap = ArenaSafeMap<MethodReference, Literal*, MethodReferenceComparator>;
+ using BootStringToLiteralMap = ArenaSafeMap<StringReference,
+ Literal*,
+ StringReferenceValueComparator>;
+ using BootTypeToLiteralMap = ArenaSafeMap<TypeReference,
+ Literal*,
+ TypeReferenceValueComparator>;
+
+ Literal* DeduplicateUint32Literal(uint32_t value, Uint32ToLiteralMap* map);
Literal* DeduplicateUint64Literal(uint64_t value);
Literal* DeduplicateMethodLiteral(MethodReference target_method, MethodToLiteralMap* map);
Literal* DeduplicateMethodAddressLiteral(MethodReference target_method);
@@ -407,6 +432,8 @@
Mips64Assembler assembler_;
const Mips64InstructionSetFeatures& isa_features_;
+ // Deduplication map for 32-bit literals, used for non-patchable boot image addresses.
+ Uint32ToLiteralMap uint32_literals_;
// Deduplication map for 64-bit literals, used for non-patchable method address or method code
// address.
Uint64ToLiteralMap uint64_literals_;
@@ -416,6 +443,16 @@
// PC-relative patch info.
ArenaDeque<PcRelativePatchInfo> pc_relative_dex_cache_patches_;
ArenaDeque<PcRelativePatchInfo> relative_call_patches_;
+ // Deduplication map for boot string literals for kBootImageLinkTimeAddress.
+ BootStringToLiteralMap boot_image_string_patches_;
+ // PC-relative String patch info; type depends on configuration (app .bss or boot image PIC).
+ ArenaDeque<PcRelativePatchInfo> pc_relative_string_patches_;
+ // Deduplication map for boot type literals for kBootImageLinkTimeAddress.
+ BootTypeToLiteralMap boot_image_type_patches_;
+ // PC-relative type patch info.
+ ArenaDeque<PcRelativePatchInfo> pc_relative_type_patches_;
+ // Deduplication map for patchable boot image addresses.
+ Uint32ToLiteralMap boot_image_address_patches_;
DISALLOW_COPY_AND_ASSIGN(CodeGeneratorMIPS64);
};
diff --git a/compiler/optimizing/common_arm.h b/compiler/optimizing/common_arm.h
index eabdbad..21c3ae6 100644
--- a/compiler/optimizing/common_arm.h
+++ b/compiler/optimizing/common_arm.h
@@ -122,10 +122,16 @@
if (type == Primitive::kPrimFloat) {
return InputSRegisterAt(instr, input_index);
} else {
+ DCHECK_EQ(type, Primitive::kPrimDouble);
return InputDRegisterAt(instr, input_index);
}
}
+inline vixl::aarch32::VRegister InputVRegister(HInstruction* instr) {
+ DCHECK_EQ(instr->InputCount(), 1u);
+ return InputVRegisterAt(instr, 0);
+}
+
inline vixl::aarch32::Register OutputRegister(HInstruction* instr) {
return RegisterFrom(instr->GetLocations()->Out(), instr->GetType());
}
@@ -140,8 +146,7 @@
return InputRegisterAt(instr, 0);
}
-inline int32_t Int32ConstantFrom(Location location) {
- HConstant* instr = location.GetConstant();
+inline int32_t Int32ConstantFrom(HInstruction* instr) {
if (instr->IsIntConstant()) {
return instr->AsIntConstant()->GetValue();
} else if (instr->IsNullConstant()) {
@@ -155,6 +160,10 @@
}
}
+inline int32_t Int32ConstantFrom(Location location) {
+ return Int32ConstantFrom(location.GetConstant());
+}
+
inline int64_t Int64ConstantFrom(Location location) {
HConstant* instr = location.GetConstant();
if (instr->IsIntConstant()) {
@@ -167,6 +176,11 @@
}
}
+inline uint64_t Uint64ConstantFrom(HInstruction* instr) {
+ DCHECK(instr->IsConstant()) << instr->DebugName();
+ return instr->AsConstant()->GetValueAsUint64();
+}
+
inline vixl::aarch32::Operand OperandFrom(Location location, Primitive::Type type) {
if (location.IsRegister()) {
return vixl::aarch32::Operand(RegisterFrom(location, type));
diff --git a/compiler/optimizing/graph_checker.cc b/compiler/optimizing/graph_checker.cc
index 188ee3a..34b52a8 100644
--- a/compiler/optimizing/graph_checker.cc
+++ b/compiler/optimizing/graph_checker.cc
@@ -20,12 +20,15 @@
#include <string>
#include <sstream>
+#include "android-base/stringprintf.h"
+
#include "base/arena_containers.h"
#include "base/bit_vector-inl.h"
-#include "base/stringprintf.h"
namespace art {
+using android::base::StringPrintf;
+
static bool IsAllowedToJumpToExitBlock(HInstruction* instruction) {
return instruction->IsThrow() || instruction->IsReturn() || instruction->IsReturnVoid();
}
diff --git a/compiler/optimizing/graph_test.cc b/compiler/optimizing/graph_test.cc
index d530564..28ee3a5 100644
--- a/compiler/optimizing/graph_test.cc
+++ b/compiler/optimizing/graph_test.cc
@@ -15,7 +15,6 @@
*/
#include "base/arena_allocator.h"
-#include "base/stringprintf.h"
#include "builder.h"
#include "nodes.h"
#include "optimizing_unit_test.h"
diff --git a/compiler/optimizing/induction_var_analysis.cc b/compiler/optimizing/induction_var_analysis.cc
index c240c67..5456b1e 100644
--- a/compiler/optimizing/induction_var_analysis.cc
+++ b/compiler/optimizing/induction_var_analysis.cc
@@ -73,10 +73,18 @@
}
/**
- * Returns narrowest data type.
+ * Returns result of implicit widening type conversion done in HIR.
*/
-static Primitive::Type Narrowest(Primitive::Type type1, Primitive::Type type2) {
- return Primitive::ComponentSize(type1) <= Primitive::ComponentSize(type2) ? type1 : type2;
+static Primitive::Type ImplicitConversion(Primitive::Type type) {
+ switch (type) {
+ case Primitive::kPrimShort:
+ case Primitive::kPrimChar:
+ case Primitive::kPrimByte:
+ case Primitive::kPrimBoolean:
+ return Primitive::kPrimInt;
+ default:
+ return type;
+ }
}
//
@@ -211,7 +219,7 @@
void HInductionVarAnalysis::ClassifyTrivial(HLoopInformation* loop, HInstruction* instruction) {
InductionInfo* info = nullptr;
if (instruction->IsPhi()) {
- info = TransferPhi(loop, instruction, /* input_index */ 0);
+ info = TransferPhi(loop, instruction, /*input_index*/ 0, /*adjust_input_size*/ 0);
} else if (instruction->IsAdd()) {
info = TransferAddSub(LookupInfo(loop, instruction->InputAt(0)),
LookupInfo(loop, instruction->InputAt(1)), kAdd);
@@ -224,15 +232,17 @@
info = TransferMul(LookupInfo(loop, instruction->InputAt(0)),
LookupInfo(loop, instruction->InputAt(1)));
} else if (instruction->IsShl()) {
- HInstruction* mulc = GetMultConstantForShift(loop, instruction);
+ HInstruction* mulc = GetShiftConstant(loop, instruction, /*initial*/ nullptr);
if (mulc != nullptr) {
info = TransferMul(LookupInfo(loop, instruction->InputAt(0)),
LookupInfo(loop, mulc));
}
+ } else if (instruction->IsSelect()) {
+ info = TransferPhi(loop, instruction, /*input_index*/ 0, /*adjust_input_size*/ 1);
} else if (instruction->IsTypeConversion()) {
- info = TransferCnv(LookupInfo(loop, instruction->InputAt(0)),
- instruction->AsTypeConversion()->GetInputType(),
- instruction->AsTypeConversion()->GetResultType());
+ info = TransferConversion(LookupInfo(loop, instruction->InputAt(0)),
+ instruction->AsTypeConversion()->GetInputType(),
+ instruction->AsTypeConversion()->GetResultType());
} else if (instruction->IsBoundsCheck()) {
info = LookupInfo(loop, instruction->InputAt(0)); // Pass-through.
}
@@ -265,12 +275,16 @@
return;
}
- // Store interesting cycle.
- AssignCycle(phi->AsPhi());
+ // Store interesting cycle in each loop phi.
+ for (size_t i = 0; i < size; i++) {
+ if (scc_[i]->IsLoopHeaderPhi()) {
+ AssignCycle(scc_[i]->AsPhi());
+ }
+ }
// Singleton is wrap-around induction if all internal links have the same meaning.
if (size == 1) {
- InductionInfo* update = TransferPhi(loop, phi, /* input_index */ 1);
+ InductionInfo* update = TransferPhi(loop, phi, /*input_index*/ 1, /*adjust_input_size*/ 0);
if (update != nullptr) {
AssignInfo(loop, phi, CreateInduction(kWrapAround,
kNop,
@@ -305,10 +319,15 @@
update = SolveOp(
loop, phi, instruction, instruction->InputAt(0), instruction->InputAt(1), kRem);
} else if (instruction->IsShl()) {
- HInstruction* mulc = GetMultConstantForShift(loop, instruction);
+ HInstruction* mulc = GetShiftConstant(loop, instruction, /*initial*/ nullptr);
if (mulc != nullptr) {
update = SolveOp(loop, phi, instruction, instruction->InputAt(0), mulc, kMul);
}
+ } else if (instruction->IsShr() || instruction->IsUShr()) {
+ HInstruction* divc = GetShiftConstant(loop, instruction, initial);
+ if (divc != nullptr) {
+ update = SolveOp(loop, phi, instruction, instruction->InputAt(0), divc, kDiv);
+ }
} else if (instruction->IsXor()) {
update = SolveOp(
loop, phi, instruction, instruction->InputAt(0), instruction->InputAt(1), kXor);
@@ -316,8 +335,10 @@
update = SolveTest(loop, phi, instruction, 0);
} else if (instruction->IsNotEqual()) {
update = SolveTest(loop, phi, instruction, 1);
+ } else if (instruction->IsSelect()) {
+ update = SolvePhi(instruction, /*input_index*/ 0, /*adjust_input_size*/ 1); // acts like Phi
} else if (instruction->IsTypeConversion()) {
- update = SolveCnv(instruction->AsTypeConversion());
+ update = SolveConversion(loop, phi, instruction->AsTypeConversion());
}
if (update == nullptr) {
return;
@@ -326,7 +347,7 @@
}
// Success if all internal links received the same temporary meaning.
- InductionInfo* induction = SolvePhi(phi, /* input_index */ 1);
+ InductionInfo* induction = SolvePhi(phi, /*input_index*/ 1, /*adjust_input_size*/ 0);
if (induction != nullptr) {
switch (induction->induction_class) {
case kInvariant:
@@ -385,12 +406,13 @@
HInductionVarAnalysis::InductionInfo* HInductionVarAnalysis::TransferPhi(HLoopInformation* loop,
HInstruction* phi,
- size_t input_index) {
+ size_t input_index,
+ size_t adjust_input_size) {
// Match all phi inputs from input_index onwards exactly.
HInputsRef inputs = phi->GetInputs();
DCHECK_LT(input_index, inputs.size());
InductionInfo* a = LookupInfo(loop, inputs[input_index]);
- for (size_t i = input_index + 1; i < inputs.size(); i++) {
+ for (size_t i = input_index + 1, n = inputs.size() - adjust_input_size; i < n; i++) {
InductionInfo* b = LookupInfo(loop, inputs[i]);
if (!InductionEqual(a, b)) {
return nullptr;
@@ -406,8 +428,9 @@
// wrap-around, or periodic can be combined with an invariant to yield a similar result.
// Two linear or two polynomial inputs can be combined too. Other combinations fail.
if (a != nullptr && b != nullptr) {
- type_ = Narrowest(type_, Narrowest(a->type, b->type));
- if (a->induction_class == kInvariant && b->induction_class == kInvariant) {
+ if (IsNarrowingLinear(a) || IsNarrowingLinear(b)) {
+ return nullptr; // no transfer
+ } else if (a->induction_class == kInvariant && b->induction_class == kInvariant) {
return CreateInvariantOp(op, a, b);
} else if ((a->induction_class == kLinear && b->induction_class == kLinear) ||
(a->induction_class == kPolynomial && b->induction_class == kPolynomial)) {
@@ -442,8 +465,9 @@
// Transfer over a unary negation: an invariant, linear, polynomial, geometric (mul),
// wrap-around, or periodic input yields a similar but negated induction as result.
if (a != nullptr) {
- type_ = Narrowest(type_, a->type);
- if (a->induction_class == kInvariant) {
+ if (IsNarrowingLinear(a)) {
+ return nullptr; // no transfer
+ } else if (a->induction_class == kInvariant) {
return CreateInvariantOp(kNeg, nullptr, a);
} else if (a->induction_class != kGeometric || a->operation == kMul) {
return CreateInduction(a->induction_class,
@@ -463,8 +487,9 @@
// wrap-around, or periodic can be multiplied with an invariant to yield a similar
// but multiplied result. Two non-invariant inputs cannot be multiplied, however.
if (a != nullptr && b != nullptr) {
- type_ = Narrowest(type_, Narrowest(a->type, b->type));
- if (a->induction_class == kInvariant && b->induction_class == kInvariant) {
+ if (IsNarrowingLinear(a) || IsNarrowingLinear(b)) {
+ return nullptr; // no transfer
+ } else if (a->induction_class == kInvariant && b->induction_class == kInvariant) {
return CreateInvariantOp(kMul, a, b);
} else if (a->induction_class == kInvariant && (b->induction_class != kGeometric ||
b->operation == kMul)) {
@@ -487,30 +512,31 @@
return nullptr;
}
-HInductionVarAnalysis::InductionInfo* HInductionVarAnalysis::TransferCnv(InductionInfo* a,
- Primitive::Type from,
- Primitive::Type to) {
+HInductionVarAnalysis::InductionInfo* HInductionVarAnalysis::TransferConversion(
+ InductionInfo* a,
+ Primitive::Type from,
+ Primitive::Type to) {
if (a != nullptr) {
- // Allow narrowing conversion on linear induction in certain cases.
- if (IsNarrowingIntegralConversion(from, to)) {
- if (a->induction_class == kLinear) {
- if (a->type == to || (a->type == from && IsNarrowingIntegralConversion(from, to))) {
- return CreateInduction(kLinear, kNop, a->op_a, a->op_b, /*fetch*/ nullptr, to);
- }
- }
+ // Allow narrowing conversion on linear induction in certain cases:
+ // induction is already at narrow type, or can be made narrower.
+ if (IsNarrowingIntegralConversion(from, to) &&
+ a->induction_class == kLinear &&
+ (a->type == to || IsNarrowingIntegralConversion(a->type, to))) {
+ return CreateInduction(kLinear, kNop, a->op_a, a->op_b, /*fetch*/ nullptr, to);
}
}
return nullptr;
}
HInductionVarAnalysis::InductionInfo* HInductionVarAnalysis::SolvePhi(HInstruction* phi,
- size_t input_index) {
+ size_t input_index,
+ size_t adjust_input_size) {
// Match all phi inputs from input_index onwards exactly.
HInputsRef inputs = phi->GetInputs();
DCHECK_LT(input_index, inputs.size());
auto ita = cycle_.find(inputs[input_index]);
if (ita != cycle_.end()) {
- for (size_t i = input_index + 1; i < inputs.size(); i++) {
+ for (size_t i = input_index + 1, n = inputs.size() - adjust_input_size; i < n; i++) {
auto itb = cycle_.find(inputs[i]);
if (itb == cycle_.end() ||
!HInductionVarAnalysis::InductionEqual(ita->second, itb->second)) {
@@ -527,7 +553,7 @@
HInstruction* entry_phi,
HInstruction* phi) {
// Match all phi inputs.
- InductionInfo* match = SolvePhi(phi, /* input_index */ 0);
+ InductionInfo* match = SolvePhi(phi, /*input_index*/ 0, /*adjust_input_size*/ 0);
if (match != nullptr) {
return match;
}
@@ -542,7 +568,7 @@
InductionInfo* initial = LookupInfo(loop, entry_phi->InputAt(0));
return CreateInduction(kPeriodic, kNop, a, initial, /*fetch*/ nullptr, type_);
}
- InductionInfo* b = SolvePhi(phi, /* input_index */ 1);
+ InductionInfo* b = SolvePhi(phi, /*input_index*/ 1, /*adjust_input_size*/ 0);
if (b != nullptr && b->induction_class == kPeriodic) {
return CreateInduction(kPeriodic, kNop, a, b, /*fetch*/ nullptr, type_);
}
@@ -574,14 +600,14 @@
return CreateInvariantOp(op, a, b);
}
}
- } else if (op == kAdd && b->induction_class == kLinear) {
+ } else if (b->induction_class == kLinear) {
// Solve within a tight cycle that adds a term that is already classified as a linear
// induction for a polynomial induction k = k + i (represented as sum over linear terms).
if (x == entry_phi && entry_phi->InputCount() == 2 && instruction == entry_phi->InputAt(1)) {
InductionInfo* initial = LookupInfo(loop, entry_phi->InputAt(0));
return CreateInduction(kPolynomial,
kNop,
- b,
+ op == kAdd ? b : TransferNeg(b),
initial,
/*fetch*/ nullptr,
type_);
@@ -689,16 +715,29 @@
return nullptr;
}
-HInductionVarAnalysis::InductionInfo* HInductionVarAnalysis::SolveCnv(HTypeConversion* conversion) {
+HInductionVarAnalysis::InductionInfo* HInductionVarAnalysis::SolveConversion(
+ HLoopInformation* loop,
+ HInstruction* entry_phi,
+ HTypeConversion* conversion) {
Primitive::Type from = conversion->GetInputType();
Primitive::Type to = conversion->GetResultType();
- // A narrowing conversion is allowed within the cycle of a linear induction, provided that the
- // narrowest encountered type is recorded with the induction to account for the precision loss.
- if (IsNarrowingIntegralConversion(from, to)) {
- auto it = cycle_.find(conversion->GetInput());
- if (it != cycle_.end() && it->second->induction_class == kInvariant) {
- type_ = Narrowest(type_, to);
- return it->second;
+ // A narrowing conversion is allowed as *last* operation of the cycle of a linear induction
+ // with an initial value that fits the type, provided that the narrowest encountered type is
+ // recorded with the induction to account for the precision loss. The narrower induction does
+ // *not* transfer to any wider operations, however, since these may yield out-of-type values
+ if (entry_phi->InputCount() == 2 && conversion == entry_phi->InputAt(1)) {
+ int64_t min = Primitive::MinValueOfIntegralType(to);
+ int64_t max = Primitive::MaxValueOfIntegralType(to);
+ int64_t value = 0;
+ InductionInfo* initial = LookupInfo(loop, entry_phi->InputAt(0));
+ if (IsNarrowingIntegralConversion(from, to) &&
+ IsAtLeast(initial, &value) && value >= min &&
+ IsAtMost(initial, &value) && value <= max) {
+ auto it = cycle_.find(conversion->GetInput());
+ if (it != cycle_.end() && it->second->induction_class == kInvariant) {
+ type_ = to;
+ return it->second;
+ }
}
}
return nullptr;
@@ -718,7 +757,7 @@
HCondition* condition = if_expr->AsCondition();
InductionInfo* a = LookupInfo(loop, condition->InputAt(0));
InductionInfo* b = LookupInfo(loop, condition->InputAt(1));
- Primitive::Type type = condition->InputAt(0)->GetType();
+ Primitive::Type type = ImplicitConversion(condition->InputAt(0)->GetType());
// Determine if the loop control uses a known sequence on an if-exit (X outside) or on
// an if-iterate (X inside), expressed as if-iterate when passed into VisitCondition().
if (a == nullptr || b == nullptr) {
@@ -890,8 +929,8 @@
int64_t stride_value,
Primitive::Type type,
IfCondition cmp) {
- const int64_t min = Primitive::MinValueOfIntegralType(type);
- const int64_t max = Primitive::MaxValueOfIntegralType(type);
+ int64_t min = Primitive::MinValueOfIntegralType(type);
+ int64_t max = Primitive::MaxValueOfIntegralType(type);
// Some rules under which it is certain at compile-time that the loop is finite.
int64_t value;
switch (cmp) {
@@ -927,8 +966,6 @@
min++;
}
// Do both bounds fit the range?
- // Note: The `value` is initialized to please valgrind - the compiler can reorder
- // the return value check with the `value` check, b/27651442 .
int64_t value = 0;
return IsAtLeast(lower_expr, &value) && value >= min &&
IsAtMost(lower_expr, &value) && value <= max &&
@@ -1035,16 +1072,27 @@
return CreateSimplifiedInvariant(kSub, b->op_b, b->op_a);
}
}
- return new (graph_->GetArena()) InductionInfo(kInvariant, op, a, b, nullptr, b->type);
+ return new (graph_->GetArena()) InductionInfo(
+ kInvariant, op, a, b, nullptr, ImplicitConversion(b->type));
}
-HInstruction* HInductionVarAnalysis::GetMultConstantForShift(HLoopInformation* loop,
- HInstruction* instruction) {
- // Obtain the constant needed to treat shift as equivalent multiplication. This yields an
- // existing instruction if the constant is already there. Otherwise, this has a side effect
- // on the HIR. The restriction on the shift factor avoids generating a negative constant
- // (viz. 1 << 31 and 1L << 63 set the sign bit). The code assumes that generalization for
- // shift factors outside [0,32) and [0,64) ranges is done by earlier simplification.
+HInstruction* HInductionVarAnalysis::GetShiftConstant(HLoopInformation* loop,
+ HInstruction* instruction,
+ InductionInfo* initial) {
+ DCHECK(instruction->IsShl() || instruction->IsShr() || instruction->IsUShr());
+ // Shift-rights are only the same as division for non-negative initial inputs.
+ // Otherwise we would round incorrectly.
+ if (initial != nullptr) {
+ int64_t value = -1;
+ if (!IsAtLeast(initial, &value) || value < 0) {
+ return nullptr;
+ }
+ }
+ // Obtain the constant needed to treat shift as equivalent multiplication or division.
+ // This yields an existing instruction if the constant is already there. Otherwise, this
+ // has a side effect on the HIR. The restriction on the shift factor avoids generating a
+ // negative constant (viz. 1 << 31 and 1L << 63 set the sign bit). The code assumes that
+ // generalization for shift factors outside [0,32) and [0,64) ranges is done earlier.
InductionInfo* b = LookupInfo(loop, instruction->InputAt(1));
int64_t value = -1;
if (IsExact(b, &value)) {
@@ -1087,6 +1135,16 @@
return InductionVarRange(this).IsConstant(info, InductionVarRange::kAtLeast, value);
}
+bool HInductionVarAnalysis::IsNarrowingLinear(InductionInfo* info) {
+ return info != nullptr &&
+ info->induction_class == kLinear &&
+ (info->type == Primitive::kPrimByte ||
+ info->type == Primitive::kPrimShort ||
+ info->type == Primitive::kPrimChar ||
+ (info->type == Primitive::kPrimInt && (info->op_a->type == Primitive::kPrimLong ||
+ info->op_b->type == Primitive::kPrimLong)));
+}
+
bool HInductionVarAnalysis::InductionEqual(InductionInfo* info1,
InductionInfo* info2) {
// Test structural equality only, without accounting for simplifications.
diff --git a/compiler/optimizing/induction_var_analysis.h b/compiler/optimizing/induction_var_analysis.h
index 4720f2d..39b39cd 100644
--- a/compiler/optimizing/induction_var_analysis.h
+++ b/compiler/optimizing/induction_var_analysis.h
@@ -115,7 +115,7 @@
InductionInfo* op_a;
InductionInfo* op_b;
HInstruction* fetch;
- Primitive::Type type; // precision of induction
+ Primitive::Type type; // precision of operation
};
bool IsVisitedNode(HInstruction* instruction) const {
@@ -160,14 +160,17 @@
InductionInfo* RotatePeriodicInduction(InductionInfo* induction, InductionInfo* last);
// Transfer operations.
- InductionInfo* TransferPhi(HLoopInformation* loop, HInstruction* phi, size_t input_index);
+ InductionInfo* TransferPhi(HLoopInformation* loop,
+ HInstruction* phi,
+ size_t input_index,
+ size_t adjust_input_size);
InductionInfo* TransferAddSub(InductionInfo* a, InductionInfo* b, InductionOp op);
InductionInfo* TransferNeg(InductionInfo* a);
InductionInfo* TransferMul(InductionInfo* a, InductionInfo* b);
- InductionInfo* TransferCnv(InductionInfo* a, Primitive::Type from, Primitive::Type to);
+ InductionInfo* TransferConversion(InductionInfo* a, Primitive::Type from, Primitive::Type to);
// Solvers.
- InductionInfo* SolvePhi(HInstruction* phi, size_t input_index);
+ InductionInfo* SolvePhi(HInstruction* phi, size_t input_index, size_t adjust_input_size);
InductionInfo* SolvePhiAllInputs(HLoopInformation* loop,
HInstruction* entry_phi,
HInstruction* phi);
@@ -188,7 +191,9 @@
HInstruction* entry_phi,
HInstruction* instruction,
int64_t oppositive_value);
- InductionInfo* SolveCnv(HTypeConversion* conversion);
+ InductionInfo* SolveConversion(HLoopInformation* loop,
+ HInstruction* entry_phi,
+ HTypeConversion* conversion);
// Trip count information.
void VisitControl(HLoopInformation* loop);
@@ -220,7 +225,9 @@
InductionInfo* LookupInfo(HLoopInformation* loop, HInstruction* instruction);
InductionInfo* CreateConstant(int64_t value, Primitive::Type type);
InductionInfo* CreateSimplifiedInvariant(InductionOp op, InductionInfo* a, InductionInfo* b);
- HInstruction* GetMultConstantForShift(HLoopInformation* loop, HInstruction* instruction);
+ HInstruction* GetShiftConstant(HLoopInformation* loop,
+ HInstruction* instruction,
+ InductionInfo* initial);
void AssignCycle(HPhi* phi);
ArenaSet<HInstruction*>* LookupCycle(HPhi* phi);
@@ -230,6 +237,7 @@
bool IsAtLeast(InductionInfo* info, /*out*/ int64_t* value);
// Helpers.
+ static bool IsNarrowingLinear(InductionInfo* info);
static bool InductionEqual(InductionInfo* info1, InductionInfo* info2);
static std::string FetchToString(HInstruction* fetch);
static std::string InductionToString(InductionInfo* info);
diff --git a/compiler/optimizing/induction_var_analysis_test.cc b/compiler/optimizing/induction_var_analysis_test.cc
index 2d182f6..82ee93d 100644
--- a/compiler/optimizing/induction_var_analysis_test.cc
+++ b/compiler/optimizing/induction_var_analysis_test.cc
@@ -87,6 +87,7 @@
constant2_ = graph_->GetIntConstant(2);
constant7_ = graph_->GetIntConstant(7);
constant100_ = graph_->GetIntConstant(100);
+ constantm1_ = graph_->GetIntConstant(-1);
float_constant0_ = graph_->GetFloatConstant(0.0f);
return_->AddInstruction(new (&allocator_) HReturnVoid());
exit_->AddInstruction(new (&allocator_) HExit());
@@ -173,6 +174,12 @@
iva_->LookupInfo(loop_body_[0]->GetLoopInformation(), instruction2));
}
+ // Returns true for narrowing linear induction.
+ bool IsNarrowingLinear(HInstruction* instruction) {
+ return HInductionVarAnalysis::IsNarrowingLinear(
+ iva_->LookupInfo(loop_body_[0]->GetLoopInformation(), instruction));
+ }
+
// Performs InductionVarAnalysis (after proper set up).
void PerformInductionVarAnalysis() {
graph_->BuildDominatorTree();
@@ -196,6 +203,7 @@
HInstruction* constant2_;
HInstruction* constant7_;
HInstruction* constant100_;
+ HInstruction* constantm1_;
HInstruction* float_constant0_;
// Loop specifics.
@@ -612,6 +620,45 @@
EXPECT_STREQ("", GetInductionInfo(div, 0).c_str());
}
+TEST_F(InductionVarAnalysisTest, FindGeometricShrInduction) {
+ // Setup:
+ // k = 100;
+ // for (int i = 0; i < 100; i++) {
+ // k = k >> 1; // geometric (/ 2)
+ // }
+ BuildLoopNest(1);
+ HPhi* k_header = InsertLoopPhi(0, 0);
+ k_header->AddInput(constant100_);
+
+ HInstruction* shr = InsertInstruction(
+ new (&allocator_) HShr(Primitive::kPrimInt, k_header, constant1_), 0);
+ k_header->AddInput(shr);
+ PerformInductionVarAnalysis();
+
+ // Note, only the phi in the cycle is classified.
+ EXPECT_STREQ("geo((100) * 2 ^ -i + (0)):PrimInt", GetInductionInfo(k_header, 0).c_str());
+ EXPECT_STREQ("", GetInductionInfo(shr, 0).c_str());
+}
+
+TEST_F(InductionVarAnalysisTest, FindNotGeometricShrInduction) {
+ // Setup:
+ // k = -1;
+ // for (int i = 0; i < 100; i++) {
+ // k = k >> 1; // initial value is negative
+ // }
+ BuildLoopNest(1);
+ HPhi* k_header = InsertLoopPhi(0, 0);
+ k_header->AddInput(constantm1_);
+
+ HInstruction* shr = InsertInstruction(
+ new (&allocator_) HShr(Primitive::kPrimInt, k_header, constant1_), 0);
+ k_header->AddInput(shr);
+ PerformInductionVarAnalysis();
+
+ EXPECT_STREQ("", GetInductionInfo(k_header, 0).c_str());
+ EXPECT_STREQ("", GetInductionInfo(shr, 0).c_str());
+}
+
TEST_F(InductionVarAnalysisTest, FindRemWrapAroundInductionAndDerived) {
// Setup:
// k = 100;
@@ -1025,16 +1072,20 @@
// }
BuildLoopNest(1);
HInstruction* conv = InsertInstruction(
- new (&allocator_) HTypeConversion(Primitive::kPrimByte, basic_[0], -1), 0);
+ new (&allocator_) HTypeConversion(Primitive::kPrimByte, basic_[0], kNoDexPc), 0);
HInstruction* store1 = InsertArrayStore(conv, 0);
HInstruction* store2 = InsertArrayStore(basic_[0], 0);
PerformInductionVarAnalysis();
- // Regular int induction (i) is "transferred" over conversion into byte induction (k).
+ // Regular int induction (i) is transferred over conversion into byte induction (k).
EXPECT_STREQ("((1) * i + (0)):PrimByte", GetInductionInfo(store1->InputAt(1), 0).c_str());
EXPECT_STREQ("((1) * i + (0)):PrimInt", GetInductionInfo(store2->InputAt(1), 0).c_str());
EXPECT_STREQ("((1) * i + (1)):PrimInt", GetInductionInfo(increment_[0], 0).c_str());
+ // Narrowing detected.
+ EXPECT_TRUE(IsNarrowingLinear(store1->InputAt(1)));
+ EXPECT_FALSE(IsNarrowingLinear(store2->InputAt(1)));
+
// Type matters!
EXPECT_FALSE(HaveSameInduction(store1->InputAt(1), store2->InputAt(1)));
@@ -1052,7 +1103,7 @@
// }
BuildLoopNest(1);
HInstruction* conv = InsertInstruction(
- new (&allocator_) HTypeConversion(Primitive::kPrimByte, basic_[0], -1), 0);
+ new (&allocator_) HTypeConversion(Primitive::kPrimByte, basic_[0], kNoDexPc), 0);
HInstruction* store1 = InsertArrayStore(conv, 0);
HInstruction* add = InsertInstruction(
new (&allocator_) HAdd(Primitive::kPrimInt, conv, constant1_), 0);
@@ -1060,11 +1111,86 @@
PerformInductionVarAnalysis();
- // Byte induction (k) is "transferred" over conversion into addition (k + 1).
- // This means only values within byte range can be trusted (even though
- // addition can jump out of the range of course).
+ // Byte induction (k) is detected, but it does not transfer over the addition,
+ // since this may yield out-of-type values.
EXPECT_STREQ("((1) * i + (0)):PrimByte", GetInductionInfo(store1->InputAt(1), 0).c_str());
- EXPECT_STREQ("((1) * i + (1)):PrimByte", GetInductionInfo(store2->InputAt(1), 0).c_str());
+ EXPECT_STREQ("", GetInductionInfo(store2->InputAt(1), 0).c_str());
+
+ // Narrowing detected.
+ EXPECT_TRUE(IsNarrowingLinear(store1->InputAt(1)));
+ EXPECT_FALSE(IsNarrowingLinear(store2->InputAt(1))); // works for null
+}
+
+TEST_F(InductionVarAnalysisTest, ByteInduction) {
+ // Setup:
+ // k = -128;
+ // for (int i = 0; i < 100; i++) {
+ // k = k + 1;
+ // k = (byte) k;
+ // }
+ BuildLoopNest(1);
+ HPhi* k_header = InsertLoopPhi(0, 0);
+ k_header->AddInput(graph_->GetIntConstant(-128));
+
+ HInstruction* add = InsertInstruction(
+ new (&allocator_) HAdd(Primitive::kPrimInt, k_header, constant1_), 0);
+ HInstruction* conv = InsertInstruction(
+ new (&allocator_) HTypeConversion(Primitive::kPrimByte, add, kNoDexPc), 0);
+ k_header->AddInput(conv);
+ PerformInductionVarAnalysis();
+
+ // Byte induction (k) is detected, but it does not transfer over the addition,
+ // since this may yield out-of-type values.
+ EXPECT_STREQ("((1) * i + (-128)):PrimByte", GetInductionInfo(k_header, 0).c_str());
+ EXPECT_STREQ("", GetInductionInfo(add, 0).c_str());
+
+ // Narrowing detected.
+ EXPECT_TRUE(IsNarrowingLinear(k_header));
+ EXPECT_FALSE(IsNarrowingLinear(add)); // works for null
+}
+
+TEST_F(InductionVarAnalysisTest, NoByteInduction1) {
+ // Setup:
+ // k = -129; / does not fit!
+ // for (int i = 0; i < 100; i++) {
+ // k = k + 1;
+ // k = (byte) k;
+ // }
+ BuildLoopNest(1);
+ HPhi* k_header = InsertLoopPhi(0, 0);
+ k_header->AddInput(graph_->GetIntConstant(-129));
+
+ HInstruction* add = InsertInstruction(
+ new (&allocator_) HAdd(Primitive::kPrimInt, k_header, constant1_), 0);
+ HInstruction* conv = InsertInstruction(
+ new (&allocator_) HTypeConversion(Primitive::kPrimByte, add, kNoDexPc), 0);
+ k_header->AddInput(conv);
+ PerformInductionVarAnalysis();
+
+ EXPECT_STREQ("", GetInductionInfo(k_header, 0).c_str());
+ EXPECT_STREQ("", GetInductionInfo(add, 0).c_str());
+}
+
+TEST_F(InductionVarAnalysisTest, NoByteInduction2) {
+ // Setup:
+ // k = 0;
+ // for (int i = 0; i < 100; i++) {
+ // k = (byte) k; // conversion not done last!
+ // k = k + 1;
+ // }
+ BuildLoopNest(1);
+ HPhi* k_header = InsertLoopPhi(0, 0);
+ k_header->AddInput(constant0_);
+
+ HInstruction* conv = InsertInstruction(
+ new (&allocator_) HTypeConversion(Primitive::kPrimByte, k_header, kNoDexPc), 0);
+ HInstruction* add = InsertInstruction(
+ new (&allocator_) HAdd(Primitive::kPrimInt, conv, constant1_), 0);
+ k_header->AddInput(add);
+ PerformInductionVarAnalysis();
+
+ EXPECT_STREQ("", GetInductionInfo(k_header, 0).c_str());
+ EXPECT_STREQ("", GetInductionInfo(add, 0).c_str());
}
TEST_F(InductionVarAnalysisTest, ByteLoopControl1) {
@@ -1075,12 +1201,20 @@
basic_[0]->ReplaceInput(graph_->GetIntConstant(-128), 0);
HInstruction* ifs = loop_header_[0]->GetLastInstruction()->GetPrevious();
ifs->ReplaceInput(graph_->GetIntConstant(127), 1);
- HInstruction* conv = new(&allocator_) HTypeConversion(Primitive::kPrimByte, increment_[0], -1);
+ HInstruction* conv =
+ new (&allocator_) HTypeConversion(Primitive::kPrimByte, increment_[0], kNoDexPc);
loop_body_[0]->InsertInstructionBefore(conv, increment_[0]->GetNext());
basic_[0]->ReplaceInput(conv, 1);
PerformInductionVarAnalysis();
- EXPECT_STREQ("((1) * i + ((-128) + (1))):PrimByte", GetInductionInfo(increment_[0], 0).c_str());
+ // Recorded at the phi, but not transferred to increment.
+ EXPECT_STREQ("((1) * i + (-128)):PrimByte", GetInductionInfo(basic_[0], 0).c_str());
+ EXPECT_STREQ("", GetInductionInfo(increment_[0], 0).c_str());
+
+ // Narrowing detected.
+ EXPECT_TRUE(IsNarrowingLinear(basic_[0]));
+ EXPECT_FALSE(IsNarrowingLinear(increment_[0])); // works for null
+
// Trip-count.
EXPECT_STREQ("(((127) - (-128)) (TC-loop) ((-128) < (127)))", GetTripCount(0).c_str());
}
@@ -1093,12 +1227,20 @@
basic_[0]->ReplaceInput(graph_->GetIntConstant(-128), 0);
HInstruction* ifs = loop_header_[0]->GetLastInstruction()->GetPrevious();
ifs->ReplaceInput(graph_->GetIntConstant(128), 1);
- HInstruction* conv = new(&allocator_) HTypeConversion(Primitive::kPrimByte, increment_[0], -1);
+ HInstruction* conv =
+ new (&allocator_) HTypeConversion(Primitive::kPrimByte, increment_[0], kNoDexPc);
loop_body_[0]->InsertInstructionBefore(conv, increment_[0]->GetNext());
basic_[0]->ReplaceInput(conv, 1);
PerformInductionVarAnalysis();
- EXPECT_STREQ("((1) * i + ((-128) + (1))):PrimByte", GetInductionInfo(increment_[0], 0).c_str());
+ // Recorded at the phi, but not transferred to increment.
+ EXPECT_STREQ("((1) * i + (-128)):PrimByte", GetInductionInfo(basic_[0], 0).c_str());
+ EXPECT_STREQ("", GetInductionInfo(increment_[0], 0).c_str());
+
+ // Narrowing detected.
+ EXPECT_TRUE(IsNarrowingLinear(basic_[0]));
+ EXPECT_FALSE(IsNarrowingLinear(increment_[0])); // works for null
+
// Trip-count undefined.
EXPECT_STREQ("", GetTripCount(0).c_str());
}
@@ -1111,13 +1253,20 @@
basic_[0]->ReplaceInput(graph_->GetIntConstant(-32768), 0);
HInstruction* ifs = loop_header_[0]->GetLastInstruction()->GetPrevious();
ifs->ReplaceInput(graph_->GetIntConstant(32767), 1);
- HInstruction* conv = new(&allocator_) HTypeConversion(Primitive::kPrimShort, increment_[0], -1);
+ HInstruction* conv =
+ new (&allocator_) HTypeConversion(Primitive::kPrimShort, increment_[0], kNoDexPc);
loop_body_[0]->InsertInstructionBefore(conv, increment_[0]->GetNext());
basic_[0]->ReplaceInput(conv, 1);
PerformInductionVarAnalysis();
- EXPECT_STREQ("((1) * i + ((-32768) + (1))):PrimShort",
- GetInductionInfo(increment_[0], 0).c_str());
+ // Recorded at the phi, but not transferred to increment.
+ EXPECT_STREQ("((1) * i + (-32768)):PrimShort", GetInductionInfo(basic_[0], 0).c_str());
+ EXPECT_STREQ("", GetInductionInfo(increment_[0], 0).c_str());
+
+ // Narrowing detected.
+ EXPECT_TRUE(IsNarrowingLinear(basic_[0]));
+ EXPECT_FALSE(IsNarrowingLinear(increment_[0])); // works for null
+
// Trip-count.
EXPECT_STREQ("(((32767) - (-32768)) (TC-loop) ((-32768) < (32767)))", GetTripCount(0).c_str());
}
@@ -1130,13 +1279,20 @@
basic_[0]->ReplaceInput(graph_->GetIntConstant(-32768), 0);
HInstruction* ifs = loop_header_[0]->GetLastInstruction()->GetPrevious();
ifs->ReplaceInput(graph_->GetIntConstant(32768), 1);
- HInstruction* conv = new(&allocator_) HTypeConversion(Primitive::kPrimShort, increment_[0], -1);
+ HInstruction* conv =
+ new (&allocator_) HTypeConversion(Primitive::kPrimShort, increment_[0], kNoDexPc);
loop_body_[0]->InsertInstructionBefore(conv, increment_[0]->GetNext());
basic_[0]->ReplaceInput(conv, 1);
PerformInductionVarAnalysis();
- EXPECT_STREQ("((1) * i + ((-32768) + (1))):PrimShort",
- GetInductionInfo(increment_[0], 0).c_str());
+ // Recorded at the phi, but not transferred to increment.
+ EXPECT_STREQ("((1) * i + (-32768)):PrimShort", GetInductionInfo(basic_[0], 0).c_str());
+ EXPECT_STREQ("", GetInductionInfo(increment_[0], 0).c_str());
+
+ // Narrowing detected.
+ EXPECT_TRUE(IsNarrowingLinear(basic_[0]));
+ EXPECT_FALSE(IsNarrowingLinear(increment_[0])); // works for null
+
// Trip-count undefined.
EXPECT_STREQ("", GetTripCount(0).c_str());
}
@@ -1148,12 +1304,20 @@
BuildLoopNest(1);
HInstruction* ifs = loop_header_[0]->GetLastInstruction()->GetPrevious();
ifs->ReplaceInput(graph_->GetIntConstant(65535), 1);
- HInstruction* conv = new(&allocator_) HTypeConversion(Primitive::kPrimChar, increment_[0], -1);
+ HInstruction* conv =
+ new (&allocator_) HTypeConversion(Primitive::kPrimChar, increment_[0], kNoDexPc);
loop_body_[0]->InsertInstructionBefore(conv, increment_[0]->GetNext());
basic_[0]->ReplaceInput(conv, 1);
PerformInductionVarAnalysis();
- EXPECT_STREQ("((1) * i + (1)):PrimChar", GetInductionInfo(increment_[0], 0).c_str());
+ // Recorded at the phi, but not transferred to increment.
+ EXPECT_STREQ("((1) * i + (0)):PrimChar", GetInductionInfo(basic_[0], 0).c_str());
+ EXPECT_STREQ("", GetInductionInfo(increment_[0], 0).c_str());
+
+ // Narrowing detected.
+ EXPECT_TRUE(IsNarrowingLinear(basic_[0]));
+ EXPECT_FALSE(IsNarrowingLinear(increment_[0])); // works for null
+
// Trip-count.
EXPECT_STREQ("((65535) (TC-loop) ((0) < (65535)))", GetTripCount(0).c_str());
}
@@ -1165,12 +1329,20 @@
BuildLoopNest(1);
HInstruction* ifs = loop_header_[0]->GetLastInstruction()->GetPrevious();
ifs->ReplaceInput(graph_->GetIntConstant(65536), 1);
- HInstruction* conv = new(&allocator_) HTypeConversion(Primitive::kPrimChar, increment_[0], -1);
+ HInstruction* conv =
+ new (&allocator_) HTypeConversion(Primitive::kPrimChar, increment_[0], kNoDexPc);
loop_body_[0]->InsertInstructionBefore(conv, increment_[0]->GetNext());
basic_[0]->ReplaceInput(conv, 1);
PerformInductionVarAnalysis();
- EXPECT_STREQ("((1) * i + (1)):PrimChar", GetInductionInfo(increment_[0], 0).c_str());
+ // Recorded at the phi, but not transferred to increment.
+ EXPECT_STREQ("((1) * i + (0)):PrimChar", GetInductionInfo(basic_[0], 0).c_str());
+ EXPECT_STREQ("", GetInductionInfo(increment_[0], 0).c_str());
+
+ // Narrowing detected.
+ EXPECT_TRUE(IsNarrowingLinear(basic_[0]));
+ EXPECT_FALSE(IsNarrowingLinear(increment_[0])); // works for null
+
// Trip-count undefined.
EXPECT_STREQ("", GetTripCount(0).c_str());
}
diff --git a/compiler/optimizing/induction_var_range.cc b/compiler/optimizing/induction_var_range.cc
index e665551..d5c4c2f 100644
--- a/compiler/optimizing/induction_var_range.cc
+++ b/compiler/optimizing/induction_var_range.cc
@@ -169,8 +169,8 @@
case Primitive::kPrimByte: {
// Constants within range only.
// TODO: maybe some room for improvement, like allowing widening conversions
- const int32_t min = Primitive::MinValueOfIntegralType(type);
- const int32_t max = Primitive::MaxValueOfIntegralType(type);
+ int32_t min = Primitive::MinValueOfIntegralType(type);
+ int32_t max = Primitive::MaxValueOfIntegralType(type);
return (IsConstantValue(v) && min <= v.b_constant && v.b_constant <= max)
? v
: InductionVarRange::Value();
@@ -551,7 +551,7 @@
int64_t b = 0;
if (IsConstant(info->op_a->op_a, kExact, &a) && CanLongValueFitIntoInt(a) && a >= 0 &&
IsConstant(info->op_a->op_b, kExact, &b) && CanLongValueFitIntoInt(b) && b >= 0) {
- // Evaluate bounds on sum_i=0^m-1(a * i + b) + c with a,b >= 0 for known
+ // Evaluate bounds on sum_i=0^m-1(a * i + b) + c with a,b >= 0 for
// maximum index value m as a * (m * (m-1)) / 2 + b * m + c.
Value c = GetVal(info->op_b, trip, in_body, is_min);
if (is_min) {
@@ -629,6 +629,7 @@
}
} else if (instruction->IsTypeConversion()) {
// Since analysis is 32-bit (or narrower), chase beyond widening along the path.
+ // For example, this discovers the length in: for (long i = 0; i < a.length; i++);
if (instruction->AsTypeConversion()->GetInputType() == Primitive::kPrimInt &&
instruction->AsTypeConversion()->GetResultType() == Primitive::kPrimLong) {
return GetFetch(instruction->InputAt(0), trip, in_body, is_min);
@@ -843,7 +844,7 @@
InductionVarRange::Value InductionVarRange::AddValue(Value v1, Value v2) const {
if (v1.is_known && v2.is_known && IsSafeAdd(v1.b_constant, v2.b_constant)) {
- const int32_t b = v1.b_constant + v2.b_constant;
+ int32_t b = v1.b_constant + v2.b_constant;
if (v1.a_constant == 0) {
return Value(v2.instruction, v2.a_constant, b);
} else if (v2.a_constant == 0) {
@@ -857,7 +858,7 @@
InductionVarRange::Value InductionVarRange::SubValue(Value v1, Value v2) const {
if (v1.is_known && v2.is_known && IsSafeSub(v1.b_constant, v2.b_constant)) {
- const int32_t b = v1.b_constant - v2.b_constant;
+ int32_t b = v1.b_constant - v2.b_constant;
if (v1.a_constant == 0 && IsSafeSub(0, v2.a_constant)) {
return Value(v2.instruction, -v2.a_constant, b);
} else if (v2.a_constant == 0) {
@@ -983,18 +984,21 @@
int64_t a = 0;
int64_t b = 0;
int64_t m = 0;
- if (IsConstant(info->op_a->op_a, kExact, &a) && a >= 0 &&
- IsConstant(info->op_a->op_b, kExact, &b) && b >= 0 &&
+ if (IsConstant(info->op_a->op_a, kExact, &a) &&
+ IsConstant(info->op_a->op_b, kExact, &b) &&
IsConstant(trip->op_a, kExact, &m) && m >= 1) {
- // Evaluate bounds on sum_i=0^m-1(a * i + b) + c with a,b >= 0 for known
+ // Evaluate bounds on sum_i=0^m-1(a * i + b) + c for known
// maximum index value m as a * (m * (m-1)) / 2 + b * m + c.
- // TODO: generalize
- HInstruction* c_instr = nullptr;
- if (GenerateCode(info->op_b, nullptr, graph, block, graph ? &c_instr : nullptr, false, false)) {
+ HInstruction* c = nullptr;
+ if (GenerateCode(info->op_b, nullptr, graph, block, graph ? &c : nullptr, false, false)) {
if (graph != nullptr) {
+ Primitive::Type type = info->type;
int64_t sum = a * ((m * (m - 1)) / 2) + b * m;
- *result = Insert(block, new (graph->GetArena()) HAdd(info->type,
- graph->GetIntConstant(sum), c_instr));
+ if (type != Primitive::kPrimLong) {
+ sum = static_cast<int32_t>(sum); // okay to truncate
+ }
+ *result =
+ Insert(block, new (graph->GetArena()) HAdd(type, graph->GetConstant(type, sum), c));
}
return true;
}
@@ -1011,35 +1015,33 @@
DCHECK_EQ(info->induction_class, HInductionVarAnalysis::kGeometric);
// Detect known base and trip count (always taken).
int64_t f = 0;
- int64_t t = 0;
- if (IsIntAndGet(info->fetch, &f) && f >= 1 && IsConstant(trip->op_a, kExact, &t) && t >= 1) {
+ int64_t m = 0;
+ if (IsIntAndGet(info->fetch, &f) && f >= 1 && IsConstant(trip->op_a, kExact, &m) && m >= 1) {
HInstruction* opa = nullptr;
HInstruction* opb = nullptr;
if (GenerateCode(info->op_a, nullptr, graph, block, &opa, false, false) &&
GenerateCode(info->op_b, nullptr, graph, block, &opb, false, false)) {
- // Compute f ^ t.
- int64_t fpowt = IntPow(f, t);
+ // Compute f ^ m for known maximum index value m.
+ int64_t fpow = IntPow(f, m);
if (graph != nullptr) {
- DCHECK(info->type == Primitive::kPrimInt); // due to codegen, generalize?
- if (fpowt == 0) {
+ DCHECK(info->operation == HInductionVarAnalysis::kMul ||
+ info->operation == HInductionVarAnalysis::kDiv);
+ Primitive::Type type = info->type;
+ if (fpow == 0) {
// Special case: repeated mul/div always yields zero.
- *result = graph->GetIntConstant(0);
- } else if (info->operation == HInductionVarAnalysis::kMul) {
- // Last value multiplication: a * f ^ t + b.
- HInstruction* mul = Insert(block,
- new (graph->GetArena()) HMul(info->type,
- opa,
- graph->GetIntConstant(fpowt)));
- *result = Insert(block, new (graph->GetArena()) HAdd(info->type, mul, opb));
+ *result = graph->GetConstant(type, 0);
} else {
- // Last value multiplication: a * f ^ -t + b.
- DCHECK_EQ(info->operation, HInductionVarAnalysis::kDiv);
- HInstruction* div = Insert(block,
- new (graph->GetArena()) HDiv(info->type,
- opa,
- graph->GetIntConstant(fpowt),
- kNoDexPc));
- *result = Insert(block, new (graph->GetArena()) HAdd(info->type, div, opb));
+ // Last value: a * f ^ m + b or a * f ^ -m + b.
+ if (type != Primitive::kPrimLong) {
+ fpow = static_cast<int32_t>(fpow); // okay to truncate
+ }
+ HInstruction* e = nullptr;
+ if (info->operation == HInductionVarAnalysis::kMul) {
+ e = new (graph->GetArena()) HMul(type, opa, graph->GetConstant(type, fpow));
+ } else {
+ e = new (graph->GetArena()) HDiv(type, opa, graph->GetConstant(type, fpow), kNoDexPc);
+ }
+ *result = Insert(block, new (graph->GetArena()) HAdd(type, Insert(block, e), opb));
}
}
return true;
@@ -1060,12 +1062,11 @@
for (; info->induction_class == HInductionVarAnalysis::kWrapAround;
info = info->op_b, ++depth) {}
// Handle wrap(x, wrap(.., y)) if trip count reaches an invariant at end.
- // TODO: generalize
- int64_t t = 0;
+ // TODO: generalize, but be careful to adjust the terminal.
+ int64_t m = 0;
if (info->induction_class == HInductionVarAnalysis::kInvariant &&
- IsConstant(trip->op_a, kExact, &t) && t >= depth &&
- GenerateCode(info, nullptr, graph, block, result, false, false)) {
- return true;
+ IsConstant(trip->op_a, kExact, &m) && m >= depth) {
+ return GenerateCode(info, nullptr, graph, block, result, false, false);
}
return false;
}
@@ -1079,43 +1080,49 @@
DCHECK(info != nullptr);
DCHECK_EQ(info->induction_class, HInductionVarAnalysis::kPeriodic);
// Count period.
- int32_t period = 1;
+ int64_t period = 1;
for (HInductionVarAnalysis::InductionInfo* p = info;
p->induction_class == HInductionVarAnalysis::kPeriodic;
p = p->op_b, ++period) {}
- // Handle periodic(x, y) case for restricted types.
- // TODO: generalize
- if (period != 2 ||
- trip->op_a->type != Primitive::kPrimInt ||
- (info->type != Primitive::kPrimInt && info->type != Primitive::kPrimBoolean)) {
- return false;
+ // Handle any periodic(x, periodic(.., y)) for known maximum index value m.
+ int64_t m = 0;
+ if (IsConstant(trip->op_a, kExact, &m) && m >= 1) {
+ int64_t li = m % period;
+ for (int64_t i = 0; i < li; info = info->op_b, i++) {}
+ if (info->induction_class == HInductionVarAnalysis::kPeriodic) {
+ info = info->op_a;
+ }
+ return GenerateCode(info, nullptr, graph, block, result, false, false);
}
- HInstruction* x_instr = nullptr;
- HInstruction* y_instr = nullptr;
- HInstruction* trip_expr = nullptr;
- if (GenerateCode(info->op_a, nullptr, graph, block, graph ? &x_instr : nullptr, false, false) &&
- GenerateCode(info->op_b, nullptr, graph, block, graph ? &y_instr : nullptr, false, false) &&
- GenerateCode(trip->op_a, nullptr, graph, block, graph ? &trip_expr : nullptr, false, false)) {
- // During actual code generation (graph != nullptr),
- // generate is_even ? x : y select instruction.
+ // Handle periodic(x, y) using even/odd-select on trip count. Enter trip count expression
+ // directly to obtain the maximum index value t even if taken test is needed.
+ HInstruction* x = nullptr;
+ HInstruction* y = nullptr;
+ HInstruction* t = nullptr;
+ if (period == 2 &&
+ GenerateCode(info->op_a, nullptr, graph, block, graph ? &x : nullptr, false, false) &&
+ GenerateCode(info->op_b, nullptr, graph, block, graph ? &y : nullptr, false, false) &&
+ GenerateCode(trip->op_a, nullptr, graph, block, graph ? &t : nullptr, false, false)) {
+ // During actual code generation (graph != nullptr), generate is_even ? x : y.
if (graph != nullptr) {
- HInstruction* is_even = Insert(block, new (graph->GetArena()) HEqual(
- Insert(block, new (graph->GetArena()) HAnd(
- Primitive::kPrimInt, trip_expr, graph->GetIntConstant(1))),
- graph->GetIntConstant(0), kNoDexPc));
- *result = Insert(block, new (graph->GetArena()) HSelect(is_even, x_instr, y_instr, kNoDexPc));
+ Primitive::Type type = trip->type;
+ HInstruction* msk =
+ Insert(block, new (graph->GetArena()) HAnd(type, t, graph->GetConstant(type, 1)));
+ HInstruction* is_even =
+ Insert(block, new (graph->GetArena()) HEqual(msk, graph->GetConstant(type, 0), kNoDexPc));
+ *result = Insert(block, new (graph->GetArena()) HSelect(is_even, x, y, kNoDexPc));
}
// Guard select with taken test if needed.
if (*needs_taken_test) {
- HInstruction* taken_test = nullptr;
- if (!GenerateCode(
- trip->op_b, nullptr, graph, block, graph ? &taken_test : nullptr, false, false)) {
+ HInstruction* is_taken = nullptr;
+ if (GenerateCode(trip->op_b, nullptr, graph, block, graph ? &is_taken : nullptr, false, false)) {
+ if (graph != nullptr) {
+ *result = Insert(block, new (graph->GetArena()) HSelect(is_taken, *result, x, kNoDexPc));
+ }
+ *needs_taken_test = false; // taken care of
+ } else {
return false;
- } else if (graph != nullptr) {
- *result = Insert(block,
- new (graph->GetArena()) HSelect(taken_test, *result, x_instr, kNoDexPc));
}
- *needs_taken_test = false; // taken care of
}
return true;
}
@@ -1134,13 +1141,8 @@
if (graph != nullptr && result == nullptr) {
return true;
}
- // Verify type safety.
- // TODO: generalize
- Primitive::Type type = Primitive::kPrimInt;
- if (info->type != Primitive::kPrimInt && info->type != Primitive::kPrimBoolean) {
- return false;
- }
// Handle current operation.
+ Primitive::Type type = info->type;
HInstruction* opa = nullptr;
HInstruction* opb = nullptr;
switch (info->induction_class) {
@@ -1214,15 +1216,15 @@
case HInductionVarAnalysis::kTripCountInBodyUnsafe:
if (is_min) {
if (graph != nullptr) {
- *result = graph->GetIntConstant(0);
+ *result = graph->GetConstant(type, 0);
}
return true;
} else if (in_body) {
if (GenerateCode(info->op_a, trip, graph, block, &opb, in_body, is_min)) {
if (graph != nullptr) {
- *result = Insert(block,
- new (graph->GetArena())
- HSub(type, opb, graph->GetIntConstant(1)));
+ *result =
+ Insert(block,
+ new (graph->GetArena()) HSub(type, opb, graph->GetConstant(type, 1)));
}
return true;
}
@@ -1236,26 +1238,31 @@
// Linear induction a * i + b, for normalized 0 <= i < TC. For ranges, this should
// be restricted to a unit stride to avoid arithmetic wrap-around situations that
// are harder to guard against. For a last value, requesting min/max based on any
- // stride yields right value.
- int64_t stride_value = 0;
- if (IsConstant(info->op_a, kExact, &stride_value)) {
- const bool is_min_a = stride_value >= 0 ? is_min : !is_min;
- if (GenerateCode(trip, trip, graph, block, &opa, in_body, is_min_a) &&
- GenerateCode(info->op_b, trip, graph, block, &opb, in_body, is_min)) {
- if (graph != nullptr) {
- HInstruction* oper;
- if (stride_value == 1) {
- oper = new (graph->GetArena()) HAdd(type, opa, opb);
- } else if (stride_value == -1) {
- oper = new (graph->GetArena()) HSub(type, opb, opa);
- } else {
- HInstruction* mul = new (graph->GetArena()) HMul(
- type, graph->GetIntConstant(stride_value), opa);
- oper = new (graph->GetArena()) HAdd(type, Insert(block, mul), opb);
+ // known stride yields right value. Always avoid any narrowing linear induction or
+ // any type mismatch between the linear induction and the trip count expression.
+ // TODO: careful runtime type conversions could generalize this latter restriction.
+ if (!HInductionVarAnalysis::IsNarrowingLinear(info) && trip->type == type) {
+ int64_t stride_value = 0;
+ if (IsConstant(info->op_a, kExact, &stride_value) &&
+ CanLongValueFitIntoInt(stride_value)) {
+ const bool is_min_a = stride_value >= 0 ? is_min : !is_min;
+ if (GenerateCode(trip, trip, graph, block, &opa, in_body, is_min_a) &&
+ GenerateCode(info->op_b, trip, graph, block, &opb, in_body, is_min)) {
+ if (graph != nullptr) {
+ HInstruction* oper;
+ if (stride_value == 1) {
+ oper = new (graph->GetArena()) HAdd(type, opa, opb);
+ } else if (stride_value == -1) {
+ oper = new (graph->GetArena()) HSub(type, opb, opa);
+ } else {
+ HInstruction* mul =
+ new (graph->GetArena()) HMul(type, graph->GetConstant(type, stride_value), opa);
+ oper = new (graph->GetArena()) HAdd(type, Insert(block, mul), opb);
+ }
+ *result = Insert(block, oper);
}
- *result = Insert(block, oper);
+ return true;
}
- return true;
}
}
break;
@@ -1270,7 +1277,7 @@
Value extreme = GetVal(info, trip, in_body, is_min);
if (IsConstantValue(extreme)) {
if (graph != nullptr) {
- *result = graph->GetIntConstant(extreme.b_constant);
+ *result = graph->GetConstant(type, extreme.b_constant);
}
return true;
}
diff --git a/compiler/optimizing/inliner.cc b/compiler/optimizing/inliner.cc
index fe4662a..d847879 100644
--- a/compiler/optimizing/inliner.cc
+++ b/compiler/optimizing/inliner.cc
@@ -480,13 +480,11 @@
}
// We successfully inlined, now add a guard.
- bool is_referrer =
- (GetMonomorphicType(classes) == outermost_graph_->GetArtMethod()->GetDeclaringClass());
AddTypeGuard(receiver,
cursor,
bb_cursor,
class_index,
- is_referrer,
+ GetMonomorphicType(classes),
invoke_instruction,
/* with_deoptimization */ true);
@@ -506,52 +504,62 @@
uint32_t dex_pc,
HInstruction* cursor,
HBasicBlock* bb_cursor) {
- HInstruction* deopt_flag = new (graph_->GetArena()) HShouldDeoptimizeFlag(dex_pc);
- HInstruction* should_deopt = new (graph_->GetArena()) HNotEqual(
+ HShouldDeoptimizeFlag* deopt_flag = new (graph_->GetArena())
+ HShouldDeoptimizeFlag(graph_->GetArena(), dex_pc);
+ HInstruction* compare = new (graph_->GetArena()) HNotEqual(
deopt_flag, graph_->GetIntConstant(0, dex_pc));
- HInstruction* deopt = new (graph_->GetArena()) HDeoptimize(should_deopt, dex_pc);
+ HInstruction* deopt = new (graph_->GetArena()) HDeoptimize(compare, dex_pc);
if (cursor != nullptr) {
bb_cursor->InsertInstructionAfter(deopt_flag, cursor);
} else {
bb_cursor->InsertInstructionBefore(deopt_flag, bb_cursor->GetFirstInstruction());
}
- bb_cursor->InsertInstructionAfter(should_deopt, deopt_flag);
- bb_cursor->InsertInstructionAfter(deopt, should_deopt);
+ bb_cursor->InsertInstructionAfter(compare, deopt_flag);
+ bb_cursor->InsertInstructionAfter(deopt, compare);
+
+ // Add receiver as input to aid CHA guard optimization later.
+ deopt_flag->AddInput(invoke_instruction->InputAt(0));
+ DCHECK_EQ(deopt_flag->InputCount(), 1u);
deopt->CopyEnvironmentFrom(invoke_instruction->GetEnvironment());
+ outermost_graph_->IncrementNumberOfCHAGuards();
}
HInstruction* HInliner::AddTypeGuard(HInstruction* receiver,
HInstruction* cursor,
HBasicBlock* bb_cursor,
dex::TypeIndex class_index,
- bool is_referrer,
+ mirror::Class* klass,
HInstruction* invoke_instruction,
bool with_deoptimization) {
+ ScopedAssertNoThreadSuspension sants("Adding compiler type guard");
+
ClassLinker* class_linker = caller_compilation_unit_.GetClassLinker();
HInstanceFieldGet* receiver_class = BuildGetReceiverClass(
class_linker, receiver, invoke_instruction->GetDexPc());
-
- const DexFile& caller_dex_file = *caller_compilation_unit_.GetDexFile();
- // Note that we will just compare the classes, so we don't need Java semantics access checks.
- // Also, the caller of `AddTypeGuard` must have guaranteed that the class is in the dex cache.
- HLoadClass* load_class = new (graph_->GetArena()) HLoadClass(graph_->GetCurrentMethod(),
- class_index,
- caller_dex_file,
- is_referrer,
- invoke_instruction->GetDexPc(),
- /* needs_access_check */ false,
- /* is_in_dex_cache */ true,
- /* is_in_boot_image */ false);
-
- HNotEqual* compare = new (graph_->GetArena()) HNotEqual(load_class, receiver_class);
- // TODO: Extend reference type propagation to understand the guard.
if (cursor != nullptr) {
bb_cursor->InsertInstructionAfter(receiver_class, cursor);
} else {
bb_cursor->InsertInstructionBefore(receiver_class, bb_cursor->GetFirstInstruction());
}
+
+ const DexFile& caller_dex_file = *caller_compilation_unit_.GetDexFile();
+ bool is_referrer = (klass == outermost_graph_->GetArtMethod()->GetDeclaringClass());
+ // Note that we will just compare the classes, so we don't need Java semantics access checks.
+ // Note that the type index and the dex file are relative to the method this type guard is
+ // inlined into.
+ HLoadClass* load_class = new (graph_->GetArena()) HLoadClass(graph_->GetCurrentMethod(),
+ class_index,
+ caller_dex_file,
+ is_referrer,
+ invoke_instruction->GetDexPc(),
+ /* needs_access_check */ false);
bb_cursor->InsertInstructionAfter(load_class, receiver_class);
+ // Sharpen after adding the instruction, as the sharpening may remove inputs.
+ HSharpening::SharpenClass(load_class, klass, handles_, codegen_, compiler_driver_);
+
+ // TODO: Extend reference type propagation to understand the guard.
+ HNotEqual* compare = new (graph_->GetArena()) HNotEqual(load_class, receiver_class);
bb_cursor->InsertInstructionAfter(compare, load_class);
if (with_deoptimization) {
HDeoptimize* deoptimize = new (graph_->GetArena()) HDeoptimize(
@@ -604,7 +612,6 @@
all_targets_inlined = false;
} else {
one_target_inlined = true;
- bool is_referrer = (classes->Get(i) == outermost_graph_->GetArtMethod()->GetDeclaringClass());
// If we have inlined all targets before, and this receiver is the last seen,
// we deoptimize instead of keeping the original invoke instruction.
@@ -616,8 +623,13 @@
// We do not support HDeoptimize in OSR methods.
deoptimize = false;
}
- HInstruction* compare = AddTypeGuard(
- receiver, cursor, bb_cursor, class_index, is_referrer, invoke_instruction, deoptimize);
+ HInstruction* compare = AddTypeGuard(receiver,
+ cursor,
+ bb_cursor,
+ class_index,
+ classes->Get(i),
+ invoke_instruction,
+ deoptimize);
if (deoptimize) {
if (return_replacement != nullptr) {
invoke_instruction->ReplaceWith(return_replacement);
diff --git a/compiler/optimizing/inliner.h b/compiler/optimizing/inliner.h
index ffebd97..0c64362 100644
--- a/compiler/optimizing/inliner.h
+++ b/compiler/optimizing/inliner.h
@@ -167,7 +167,7 @@
HInstruction* cursor,
HBasicBlock* bb_cursor,
dex::TypeIndex class_index,
- bool is_referrer,
+ mirror::Class* klass,
HInstruction* invoke_instruction,
bool with_deoptimization)
REQUIRES_SHARED(Locks::mutator_lock_);
diff --git a/compiler/optimizing/instruction_builder.cc b/compiler/optimizing/instruction_builder.cc
index b97581b..1ca3218 100644
--- a/compiler/optimizing/instruction_builder.cc
+++ b/compiler/optimizing/instruction_builder.cc
@@ -937,9 +937,7 @@
outer_dex_file,
IsOutermostCompilingClass(type_index),
dex_pc,
- needs_access_check,
- /* is_in_dex_cache */ false,
- /* is_in_boot_image */ false);
+ needs_access_check);
AppendInstruction(load_class);
HInstruction* cls = load_class;
@@ -1029,9 +1027,7 @@
outer_dex_file,
is_outer_class,
dex_pc,
- /*needs_access_check*/ false,
- /* is_in_dex_cache */ false,
- /* is_in_boot_image */ false);
+ /*needs_access_check*/ false);
AppendInstruction(load_class);
clinit_check = new (arena_) HClinitCheck(load_class, dex_pc);
AppendInstruction(clinit_check);
@@ -1388,9 +1384,7 @@
outer_dex_file,
is_outer_class,
dex_pc,
- /*needs_access_check*/ false,
- /* is_in_dex_cache */ false,
- /* is_in_boot_image */ false);
+ /*needs_access_check*/ false);
AppendInstruction(constant);
HInstruction* cls = constant;
@@ -1664,9 +1658,7 @@
dex_file,
IsOutermostCompilingClass(type_index),
dex_pc,
- !can_access,
- /* is_in_dex_cache */ false,
- /* is_in_boot_image */ false);
+ !can_access);
AppendInstruction(cls);
TypeCheckKind check_kind = ComputeTypeCheckKind(resolved_class);
@@ -2656,9 +2648,7 @@
*dex_file_,
IsOutermostCompilingClass(type_index),
dex_pc,
- !can_access,
- /* is_in_dex_cache */ false,
- /* is_in_boot_image */ false));
+ !can_access));
UpdateLocal(instruction.VRegA_21c(), current_block_->GetLastInstruction());
break;
}
diff --git a/compiler/optimizing/instruction_simplifier.cc b/compiler/optimizing/instruction_simplifier.cc
index c615df1..439e3b6 100644
--- a/compiler/optimizing/instruction_simplifier.cc
+++ b/compiler/optimizing/instruction_simplifier.cc
@@ -1897,7 +1897,8 @@
static bool NoEscapeForStringBufferReference(HInstruction* reference, HInstruction* user) {
if (user->IsInvokeStaticOrDirect()) {
// Any constructor on StringBuffer is okay.
- return user->AsInvokeStaticOrDirect()->GetResolvedMethod()->IsConstructor() &&
+ return user->AsInvokeStaticOrDirect()->GetResolvedMethod() != nullptr &&
+ user->AsInvokeStaticOrDirect()->GetResolvedMethod()->IsConstructor() &&
user->InputAt(0) == reference;
} else if (user->IsInvokeVirtual()) {
switch (user->AsInvokeVirtual()->GetIntrinsic()) {
diff --git a/compiler/optimizing/intrinsics_arm_vixl.cc b/compiler/optimizing/intrinsics_arm_vixl.cc
index fc69f79..85e84d8 100644
--- a/compiler/optimizing/intrinsics_arm_vixl.cc
+++ b/compiler/optimizing/intrinsics_arm_vixl.cc
@@ -1509,7 +1509,7 @@
SlowPathCodeARMVIXL* slow_path = nullptr;
HInstruction* code_point = invoke->InputAt(1);
if (code_point->IsIntConstant()) {
- if (static_cast<uint32_t>(code_point->AsIntConstant()->GetValue()) >
+ if (static_cast<uint32_t>(Int32ConstantFrom(code_point)) >
std::numeric_limits<uint16_t>::max()) {
// Always needs the slow-path. We could directly dispatch to it, but this case should be
// rare, so for simplicity just put the full slow-path down and branch unconditionally.
diff --git a/compiler/optimizing/linearize_test.cc b/compiler/optimizing/linearize_test.cc
index 13e14c5..3831aa6 100644
--- a/compiler/optimizing/linearize_test.cc
+++ b/compiler/optimizing/linearize_test.cc
@@ -18,7 +18,6 @@
#include "arch/x86/instruction_set_features_x86.h"
#include "base/arena_allocator.h"
-#include "base/stringprintf.h"
#include "builder.h"
#include "code_generator.h"
#include "code_generator_x86.h"
diff --git a/compiler/optimizing/nodes.cc b/compiler/optimizing/nodes.cc
index 1e946d6..b9e284f 100644
--- a/compiler/optimizing/nodes.cc
+++ b/compiler/optimizing/nodes.cc
@@ -1108,13 +1108,23 @@
return HasEnvironment() ? environment_->Size() : 0;
}
-void HPhi::AddInput(HInstruction* input) {
+void HVariableInputSizeInstruction::AddInput(HInstruction* input) {
DCHECK(input->GetBlock() != nullptr);
inputs_.push_back(HUserRecord<HInstruction*>(input));
input->AddUseAt(this, inputs_.size() - 1);
}
-void HPhi::RemoveInputAt(size_t index) {
+void HVariableInputSizeInstruction::InsertInputAt(size_t index, HInstruction* input) {
+ inputs_.insert(inputs_.begin() + index, HUserRecord<HInstruction*>(input));
+ input->AddUseAt(this, index);
+ // Update indexes in use nodes of inputs that have been pushed further back by the insert().
+ for (size_t i = index + 1u, e = inputs_.size(); i < e; ++i) {
+ DCHECK_EQ(inputs_[i].GetUseNode()->GetIndex(), i - 1u);
+ inputs_[i].GetUseNode()->SetIndex(i);
+ }
+}
+
+void HVariableInputSizeInstruction::RemoveInputAt(size_t index) {
RemoveAsUserOfInput(index);
inputs_.erase(inputs_.begin() + index);
// Update indexes in use nodes of inputs that have been pulled forward by the erase().
@@ -1347,7 +1357,9 @@
void HInstruction::MoveBefore(HInstruction* cursor) {
DCHECK(!IsPhi());
DCHECK(!IsControlFlow());
- DCHECK(CanBeMoved());
+ DCHECK(CanBeMoved() ||
+ // HShouldDeoptimizeFlag can only be moved by CHAGuardOptimization.
+ IsShouldDeoptimizeFlag());
DCHECK(!cursor->IsPhi());
next_->previous_ = previous_;
@@ -2386,26 +2398,6 @@
return !opt.GetDoesNotNeedDexCache();
}
-void HInvokeStaticOrDirect::InsertInputAt(size_t index, HInstruction* input) {
- inputs_.insert(inputs_.begin() + index, HUserRecord<HInstruction*>(input));
- input->AddUseAt(this, index);
- // Update indexes in use nodes of inputs that have been pushed further back by the insert().
- for (size_t i = index + 1u, e = inputs_.size(); i < e; ++i) {
- DCHECK_EQ(inputs_[i].GetUseNode()->GetIndex(), i - 1u);
- inputs_[i].GetUseNode()->SetIndex(i);
- }
-}
-
-void HInvokeStaticOrDirect::RemoveInputAt(size_t index) {
- RemoveAsUserOfInput(index);
- inputs_.erase(inputs_.begin() + index);
- // Update indexes in use nodes of inputs that have been pulled forward by the erase().
- for (size_t i = index, e = inputs_.size(); i < e; ++i) {
- DCHECK_EQ(inputs_[i].GetUseNode()->GetIndex(), i + 1u);
- inputs_[i].GetUseNode()->SetIndex(i);
- }
-}
-
std::ostream& operator<<(std::ostream& os, HInvokeStaticOrDirect::MethodLoadKind rhs) {
switch (rhs) {
case HInvokeStaticOrDirect::MethodLoadKind::kStringInit:
diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h
index 4a8cfcb..1f0c8e8 100644
--- a/compiler/optimizing/nodes.h
+++ b/compiler/optimizing/nodes.h
@@ -330,6 +330,7 @@
invoke_type_(invoke_type),
in_ssa_form_(false),
should_generate_constructor_barrier_(should_generate_constructor_barrier),
+ number_of_cha_guards_(0),
instruction_set_(instruction_set),
cached_null_constant_(nullptr),
cached_int_constants_(std::less<int32_t>(), arena->Adapter(kArenaAllocConstantsMap)),
@@ -551,9 +552,7 @@
}
bool HasShouldDeoptimizeFlag() const {
- // TODO: if all CHA guards can be eliminated, there is no need for the flag
- // even if cha_single_implementation_list_ is not empty.
- return !cha_single_implementation_list_.empty();
+ return number_of_cha_guards_ != 0;
}
bool HasTryCatch() const { return has_try_catch_; }
@@ -572,6 +571,10 @@
ReferenceTypeInfo GetInexactObjectRti() const { return inexact_object_rti_; }
+ uint32_t GetNumberOfCHAGuards() { return number_of_cha_guards_; }
+ void SetNumberOfCHAGuards(uint32_t num) { number_of_cha_guards_ = num; }
+ void IncrementNumberOfCHAGuards() { number_of_cha_guards_++; }
+
private:
void RemoveInstructionsAsUsersFromDeadBlocks(const ArenaBitVector& visited) const;
void RemoveDeadBlocks(const ArenaBitVector& visited);
@@ -667,6 +670,10 @@
const bool should_generate_constructor_barrier_;
+ // Number of CHA guards in the graph. Used to short-circuit the
+ // CHA guard optimization pass when there is no CHA guard left.
+ uint32_t number_of_cha_guards_;
+
const InstructionSet instruction_set_;
// Cached constants.
@@ -2347,6 +2354,32 @@
DISALLOW_COPY_AND_ASSIGN(HBackwardInstructionIterator);
};
+class HVariableInputSizeInstruction : public HInstruction {
+ public:
+ using HInstruction::GetInputRecords; // Keep the const version visible.
+ ArrayRef<HUserRecord<HInstruction*>> GetInputRecords() OVERRIDE {
+ return ArrayRef<HUserRecord<HInstruction*>>(inputs_);
+ }
+
+ void AddInput(HInstruction* input);
+ void InsertInputAt(size_t index, HInstruction* input);
+ void RemoveInputAt(size_t index);
+
+ protected:
+ HVariableInputSizeInstruction(SideEffects side_effects,
+ uint32_t dex_pc,
+ ArenaAllocator* arena,
+ size_t number_of_inputs,
+ ArenaAllocKind kind)
+ : HInstruction(side_effects, dex_pc),
+ inputs_(number_of_inputs, arena->Adapter(kind)) {}
+
+ ArenaVector<HUserRecord<HInstruction*>> inputs_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(HVariableInputSizeInstruction);
+};
+
template<size_t N>
class HTemplateInstruction: public HInstruction {
public:
@@ -2438,15 +2471,19 @@
DISALLOW_COPY_AND_ASSIGN(HReturn);
};
-class HPhi FINAL : public HInstruction {
+class HPhi FINAL : public HVariableInputSizeInstruction {
public:
HPhi(ArenaAllocator* arena,
uint32_t reg_number,
size_t number_of_inputs,
Primitive::Type type,
uint32_t dex_pc = kNoDexPc)
- : HInstruction(SideEffects::None(), dex_pc),
- inputs_(number_of_inputs, arena->Adapter(kArenaAllocPhiInputs)),
+ : HVariableInputSizeInstruction(
+ SideEffects::None(),
+ dex_pc,
+ arena,
+ number_of_inputs,
+ kArenaAllocPhiInputs),
reg_number_(reg_number) {
SetPackedField<TypeField>(ToPhiType(type));
DCHECK_NE(GetType(), Primitive::kPrimVoid);
@@ -2464,14 +2501,6 @@
bool IsCatchPhi() const { return GetBlock()->IsCatchBlock(); }
- using HInstruction::GetInputRecords; // Keep the const version visible.
- ArrayRef<HUserRecord<HInstruction*>> GetInputRecords() OVERRIDE FINAL {
- return ArrayRef<HUserRecord<HInstruction*>>(inputs_);
- }
-
- void AddInput(HInstruction* input);
- void RemoveInputAt(size_t index);
-
Primitive::Type GetType() const OVERRIDE { return GetPackedField<TypeField>(); }
void SetType(Primitive::Type new_type) {
// Make sure that only valid type changes occur. The following are allowed:
@@ -2527,7 +2556,6 @@
static_assert(kNumberOfPhiPackedBits <= kMaxNumberOfPackedBits, "Too many packed fields.");
using TypeField = BitField<Primitive::Type, kFieldType, kFieldTypeSize>;
- ArenaVector<HUserRecord<HInstruction*>> inputs_;
const uint32_t reg_number_;
DISALLOW_COPY_AND_ASSIGN(HPhi);
@@ -2904,14 +2932,20 @@
// if it's true, starts to do deoptimization.
// It has a 4-byte slot on stack.
// TODO: allocate a register for this flag.
-class HShouldDeoptimizeFlag FINAL : public HExpression<0> {
+class HShouldDeoptimizeFlag FINAL : public HVariableInputSizeInstruction {
public:
- // TODO: use SideEffects to aid eliminating some CHA guards.
- explicit HShouldDeoptimizeFlag(uint32_t dex_pc)
- : HExpression(Primitive::kPrimInt, SideEffects::None(), dex_pc) {
+ // CHA guards are only optimized in a separate pass and it has no side effects
+ // with regard to other passes.
+ HShouldDeoptimizeFlag(ArenaAllocator* arena, uint32_t dex_pc)
+ : HVariableInputSizeInstruction(SideEffects::None(), dex_pc, arena, 0, kArenaAllocCHA) {
}
- // We don't eliminate CHA guards yet.
+ Primitive::Type GetType() const OVERRIDE { return Primitive::kPrimInt; }
+
+ // We do all CHA guard elimination/motion in a single pass, after which there is no
+ // further guard elimination/motion since a guard might have been used for justification
+ // of the elimination of another guard. Therefore, we pretend this guard cannot be moved
+ // to avoid other optimizations trying to move it.
bool CanBeMoved() const OVERRIDE { return false; }
DECLARE_INSTRUCTION(ShouldDeoptimizeFlag);
@@ -3791,15 +3825,10 @@
kCanThrow // Intrinsic may throw exceptions.
};
-class HInvoke : public HInstruction {
+class HInvoke : public HVariableInputSizeInstruction {
public:
bool NeedsEnvironment() const OVERRIDE;
- using HInstruction::GetInputRecords; // Keep the const version visible.
- ArrayRef<HUserRecord<HInstruction*>> GetInputRecords() OVERRIDE {
- return ArrayRef<HUserRecord<HInstruction*>>(inputs_);
- }
-
void SetArgumentAt(size_t index, HInstruction* argument) {
SetRawInputAt(index, argument);
}
@@ -3878,12 +3907,14 @@
uint32_t dex_method_index,
ArtMethod* resolved_method,
InvokeType invoke_type)
- : HInstruction(
- SideEffects::AllExceptGCDependency(), dex_pc), // Assume write/read on all fields/arrays.
+ : HVariableInputSizeInstruction(
+ SideEffects::AllExceptGCDependency(), // Assume write/read on all fields/arrays.
+ dex_pc,
+ arena,
+ number_of_arguments + number_of_other_inputs,
+ kArenaAllocInvokeInputs),
number_of_arguments_(number_of_arguments),
resolved_method_(resolved_method),
- inputs_(number_of_arguments + number_of_other_inputs,
- arena->Adapter(kArenaAllocInvokeInputs)),
dex_method_index_(dex_method_index),
intrinsic_(Intrinsics::kNone),
intrinsic_optimizations_(0) {
@@ -3894,7 +3925,6 @@
uint32_t number_of_arguments_;
ArtMethod* const resolved_method_;
- ArenaVector<HUserRecord<HInstruction*>> inputs_;
const uint32_t dex_method_index_;
Intrinsics intrinsic_;
@@ -4184,10 +4214,6 @@
DECLARE_INSTRUCTION(InvokeStaticOrDirect);
- protected:
- void InsertInputAt(size_t index, HInstruction* input);
- void RemoveInputAt(size_t index);
-
private:
static constexpr size_t kFieldClinitCheckRequirement = kNumberOfInvokePackedBits;
static constexpr size_t kFieldClinitCheckRequirementSize =
@@ -5519,9 +5545,7 @@
const DexFile& dex_file,
bool is_referrers_class,
uint32_t dex_pc,
- bool needs_access_check,
- bool is_in_dex_cache,
- bool is_in_boot_image)
+ bool needs_access_check)
: HInstruction(SideEffectsForArchRuntimeCalls(), dex_pc),
special_input_(HUserRecord<HInstruction*>(current_method)),
type_index_(type_index),
@@ -5534,8 +5558,8 @@
SetPackedField<LoadKindField>(
is_referrers_class ? LoadKind::kReferrersClass : LoadKind::kDexCacheViaMethod);
SetPackedFlag<kFlagNeedsAccessCheck>(needs_access_check);
- SetPackedFlag<kFlagIsInDexCache>(is_in_dex_cache);
- SetPackedFlag<kFlagIsInBootImage>(is_in_boot_image);
+ SetPackedFlag<kFlagIsInDexCache>(false);
+ SetPackedFlag<kFlagIsInBootImage>(false);
SetPackedFlag<kFlagGenerateClInitCheck>(false);
}
diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc
index ba7012a..4bf5b08 100644
--- a/compiler/optimizing/optimizing_compiler.cc
+++ b/compiler/optimizing/optimizing_compiler.cc
@@ -54,6 +54,7 @@
#include "base/timing_logger.h"
#include "bounds_check_elimination.h"
#include "builder.h"
+#include "cha_guard_optimization.h"
#include "code_generator.h"
#include "compiled_method.h"
#include "compiler.h"
@@ -517,6 +518,8 @@
return new (arena) SideEffectsAnalysis(graph);
} else if (opt_name == HLoopOptimization::kLoopOptimizationPassName) {
return new (arena) HLoopOptimization(graph, most_recent_induction);
+ } else if (opt_name == CHAGuardOptimization::kCHAGuardOptimizationPassName) {
+ return new (arena) CHAGuardOptimization(graph);
#ifdef ART_ENABLE_CODEGEN_arm
} else if (opt_name == arm::DexCacheArrayFixups::kDexCacheArrayFixupsArmPassName) {
return new (arena) arm::DexCacheArrayFixups(graph, codegen, stats);
@@ -779,6 +782,7 @@
InstructionSimplifier* simplify4 = new (arena) InstructionSimplifier(
graph, stats, "instruction_simplifier$before_codegen");
IntrinsicsRecognizer* intrinsics = new (arena) IntrinsicsRecognizer(graph, stats);
+ CHAGuardOptimization* cha_guard = new (arena) CHAGuardOptimization(graph);
HOptimization* optimizations1[] = {
intrinsics,
@@ -807,6 +811,7 @@
fold3, // evaluates code generated by dynamic bce
simplify3,
lse,
+ cha_guard,
dce3,
// The codegen has a few assumptions that only the instruction simplifier
// can satisfy. For example, the code generator does not expect to see a
@@ -1193,7 +1198,7 @@
}
uint8_t* stack_map_data = nullptr;
uint8_t* roots_data = nullptr;
- code_cache->ReserveData(
+ uint32_t data_size = code_cache->ReserveData(
self, stack_map_size, number_of_roots, method, &stack_map_data, &roots_data);
if (stack_map_data == nullptr || roots_data == nullptr) {
return false;
@@ -1212,6 +1217,7 @@
codegen->GetFpuSpillMask(),
code_allocator.GetMemory().data(),
code_allocator.GetSize(),
+ data_size,
osr,
roots,
codegen->GetGraph()->HasShouldDeoptimizeFlag(),
diff --git a/compiler/optimizing/pretty_printer.h b/compiler/optimizing/pretty_printer.h
index 5891350..c6579dc 100644
--- a/compiler/optimizing/pretty_printer.h
+++ b/compiler/optimizing/pretty_printer.h
@@ -17,7 +17,8 @@
#ifndef ART_COMPILER_OPTIMIZING_PRETTY_PRINTER_H_
#define ART_COMPILER_OPTIMIZING_PRETTY_PRINTER_H_
-#include "base/stringprintf.h"
+#include "android-base/stringprintf.h"
+
#include "nodes.h"
namespace art {
@@ -108,7 +109,7 @@
: HPrettyPrinter(graph), str_(""), current_block_(nullptr) { }
void PrintInt(int value) OVERRIDE {
- str_ += StringPrintf("%d", value);
+ str_ += android::base::StringPrintf("%d", value);
}
void PrintString(const char* value) OVERRIDE {
diff --git a/compiler/optimizing/pretty_printer_test.cc b/compiler/optimizing/pretty_printer_test.cc
index 951cdfb..1af94f3 100644
--- a/compiler/optimizing/pretty_printer_test.cc
+++ b/compiler/optimizing/pretty_printer_test.cc
@@ -15,7 +15,6 @@
*/
#include "base/arena_allocator.h"
-#include "base/stringprintf.h"
#include "builder.h"
#include "dex_file.h"
#include "dex_instruction.h"
diff --git a/compiler/optimizing/sharpening.cc b/compiler/optimizing/sharpening.cc
index 91efb80..91826cf 100644
--- a/compiler/optimizing/sharpening.cc
+++ b/compiler/optimizing/sharpening.cc
@@ -140,6 +140,25 @@
}
void HSharpening::ProcessLoadClass(HLoadClass* load_class) {
+ ScopedObjectAccess soa(Thread::Current());
+ StackHandleScope<1> hs(soa.Self());
+ Runtime* runtime = Runtime::Current();
+ ClassLinker* class_linker = runtime->GetClassLinker();
+ const DexFile& dex_file = load_class->GetDexFile();
+ dex::TypeIndex type_index = load_class->GetTypeIndex();
+ Handle<mirror::DexCache> dex_cache = IsSameDexFile(dex_file, *compilation_unit_.GetDexFile())
+ ? compilation_unit_.GetDexCache()
+ : hs.NewHandle(class_linker->FindDexCache(soa.Self(), dex_file));
+ mirror::Class* cls = dex_cache->GetResolvedType(type_index);
+ SharpenClass(load_class, cls, handles_, codegen_, compiler_driver_);
+}
+
+void HSharpening::SharpenClass(HLoadClass* load_class,
+ mirror::Class* klass,
+ VariableSizedHandleScope* handles,
+ CodeGenerator* codegen,
+ CompilerDriver* compiler_driver) {
+ ScopedAssertNoThreadSuspension sants("Sharpening class in compiler");
DCHECK(load_class->GetLoadKind() == HLoadClass::LoadKind::kDexCacheViaMethod ||
load_class->GetLoadKind() == HLoadClass::LoadKind::kReferrersClass)
<< load_class->GetLoadKind();
@@ -153,69 +172,60 @@
bool is_in_boot_image = false;
HLoadClass::LoadKind desired_load_kind = static_cast<HLoadClass::LoadKind>(-1);
uint64_t address = 0u; // Class or dex cache element address.
- {
- ScopedObjectAccess soa(Thread::Current());
- StackHandleScope<1> hs(soa.Self());
- Runtime* runtime = Runtime::Current();
- ClassLinker* class_linker = runtime->GetClassLinker();
- Handle<mirror::DexCache> dex_cache = IsSameDexFile(dex_file, *compilation_unit_.GetDexFile())
- ? compilation_unit_.GetDexCache()
- : hs.NewHandle(class_linker->FindDexCache(soa.Self(), dex_file));
- mirror::Class* klass = dex_cache->GetResolvedType(type_index);
- if (codegen_->GetCompilerOptions().IsBootImage()) {
- // Compiling boot image. Check if the class is a boot image class.
- DCHECK(!runtime->UseJitCompilation());
- if (!compiler_driver_->GetSupportBootImageFixup()) {
- // MIPS64 or compiler_driver_test. Do not sharpen.
- desired_load_kind = HLoadClass::LoadKind::kDexCacheViaMethod;
- } else if ((klass != nullptr) && compiler_driver_->IsImageClass(
- dex_file.StringDataByIdx(dex_file.GetTypeId(type_index).descriptor_idx_))) {
- is_in_boot_image = true;
- is_in_dex_cache = true;
- desired_load_kind = codegen_->GetCompilerOptions().GetCompilePic()
- ? HLoadClass::LoadKind::kBootImageLinkTimePcRelative
- : HLoadClass::LoadKind::kBootImageLinkTimeAddress;
- } else {
- // Not a boot image class. We must go through the dex cache.
- DCHECK(ContainsElement(compiler_driver_->GetDexFilesForOatFile(), &dex_file));
- desired_load_kind = HLoadClass::LoadKind::kDexCachePcRelative;
- }
+ Runtime* runtime = Runtime::Current();
+ if (codegen->GetCompilerOptions().IsBootImage()) {
+ // Compiling boot image. Check if the class is a boot image class.
+ DCHECK(!runtime->UseJitCompilation());
+ if (!compiler_driver->GetSupportBootImageFixup()) {
+ // MIPS64 or compiler_driver_test. Do not sharpen.
+ desired_load_kind = HLoadClass::LoadKind::kDexCacheViaMethod;
+ } else if ((klass != nullptr) && compiler_driver->IsImageClass(
+ dex_file.StringDataByIdx(dex_file.GetTypeId(type_index).descriptor_idx_))) {
+ is_in_boot_image = true;
+ is_in_dex_cache = true;
+ desired_load_kind = codegen->GetCompilerOptions().GetCompilePic()
+ ? HLoadClass::LoadKind::kBootImageLinkTimePcRelative
+ : HLoadClass::LoadKind::kBootImageLinkTimeAddress;
} else {
- is_in_boot_image = (klass != nullptr) && runtime->GetHeap()->ObjectIsInBootImageSpace(klass);
- if (runtime->UseJitCompilation()) {
- // TODO: Make sure we don't set the "compile PIC" flag for JIT as that's bogus.
- // DCHECK(!codegen_->GetCompilerOptions().GetCompilePic());
- is_in_dex_cache = (klass != nullptr);
- if (is_in_boot_image) {
- // TODO: Use direct pointers for all non-moving spaces, not just boot image. Bug: 29530787
- desired_load_kind = HLoadClass::LoadKind::kBootImageAddress;
- address = reinterpret_cast64<uint64_t>(klass);
- } else if (is_in_dex_cache) {
- desired_load_kind = HLoadClass::LoadKind::kJitTableAddress;
- // We store in the address field the location of the stack reference maintained
- // by the handle. We do this now so that the code generation does not need to figure
- // out which class loader to use.
- address = reinterpret_cast<uint64_t>(handles_->NewHandle(klass).GetReference());
- } else {
- // Class not loaded yet. This happens when the dex code requesting
- // this `HLoadClass` hasn't been executed in the interpreter.
- // Fallback to the dex cache.
- // TODO(ngeoffray): Generate HDeoptimize instead.
- desired_load_kind = HLoadClass::LoadKind::kDexCacheViaMethod;
- }
- } else if (is_in_boot_image && !codegen_->GetCompilerOptions().GetCompilePic()) {
- // AOT app compilation. Check if the class is in the boot image.
+ // Not a boot image class. We must go through the dex cache.
+ DCHECK(ContainsElement(compiler_driver->GetDexFilesForOatFile(), &dex_file));
+ desired_load_kind = HLoadClass::LoadKind::kDexCachePcRelative;
+ }
+ } else {
+ is_in_boot_image = (klass != nullptr) && runtime->GetHeap()->ObjectIsInBootImageSpace(klass);
+ if (runtime->UseJitCompilation()) {
+ // TODO: Make sure we don't set the "compile PIC" flag for JIT as that's bogus.
+ // DCHECK(!codegen_->GetCompilerOptions().GetCompilePic());
+ is_in_dex_cache = (klass != nullptr);
+ if (is_in_boot_image) {
+ // TODO: Use direct pointers for all non-moving spaces, not just boot image. Bug: 29530787
desired_load_kind = HLoadClass::LoadKind::kBootImageAddress;
address = reinterpret_cast64<uint64_t>(klass);
+ } else if (is_in_dex_cache) {
+ desired_load_kind = HLoadClass::LoadKind::kJitTableAddress;
+ // We store in the address field the location of the stack reference maintained
+ // by the handle. We do this now so that the code generation does not need to figure
+ // out which class loader to use.
+ address = reinterpret_cast<uint64_t>(handles->NewHandle(klass).GetReference());
} else {
- // Not JIT and either the klass is not in boot image or we are compiling in PIC mode.
- // Use PC-relative load from the dex cache if the dex file belongs
- // to the oat file that we're currently compiling.
- desired_load_kind =
- ContainsElement(compiler_driver_->GetDexFilesForOatFile(), &load_class->GetDexFile())
- ? HLoadClass::LoadKind::kDexCachePcRelative
- : HLoadClass::LoadKind::kDexCacheViaMethod;
+ // Class not loaded yet. This happens when the dex code requesting
+ // this `HLoadClass` hasn't been executed in the interpreter.
+ // Fallback to the dex cache.
+ // TODO(ngeoffray): Generate HDeoptimize instead.
+ desired_load_kind = HLoadClass::LoadKind::kDexCacheViaMethod;
}
+ } else if (is_in_boot_image && !codegen->GetCompilerOptions().GetCompilePic()) {
+ // AOT app compilation. Check if the class is in the boot image.
+ desired_load_kind = HLoadClass::LoadKind::kBootImageAddress;
+ address = reinterpret_cast64<uint64_t>(klass);
+ } else {
+ // Not JIT and either the klass is not in boot image or we are compiling in PIC mode.
+ // Use PC-relative load from the dex cache if the dex file belongs
+ // to the oat file that we're currently compiling.
+ desired_load_kind =
+ ContainsElement(compiler_driver->GetDexFilesForOatFile(), &load_class->GetDexFile())
+ ? HLoadClass::LoadKind::kDexCachePcRelative
+ : HLoadClass::LoadKind::kDexCacheViaMethod;
}
}
DCHECK_NE(desired_load_kind, static_cast<HLoadClass::LoadKind>(-1));
@@ -241,7 +251,7 @@
load_class->MarkInDexCache();
}
- HLoadClass::LoadKind load_kind = codegen_->GetSupportedLoadClassKind(desired_load_kind);
+ HLoadClass::LoadKind load_kind = codegen->GetSupportedLoadClassKind(desired_load_kind);
switch (load_kind) {
case HLoadClass::LoadKind::kBootImageLinkTimeAddress:
case HLoadClass::LoadKind::kBootImageLinkTimePcRelative:
@@ -254,7 +264,7 @@
load_class->SetLoadKindWithAddress(load_kind, address);
break;
case HLoadClass::LoadKind::kDexCachePcRelative: {
- PointerSize pointer_size = InstructionSetPointerSize(codegen_->GetInstructionSet());
+ PointerSize pointer_size = InstructionSetPointerSize(codegen->GetInstructionSet());
DexCacheArraysLayout layout(pointer_size, &dex_file);
size_t element_index = layout.TypeOffset(type_index);
load_class->SetLoadKindWithDexCacheReference(load_kind, dex_file, element_index);
diff --git a/compiler/optimizing/sharpening.h b/compiler/optimizing/sharpening.h
index 7418954..ae5ccb3 100644
--- a/compiler/optimizing/sharpening.h
+++ b/compiler/optimizing/sharpening.h
@@ -47,6 +47,14 @@
static constexpr const char* kSharpeningPassName = "sharpening";
+ // Used internally but also by the inliner.
+ static void SharpenClass(HLoadClass* load_class,
+ mirror::Class* klass,
+ VariableSizedHandleScope* handles,
+ CodeGenerator* codegen,
+ CompilerDriver* compiler_driver)
+ REQUIRES_SHARED(Locks::mutator_lock_);
+
private:
void ProcessInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke);
void ProcessLoadClass(HLoadClass* load_class);
diff --git a/compiler/optimizing/ssa_test.cc b/compiler/optimizing/ssa_test.cc
index 4297634..f69f417 100644
--- a/compiler/optimizing/ssa_test.cc
+++ b/compiler/optimizing/ssa_test.cc
@@ -14,8 +14,9 @@
* limitations under the License.
*/
+#include "android-base/stringprintf.h"
+
#include "base/arena_allocator.h"
-#include "base/stringprintf.h"
#include "builder.h"
#include "dex_file.h"
#include "dex_instruction.h"
@@ -35,7 +36,7 @@
explicit SsaPrettyPrinter(HGraph* graph) : HPrettyPrinter(graph), str_("") {}
void PrintInt(int value) OVERRIDE {
- str_ += StringPrintf("%d", value);
+ str_ += android::base::StringPrintf("%d", value);
}
void PrintString(const char* value) OVERRIDE {
diff --git a/compiler/utils/arm/assembler_thumb2_test.cc b/compiler/utils/arm/assembler_thumb2_test.cc
index 30e8f4e..0147a76 100644
--- a/compiler/utils/arm/assembler_thumb2_test.cc
+++ b/compiler/utils/arm/assembler_thumb2_test.cc
@@ -16,12 +16,15 @@
#include "assembler_thumb2.h"
+#include "android-base/stringprintf.h"
+
#include "base/stl_util.h"
-#include "base/stringprintf.h"
#include "utils/assembler_test.h"
namespace art {
+using android::base::StringPrintf;
+
class AssemblerThumb2Test : public AssemblerTest<arm::Thumb2Assembler,
arm::Register, arm::SRegister,
uint32_t> {
diff --git a/compiler/utils/arm/jni_macro_assembler_arm_vixl.cc b/compiler/utils/arm/jni_macro_assembler_arm_vixl.cc
index 4e64f13..d07c047 100644
--- a/compiler/utils/arm/jni_macro_assembler_arm_vixl.cc
+++ b/compiler/utils/arm/jni_macro_assembler_arm_vixl.cc
@@ -592,7 +592,9 @@
ExactAssemblyScope guard(asm_.GetVIXLAssembler(),
vixl32::kMaxInstructionSizeInBytes,
CodeBufferCheckScope::kMaximumSize);
- ___ b(ne, Narrow, exception_blocks_.back()->Entry());
+ vixl32::Label* label = exception_blocks_.back()->Entry();
+ ___ b(ne, Narrow, label);
+ ___ AddBranchLabel(label);
}
// TODO: think about using CBNZ here.
}
diff --git a/compiler/utils/assembler_thumb_test.cc b/compiler/utils/assembler_thumb_test.cc
index 50a1d9f..4e9b619 100644
--- a/compiler/utils/assembler_thumb_test.cc
+++ b/compiler/utils/assembler_thumb_test.cc
@@ -1717,6 +1717,11 @@
__ ExceptionPoll(scratch_register, 0);
+ // Push the target out of range of branch emitted by ExceptionPoll.
+ for (int i = 0; i < 64; i++) {
+ __ Store(FrameOffset(2047), scratch_register, 4);
+ }
+
__ DecreaseFrameSize(4096);
__ DecreaseFrameSize(32);
__ RemoveFrame(frame_size, callee_save_regs);
diff --git a/compiler/utils/assembler_thumb_test_expected.cc.inc b/compiler/utils/assembler_thumb_test_expected.cc.inc
index 69e1d8f..b16d99a 100644
--- a/compiler/utils/assembler_thumb_test_expected.cc.inc
+++ b/compiler/utils/assembler_thumb_test_expected.cc.inc
@@ -5458,94 +5458,160 @@
};
const char* const VixlJniHelpersResults[] = {
- " 0: e92d 4de0 stmdb sp!, {r5, r6, r7, r8, sl, fp, lr}\n",
- " 4: ed2d 8a10 vpush {s16-s31}\n",
- " 8: b089 sub sp, #36 ; 0x24\n",
- " a: 9000 str r0, [sp, #0]\n",
- " c: 9121 str r1, [sp, #132] ; 0x84\n",
- " e: ed8d 0a22 vstr s0, [sp, #136] ; 0x88\n",
- " 12: 9223 str r2, [sp, #140] ; 0x8c\n",
- " 14: 9324 str r3, [sp, #144] ; 0x90\n",
- " 16: b088 sub sp, #32\n",
- " 18: f5ad 5d80 sub.w sp, sp, #4096 ; 0x1000\n",
- " 1c: 9808 ldr r0, [sp, #32]\n",
- " 1e: 981f ldr r0, [sp, #124] ; 0x7c\n",
- " 20: 9821 ldr r0, [sp, #132] ; 0x84\n",
- " 22: 98ff ldr r0, [sp, #1020] ; 0x3fc\n",
- " 24: f8dd 0400 ldr.w r0, [sp, #1024] ; 0x400\n",
- " 28: f8dd cffc ldr.w ip, [sp, #4092] ; 0xffc\n",
- " 2c: f50d 5c80 add.w ip, sp, #4096 ; 0x1000\n",
- " 30: f8dc c000 ldr.w ip, [ip]\n",
- " 34: f8d9 c200 ldr.w ip, [r9, #512] ; 0x200\n",
- " 38: f8dc 0080 ldr.w r0, [ip, #128] ; 0x80\n",
- " 3c: 9008 str r0, [sp, #32]\n",
- " 3e: 901f str r0, [sp, #124] ; 0x7c\n",
- " 40: 9021 str r0, [sp, #132] ; 0x84\n",
- " 42: 90ff str r0, [sp, #1020] ; 0x3fc\n",
- " 44: f8cd 0400 str.w r0, [sp, #1024] ; 0x400\n",
- " 48: f8cd cffc str.w ip, [sp, #4092] ; 0xffc\n",
- " 4c: f84d 5d04 str.w r5, [sp, #-4]!\n",
- " 50: f50d 5580 add.w r5, sp, #4096 ; 0x1000\n",
- " 54: f8c5 c004 str.w ip, [r5, #4]\n",
- " 58: f85d 5b04 ldr.w r5, [sp], #4\n",
- " 5c: f04f 0cff mov.w ip, #255 ; 0xff\n",
- " 60: f8cd c030 str.w ip, [sp, #48] ; 0x30\n",
- " 64: f06f 4c7f mvn.w ip, #4278190080 ; 0xff000000\n",
- " 68: f8cd c030 str.w ip, [sp, #48] ; 0x30\n",
- " 6c: f8cd c030 str.w ip, [sp, #48] ; 0x30\n",
- " 70: f8cd c030 str.w ip, [sp, #48] ; 0x30\n",
- " 74: 900c str r0, [sp, #48] ; 0x30\n",
- " 76: f8dd c030 ldr.w ip, [sp, #48] ; 0x30\n",
- " 7a: f8cd c034 str.w ip, [sp, #52] ; 0x34\n",
- " 7e: f50d 5c80 add.w ip, sp, #4096 ; 0x1000\n",
- " 82: f8c9 c200 str.w ip, [r9, #512] ; 0x200\n",
- " 86: f8c9 d200 str.w sp, [r9, #512] ; 0x200\n",
- " 8a: f8d0 c030 ldr.w ip, [r0, #48] ; 0x30\n",
- " 8e: 47e0 blx ip\n",
- " 90: f8dd c02c ldr.w ip, [sp, #44] ; 0x2c\n",
- " 94: f8cd c030 str.w ip, [sp, #48] ; 0x30\n",
- " 98: f8d9 c200 ldr.w ip, [r9, #512] ; 0x200\n",
- " 9c: f8cd c02c str.w ip, [sp, #44] ; 0x2c\n",
- " a0: f8dd c02c ldr.w ip, [sp, #44] ; 0x2c\n",
- " a4: f8cd c030 str.w ip, [sp, #48] ; 0x30\n",
- " a8: 4648 mov r0, r9\n",
- " aa: f8cd 9030 str.w r9, [sp, #48] ; 0x30\n",
- " ae: 4684 mov ip, r0\n",
- " b0: f1bc 0f00 cmp.w ip, #0\n",
- " b4: bf18 it ne\n",
- " b6: f10d 0c30 addne.w ip, sp, #48 ; 0x30\n",
- " ba: f10d 0c30 add.w ip, sp, #48 ; 0x30\n",
- " be: f1bc 0f00 cmp.w ip, #0\n",
- " c2: bf0c ite eq\n",
- " c4: 2000 moveq r0, #0\n",
- " c6: a80c addne r0, sp, #48 ; 0x30\n",
- " c8: f8dd c040 ldr.w ip, [sp, #64] ; 0x40\n",
- " cc: f1bc 0f00 cmp.w ip, #0\n",
- " d0: bf18 it ne\n",
- " d2: f10d 0c40 addne.w ip, sp, #64 ; 0x40\n",
- " d6: f8cd c030 str.w ip, [sp, #48] ; 0x30\n",
- " da: f1bc 0f00 cmp.w ip, #0\n",
- " de: bf0c ite eq\n",
- " e0: 2000 moveq r0, #0\n",
- " e2: 4668 movne r0, sp\n",
- " e4: f1bc 0f00 cmp.w ip, #0\n",
- " e8: bf0c ite eq\n",
- " ea: 2000 moveq r0, #0\n",
- " ec: f20d 4001 addwne r0, sp, #1025 ; 0x401\n",
- " f0: f1bc 0f00 cmp.w ip, #0\n",
- " f4: bf18 it ne\n",
- " f6: f20d 4c01 addwne ip, sp, #1025 ; 0x401\n",
- " fa: f8d9 c084 ldr.w ip, [r9, #132] ; 0x84\n",
- " fe: f1bc 0f00 cmp.w ip, #0\n",
- " 102: d107 bne.n 114 <VixlJniHelpers+0x114>\n",
- " 104: f50d 5d80 add.w sp, sp, #4096 ; 0x1000\n",
- " 108: b008 add sp, #32\n",
- " 10a: b009 add sp, #36 ; 0x24\n",
- " 10c: ecbd 8a10 vpop {s16-s31}\n",
- " 110: e8bd 8de0 ldmia.w sp!, {r5, r6, r7, r8, sl, fp, pc}\n",
- " 114: 4660 mov r0, ip\n",
- " 116: f8d9 c2b0 ldr.w ip, [r9, #688] ; 0x2b0\n",
- " 11a: 47e0 blx ip\n",
+ " 0: e92d 4de0 stmdb sp!, {r5, r6, r7, r8, sl, fp, lr}\n",
+ " 4: ed2d 8a10 vpush {s16-s31}\n",
+ " 8: b089 sub sp, #36 ; 0x24\n",
+ " a: 9000 str r0, [sp, #0]\n",
+ " c: 9121 str r1, [sp, #132] ; 0x84\n",
+ " e: ed8d 0a22 vstr s0, [sp, #136] ; 0x88\n",
+ " 12: 9223 str r2, [sp, #140] ; 0x8c\n",
+ " 14: 9324 str r3, [sp, #144] ; 0x90\n",
+ " 16: b088 sub sp, #32\n",
+ " 18: f5ad 5d80 sub.w sp, sp, #4096 ; 0x1000\n",
+ " 1c: 9808 ldr r0, [sp, #32]\n",
+ " 1e: 981f ldr r0, [sp, #124] ; 0x7c\n",
+ " 20: 9821 ldr r0, [sp, #132] ; 0x84\n",
+ " 22: 98ff ldr r0, [sp, #1020] ; 0x3fc\n",
+ " 24: f8dd 0400 ldr.w r0, [sp, #1024] ; 0x400\n",
+ " 28: f8dd cffc ldr.w ip, [sp, #4092] ; 0xffc\n",
+ " 2c: f50d 5c80 add.w ip, sp, #4096 ; 0x1000\n",
+ " 30: f8dc c000 ldr.w ip, [ip]\n",
+ " 34: f8d9 c200 ldr.w ip, [r9, #512] ; 0x200\n",
+ " 38: f8dc 0080 ldr.w r0, [ip, #128] ; 0x80\n",
+ " 3c: 9008 str r0, [sp, #32]\n",
+ " 3e: 901f str r0, [sp, #124] ; 0x7c\n",
+ " 40: 9021 str r0, [sp, #132] ; 0x84\n",
+ " 42: 90ff str r0, [sp, #1020] ; 0x3fc\n",
+ " 44: f8cd 0400 str.w r0, [sp, #1024] ; 0x400\n",
+ " 48: f8cd cffc str.w ip, [sp, #4092] ; 0xffc\n",
+ " 4c: f84d 5d04 str.w r5, [sp, #-4]!\n",
+ " 50: f50d 5580 add.w r5, sp, #4096 ; 0x1000\n",
+ " 54: f8c5 c004 str.w ip, [r5, #4]\n",
+ " 58: f85d 5b04 ldr.w r5, [sp], #4\n",
+ " 5c: f04f 0cff mov.w ip, #255 ; 0xff\n",
+ " 60: f8cd c030 str.w ip, [sp, #48] ; 0x30\n",
+ " 64: f06f 4c7f mvn.w ip, #4278190080 ; 0xff000000\n",
+ " 68: f8cd c030 str.w ip, [sp, #48] ; 0x30\n",
+ " 6c: f8cd c030 str.w ip, [sp, #48] ; 0x30\n",
+ " 70: f8cd c030 str.w ip, [sp, #48] ; 0x30\n",
+ " 74: 900c str r0, [sp, #48] ; 0x30\n",
+ " 76: f8dd c030 ldr.w ip, [sp, #48] ; 0x30\n",
+ " 7a: f8cd c034 str.w ip, [sp, #52] ; 0x34\n",
+ " 7e: f50d 5c80 add.w ip, sp, #4096 ; 0x1000\n",
+ " 82: f8c9 c200 str.w ip, [r9, #512] ; 0x200\n",
+ " 86: f8c9 d200 str.w sp, [r9, #512] ; 0x200\n",
+ " 8a: f8d0 c030 ldr.w ip, [r0, #48] ; 0x30\n",
+ " 8e: 47e0 blx ip\n",
+ " 90: f8dd c02c ldr.w ip, [sp, #44] ; 0x2c\n",
+ " 94: f8cd c030 str.w ip, [sp, #48] ; 0x30\n",
+ " 98: f8d9 c200 ldr.w ip, [r9, #512] ; 0x200\n",
+ " 9c: f8cd c02c str.w ip, [sp, #44] ; 0x2c\n",
+ " a0: f8dd c02c ldr.w ip, [sp, #44] ; 0x2c\n",
+ " a4: f8cd c030 str.w ip, [sp, #48] ; 0x30\n",
+ " a8: 4648 mov r0, r9\n",
+ " aa: f8cd 9030 str.w r9, [sp, #48] ; 0x30\n",
+ " ae: 4684 mov ip, r0\n",
+ " b0: f1bc 0f00 cmp.w ip, #0\n",
+ " b4: bf18 it ne\n",
+ " b6: f10d 0c30 addne.w ip, sp, #48 ; 0x30\n",
+ " ba: f10d 0c30 add.w ip, sp, #48 ; 0x30\n",
+ " be: f1bc 0f00 cmp.w ip, #0\n",
+ " c2: bf0c ite eq\n",
+ " c4: 2000 moveq r0, #0\n",
+ " c6: a80c addne r0, sp, #48 ; 0x30\n",
+ " c8: f8dd c040 ldr.w ip, [sp, #64] ; 0x40\n",
+ " cc: f1bc 0f00 cmp.w ip, #0\n",
+ " d0: bf18 it ne\n",
+ " d2: f10d 0c40 addne.w ip, sp, #64 ; 0x40\n",
+ " d6: f8cd c030 str.w ip, [sp, #48] ; 0x30\n",
+ " da: f1bc 0f00 cmp.w ip, #0\n",
+ " de: bf0c ite eq\n",
+ " e0: 2000 moveq r0, #0\n",
+ " e2: 4668 movne r0, sp\n",
+ " e4: f1bc 0f00 cmp.w ip, #0\n",
+ " e8: bf0c ite eq\n",
+ " ea: 2000 moveq r0, #0\n",
+ " ec: f20d 4001 addwne r0, sp, #1025 ; 0x401\n",
+ " f0: f1bc 0f00 cmp.w ip, #0\n",
+ " f4: bf18 it ne\n",
+ " f6: f20d 4c01 addwne ip, sp, #1025 ; 0x401\n",
+ " fa: f8d9 c084 ldr.w ip, [r9, #132] ; 0x84\n",
+ " fe: f1bc 0f00 cmp.w ip, #0\n",
+ " 102: d16f bne.n 1e4 <VixlJniHelpers+0x1e4>\n",
+ " 104: f8cd c7ff str.w ip, [sp, #2047] ; 0x7ff\n",
+ " 108: f8cd c7ff str.w ip, [sp, #2047] ; 0x7ff\n",
+ " 10c: f8cd c7ff str.w ip, [sp, #2047] ; 0x7ff\n",
+ " 110: f8cd c7ff str.w ip, [sp, #2047] ; 0x7ff\n",
+ " 114: f8cd c7ff str.w ip, [sp, #2047] ; 0x7ff\n",
+ " 118: f8cd c7ff str.w ip, [sp, #2047] ; 0x7ff\n",
+ " 11c: f8cd c7ff str.w ip, [sp, #2047] ; 0x7ff\n",
+ " 120: f8cd c7ff str.w ip, [sp, #2047] ; 0x7ff\n",
+ " 124: f8cd c7ff str.w ip, [sp, #2047] ; 0x7ff\n",
+ " 128: f8cd c7ff str.w ip, [sp, #2047] ; 0x7ff\n",
+ " 12c: f8cd c7ff str.w ip, [sp, #2047] ; 0x7ff\n",
+ " 130: f8cd c7ff str.w ip, [sp, #2047] ; 0x7ff\n",
+ " 134: f8cd c7ff str.w ip, [sp, #2047] ; 0x7ff\n",
+ " 138: f8cd c7ff str.w ip, [sp, #2047] ; 0x7ff\n",
+ " 13c: f8cd c7ff str.w ip, [sp, #2047] ; 0x7ff\n",
+ " 140: f8cd c7ff str.w ip, [sp, #2047] ; 0x7ff\n",
+ " 144: f8cd c7ff str.w ip, [sp, #2047] ; 0x7ff\n",
+ " 148: f8cd c7ff str.w ip, [sp, #2047] ; 0x7ff\n",
+ " 14c: f8cd c7ff str.w ip, [sp, #2047] ; 0x7ff\n",
+ " 150: f8cd c7ff str.w ip, [sp, #2047] ; 0x7ff\n",
+ " 154: f8cd c7ff str.w ip, [sp, #2047] ; 0x7ff\n",
+ " 158: f8cd c7ff str.w ip, [sp, #2047] ; 0x7ff\n",
+ " 15c: f8cd c7ff str.w ip, [sp, #2047] ; 0x7ff\n",
+ " 160: f8cd c7ff str.w ip, [sp, #2047] ; 0x7ff\n",
+ " 164: f8cd c7ff str.w ip, [sp, #2047] ; 0x7ff\n",
+ " 168: f8cd c7ff str.w ip, [sp, #2047] ; 0x7ff\n",
+ " 16c: f8cd c7ff str.w ip, [sp, #2047] ; 0x7ff\n",
+ " 170: f8cd c7ff str.w ip, [sp, #2047] ; 0x7ff\n",
+ " 174: f8cd c7ff str.w ip, [sp, #2047] ; 0x7ff\n",
+ " 178: f8cd c7ff str.w ip, [sp, #2047] ; 0x7ff\n",
+ " 17c: f8cd c7ff str.w ip, [sp, #2047] ; 0x7ff\n",
+ " 180: f8cd c7ff str.w ip, [sp, #2047] ; 0x7ff\n",
+ " 184: f8cd c7ff str.w ip, [sp, #2047] ; 0x7ff\n",
+ " 188: f8cd c7ff str.w ip, [sp, #2047] ; 0x7ff\n",
+ " 18c: f8cd c7ff str.w ip, [sp, #2047] ; 0x7ff\n",
+ " 190: f8cd c7ff str.w ip, [sp, #2047] ; 0x7ff\n",
+ " 194: f8cd c7ff str.w ip, [sp, #2047] ; 0x7ff\n",
+ " 198: f8cd c7ff str.w ip, [sp, #2047] ; 0x7ff\n",
+ " 19c: f8cd c7ff str.w ip, [sp, #2047] ; 0x7ff\n",
+ " 1a0: f8cd c7ff str.w ip, [sp, #2047] ; 0x7ff\n",
+ " 1a4: f8cd c7ff str.w ip, [sp, #2047] ; 0x7ff\n",
+ " 1a8: f8cd c7ff str.w ip, [sp, #2047] ; 0x7ff\n",
+ " 1ac: f8cd c7ff str.w ip, [sp, #2047] ; 0x7ff\n",
+ " 1b0: f8cd c7ff str.w ip, [sp, #2047] ; 0x7ff\n",
+ " 1b4: f8cd c7ff str.w ip, [sp, #2047] ; 0x7ff\n",
+ " 1b8: f8cd c7ff str.w ip, [sp, #2047] ; 0x7ff\n",
+ " 1bc: f8cd c7ff str.w ip, [sp, #2047] ; 0x7ff\n",
+ " 1c0: f8cd c7ff str.w ip, [sp, #2047] ; 0x7ff\n",
+ " 1c4: f8cd c7ff str.w ip, [sp, #2047] ; 0x7ff\n",
+ " 1c8: f8cd c7ff str.w ip, [sp, #2047] ; 0x7ff\n",
+ " 1cc: f8cd c7ff str.w ip, [sp, #2047] ; 0x7ff\n",
+ " 1d0: f8cd c7ff str.w ip, [sp, #2047] ; 0x7ff\n",
+ " 1d4: f8cd c7ff str.w ip, [sp, #2047] ; 0x7ff\n",
+ " 1d8: f8cd c7ff str.w ip, [sp, #2047] ; 0x7ff\n",
+ " 1dc: f8cd c7ff str.w ip, [sp, #2047] ; 0x7ff\n",
+ " 1e0: f000 b802 b.w 1e8 <VixlJniHelpers+0x1e8>\n",
+ " 1e4: f000 b81a b.w 21c <VixlJniHelpers+0x21c>\n",
+ " 1e8: f8cd c7ff str.w ip, [sp, #2047] ; 0x7ff\n",
+ " 1ec: f8cd c7ff str.w ip, [sp, #2047] ; 0x7ff\n",
+ " 1f0: f8cd c7ff str.w ip, [sp, #2047] ; 0x7ff\n",
+ " 1f4: f8cd c7ff str.w ip, [sp, #2047] ; 0x7ff\n",
+ " 1f8: f8cd c7ff str.w ip, [sp, #2047] ; 0x7ff\n",
+ " 1fc: f8cd c7ff str.w ip, [sp, #2047] ; 0x7ff\n",
+ " 200: f8cd c7ff str.w ip, [sp, #2047] ; 0x7ff\n",
+ " 204: f8cd c7ff str.w ip, [sp, #2047] ; 0x7ff\n",
+ " 208: f8cd c7ff str.w ip, [sp, #2047] ; 0x7ff\n",
+ " 20c: f50d 5d80 add.w sp, sp, #4096 ; 0x1000\n",
+ " 210: b008 add sp, #32\n",
+ " 212: b009 add sp, #36 ; 0x24\n",
+ " 214: ecbd 8a10 vpop {s16-s31}\n",
+ " 218: e8bd 8de0 ldmia.w sp!, {r5, r6, r7, r8, sl, fp, pc}\n",
+ " 21c: 4660 mov r0, ip\n",
+ " 21e: f8d9 c2b0 ldr.w ip, [r9, #688] ; 0x2b0\n",
+ " 222: 47e0 blx ip\n",
nullptr
};
diff --git a/compiler/utils/dedupe_set-inl.h b/compiler/utils/dedupe_set-inl.h
index ac54813..c06e9ca 100644
--- a/compiler/utils/dedupe_set-inl.h
+++ b/compiler/utils/dedupe_set-inl.h
@@ -23,10 +23,11 @@
#include <inttypes.h>
#include <unordered_map>
+#include "android-base/stringprintf.h"
+
#include "base/mutex.h"
#include "base/hash_set.h"
#include "base/stl_util.h"
-#include "base/stringprintf.h"
#include "base/time_utils.h"
namespace art {
@@ -238,13 +239,13 @@
for (HashType shard = 0; shard < kShard; ++shard) {
shards_[shard]->UpdateStats(self, &stats);
}
- return StringPrintf("%zu collisions, %zu max hash collisions, "
- "%zu/%zu probe distance, %" PRIu64 " ns hash time",
- stats.collision_sum,
- stats.collision_max,
- stats.total_probe_distance,
- stats.total_size,
- hash_time_);
+ return android::base::StringPrintf("%zu collisions, %zu max hash collisions, "
+ "%zu/%zu probe distance, %" PRIu64 " ns hash time",
+ stats.collision_sum,
+ stats.collision_max,
+ stats.total_probe_distance,
+ stats.total_size,
+ hash_time_);
}
diff --git a/compiler/utils/mips64/assembler_mips64.cc b/compiler/utils/mips64/assembler_mips64.cc
index 84280b9..04430b1 100644
--- a/compiler/utils/mips64/assembler_mips64.cc
+++ b/compiler/utils/mips64/assembler_mips64.cc
@@ -1829,10 +1829,10 @@
// Far label.
case Branch::kFarLabel:
- offset += (offset & 0x8000) << 1; // Account for sign extension in addiu.
+ offset += (offset & 0x8000) << 1; // Account for sign extension in daddiu.
CHECK_EQ(overwrite_location_, branch->GetOffsetLocation());
Auipc(AT, High16Bits(offset));
- Addiu(lhs, AT, Low16Bits(offset));
+ Daddiu(lhs, AT, Low16Bits(offset));
break;
// Far literals.
case Branch::kFarLiteral:
diff --git a/compiler/utils/mips64/assembler_mips64_test.cc b/compiler/utils/mips64/assembler_mips64_test.cc
index 31d3e4c..9d0d0fc 100644
--- a/compiler/utils/mips64/assembler_mips64_test.cc
+++ b/compiler/utils/mips64/assembler_mips64_test.cc
@@ -605,15 +605,18 @@
}
TEST_F(AssemblerMIPS64Test, LongBalc) {
+ constexpr uint32_t kNopCount1 = (1u << 25) + 1;
+ constexpr uint32_t kNopCount2 = (1u << 25) + 1;
+ constexpr uint32_t kRequiredCapacity = (kNopCount1 + kNopCount2 + 6u) * 4u;
+ ASSERT_LT(__ GetBuffer()->Capacity(), kRequiredCapacity);
+ __ GetBuffer()->ExtendCapacity(kRequiredCapacity);
mips64::Mips64Label label1, label2;
__ Balc(&label1);
- constexpr uint32_t kNopCount1 = (1u << 25) + 1;
for (uint32_t i = 0; i != kNopCount1; ++i) {
__ Nop();
}
__ Bind(&label1);
__ Balc(&label2);
- constexpr uint32_t kNopCount2 = (1u << 25) + 1;
for (uint32_t i = 0; i != kNopCount2; ++i) {
__ Nop();
}
@@ -876,7 +879,7 @@
std::string expected =
"1:\n"
"auipc $at, %hi(2f - 1b)\n"
- "addiu $v0, $at, %lo(2f - 1b)\n" +
+ "daddiu $v0, $at, %lo(2f - 1b)\n" +
RepeatInsn(kAdduCount, "addu $zero, $zero, $zero\n") +
"2:\n";
DriverStr(expected, "LoadNearestFarLabelAddress");
@@ -1067,7 +1070,7 @@
"ld $v0, %lo(3f - 1b)($at)\n"
"2:\n"
"auipc $at, %hi(3f - 2b)\n"
- "addiu $v1, $at, %lo(3f - 2b)\n" +
+ "daddiu $v1, $at, %lo(3f - 2b)\n" +
RepeatInsn(kAdduCount, "addu $zero, $zero, $zero\n") +
"nop\n"
"3:\n"
diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc
index 5a0f0c6..2346635 100644
--- a/dex2oat/dex2oat.cc
+++ b/dex2oat/dex2oat.cc
@@ -33,6 +33,7 @@
#include <sys/utsname.h>
#endif
+#include "android-base/stringprintf.h"
#include "android-base/strings.h"
#include "arch/instruction_set_features.h"
@@ -87,6 +88,9 @@
namespace art {
+using android::base::StringAppendV;
+using android::base::StringPrintf;
+
static constexpr size_t kDefaultMinDexFilesForSwap = 2;
static constexpr size_t kDefaultMinDexFileCumulativeSizeForSwap = 20 * MB;
diff --git a/dex2oat/dex2oat_test.cc b/dex2oat/dex2oat_test.cc
index b6b62a8..cdb3b9f 100644
--- a/dex2oat/dex2oat_test.cc
+++ b/dex2oat/dex2oat_test.cc
@@ -19,11 +19,15 @@
#include <string>
#include <vector>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include "android-base/stringprintf.h"
+
#include "common_runtime_test.h"
#include "base/logging.h"
#include "base/macros.h"
-#include "base/stringprintf.h"
#include "dex_file-inl.h"
#include "dex2oat_environment_test.h"
#include "jit/offline_profiling_info.h"
@@ -31,9 +35,6 @@
#include "oat_file.h"
#include "utils.h"
-#include <sys/wait.h>
-#include <unistd.h>
-
namespace art {
class Dex2oatTest : public Dex2oatEnvironmentTest {
@@ -217,7 +218,7 @@
std::unique_ptr<ScratchFile> sf;
if (use_fd) {
sf.reset(new ScratchFile());
- copy.push_back(StringPrintf("--swap-fd=%d", sf->GetFd()));
+ copy.push_back(android::base::StringPrintf("--swap-fd=%d", sf->GetFd()));
} else {
std::string swap_location = GetOdexDir() + "/Dex2OatSwapTest.odex.swap";
copy.push_back("--swap-file=" + swap_location);
diff --git a/dexdump/dexdump.cc b/dexdump/dexdump.cc
index 916984c..d5776fa 100644
--- a/dexdump/dexdump.cc
+++ b/dexdump/dexdump.cc
@@ -42,7 +42,8 @@
#include <sstream>
#include <vector>
-#include "base/stringprintf.h"
+#include "android-base/stringprintf.h"
+
#include "dexdump_cfg.h"
#include "dex_file-inl.h"
#include "dex_file_types.h"
@@ -887,8 +888,10 @@
const char* name = pDexFile->StringDataByIdx(pMethodId.name_idx_);
const Signature signature = pDexFile->GetMethodSignature(pMethodId);
const char* backDescriptor = pDexFile->StringByTypeIdx(pMethodId.class_idx_);
- method = StringPrintf("%s.%s:%s",
- backDescriptor, name, signature.ToString().c_str());
+ method = android::base::StringPrintf("%s.%s:%s",
+ backDescriptor,
+ name,
+ signature.ToString().c_str());
}
if (secondary_index < pDexFile->GetHeader().proto_ids_size_) {
const DexFile::ProtoId& protoId = pDexFile->GetProtoId(secondary_index);
diff --git a/dexdump/dexdump_test.cc b/dexdump/dexdump_test.cc
index d28ca28..53dda6a 100644
--- a/dexdump/dexdump_test.cc
+++ b/dexdump/dexdump_test.cc
@@ -21,7 +21,6 @@
#include <sys/types.h>
#include <unistd.h>
-#include "base/stringprintf.h"
#include "common_runtime_test.h"
#include "runtime/arch/instruction_set.h"
#include "runtime/os.h"
diff --git a/dexlayout/dexlayout.cc b/dexlayout/dexlayout.cc
index cfe4837..cac6090 100644
--- a/dexlayout/dexlayout.cc
+++ b/dexlayout/dexlayout.cc
@@ -30,7 +30,8 @@
#include <sstream>
#include <vector>
-#include "base/stringprintf.h"
+#include "android-base/stringprintf.h"
+
#include "dex_ir_builder.h"
#include "dex_file-inl.h"
#include "dex_instruction-inl.h"
@@ -43,6 +44,8 @@
namespace art {
+using android::base::StringPrintf;
+
/*
* Flags for use with createAccessFlagStr().
*/
diff --git a/dexlayout/dexlayout_test.cc b/dexlayout/dexlayout_test.cc
index 665baa6..46a1c43 100644
--- a/dexlayout/dexlayout_test.cc
+++ b/dexlayout/dexlayout_test.cc
@@ -21,7 +21,6 @@
#include <sys/types.h>
#include <unistd.h>
-#include "base/stringprintf.h"
#include "base/unix_file/fd_file.h"
#include "common_runtime_test.h"
#include "utils.h"
diff --git a/dexlist/dexlist_test.cc b/dexlist/dexlist_test.cc
index da1dd7f..1320942 100644
--- a/dexlist/dexlist_test.cc
+++ b/dexlist/dexlist_test.cc
@@ -21,7 +21,6 @@
#include <sys/types.h>
#include <unistd.h>
-#include "base/stringprintf.h"
#include "common_runtime_test.h"
#include "runtime/arch/instruction_set.h"
#include "runtime/gc/heap.h"
diff --git a/imgdiag/imgdiag.cc b/imgdiag/imgdiag.cc
index a374686..f307cbc 100644
--- a/imgdiag/imgdiag.cc
+++ b/imgdiag/imgdiag.cc
@@ -26,9 +26,10 @@
#include <map>
#include <unordered_set>
+#include "android-base/stringprintf.h"
+
#include "art_method-inl.h"
#include "base/unix_file/fd_file.h"
-#include "base/stringprintf.h"
#include "gc/space/image_space.h"
#include "gc/heap.h"
#include "mirror/class-inl.h"
@@ -46,6 +47,8 @@
namespace art {
+using android::base::StringPrintf;
+
class ImgDiagDumper {
public:
explicit ImgDiagDumper(std::ostream* os,
diff --git a/imgdiag/imgdiag_test.cc b/imgdiag/imgdiag_test.cc
index 9f771ba..3f2afc0 100644
--- a/imgdiag/imgdiag_test.cc
+++ b/imgdiag/imgdiag_test.cc
@@ -20,12 +20,13 @@
#include "common_runtime_test.h"
+#include "android-base/stringprintf.h"
+
#include "runtime/os.h"
#include "runtime/arch/instruction_set.h"
#include "runtime/utils.h"
#include "runtime/gc/space/image_space.h"
#include "runtime/gc/heap.h"
-#include "base/stringprintf.h"
#include <sys/types.h>
#include <unistd.h>
@@ -57,7 +58,7 @@
virtual void SetUpRuntimeOptions(RuntimeOptions* options) OVERRIDE {
// Needs to live until CommonRuntimeTest::SetUp finishes, since we pass it a cstring.
- runtime_args_image_ = StringPrintf("-Ximage:%s", GetCoreArtLocation().c_str());
+ runtime_args_image_ = android::base::StringPrintf("-Ximage:%s", GetCoreArtLocation().c_str());
options->push_back(std::make_pair(runtime_args_image_, nullptr));
}
diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc
index e4462d8..148ee88 100644
--- a/oatdump/oatdump.cc
+++ b/oatdump/oatdump.cc
@@ -26,6 +26,7 @@
#include <unordered_set>
#include <vector>
+#include "android-base/stringprintf.h"
#include "android-base/strings.h"
#include "arch/instruction_set_features.h"
@@ -76,6 +77,8 @@
namespace art {
+using android::base::StringPrintf;
+
const char* image_methods_descriptions_[] = {
"kResolutionMethod",
"kImtConflictMethod",
@@ -89,6 +92,7 @@
const char* image_roots_descriptions_[] = {
"kDexCaches",
"kClassRoots",
+ "kClassLoader",
};
// Map is so that we don't allocate multiple dex files for the same OatDexFile.
@@ -1506,12 +1510,13 @@
os << "ROOTS: " << reinterpret_cast<void*>(image_header_.GetImageRoots()) << "\n";
static_assert(arraysize(image_roots_descriptions_) ==
static_cast<size_t>(ImageHeader::kImageRootsMax), "sizes must match");
- for (int i = 0; i < ImageHeader::kImageRootsMax; i++) {
+ DCHECK_LE(image_header_.GetImageRoots()->GetLength(), ImageHeader::kImageRootsMax);
+ for (int32_t i = 0, size = image_header_.GetImageRoots()->GetLength(); i != size; ++i) {
ImageHeader::ImageRoot image_root = static_cast<ImageHeader::ImageRoot>(i);
const char* image_root_description = image_roots_descriptions_[i];
mirror::Object* image_root_object = image_header_.GetImageRoot(image_root);
indent_os << StringPrintf("%s: %p\n", image_root_description, image_root_object);
- if (image_root_object->IsObjectArray()) {
+ if (image_root_object != nullptr && image_root_object->IsObjectArray()) {
mirror::ObjectArray<mirror::Object>* image_root_object_array
= image_root_object->AsObjectArray<mirror::Object>();
ScopedIndentation indent2(&vios_);
diff --git a/oatdump/oatdump_test.cc b/oatdump/oatdump_test.cc
index a2eba45..e77d03b 100644
--- a/oatdump/oatdump_test.cc
+++ b/oatdump/oatdump_test.cc
@@ -22,7 +22,6 @@
#include "common_runtime_test.h"
-#include "base/stringprintf.h"
#include "base/unix_file/fd_file.h"
#include "runtime/arch/instruction_set.h"
#include "runtime/gc/heap.h"
diff --git a/patchoat/patchoat.cc b/patchoat/patchoat.cc
index 62d1ddf..7ae13a5 100644
--- a/patchoat/patchoat.cc
+++ b/patchoat/patchoat.cc
@@ -24,6 +24,7 @@
#include <string>
#include <vector>
+#include "android-base/stringprintf.h"
#include "android-base/strings.h"
#include "art_field-inl.h"
@@ -31,7 +32,6 @@
#include "base/dumpable.h"
#include "base/scoped_flock.h"
#include "base/stringpiece.h"
-#include "base/stringprintf.h"
#include "base/unix_file/fd_file.h"
#include "base/unix_file/random_access_file_utils.h"
#include "elf_utils.h"
@@ -939,7 +939,7 @@
static void UsageErrorV(const char* fmt, va_list ap) {
std::string error;
- StringAppendV(&error, fmt, ap);
+ android::base::StringAppendV(&error, fmt, ap);
LOG(ERROR) << error;
}
diff --git a/profman/profman.cc b/profman/profman.cc
index 0b2d172..e538407 100644
--- a/profman/profman.cc
+++ b/profman/profman.cc
@@ -25,12 +25,12 @@
#include <string>
#include <vector>
+#include "android-base/stringprintf.h"
#include "android-base/strings.h"
#include "base/dumpable.h"
#include "base/scoped_flock.h"
#include "base/stringpiece.h"
-#include "base/stringprintf.h"
#include "base/time_utils.h"
#include "base/unix_file/fd_file.h"
#include "dex_file.h"
@@ -61,7 +61,7 @@
static void UsageErrorV(const char* fmt, va_list ap) {
std::string error;
- StringAppendV(&error, fmt, ap);
+ android::base::StringAppendV(&error, fmt, ap);
LOG(ERROR) << error;
}
diff --git a/runtime/Android.bp b/runtime/Android.bp
index 32ebee2..86019bf 100644
--- a/runtime/Android.bp
+++ b/runtime/Android.bp
@@ -40,7 +40,6 @@
"base/scoped_arena_allocator.cc",
"base/scoped_flock.cc",
"base/stringpiece.cc",
- "base/stringprintf.cc",
"base/time_utils.cc",
"base/timing_logger.cc",
"base/unix_file/fd_file.cc",
@@ -508,7 +507,6 @@
"base/histogram_test.cc",
"base/mutex_test.cc",
"base/scoped_flock_test.cc",
- "base/stringprintf_test.cc",
"base/time_utils_test.cc",
"base/timing_logger_test.cc",
"base/transform_array_ref_test.cc",
diff --git a/runtime/arch/arm/instruction_set_features_arm.cc b/runtime/arch/arm/instruction_set_features_arm.cc
index f264b82..181b2ed 100644
--- a/runtime/arch/arm/instruction_set_features_arm.cc
+++ b/runtime/arch/arm/instruction_set_features_arm.cc
@@ -24,10 +24,10 @@
#include "signal.h"
#include <fstream>
+#include "android-base/stringprintf.h"
#include "android-base/strings.h"
-#include "base/stringprintf.h"
-#include "utils.h" // For Trim.
+#include "base/logging.h"
#if defined(__arm__)
extern "C" bool artCheckForArmSdivInstruction();
@@ -35,6 +35,8 @@
namespace art {
+using android::base::StringPrintf;
+
ArmFeaturesUniquePtr ArmInstructionSetFeatures::FromVariant(
const std::string& variant, std::string* error_msg) {
// Assume all ARM processors are SMP.
diff --git a/runtime/arch/arm64/instruction_set_features_arm64.cc b/runtime/arch/arm64/instruction_set_features_arm64.cc
index f7b5a76..52d8b3e 100644
--- a/runtime/arch/arm64/instruction_set_features_arm64.cc
+++ b/runtime/arch/arm64/instruction_set_features_arm64.cc
@@ -19,14 +19,16 @@
#include <fstream>
#include <sstream>
+#include "android-base/stringprintf.h"
#include "android-base/strings.h"
+#include "base/logging.h"
#include "base/stl_util.h"
-#include "base/stringprintf.h"
-#include "utils.h" // For Trim.
namespace art {
+using android::base::StringPrintf;
+
Arm64FeaturesUniquePtr Arm64InstructionSetFeatures::FromVariant(
const std::string& variant, std::string* error_msg) {
const bool smp = true; // Conservative default.
diff --git a/runtime/arch/instruction_set_features_test.cc b/runtime/arch/instruction_set_features_test.cc
index 0b8e531..d489392 100644
--- a/runtime/arch/instruction_set_features_test.cc
+++ b/runtime/arch/instruction_set_features_test.cc
@@ -22,11 +22,14 @@
#include "android-base/properties.h"
#endif
+#include "android-base/stringprintf.h"
+
#include "base/logging.h"
-#include "base/stringprintf.h"
namespace art {
+using android::base::StringPrintf;
+
#ifdef ART_TARGET_ANDROID
#if defined(__aarch64__)
TEST(InstructionSetFeaturesTest, DISABLED_FeaturesFromSystemPropertyVariant) {
diff --git a/runtime/arch/instruction_set_test.cc b/runtime/arch/instruction_set_test.cc
index 5dfc4b4..5aae93a 100644
--- a/runtime/arch/instruction_set_test.cc
+++ b/runtime/arch/instruction_set_test.cc
@@ -19,7 +19,6 @@
#include <gtest/gtest.h>
#include "base/enums.h"
-#include "base/stringprintf.h"
namespace art {
diff --git a/runtime/arch/mips/instruction_set_features_mips.cc b/runtime/arch/mips/instruction_set_features_mips.cc
index a65c967..5b50573 100644
--- a/runtime/arch/mips/instruction_set_features_mips.cc
+++ b/runtime/arch/mips/instruction_set_features_mips.cc
@@ -19,14 +19,16 @@
#include <fstream>
#include <sstream>
+#include "android-base/stringprintf.h"
#include "android-base/strings.h"
+#include "base/logging.h"
#include "base/stl_util.h"
-#include "base/stringprintf.h"
-#include "utils.h" // For Trim.
namespace art {
+using android::base::StringPrintf;
+
// An enum for the Mips revision.
enum class MipsLevel {
kBase,
diff --git a/runtime/arch/mips64/instruction_set_features_mips64.cc b/runtime/arch/mips64/instruction_set_features_mips64.cc
index e564d1e..92c44e8 100644
--- a/runtime/arch/mips64/instruction_set_features_mips64.cc
+++ b/runtime/arch/mips64/instruction_set_features_mips64.cc
@@ -19,13 +19,15 @@
#include <fstream>
#include <sstream>
+#include "android-base/stringprintf.h"
#include "android-base/strings.h"
-#include "base/stringprintf.h"
-#include "utils.h" // For Trim.
+#include "base/logging.h"
namespace art {
+using android::base::StringPrintf;
+
Mips64FeaturesUniquePtr Mips64InstructionSetFeatures::FromVariant(
const std::string& variant, std::string* error_msg ATTRIBUTE_UNUSED) {
if (variant != "default" && variant != "mips64r6") {
diff --git a/runtime/arch/x86/instruction_set_features_x86.cc b/runtime/arch/x86/instruction_set_features_x86.cc
index cc102ec..c520d63 100644
--- a/runtime/arch/x86/instruction_set_features_x86.cc
+++ b/runtime/arch/x86/instruction_set_features_x86.cc
@@ -19,14 +19,16 @@
#include <fstream>
#include <sstream>
+#include "android-base/stringprintf.h"
#include "android-base/strings.h"
#include "arch/x86_64/instruction_set_features_x86_64.h"
-#include "base/stringprintf.h"
-#include "utils.h" // For Trim.
+#include "base/logging.h"
namespace art {
+using android::base::StringPrintf;
+
// Feature-support arrays.
static constexpr const char* x86_known_variants[] = {
diff --git a/runtime/art_method.cc b/runtime/art_method.cc
index 96b6f18..dfc7837 100644
--- a/runtime/art_method.cc
+++ b/runtime/art_method.cc
@@ -18,6 +18,8 @@
#include <cstddef>
+#include "android-base/stringprintf.h"
+
#include "arch/context.h"
#include "art_field-inl.h"
#include "art_method-inl.h"
@@ -46,6 +48,8 @@
namespace art {
+using android::base::StringPrintf;
+
extern "C" void art_quick_invoke_stub(ArtMethod*, uint32_t*, uint32_t, Thread*, JValue*,
const char*);
extern "C" void art_quick_invoke_static_stub(ArtMethod*, uint32_t*, uint32_t, Thread*, JValue*,
diff --git a/runtime/asm_support.h b/runtime/asm_support.h
index 5ef1f06..e4972da 100644
--- a/runtime/asm_support.h
+++ b/runtime/asm_support.h
@@ -175,19 +175,19 @@
#define MIRROR_CLASS_IF_TABLE_OFFSET (16 + MIRROR_OBJECT_HEADER_SIZE)
ADD_TEST_EQ(MIRROR_CLASS_IF_TABLE_OFFSET,
art::mirror::Class::IfTableOffset().Int32Value())
-#define MIRROR_CLASS_ACCESS_FLAGS_OFFSET (64 + MIRROR_OBJECT_HEADER_SIZE)
+#define MIRROR_CLASS_ACCESS_FLAGS_OFFSET (56 + MIRROR_OBJECT_HEADER_SIZE)
ADD_TEST_EQ(MIRROR_CLASS_ACCESS_FLAGS_OFFSET,
art::mirror::Class::AccessFlagsOffset().Int32Value())
-#define MIRROR_CLASS_OBJECT_SIZE_OFFSET (96 + MIRROR_OBJECT_HEADER_SIZE)
+#define MIRROR_CLASS_OBJECT_SIZE_OFFSET (88 + MIRROR_OBJECT_HEADER_SIZE)
ADD_TEST_EQ(MIRROR_CLASS_OBJECT_SIZE_OFFSET,
art::mirror::Class::ObjectSizeOffset().Int32Value())
-#define MIRROR_CLASS_OBJECT_SIZE_ALLOC_FAST_PATH_OFFSET (100 + MIRROR_OBJECT_HEADER_SIZE)
+#define MIRROR_CLASS_OBJECT_SIZE_ALLOC_FAST_PATH_OFFSET (92 + MIRROR_OBJECT_HEADER_SIZE)
ADD_TEST_EQ(MIRROR_CLASS_OBJECT_SIZE_ALLOC_FAST_PATH_OFFSET,
art::mirror::Class::ObjectSizeAllocFastPathOffset().Int32Value())
-#define MIRROR_CLASS_OBJECT_PRIMITIVE_TYPE_OFFSET (104 + MIRROR_OBJECT_HEADER_SIZE)
+#define MIRROR_CLASS_OBJECT_PRIMITIVE_TYPE_OFFSET (96 + MIRROR_OBJECT_HEADER_SIZE)
ADD_TEST_EQ(MIRROR_CLASS_OBJECT_PRIMITIVE_TYPE_OFFSET,
art::mirror::Class::PrimitiveTypeOffset().Int32Value())
-#define MIRROR_CLASS_STATUS_OFFSET (112 + MIRROR_OBJECT_HEADER_SIZE)
+#define MIRROR_CLASS_STATUS_OFFSET (104 + MIRROR_OBJECT_HEADER_SIZE)
ADD_TEST_EQ(MIRROR_CLASS_STATUS_OFFSET,
art::mirror::Class::StatusOffset().Int32Value())
diff --git a/runtime/base/arena_allocator.cc b/runtime/base/arena_allocator.cc
index 5cdf671..61e0aab 100644
--- a/runtime/base/arena_allocator.cc
+++ b/runtime/base/arena_allocator.cc
@@ -83,18 +83,19 @@
"GraphChecker ",
"Verifier ",
"CallingConv ",
+ "CHA ",
};
template <bool kCount>
ArenaAllocatorStatsImpl<kCount>::ArenaAllocatorStatsImpl()
- : num_allocations_(0u) {
- std::fill_n(alloc_stats_, arraysize(alloc_stats_), 0u);
+ : num_allocations_(0u),
+ alloc_stats_(kNumArenaAllocKinds, 0u) {
}
template <bool kCount>
void ArenaAllocatorStatsImpl<kCount>::Copy(const ArenaAllocatorStatsImpl& other) {
num_allocations_ = other.num_allocations_;
- std::copy(other.alloc_stats_, other.alloc_stats_ + arraysize(alloc_stats_), alloc_stats_);
+ std::copy_n(other.alloc_stats_.begin(), kNumArenaAllocKinds, alloc_stats_.begin());
}
template <bool kCount>
@@ -111,7 +112,7 @@
template <bool kCount>
size_t ArenaAllocatorStatsImpl<kCount>::BytesAllocated() const {
const size_t init = 0u; // Initial value of the correct type.
- return std::accumulate(alloc_stats_, alloc_stats_ + arraysize(alloc_stats_), init);
+ return std::accumulate(alloc_stats_.begin(), alloc_stats_.end(), init);
}
template <bool kCount>
diff --git a/runtime/base/arena_allocator.h b/runtime/base/arena_allocator.h
index 2feb28a..6c764cb 100644
--- a/runtime/base/arena_allocator.h
+++ b/runtime/base/arena_allocator.h
@@ -21,6 +21,7 @@
#include <stddef.h>
#include "base/bit_utils.h"
+#include "base/dchecked_vector.h"
#include "base/memory_tool.h"
#include "debug_stack.h"
#include "macros.h"
@@ -132,8 +133,7 @@
private:
size_t num_allocations_;
- // TODO: Use std::array<size_t, kNumArenaAllocKinds> from C++11 when we upgrade the STL.
- size_t alloc_stats_[kNumArenaAllocKinds]; // Bytes used by various allocation kinds.
+ dchecked_vector<size_t> alloc_stats_; // Bytes used by various allocation kinds.
static const char* const kAllocNames[];
};
diff --git a/runtime/base/file_magic.cc b/runtime/base/file_magic.cc
index de6f423..568a7ae 100644
--- a/runtime/base/file_magic.cc
+++ b/runtime/base/file_magic.cc
@@ -20,13 +20,16 @@
#include <sys/stat.h>
#include <sys/types.h>
+#include "android-base/stringprintf.h"
+
#include "base/logging.h"
#include "base/unix_file/fd_file.h"
#include "dex_file.h"
-#include "stringprintf.h"
namespace art {
+using android::base::StringPrintf;
+
File OpenAndReadMagic(const char* filename, uint32_t* magic, std::string* error_msg) {
CHECK(magic != nullptr);
File fd(filename, O_RDONLY, /* check_usage */ false);
diff --git a/runtime/base/mutex-inl.h b/runtime/base/mutex-inl.h
index 92b7c65..44a84c8 100644
--- a/runtime/base/mutex-inl.h
+++ b/runtime/base/mutex-inl.h
@@ -21,7 +21,6 @@
#include "mutex.h"
-#include "base/stringprintf.h"
#include "base/value_object.h"
#include "thread.h"
#include "utils.h"
diff --git a/runtime/base/mutex.cc b/runtime/base/mutex.cc
index ce452cb..9116097 100644
--- a/runtime/base/mutex.cc
+++ b/runtime/base/mutex.cc
@@ -19,6 +19,8 @@
#include <errno.h>
#include <sys/time.h>
+#include "android-base/stringprintf.h"
+
#include "atomic.h"
#include "base/logging.h"
#include "base/time_utils.h"
@@ -30,6 +32,8 @@
namespace art {
+using android::base::StringPrintf;
+
static Atomic<Locks::ClientCallback*> safe_to_call_abort_callback(nullptr);
Mutex* Locks::abort_lock_ = nullptr;
diff --git a/runtime/base/scoped_flock.cc b/runtime/base/scoped_flock.cc
index 0e8031f..d4bb56b 100644
--- a/runtime/base/scoped_flock.cc
+++ b/runtime/base/scoped_flock.cc
@@ -19,12 +19,15 @@
#include <sys/file.h>
#include <sys/stat.h>
+#include "android-base/stringprintf.h"
+
#include "base/logging.h"
-#include "base/stringprintf.h"
#include "base/unix_file/fd_file.h"
namespace art {
+using android::base::StringPrintf;
+
bool ScopedFlock::Init(const char* filename, std::string* error_msg) {
return Init(filename, O_CREAT | O_RDWR, true, error_msg);
}
diff --git a/runtime/base/stringprintf.cc b/runtime/base/stringprintf.cc
deleted file mode 100644
index 8fd9257..0000000
--- a/runtime/base/stringprintf.cc
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include "stringprintf.h"
-
-#include <stdio.h>
-
-namespace art {
-
-void StringAppendV(std::string* dst, const char* format, va_list ap) {
- // First try with a small fixed size buffer
- char space[1024];
-
- // It's possible for methods that use a va_list to invalidate
- // the data in it upon use. The fix is to make a copy
- // of the structure before using it and use that copy instead.
- va_list backup_ap;
- va_copy(backup_ap, ap);
- int result = vsnprintf(space, sizeof(space), format, backup_ap);
- va_end(backup_ap);
-
- if (result < static_cast<int>(sizeof(space))) {
- if (result >= 0) {
- // Normal case -- everything fit.
- dst->append(space, result);
- return;
- }
-
- if (result < 0) {
- // Just an error.
- return;
- }
- }
-
- // Increase the buffer size to the size requested by vsnprintf,
- // plus one for the closing \0.
- int length = result+1;
- char* buf = new char[length];
-
- // Restore the va_list before we use it again
- va_copy(backup_ap, ap);
- result = vsnprintf(buf, length, format, backup_ap);
- va_end(backup_ap);
-
- if (result >= 0 && result < length) {
- // It fit
- dst->append(buf, result);
- }
- delete[] buf;
-}
-
-std::string StringPrintf(const char* fmt, ...) {
- va_list ap;
- va_start(ap, fmt);
- std::string result;
- StringAppendV(&result, fmt, ap);
- va_end(ap);
- return result;
-}
-
-void StringAppendF(std::string* dst, const char* format, ...) {
- va_list ap;
- va_start(ap, format);
- StringAppendV(dst, format, ap);
- va_end(ap);
-}
-
-} // namespace art
diff --git a/runtime/base/stringprintf.h b/runtime/base/stringprintf.h
deleted file mode 100644
index 4767a75..0000000
--- a/runtime/base/stringprintf.h
+++ /dev/null
@@ -1,38 +0,0 @@
-/*
- * Copyright (C) 2011 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef ART_RUNTIME_BASE_STRINGPRINTF_H_
-#define ART_RUNTIME_BASE_STRINGPRINTF_H_
-
-#include <stdarg.h>
-#include <string>
-
-namespace art {
-
-// Returns a string corresponding to printf-like formatting of the arguments.
-std::string StringPrintf(const char* fmt, ...)
- __attribute__((__format__(__printf__, 1, 2)));
-
-// Appends a printf-like formatting of the arguments to 'dst'.
-void StringAppendF(std::string* dst, const char* fmt, ...)
- __attribute__((__format__(__printf__, 2, 3)));
-
-// Appends a printf-like formatting of the arguments to 'dst'.
-void StringAppendV(std::string* dst, const char* format, va_list ap);
-
-} // namespace art
-
-#endif // ART_RUNTIME_BASE_STRINGPRINTF_H_
diff --git a/runtime/base/time_utils.cc b/runtime/base/time_utils.cc
index 57f198d..3c09d5a 100644
--- a/runtime/base/time_utils.cc
+++ b/runtime/base/time_utils.cc
@@ -20,8 +20,9 @@
#include "time_utils.h"
+#include "android-base/stringprintf.h"
+
#include "base/logging.h"
-#include "base/stringprintf.h"
#if defined(__APPLE__)
#include <sys/time.h>
@@ -29,6 +30,8 @@
namespace art {
+using android::base::StringPrintf;
+
std::string PrettyDuration(uint64_t nano_duration, size_t max_fraction_digits) {
if (nano_duration == 0) {
return "0";
diff --git a/runtime/cha.cc b/runtime/cha.cc
index be675a8..d94b091 100644
--- a/runtime/cha.cc
+++ b/runtime/cha.cc
@@ -100,7 +100,11 @@
bool VisitFrame() OVERRIDE REQUIRES_SHARED(Locks::mutator_lock_) {
ArtMethod* method = GetMethod();
- if (method == nullptr || method->IsRuntimeMethod() || method->IsNative()) {
+ // Avoid types of methods that do not have an oat quick method header.
+ if (method == nullptr ||
+ method->IsRuntimeMethod() ||
+ method->IsNative() ||
+ method->IsProxyMethod()) {
return true;
}
if (GetCurrentQuickFrame() == nullptr) {
@@ -110,6 +114,7 @@
// Method may have multiple versions of compiled code. Check
// the method header to see if it has should_deoptimize flag.
const OatQuickMethodHeader* method_header = GetCurrentOatQuickMethodHeader();
+ DCHECK(method_header != nullptr);
if (!method_header->HasShouldDeoptimizeFlag()) {
// This compiled version doesn't have should_deoptimize flag. Skip.
return true;
diff --git a/runtime/check_jni.cc b/runtime/check_jni.cc
index 6c27bc6..1c3328e 100644
--- a/runtime/check_jni.cc
+++ b/runtime/check_jni.cc
@@ -20,6 +20,8 @@
#include <sys/mman.h>
#include <zlib.h>
+#include "android-base/stringprintf.h"
+
#include "art_field-inl.h"
#include "art_method-inl.h"
#include "base/logging.h"
@@ -42,6 +44,9 @@
namespace art {
+using android::base::StringAppendF;
+using android::base::StringPrintf;
+
/*
* ===========================================================================
* JNI function helpers
diff --git a/runtime/class_linker-inl.h b/runtime/class_linker-inl.h
index 0a65cd1..a11257f 100644
--- a/runtime/class_linker-inl.h
+++ b/runtime/class_linker-inl.h
@@ -25,6 +25,7 @@
#include "mirror/class_loader.h"
#include "mirror/dex_cache-inl.h"
#include "mirror/iftable.h"
+#include "mirror/throwable.h"
#include "mirror/object_array.h"
#include "handle_scope-inl.h"
#include "scoped_thread_state_change-inl.h"
@@ -72,7 +73,7 @@
// MethodVerifier refuses methods with string_idx out of bounds.
DCHECK_LT(string_idx.index_, declaring_class->GetDexFile().NumStringIds());
ObjPtr<mirror::String> string =
- mirror::StringDexCachePair::Lookup(declaring_class->GetDexCacheStrings(),
+ mirror::StringDexCachePair::Lookup(declaring_class->GetDexCache()->GetStrings(),
string_idx.index_,
mirror::DexCache::kDexCacheStringCacheSize).Read();
if (UNLIKELY(string == nullptr)) {
@@ -89,17 +90,28 @@
inline mirror::Class* ClassLinker::ResolveType(dex::TypeIndex type_idx, ArtMethod* referrer) {
Thread::PoisonObjectPointersIfDebug();
+ if (kIsDebugBuild) {
+ Thread::Current()->AssertNoPendingException();
+ }
ObjPtr<mirror::Class> resolved_type =
referrer->GetDexCacheResolvedType(type_idx, image_pointer_size_);
if (UNLIKELY(resolved_type == nullptr)) {
- ObjPtr<mirror::Class> declaring_class = referrer->GetDeclaringClass();
StackHandleScope<2> hs(Thread::Current());
- Handle<mirror::DexCache> dex_cache(hs.NewHandle(declaring_class->GetDexCache()));
- Handle<mirror::ClassLoader> class_loader(hs.NewHandle(declaring_class->GetClassLoader()));
- const DexFile& dex_file = *dex_cache->GetDexFile();
- resolved_type = ResolveType(dex_file, type_idx, dex_cache, class_loader);
- // Note: We cannot check here to see whether we added the type to the cache. The type
- // might be an erroneous class, which results in it being hidden from us.
+ // There could be an out of bounds exception from GetDexCacheResolvedType, don't call
+ // ResolveType for this case.
+ if (LIKELY(!hs.Self()->IsExceptionPending())) {
+ ObjPtr<mirror::Class> declaring_class = referrer->GetDeclaringClass();
+ Handle<mirror::DexCache> dex_cache(hs.NewHandle(declaring_class->GetDexCache()));
+ Handle<mirror::ClassLoader> class_loader(hs.NewHandle(declaring_class->GetClassLoader()));
+ const DexFile& dex_file = *dex_cache->GetDexFile();
+ resolved_type = ResolveType(dex_file, type_idx, dex_cache, class_loader);
+ // Note: We cannot check here to see whether we added the type to the cache. The type
+ // might be an erroneous class, which results in it being hidden from us.
+ } else {
+ // Make sure its an array out of bounds exception.
+ DCHECK(hs.Self()->GetException()->GetClass()->DescriptorEquals(
+ "Ljava/lang/ArrayIndexOutOfBoundsException;"));
+ }
}
return resolved_type.Ptr();
}
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index f3a5be2..5b8d4e4 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -28,6 +28,8 @@
#include <utility>
#include <vector>
+#include "android-base/stringprintf.h"
+
#include "art_field-inl.h"
#include "art_method-inl.h"
#include "base/arena_allocator.h"
@@ -106,6 +108,8 @@
namespace art {
+using android::base::StringPrintf;
+
static constexpr bool kSanityCheckObjects = kIsDebugBuild;
static constexpr bool kVerifyArtMethodDeclaringClasses = kIsDebugBuild;
@@ -1401,39 +1405,20 @@
return true;
}
-// Update the class loader and resolved string dex cache array of classes. Should only be used on
-// classes in the image space.
-class UpdateClassLoaderAndResolvedStringsVisitor {
+// Update the class loader. Should only be used on classes in the image space.
+class UpdateClassLoaderVisitor {
public:
- UpdateClassLoaderAndResolvedStringsVisitor(gc::space::ImageSpace* space,
- ObjPtr<mirror::ClassLoader> class_loader,
- bool forward_strings)
+ UpdateClassLoaderVisitor(gc::space::ImageSpace* space, ObjPtr<mirror::ClassLoader> class_loader)
: space_(space),
- class_loader_(class_loader),
- forward_strings_(forward_strings) {}
+ class_loader_(class_loader) {}
bool operator()(ObjPtr<mirror::Class> klass) const REQUIRES_SHARED(Locks::mutator_lock_) {
- if (forward_strings_) {
- mirror::StringDexCacheType* strings = klass->GetDexCacheStrings();
- if (strings != nullptr) {
- DCHECK(
- space_->GetImageHeader().GetImageSection(ImageHeader::kSectionDexCacheArrays).Contains(
- reinterpret_cast<uint8_t*>(strings) - space_->Begin()))
- << "String dex cache array for " << klass->PrettyClass() << " is not in app image";
- // Dex caches have already been updated, so take the strings pointer from there.
- mirror::StringDexCacheType* new_strings = klass->GetDexCache()->GetStrings();
- DCHECK_NE(strings, new_strings);
- klass->SetDexCacheStrings(new_strings);
- }
- }
- // Finally, update class loader.
klass->SetClassLoader(class_loader_);
return true;
}
gc::space::ImageSpace* const space_;
ObjPtr<mirror::ClassLoader> const class_loader_;
- const bool forward_strings_;
};
static std::unique_ptr<const DexFile> OpenOatDexFile(const OatFile* oat_file,
@@ -1655,13 +1640,6 @@
Runtime* const runtime = Runtime::Current();
gc::Heap* const heap = runtime->GetHeap();
Thread* const self = Thread::Current();
- StackHandleScope<2> hs(self);
- Handle<mirror::ObjectArray<mirror::DexCache>> dex_caches(
- hs.NewHandle(dex_caches_object->AsObjectArray<mirror::DexCache>()));
- Handle<mirror::ObjectArray<mirror::Class>> class_roots(hs.NewHandle(
- header.GetImageRoot(ImageHeader::kClassRoots)->AsObjectArray<mirror::Class>()));
- const OatFile* oat_file = space->GetOatFile();
- std::unordered_set<mirror::ClassLoader*> image_class_loaders;
// Check that the image is what we are expecting.
if (image_pointer_size_ != space->GetImageHeader().GetPointerSize()) {
*error_msg = StringPrintf("Application image pointer size does not match runtime: %zu vs %zu",
@@ -1669,6 +1647,22 @@
image_pointer_size_);
return false;
}
+ size_t expected_image_roots = ImageHeader::NumberOfImageRoots(app_image);
+ if (static_cast<size_t>(header.GetImageRoots()->GetLength()) != expected_image_roots) {
+ *error_msg = StringPrintf("Expected %zu image roots but got %d",
+ expected_image_roots,
+ header.GetImageRoots()->GetLength());
+ return false;
+ }
+ StackHandleScope<3> hs(self);
+ Handle<mirror::ObjectArray<mirror::DexCache>> dex_caches(
+ hs.NewHandle(dex_caches_object->AsObjectArray<mirror::DexCache>()));
+ Handle<mirror::ObjectArray<mirror::Class>> class_roots(hs.NewHandle(
+ header.GetImageRoot(ImageHeader::kClassRoots)->AsObjectArray<mirror::Class>()));
+ static_assert(ImageHeader::kClassLoader + 1u == ImageHeader::kImageRootsMax,
+ "Class loader should be the last image root.");
+ MutableHandle<mirror::ClassLoader> image_class_loader(hs.NewHandle(
+ app_image ? header.GetImageRoot(ImageHeader::kClassLoader)->AsClassLoader() : nullptr));
DCHECK(class_roots.Get() != nullptr);
if (class_roots->GetLength() != static_cast<int32_t>(kClassRootsMax)) {
*error_msg = StringPrintf("Expected %d class roots but got %d",
@@ -1683,6 +1677,7 @@
return false;
}
}
+ const OatFile* oat_file = space->GetOatFile();
if (oat_file->GetOatHeader().GetDexFileCount() !=
static_cast<uint32_t>(dex_caches->GetLength())) {
*error_msg = "Dex cache count and dex file count mismatch while trying to initialize from "
@@ -1715,15 +1710,11 @@
// The current dex file field is bogus, overwrite it so that we can get the dex file in the
// loop below.
h_dex_cache->SetDexFile(dex_file.get());
- // Check that each class loader resolved the same way.
- // TODO: Store image class loaders as image roots.
GcRoot<mirror::Class>* const types = h_dex_cache->GetResolvedTypes();
for (int32_t j = 0, num_types = h_dex_cache->NumResolvedTypes(); j < num_types; j++) {
ObjPtr<mirror::Class> klass = types[j].Read();
if (klass != nullptr) {
DCHECK_NE(klass->GetStatus(), mirror::Class::kStatusError);
- ObjPtr<mirror::ClassLoader> image_class_loader = klass->GetClassLoader();
- image_class_loaders.insert(image_class_loader.Ptr());
}
}
} else {
@@ -1749,59 +1740,57 @@
// for PathClassLoader does this by looping through the array of dex files. To ensure they
// resolve the same way, simply flatten the hierarchy in the way the resolution order would be,
// and check that the dex file names are the same.
- for (ObjPtr<mirror::ClassLoader> image_class_loader : image_class_loaders) {
- if (IsBootClassLoader(soa, image_class_loader)) {
- // The dex cache can reference types from the boot class loader.
- continue;
+ if (IsBootClassLoader(soa, image_class_loader.Get())) {
+ *error_msg = "Unexpected BootClassLoader in app image";
+ return false;
+ }
+ std::list<mirror::String*> image_dex_file_names;
+ std::string temp_error_msg;
+ if (!FlattenPathClassLoader(image_class_loader.Get(), &image_dex_file_names, &temp_error_msg)) {
+ *error_msg = StringPrintf("Failed to flatten image class loader hierarchy '%s'",
+ temp_error_msg.c_str());
+ return false;
+ }
+ std::list<mirror::String*> loader_dex_file_names;
+ if (!FlattenPathClassLoader(class_loader.Get(), &loader_dex_file_names, &temp_error_msg)) {
+ *error_msg = StringPrintf("Failed to flatten class loader hierarchy '%s'",
+ temp_error_msg.c_str());
+ return false;
+ }
+ // Add the temporary dex path list elements at the end.
+ auto elements = soa.Decode<mirror::ObjectArray<mirror::Object>>(dex_elements);
+ for (size_t i = 0, num_elems = elements->GetLength(); i < num_elems; ++i) {
+ ObjPtr<mirror::Object> element = elements->GetWithoutChecks(i);
+ if (element != nullptr) {
+ // If we are somewhere in the middle of the array, there may be nulls at the end.
+ loader_dex_file_names.push_back(GetDexPathListElementName(element));
}
- std::list<mirror::String*> image_dex_file_names;
- std::string temp_error_msg;
- if (!FlattenPathClassLoader(image_class_loader, &image_dex_file_names, &temp_error_msg)) {
- *error_msg = StringPrintf("Failed to flatten image class loader hierarchy '%s'",
- temp_error_msg.c_str());
- return false;
+ }
+ // Ignore the number of image dex files since we are adding those to the class loader anyways.
+ CHECK_GE(static_cast<size_t>(image_dex_file_names.size()),
+ static_cast<size_t>(dex_caches->GetLength()));
+ size_t image_count = image_dex_file_names.size() - dex_caches->GetLength();
+ // Check that the dex file names match.
+ bool equal = image_count == loader_dex_file_names.size();
+ if (equal) {
+ auto it1 = image_dex_file_names.begin();
+ auto it2 = loader_dex_file_names.begin();
+ for (size_t i = 0; equal && i < image_count; ++i, ++it1, ++it2) {
+ equal = equal && (*it1)->Equals(*it2);
}
- std::list<mirror::String*> loader_dex_file_names;
- if (!FlattenPathClassLoader(class_loader.Get(), &loader_dex_file_names, &temp_error_msg)) {
- *error_msg = StringPrintf("Failed to flatten class loader hierarchy '%s'",
- temp_error_msg.c_str());
- return false;
+ }
+ if (!equal) {
+ VLOG(image) << "Image dex files " << image_dex_file_names.size();
+ for (ObjPtr<mirror::String> name : image_dex_file_names) {
+ VLOG(image) << name->ToModifiedUtf8();
}
- // Add the temporary dex path list elements at the end.
- auto elements = soa.Decode<mirror::ObjectArray<mirror::Object>>(dex_elements);
- for (size_t i = 0, num_elems = elements->GetLength(); i < num_elems; ++i) {
- ObjPtr<mirror::Object> element = elements->GetWithoutChecks(i);
- if (element != nullptr) {
- // If we are somewhere in the middle of the array, there may be nulls at the end.
- loader_dex_file_names.push_back(GetDexPathListElementName(element));
- }
+ VLOG(image) << "Loader dex files " << loader_dex_file_names.size();
+ for (ObjPtr<mirror::String> name : loader_dex_file_names) {
+ VLOG(image) << name->ToModifiedUtf8();
}
- // Ignore the number of image dex files since we are adding those to the class loader anyways.
- CHECK_GE(static_cast<size_t>(image_dex_file_names.size()),
- static_cast<size_t>(dex_caches->GetLength()));
- size_t image_count = image_dex_file_names.size() - dex_caches->GetLength();
- // Check that the dex file names match.
- bool equal = image_count == loader_dex_file_names.size();
- if (equal) {
- auto it1 = image_dex_file_names.begin();
- auto it2 = loader_dex_file_names.begin();
- for (size_t i = 0; equal && i < image_count; ++i, ++it1, ++it2) {
- equal = equal && (*it1)->Equals(*it2);
- }
- }
- if (!equal) {
- VLOG(image) << "Image dex files " << image_dex_file_names.size();
- for (ObjPtr<mirror::String> name : image_dex_file_names) {
- VLOG(image) << name->ToModifiedUtf8();
- }
- VLOG(image) << "Loader dex files " << loader_dex_file_names.size();
- for (ObjPtr<mirror::String> name : loader_dex_file_names) {
- VLOG(image) << name->ToModifiedUtf8();
- }
- *error_msg = "Rejecting application image due to class loader mismatch";
- // Ignore class loader mismatch for now since these would just use possibly incorrect
- // oat code anyways. The structural class check should be done in the parent.
- }
+ *error_msg = "Rejecting application image due to class loader mismatch";
+ // Ignore class loader mismatch for now since these would just use possibly incorrect
+ // oat code anyways. The structural class check should be done in the parent.
}
}
@@ -1856,10 +1845,8 @@
}
// Update class loader and resolved strings. If added_class_table is false, the resolved
// strings were forwarded UpdateAppImageClassLoadersAndDexCaches.
- UpdateClassLoaderAndResolvedStringsVisitor visitor(space,
- class_loader.Get(),
- forward_dex_cache_arrays);
- for (ClassTable::TableSlot& root : temp_set) {
+ UpdateClassLoaderVisitor visitor(space, class_loader.Get());
+ for (const ClassTable::TableSlot& root : temp_set) {
visitor(root.Read());
}
// forward_dex_cache_arrays is true iff we copied all of the dex cache arrays into the .bss.
@@ -2516,8 +2503,9 @@
// the Java-side could still succeed for racy programs if another thread is actively
// modifying the class loader's path list.
- if (Runtime::Current()->IsAotCompiler()) {
- // Oops, compile-time, can't run actual class-loader code.
+ if (!self->CanCallIntoJava()) {
+ // Oops, we can't call into java so we can't run actual class-loader code.
+ // This is true for e.g. for the compiler (jit or aot).
ObjPtr<mirror::Throwable> pre_allocated =
Runtime::Current()->GetPreAllocatedNoClassDefFoundError();
self->SetException(pre_allocated);
@@ -2659,6 +2647,8 @@
ObjectLock<mirror::Class> lock(self, klass);
klass->SetClinitThreadId(self->GetTid());
+ // Make sure we have a valid empty iftable even if there are errors.
+ klass->SetIfTable(GetClassRoot(kJavaLangObject)->GetIfTable());
// Add the newly loaded class to the loaded classes table.
ObjPtr<mirror::Class> existing = InsertClass(descriptor, klass.Get(), hash);
@@ -3010,7 +3000,6 @@
klass->SetDexClassDefIndex(dex_file.GetIndexForClassDef(dex_class_def));
klass->SetDexTypeIndex(dex_class_def.class_idx_);
- CHECK(klass->GetDexCacheStrings() != nullptr);
}
void ClassLinker::LoadClass(Thread* self,
diff --git a/runtime/class_linker_test.cc b/runtime/class_linker_test.cc
index 685677b..42108d8 100644
--- a/runtime/class_linker_test.cc
+++ b/runtime/class_linker_test.cc
@@ -278,8 +278,6 @@
EXPECT_FALSE(klass->IsArrayClass());
EXPECT_TRUE(klass->GetComponentType() == nullptr);
EXPECT_TRUE(klass->IsInSamePackage(klass.Get()));
- EXPECT_TRUE(klass->GetDexCacheStrings() != nullptr);
- EXPECT_EQ(klass->GetDexCacheStrings(), klass->GetDexCache()->GetStrings());
std::string temp2;
EXPECT_TRUE(mirror::Class::IsInSamePackage(klass->GetDescriptor(&temp),
klass->GetDescriptor(&temp2)));
@@ -590,7 +588,6 @@
addOffset(OFFSETOF_MEMBER(mirror::Class, component_type_), "componentType");
addOffset(OFFSETOF_MEMBER(mirror::Class, copied_methods_offset_), "copiedMethodsOffset");
addOffset(OFFSETOF_MEMBER(mirror::Class, dex_cache_), "dexCache");
- addOffset(OFFSETOF_MEMBER(mirror::Class, dex_cache_strings_), "dexCacheStrings");
addOffset(OFFSETOF_MEMBER(mirror::Class, dex_class_def_idx_), "dexClassDefIndex");
addOffset(OFFSETOF_MEMBER(mirror::Class, dex_type_idx_), "dexTypeIndex");
addOffset(OFFSETOF_MEMBER(mirror::Class, ext_data_), "extData");
@@ -1175,6 +1172,24 @@
EXPECT_TRUE(init->IsInitialized());
}
+TEST_F(ClassLinkerTest, ErroneousClass) {
+ ScopedObjectAccess soa(Thread::Current());
+ jobject jclass_loader = LoadMultiDex("ErroneousA", "ErroneousB");
+ StackHandleScope<1> hs(soa.Self());
+ Handle<mirror::ClassLoader> class_loader(
+ hs.NewHandle(soa.Decode<mirror::ClassLoader>(jclass_loader)));
+ hs.Self()->AssertNoPendingException();
+ const char* descriptor = "LErroneous;";
+ ObjPtr<mirror::Class> klass = class_linker_->FindClass(soa.Self(), descriptor, class_loader);
+ // Erronenous since we are extending final class.
+ hs.Self()->AssertPendingException();
+ EXPECT_TRUE(klass == nullptr);
+ klass = class_linker_->LookupClass(soa.Self(), descriptor, class_loader.Get());
+ EXPECT_FALSE(klass == nullptr);
+ EXPECT_TRUE(klass->IsErroneous());
+ EXPECT_TRUE(klass->GetIfTable() != nullptr);
+}
+
TEST_F(ClassLinkerTest, FinalizableBit) {
ScopedObjectAccess soa(Thread::Current());
mirror::Class* c;
diff --git a/runtime/common_runtime_test.cc b/runtime/common_runtime_test.cc
index 8226e60..743fcc8 100644
--- a/runtime/common_runtime_test.cc
+++ b/runtime/common_runtime_test.cc
@@ -24,11 +24,12 @@
#include <stdlib.h>
#include "../../external/icu/icu4c/source/common/unicode/uvernum.h"
+#include "android-base/stringprintf.h"
+
#include "art_field-inl.h"
#include "base/macros.h"
#include "base/logging.h"
#include "base/stl_util.h"
-#include "base/stringprintf.h"
#include "base/unix_file/fd_file.h"
#include "class_linker.h"
#include "compiler_callbacks.h"
@@ -65,6 +66,8 @@
namespace art {
+using android::base::StringPrintf;
+
ScratchFile::ScratchFile() {
// ANDROID_DATA needs to be set
CHECK_NE(static_cast<char*>(nullptr), getenv("ANDROID_DATA")) <<
diff --git a/runtime/common_throws.cc b/runtime/common_throws.cc
index 9f0dbbb..c30272e 100644
--- a/runtime/common_throws.cc
+++ b/runtime/common_throws.cc
@@ -18,6 +18,7 @@
#include <sstream>
+#include "android-base/stringprintf.h"
#include "ScopedLocalRef.h"
#include "art_field-inl.h"
@@ -37,6 +38,9 @@
namespace art {
+using android::base::StringAppendV;
+using android::base::StringPrintf;
+
static void AddReferrerLocation(std::ostream& os, ObjPtr<mirror::Class> referrer)
REQUIRES_SHARED(Locks::mutator_lock_) {
if (referrer != nullptr) {
diff --git a/runtime/debugger.cc b/runtime/debugger.cc
index e339666..df4413d 100644
--- a/runtime/debugger.cc
+++ b/runtime/debugger.cc
@@ -20,6 +20,8 @@
#include <set>
+#include "android-base/stringprintf.h"
+
#include "arch/context.h"
#include "art_field-inl.h"
#include "art_method-inl.h"
@@ -61,6 +63,8 @@
namespace art {
+using android::base::StringPrintf;
+
// The key identifying the debugger to update instrumentation.
static constexpr const char* kDbgInstrumentationKey = "Debugger";
diff --git a/runtime/dex_file.cc b/runtime/dex_file.cc
index aa8fb38..7d704ad 100644
--- a/runtime/dex_file.cc
+++ b/runtime/dex_file.cc
@@ -28,10 +28,11 @@
#include <sstream>
#include <type_traits>
+#include "android-base/stringprintf.h"
+
#include "base/enums.h"
#include "base/file_magic.h"
#include "base/logging.h"
-#include "base/stringprintf.h"
#include "base/systrace.h"
#include "base/unix_file/fd_file.h"
#include "dex_file-inl.h"
@@ -45,6 +46,8 @@
namespace art {
+using android::base::StringPrintf;
+
static_assert(sizeof(dex::StringIndex) == sizeof(uint32_t), "StringIndex size is wrong");
static_assert(std::is_trivially_copyable<dex::StringIndex>::value, "StringIndex not trivial");
static_assert(sizeof(dex::TypeIndex) == sizeof(uint16_t), "TypeIndex size is wrong");
diff --git a/runtime/dex_file_annotations.cc b/runtime/dex_file_annotations.cc
index 52b9f11..9504e8b 100644
--- a/runtime/dex_file_annotations.cc
+++ b/runtime/dex_file_annotations.cc
@@ -18,6 +18,8 @@
#include <stdlib.h>
+#include "android-base/stringprintf.h"
+
#include "art_field-inl.h"
#include "art_method-inl.h"
#include "class_linker-inl.h"
@@ -31,6 +33,8 @@
namespace art {
+using android::base::StringPrintf;
+
struct DexFile::AnnotationValue {
JValue value_;
uint8_t type_;
diff --git a/runtime/dex_file_verifier.cc b/runtime/dex_file_verifier.cc
index 07f0fca..a3ab9fa 100644
--- a/runtime/dex_file_verifier.cc
+++ b/runtime/dex_file_verifier.cc
@@ -22,7 +22,8 @@
#include <limits>
#include <memory>
-#include "base/stringprintf.h"
+#include "android-base/stringprintf.h"
+
#include "dex_file-inl.h"
#include "experimental_flags.h"
#include "leb128.h"
@@ -32,6 +33,9 @@
namespace art {
+using android::base::StringAppendV;
+using android::base::StringPrintf;
+
static constexpr uint32_t kTypeIdLimit = std::numeric_limits<uint16_t>::max();
static bool IsValidOrNoTypeId(uint16_t low, uint16_t high) {
diff --git a/runtime/dex_instruction.cc b/runtime/dex_instruction.cc
index 9902389..7b8974f 100644
--- a/runtime/dex_instruction.cc
+++ b/runtime/dex_instruction.cc
@@ -21,12 +21,15 @@
#include <iomanip>
#include <sstream>
-#include "base/stringprintf.h"
+#include "android-base/stringprintf.h"
+
#include "dex_file-inl.h"
#include "utils.h"
namespace art {
+using android::base::StringPrintf;
+
const char* const Instruction::kInstructionNames[] = {
#define INSTRUCTION_NAME(o, c, pname, f, i, a, v) pname,
#include "dex_instruction_list.h"
diff --git a/runtime/elf_file.cc b/runtime/elf_file.cc
index ee0f340..59b734f 100644
--- a/runtime/elf_file.cc
+++ b/runtime/elf_file.cc
@@ -20,11 +20,11 @@
#include <sys/types.h>
#include <unistd.h>
+#include "android-base/stringprintf.h"
#include "android-base/strings.h"
#include "arch/instruction_set.h"
#include "base/logging.h"
-#include "base/stringprintf.h"
#include "base/stl_util.h"
#include "base/unix_file/fd_file.h"
#include "elf_file_impl.h"
@@ -34,6 +34,8 @@
namespace art {
+using android::base::StringPrintf;
+
template <typename ElfTypes>
ElfFileImpl<ElfTypes>::ElfFileImpl(File* file, bool writable,
bool program_header_only,
diff --git a/runtime/gc/accounting/space_bitmap.cc b/runtime/gc/accounting/space_bitmap.cc
index f4d0bc7..eb9f039 100644
--- a/runtime/gc/accounting/space_bitmap.cc
+++ b/runtime/gc/accounting/space_bitmap.cc
@@ -16,8 +16,9 @@
#include "space_bitmap-inl.h"
+#include "android-base/stringprintf.h"
+
#include "art_field-inl.h"
-#include "base/stringprintf.h"
#include "dex_file-inl.h"
#include "mem_map.h"
#include "mirror/object-inl.h"
@@ -28,6 +29,8 @@
namespace gc {
namespace accounting {
+using android::base::StringPrintf;
+
template<size_t kAlignment>
size_t SpaceBitmap<kAlignment>::ComputeBitmapSize(uint64_t capacity) {
const uint64_t kBytesCoveredPerWord = kAlignment * kBitsPerIntPtrT;
diff --git a/runtime/gc/allocator/rosalloc.cc b/runtime/gc/allocator/rosalloc.cc
index 2e4475f..35a251f 100644
--- a/runtime/gc/allocator/rosalloc.cc
+++ b/runtime/gc/allocator/rosalloc.cc
@@ -16,6 +16,13 @@
#include "rosalloc.h"
+#include <map>
+#include <list>
+#include <sstream>
+#include <vector>
+
+#include "android-base/stringprintf.h"
+
#include "base/memory_tool.h"
#include "base/mutex-inl.h"
#include "gc/space/memory_tool_settings.h"
@@ -26,15 +33,12 @@
#include "thread-inl.h"
#include "thread_list.h"
-#include <map>
-#include <list>
-#include <sstream>
-#include <vector>
-
namespace art {
namespace gc {
namespace allocator {
+using android::base::StringPrintf;
+
static constexpr bool kUsePrefetchDuringAllocRun = false;
static constexpr bool kPrefetchNewRunDataByZeroing = false;
static constexpr size_t kPrefetchStride = 64;
diff --git a/runtime/gc/collector/garbage_collector.cc b/runtime/gc/collector/garbage_collector.cc
index ed16854..01bcb7d 100644
--- a/runtime/gc/collector/garbage_collector.cc
+++ b/runtime/gc/collector/garbage_collector.cc
@@ -18,6 +18,8 @@
#include "garbage_collector.h"
+#include "android-base/stringprintf.h"
+
#include "base/dumpable.h"
#include "base/histogram-inl.h"
#include "base/logging.h"
@@ -81,7 +83,7 @@
}
void GarbageCollector::Run(GcCause gc_cause, bool clear_soft_references) {
- ScopedTrace trace(StringPrintf("%s %s GC", PrettyCause(gc_cause), GetName()));
+ ScopedTrace trace(android::base::StringPrintf("%s %s GC", PrettyCause(gc_cause), GetName()));
Thread* self = Thread::Current();
uint64_t start_time = NanoTime();
Iteration* current_iteration = GetCurrentIteration();
diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc
index 3ed138c..34d8284 100644
--- a/runtime/gc/heap.cc
+++ b/runtime/gc/heap.cc
@@ -21,6 +21,8 @@
#include <unwind.h> // For GC verification.
#include <vector>
+#include "android-base/stringprintf.h"
+
#include "allocation_listener.h"
#include "art_field-inl.h"
#include "base/allocator.h"
@@ -82,6 +84,8 @@
namespace gc {
+using android::base::StringPrintf;
+
static constexpr size_t kCollectorTransitionStressIterations = 0;
static constexpr size_t kCollectorTransitionStressWait = 10 * 1000; // Microseconds
// Minimum amount of remaining bytes before a concurrent GC is triggered.
@@ -3919,7 +3923,7 @@
ScopedObjectAccess soa(env);
env->ThrowNew(WellKnownClasses::java_lang_RuntimeException,
StringPrintf("Attempted to free %zd native bytes with only %zd native bytes "
- "registered as allocated", bytes, expected_size).c_str());
+ "registered as allocated", bytes, expected_size).c_str());
break;
}
} while (!native_bytes_allocated_.CompareExchangeWeakRelaxed(expected_size,
diff --git a/runtime/gc/reference_processor.cc b/runtime/gc/reference_processor.cc
index 2cde7d5..081be96 100644
--- a/runtime/gc/reference_processor.cc
+++ b/runtime/gc/reference_processor.cc
@@ -75,11 +75,10 @@
MutexLock mu(self, *Locks::reference_processor_lock_);
while ((!kUseReadBarrier && SlowPathEnabled()) ||
(kUseReadBarrier && !self->GetWeakRefAccessEnabled())) {
- mirror::HeapReference<mirror::Object>* const referent_addr =
- reference->GetReferentReferenceAddr();
+ ObjPtr<mirror::Object> referent = reference->GetReferent<kWithoutReadBarrier>();
// If the referent became cleared, return it. Don't need barrier since thread roots can't get
// updated until after we leave the function due to holding the mutator lock.
- if (referent_addr->AsMirrorPtr() == nullptr) {
+ if (referent == nullptr) {
return nullptr;
}
// Try to see if the referent is already marked by using the is_marked_callback. We can return
@@ -91,10 +90,15 @@
// case only black nodes can be safely returned. If the GC is preserving references, the
// mutator could take a white field from a grey or white node and move it somewhere else
// in the heap causing corruption since this field would get swept.
- if (collector_->IsMarkedHeapReference(referent_addr)) {
+ // Use the cached referent instead of calling GetReferent since other threads could call
+ // Reference.clear() after we did the null check resulting in a null pointer being
+ // incorrectly passed to IsMarked. b/33569625
+ ObjPtr<mirror::Object> forwarded_ref = collector_->IsMarked(referent.Ptr());
+ if (forwarded_ref != nullptr) {
+ // Non null means that it is marked.
if (!preserving_references_ ||
(LIKELY(!reference->IsFinalizerReferenceInstance()) && reference->IsUnprocessed())) {
- return referent_addr->AsMirrorPtr();
+ return forwarded_ref;
}
}
}
@@ -265,11 +269,23 @@
}
}
-bool ReferenceProcessor::MakeCircularListIfUnenqueued(
- ObjPtr<mirror::FinalizerReference> reference) {
+void ReferenceProcessor::ClearReferent(ObjPtr<mirror::Reference> ref) {
Thread* self = Thread::Current();
MutexLock mu(self, *Locks::reference_processor_lock_);
- // Wait untul we are done processing reference.
+ // Need to wait until reference processing is done since IsMarkedHeapReference does not have a
+ // CAS. If we do not wait, it can result in the GC un-clearing references due to race conditions.
+ // This also handles the race where the referent gets cleared after a null check but before
+ // IsMarkedHeapReference is called.
+ WaitUntilDoneProcessingReferences(self);
+ if (Runtime::Current()->IsActiveTransaction()) {
+ ref->ClearReferent<true>();
+ } else {
+ ref->ClearReferent<false>();
+ }
+}
+
+void ReferenceProcessor::WaitUntilDoneProcessingReferences(Thread* self) {
+ // Wait until we are done processing reference.
while ((!kUseReadBarrier && SlowPathEnabled()) ||
(kUseReadBarrier && !self->GetWeakRefAccessEnabled())) {
// Check and run the empty checkpoint before blocking so the empty checkpoint will work in the
@@ -277,6 +293,13 @@
self->CheckEmptyCheckpoint();
condition_.WaitHoldingLocks(self);
}
+}
+
+bool ReferenceProcessor::MakeCircularListIfUnenqueued(
+ ObjPtr<mirror::FinalizerReference> reference) {
+ Thread* self = Thread::Current();
+ MutexLock mu(self, *Locks::reference_processor_lock_);
+ WaitUntilDoneProcessingReferences(self);
// At this point, since the sentinel of the reference is live, it is guaranteed to not be
// enqueued if we just finished processing references. Otherwise, we may be doing the main GC
// phase. Since we are holding the reference processor lock, it guarantees that reference
diff --git a/runtime/gc/reference_processor.h b/runtime/gc/reference_processor.h
index 759b7e1..b15544d 100644
--- a/runtime/gc/reference_processor.h
+++ b/runtime/gc/reference_processor.h
@@ -73,6 +73,9 @@
REQUIRES_SHARED(Locks::mutator_lock_)
REQUIRES(!Locks::reference_processor_lock_,
!Locks::reference_queue_finalizer_references_lock_);
+ void ClearReferent(ObjPtr<mirror::Reference> ref)
+ REQUIRES_SHARED(Locks::mutator_lock_)
+ REQUIRES(!Locks::reference_processor_lock_);
private:
bool SlowPathEnabled() REQUIRES_SHARED(Locks::mutator_lock_);
@@ -84,6 +87,10 @@
// referents.
void StartPreservingReferences(Thread* self) REQUIRES(!Locks::reference_processor_lock_);
void StopPreservingReferences(Thread* self) REQUIRES(!Locks::reference_processor_lock_);
+ // Wait until reference processing is done.
+ void WaitUntilDoneProcessingReferences(Thread* self)
+ REQUIRES_SHARED(Locks::mutator_lock_)
+ REQUIRES(Locks::reference_processor_lock_);
// Collector which is clearing references, used by the GetReferent to return referents which are
// already marked.
collector::GarbageCollector* collector_ GUARDED_BY(Locks::reference_processor_lock_);
diff --git a/runtime/gc/space/image_space.cc b/runtime/gc/space/image_space.cc
index 76f3692..e03958d 100644
--- a/runtime/gc/space/image_space.cc
+++ b/runtime/gc/space/image_space.cc
@@ -22,6 +22,7 @@
#include <sys/types.h>
#include <unistd.h>
+#include "android-base/stringprintf.h"
#include "android-base/strings.h"
#include "art_method.h"
@@ -45,6 +46,9 @@
namespace gc {
namespace space {
+using android::base::StringAppendF;
+using android::base::StringPrintf;
+
Atomic<uint32_t> ImageSpace::bitmap_index_(0);
ImageSpace::ImageSpace(const std::string& image_filename,
diff --git a/runtime/gc/space/image_space_fs.h b/runtime/gc/space/image_space_fs.h
index fa941c0..5999548 100644
--- a/runtime/gc/space/image_space_fs.h
+++ b/runtime/gc/space/image_space_fs.h
@@ -20,9 +20,10 @@
#include <dirent.h>
#include <dlfcn.h>
+#include "android-base/stringprintf.h"
+
#include "base/logging.h"
#include "base/macros.h"
-#include "base/stringprintf.h"
#include "base/unix_file/fd_file.h"
#include "globals.h"
#include "os.h"
@@ -56,7 +57,7 @@
continue;
}
// We only want to delete regular files and symbolic links.
- std::string file = StringPrintf("%s/%s", dir.c_str(), name);
+ std::string file = android::base::StringPrintf("%s/%s", dir.c_str(), name);
if (de->d_type != DT_REG && de->d_type != DT_LNK) {
if (de->d_type == DT_DIR) {
if (recurse) {
diff --git a/runtime/gc/space/malloc_space.cc b/runtime/gc/space/malloc_space.cc
index b1572cc..a186f4c 100644
--- a/runtime/gc/space/malloc_space.cc
+++ b/runtime/gc/space/malloc_space.cc
@@ -16,6 +16,8 @@
#include "malloc_space.h"
+#include "android-base/stringprintf.h"
+
#include "gc/accounting/card_table-inl.h"
#include "gc/accounting/space_bitmap-inl.h"
#include "gc/heap.h"
@@ -33,6 +35,8 @@
namespace gc {
namespace space {
+using android::base::StringPrintf;
+
size_t MallocSpace::bitmap_index_ = 0;
MallocSpace::MallocSpace(const std::string& name, MemMap* mem_map,
diff --git a/runtime/generated/asm_support_gen.h b/runtime/generated/asm_support_gen.h
index f13ff8c..bebcd71 100644
--- a/runtime/generated/asm_support_gen.h
+++ b/runtime/generated/asm_support_gen.h
@@ -74,8 +74,6 @@
DEFINE_CHECK_EQ(static_cast<int32_t>(ART_METHOD_QUICK_CODE_OFFSET_64), (static_cast<int32_t>(art::ArtMethod:: EntryPointFromQuickCompiledCodeOffset(art::PointerSize::k64).Int32Value())))
#define ART_METHOD_DECLARING_CLASS_OFFSET 0
DEFINE_CHECK_EQ(static_cast<int32_t>(ART_METHOD_DECLARING_CLASS_OFFSET), (static_cast<int32_t>(art::ArtMethod:: DeclaringClassOffset().Int32Value())))
-#define DECLARING_CLASS_DEX_CACHE_STRINGS_OFFSET 40
-DEFINE_CHECK_EQ(static_cast<int32_t>(DECLARING_CLASS_DEX_CACHE_STRINGS_OFFSET), (static_cast<int32_t>(art::mirror::Class:: DexCacheStringsOffset().Int32Value())))
#define STRING_DEX_CACHE_ELEMENT_SIZE_SHIFT 3
DEFINE_CHECK_EQ(static_cast<int32_t>(STRING_DEX_CACHE_ELEMENT_SIZE_SHIFT), (static_cast<int32_t>(art::WhichPowerOf2(sizeof(art::mirror::StringDexCachePair)))))
#define STRING_DEX_CACHE_SIZE_MINUS_ONE 1023
diff --git a/runtime/hprof/hprof.cc b/runtime/hprof/hprof.cc
index 8cbe491..fe6a6e9 100644
--- a/runtime/hprof/hprof.cc
+++ b/runtime/hprof/hprof.cc
@@ -37,9 +37,10 @@
#include <set>
+#include "android-base/stringprintf.h"
+
#include "art_field-inl.h"
#include "base/logging.h"
-#include "base/stringprintf.h"
#include "base/time_utils.h"
#include "base/unix_file/fd_file.h"
#include "class_linker.h"
@@ -797,8 +798,9 @@
file->Erase();
}
if (!okay) {
- std::string msg(StringPrintf("Couldn't dump heap; writing \"%s\" failed: %s",
- filename_.c_str(), strerror(errno)));
+ std::string msg(android::base::StringPrintf("Couldn't dump heap; writing \"%s\" failed: %s",
+ filename_.c_str(),
+ strerror(errno)));
ThrowRuntimeException("%s", msg.c_str());
LOG(ERROR) << msg;
}
diff --git a/runtime/image.cc b/runtime/image.cc
index 52c9f4e..2ef60c3 100644
--- a/runtime/image.cc
+++ b/runtime/image.cc
@@ -25,7 +25,7 @@
namespace art {
const uint8_t ImageHeader::kImageMagic[] = { 'a', 'r', 't', '\n' };
-const uint8_t ImageHeader::kImageVersion[] = { '0', '3', '3', '\0' };
+const uint8_t ImageHeader::kImageVersion[] = { '0', '3', '4', '\0' }; // mirror::Class update
ImageHeader::ImageHeader(uint32_t image_begin,
uint32_t image_size,
diff --git a/runtime/image.h b/runtime/image.h
index da9976a..6c76f49 100644
--- a/runtime/image.h
+++ b/runtime/image.h
@@ -189,6 +189,7 @@
enum ImageRoot {
kDexCaches,
kClassRoots,
+ kClassLoader, // App image only.
kImageRootsMax,
};
@@ -206,6 +207,10 @@
kSectionCount, // Number of elements in enum.
};
+ static size_t NumberOfImageRoots(bool app_image) {
+ return app_image ? kImageRootsMax : kImageRootsMax - 1u;
+ }
+
ArtMethod* GetImageMethod(ImageMethod index) const;
void SetImageMethod(ImageMethod index, ArtMethod* method);
diff --git a/runtime/indirect_reference_table-inl.h b/runtime/indirect_reference_table-inl.h
index 9c634fa..0e66ae9 100644
--- a/runtime/indirect_reference_table-inl.h
+++ b/runtime/indirect_reference_table-inl.h
@@ -19,6 +19,8 @@
#include "indirect_reference_table.h"
+#include "android-base/stringprintf.h"
+
#include "base/dumpable.h"
#include "gc_root-inl.h"
#include "obj_ptr-inl.h"
@@ -38,15 +40,15 @@
return false;
}
if (UNLIKELY(GetIndirectRefKind(iref) == kHandleScopeOrInvalid)) {
- AbortIfNoCheckJNI(StringPrintf("JNI ERROR (app bug): invalid %s %p",
- GetIndirectRefKindString(kind_),
- iref));
+ AbortIfNoCheckJNI(android::base::StringPrintf("JNI ERROR (app bug): invalid %s %p",
+ GetIndirectRefKindString(kind_),
+ iref));
return false;
}
const uint32_t top_index = segment_state_.top_index;
uint32_t idx = ExtractIndex(iref);
if (UNLIKELY(idx >= top_index)) {
- std::string msg = StringPrintf(
+ std::string msg = android::base::StringPrintf(
"JNI ERROR (app bug): accessed stale %s %p (index %d in a table of size %d)",
GetIndirectRefKindString(kind_),
iref,
@@ -56,9 +58,9 @@
return false;
}
if (UNLIKELY(table_[idx].GetReference()->IsNull())) {
- AbortIfNoCheckJNI(StringPrintf("JNI ERROR (app bug): accessed deleted %s %p",
- GetIndirectRefKindString(kind_),
- iref));
+ AbortIfNoCheckJNI(android::base::StringPrintf("JNI ERROR (app bug): accessed deleted %s %p",
+ GetIndirectRefKindString(kind_),
+ iref));
return false;
}
if (UNLIKELY(!CheckEntry("use", iref, idx))) {
@@ -73,7 +75,7 @@
uint32_t idx) const {
IndirectRef checkRef = ToIndirectRef(idx);
if (UNLIKELY(checkRef != iref)) {
- std::string msg = StringPrintf(
+ std::string msg = android::base::StringPrintf(
"JNI ERROR (app bug): attempt to %s stale %s %p (should be %p)",
what,
GetIndirectRefKindString(kind_),
diff --git a/runtime/indirect_reference_table_test.cc b/runtime/indirect_reference_table_test.cc
index 722b411..bf4cab2 100644
--- a/runtime/indirect_reference_table_test.cc
+++ b/runtime/indirect_reference_table_test.cc
@@ -16,6 +16,8 @@
#include "indirect_reference_table-inl.h"
+#include "android-base/stringprintf.h"
+
#include "class_linker-inl.h"
#include "common_runtime_test.h"
#include "mirror/object-inl.h"
@@ -23,6 +25,8 @@
namespace art {
+using android::base::StringPrintf;
+
class IndirectReferenceTableTest : public CommonRuntimeTest {};
static void CheckDump(IndirectReferenceTable* irt, size_t num_objects, size_t num_unique)
diff --git a/runtime/instrumentation.cc b/runtime/instrumentation.cc
index 870d1ae..03ef962 100644
--- a/runtime/instrumentation.cc
+++ b/runtime/instrumentation.cc
@@ -1098,10 +1098,14 @@
Dbg::IsForcedInterpreterNeededForUpcall(self, visitor.caller));
if (deoptimize && Runtime::Current()->IsDeoptimizeable(*return_pc)) {
if (kVerboseInstrumentation) {
- LOG(INFO) << StringPrintf("Deoptimizing %s by returning from %s with result %#" PRIx64 " in ",
- visitor.caller->PrettyMethod().c_str(),
- method->PrettyMethod().c_str(),
- return_value.GetJ()) << *self;
+ LOG(INFO) << "Deoptimizing "
+ << visitor.caller->PrettyMethod()
+ << " by returning from "
+ << method->PrettyMethod()
+ << " with result "
+ << std::hex << return_value.GetJ() << std::dec
+ << " in "
+ << *self;
}
self->PushDeoptimizationContext(return_value,
return_shorty == 'L',
diff --git a/runtime/interpreter/interpreter_common.cc b/runtime/interpreter/interpreter_common.cc
index 22da07d..a09e71b 100644
--- a/runtime/interpreter/interpreter_common.cc
+++ b/runtime/interpreter/interpreter_common.cc
@@ -676,7 +676,7 @@
CHECK(Runtime::Current()->IsActiveTransaction());
// Constructs abort message.
std::string abort_msg;
- StringAppendV(&abort_msg, fmt, args);
+ android::base::StringAppendV(&abort_msg, fmt, args);
// Throws an exception so we can abort the transaction and rollback every change.
Runtime::Current()->AbortTransactionAndThrowAbortError(self, abort_msg);
}
diff --git a/runtime/interpreter/interpreter_common.h b/runtime/interpreter/interpreter_common.h
index c9a5b44..423f054 100644
--- a/runtime/interpreter/interpreter_common.h
+++ b/runtime/interpreter/interpreter_common.h
@@ -25,6 +25,8 @@
#include <sstream>
#include <atomic>
+#include "android-base/stringprintf.h"
+
#include "art_field-inl.h"
#include "art_method-inl.h"
#include "base/enums.h"
@@ -254,7 +256,7 @@
DCHECK_LT(string_idx.index_ % mirror::DexCache::kDexCacheStringCacheSize,
declaring_class->GetDexFile().NumStringIds());
ObjPtr<mirror::String> string_ptr =
- mirror::StringDexCachePair::Lookup(declaring_class->GetDexCacheStrings(),
+ mirror::StringDexCachePair::Lookup(declaring_class->GetDexCache()->GetStrings(),
string_idx.index_,
mirror::DexCache::kDexCacheStringCacheSize).Read();
if (UNLIKELY(string_ptr == nullptr)) {
@@ -430,12 +432,12 @@
#define TRACE_LOG std::cerr
std::ostringstream oss;
oss << shadow_frame.GetMethod()->PrettyMethod()
- << StringPrintf("\n0x%x: ", dex_pc)
+ << android::base::StringPrintf("\n0x%x: ", dex_pc)
<< inst->DumpString(shadow_frame.GetMethod()->GetDexFile()) << "\n";
for (uint32_t i = 0; i < shadow_frame.NumberOfVRegs(); ++i) {
uint32_t raw_value = shadow_frame.GetVReg(i);
ObjPtr<mirror::Object> ref_value = shadow_frame.GetVRegReference(i);
- oss << StringPrintf(" vreg%u=0x%08X", i, raw_value);
+ oss << android::base::StringPrintf(" vreg%u=0x%08X", i, raw_value);
if (ref_value != nullptr) {
if (ref_value->GetClass()->IsStringClass() &&
!ref_value->AsString()->IsValueNull()) {
diff --git a/runtime/interpreter/unstarted_runtime.cc b/runtime/interpreter/unstarted_runtime.cc
index a5b1038..7dd3d3d 100644
--- a/runtime/interpreter/unstarted_runtime.cc
+++ b/runtime/interpreter/unstarted_runtime.cc
@@ -25,6 +25,7 @@
#include <locale>
#include <unordered_map>
+#include "android-base/stringprintf.h"
#include "ScopedLocalRef.h"
#include "art_method-inl.h"
@@ -56,6 +57,9 @@
namespace art {
namespace interpreter {
+using android::base::StringAppendV;
+using android::base::StringPrintf;
+
static void AbortTransactionOrFail(Thread* self, const char* fmt, ...)
__attribute__((__format__(__printf__, 2, 3)))
REQUIRES_SHARED(Locks::mutator_lock_);
diff --git a/runtime/java_vm_ext.cc b/runtime/java_vm_ext.cc
index caf705a..f80c43d 100644
--- a/runtime/java_vm_ext.cc
+++ b/runtime/java_vm_ext.cc
@@ -18,6 +18,8 @@
#include <dlfcn.h>
+#include "android-base/stringprintf.h"
+
#include "art_method.h"
#include "base/dumpable.h"
#include "base/mutex.h"
@@ -42,6 +44,9 @@
namespace art {
+using android::base::StringAppendF;
+using android::base::StringAppendV;
+
static constexpr size_t kGlobalsMax = 51200; // Arbitrary sanity check. (Must fit in 16 bits.)
static constexpr size_t kWeakGlobalsMax = 51200; // Arbitrary sanity check. (Must fit in 16 bits.)
diff --git a/runtime/jdwp/jdwp_adb.cc b/runtime/jdwp/jdwp_adb.cc
index e9d6d07..0eff2ab 100644
--- a/runtime/jdwp/jdwp_adb.cc
+++ b/runtime/jdwp/jdwp_adb.cc
@@ -20,8 +20,9 @@
#include <sys/un.h>
#include <unistd.h>
+#include "android-base/stringprintf.h"
+
#include "base/logging.h"
-#include "base/stringprintf.h"
#include "jdwp/jdwp_priv.h"
#ifdef ART_TARGET_ANDROID
@@ -52,6 +53,8 @@
namespace JDWP {
+using android::base::StringPrintf;
+
struct JdwpAdbState : public JdwpNetStateBase {
public:
explicit JdwpAdbState(JdwpState* state) : JdwpNetStateBase(state) {
diff --git a/runtime/jdwp/jdwp_event.cc b/runtime/jdwp/jdwp_event.cc
index 5574a11..172f52a 100644
--- a/runtime/jdwp/jdwp_event.cc
+++ b/runtime/jdwp/jdwp_event.cc
@@ -21,10 +21,11 @@
#include <string.h>
#include <unistd.h>
+#include "android-base/stringprintf.h"
+
#include "art_field-inl.h"
#include "art_method-inl.h"
#include "base/logging.h"
-#include "base/stringprintf.h"
#include "debugger.h"
#include "jdwp/jdwp_constants.h"
#include "jdwp/jdwp_expand_buf.h"
@@ -103,6 +104,8 @@
namespace JDWP {
+using android::base::StringPrintf;
+
/*
* Stuff to compare against when deciding if a mod matches. Only the
* values for mods valid for the event being evaluated will be filled in.
diff --git a/runtime/jdwp/jdwp_handler.cc b/runtime/jdwp/jdwp_handler.cc
index 0f2d188..964c567 100644
--- a/runtime/jdwp/jdwp_handler.cc
+++ b/runtime/jdwp/jdwp_handler.cc
@@ -20,11 +20,12 @@
#include <memory>
#include <string>
+#include "android-base/stringprintf.h"
+
#include "atomic.h"
#include "base/hex_dump.h"
#include "base/logging.h"
#include "base/macros.h"
-#include "base/stringprintf.h"
#include "debugger.h"
#include "jdwp/jdwp_constants.h"
#include "jdwp/jdwp_event.h"
@@ -39,6 +40,8 @@
namespace JDWP {
+using android::base::StringPrintf;
+
std::string DescribeField(const FieldId& field_id) {
return StringPrintf("%#" PRIx64 " (%s)", field_id, Dbg::GetFieldName(field_id).c_str());
}
diff --git a/runtime/jdwp/jdwp_main.cc b/runtime/jdwp/jdwp_main.cc
index e3bf3e5..7707ba4 100644
--- a/runtime/jdwp/jdwp_main.cc
+++ b/runtime/jdwp/jdwp_main.cc
@@ -20,6 +20,8 @@
#include <time.h>
#include <unistd.h>
+#include "android-base/stringprintf.h"
+
#include "atomic.h"
#include "base/logging.h"
#include "base/time_utils.h"
@@ -31,6 +33,8 @@
namespace JDWP {
+using android::base::StringPrintf;
+
static void* StartJdwpThread(void* arg);
/*
diff --git a/runtime/jdwp/jdwp_request.cc b/runtime/jdwp/jdwp_request.cc
index 18f40a1..6af267e 100644
--- a/runtime/jdwp/jdwp_request.cc
+++ b/runtime/jdwp/jdwp_request.cc
@@ -18,7 +18,8 @@
#include <inttypes.h>
-#include "base/stringprintf.h"
+#include "android-base/stringprintf.h"
+
#include "jdwp/jdwp_priv.h"
namespace art {
@@ -100,7 +101,7 @@
ObjectId Request::ReadObjectId(const char* specific_kind) {
ObjectId id = Read8BE();
- VLOG(jdwp) << StringPrintf(" %s id %#" PRIx64, specific_kind, id);
+ VLOG(jdwp) << android::base::StringPrintf(" %s id %#" PRIx64, specific_kind, id);
return id;
}
diff --git a/runtime/jdwp/jdwp_socket.cc b/runtime/jdwp/jdwp_socket.cc
index 3be7fd6..97662f0 100644
--- a/runtime/jdwp/jdwp_socket.cc
+++ b/runtime/jdwp/jdwp_socket.cc
@@ -26,8 +26,9 @@
#include <sys/types.h>
#include <unistd.h>
+#include "android-base/stringprintf.h"
+
#include "base/logging.h"
-#include "base/stringprintf.h"
#include "jdwp/jdwp_priv.h"
namespace art {
@@ -500,7 +501,7 @@
*/
if (IsAwaitingHandshake()) {
if (memcmp(input_buffer_, kMagicHandshake, kMagicHandshakeLen) != 0) {
- LOG(ERROR) << StringPrintf("ERROR: bad handshake '%.14s'", input_buffer_);
+ LOG(ERROR) << android::base::StringPrintf("ERROR: bad handshake '%.14s'", input_buffer_);
goto fail;
}
diff --git a/runtime/jit/jit.cc b/runtime/jit/jit.cc
index dac2e60..b7125a8 100644
--- a/runtime/jit/jit.cc
+++ b/runtime/jit/jit.cc
@@ -291,7 +291,7 @@
// is not null when we instrument.
thread_pool_.reset(new ThreadPool("Jit thread pool", 1));
thread_pool_->SetPthreadPriority(kJitPoolThreadPthreadPriority);
- thread_pool_->StartWorkers(Thread::Current());
+ Start();
}
void Jit::DeleteThreadPool() {
@@ -710,14 +710,23 @@
}
}
+void Jit::Stop() {
+ Thread* self = Thread::Current();
+ // TODO(ngeoffray): change API to not require calling WaitForCompilationToFinish twice.
+ WaitForCompilationToFinish(self);
+ GetThreadPool()->StopWorkers(self);
+ WaitForCompilationToFinish(self);
+}
+
+void Jit::Start() {
+ GetThreadPool()->StartWorkers(Thread::Current());
+}
+
ScopedJitSuspend::ScopedJitSuspend() {
jit::Jit* jit = Runtime::Current()->GetJit();
was_on_ = (jit != nullptr) && (jit->GetThreadPool() != nullptr);
if (was_on_) {
- Thread* self = Thread::Current();
- jit->WaitForCompilationToFinish(self);
- jit->GetThreadPool()->StopWorkers(self);
- jit->WaitForCompilationToFinish(self);
+ jit->Stop();
}
}
@@ -725,7 +734,7 @@
if (was_on_) {
DCHECK(Runtime::Current()->GetJit() != nullptr);
DCHECK(Runtime::Current()->GetJit()->GetThreadPool() != nullptr);
- Runtime::Current()->GetJit()->GetThreadPool()->StartWorkers(Thread::Current());
+ Runtime::Current()->GetJit()->Start();
}
}
diff --git a/runtime/jit/jit.h b/runtime/jit/jit.h
index a230c78..05c3905 100644
--- a/runtime/jit/jit.h
+++ b/runtime/jit/jit.h
@@ -179,6 +179,12 @@
return thread_pool_.get();
}
+ // Stop the JIT by waiting for all current compilations and enqueued compilations to finish.
+ void Stop();
+
+ // Start JIT threads.
+ void Start();
+
private:
Jit();
diff --git a/runtime/jit/jit_code_cache.cc b/runtime/jit/jit_code_cache.cc
index 1b0ad83..f43e30d 100644
--- a/runtime/jit/jit_code_cache.cc
+++ b/runtime/jit/jit_code_cache.cc
@@ -217,6 +217,7 @@
size_t fp_spill_mask,
const uint8_t* code,
size_t code_size,
+ size_t data_size,
bool osr,
Handle<mirror::ObjectArray<mirror::Object>> roots,
bool has_should_deoptimize_flag,
@@ -230,6 +231,7 @@
fp_spill_mask,
code,
code_size,
+ data_size,
osr,
roots,
has_should_deoptimize_flag,
@@ -246,6 +248,7 @@
fp_spill_mask,
code,
code_size,
+ data_size,
osr,
roots,
has_should_deoptimize_flag,
@@ -513,6 +516,7 @@
size_t fp_spill_mask,
const uint8_t* code,
size_t code_size,
+ size_t data_size,
bool osr,
Handle<mirror::ObjectArray<mirror::Object>> roots,
bool has_should_deoptimize_flag,
@@ -547,6 +551,11 @@
core_spill_mask,
fp_spill_mask,
code_size);
+ DCHECK_EQ(FromStackMapToRoots(stack_map), roots_data);
+ DCHECK_LE(roots_data, stack_map);
+ // Flush data cache, as compiled code references literals in it.
+ FlushDataCache(reinterpret_cast<char*>(roots_data),
+ reinterpret_cast<char*>(roots_data + data_size));
// Flush caches before we remove write permission because on some ARMv8 hardware,
// flushing caches require write permissions.
//
@@ -657,12 +666,12 @@
FreeData(reinterpret_cast<uint8_t*>(roots_data));
}
-void JitCodeCache::ReserveData(Thread* self,
- size_t stack_map_size,
- size_t number_of_roots,
- ArtMethod* method,
- uint8_t** stack_map_data,
- uint8_t** roots_data) {
+size_t JitCodeCache::ReserveData(Thread* self,
+ size_t stack_map_size,
+ size_t number_of_roots,
+ ArtMethod* method,
+ uint8_t** stack_map_data,
+ uint8_t** roots_data) {
size_t table_size = ComputeRootTableSize(number_of_roots);
size_t size = RoundUp(stack_map_size + table_size, sizeof(void*));
uint8_t* result = nullptr;
@@ -695,9 +704,11 @@
*roots_data = result;
*stack_map_data = result + table_size;
FillRootTableLength(*roots_data, number_of_roots);
+ return size;
} else {
*roots_data = nullptr;
*stack_map_data = nullptr;
+ return 0;
}
}
diff --git a/runtime/jit/jit_code_cache.h b/runtime/jit/jit_code_cache.h
index 30e2efb..d97742d 100644
--- a/runtime/jit/jit_code_cache.h
+++ b/runtime/jit/jit_code_cache.h
@@ -106,6 +106,7 @@
size_t fp_spill_mask,
const uint8_t* code,
size_t code_size,
+ size_t data_size,
bool osr,
Handle<mirror::ObjectArray<mirror::Object>> roots,
bool has_should_deoptimize_flag,
@@ -121,12 +122,13 @@
// Allocate a region of data that contain `size` bytes, and potentially space
// for storing `number_of_roots` roots. Returns null if there is no more room.
- void ReserveData(Thread* self,
- size_t size,
- size_t number_of_roots,
- ArtMethod* method,
- uint8_t** stack_map_data,
- uint8_t** roots_data)
+ // Return the number of bytes allocated.
+ size_t ReserveData(Thread* self,
+ size_t size,
+ size_t number_of_roots,
+ ArtMethod* method,
+ uint8_t** stack_map_data,
+ uint8_t** roots_data)
REQUIRES_SHARED(Locks::mutator_lock_)
REQUIRES(!lock_);
@@ -237,6 +239,7 @@
size_t fp_spill_mask,
const uint8_t* code,
size_t code_size,
+ size_t data_size,
bool osr,
Handle<mirror::ObjectArray<mirror::Object>> roots,
bool has_should_deoptimize_flag,
diff --git a/runtime/jni_env_ext.cc b/runtime/jni_env_ext.cc
index 342e0d2..5a3fafa 100644
--- a/runtime/jni_env_ext.cc
+++ b/runtime/jni_env_ext.cc
@@ -19,6 +19,8 @@
#include <algorithm>
#include <vector>
+#include "android-base/stringprintf.h"
+
#include "check_jni.h"
#include "indirect_reference_table.h"
#include "java_vm_ext.h"
@@ -30,6 +32,8 @@
namespace art {
+using android::base::StringPrintf;
+
static constexpr size_t kMonitorsInitial = 32; // Arbitrary.
static constexpr size_t kMonitorsMax = 4096; // Arbitrary sanity check.
diff --git a/runtime/jni_internal_test.cc b/runtime/jni_internal_test.cc
index a421c34..4da5e23 100644
--- a/runtime/jni_internal_test.cc
+++ b/runtime/jni_internal_test.cc
@@ -16,6 +16,8 @@
#include "jni_internal.h"
+#include "android-base/stringprintf.h"
+
#include "art_method-inl.h"
#include "common_compiler_test.h"
#include "indirect_reference_table.h"
@@ -27,6 +29,8 @@
namespace art {
+using android::base::StringPrintf;
+
// TODO: Convert to CommonRuntimeTest. Currently MakeExecutable is used.
class JniInternalTest : public CommonCompilerTest {
protected:
diff --git a/runtime/mem_map.cc b/runtime/mem_map.cc
index 6da72e4..19a65bb 100644
--- a/runtime/mem_map.cc
+++ b/runtime/mem_map.cc
@@ -24,7 +24,8 @@
#include <memory>
#include <sstream>
-#include "base/stringprintf.h"
+#include "android-base/stringprintf.h"
+
#include "base/unix_file/fd_file.h"
#include "os.h"
#include "thread-inl.h"
@@ -42,6 +43,8 @@
namespace art {
+using android::base::StringPrintf;
+
static std::ostream& operator<<(
std::ostream& os,
std::pair<BacktraceMap::const_iterator, BacktraceMap::const_iterator> iters) {
diff --git a/runtime/method_handles.cc b/runtime/method_handles.cc
index 3c22d7f..da510ce 100644
--- a/runtime/method_handles.cc
+++ b/runtime/method_handles.cc
@@ -14,9 +14,10 @@
* limitations under the License.
*/
-#include "method_handles.h"
-
#include "method_handles-inl.h"
+
+#include "android-base/stringprintf.h"
+
#include "jvalue.h"
#include "jvalue-inl.h"
#include "reflection.h"
@@ -25,6 +26,8 @@
namespace art {
+using android::base::StringPrintf;
+
namespace {
#define PRIMITIVES_LIST(V) \
diff --git a/runtime/mirror/array-inl.h b/runtime/mirror/array-inl.h
index 7d7c1d7..a5db0c0 100644
--- a/runtime/mirror/array-inl.h
+++ b/runtime/mirror/array-inl.h
@@ -19,10 +19,11 @@
#include "array.h"
+#include "android-base/stringprintf.h"
+
#include "base/bit_utils.h"
#include "base/casts.h"
#include "base/logging.h"
-#include "base/stringprintf.h"
#include "class-inl.h"
#include "gc/heap-inl.h"
#include "obj_ptr-inl.h"
@@ -167,9 +168,9 @@
#else
// 32-bit.
if (UNLIKELY(size == 0)) {
- self->ThrowOutOfMemoryError(StringPrintf("%s of length %d would overflow",
- array_class->PrettyDescriptor().c_str(),
- component_count).c_str());
+ self->ThrowOutOfMemoryError(android::base::StringPrintf("%s of length %d would overflow",
+ array_class->PrettyDescriptor().c_str(),
+ component_count).c_str());
return nullptr;
}
#endif
diff --git a/runtime/mirror/array.cc b/runtime/mirror/array.cc
index 8afa4aa..cc548b9 100644
--- a/runtime/mirror/array.cc
+++ b/runtime/mirror/array.cc
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-#include "array.h"
+#include "array-inl.h"
#include "class.h"
#include "class-inl.h"
@@ -23,7 +23,6 @@
#include "dex_file-inl.h"
#include "gc/accounting/card_table-inl.h"
#include "object-inl.h"
-#include "object_array.h"
#include "object_array-inl.h"
#include "handle_scope-inl.h"
#include "thread.h"
@@ -32,6 +31,8 @@
namespace art {
namespace mirror {
+using android::base::StringPrintf;
+
// Create a multi-dimensional array of Objects or primitive types.
//
// We have to generate the names for X[], X[][], X[][][], and so on. The
diff --git a/runtime/mirror/class-inl.h b/runtime/mirror/class-inl.h
index 5fdf8f3..2fb8d28 100644
--- a/runtime/mirror/class-inl.h
+++ b/runtime/mirror/class-inl.h
@@ -937,14 +937,6 @@
}
}
-inline void Class::SetDexCacheStrings(StringDexCacheType* new_dex_cache_strings) {
- SetFieldPtr<false>(DexCacheStringsOffset(), new_dex_cache_strings);
-}
-
-inline StringDexCacheType* Class::GetDexCacheStrings() {
- return GetFieldPtr64<StringDexCacheType*>(DexCacheStringsOffset());
-}
-
template<ReadBarrierOption kReadBarrierOption, class Visitor>
void Class::VisitNativeRoots(Visitor& visitor, PointerSize pointer_size) {
for (ArtField& field : GetSFieldsUnchecked()) {
@@ -1095,12 +1087,6 @@
if (methods != new_methods) {
dest->SetMethodsPtrInternal(new_methods);
}
- // Update dex cache strings.
- StringDexCacheType* strings = GetDexCacheStrings();
- StringDexCacheType* new_strings = visitor(strings);
- if (strings != new_strings) {
- dest->SetDexCacheStrings(new_strings);
- }
// Fix up embedded tables.
if (!IsTemp() && ShouldHaveEmbeddedVTable<kVerifyNone, kReadBarrierOption>()) {
for (int32_t i = 0, count = GetEmbeddedVTableLength(); i < count; ++i) {
diff --git a/runtime/mirror/class.cc b/runtime/mirror/class.cc
index a862c97..9964b73 100644
--- a/runtime/mirror/class.cc
+++ b/runtime/mirror/class.cc
@@ -16,6 +16,8 @@
#include "class.h"
+#include "android-base/stringprintf.h"
+
#include "art_field-inl.h"
#include "art_method-inl.h"
#include "class_ext.h"
@@ -40,6 +42,8 @@
namespace art {
namespace mirror {
+using android::base::StringPrintf;
+
GcRoot<Class> Class::java_lang_Class_;
void Class::SetClassClass(ObjPtr<Class> java_lang_Class) {
@@ -187,7 +191,6 @@
void Class::SetDexCache(ObjPtr<DexCache> new_dex_cache) {
SetFieldObject<false>(OFFSET_OF_OBJECT_MEMBER(Class, dex_cache_), new_dex_cache);
- SetDexCacheStrings(new_dex_cache != nullptr ? new_dex_cache->GetStrings() : nullptr);
}
void Class::SetClassSize(uint32_t new_class_size) {
diff --git a/runtime/mirror/class.h b/runtime/mirror/class.h
index d7449c8..fb2792a 100644
--- a/runtime/mirror/class.h
+++ b/runtime/mirror/class.h
@@ -1247,13 +1247,6 @@
bool GetSlowPathEnabled() REQUIRES_SHARED(Locks::mutator_lock_);
void SetSlowPath(bool enabled) REQUIRES_SHARED(Locks::mutator_lock_);
- StringDexCacheType* GetDexCacheStrings() REQUIRES_SHARED(Locks::mutator_lock_);
- void SetDexCacheStrings(StringDexCacheType* new_dex_cache_strings)
- REQUIRES_SHARED(Locks::mutator_lock_);
- static MemberOffset DexCacheStringsOffset() {
- return OFFSET_OF_OBJECT_MEMBER(Class, dex_cache_strings_);
- }
-
// May cause thread suspension due to EqualParameters.
ArtMethod* GetDeclaredConstructor(Thread* self,
Handle<ObjectArray<Class>> args,
@@ -1438,9 +1431,6 @@
// virtual_ methods_ for miranda methods.
HeapReference<PointerArray> vtable_;
- // Short cuts to dex_cache_ member for fast compiled code access.
- uint64_t dex_cache_strings_;
-
// instance fields
//
// These describe the layout of the contents of an Object.
diff --git a/runtime/mirror/object_array-inl.h b/runtime/mirror/object_array-inl.h
index 0fdf132..3e04bf6 100644
--- a/runtime/mirror/object_array-inl.h
+++ b/runtime/mirror/object_array-inl.h
@@ -17,12 +17,13 @@
#ifndef ART_RUNTIME_MIRROR_OBJECT_ARRAY_INL_H_
#define ART_RUNTIME_MIRROR_OBJECT_ARRAY_INL_H_
-#include <string>
-
#include "object_array.h"
+#include <string>
+
+#include "android-base/stringprintf.h"
+
#include "array-inl.h"
-#include "base/stringprintf.h"
#include "gc/heap.h"
#include "mirror/class.h"
#include "obj_ptr-inl.h"
@@ -330,13 +331,15 @@
std::string actualSrcType(mirror::Object::PrettyTypeOf(o));
std::string dstType(PrettyTypeOf());
Thread* self = Thread::Current();
+ std::string msg = android::base::StringPrintf(
+ "source[%d] of type %s cannot be stored in destination array of type %s",
+ src_pos + i,
+ actualSrcType.c_str(),
+ dstType.c_str());
if (throw_exception) {
- self->ThrowNewExceptionF("Ljava/lang/ArrayStoreException;",
- "source[%d] of type %s cannot be stored in destination array of type %s",
- src_pos + i, actualSrcType.c_str(), dstType.c_str());
+ self->ThrowNewException("Ljava/lang/ArrayStoreException;", msg.c_str());
} else {
- LOG(FATAL) << StringPrintf("source[%d] of type %s cannot be stored in destination array of type %s",
- src_pos + i, actualSrcType.c_str(), dstType.c_str());
+ LOG(FATAL) << msg;
}
}
}
diff --git a/runtime/mirror/string-inl.h b/runtime/mirror/string-inl.h
index 95516ac..9b8445d 100644
--- a/runtime/mirror/string-inl.h
+++ b/runtime/mirror/string-inl.h
@@ -16,6 +16,10 @@
#ifndef ART_RUNTIME_MIRROR_STRING_INL_H_
#define ART_RUNTIME_MIRROR_STRING_INL_H_
+#include "string.h"
+
+#include "android-base/stringprintf.h"
+
#include "array.h"
#include "base/bit_utils.h"
#include "class.h"
@@ -24,7 +28,6 @@
#include "globals.h"
#include "intern_table.h"
#include "runtime.h"
-#include "string.h"
#include "thread.h"
#include "utf.h"
#include "utils.h"
@@ -228,9 +231,10 @@
"kObjectAlignment must be at least as big as Java char alignment");
const size_t max_length = RoundDown(max_alloc_length, kObjectAlignment / block_size);
if (UNLIKELY(length > max_length)) {
- self->ThrowOutOfMemoryError(StringPrintf("%s of length %d would overflow",
- Class::PrettyDescriptor(string_class).c_str(),
- static_cast<int>(length)).c_str());
+ self->ThrowOutOfMemoryError(
+ android::base::StringPrintf("%s of length %d would overflow",
+ Class::PrettyDescriptor(string_class).c_str(),
+ static_cast<int>(length)).c_str());
return nullptr;
}
diff --git a/runtime/mirror/throwable.cc b/runtime/mirror/throwable.cc
index ade4e87..e50409f 100644
--- a/runtime/mirror/throwable.cc
+++ b/runtime/mirror/throwable.cc
@@ -16,6 +16,8 @@
#include "throwable.h"
+#include "android-base/stringprintf.h"
+
#include "art_method-inl.h"
#include "base/enums.h"
#include "class-inl.h"
@@ -31,6 +33,8 @@
namespace art {
namespace mirror {
+using android::base::StringPrintf;
+
GcRoot<Class> Throwable::java_lang_Throwable_;
void Throwable::SetDetailMessage(ObjPtr<String> new_detail_message) {
diff --git a/runtime/monitor.cc b/runtime/monitor.cc
index e7de7e6..222eb5c 100644
--- a/runtime/monitor.cc
+++ b/runtime/monitor.cc
@@ -18,6 +18,8 @@
#include <vector>
+#include "android-base/stringprintf.h"
+
#include "art_method-inl.h"
#include "base/mutex.h"
#include "base/stl_util.h"
@@ -38,6 +40,8 @@
namespace art {
+using android::base::StringPrintf;
+
static constexpr uint64_t kLongWaitMs = 100;
/*
diff --git a/runtime/native/dalvik_system_DexFile.cc b/runtime/native/dalvik_system_DexFile.cc
index 1a77072..cd0e55f 100644
--- a/runtime/native/dalvik_system_DexFile.cc
+++ b/runtime/native/dalvik_system_DexFile.cc
@@ -18,9 +18,10 @@
#include <sstream>
+#include "android-base/stringprintf.h"
+
#include "base/logging.h"
#include "base/stl_util.h"
-#include "base/stringprintf.h"
#include "class_linker.h"
#include "common_throws.h"
#include "compiler_filter.h"
@@ -43,6 +44,8 @@
namespace art {
+using android::base::StringPrintf;
+
static bool ConvertJavaArrayToDexFiles(
JNIEnv* env,
jobject arrayObject,
diff --git a/runtime/native/dalvik_system_InMemoryDexClassLoader_DexData.cc b/runtime/native/dalvik_system_InMemoryDexClassLoader_DexData.cc
index db245aa..981be68 100644
--- a/runtime/native/dalvik_system_InMemoryDexClassLoader_DexData.cc
+++ b/runtime/native/dalvik_system_InMemoryDexClassLoader_DexData.cc
@@ -16,6 +16,8 @@
#include "dalvik_system_InMemoryDexClassLoader_DexData.h"
+#include "android-base/stringprintf.h"
+
#include "class_linker.h"
#include "common_throws.h"
#include "dex_file.h"
@@ -29,6 +31,8 @@
namespace art {
+using android::base::StringPrintf;
+
static std::unique_ptr<MemMap> AllocateDexMemoryMap(JNIEnv* env, jint start, jint end) {
if (end <= start) {
ScopedObjectAccess soa(env);
diff --git a/runtime/native/dalvik_system_VMRuntime.cc b/runtime/native/dalvik_system_VMRuntime.cc
index 3058df4..1af8619 100644
--- a/runtime/native/dalvik_system_VMRuntime.cc
+++ b/runtime/native/dalvik_system_VMRuntime.cc
@@ -27,6 +27,8 @@
#include "toStringArray.h"
#pragma GCC diagnostic pop
+#include "android-base/stringprintf.h"
+
#include "art_method-inl.h"
#include "arch/instruction_set.h"
#include "base/enums.h"
@@ -54,6 +56,8 @@
namespace art {
+using android::base::StringPrintf;
+
static jfloat VMRuntime_getTargetHeapUtilization(JNIEnv*, jobject) {
return Runtime::Current()->GetHeap()->GetTargetHeapUtilization();
}
diff --git a/runtime/native/dalvik_system_ZygoteHooks.cc b/runtime/native/dalvik_system_ZygoteHooks.cc
index a78909b..10fc90b 100644
--- a/runtime/native/dalvik_system_ZygoteHooks.cc
+++ b/runtime/native/dalvik_system_ZygoteHooks.cc
@@ -18,6 +18,8 @@
#include <stdlib.h>
+#include "android-base/stringprintf.h"
+
#include "arch/instruction_set.h"
#include "debugger.h"
#include "java_vm_ext.h"
@@ -37,6 +39,8 @@
namespace art {
+using android::base::StringPrintf;
+
static void EnableDebugger() {
#if defined(__linux__)
// To let a non-privileged gdbserver attach to this
diff --git a/runtime/native/java_lang_ref_Reference.cc b/runtime/native/java_lang_ref_Reference.cc
index bedca10..c778068 100644
--- a/runtime/native/java_lang_ref_Reference.cc
+++ b/runtime/native/java_lang_ref_Reference.cc
@@ -33,8 +33,15 @@
return soa.AddLocalReference<jobject>(referent);
}
+static void Reference_clearReferent(JNIEnv* env, jobject javaThis) {
+ ScopedFastNativeObjectAccess soa(env);
+ ObjPtr<mirror::Reference> ref = soa.Decode<mirror::Reference>(javaThis);
+ Runtime::Current()->GetHeap()->GetReferenceProcessor()->ClearReferent(ref);
+}
+
static JNINativeMethod gMethods[] = {
NATIVE_METHOD(Reference, getReferent, "!()Ljava/lang/Object;"),
+ NATIVE_METHOD(Reference, clearReferent, "!()V"),
};
void register_java_lang_ref_Reference(JNIEnv* env) {
diff --git a/runtime/native/java_lang_reflect_Executable.cc b/runtime/native/java_lang_reflect_Executable.cc
index 73b81a7..ee59c4a 100644
--- a/runtime/native/java_lang_reflect_Executable.cc
+++ b/runtime/native/java_lang_reflect_Executable.cc
@@ -16,6 +16,8 @@
#include "java_lang_reflect_Executable.h"
+#include "android-base/stringprintf.h"
+
#include "art_method-inl.h"
#include "dex_file_annotations.h"
#include "handle.h"
@@ -30,6 +32,8 @@
namespace art {
+using android::base::StringPrintf;
+
static jobjectArray Executable_getDeclaredAnnotationsNative(JNIEnv* env, jobject javaMethod) {
ScopedFastNativeObjectAccess soa(env);
ArtMethod* method = ArtMethod::FromReflectedMethod(soa, javaMethod);
diff --git a/runtime/native/java_lang_reflect_Field.cc b/runtime/native/java_lang_reflect_Field.cc
index 6206948..374eeb5 100644
--- a/runtime/native/java_lang_reflect_Field.cc
+++ b/runtime/native/java_lang_reflect_Field.cc
@@ -16,6 +16,8 @@
#include "java_lang_reflect_Field.h"
+#include "android-base/stringprintf.h"
+
#include "class_linker.h"
#include "class_linker-inl.h"
#include "common_throws.h"
@@ -30,6 +32,8 @@
namespace art {
+using android::base::StringPrintf;
+
template<bool kIsSet>
ALWAYS_INLINE inline static bool VerifyFieldAccess(Thread* self,
ObjPtr<mirror::Field> field,
diff --git a/runtime/native/java_lang_reflect_Parameter.cc b/runtime/native/java_lang_reflect_Parameter.cc
index 16164d2..0bb9e38 100644
--- a/runtime/native/java_lang_reflect_Parameter.cc
+++ b/runtime/native/java_lang_reflect_Parameter.cc
@@ -16,6 +16,8 @@
#include "java_lang_reflect_Parameter.h"
+#include "android-base/stringprintf.h"
+
#include "art_method-inl.h"
#include "common_throws.h"
#include "dex_file-inl.h"
@@ -26,6 +28,8 @@
namespace art {
+using android::base::StringPrintf;
+
static jobject Parameter_getAnnotationNative(JNIEnv* env,
jclass,
jobject javaMethod,
diff --git a/runtime/native_stack_dump.cc b/runtime/native_stack_dump.cc
index 5565565..7460d62 100644
--- a/runtime/native_stack_dump.cc
+++ b/runtime/native_stack_dump.cc
@@ -37,10 +37,11 @@
#include <sys/time.h>
#include <sys/types.h>
+#include "android-base/stringprintf.h"
+
#include "arch/instruction_set.h"
#include "base/memory_tool.h"
#include "base/mutex.h"
-#include "base/stringprintf.h"
#include "base/unix_file/fd_file.h"
#include "oat_quick_method_header.h"
#include "os.h"
@@ -53,6 +54,8 @@
#if defined(__linux__)
+using android::base::StringPrintf;
+
static constexpr bool kUseAddr2line = !kIsTargetBuild;
ALWAYS_INLINE
diff --git a/runtime/oat.cc b/runtime/oat.cc
index aab0e81..cebe765 100644
--- a/runtime/oat.cc
+++ b/runtime/oat.cc
@@ -19,12 +19,15 @@
#include <string.h>
#include <zlib.h>
+#include "android-base/stringprintf.h"
+
#include "arch/instruction_set_features.h"
#include "base/bit_utils.h"
-#include "base/stringprintf.h"
namespace art {
+using android::base::StringPrintf;
+
constexpr uint8_t OatHeader::kOatMagic[4];
constexpr uint8_t OatHeader::kOatVersion[4];
constexpr const char OatHeader::kTrueValue[];
diff --git a/runtime/oat_file.cc b/runtime/oat_file.cc
index bdf8b0e..0bf7136 100644
--- a/runtime/oat_file.cc
+++ b/runtime/oat_file.cc
@@ -32,6 +32,8 @@
#include "android/dlext.h"
#endif
+#include "android-base/stringprintf.h"
+
#include "art_method-inl.h"
#include "base/bit_vector.h"
#include "base/enums.h"
@@ -58,6 +60,8 @@
namespace art {
+using android::base::StringPrintf;
+
// Whether OatFile::Open will try dlopen. Fallback is our own ELF loader.
static constexpr bool kUseDlopen = true;
diff --git a/runtime/oat_file_assistant.cc b/runtime/oat_file_assistant.cc
index 7f7b1b5..ee7cf9d 100644
--- a/runtime/oat_file_assistant.cc
+++ b/runtime/oat_file_assistant.cc
@@ -23,7 +23,6 @@
#include "android-base/strings.h"
#include "base/logging.h"
-#include "base/stringprintf.h"
#include "compiler_filter.h"
#include "class_linker.h"
#include "gc/heap.h"
diff --git a/runtime/oat_file_manager.cc b/runtime/oat_file_manager.cc
index 5641459..33bd0f3 100644
--- a/runtime/oat_file_manager.cc
+++ b/runtime/oat_file_manager.cc
@@ -20,6 +20,8 @@
#include <queue>
#include <vector>
+#include "android-base/stringprintf.h"
+
#include "base/logging.h"
#include "base/stl_util.h"
#include "base/systrace.h"
@@ -38,6 +40,8 @@
namespace art {
+using android::base::StringPrintf;
+
// If true, then we attempt to load the application image if it exists.
static constexpr bool kEnableAppImage = true;
diff --git a/runtime/openjdkjvmti/OpenjdkJvmTi.cc b/runtime/openjdkjvmti/OpenjdkJvmTi.cc
index 1ad3f08..5f97b60 100644
--- a/runtime/openjdkjvmti/OpenjdkJvmTi.cc
+++ b/runtime/openjdkjvmti/OpenjdkJvmTi.cc
@@ -700,7 +700,7 @@
jmethodID method,
jint* entry_count_ptr,
jvmtiLineNumberEntry** table_ptr) {
- return ERR(NOT_IMPLEMENTED);
+ return MethodUtil::GetLineNumberTable(env, method, entry_count_ptr, table_ptr);
}
static jvmtiError GetMethodLocation(jvmtiEnv* env,
diff --git a/runtime/openjdkjvmti/ti_method.cc b/runtime/openjdkjvmti/ti_method.cc
index ffa5ac7..a0a0923 100644
--- a/runtime/openjdkjvmti/ti_method.cc
+++ b/runtime/openjdkjvmti/ti_method.cc
@@ -130,4 +130,63 @@
return ERR(NONE);
}
+using LineNumberContext = std::vector<jvmtiLineNumberEntry>;
+
+static bool CollectLineNumbers(void* void_context, const art::DexFile::PositionInfo& entry) {
+ LineNumberContext* context = reinterpret_cast<LineNumberContext*>(void_context);
+ jvmtiLineNumberEntry jvmti_entry = { static_cast<jlocation>(entry.address_),
+ static_cast<jint>(entry.line_) };
+ context->push_back(jvmti_entry);
+ return false; // Collect all, no early exit.
+}
+
+jvmtiError MethodUtil::GetLineNumberTable(jvmtiEnv* env,
+ jmethodID method,
+ jint* entry_count_ptr,
+ jvmtiLineNumberEntry** table_ptr) {
+ if (method == nullptr) {
+ return ERR(NULL_POINTER);
+ }
+ art::ArtMethod* art_method = art::jni::DecodeArtMethod(method);
+ DCHECK(!art_method->IsRuntimeMethod());
+
+ const art::DexFile::CodeItem* code_item;
+ const art::DexFile* dex_file;
+ {
+ art::ScopedObjectAccess soa(art::Thread::Current());
+
+ if (art_method->IsProxyMethod()) {
+ return ERR(ABSENT_INFORMATION);
+ }
+ if (art_method->IsNative()) {
+ return ERR(NATIVE_METHOD);
+ }
+ if (entry_count_ptr == nullptr || table_ptr == nullptr) {
+ return ERR(NULL_POINTER);
+ }
+
+ code_item = art_method->GetCodeItem();
+ dex_file = art_method->GetDexFile();
+ DCHECK(code_item != nullptr) << art_method->PrettyMethod() << " " << dex_file->GetLocation();
+ }
+
+ LineNumberContext context;
+ bool success = dex_file->DecodeDebugPositionInfo(code_item, CollectLineNumbers, &context);
+ if (!success) {
+ return ERR(ABSENT_INFORMATION);
+ }
+
+ unsigned char* data;
+ jlong mem_size = context.size() * sizeof(jvmtiLineNumberEntry);
+ jvmtiError alloc_error = env->Allocate(mem_size, &data);
+ if (alloc_error != ERR(NONE)) {
+ return alloc_error;
+ }
+ *table_ptr = reinterpret_cast<jvmtiLineNumberEntry*>(data);
+ memcpy(*table_ptr, context.data(), mem_size);
+ *entry_count_ptr = static_cast<jint>(context.size());
+
+ return ERR(NONE);
+}
+
} // namespace openjdkjvmti
diff --git a/runtime/openjdkjvmti/ti_method.h b/runtime/openjdkjvmti/ti_method.h
index 43f11f9..fb2fbb2 100644
--- a/runtime/openjdkjvmti/ti_method.h
+++ b/runtime/openjdkjvmti/ti_method.h
@@ -52,6 +52,11 @@
static jvmtiError GetMethodModifiers(jvmtiEnv* env,
jmethodID method,
jint* modifiers_ptr);
+
+ static jvmtiError GetLineNumberTable(jvmtiEnv* env,
+ jmethodID method,
+ jint* entry_count_ptr,
+ jvmtiLineNumberEntry** table_ptr);
};
} // namespace openjdkjvmti
diff --git a/runtime/openjdkjvmti/ti_redefine.cc b/runtime/openjdkjvmti/ti_redefine.cc
index d0349b9..68815e7 100644
--- a/runtime/openjdkjvmti/ti_redefine.cc
+++ b/runtime/openjdkjvmti/ti_redefine.cc
@@ -33,6 +33,8 @@
#include <limits>
+#include "android-base/stringprintf.h"
+
#include "art_jvmti.h"
#include "base/logging.h"
#include "events-inl.h"
@@ -49,13 +51,15 @@
namespace openjdkjvmti {
+using android::base::StringPrintf;
+
// Moves dex data to an anonymous, read-only mmap'd region.
std::unique_ptr<art::MemMap> Redefiner::MoveDataToMemMap(const std::string& original_location,
jint data_len,
unsigned char* dex_data,
std::string* error_msg) {
std::unique_ptr<art::MemMap> map(art::MemMap::MapAnonymous(
- art::StringPrintf("%s-transformed", original_location.c_str()).c_str(),
+ StringPrintf("%s-transformed", original_location.c_str()).c_str(),
nullptr,
data_len,
PROT_READ|PROT_WRITE,
@@ -246,9 +250,9 @@
}
void Redefiner::RecordFailure(jvmtiError result, const std::string& error_msg) {
- *error_msg_ = art::StringPrintf("Unable to perform redefinition of '%s': %s",
- class_sig_,
- error_msg.c_str());
+ *error_msg_ = StringPrintf("Unable to perform redefinition of '%s': %s",
+ class_sig_,
+ error_msg.c_str());
result_ = result;
}
@@ -392,19 +396,14 @@
}
}
-// Performs updates to class that will allow us to verify it.
-bool Redefiner::UpdateClass(art::ObjPtr<art::mirror::Class> mclass,
- art::ObjPtr<art::mirror::DexCache> new_dex_cache) {
+bool Redefiner::UpdateMethods(art::ObjPtr<art::mirror::Class> mclass,
+ art::ObjPtr<art::mirror::DexCache> new_dex_cache,
+ const art::DexFile::ClassDef& class_def) {
art::ClassLinker* linker = runtime_->GetClassLinker();
art::PointerSize image_pointer_size = linker->GetImagePointerSize();
- const art::DexFile::ClassDef* class_def = art::OatFile::OatDexFile::FindClassDef(
- *dex_file_, class_sig_, art::ComputeModifiedUtf8Hash(class_sig_));
- if (class_def == nullptr) {
- RecordFailure(ERR(INVALID_CLASS_FORMAT), "Unable to find ClassDef!");
- return false;
- }
- const art::DexFile::TypeId& declaring_class_id = dex_file_->GetTypeId(class_def->class_idx_);
+ const art::DexFile::TypeId& declaring_class_id = dex_file_->GetTypeId(class_def.class_idx_);
const art::DexFile& old_dex_file = mclass->GetDexFile();
+ // Update methods.
for (art::ArtMethod& method : mclass->GetMethods(image_pointer_size)) {
const art::DexFile::StringId* new_name_id = dex_file_->FindStringId(method.GetName());
art::dex::TypeIndex method_return_idx =
@@ -431,15 +430,58 @@
uint32_t dex_method_idx = dex_file_->GetIndexForMethodId(*method_id);
method.SetDexMethodIndex(dex_method_idx);
linker->SetEntryPointsToInterpreter(&method);
- method.SetCodeItemOffset(dex_file_->FindCodeItemOffset(*class_def, dex_method_idx));
+ method.SetCodeItemOffset(dex_file_->FindCodeItemOffset(class_def, dex_method_idx));
method.SetDexCacheResolvedMethods(new_dex_cache->GetResolvedMethods(), image_pointer_size);
method.SetDexCacheResolvedTypes(new_dex_cache->GetResolvedTypes(), image_pointer_size);
}
+ return true;
+}
+
+bool Redefiner::UpdateFields(art::ObjPtr<art::mirror::Class> mclass) {
+ // TODO The IFields & SFields pointers should be combined like the methods_ arrays were.
+ for (auto fields_iter : {mclass->GetIFields(), mclass->GetSFields()}) {
+ for (art::ArtField& field : fields_iter) {
+ std::string declaring_class_name;
+ const art::DexFile::TypeId* new_declaring_id =
+ dex_file_->FindTypeId(field.GetDeclaringClass()->GetDescriptor(&declaring_class_name));
+ const art::DexFile::StringId* new_name_id = dex_file_->FindStringId(field.GetName());
+ const art::DexFile::TypeId* new_type_id = dex_file_->FindTypeId(field.GetTypeDescriptor());
+ // TODO Handle error, cleanup.
+ CHECK(new_name_id != nullptr && new_type_id != nullptr && new_declaring_id != nullptr);
+ const art::DexFile::FieldId* new_field_id =
+ dex_file_->FindFieldId(*new_declaring_id, *new_name_id, *new_type_id);
+ CHECK(new_field_id != nullptr);
+ // We only need to update the index since the other data in the ArtField cannot be updated.
+ field.SetDexFieldIndex(dex_file_->GetIndexForFieldId(*new_field_id));
+ }
+ }
+ return true;
+}
+
+// Performs updates to class that will allow us to verify it.
+bool Redefiner::UpdateClass(art::ObjPtr<art::mirror::Class> mclass,
+ art::ObjPtr<art::mirror::DexCache> new_dex_cache) {
+ const art::DexFile::ClassDef* class_def = art::OatFile::OatDexFile::FindClassDef(
+ *dex_file_, class_sig_, art::ComputeModifiedUtf8Hash(class_sig_));
+ if (class_def == nullptr) {
+ RecordFailure(ERR(INVALID_CLASS_FORMAT), "Unable to find ClassDef!");
+ return false;
+ }
+ if (!UpdateMethods(mclass, new_dex_cache, *class_def)) {
+ // TODO Investigate appropriate error types.
+ RecordFailure(ERR(INTERNAL), "Unable to update class methods.");
+ return false;
+ }
+ if (!UpdateFields(mclass)) {
+ // TODO Investigate appropriate error types.
+ RecordFailure(ERR(INTERNAL), "Unable to update class fields.");
+ return false;
+ }
+
// Update the class fields.
// Need to update class last since the ArtMethod gets its DexFile from the class (which is needed
// to call GetReturnTypeDescriptor and GetParameterTypeList above).
mclass->SetDexCache(new_dex_cache.Ptr());
- mclass->SetDexCacheStrings(new_dex_cache->GetStrings());
mclass->SetDexClassDefIndex(dex_file_->GetIndexForClassDef(*class_def));
mclass->SetDexTypeIndex(dex_file_->GetIndexForTypeId(*dex_file_->FindTypeId(class_sig_)));
return true;
diff --git a/runtime/openjdkjvmti/ti_redefine.h b/runtime/openjdkjvmti/ti_redefine.h
index c819acd..73cfc2b 100644
--- a/runtime/openjdkjvmti/ti_redefine.h
+++ b/runtime/openjdkjvmti/ti_redefine.h
@@ -159,6 +159,14 @@
art::ObjPtr<art::mirror::LongArray> original_cookie)
REQUIRES(art::Locks::mutator_lock_);
+ bool UpdateFields(art::ObjPtr<art::mirror::Class> mclass)
+ REQUIRES(art::Locks::mutator_lock_);
+
+ bool UpdateMethods(art::ObjPtr<art::mirror::Class> mclass,
+ art::ObjPtr<art::mirror::DexCache> new_dex_cache,
+ const art::DexFile::ClassDef& class_def)
+ REQUIRES(art::Locks::mutator_lock_);
+
bool UpdateClass(art::ObjPtr<art::mirror::Class> mclass,
art::ObjPtr<art::mirror::DexCache> new_dex_cache)
REQUIRES(art::Locks::mutator_lock_);
diff --git a/runtime/parsed_options_test.cc b/runtime/parsed_options_test.cc
index 5b90c6a..8948c71 100644
--- a/runtime/parsed_options_test.cc
+++ b/runtime/parsed_options_test.cc
@@ -19,7 +19,6 @@
#include <memory>
#include "arch/instruction_set.h"
-#include "base/stringprintf.h"
#include "common_runtime_test.h"
namespace art {
diff --git a/runtime/plugin.cc b/runtime/plugin.cc
index 481b1ca..731967c 100644
--- a/runtime/plugin.cc
+++ b/runtime/plugin.cc
@@ -17,11 +17,15 @@
#include "plugin.h"
#include <dlfcn.h>
-#include "base/stringprintf.h"
+
+#include "android-base/stringprintf.h"
+
#include "base/logging.h"
namespace art {
+using android::base::StringPrintf;
+
const char* PLUGIN_INITIALIZATION_FUNCTION_NAME = "ArtPlugin_Initialize";
const char* PLUGIN_DEINITIALIZATION_FUNCTION_NAME = "ArtPlugin_Deinitialize";
diff --git a/runtime/reference_table.cc b/runtime/reference_table.cc
index 1c975a4..d8b9dcc 100644
--- a/runtime/reference_table.cc
+++ b/runtime/reference_table.cc
@@ -16,6 +16,8 @@
#include "reference_table.h"
+#include "android-base/stringprintf.h"
+
#include "base/mutex.h"
#include "indirect_reference_table.h"
#include "mirror/array.h"
@@ -30,6 +32,9 @@
namespace art {
+using android::base::StringAppendF;
+using android::base::StringPrintf;
+
ReferenceTable::ReferenceTable(const char* name, size_t initial_size, size_t max_size)
: name_(name), max_size_(max_size) {
CHECK_LE(initial_size, max_size);
diff --git a/runtime/reference_table_test.cc b/runtime/reference_table_test.cc
index d80a9b3..9523e92 100644
--- a/runtime/reference_table_test.cc
+++ b/runtime/reference_table_test.cc
@@ -16,6 +16,8 @@
#include "reference_table.h"
+#include "android-base/stringprintf.h"
+
#include "class_linker.h"
#include "common_runtime_test.h"
#include "handle_scope-inl.h"
@@ -30,6 +32,8 @@
namespace art {
+using android::base::StringPrintf;
+
class ReferenceTableTest : public CommonRuntimeTest {};
static mirror::Object* CreateWeakReference(mirror::Object* referent)
diff --git a/runtime/reflection-inl.h b/runtime/reflection-inl.h
index 68e7a10..62ce9e9 100644
--- a/runtime/reflection-inl.h
+++ b/runtime/reflection-inl.h
@@ -19,7 +19,8 @@
#include "reflection.h"
-#include "base/stringprintf.h"
+#include "android-base/stringprintf.h"
+
#include "common_throws.h"
#include "jvalue-inl.h"
#include "mirror/object-inl.h"
@@ -103,13 +104,14 @@
}
if (!unbox_for_result) {
- ThrowIllegalArgumentException(StringPrintf("Invalid primitive conversion from %s to %s",
- PrettyDescriptor(srcType).c_str(),
- PrettyDescriptor(dstType).c_str()).c_str());
+ ThrowIllegalArgumentException(
+ android::base::StringPrintf("Invalid primitive conversion from %s to %s",
+ PrettyDescriptor(srcType).c_str(),
+ PrettyDescriptor(dstType).c_str()).c_str());
} else {
- ThrowClassCastException(StringPrintf("Couldn't convert result of type %s to %s",
- PrettyDescriptor(srcType).c_str(),
- PrettyDescriptor(dstType).c_str()).c_str());
+ ThrowClassCastException(android::base::StringPrintf("Couldn't convert result of type %s to %s",
+ PrettyDescriptor(srcType).c_str(),
+ PrettyDescriptor(dstType).c_str()).c_str());
}
return false;
}
diff --git a/runtime/reflection.cc b/runtime/reflection.cc
index 8446b52..4d24501 100644
--- a/runtime/reflection.cc
+++ b/runtime/reflection.cc
@@ -34,6 +34,8 @@
namespace art {
+using android::base::StringPrintf;
+
class ArgArray {
public:
ArgArray(const char* shorty, uint32_t shorty_len)
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index 59c5961..2086d70 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -1199,6 +1199,8 @@
CHECK_EQ(self->GetThreadId(), ThreadList::kMainThreadId);
CHECK(self != nullptr);
+ self->SetCanCallIntoJava(!IsAotCompiler());
+
// Set us to runnable so tools using a runtime can allocate and GC by default
self->TransitionFromSuspendedToRunnable();
@@ -1721,7 +1723,7 @@
if (space->IsImageSpace()) {
auto* image_space = space->AsImageSpace();
const auto& image_header = image_space->GetImageHeader();
- for (size_t i = 0; i < ImageHeader::kImageRootsMax; ++i) {
+ for (int32_t i = 0, size = image_header.GetImageRoots()->GetLength(); i != size; ++i) {
auto* obj = image_header.GetImageRoot(static_cast<ImageHeader::ImageRoot>(i));
if (obj != nullptr) {
auto* after_obj = obj;
diff --git a/runtime/runtime_android.cc b/runtime/runtime_android.cc
index be97860..0a996a9 100644
--- a/runtime/runtime_android.cc
+++ b/runtime/runtime_android.cc
@@ -21,7 +21,6 @@
#include "base/logging.h"
#include "base/mutex.h"
-#include "base/stringprintf.h"
#include "thread-inl.h"
#include "utils.h"
diff --git a/runtime/runtime_linux.cc b/runtime/runtime_linux.cc
index 93704a9..b8894d2 100644
--- a/runtime/runtime_linux.cc
+++ b/runtime/runtime_linux.cc
@@ -24,11 +24,12 @@
#include <iostream>
#include <sstream>
+#include "android-base/stringprintf.h"
+
#include "base/dumpable.h"
#include "base/logging.h"
#include "base/macros.h"
#include "base/mutex.h"
-#include "base/stringprintf.h"
#include "native_stack_dump.h"
#include "thread-inl.h"
#include "thread_list.h"
@@ -36,6 +37,8 @@
namespace art {
+using android::base::StringPrintf;
+
static constexpr bool kUseSigRTTimeout = true;
static constexpr bool kDumpNativeStackOnTimeout = true;
diff --git a/runtime/stack.cc b/runtime/stack.cc
index 792da88..3fed7c9 100644
--- a/runtime/stack.cc
+++ b/runtime/stack.cc
@@ -16,6 +16,8 @@
#include "stack.h"
+#include "android-base/stringprintf.h"
+
#include "arch/context.h"
#include "art_method-inl.h"
#include "base/enums.h"
@@ -39,6 +41,8 @@
namespace art {
+using android::base::StringPrintf;
+
static constexpr bool kDebugStackWalk = false;
mirror::Object* ShadowFrame::GetThisObject() const {
diff --git a/runtime/thread.cc b/runtime/thread.cc
index d79bf36..aff12ff 100644
--- a/runtime/thread.cc
+++ b/runtime/thread.cc
@@ -28,6 +28,8 @@
#include <list>
#include <sstream>
+#include "android-base/stringprintf.h"
+
#include "arch/context.h"
#include "art_field-inl.h"
#include "art_method-inl.h"
@@ -88,6 +90,9 @@
namespace art {
+using android::base::StringAppendV;
+using android::base::StringPrintf;
+
extern "C" NO_RETURN void artDeoptimize(Thread* self);
bool Thread::is_started_ = false;
@@ -1802,7 +1807,11 @@
}
}
-Thread::Thread(bool daemon) : tls32_(daemon), wait_monitor_(nullptr), interrupted_(false) {
+Thread::Thread(bool daemon)
+ : tls32_(daemon),
+ wait_monitor_(nullptr),
+ interrupted_(false),
+ can_call_into_java_(true) {
wait_mutex_ = new Mutex("a thread wait mutex");
wait_cond_ = new ConditionVariable("a thread wait condition variable", *wait_mutex_);
tlsPtr_.instrumentation_stack = new std::deque<instrumentation::InstrumentationStackFrame>;
diff --git a/runtime/thread.h b/runtime/thread.h
index 31cd0eb..411d85f 100644
--- a/runtime/thread.h
+++ b/runtime/thread.h
@@ -881,6 +881,15 @@
--tls32_.disable_thread_flip_count;
}
+ // Returns true if the thread is allowed to call into java.
+ bool CanCallIntoJava() const {
+ return can_call_into_java_;
+ }
+
+ void SetCanCallIntoJava(bool can_call_into_java) {
+ can_call_into_java_ = can_call_into_java;
+ }
+
// Activates single step control for debugging. The thread takes the
// ownership of the given SingleStepControl*. It is deleted by a call
// to DeactivateSingleStepControl or upon thread destruction.
@@ -1588,6 +1597,10 @@
// Pending extra checkpoints if checkpoint_function_ is already used.
std::list<Closure*> checkpoint_overflow_ GUARDED_BY(Locks::thread_suspend_count_lock_);
+ // True if the thread is allowed to call back into java (for e.g. during class resolution).
+ // By default this is true.
+ bool can_call_into_java_;
+
friend class Dbg; // For SetStateUnsafe.
friend class gc::collector::SemiSpace; // For getting stack traces.
friend class Runtime; // For CreatePeer.
diff --git a/runtime/thread_list.cc b/runtime/thread_list.cc
index 664eeb4..bf9eef8 100644
--- a/runtime/thread_list.cc
+++ b/runtime/thread_list.cc
@@ -25,6 +25,8 @@
#include <sstream>
+#include "android-base/stringprintf.h"
+
#include "base/histogram-inl.h"
#include "base/mutex-inl.h"
#include "base/systrace.h"
@@ -52,6 +54,8 @@
namespace art {
+using android::base::StringPrintf;
+
static constexpr uint64_t kLongThreadSuspendThreshold = MsToNs(5);
static constexpr uint64_t kThreadSuspendTimeoutMs = 30 * 1000; // 30s.
// Use 0 since we want to yield to prevent blocking for an unpredictable amount of time.
diff --git a/runtime/thread_pool.cc b/runtime/thread_pool.cc
index d9d2ea3..d9179c3 100644
--- a/runtime/thread_pool.cc
+++ b/runtime/thread_pool.cc
@@ -21,6 +21,8 @@
#include <sys/time.h>
#include <sys/resource.h>
+#include "android-base/stringprintf.h"
+
#include "base/bit_utils.h"
#include "base/casts.h"
#include "base/logging.h"
@@ -31,6 +33,8 @@
namespace art {
+using android::base::StringPrintf;
+
static constexpr bool kMeasureWaitTime = false;
ThreadPoolWorker::ThreadPoolWorker(ThreadPool* thread_pool, const std::string& name,
@@ -86,6 +90,8 @@
Runtime* runtime = Runtime::Current();
CHECK(runtime->AttachCurrentThread(worker->name_.c_str(), true, nullptr, false));
worker->thread_ = Thread::Current();
+ // Thread pool workers cannot call into java.
+ worker->thread_->SetCanCallIntoJava(false);
// Do work until its time to shut down.
worker->Run();
runtime->DetachCurrentThread();
diff --git a/runtime/thread_pool.h b/runtime/thread_pool.h
index eaadfe0..7ecfcd1 100644
--- a/runtime/thread_pool.h
+++ b/runtime/thread_pool.h
@@ -80,6 +80,7 @@
DISALLOW_COPY_AND_ASSIGN(ThreadPoolWorker);
};
+// Note that thread pool workers will set Thread#setCanCallIntoJava to false.
class ThreadPool {
public:
// Returns the number of threads in the thread pool.
diff --git a/runtime/ti/agent.cc b/runtime/ti/agent.cc
index d21ff77..0bba44c 100644
--- a/runtime/ti/agent.cc
+++ b/runtime/ti/agent.cc
@@ -15,12 +15,17 @@
*/
#include "agent.h"
+
+#include "android-base/stringprintf.h"
+
#include "java_vm_ext.h"
#include "runtime.h"
namespace art {
namespace ti {
+using android::base::StringPrintf;
+
const char* AGENT_ON_LOAD_FUNCTION_NAME = "Agent_OnLoad";
const char* AGENT_ON_ATTACH_FUNCTION_NAME = "Agent_OnAttach";
const char* AGENT_ON_UNLOAD_FUNCTION_NAME = "Agent_OnUnload";
diff --git a/runtime/ti/agent.h b/runtime/ti/agent.h
index 6561756..7408aee 100644
--- a/runtime/ti/agent.h
+++ b/runtime/ti/agent.h
@@ -20,7 +20,6 @@
#include <dlfcn.h>
#include <jni.h> // for jint, JavaVM* etc declarations
-#include "base/stringprintf.h"
#include "runtime.h"
#include "utils.h"
diff --git a/runtime/trace.cc b/runtime/trace.cc
index f564de4..9d9360e 100644
--- a/runtime/trace.cc
+++ b/runtime/trace.cc
@@ -19,6 +19,8 @@
#include <sys/uio.h>
#include <unistd.h>
+#include "android-base/stringprintf.h"
+
#include "art_method-inl.h"
#include "base/casts.h"
#include "base/enums.h"
@@ -46,6 +48,8 @@
namespace art {
+using android::base::StringPrintf;
+
static constexpr size_t TraceActionBits = MinimumBitsToStore(
static_cast<size_t>(kTraceMethodActionMask));
static constexpr uint8_t kOpNewMethod = 1U;
diff --git a/runtime/utils.cc b/runtime/utils.cc
index 4732f59..8867743 100644
--- a/runtime/utils.cc
+++ b/runtime/utils.cc
@@ -25,6 +25,7 @@
#include <unistd.h>
#include <memory>
+#include "android-base/stringprintf.h"
#include "android-base/strings.h"
#include "base/stl_util.h"
@@ -48,6 +49,9 @@
namespace art {
+using android::base::StringAppendF;
+using android::base::StringPrintf;
+
static const uint8_t kBase64Map[256] = {
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
diff --git a/runtime/utils.h b/runtime/utils.h
index 04e0dde..16ef706 100644
--- a/runtime/utils.h
+++ b/runtime/utils.h
@@ -284,6 +284,12 @@
__builtin___clear_cache(begin, end);
}
+inline void FlushDataCache(char* begin, char* end) {
+ // Same as FlushInstructionCache for lack of other builtin. __builtin___clear_cache
+ // flushes both caches.
+ __builtin___clear_cache(begin, end);
+}
+
template <typename T>
constexpr PointerSize ConvertToPointerSize(T any) {
if (any == 4 || any == 8) {
diff --git a/runtime/verifier/method_verifier.cc b/runtime/verifier/method_verifier.cc
index ebecc85..7e23c8b 100644
--- a/runtime/verifier/method_verifier.cc
+++ b/runtime/verifier/method_verifier.cc
@@ -18,6 +18,8 @@
#include <iostream>
+#include "android-base/stringprintf.h"
+
#include "art_field-inl.h"
#include "art_method-inl.h"
#include "base/enums.h"
@@ -55,6 +57,8 @@
namespace art {
namespace verifier {
+using android::base::StringPrintf;
+
static constexpr bool kTimeVerifyMethod = !kIsDebugBuild;
static constexpr bool kDebugVerify = false;
// TODO: Add a constant to method_verifier to turn on verbose logging?
diff --git a/runtime/verifier/reg_type.cc b/runtime/verifier/reg_type.cc
index ab23773..52f7e34 100644
--- a/runtime/verifier/reg_type.cc
+++ b/runtime/verifier/reg_type.cc
@@ -16,6 +16,8 @@
#include "reg_type-inl.h"
+#include "android-base/stringprintf.h"
+
#include "base/arena_bit_vector.h"
#include "base/bit_vector-inl.h"
#include "base/casts.h"
@@ -35,6 +37,8 @@
namespace art {
namespace verifier {
+using android::base::StringPrintf;
+
const UndefinedType* UndefinedType::instance_ = nullptr;
const ConflictType* ConflictType::instance_ = nullptr;
const BooleanType* BooleanType::instance_ = nullptr;
diff --git a/runtime/verifier/register_line.cc b/runtime/verifier/register_line.cc
index a6088aa..383d890 100644
--- a/runtime/verifier/register_line.cc
+++ b/runtime/verifier/register_line.cc
@@ -16,7 +16,8 @@
#include "register_line.h"
-#include "base/stringprintf.h"
+#include "android-base/stringprintf.h"
+
#include "dex_instruction-inl.h"
#include "method_verifier-inl.h"
#include "register_line-inl.h"
@@ -25,6 +26,8 @@
namespace art {
namespace verifier {
+using android::base::StringPrintf;
+
bool RegisterLine::CheckConstructorReturn(MethodVerifier* verifier) const {
if (kIsDebugBuild && this_initialized_) {
// Ensure that there is no UninitializedThisReference type anymore if this_initialized_ is true.
diff --git a/runtime/well_known_classes.cc b/runtime/well_known_classes.cc
index 3549586..009170c 100644
--- a/runtime/well_known_classes.cc
+++ b/runtime/well_known_classes.cc
@@ -20,6 +20,8 @@
#include <sstream>
+#include "android-base/stringprintf.h"
+
#include "base/logging.h"
#include "entrypoints/quick/quick_entrypoints_enum.h"
#include "jni_internal.h"
@@ -189,7 +191,7 @@
static jmethodID CachePrimitiveBoxingMethod(JNIEnv* env, char prim_name, const char* boxed_name) {
ScopedLocalRef<jclass> boxed_class(env, env->FindClass(boxed_name));
return CacheMethod(env, boxed_class.get(), true, "valueOf",
- StringPrintf("(%c)L%s;", prim_name, boxed_name).c_str());
+ android::base::StringPrintf("(%c)L%s;", prim_name, boxed_name).c_str());
}
#define STRING_INIT_LIST(V) \
diff --git a/runtime/zip_archive.cc b/runtime/zip_archive.cc
index d96fb42..cd79bb6 100644
--- a/runtime/zip_archive.cc
+++ b/runtime/zip_archive.cc
@@ -23,7 +23,6 @@
#include <unistd.h>
#include <vector>
-#include "base/stringprintf.h"
#include "base/unix_file/fd_file.h"
namespace art {
diff --git a/test/137-cfi/cfi.cc b/test/137-cfi/cfi.cc
index 113b35f..3b237f4 100644
--- a/test/137-cfi/cfi.cc
+++ b/test/137-cfi/cfi.cc
@@ -25,11 +25,11 @@
#include "jni.h"
+#include "android-base/stringprintf.h"
#include <backtrace/Backtrace.h>
#include "base/logging.h"
#include "base/macros.h"
-#include "base/stringprintf.h"
#include "gc/heap.h"
#include "gc/space/image_space.h"
#include "oat_file.h"
@@ -91,7 +91,7 @@
static void MoreErrorInfo(pid_t pid, bool sig_quit_on_fail) {
printf("Secondary pid is %d\n", pid);
- PrintFileToLog(StringPrintf("/proc/%d/maps", pid), ::android::base::ERROR);
+ PrintFileToLog(android::base::StringPrintf("/proc/%d/maps", pid), ::android::base::ERROR);
if (sig_quit_on_fail) {
int res = kill(pid, SIGQUIT);
diff --git a/test/141-class-unload/expected.txt b/test/141-class-unload/expected.txt
index 0a03ecb..a1c5fa8 100644
--- a/test/141-class-unload/expected.txt
+++ b/test/141-class-unload/expected.txt
@@ -20,5 +20,6 @@
JNI_OnLoad called
class null false test
JNI_OnUnload called
+JNI_OnLoad called
Number of loaded unload-ex maps 0
Too small false
diff --git a/test/141-class-unload/jni_unload.cc b/test/141-class-unload/jni_unload.cc
index bbbb0a6..9b7e171 100644
--- a/test/141-class-unload/jni_unload.cc
+++ b/test/141-class-unload/jni_unload.cc
@@ -32,5 +32,19 @@
}
}
+extern "C" JNIEXPORT void JNICALL Java_Main_stopJit(JNIEnv*, jclass) {
+ jit::Jit* jit = Runtime::Current()->GetJit();
+ if (jit != nullptr) {
+ jit->Stop();
+ }
+}
+
+extern "C" JNIEXPORT void JNICALL Java_Main_startJit(JNIEnv*, jclass) {
+ jit::Jit* jit = Runtime::Current()->GetJit();
+ if (jit != nullptr) {
+ jit->Start();
+ }
+}
+
} // namespace
} // namespace art
diff --git a/test/141-class-unload/src/Main.java b/test/141-class-unload/src/Main.java
index 2a6e944..595c70d 100644
--- a/test/141-class-unload/src/Main.java
+++ b/test/141-class-unload/src/Main.java
@@ -55,11 +55,15 @@
}
private static void testOatFilesUnloaded(int pid) throws Exception {
+ System.loadLibrary(nativeLibraryName);
+ // Stop the JIT to ensure its threads and work queue are not keeping classes
+ // artifically alive.
+ stopJit();
+ Runtime.getRuntime().gc();
+ System.runFinalization();
BufferedReader reader = new BufferedReader(new FileReader ("/proc/" + pid + "/maps"));
String line;
int count = 0;
- Runtime.getRuntime().gc();
- System.runFinalization();
while ((line = reader.readLine()) != null) {
if (line.contains("@141-class-unload-ex.jar")) {
System.out.println(line);
@@ -67,6 +71,7 @@
}
}
System.out.println("Number of loaded unload-ex maps " + count);
+ startJit();
}
private static void stressTest(Constructor<?> constructor) throws Exception {
@@ -229,4 +234,7 @@
private static int getPid() throws Exception {
return Integer.parseInt(new File("/proc/self").getCanonicalFile().getName());
}
+
+ public static native void stopJit();
+ public static native void startJit();
}
diff --git a/test/153-reference-stress/expected.txt b/test/153-reference-stress/expected.txt
new file mode 100644
index 0000000..7ef22e9
--- /dev/null
+++ b/test/153-reference-stress/expected.txt
@@ -0,0 +1 @@
+PASS
diff --git a/test/153-reference-stress/info.txt b/test/153-reference-stress/info.txt
new file mode 100644
index 0000000..6bc0040
--- /dev/null
+++ b/test/153-reference-stress/info.txt
@@ -0,0 +1 @@
+Tests java.lang.ref.Reference.get() and GC running in parallel.
diff --git a/test/153-reference-stress/src/Main.java b/test/153-reference-stress/src/Main.java
new file mode 100644
index 0000000..fc6f9cc
--- /dev/null
+++ b/test/153-reference-stress/src/Main.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.lang.ref.WeakReference;
+
+public class Main {
+ static final int numWeakReferences = 16 * 1024;
+ static WeakReference[] weakReferences = new WeakReference[numWeakReferences];
+ static volatile boolean done = false;
+ static Object keepAlive;
+
+ public static void main(String[] args) throws Exception {
+ // Try to call Reference.get repeatedly while the GC is running.
+ Thread gcThread = new GcThread();
+ Thread[] readerThread = new ReaderThread[4];
+ for (int i = 0; i < readerThread.length; ++i) {
+ readerThread[i] = new ReaderThread();
+ }
+ gcThread.start();
+ for (int i = 0; i < readerThread.length; ++i) {
+ readerThread[i].start();
+ }
+ gcThread.join();
+ for (int i = 0; i < readerThread.length; ++i) {
+ readerThread[i].join();
+ }
+ System.out.println("PASS");
+ }
+
+ static class GcThread extends Thread {
+ GcThread() {
+ Object temp = new Object();
+ for (int j = 0; j < weakReferences.length; ++j) {
+ weakReferences[j] = new WeakReference(temp);
+ }
+ }
+ public void run() {
+ for (int i = 0; i < 1000; ++i) {
+ Object o = new Object();
+ for (int j = 0; j < weakReferences.length; ++j) {
+ weakReferences[j] = new WeakReference(o);
+ }
+ }
+ done = true;
+ }
+ }
+
+ static class ReaderThread extends Thread {
+ public void run() {
+ while (!done) {
+ for (int j = 0; j < weakReferences.length; ++j) {
+ keepAlive = weakReferences[j].get();
+ }
+ for (int j = 0; j < weakReferences.length; ++j) {
+ weakReferences[j].clear();
+ }
+ }
+ }
+ }
+}
diff --git a/test/530-checker-loops3/src/Main.java b/test/530-checker-loops3/src/Main.java
index 209786a..dfc4a5f 100644
--- a/test/530-checker-loops3/src/Main.java
+++ b/test/530-checker-loops3/src/Main.java
@@ -235,6 +235,59 @@
}
}
+ /// CHECK-START: void Main.shortBound1(int[], short) BCE (before)
+ /// CHECK-DAG: BoundsCheck loop:{{B\d+}}
+ //
+ /// CHECK-START: void Main.shortBound1(int[], short) BCE (after)
+ /// CHECK-DAG: Deoptimize loop:none
+ /// CHECK-DAG: Deoptimize loop:none
+ /// CHECK-DAG: Deoptimize loop:none
+ /// CHECK-NOT: Deoptimize
+ //
+ /// CHECK-START: void Main.shortBound1(int[], short) BCE (after)
+ /// CHECK-NOT: BoundsCheck
+ public static void shortBound1(int[] array, short s) {
+ // Lower precision bound will appear in deopt arithmetic
+ // and follows normal implicit widening conversion.
+ for (int i = 0; i < s; i++) {
+ array[i] = 222;
+ }
+ }
+
+ /// CHECK-START: void Main.shortBound2(int[], short) BCE (before)
+ /// CHECK-DAG: BoundsCheck loop:{{B\d+}}
+ //
+ /// CHECK-START: void Main.shortBound2(int[], short) BCE (after)
+ /// CHECK-DAG: Deoptimize loop:none
+ /// CHECK-DAG: Deoptimize loop:none
+ /// CHECK-DAG: Deoptimize loop:none
+ /// CHECK-NOT: Deoptimize
+ //
+ /// CHECK-START: void Main.shortBound2(int[], short) BCE (after)
+ /// CHECK-NOT: BoundsCheck
+ public static void shortBound2(int[] array, short s) {
+ // Lower precision bound will appear in deopt arithmetic
+ // and follows normal implicit widening conversion.
+ for (int i = 0; s > i; i++) {
+ array[i] = 444;
+ }
+ }
+
+ /// CHECK-START: void Main.narrowingFromLong(int[], int) BCE (before)
+ /// CHECK-DAG: BoundsCheck loop:{{B\d+}}
+ //
+ /// CHECK-START: void Main.narrowingFromLong(int[], int) BCE (after)
+ /// CHECK-DAG: BoundsCheck loop:{{B\d+}}
+ public static void narrowingFromLong(int[] array, int n) {
+ // Parallel induction in long precision that is narrowed provides type
+ // conversion challenges for BCE in deopt arithmetic when combined
+ // with the int loop induction. Therefore, currently skipped.
+ long l = 0;
+ for (int i = 0; i < n; i++, l++) {
+ array[(int)l] = 888;
+ }
+ }
+
//
// Verifier.
//
@@ -316,6 +369,38 @@
} catch (ArrayIndexOutOfBoundsException e) {
}
+ shortBound1(a, (short)a.length);
+ for (int i = 0; i < a.length; i++) {
+ expectEquals(222, a[i]);
+ }
+ shortBound2(a, (short)a.length);
+ for (int i = 0; i < a.length; i++) {
+ expectEquals(444, a[i]);
+ }
+
+ try {
+ shortBound1(a, (short)(a.length + 1));
+ throw new Error("Should throw AIOOBE");
+ } catch (ArrayIndexOutOfBoundsException e) {
+ }
+ for (int i = 0; i < a.length; i++) {
+ expectEquals(222, a[i]);
+ }
+
+ try {
+ shortBound2(a, (short)(a.length + 1));
+ throw new Error("Should throw AIOOBE");
+ } catch (ArrayIndexOutOfBoundsException e) {
+ }
+ for (int i = 0; i < a.length; i++) {
+ expectEquals(444, a[i]);
+ }
+
+ narrowingFromLong(a, a.length);
+ for (int i = 0; i < a.length; i++) {
+ expectEquals(888, a[i]);
+ }
+
System.out.println("passed");
}
diff --git a/test/530-checker-loops4/src/Main.java b/test/530-checker-loops4/src/Main.java
index 7d3d7d9..91af1f4 100644
--- a/test/530-checker-loops4/src/Main.java
+++ b/test/530-checker-loops4/src/Main.java
@@ -96,7 +96,29 @@
/// CHECK-NOT: Phi
public static int geo4(int a) {
for (int i = 0; i < 10; i++) {
- a %= 7;
+ a %= 7; // a wrap-around induction
+ }
+ return a;
+ }
+
+ /// CHECK-START: int Main.geo5() loop_optimization (before)
+ /// CHECK-DAG: Phi loop:<<Loop:B\d+>>
+ /// CHECK-DAG: Shr loop:<<Loop>>
+ //
+ /// CHECK-START: int Main.geo5() loop_optimization (after)
+ /// CHECK-DAG: <<Zero:i\d+>> IntConstant 0 loop:none
+ /// CHECK-DAG: <<Int1:i\d+>> IntConstant 2147483647 loop:none
+ /// CHECK-DAG: <<Int2:i\d+>> IntConstant 1024 loop:none
+ /// CHECK-DAG: <<Div:i\d+>> Div [<<Int1>>,<<Int2>>] loop:none
+ /// CHECK-DAG: <<Add:i\d+>> Add [<<Div>>,<<Zero>>] loop:none
+ /// CHECK-DAG: Return [<<Add>>] loop:none
+ //
+ /// CHECK-START: int Main.geo5() loop_optimization (after)
+ /// CHECK-NOT: Phi
+ public static int geo5() {
+ int a = 0x7fffffff;
+ for (int i = 0; i < 10; i++) {
+ a >>= 1;
}
return a;
}
@@ -186,7 +208,7 @@
int r = 0;
for (int i = 0; i < 100; i++) { // a converges to 0
r += x[a];
- a %= 5;
+ a %= 5; // a wrap-around induction
}
return r;
}
@@ -305,6 +327,8 @@
expectEquals(i % 7, geo4(i));
}
+ expectEquals(0x1fffff, geo5());
+
expectEquals(34, geo1BCE());
expectEquals(36, geo2BCE());
expectEquals(131, geo3BCE());
diff --git a/test/552-checker-sharpening/src/Main.java b/test/552-checker-sharpening/src/Main.java
index 9e475ab..fe6ff13 100644
--- a/test/552-checker-sharpening/src/Main.java
+++ b/test/552-checker-sharpening/src/Main.java
@@ -55,6 +55,9 @@
/// CHECK-NOT: MipsDexCacheArraysBase
/// CHECK: InvokeStaticOrDirect method_load_kind:dex_cache_pc_relative
+ /// CHECK-START-MIPS64: int Main.testSimple(int) sharpening (after)
+ /// CHECK: InvokeStaticOrDirect method_load_kind:dex_cache_pc_relative
+
/// CHECK-START-X86: int Main.testSimple(int) sharpening (after)
/// CHECK-NOT: X86ComputeBaseMethodAddress
/// CHECK: InvokeStaticOrDirect method_load_kind:dex_cache_pc_relative
@@ -96,6 +99,10 @@
/// CHECK: InvokeStaticOrDirect method_load_kind:dex_cache_pc_relative
/// CHECK: InvokeStaticOrDirect method_load_kind:dex_cache_pc_relative
+ /// CHECK-START-MIPS64: int Main.testDiamond(boolean, int) sharpening (after)
+ /// CHECK: InvokeStaticOrDirect method_load_kind:dex_cache_pc_relative
+ /// CHECK: InvokeStaticOrDirect method_load_kind:dex_cache_pc_relative
+
/// CHECK-START-X86: int Main.testDiamond(boolean, int) sharpening (after)
/// CHECK-NOT: X86ComputeBaseMethodAddress
/// CHECK: InvokeStaticOrDirect method_load_kind:dex_cache_pc_relative
@@ -274,6 +281,11 @@
// TODO: Remove DexCacheViaMethod when read barrier config supports BootImageAddress.
/// CHECK: LoadString load_kind:{{BootImageAddress|BssEntry|DexCacheViaMethod}}
+ /// CHECK-START-MIPS64: java.lang.String Main.$noinline$getBootImageString() sharpening (after)
+ // Note: load kind depends on PIC/non-PIC
+ // TODO: Remove DexCacheViaMethod when read barrier config supports BootImageAddress.
+ /// CHECK: LoadString load_kind:{{BootImageAddress|BssEntry|DexCacheViaMethod}}
+
public static String $noinline$getBootImageString() {
// Prevent inlining to avoid the string comparison being optimized away.
if (doThrow) { throw new Error(); }
@@ -303,6 +315,9 @@
/// CHECK-START-MIPS: java.lang.String Main.$noinline$getNonBootImageString() sharpening (after)
/// CHECK: LoadString load_kind:BssEntry
+ /// CHECK-START-MIPS64: java.lang.String Main.$noinline$getNonBootImageString() sharpening (after)
+ /// CHECK: LoadString load_kind:BssEntry
+
public static String $noinline$getNonBootImageString() {
// Prevent inlining to avoid the string comparison being optimized away.
if (doThrow) { throw new Error(); }
@@ -338,6 +353,11 @@
// TODO: Remove DexCacheViaMethod when read barrier config supports BootImageAddress.
/// CHECK: LoadClass load_kind:{{BootImageAddress|DexCachePcRelative|DexCacheViaMethod}} class_name:java.lang.String
+ /// CHECK-START-MIPS64: java.lang.Class Main.$noinline$getStringClass() sharpening (after)
+ // Note: load kind depends on PIC/non-PIC
+ // TODO: Remove DexCacheViaMethod when read barrier config supports BootImageAddress.
+ /// CHECK: LoadClass load_kind:{{BootImageAddress|DexCachePcRelative|DexCacheViaMethod}} class_name:java.lang.String
+
public static Class<?> $noinline$getStringClass() {
// Prevent inlining to avoid the string comparison being optimized away.
if (doThrow) { throw new Error(); }
@@ -375,6 +395,9 @@
/// CHECK-DAG: MipsDexCacheArraysBase
/// CHECK-DAG: LoadClass load_kind:DexCachePcRelative class_name:Other
+ /// CHECK-START-MIPS64: java.lang.Class Main.$noinline$getOtherClass() sharpening (after)
+ /// CHECK: LoadClass load_kind:DexCachePcRelative class_name:Other
+
public static Class<?> $noinline$getOtherClass() {
// Prevent inlining to avoid the string comparison being optimized away.
if (doThrow) { throw new Error(); }
diff --git a/test/616-cha-regression-proxy-method/expected.txt b/test/616-cha-regression-proxy-method/expected.txt
new file mode 100644
index 0000000..6a5618e
--- /dev/null
+++ b/test/616-cha-regression-proxy-method/expected.txt
@@ -0,0 +1 @@
+JNI_OnLoad called
diff --git a/test/616-cha-regression-proxy-method/info.txt b/test/616-cha-regression-proxy-method/info.txt
new file mode 100644
index 0000000..386a07f
--- /dev/null
+++ b/test/616-cha-regression-proxy-method/info.txt
@@ -0,0 +1 @@
+Regression test for Class Hierarchy Analysis (CHA) on visiting proxy method frame.
diff --git a/test/616-cha-regression-proxy-method/src/Main.java b/test/616-cha-regression-proxy-method/src/Main.java
new file mode 100644
index 0000000..19c92be
--- /dev/null
+++ b/test/616-cha-regression-proxy-method/src/Main.java
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+
+class Main1 {
+ void foo(int i) {
+ if (i != 1) {
+ printError("error1");
+ }
+ }
+
+ void printError(String msg) {
+ System.out.println(msg);
+ }
+}
+
+class Main2 extends Main1 {
+ void foo(int i) {
+ if (i != 2) {
+ printError("error2");
+ }
+ }
+}
+
+class Proxied implements Runnable {
+ public void run() {
+ synchronized(Main.class) {
+ Main.sOtherThreadStarted = true;
+ // Wait for Main2 to be linked and deoptimization is triggered.
+ try {
+ Main.class.wait();
+ } catch (Exception e) {
+ }
+ }
+ }
+}
+
+class MyInvocationHandler implements InvocationHandler {
+ private final Proxied proxied;
+
+ public MyInvocationHandler(Proxied proxied) {
+ this.proxied = proxied;
+ }
+
+ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
+ return method.invoke(proxied, args);
+ }
+}
+
+public class Main {
+ static Main1 sMain1;
+ static Main1 sMain2;
+ static volatile boolean sOtherThreadStarted;
+
+ // sMain1.foo() will be always be Main1.foo() before Main2 is loaded/linked.
+ // So sMain1.foo() can be devirtualized to Main1.foo() and be inlined.
+ // After Dummy.createMain2() which links in Main2, live testOverride() on stack
+ // should be deoptimized.
+ static void testOverride() {
+ sMain1.foo(sMain1.getClass() == Main1.class ? 1 : 2);
+
+ // Wait for the other thread to start.
+ while (!sOtherThreadStarted);
+ // Create an Main2 instance and assign it to sMain2.
+ // sMain1 is kept the same.
+ sMain2 = Dummy.createMain2();
+ // Wake up the other thread.
+ synchronized(Main.class) {
+ Main.class.notify();
+ }
+
+ // There should be a deoptimization here right after Main2 is linked by
+ // calling Dummy.createMain2(), even though sMain1 didn't change.
+ // The behavior here would be different if inline-cache is used, which
+ // doesn't deoptimize since sMain1 still hits the type cache.
+ sMain1.foo(sMain1.getClass() == Main1.class ? 1 : 2);
+ if (sMain2 != null) {
+ sMain2.foo(sMain2.getClass() == Main1.class ? 1 : 2);
+ }
+ }
+
+ // Test scenarios under which CHA-based devirtualization happens,
+ // and class loading that overrides a method can invalidate compiled code.
+ // Also create a proxy method such that a proxy method's frame is visited
+ // during stack walking.
+ public static void main(String[] args) {
+ System.loadLibrary(args[0]);
+ // sMain1 is an instance of Main1. Main2 hasn't bee loaded yet.
+ sMain1 = new Main1();
+
+ // Create another thread that calls a proxy method.
+ new Thread() {
+ public void run() {
+ Runnable proxy = (Runnable)Proxy.newProxyInstance(
+ Proxied.class.getClassLoader(),
+ new Class[] { Runnable.class },
+ new MyInvocationHandler(new Proxied()));
+ proxy.run();
+ }
+ }.start();
+
+ ensureJitCompiled(Main.class, "testOverride");
+ // This will create Main2 instance in the middle of testOverride().
+ testOverride();
+ }
+
+ private static native void ensureJitCompiled(Class<?> itf, String method_name);
+}
+
+// Put createMain2() in another class to avoid class loading due to verifier.
+class Dummy {
+ static Main1 createMain2() {
+ return new Main2();
+ }
+}
diff --git a/test/616-cha/src/Main.java b/test/616-cha/src/Main.java
index 787318d..b617944 100644
--- a/test/616-cha/src/Main.java
+++ b/test/616-cha/src/Main.java
@@ -179,7 +179,7 @@
}
}
- // Test scanerios under which CHA-based devirtualization happens,
+ // Test scenarios under which CHA-based devirtualization happens,
// and class loading that overrides a method can invalidate compiled code.
// Also test pure non-overriding case, which is more for checking generated
// code form.
@@ -206,11 +206,6 @@
// sMain1 is an instance of Main1. Main2 hasn't bee loaded yet.
sMain1 = new Main1();
- // Loop enough to get testOverride() JITed.
- for (int i=0; i<100; i++) {
- testOverride(false, false, false);
- }
-
ensureJitCompiled(Main.class, "testOverride");
testOverride(false, false, true);
@@ -244,7 +239,7 @@
private static native boolean hasSingleImplementation(Class<?> clazz, String method_name);
}
-// Do it in another class to avoid class loading due to verifier.
+// Put createMain2() in another class to avoid class loading due to verifier.
class Dummy {
static Main1 createMain2() {
return new Main2();
diff --git a/test/618-checker-induction/src/Main.java b/test/618-checker-induction/src/Main.java
index 87a69b2..ad3ff44 100644
--- a/test/618-checker-induction/src/Main.java
+++ b/test/618-checker-induction/src/Main.java
@@ -248,6 +248,33 @@
return closed; // only needs last value
}
+ /// CHECK-START: int Main.closedFormInductionTrivialIf() loop_optimization (before)
+ /// CHECK-DAG: <<Phi1:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Phi2:i\d+>> Phi loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: Select loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: Return [<<Phi1>>] loop:none
+ //
+ /// CHECK-START: int Main.closedFormInductionTrivialIf() loop_optimization (after)
+ /// CHECK-NOT: Phi
+ /// CHECK-NOT: Select
+ //
+ /// CHECK-START: int Main.closedFormInductionTrivialIf() instruction_simplifier$after_bce (after)
+ /// CHECK-DAG: <<Int:i\d+>> IntConstant 81 loop:none
+ /// CHECK-DAG: Return [<<Int>>] loop:none
+ static int closedFormInductionTrivialIf() {
+ int closed = 11;
+ for (int i = 0; i < 10; i++) {
+ // Trivial if becomes trivial select at HIR level.
+ // Make sure this is still recognized as induction.
+ if (i < 5) {
+ closed += 7;
+ } else {
+ closed += 7;
+ }
+ }
+ return closed; // only needs last value
+ }
+
/// CHECK-START: int Main.closedFormNested() loop_optimization (before)
/// CHECK-DAG: <<Phi1:i\d+>> Phi loop:<<Loop1:B\d+>> outer_loop:none
/// CHECK-DAG: <<Phi2:i\d+>> Phi loop:<<Loop1>> outer_loop:none
@@ -438,6 +465,20 @@
return i;
}
+ // TODO: handle as closed/empty eventually?
+ static int mainIndexShort1(short s) {
+ int i = 0;
+ for (i = 0; i < s; i++) { }
+ return i;
+ }
+
+ // TODO: handle as closed/empty eventually?
+ static int mainIndexShort2(short s) {
+ int i = 0;
+ for (i = 0; s > i; i++) { }
+ return i;
+ }
+
/// CHECK-START: int Main.periodicReturnedN(int) loop_optimization (before)
/// CHECK-DAG: <<Phi1:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
/// CHECK-DAG: <<Phi2:i\d+>> Phi loop:<<Loop>> outer_loop:none
@@ -666,6 +707,75 @@
return x;
}
+ /// CHECK-START: float Main.periodicFloat10() loop_optimization (before)
+ /// CHECK-DAG: <<Phi1:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Phi2:f\d+>> Phi loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Phi3:f\d+>> Phi loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Phi4:f\d+>> Phi loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: Return [<<Phi2>>] loop:none
+ //
+ /// CHECK-START: float Main.periodicFloat10() loop_optimization (after)
+ /// CHECK-NOT: Phi
+ //
+ /// CHECK-START: float Main.periodicFloat10() loop_optimization (after)
+ /// CHECK-DAG: <<Float:f\d+>> FloatConstant 2 loop:none
+ /// CHECK-DAG: Return [<<Float>>] loop:none
+ private static float periodicFloat10() {
+ float r = 4.5f;
+ float s = 2.0f;
+ float t = -1.0f;
+ for (int i = 0; i < 10; i++) {
+ float tmp = t; t = r; r = s; s = tmp;
+ }
+ return r;
+ }
+
+ /// CHECK-START: float Main.periodicFloat11() loop_optimization (before)
+ /// CHECK-DAG: <<Phi1:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Phi2:f\d+>> Phi loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Phi3:f\d+>> Phi loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Phi4:f\d+>> Phi loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: Return [<<Phi2>>] loop:none
+ //
+ /// CHECK-START: float Main.periodicFloat11() loop_optimization (after)
+ /// CHECK-NOT: Phi
+ //
+ /// CHECK-START: float Main.periodicFloat11() loop_optimization (after)
+ /// CHECK-DAG: <<Float:f\d+>> FloatConstant -1 loop:none
+ /// CHECK-DAG: Return [<<Float>>] loop:none
+ private static float periodicFloat11() {
+ float r = 4.5f;
+ float s = 2.0f;
+ float t = -1.0f;
+ for (int i = 0; i < 11; i++) {
+ float tmp = t; t = r; r = s; s = tmp;
+ }
+ return r;
+ }
+
+ /// CHECK-START: float Main.periodicFloat12() loop_optimization (before)
+ /// CHECK-DAG: <<Phi1:i\d+>> Phi loop:<<Loop:B\d+>> outer_loop:none
+ /// CHECK-DAG: <<Phi2:f\d+>> Phi loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Phi3:f\d+>> Phi loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: <<Phi4:f\d+>> Phi loop:<<Loop>> outer_loop:none
+ /// CHECK-DAG: Return [<<Phi2>>] loop:none
+ //
+ /// CHECK-START: float Main.periodicFloat12() loop_optimization (after)
+ /// CHECK-NOT: Phi
+ //
+ /// CHECK-START: float Main.periodicFloat12() loop_optimization (after)
+ /// CHECK-DAG: <<Float:f\d+>> FloatConstant 4.5 loop:none
+ /// CHECK-DAG: Return [<<Float>>] loop:none
+ private static float periodicFloat12() {
+ float r = 4.5f;
+ float s = 2.0f;
+ float t = -1.0f;
+ for (int i = 0; i < 12; i++) {
+ float tmp = t; t = r; r = s; s = tmp;
+ }
+ return r;
+ }
+
private static int exceptionExitBeforeAdd() {
int k = 0;
try {
@@ -732,6 +842,7 @@
expectEquals(12395, closedFormInductionUp());
expectEquals(12295, closedFormInductionInAndDown(12345));
+ expectEquals(81, closedFormInductionTrivialIf());
expectEquals(10 * 10, closedFormNested());
expectEquals(12345 + 17 * 23 * 7, closedFormNestedAlt());
for (int n = -4; n < 10; n++) {
@@ -751,6 +862,8 @@
for (int n = -4; n < 4; n++) {
int tc = (n <= 0) ? 0 : n;
expectEquals(tc, mainIndexReturnedN(n));
+ expectEquals(tc, mainIndexShort1((short) n));
+ expectEquals(tc, mainIndexShort2((short) n));
expectEquals(tc & 1, periodicReturnedN(n));
expectEquals((tc * (tc + 1)) / 2, getSumN(n));
}
@@ -775,6 +888,10 @@
expectEquals(!even, periodicBoolIdiom3N(false, n));
}
+ expectEquals( 2.0f, periodicFloat10());
+ expectEquals(-1.0f, periodicFloat11());
+ expectEquals( 4.5f, periodicFloat12());
+
expectEquals(100, exceptionExitBeforeAdd());
expectEquals(100, exceptionExitAfterAdd());
a = null;
@@ -787,6 +904,12 @@
System.out.println("passed");
}
+ private static void expectEquals(float expected, float result) {
+ if (expected != result) {
+ throw new Error("Expected: " + expected + ", found: " + result);
+ }
+ }
+
private static void expectEquals(int expected, int result) {
if (expected != result) {
throw new Error("Expected: " + expected + ", found: " + result);
diff --git a/test/624-checker-stringops/src/Main.java b/test/624-checker-stringops/src/Main.java
index d965e3f..75b782e 100644
--- a/test/624-checker-stringops/src/Main.java
+++ b/test/624-checker-stringops/src/Main.java
@@ -258,6 +258,20 @@
return b.length();
}
+ // Regression b/33656359: StringBuffer x is passed to constructor of String
+ // (this caused old code to crash due to missing nullptr check).
+ //
+ /// CHECK-START: void Main.doesNothing() instruction_simplifier (before)
+ /// CHECK-DAG: InvokeVirtual intrinsic:StringBufferToString
+ //
+ /// CHECK-START: void Main.doesNothing() instruction_simplifier (after)
+ /// CHECK-DAG: InvokeVirtual intrinsic:StringBufferToString
+ static void doesNothing() {
+ StringBuffer x = new StringBuffer();
+ String y = new String(x);
+ x.toString();
+ }
+
public static void main(String[] args) {
expectEquals(1865, liveIndexOf());
expectEquals(29, deadIndexOf());
@@ -281,6 +295,8 @@
expectEquals(0, bufferDeadLoop());
expectEquals(0, builderDeadLoop());
+ doesNothing();
+
System.out.println("passed");
}
diff --git a/test/911-get-stack-trace/expected.txt b/test/911-get-stack-trace/expected.txt
index 77c77ca..f8c97ce 100644
--- a/test/911-get-stack-trace/expected.txt
+++ b/test/911-get-stack-trace/expected.txt
@@ -3,206 +3,206 @@
###################
From top
---------
- getStackTrace (Ljava/lang/Thread;II)[[Ljava/lang/String; -1
- print (Ljava/lang/Thread;II)V 0
- printOrWait (IILMain$ControlData;)V 6
- baz (IIILMain$ControlData;)Ljava/lang/Object; 2
- bar (IIILMain$ControlData;)J 0
- foo (IIILMain$ControlData;)I 0
- baz (IIILMain$ControlData;)Ljava/lang/Object; 9
- bar (IIILMain$ControlData;)J 0
- foo (IIILMain$ControlData;)I 0
- baz (IIILMain$ControlData;)Ljava/lang/Object; 9
- bar (IIILMain$ControlData;)J 0
- foo (IIILMain$ControlData;)I 0
- baz (IIILMain$ControlData;)Ljava/lang/Object; 9
- bar (IIILMain$ControlData;)J 0
- foo (IIILMain$ControlData;)I 0
- baz (IIILMain$ControlData;)Ljava/lang/Object; 9
- bar (IIILMain$ControlData;)J 0
- foo (IIILMain$ControlData;)I 0
- doTest ()V 38
- main ([Ljava/lang/String;)V 6
+ getStackTrace (Ljava/lang/Thread;II)[[Ljava/lang/String; -1 -2
+ print (Ljava/lang/Thread;II)V 0 124
+ printOrWait (IILMain$ControlData;)V 6 151
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 2 142
+ bar (IIILMain$ControlData;)J 0 136
+ foo (IIILMain$ControlData;)I 0 131
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 9 144
+ bar (IIILMain$ControlData;)J 0 136
+ foo (IIILMain$ControlData;)I 0 131
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 9 144
+ bar (IIILMain$ControlData;)J 0 136
+ foo (IIILMain$ControlData;)I 0 131
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 9 144
+ bar (IIILMain$ControlData;)J 0 136
+ foo (IIILMain$ControlData;)I 0 131
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 9 144
+ bar (IIILMain$ControlData;)J 0 136
+ foo (IIILMain$ControlData;)I 0 131
+ doTest ()V 38 34
+ main ([Ljava/lang/String;)V 6 24
---------
- print (Ljava/lang/Thread;II)V 0
- printOrWait (IILMain$ControlData;)V 6
- baz (IIILMain$ControlData;)Ljava/lang/Object; 2
- bar (IIILMain$ControlData;)J 0
- foo (IIILMain$ControlData;)I 0
- baz (IIILMain$ControlData;)Ljava/lang/Object; 9
- bar (IIILMain$ControlData;)J 0
- foo (IIILMain$ControlData;)I 0
- baz (IIILMain$ControlData;)Ljava/lang/Object; 9
- bar (IIILMain$ControlData;)J 0
- foo (IIILMain$ControlData;)I 0
- baz (IIILMain$ControlData;)Ljava/lang/Object; 9
- bar (IIILMain$ControlData;)J 0
- foo (IIILMain$ControlData;)I 0
- baz (IIILMain$ControlData;)Ljava/lang/Object; 9
- bar (IIILMain$ControlData;)J 0
- foo (IIILMain$ControlData;)I 0
- doTest ()V 42
- main ([Ljava/lang/String;)V 6
+ print (Ljava/lang/Thread;II)V 0 124
+ printOrWait (IILMain$ControlData;)V 6 151
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 2 142
+ bar (IIILMain$ControlData;)J 0 136
+ foo (IIILMain$ControlData;)I 0 131
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 9 144
+ bar (IIILMain$ControlData;)J 0 136
+ foo (IIILMain$ControlData;)I 0 131
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 9 144
+ bar (IIILMain$ControlData;)J 0 136
+ foo (IIILMain$ControlData;)I 0 131
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 9 144
+ bar (IIILMain$ControlData;)J 0 136
+ foo (IIILMain$ControlData;)I 0 131
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 9 144
+ bar (IIILMain$ControlData;)J 0 136
+ foo (IIILMain$ControlData;)I 0 131
+ doTest ()V 42 35
+ main ([Ljava/lang/String;)V 6 24
---------
- getStackTrace (Ljava/lang/Thread;II)[[Ljava/lang/String; -1
- print (Ljava/lang/Thread;II)V 0
- printOrWait (IILMain$ControlData;)V 6
- baz (IIILMain$ControlData;)Ljava/lang/Object; 2
- bar (IIILMain$ControlData;)J 0
+ getStackTrace (Ljava/lang/Thread;II)[[Ljava/lang/String; -1 -2
+ print (Ljava/lang/Thread;II)V 0 124
+ printOrWait (IILMain$ControlData;)V 6 151
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 2 142
+ bar (IIILMain$ControlData;)J 0 136
---------
- printOrWait (IILMain$ControlData;)V 6
- baz (IIILMain$ControlData;)Ljava/lang/Object; 2
- bar (IIILMain$ControlData;)J 0
- foo (IIILMain$ControlData;)I 0
- baz (IIILMain$ControlData;)Ljava/lang/Object; 9
+ printOrWait (IILMain$ControlData;)V 6 151
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 2 142
+ bar (IIILMain$ControlData;)J 0 136
+ foo (IIILMain$ControlData;)I 0 131
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 9 144
From bottom
---------
- main ([Ljava/lang/String;)V 6
+ main ([Ljava/lang/String;)V 6 24
---------
- baz (IIILMain$ControlData;)Ljava/lang/Object; 9
- bar (IIILMain$ControlData;)J 0
- foo (IIILMain$ControlData;)I 0
- doTest ()V 65
- main ([Ljava/lang/String;)V 6
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 9 144
+ bar (IIILMain$ControlData;)J 0 136
+ foo (IIILMain$ControlData;)I 0 131
+ doTest ()V 65 41
+ main ([Ljava/lang/String;)V 6 24
---------
- bar (IIILMain$ControlData;)J 0
- foo (IIILMain$ControlData;)I 0
- baz (IIILMain$ControlData;)Ljava/lang/Object; 9
- bar (IIILMain$ControlData;)J 0
- foo (IIILMain$ControlData;)I 0
+ bar (IIILMain$ControlData;)J 0 136
+ foo (IIILMain$ControlData;)I 0 131
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 9 144
+ bar (IIILMain$ControlData;)J 0 136
+ foo (IIILMain$ControlData;)I 0 131
################################
### Other thread (suspended) ###
################################
From top
---------
- wait ()V -1
- printOrWait (IILMain$ControlData;)V 24
- baz (IIILMain$ControlData;)Ljava/lang/Object; 2
- bar (IIILMain$ControlData;)J 0
- foo (IIILMain$ControlData;)I 0
- baz (IIILMain$ControlData;)Ljava/lang/Object; 9
- bar (IIILMain$ControlData;)J 0
- foo (IIILMain$ControlData;)I 0
- baz (IIILMain$ControlData;)Ljava/lang/Object; 9
- bar (IIILMain$ControlData;)J 0
- foo (IIILMain$ControlData;)I 0
- baz (IIILMain$ControlData;)Ljava/lang/Object; 9
- bar (IIILMain$ControlData;)J 0
- foo (IIILMain$ControlData;)I 0
- baz (IIILMain$ControlData;)Ljava/lang/Object; 9
- bar (IIILMain$ControlData;)J 0
- foo (IIILMain$ControlData;)I 0
- run ()V 4
+ wait ()V -1 -2
+ printOrWait (IILMain$ControlData;)V 24 157
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 2 142
+ bar (IIILMain$ControlData;)J 0 136
+ foo (IIILMain$ControlData;)I 0 131
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 9 144
+ bar (IIILMain$ControlData;)J 0 136
+ foo (IIILMain$ControlData;)I 0 131
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 9 144
+ bar (IIILMain$ControlData;)J 0 136
+ foo (IIILMain$ControlData;)I 0 131
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 9 144
+ bar (IIILMain$ControlData;)J 0 136
+ foo (IIILMain$ControlData;)I 0 131
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 9 144
+ bar (IIILMain$ControlData;)J 0 136
+ foo (IIILMain$ControlData;)I 0 131
+ run ()V 4 54
---------
- printOrWait (IILMain$ControlData;)V 24
- baz (IIILMain$ControlData;)Ljava/lang/Object; 2
- bar (IIILMain$ControlData;)J 0
- foo (IIILMain$ControlData;)I 0
- baz (IIILMain$ControlData;)Ljava/lang/Object; 9
- bar (IIILMain$ControlData;)J 0
- foo (IIILMain$ControlData;)I 0
- baz (IIILMain$ControlData;)Ljava/lang/Object; 9
- bar (IIILMain$ControlData;)J 0
- foo (IIILMain$ControlData;)I 0
- baz (IIILMain$ControlData;)Ljava/lang/Object; 9
- bar (IIILMain$ControlData;)J 0
- foo (IIILMain$ControlData;)I 0
- baz (IIILMain$ControlData;)Ljava/lang/Object; 9
- bar (IIILMain$ControlData;)J 0
- foo (IIILMain$ControlData;)I 0
- run ()V 4
+ printOrWait (IILMain$ControlData;)V 24 157
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 2 142
+ bar (IIILMain$ControlData;)J 0 136
+ foo (IIILMain$ControlData;)I 0 131
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 9 144
+ bar (IIILMain$ControlData;)J 0 136
+ foo (IIILMain$ControlData;)I 0 131
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 9 144
+ bar (IIILMain$ControlData;)J 0 136
+ foo (IIILMain$ControlData;)I 0 131
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 9 144
+ bar (IIILMain$ControlData;)J 0 136
+ foo (IIILMain$ControlData;)I 0 131
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 9 144
+ bar (IIILMain$ControlData;)J 0 136
+ foo (IIILMain$ControlData;)I 0 131
+ run ()V 4 54
---------
- wait ()V -1
- printOrWait (IILMain$ControlData;)V 24
- baz (IIILMain$ControlData;)Ljava/lang/Object; 2
- bar (IIILMain$ControlData;)J 0
- foo (IIILMain$ControlData;)I 0
+ wait ()V -1 -2
+ printOrWait (IILMain$ControlData;)V 24 157
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 2 142
+ bar (IIILMain$ControlData;)J 0 136
+ foo (IIILMain$ControlData;)I 0 131
---------
- baz (IIILMain$ControlData;)Ljava/lang/Object; 2
- bar (IIILMain$ControlData;)J 0
- foo (IIILMain$ControlData;)I 0
- baz (IIILMain$ControlData;)Ljava/lang/Object; 9
- bar (IIILMain$ControlData;)J 0
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 2 142
+ bar (IIILMain$ControlData;)J 0 136
+ foo (IIILMain$ControlData;)I 0 131
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 9 144
+ bar (IIILMain$ControlData;)J 0 136
From bottom
---------
- run ()V 4
+ run ()V 4 54
---------
- foo (IIILMain$ControlData;)I 0
- baz (IIILMain$ControlData;)Ljava/lang/Object; 9
- bar (IIILMain$ControlData;)J 0
- foo (IIILMain$ControlData;)I 0
- run ()V 4
+ foo (IIILMain$ControlData;)I 0 131
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 9 144
+ bar (IIILMain$ControlData;)J 0 136
+ foo (IIILMain$ControlData;)I 0 131
+ run ()V 4 54
---------
- baz (IIILMain$ControlData;)Ljava/lang/Object; 9
- bar (IIILMain$ControlData;)J 0
- foo (IIILMain$ControlData;)I 0
- baz (IIILMain$ControlData;)Ljava/lang/Object; 9
- bar (IIILMain$ControlData;)J 0
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 9 144
+ bar (IIILMain$ControlData;)J 0 136
+ foo (IIILMain$ControlData;)I 0 131
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 9 144
+ bar (IIILMain$ControlData;)J 0 136
###########################
### Other thread (live) ###
###########################
From top
---------
- printOrWait (IILMain$ControlData;)V 44
- baz (IIILMain$ControlData;)Ljava/lang/Object; 2
- bar (IIILMain$ControlData;)J 0
- foo (IIILMain$ControlData;)I 0
- baz (IIILMain$ControlData;)Ljava/lang/Object; 9
- bar (IIILMain$ControlData;)J 0
- foo (IIILMain$ControlData;)I 0
- baz (IIILMain$ControlData;)Ljava/lang/Object; 9
- bar (IIILMain$ControlData;)J 0
- foo (IIILMain$ControlData;)I 0
- baz (IIILMain$ControlData;)Ljava/lang/Object; 9
- bar (IIILMain$ControlData;)J 0
- foo (IIILMain$ControlData;)I 0
- baz (IIILMain$ControlData;)Ljava/lang/Object; 9
- bar (IIILMain$ControlData;)J 0
- foo (IIILMain$ControlData;)I 0
- run ()V 4
+ printOrWait (IILMain$ControlData;)V 44 164
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 2 142
+ bar (IIILMain$ControlData;)J 0 136
+ foo (IIILMain$ControlData;)I 0 131
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 9 144
+ bar (IIILMain$ControlData;)J 0 136
+ foo (IIILMain$ControlData;)I 0 131
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 9 144
+ bar (IIILMain$ControlData;)J 0 136
+ foo (IIILMain$ControlData;)I 0 131
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 9 144
+ bar (IIILMain$ControlData;)J 0 136
+ foo (IIILMain$ControlData;)I 0 131
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 9 144
+ bar (IIILMain$ControlData;)J 0 136
+ foo (IIILMain$ControlData;)I 0 131
+ run ()V 4 88
---------
- baz (IIILMain$ControlData;)Ljava/lang/Object; 2
- bar (IIILMain$ControlData;)J 0
- foo (IIILMain$ControlData;)I 0
- baz (IIILMain$ControlData;)Ljava/lang/Object; 9
- bar (IIILMain$ControlData;)J 0
- foo (IIILMain$ControlData;)I 0
- baz (IIILMain$ControlData;)Ljava/lang/Object; 9
- bar (IIILMain$ControlData;)J 0
- foo (IIILMain$ControlData;)I 0
- baz (IIILMain$ControlData;)Ljava/lang/Object; 9
- bar (IIILMain$ControlData;)J 0
- foo (IIILMain$ControlData;)I 0
- baz (IIILMain$ControlData;)Ljava/lang/Object; 9
- bar (IIILMain$ControlData;)J 0
- foo (IIILMain$ControlData;)I 0
- run ()V 4
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 2 142
+ bar (IIILMain$ControlData;)J 0 136
+ foo (IIILMain$ControlData;)I 0 131
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 9 144
+ bar (IIILMain$ControlData;)J 0 136
+ foo (IIILMain$ControlData;)I 0 131
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 9 144
+ bar (IIILMain$ControlData;)J 0 136
+ foo (IIILMain$ControlData;)I 0 131
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 9 144
+ bar (IIILMain$ControlData;)J 0 136
+ foo (IIILMain$ControlData;)I 0 131
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 9 144
+ bar (IIILMain$ControlData;)J 0 136
+ foo (IIILMain$ControlData;)I 0 131
+ run ()V 4 88
---------
- printOrWait (IILMain$ControlData;)V 44
- baz (IIILMain$ControlData;)Ljava/lang/Object; 2
- bar (IIILMain$ControlData;)J 0
- foo (IIILMain$ControlData;)I 0
- baz (IIILMain$ControlData;)Ljava/lang/Object; 9
+ printOrWait (IILMain$ControlData;)V 44 164
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 2 142
+ bar (IIILMain$ControlData;)J 0 136
+ foo (IIILMain$ControlData;)I 0 131
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 9 144
---------
- bar (IIILMain$ControlData;)J 0
- foo (IIILMain$ControlData;)I 0
- baz (IIILMain$ControlData;)Ljava/lang/Object; 9
- bar (IIILMain$ControlData;)J 0
- foo (IIILMain$ControlData;)I 0
+ bar (IIILMain$ControlData;)J 0 136
+ foo (IIILMain$ControlData;)I 0 131
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 9 144
+ bar (IIILMain$ControlData;)J 0 136
+ foo (IIILMain$ControlData;)I 0 131
From bottom
---------
- run ()V 4
+ run ()V 4 88
---------
- foo (IIILMain$ControlData;)I 0
- baz (IIILMain$ControlData;)Ljava/lang/Object; 9
- bar (IIILMain$ControlData;)J 0
- foo (IIILMain$ControlData;)I 0
- run ()V 4
+ foo (IIILMain$ControlData;)I 0 131
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 9 144
+ bar (IIILMain$ControlData;)J 0 136
+ foo (IIILMain$ControlData;)I 0 131
+ run ()V 4 88
---------
- baz (IIILMain$ControlData;)Ljava/lang/Object; 9
- bar (IIILMain$ControlData;)J 0
- foo (IIILMain$ControlData;)I 0
- baz (IIILMain$ControlData;)Ljava/lang/Object; 9
- bar (IIILMain$ControlData;)J 0
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 9 144
+ bar (IIILMain$ControlData;)J 0 136
+ foo (IIILMain$ControlData;)I 0 131
+ baz (IIILMain$ControlData;)Ljava/lang/Object; 9 144
+ bar (IIILMain$ControlData;)J 0 136
diff --git a/test/911-get-stack-trace/stack_trace.cc b/test/911-get-stack-trace/stack_trace.cc
index b5b5678..57f6a92 100644
--- a/test/911-get-stack-trace/stack_trace.cc
+++ b/test/911-get-stack-trace/stack_trace.cc
@@ -20,9 +20,10 @@
#include <memory>
#include <stdio.h>
+#include "android-base/stringprintf.h"
+
#include "base/logging.h"
#include "base/macros.h"
-#include "base/stringprintf.h"
#include "jni.h"
#include "openjdkjvmti/jvmti.h"
#include "ScopedLocalRef.h"
@@ -32,6 +33,25 @@
namespace art {
namespace Test911GetStackTrace {
+using android::base::StringPrintf;
+
+static jint FindLineNumber(jint line_number_count,
+ jvmtiLineNumberEntry* line_number_table,
+ jlocation location) {
+ if (line_number_table == nullptr) {
+ return -2;
+ }
+
+ jint line_number = -1;
+ for (jint i = 0; i != line_number_count; ++i) {
+ if (line_number_table[i].start_location > location) {
+ return line_number;
+ }
+ line_number = line_number_table[i].line_number;
+ }
+ return line_number;
+}
+
extern "C" JNIEXPORT jobjectArray JNICALL Java_Main_getStackTrace(
JNIEnv* env, jclass klass ATTRIBUTE_UNUSED, jthread thread, jint start, jint max) {
std::unique_ptr<jvmtiFrameInfo[]> frames(new jvmtiFrameInfo[max]);
@@ -61,6 +81,26 @@
}
}
+ jint line_number_count;
+ jvmtiLineNumberEntry* line_number_table;
+ {
+ jvmtiError line_result = jvmti_env->GetLineNumberTable(frames[method_index].method,
+ &line_number_count,
+ &line_number_table);
+ if (line_result != JVMTI_ERROR_NONE) {
+ // Accept absent info and native method errors.
+ if (line_result != JVMTI_ERROR_ABSENT_INFORMATION &&
+ line_result != JVMTI_ERROR_NATIVE_METHOD) {
+ char* err;
+ jvmti_env->GetErrorName(line_result, &err);
+ printf("Failure running GetLineNumberTable: %s\n", err);
+ return nullptr;
+ }
+ line_number_table = nullptr;
+ line_number_count = 0;
+ }
+ }
+
auto inner_callback = [&](jint component_index) -> jstring {
switch (component_index) {
case 0:
@@ -69,11 +109,17 @@
return (sig == nullptr) ? nullptr : env->NewStringUTF(sig);
case 2:
return env->NewStringUTF(StringPrintf("%" PRId64, frames[method_index].location).c_str());
+ case 3: {
+ jint line_number = FindLineNumber(line_number_count,
+ line_number_table,
+ frames[method_index].location);
+ return env->NewStringUTF(StringPrintf("%d", line_number).c_str());
+ }
}
LOG(FATAL) << "Unreachable";
UNREACHABLE();
};
- jobjectArray inner_array = CreateObjectArray(env, 3, "java/lang/String", inner_callback);
+ jobjectArray inner_array = CreateObjectArray(env, 4, "java/lang/String", inner_callback);
if (name != nullptr) {
jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(name));
@@ -84,6 +130,9 @@
if (gen != nullptr) {
jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(gen));
}
+ if (line_number_table != nullptr) {
+ jvmti_env->Deallocate(reinterpret_cast<unsigned char*>(line_number_table));
+ }
return inner_array;
};
diff --git a/test/913-heaps/expected.txt b/test/913-heaps/expected.txt
index e5fa53f..7522a65 100644
--- a/test/913-heaps/expected.txt
+++ b/test/913-heaps/expected.txt
@@ -5,12 +5,12 @@
root@root --(thread)--> 3000@0 [size=132, length=-1]
0@0 --(array-element@0)--> 1@1000 [size=16, length=-1]
1001@0 --(superclass)--> 1000@0 [size=123, length=-1]
-1002@0 --(interface)--> 2001@0 [size=132, length=-1]
+1002@0 --(interface)--> 2001@0 [size=124, length=-1]
1002@0 --(superclass)--> 1001@0 [size=123, length=-1]
1@1000 --(class)--> 1000@0 [size=123, length=-1]
1@1000 --(field@12)--> 3@1001 [size=24, length=-1]
1@1000 --(field@8)--> 2@1000 [size=16, length=-1]
-2001@0 --(interface)--> 2000@0 [size=132, length=-1]
+2001@0 --(interface)--> 2000@0 [size=124, length=-1]
2@1000 --(class)--> 1000@0 [size=123, length=-1]
3@1001 --(class)--> 1001@0 [size=123, length=-1]
3@1001 --(field@16)--> 4@1000 [size=16, length=-1]
@@ -22,12 +22,12 @@
6@1000 --(class)--> 1000@0 [size=123, length=-1]
---
1001@0 --(superclass)--> 1000@0 [size=123, length=-1]
-1002@0 --(interface)--> 2001@0 [size=132, length=-1]
+1002@0 --(interface)--> 2001@0 [size=124, length=-1]
1002@0 --(superclass)--> 1001@0 [size=123, length=-1]
1@1000 --(class)--> 1000@0 [size=123, length=-1]
1@1000 --(field@12)--> 3@1001 [size=24, length=-1]
1@1000 --(field@8)--> 2@1000 [size=16, length=-1]
-2001@0 --(interface)--> 2000@0 [size=132, length=-1]
+2001@0 --(interface)--> 2000@0 [size=124, length=-1]
2@1000 --(class)--> 1000@0 [size=123, length=-1]
3@1001 --(class)--> 1001@0 [size=123, length=-1]
3@1001 --(field@16)--> 4@1000 [size=16, length=-1]
@@ -46,12 +46,12 @@
root@root --(thread)--> 1@1000 [size=16, length=-1]
root@root --(thread)--> 3000@0 [size=132, length=-1]
1001@0 --(superclass)--> 1000@0 [size=123, length=-1]
-1002@0 --(interface)--> 2001@0 [size=132, length=-1]
+1002@0 --(interface)--> 2001@0 [size=124, length=-1]
1002@0 --(superclass)--> 1001@0 [size=123, length=-1]
1@1000 --(class)--> 1000@0 [size=123, length=-1]
1@1000 --(field@12)--> 3@1001 [size=24, length=-1]
1@1000 --(field@8)--> 2@1000 [size=16, length=-1]
-2001@0 --(interface)--> 2000@0 [size=132, length=-1]
+2001@0 --(interface)--> 2000@0 [size=124, length=-1]
2@1000 --(class)--> 1000@0 [size=123, length=-1]
3@1001 --(class)--> 1001@0 [size=123, length=-1]
3@1001 --(field@16)--> 4@1000 [size=16, length=-1]
@@ -63,12 +63,12 @@
6@1000 --(class)--> 1000@0 [size=123, length=-1]
---
1001@0 --(superclass)--> 1000@0 [size=123, length=-1]
-1002@0 --(interface)--> 2001@0 [size=132, length=-1]
+1002@0 --(interface)--> 2001@0 [size=124, length=-1]
1002@0 --(superclass)--> 1001@0 [size=123, length=-1]
1@1000 --(class)--> 1000@0 [size=123, length=-1]
1@1000 --(field@12)--> 3@1001 [size=24, length=-1]
1@1000 --(field@8)--> 2@1000 [size=16, length=-1]
-2001@0 --(interface)--> 2000@0 [size=132, length=-1]
+2001@0 --(interface)--> 2000@0 [size=124, length=-1]
2@1000 --(class)--> 1000@0 [size=123, length=-1]
3@1001 --(class)--> 1001@0 [size=123, length=-1]
3@1001 --(field@16)--> 4@1000 [size=16, length=-1]
diff --git a/test/913-heaps/heaps.cc b/test/913-heaps/heaps.cc
index 7b00fcd..49ab7dd 100644
--- a/test/913-heaps/heaps.cc
+++ b/test/913-heaps/heaps.cc
@@ -22,9 +22,10 @@
#include <vector>
+#include "android-base/stringprintf.h"
+
#include "base/logging.h"
#include "base/macros.h"
-#include "base/stringprintf.h"
#include "jit/jit.h"
#include "jni.h"
#include "native_stack_dump.h"
@@ -39,6 +40,8 @@
namespace art {
namespace Test913Heaps {
+using android::base::StringPrintf;
+
extern "C" JNIEXPORT void JNICALL Java_Main_forceGarbageCollection(JNIEnv* env ATTRIBUTE_UNUSED,
jclass klass ATTRIBUTE_UNUSED) {
jvmtiError ret = jvmti_env->ForceGarbageCollection();
@@ -180,7 +183,7 @@
if (*tag_ptr >= 1000) {
// This is a class or interface, the size of which will be dependent on the architecture.
// Do not print the size, but detect known values and "normalize" for the golden file.
- if ((sizeof(void*) == 4 && size == 180) || (sizeof(void*) == 8 && size == 232)) {
+ if ((sizeof(void*) == 4 && size == 172) || (sizeof(void*) == 8 && size == 224)) {
adapted_size = 123;
}
}
diff --git a/test/917-fields-transformation/build b/test/917-fields-transformation/build
new file mode 100755
index 0000000..898e2e5
--- /dev/null
+++ b/test/917-fields-transformation/build
@@ -0,0 +1,17 @@
+#!/bin/bash
+#
+# Copyright 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+./default-build "$@" --experimental agents
diff --git a/test/917-fields-transformation/expected.txt b/test/917-fields-transformation/expected.txt
new file mode 100644
index 0000000..bcdd201
--- /dev/null
+++ b/test/917-fields-transformation/expected.txt
@@ -0,0 +1,12 @@
+Result is Hello
+take1 is Hello
+take2 is Goodbye
+Result is start
+take1 is start
+take2 is end
+Result is Goodbye
+take1 is Hello
+take2 is Goodbye
+Result is end
+take1 is start
+take2 is end
diff --git a/test/917-fields-transformation/info.txt b/test/917-fields-transformation/info.txt
new file mode 100644
index 0000000..4cd1bd9
--- /dev/null
+++ b/test/917-fields-transformation/info.txt
@@ -0,0 +1 @@
+Tests field access after class redefinition support in the jvmti plugin.
diff --git a/test/917-fields-transformation/run b/test/917-fields-transformation/run
new file mode 100755
index 0000000..a434b63
--- /dev/null
+++ b/test/917-fields-transformation/run
@@ -0,0 +1,43 @@
+#!/bin/bash
+#
+# Copyright 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+plugin=libopenjdkjvmtid.so
+agent=libtiagentd.so
+lib=tiagentd
+if [[ "$@" == *"-O"* ]]; then
+ agent=libtiagent.so
+ plugin=libopenjdkjvmti.so
+ lib=tiagent
+fi
+
+if [[ "$@" == *"--jvm"* ]]; then
+ arg="jvm"
+else
+ arg="art"
+ if [[ "$@" != *"--debuggable"* ]]; then
+ other_args=" -Xcompiler-option --debuggable "
+ else
+ other_args=""
+ fi
+fi
+
+./default-run "$@" --experimental agents \
+ --experimental runtime-plugins \
+ --runtime-option -agentpath:${agent}=917-fields-transformation,${arg} \
+ --android-runtime-option -Xplugin:${plugin} \
+ --android-runtime-option -Xfully-deoptable \
+ ${other_args} \
+ --args ${lib}
diff --git a/test/917-fields-transformation/src/Main.java b/test/917-fields-transformation/src/Main.java
new file mode 100644
index 0000000..5378bb7
--- /dev/null
+++ b/test/917-fields-transformation/src/Main.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.util.Base64;
+public class Main {
+
+ // base64 encoded class/dex file for
+ // class Transform {
+ // public String take1;
+ // public String take2;
+ //
+ // public Transform(String a, String b) {
+ // take1 = a;
+ // take2 = b;
+ // }
+ //
+ // public String getResult() {
+ // return take2;
+ // }
+ // }
+ private static final byte[] CLASS_BYTES = Base64.getDecoder().decode(
+ "yv66vgAAADQAFwoABQARCQAEABIJAAQAEwcAFAcAFQEABXRha2UxAQASTGphdmEvbGFuZy9TdHJp" +
+ "bmc7AQAFdGFrZTIBAAY8aW5pdD4BACcoTGphdmEvbGFuZy9TdHJpbmc7TGphdmEvbGFuZy9TdHJp" +
+ "bmc7KVYBAARDb2RlAQAPTGluZU51bWJlclRhYmxlAQAJZ2V0UmVzdWx0AQAUKClMamF2YS9sYW5n" +
+ "L1N0cmluZzsBAApTb3VyY2VGaWxlAQAOVHJhbnNmb3JtLmphdmEMAAkAFgwABgAHDAAIAAcBAAlU" +
+ "cmFuc2Zvcm0BABBqYXZhL2xhbmcvT2JqZWN0AQADKClWACAABAAFAAAAAgABAAYABwAAAAEACAAH" +
+ "AAAAAgABAAkACgABAAsAAAAzAAIAAwAAAA8qtwABKiu1AAIqLLUAA7EAAAABAAwAAAASAAQAAAAU" +
+ "AAQAFQAJABYADgAXAAEADQAOAAEACwAAAB0AAQABAAAABSq0AAOwAAAAAQAMAAAABgABAAAAGgAB" +
+ "AA8AAAACABA=");
+ private static final byte[] DEX_BYTES = Base64.getDecoder().decode(
+ "ZGV4CjAzNQAGUTBb4jIABRlaI9rejdk7RCfyqR2kmNSkAgAAcAAAAHhWNBIAAAAAAAAAAAQCAAAM" +
+ "AAAAcAAAAAQAAACgAAAAAwAAALAAAAACAAAA1AAAAAMAAADkAAAAAQAAAPwAAACIAQAAHAEAAFwB" +
+ "AABkAQAAZwEAAHQBAACIAQAAnAEAAKwBAACvAQAAtAEAAMgBAADTAQAA2gEAAAIAAAADAAAABAAA" +
+ "AAYAAAABAAAAAgAAAAAAAAAGAAAAAwAAAAAAAAAHAAAAAwAAAFQBAAAAAAIACgAAAAAAAgALAAAA" +
+ "AAACAAAAAAAAAAAACQAAAAEAAQAAAAAAAAAAAAAAAAABAAAAAAAAAAUAAAAAAAAA8AEAAAAAAAAD" +
+ "AAMAAQAAAOEBAAAIAAAAcBACAAAAWwEAAFsCAQAOAAIAAQAAAAAA6wEAAAMAAABUEAEAEQAAAAIA" +
+ "AAACAAIABjxpbml0PgABTAALTFRyYW5zZm9ybTsAEkxqYXZhL2xhbmcvT2JqZWN0OwASTGphdmEv" +
+ "bGFuZy9TdHJpbmc7AA5UcmFuc2Zvcm0uamF2YQABVgADVkxMABJlbWl0dGVyOiBqYWNrLTQuMTkA" +
+ "CWdldFJlc3VsdAAFdGFrZTEABXRha2UyABQCAAAHDjwtLQAaAAcOAAACAQEAAQEBAIGABJwCAQG8" +
+ "AgAADQAAAAAAAAABAAAAAAAAAAEAAAAMAAAAcAAAAAIAAAAEAAAAoAAAAAMAAAADAAAAsAAAAAQA" +
+ "AAACAAAA1AAAAAUAAAADAAAA5AAAAAYAAAABAAAA/AAAAAEgAAACAAAAHAEAAAEQAAABAAAAVAEA" +
+ "AAIgAAAMAAAAXAEAAAMgAAACAAAA4QEAAAAgAAABAAAA8AEAAAAQAAABAAAABAIAAA==");
+
+ public static void main(String[] args) {
+ System.loadLibrary(args[1]);
+ doTest(new Transform("Hello", "Goodbye"),
+ new Transform("start", "end"));
+ }
+
+ private static void printTransform(Transform t) {
+ System.out.println("Result is " + t.getResult());
+ System.out.println("take1 is " + t.take1);
+ System.out.println("take2 is " + t.take2);
+ }
+ public static void doTest(Transform t1, Transform t2) {
+ printTransform(t1);
+ printTransform(t2);
+ doCommonClassRedefinition(Transform.class, CLASS_BYTES, DEX_BYTES);
+ printTransform(t1);
+ printTransform(t2);
+ }
+
+ // Transforms the class
+ private static native void doCommonClassRedefinition(Class<?> target,
+ byte[] class_file,
+ byte[] dex_file);
+}
diff --git a/runtime/base/stringprintf_test.cc b/test/917-fields-transformation/src/Transform.java
similarity index 62%
rename from runtime/base/stringprintf_test.cc
rename to test/917-fields-transformation/src/Transform.java
index 0bfde33..6fe6223 100644
--- a/runtime/base/stringprintf_test.cc
+++ b/test/917-fields-transformation/src/Transform.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2011 The Android Open Source Project
+ * Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,16 +14,16 @@
* limitations under the License.
*/
-#include "stringprintf.h"
+class Transform {
+ public String take1;
+ public String take2;
-#include "gtest/gtest.h"
+ public Transform(String take1, String take2) {
+ this.take1 = take1;
+ this.take2 = take2;
+ }
-namespace art {
-
-TEST(StringPrintfTest, HexSizeT) {
- size_t size = 0x00107e59;
- EXPECT_STREQ("00107e59", StringPrintf("%08zx", size).c_str());
- EXPECT_STREQ("0x00107e59", StringPrintf("0x%08zx", size).c_str());
+ public String getResult() {
+ return take1;
+ }
}
-
-} // namespace art
diff --git a/test/Android.run-test.mk b/test/Android.run-test.mk
index 58246cb..543ac04 100644
--- a/test/Android.run-test.mk
+++ b/test/Android.run-test.mk
@@ -226,6 +226,10 @@
$(shell echo $(1) | tr '[:lower:]' '[:upper:]' | tr '-' '_')
endef # name-to-var
+# Disable 153-reference-stress temporarily until a fix arrives. b/33389022.
+ART_TEST_RUN_TEST_SKIP += \
+ 153-reference-stress
+
ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),$(RUN_TYPES),$(PREBUILD_TYPES), \
$(COMPILER_TYPES),$(RELOCATE_TYPES),$(TRACE_TYPES),$(GC_TYPES),$(JNI_TYPES), \
$(IMAGE_TYPES), $(PICTEST_TYPES), $(DEBUGGABLE_TYPES), $(ART_TEST_RUN_TEST_SKIP), $(ALL_ADDRESS_SIZES))
@@ -280,6 +284,7 @@
911-get-stack-trace \
912-classes \
913-heaps \
+ 917-fields-transformation \
ifneq (,$(filter target,$(TARGET_TYPES)))
ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,target,$(RUN_TYPES),$(PREBUILD_TYPES), \
@@ -492,6 +497,24 @@
$(PICTEST_TYPES),$(DEBUGGABLE_TYPES), $(TEST_ART_BROKEN_TRACING_RUN_TESTS),$(ALL_ADDRESS_SIZES))
endif
+TEST_ART_BROKEN_TRACING_RUN_TESTS :=
+
+# These tests expect JIT compilation, which is suppressed when tracing.
+TEST_ART_BROKEN_JIT_TRACING_RUN_TESTS := \
+ 604-hot-static-interface \
+ 612-jit-dex-cache \
+ 613-inlining-dex-cache \
+ 616-cha \
+ 626-set-resolved-string \
+
+ifneq (,$(filter trace stream,$(TRACE_TYPES)))
+ ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),$(RUN_TYPES),$(PREBUILD_TYPES), \
+ jit,$(RELOCATE_TYPES),trace stream,$(GC_TYPES),$(JNI_TYPES),$(IMAGE_TYPES), \
+ $(PICTEST_TYPES),$(DEBUGGABLE_TYPES), $(TEST_ART_BROKEN_JIT_TRACING_RUN_TESTS),$(ALL_ADDRESS_SIZES))
+endif
+
+TEST_ART_BROKEN_JIT_TRACING_RUN_TESTS :=
+
# Known broken tests for the interpreter.
# CFI unwinding expects managed frames.
# 629 requires compilation.
diff --git a/runtime/base/stringprintf_test.cc b/test/ErroneousA/ErroneousA.java
similarity index 61%
copy from runtime/base/stringprintf_test.cc
copy to test/ErroneousA/ErroneousA.java
index 0bfde33..49da544 100644
--- a/runtime/base/stringprintf_test.cc
+++ b/test/ErroneousA/ErroneousA.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2011 The Android Open Source Project
+ * Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,16 +14,4 @@
* limitations under the License.
*/
-#include "stringprintf.h"
-
-#include "gtest/gtest.h"
-
-namespace art {
-
-TEST(StringPrintfTest, HexSizeT) {
- size_t size = 0x00107e59;
- EXPECT_STREQ("00107e59", StringPrintf("%08zx", size).c_str());
- EXPECT_STREQ("0x00107e59", StringPrintf("0x%08zx", size).c_str());
-}
-
-} // namespace art
+final class FinalSuper {}
diff --git a/runtime/base/stringprintf_test.cc b/test/ErroneousB/ErroneousB.java
similarity index 61%
copy from runtime/base/stringprintf_test.cc
copy to test/ErroneousB/ErroneousB.java
index 0bfde33..6c2902a 100644
--- a/runtime/base/stringprintf_test.cc
+++ b/test/ErroneousB/ErroneousB.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2011 The Android Open Source Project
+ * Copyright (C) 2016 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -14,16 +14,7 @@
* limitations under the License.
*/
-#include "stringprintf.h"
+// Only final in first dex.
+class FinalSuper {}
-#include "gtest/gtest.h"
-
-namespace art {
-
-TEST(StringPrintfTest, HexSizeT) {
- size_t size = 0x00107e59;
- EXPECT_STREQ("00107e59", StringPrintf("%08zx", size).c_str());
- EXPECT_STREQ("0x00107e59", StringPrintf("0x%08zx", size).c_str());
-}
-
-} // namespace art
+class Erroneous extends FinalSuper {}
diff --git a/test/ti-agent/common_load.cc b/test/ti-agent/common_load.cc
index 2795cbc..3886148 100644
--- a/test/ti-agent/common_load.cc
+++ b/test/ti-agent/common_load.cc
@@ -66,6 +66,7 @@
{ "911-get-stack-trace", Test911GetStackTrace::OnLoad, nullptr },
{ "912-classes", Test912Classes::OnLoad, nullptr },
{ "913-heaps", Test913Heaps::OnLoad, nullptr },
+ { "917-fields-transformation", common_redefine::OnLoad, nullptr },
};
static AgentLib* FindAgent(char* name) {
diff --git a/tools/cpp-define-generator/offset_dexcache.def b/tools/cpp-define-generator/offset_dexcache.def
index 4b9d481..abb5e1e 100644
--- a/tools/cpp-define-generator/offset_dexcache.def
+++ b/tools/cpp-define-generator/offset_dexcache.def
@@ -38,7 +38,6 @@
DEFINE_ART_METHOD_OFFSET_SIZED(JNI, EntryPointFromJni)
DEFINE_ART_METHOD_OFFSET_SIZED(QUICK_CODE, EntryPointFromQuickCompiledCode)
DEFINE_ART_METHOD_OFFSET(DECLARING_CLASS, DeclaringClass)
-DEFINE_DECLARING_CLASS_OFFSET(DEX_CACHE_STRINGS, DexCacheStrings)
#undef DEFINE_ART_METHOD_OFFSET
#undef DEFINE_ART_METHOD_OFFSET_32
diff --git a/tools/jfuzz/run_jfuzz_test.py b/tools/jfuzz/run_jfuzz_test.py
index fd8415d..42745d2 100755
--- a/tools/jfuzz/run_jfuzz_test.py
+++ b/tools/jfuzz/run_jfuzz_test.py
@@ -414,14 +414,16 @@
retc2: int, normalized return code of second runner
"""
if retc1 == retc2:
- # Non-divergent in return code.
+ # No divergence in return code.
if retc1 == RetCode.SUCCESS:
# Both compilations and runs were successful, inspect generated output.
runner1_out = self._runner1.output_file
runner2_out = self._runner2.output_file
if not filecmp.cmp(runner1_out, runner2_out, shallow=False):
+ # Divergence in output.
self.ReportDivergence(retc1, retc2, is_output_divergence=True)
else:
+ # No divergence in output.
self._num_success += 1
elif retc1 == RetCode.TIMEOUT:
self._num_timed_out += 1
@@ -429,8 +431,12 @@
self._num_not_compiled += 1
else:
self._num_not_run += 1
+ elif self._true_divergence_only and RetCode.TIMEOUT in (retc1, retc2):
+ # When only true divergences are requested, any divergence in return
+ # code where one is a time out is treated as a regular time out.
+ self._num_timed_out += 1
else:
- # Divergent in return code.
+ # Divergence in return code.
self.ReportDivergence(retc1, retc2, is_output_divergence=False)
def GetCurrentDivergenceDir(self):
@@ -450,13 +456,12 @@
os.mkdir(ddir)
for f in glob('*.txt') + ['Test.java']:
shutil.copy(f, ddir)
- if not (self._true_divergence_only and RetCode.TIMEOUT in (retc1, retc2)):
- # Maybe run bisection bug search.
- if retc1 in BISECTABLE_RET_CODES and retc2 in BISECTABLE_RET_CODES:
- self.MaybeBisectDivergence(retc1, retc2, is_output_divergence)
- # Call reporting script.
- if self._report_script:
- self.RunReportScript(retc1, retc2, is_output_divergence)
+ # Maybe run bisection bug search.
+ if retc1 in BISECTABLE_RET_CODES and retc2 in BISECTABLE_RET_CODES:
+ self.MaybeBisectDivergence(retc1, retc2, is_output_divergence)
+ # Call reporting script.
+ if self._report_script:
+ self.RunReportScript(retc1, retc2, is_output_divergence)
def RunReportScript(self, retc1, retc2, is_output_divergence):
"""Runs report script."""