summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Android.mk10
-rw-r--r--compiler/driver/compiler_driver.cc13
-rw-r--r--compiler/driver/compiler_driver.h1
-rw-r--r--compiler/elf_writer.cc8
-rw-r--r--compiler/elf_writer_test.cc20
-rw-r--r--compiler/image_writer.cc11
-rw-r--r--compiler/oat_test.cc30
-rw-r--r--compiler/optimizing/bounds_check_elimination.cc23
-rw-r--r--compiler/optimizing/code_generator.cc14
-rw-r--r--compiler/optimizing/code_generator.h3
-rw-r--r--compiler/optimizing/code_generator_arm.cc25
-rw-r--r--compiler/optimizing/code_generator_arm.h6
-rw-r--r--compiler/optimizing/code_generator_arm64.cc28
-rw-r--r--compiler/optimizing/code_generator_arm64.h5
-rw-r--r--compiler/optimizing/code_generator_mips.cc25
-rw-r--r--compiler/optimizing/code_generator_mips.h4
-rw-r--r--compiler/optimizing/code_generator_mips64.cc38
-rw-r--r--compiler/optimizing/code_generator_mips64.h4
-rw-r--r--compiler/optimizing/code_generator_x86.cc26
-rw-r--r--compiler/optimizing/code_generator_x86.h4
-rw-r--r--compiler/optimizing/code_generator_x86_64.cc25
-rw-r--r--compiler/optimizing/code_generator_x86_64.h4
-rw-r--r--compiler/optimizing/graph_checker.cc70
-rw-r--r--compiler/optimizing/inliner.cc18
-rw-r--r--compiler/optimizing/instruction_simplifier.cc66
-rw-r--r--compiler/optimizing/nodes.h23
-rw-r--r--compiler/optimizing/optimizing_compiler.cc8
-rw-r--r--compiler/optimizing/optimizing_compiler_stats.h8
-rw-r--r--oatdump/oatdump.cc235
-rw-r--r--runtime/Android.mk1
-rw-r--r--runtime/art_method-inl.h23
-rw-r--r--runtime/art_method.h4
-rw-r--r--runtime/base/histogram-inl.h8
-rw-r--r--runtime/base/histogram.h1
-rw-r--r--runtime/base/length_prefixed_array.h8
-rw-r--r--runtime/class_linker.cc95
-rw-r--r--runtime/class_linker.h6
-rw-r--r--runtime/dex_file_verifier.cc61
-rw-r--r--runtime/dex_file_verifier.h2
-rw-r--r--runtime/dex_file_verifier_test.cc59
-rw-r--r--runtime/elf_file.cc81
-rw-r--r--runtime/elf_file.h13
-rw-r--r--runtime/elf_file_impl.h18
-rw-r--r--runtime/entrypoints/entrypoint_utils-inl.h18
-rw-r--r--runtime/gc/allocation_record.cc18
-rw-r--r--runtime/gc/allocation_record.h2
-rw-r--r--runtime/gc/heap-inl.h6
-rw-r--r--runtime/gc/heap.cc4
-rw-r--r--runtime/gc/heap.h4
-rw-r--r--runtime/gc/space/image_space.cc215
-rw-r--r--runtime/gc/space/image_space_fs.h307
-rw-r--r--runtime/jit/debugger_interface.cc8
-rw-r--r--runtime/jit/jit.cc15
-rw-r--r--runtime/jit/jit.h18
-rw-r--r--runtime/jit/jit_code_cache.cc36
-rw-r--r--runtime/jit/jit_code_cache.h18
-rw-r--r--runtime/native/java_lang_Runtime.cc134
-rw-r--r--runtime/oat_file.cc36
-rw-r--r--runtime/oat_file.h1
-rw-r--r--runtime/oat_file_assistant.cc18
-rw-r--r--runtime/oat_file_assistant_test.cc44
-rw-r--r--runtime/openjdkjvm/OpenjdkJvm.cc8
-rw-r--r--runtime/primitive.h14
-rw-r--r--runtime/runtime.cc10
-rw-r--r--test/458-checker-instruction-simplification/src/Main.java143
-rw-r--r--test/529-checker-unresolved/build46
-rw-r--r--test/529-checker-unresolved/src-dex2oat-unresolved/UnresolvedClass.java (renamed from test/529-checker-unresolved/src/Unresolved.java)12
-rw-r--r--test/529-checker-unresolved/src-dex2oat-unresolved/UnresolvedInterface.java (renamed from runtime/native/java_lang_Runtime.h)17
-rw-r--r--test/529-checker-unresolved/src-dex2oat-unresolved/UnresolvedSuperClass.java21
-rw-r--r--test/551-checker-shifter-operand/src/Main.java4
-rw-r--r--test/566-checker-signum/src/Main.java216
-rw-r--r--test/567-checker-compare/src/Main.java924
-rw-r--r--test/582-checker-bce-length/expected.txt1
-rw-r--r--test/582-checker-bce-length/info.txt1
-rw-r--r--test/582-checker-bce-length/src/Main.java99
-rw-r--r--test/583-checker-zero/expected.txt0
-rw-r--r--test/583-checker-zero/info.txt2
-rw-r--r--test/583-checker-zero/src/Main.java29
-rw-r--r--test/584-checker-div-bool/expected.txt0
-rw-r--r--test/584-checker-div-bool/info.txt2
-rw-r--r--test/584-checker-div-bool/src/Main.java41
-rw-r--r--test/585-inline-unresolved/expected.txt0
-rw-r--r--test/585-inline-unresolved/info.txt2
-rw-r--r--test/585-inline-unresolved/smali/TestCase.smali48
-rw-r--r--test/585-inline-unresolved/src/Main.java22
-rw-r--r--test/Android.run-test.mk3
-rwxr-xr-xtest/etc/default-build117
-rwxr-xr-xtest/etc/run-test-jar8
-rwxr-xr-xtest/run-test7
-rw-r--r--tools/libcore_failures.txt5
90 files changed, 2869 insertions, 970 deletions
diff --git a/Android.mk b/Android.mk
index e762814385..e89f6178e7 100644
--- a/Android.mk
+++ b/Android.mk
@@ -270,11 +270,19 @@ endif
test-art-host-dexdump: $(addprefix $(HOST_OUT_EXECUTABLES)/, dexdump2 dexlist)
ANDROID_HOST_OUT=$(realpath $(HOST_OUT)) art/test/dexdump/run-all-tests
-# Valgrind. Currently only 32b gtests.
+# Valgrind. Currently only 32b gtests. TODO: change this from 32-bit only to both 32-bit and 64-bit.
.PHONY: valgrind-test-art-host
valgrind-test-art-host: valgrind-test-art-host-gtest32
$(hide) $(call ART_TEST_PREREQ_FINISHED,$@)
+.PHONY: valgrind-test-art-host32
+valgrind-test-art-host32: valgrind-test-art-host-gtest32
+ $(hide) $(call ART_TEST_PREREQ_FINISHED,$@)
+
+.PHONY: valgrind-test-art-host64
+valgrind-test-art-host64: valgrind-test-art-host-gtest64
+ $(hide) $(call ART_TEST_PREREQ_FINISHED,$@)
+
########################################################################
# target test rules
diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc
index 5d8e3baacb..cd9d80fe9b 100644
--- a/compiler/driver/compiler_driver.cc
+++ b/compiler/driver/compiler_driver.cc
@@ -376,7 +376,8 @@ CompilerDriver::CompilerDriver(
support_boot_image_fixup_(instruction_set != kMips && instruction_set != kMips64),
dex_files_for_oat_file_(nullptr),
compiled_method_storage_(swap_fd),
- profile_compilation_info_(profile_compilation_info) {
+ profile_compilation_info_(profile_compilation_info),
+ max_arena_alloc_(0) {
DCHECK(compiler_options_ != nullptr);
DCHECK(method_inliner_map_ != nullptr);
@@ -2487,6 +2488,9 @@ void CompilerDriver::Compile(jobject class_loader,
parallel_thread_pool_.get(),
parallel_thread_count_,
timings);
+ const ArenaPool* const arena_pool = Runtime::Current()->GetArenaPool();
+ const size_t arena_alloc = arena_pool->GetBytesAllocated();
+ max_arena_alloc_ = std::max(arena_alloc, max_arena_alloc_);
Runtime::Current()->ReclaimArenaPoolMemory();
}
VLOG(compiler) << "Compile: " << GetMemoryUsageString(false);
@@ -2726,12 +2730,9 @@ bool CompilerDriver::RequiresConstructorBarrier(Thread* self, const DexFile* dex
std::string CompilerDriver::GetMemoryUsageString(bool extended) const {
std::ostringstream oss;
- Runtime* const runtime = Runtime::Current();
- const ArenaPool* const arena_pool = runtime->GetArenaPool();
- const gc::Heap* const heap = runtime->GetHeap();
- const size_t arena_alloc = arena_pool->GetBytesAllocated();
+ const gc::Heap* const heap = Runtime::Current()->GetHeap();
const size_t java_alloc = heap->GetBytesAllocated();
- oss << "arena alloc=" << PrettySize(arena_alloc) << " (" << arena_alloc << "B)";
+ oss << "arena alloc=" << PrettySize(max_arena_alloc_) << " (" << max_arena_alloc_ << "B)";
oss << " java alloc=" << PrettySize(java_alloc) << " (" << java_alloc << "B)";
#if defined(__BIONIC__) || defined(__GLIBC__)
const struct mallinfo info = mallinfo();
diff --git a/compiler/driver/compiler_driver.h b/compiler/driver/compiler_driver.h
index 42a5bc15e4..4ef26ddd6c 100644
--- a/compiler/driver/compiler_driver.h
+++ b/compiler/driver/compiler_driver.h
@@ -694,6 +694,7 @@ class CompilerDriver {
// Info for profile guided compilation.
const ProfileCompilationInfo* const profile_compilation_info_;
+ size_t max_arena_alloc_;
friend class CompileClassVisitor;
DISALLOW_COPY_AND_ASSIGN(CompilerDriver);
};
diff --git a/compiler/elf_writer.cc b/compiler/elf_writer.cc
index 4219d97411..ca0869a839 100644
--- a/compiler/elf_writer.cc
+++ b/compiler/elf_writer.cc
@@ -42,7 +42,11 @@ void ElfWriter::GetOatElfInformation(File* file,
size_t* oat_loaded_size,
size_t* oat_data_offset) {
std::string error_msg;
- std::unique_ptr<ElfFile> elf_file(ElfFile::Open(file, false, false, &error_msg));
+ std::unique_ptr<ElfFile> elf_file(ElfFile::Open(file,
+ false,
+ false,
+ /*low_4gb*/false,
+ &error_msg));
CHECK(elf_file.get() != nullptr) << error_msg;
bool success = elf_file->GetLoadedSize(oat_loaded_size, &error_msg);
@@ -54,7 +58,7 @@ void ElfWriter::GetOatElfInformation(File* file,
bool ElfWriter::Fixup(File* file, uintptr_t oat_data_begin) {
std::string error_msg;
- std::unique_ptr<ElfFile> elf_file(ElfFile::Open(file, true, false, &error_msg));
+ std::unique_ptr<ElfFile> elf_file(ElfFile::Open(file, true, false, /*low_4gb*/false, &error_msg));
CHECK(elf_file.get() != nullptr) << error_msg;
// Lookup "oatdata" symbol address.
diff --git a/compiler/elf_writer_test.cc b/compiler/elf_writer_test.cc
index 7cf774e95f..449f514184 100644
--- a/compiler/elf_writer_test.cc
+++ b/compiler/elf_writer_test.cc
@@ -64,7 +64,11 @@ TEST_F(ElfWriterTest, dlsym) {
ASSERT_TRUE(file.get() != nullptr);
{
std::string error_msg;
- std::unique_ptr<ElfFile> ef(ElfFile::Open(file.get(), false, false, &error_msg));
+ std::unique_ptr<ElfFile> ef(ElfFile::Open(file.get(),
+ false,
+ false,
+ /*low_4gb*/false,
+ &error_msg));
CHECK(ef.get() != nullptr) << error_msg;
EXPECT_ELF_FILE_ADDRESS(ef, dl_oatdata, "oatdata", false);
EXPECT_ELF_FILE_ADDRESS(ef, dl_oatexec, "oatexec", false);
@@ -72,7 +76,11 @@ TEST_F(ElfWriterTest, dlsym) {
}
{
std::string error_msg;
- std::unique_ptr<ElfFile> ef(ElfFile::Open(file.get(), false, false, &error_msg));
+ std::unique_ptr<ElfFile> ef(ElfFile::Open(file.get(),
+ false,
+ false,
+ /*low_4gb*/false,
+ &error_msg));
CHECK(ef.get() != nullptr) << error_msg;
EXPECT_ELF_FILE_ADDRESS(ef, dl_oatdata, "oatdata", true);
EXPECT_ELF_FILE_ADDRESS(ef, dl_oatexec, "oatexec", true);
@@ -80,9 +88,13 @@ TEST_F(ElfWriterTest, dlsym) {
}
{
std::string error_msg;
- std::unique_ptr<ElfFile> ef(ElfFile::Open(file.get(), false, true, &error_msg));
+ std::unique_ptr<ElfFile> ef(ElfFile::Open(file.get(),
+ false,
+ true,
+ /*low_4gb*/false,
+ &error_msg));
CHECK(ef.get() != nullptr) << error_msg;
- CHECK(ef->Load(false, &error_msg)) << error_msg;
+ CHECK(ef->Load(false, /*low_4gb*/false, &error_msg)) << error_msg;
EXPECT_EQ(dl_oatdata, ef->FindDynamicSymbolAddress("oatdata"));
EXPECT_EQ(dl_oatexec, ef->FindDynamicSymbolAddress("oatexec"));
EXPECT_EQ(dl_oatlastword, ef->FindDynamicSymbolAddress("oatlastword"));
diff --git a/compiler/image_writer.cc b/compiler/image_writer.cc
index b1b971f6ba..0b6981016d 100644
--- a/compiler/image_writer.cc
+++ b/compiler/image_writer.cc
@@ -1542,15 +1542,16 @@ void ImageWriter::CopyAndFixupNativeData(size_t oat_index) {
}
case kNativeObjectRelocationTypeArtMethodArrayClean:
case kNativeObjectRelocationTypeArtMethodArrayDirty: {
- memcpy(dest, pair.first, LengthPrefixedArray<ArtMethod>::ComputeSize(
- 0,
- ArtMethod::Size(target_ptr_size_),
- ArtMethod::Alignment(target_ptr_size_)));
+ size_t size = ArtMethod::Size(target_ptr_size_);
+ size_t alignment = ArtMethod::Alignment(target_ptr_size_);
+ memcpy(dest, pair.first, LengthPrefixedArray<ArtMethod>::ComputeSize(0, size, alignment));
+ // Clear padding to avoid non-deterministic data in the image (and placate valgrind).
+ reinterpret_cast<LengthPrefixedArray<ArtMethod>*>(dest)->ClearPadding(size, alignment);
break;
+ }
case kNativeObjectRelocationTypeDexCacheArray:
// Nothing to copy here, everything is done in FixupDexCache().
break;
- }
}
}
// Fixup the image method roots.
diff --git a/compiler/oat_test.cc b/compiler/oat_test.cc
index d22044aca3..4b48107848 100644
--- a/compiler/oat_test.cc
+++ b/compiler/oat_test.cc
@@ -230,7 +230,7 @@ class OatTest : public CommonCompilerTest {
return elf_writer->End();
}
- void TestDexFileInput(bool verify);
+ void TestDexFileInput(bool verify, bool low_4gb);
void TestZipFileInput(bool verify);
std::unique_ptr<const InstructionSetFeatures> insn_features_;
@@ -374,8 +374,14 @@ TEST_F(OatTest, WriteRead) {
if (kCompile) { // OatWriter strips the code, regenerate to compare
compiler_driver_->CompileAll(class_loader, class_linker->GetBootClassPath(), &timings);
}
- std::unique_ptr<OatFile> oat_file(OatFile::Open(tmp.GetFilename(), tmp.GetFilename(), nullptr,
- nullptr, false, nullptr, &error_msg));
+ std::unique_ptr<OatFile> oat_file(OatFile::Open(tmp.GetFilename(),
+ tmp.GetFilename(),
+ nullptr,
+ nullptr,
+ false,
+ /*low_4gb*/true,
+ nullptr,
+ &error_msg));
ASSERT_TRUE(oat_file.get() != nullptr) << error_msg;
const OatHeader& oat_header = oat_file->GetOatHeader();
ASSERT_TRUE(oat_header.IsValid());
@@ -504,6 +510,7 @@ TEST_F(OatTest, EmptyTextSection) {
nullptr,
nullptr,
false,
+ /*low_4gb*/false,
nullptr,
&error_msg));
ASSERT_TRUE(oat_file != nullptr);
@@ -518,7 +525,7 @@ static void MaybeModifyDexFileToFail(bool verify, std::unique_ptr<const DexFile>
}
}
-void OatTest::TestDexFileInput(bool verify) {
+void OatTest::TestDexFileInput(bool verify, bool low_4gb) {
TimingLogger timings("OatTest::DexFileInput", false, false);
std::vector<const char*> input_filenames;
@@ -572,8 +579,13 @@ void OatTest::TestDexFileInput(bool verify) {
nullptr,
nullptr,
false,
+ low_4gb,
nullptr,
&error_msg));
+ if (low_4gb) {
+ uintptr_t begin = reinterpret_cast<uintptr_t>(opened_oat_file->Begin());
+ EXPECT_EQ(begin, static_cast<uint32_t>(begin));
+ }
ASSERT_TRUE(opened_oat_file != nullptr);
ASSERT_EQ(2u, opened_oat_file->GetOatDexFiles().size());
std::unique_ptr<const DexFile> opened_dex_file1 =
@@ -595,11 +607,15 @@ void OatTest::TestDexFileInput(bool verify) {
}
TEST_F(OatTest, DexFileInputCheckOutput) {
- TestDexFileInput(false);
+ TestDexFileInput(false, /*low_4gb*/false);
+}
+
+TEST_F(OatTest, DexFileInputCheckOutputLow4GB) {
+ TestDexFileInput(false, /*low_4gb*/true);
}
TEST_F(OatTest, DexFileInputCheckVerifier) {
- TestDexFileInput(true);
+ TestDexFileInput(true, /*low_4gb*/false);
}
void OatTest::TestZipFileInput(bool verify) {
@@ -667,6 +683,7 @@ void OatTest::TestZipFileInput(bool verify) {
nullptr,
nullptr,
false,
+ /*low_4gb*/false,
nullptr,
&error_msg));
ASSERT_TRUE(opened_oat_file != nullptr);
@@ -714,6 +731,7 @@ void OatTest::TestZipFileInput(bool verify) {
nullptr,
nullptr,
false,
+ /*low_4gb*/false,
nullptr,
&error_msg));
ASSERT_TRUE(opened_oat_file != nullptr);
diff --git a/compiler/optimizing/bounds_check_elimination.cc b/compiler/optimizing/bounds_check_elimination.cc
index f2929bcc18..084360f22b 100644
--- a/compiler/optimizing/bounds_check_elimination.cc
+++ b/compiler/optimizing/bounds_check_elimination.cc
@@ -535,6 +535,7 @@ class BCEVisitor : public HGraphVisitor {
graph->GetArena()->Adapter(kArenaAllocBoundsCheckElimination)),
dynamic_bce_standby_(
graph->GetArena()->Adapter(kArenaAllocBoundsCheckElimination)),
+ record_dynamic_bce_standby_(true),
early_exit_loop_(
std::less<uint32_t>(),
graph->GetArena()->Adapter(kArenaAllocBoundsCheckElimination)),
@@ -556,6 +557,7 @@ class BCEVisitor : public HGraphVisitor {
void Finish() {
// Retry dynamic bce candidates on standby that are still in the graph.
+ record_dynamic_bce_standby_ = false;
for (HBoundsCheck* bounds_check : dynamic_bce_standby_) {
if (bounds_check->IsInBlock()) {
TryDynamicBCE(bounds_check);
@@ -1191,7 +1193,7 @@ class BCEVisitor : public HGraphVisitor {
if (!array_length->IsArrayLength()) {
continue; // disregard phis and constants
}
- // Collect all bounds checks are still there and that are related as "a[base + constant]"
+ // Collect all bounds checks that are still there and that are related as "a[base + constant]"
// for a base instruction (possibly absent) and various constants. Note that no attempt
// is made to partition the set into matching subsets (viz. a[0], a[1] and a[base+1] and
// a[base+2] are considered as one set).
@@ -1214,7 +1216,12 @@ class BCEVisitor : public HGraphVisitor {
HInstruction* other_array_length = other_bounds_check->InputAt(1);
ValueBound other_value = ValueBound::AsValueBound(other_index);
if (array_length == other_array_length && base == other_value.GetInstruction()) {
- int32_t other_c = other_value.GetConstant();
+ // Reject certain OOB if BoundsCheck(l, l) occurs on considered subset.
+ if (array_length == other_index) {
+ candidates.clear();
+ standby.clear();
+ break;
+ }
// Since a subsequent dominated block could be under a conditional, only accept
// the other bounds check if it is in same block or both blocks dominate the exit.
// TODO: we could improve this by testing proper post-dominance, or even if this
@@ -1222,6 +1229,7 @@ class BCEVisitor : public HGraphVisitor {
HBasicBlock* exit = GetGraph()->GetExitBlock();
if (block == user->GetBlock() ||
(block->Dominates(exit) && other_block->Dominates(exit))) {
+ int32_t other_c = other_value.GetConstant();
min_c = std::min(min_c, other_c);
max_c = std::max(max_c, other_c);
candidates.push_back(other_bounds_check);
@@ -1251,7 +1259,11 @@ class BCEVisitor : public HGraphVisitor {
distance <= kMaxLengthForAddingDeoptimize) { // reject likely/certain deopt
AddCompareWithDeoptimization(block, array_length, base, min_c, max_c);
for (HInstruction* other_bounds_check : candidates) {
- ReplaceInstruction(other_bounds_check, other_bounds_check->InputAt(0));
+ // Only replace if still in the graph. This avoids visiting the same
+ // bounds check twice if it occurred multiple times in the use list.
+ if (other_bounds_check->IsInBlock()) {
+ ReplaceInstruction(other_bounds_check, other_bounds_check->InputAt(0));
+ }
}
}
}
@@ -1467,7 +1479,9 @@ class BCEVisitor : public HGraphVisitor {
}
// If bounds check made it this far, it is worthwhile to check later if
// the loop was forced finite by another candidate.
- dynamic_bce_standby_.push_back(check);
+ if (record_dynamic_bce_standby_) {
+ dynamic_bce_standby_.push_back(check);
+ }
return false;
}
return true;
@@ -1691,6 +1705,7 @@ class BCEVisitor : public HGraphVisitor {
// Stand by list for dynamic bce.
ArenaVector<HBoundsCheck*> dynamic_bce_standby_;
+ bool record_dynamic_bce_standby_;
// Early-exit loop bookkeeping.
ArenaSafeMap<uint32_t, bool> early_exit_loop_;
diff --git a/compiler/optimizing/code_generator.cc b/compiler/optimizing/code_generator.cc
index af50363e31..f19872722c 100644
--- a/compiler/optimizing/code_generator.cc
+++ b/compiler/optimizing/code_generator.cc
@@ -706,7 +706,7 @@ void CodeGenerator::RecordPcInfo(HInstruction* instruction,
uint32_t dex_pc,
SlowPathCode* slow_path) {
if (instruction != nullptr) {
- // The code generated for some type conversions and comparisons
+ // The code generated for some type conversions
// may call the runtime, thus normally requiring a subsequent
// call to this method. However, the method verifier does not
// produce PC information for certain instructions, which are
@@ -717,7 +717,7 @@ void CodeGenerator::RecordPcInfo(HInstruction* instruction,
// CodeGenerator::RecordPcInfo without triggering an error in
// CodeGenerator::BuildNativeGCMap ("Missing ref for dex pc 0x")
// thereafter.
- if (instruction->IsTypeConversion() || instruction->IsCompare()) {
+ if (instruction->IsTypeConversion()) {
return;
}
if (instruction->IsRem()) {
@@ -1110,6 +1110,16 @@ void CodeGenerator::MaybeRecordImplicitNullCheck(HInstruction* instr) {
}
}
+void CodeGenerator::GenerateNullCheck(HNullCheck* instruction) {
+ if (IsImplicitNullCheckAllowed(instruction)) {
+ MaybeRecordStat(kImplicitNullCheckGenerated);
+ GenerateImplicitNullCheck(instruction);
+ } else {
+ MaybeRecordStat(kExplicitNullCheckGenerated);
+ GenerateExplicitNullCheck(instruction);
+ }
+}
+
void CodeGenerator::ClearSpillSlotsFromLoopPhisInStackMap(HSuspendCheck* suspend_check) const {
LocationSummary* locations = suspend_check->GetLocations();
HBasicBlock* block = suspend_check->GetBlock();
diff --git a/compiler/optimizing/code_generator.h b/compiler/optimizing/code_generator.h
index 9297fc956f..3066048132 100644
--- a/compiler/optimizing/code_generator.h
+++ b/compiler/optimizing/code_generator.h
@@ -278,6 +278,9 @@ class CodeGenerator {
bool CanMoveNullCheckToUser(HNullCheck* null_check);
void MaybeRecordImplicitNullCheck(HInstruction* instruction);
+ void GenerateNullCheck(HNullCheck* null_check);
+ virtual void GenerateImplicitNullCheck(HNullCheck* null_check) = 0;
+ virtual void GenerateExplicitNullCheck(HNullCheck* null_check) = 0;
// Records a stack map which the runtime might use to set catch phi values
// during exception delivery.
diff --git a/compiler/optimizing/code_generator_arm.cc b/compiler/optimizing/code_generator_arm.cc
index 0b7fefafdd..34fd9ff2a5 100644
--- a/compiler/optimizing/code_generator_arm.cc
+++ b/compiler/optimizing/code_generator_arm.cc
@@ -3151,6 +3151,7 @@ void InstructionCodeGeneratorARM::VisitDivZeroCheck(HDivZeroCheck* instruction)
Location value = locations->InAt(0);
switch (instruction->GetType()) {
+ case Primitive::kPrimBoolean:
case Primitive::kPrimByte:
case Primitive::kPrimChar:
case Primitive::kPrimShort:
@@ -3671,6 +3672,10 @@ void LocationsBuilderARM::VisitCompare(HCompare* compare) {
LocationSummary* locations =
new (GetGraph()->GetArena()) LocationSummary(compare, LocationSummary::kNoCall);
switch (compare->InputAt(0)->GetType()) {
+ case Primitive::kPrimBoolean:
+ case Primitive::kPrimByte:
+ case Primitive::kPrimShort:
+ case Primitive::kPrimChar:
case Primitive::kPrimInt:
case Primitive::kPrimLong: {
locations->SetInAt(0, Location::RequiresRegister());
@@ -3701,6 +3706,10 @@ void InstructionCodeGeneratorARM::VisitCompare(HCompare* compare) {
Primitive::Type type = compare->InputAt(0)->GetType();
Condition less_cond;
switch (type) {
+ case Primitive::kPrimBoolean:
+ case Primitive::kPrimByte:
+ case Primitive::kPrimShort:
+ case Primitive::kPrimChar:
case Primitive::kPrimInt: {
__ LoadImmediate(out, 0);
__ cmp(left.AsRegister<Register>(),
@@ -4284,19 +4293,19 @@ void LocationsBuilderARM::VisitNullCheck(HNullCheck* instruction) {
}
}
-void InstructionCodeGeneratorARM::GenerateImplicitNullCheck(HNullCheck* instruction) {
- if (codegen_->CanMoveNullCheckToUser(instruction)) {
+void CodeGeneratorARM::GenerateImplicitNullCheck(HNullCheck* instruction) {
+ if (CanMoveNullCheckToUser(instruction)) {
return;
}
Location obj = instruction->GetLocations()->InAt(0);
__ LoadFromOffset(kLoadWord, IP, obj.AsRegister<Register>(), 0);
- codegen_->RecordPcInfo(instruction, instruction->GetDexPc());
+ RecordPcInfo(instruction, instruction->GetDexPc());
}
-void InstructionCodeGeneratorARM::GenerateExplicitNullCheck(HNullCheck* instruction) {
+void CodeGeneratorARM::GenerateExplicitNullCheck(HNullCheck* instruction) {
SlowPathCode* slow_path = new (GetGraph()->GetArena()) NullCheckSlowPathARM(instruction);
- codegen_->AddSlowPath(slow_path);
+ AddSlowPath(slow_path);
LocationSummary* locations = instruction->GetLocations();
Location obj = locations->InAt(0);
@@ -4305,11 +4314,7 @@ void InstructionCodeGeneratorARM::GenerateExplicitNullCheck(HNullCheck* instruct
}
void InstructionCodeGeneratorARM::VisitNullCheck(HNullCheck* instruction) {
- if (codegen_->IsImplicitNullCheckAllowed(instruction)) {
- GenerateImplicitNullCheck(instruction);
- } else {
- GenerateExplicitNullCheck(instruction);
- }
+ codegen_->GenerateNullCheck(instruction);
}
void LocationsBuilderARM::VisitArrayGet(HArrayGet* instruction) {
diff --git a/compiler/optimizing/code_generator_arm.h b/compiler/optimizing/code_generator_arm.h
index 06e7c0015c..5c0f31c0cb 100644
--- a/compiler/optimizing/code_generator_arm.h
+++ b/compiler/optimizing/code_generator_arm.h
@@ -274,9 +274,6 @@ class InstructionCodeGeneratorARM : public InstructionCodeGenerator {
Location root,
Register obj,
uint32_t offset);
-
- void GenerateImplicitNullCheck(HNullCheck* instruction);
- void GenerateExplicitNullCheck(HNullCheck* instruction);
void GenerateTestAndBranch(HInstruction* instruction,
size_t condition_input_index,
Label* true_target,
@@ -514,6 +511,9 @@ class CodeGeneratorARM : public CodeGenerator {
void GenerateNop();
+ void GenerateImplicitNullCheck(HNullCheck* instruction);
+ void GenerateExplicitNullCheck(HNullCheck* instruction);
+
private:
// Factored implementation of GenerateFieldLoadWithBakerReadBarrier
// and GenerateArrayLoadWithBakerReadBarrier.
diff --git a/compiler/optimizing/code_generator_arm64.cc b/compiler/optimizing/code_generator_arm64.cc
index 89b9e2c599..a220e59946 100644
--- a/compiler/optimizing/code_generator_arm64.cc
+++ b/compiler/optimizing/code_generator_arm64.cc
@@ -2388,6 +2388,10 @@ void LocationsBuilderARM64::VisitCompare(HCompare* compare) {
new (GetGraph()->GetArena()) LocationSummary(compare, LocationSummary::kNoCall);
Primitive::Type in_type = compare->InputAt(0)->GetType();
switch (in_type) {
+ case Primitive::kPrimBoolean:
+ case Primitive::kPrimByte:
+ case Primitive::kPrimShort:
+ case Primitive::kPrimChar:
case Primitive::kPrimInt:
case Primitive::kPrimLong: {
locations->SetInAt(0, Location::RequiresRegister());
@@ -2417,6 +2421,10 @@ void InstructionCodeGeneratorARM64::VisitCompare(HCompare* compare) {
// 1 if: left > right
// -1 if: left < right
switch (in_type) {
+ case Primitive::kPrimBoolean:
+ case Primitive::kPrimByte:
+ case Primitive::kPrimShort:
+ case Primitive::kPrimChar:
case Primitive::kPrimInt:
case Primitive::kPrimLong: {
Register result = OutputRegister(compare);
@@ -2718,8 +2726,8 @@ void InstructionCodeGeneratorARM64::VisitDivZeroCheck(HDivZeroCheck* instruction
Primitive::Type type = instruction->GetType();
- if ((type == Primitive::kPrimBoolean) || !Primitive::IsIntegralType(type)) {
- LOG(FATAL) << "Unexpected type " << type << " for DivZeroCheck.";
+ if (!Primitive::IsIntegralType(type)) {
+ LOG(FATAL) << "Unexpected type " << type << " for DivZeroCheck.";
return;
}
@@ -4193,20 +4201,20 @@ void LocationsBuilderARM64::VisitNullCheck(HNullCheck* instruction) {
}
}
-void InstructionCodeGeneratorARM64::GenerateImplicitNullCheck(HNullCheck* instruction) {
- if (codegen_->CanMoveNullCheckToUser(instruction)) {
+void CodeGeneratorARM64::GenerateImplicitNullCheck(HNullCheck* instruction) {
+ if (CanMoveNullCheckToUser(instruction)) {
return;
}
BlockPoolsScope block_pools(GetVIXLAssembler());
Location obj = instruction->GetLocations()->InAt(0);
__ Ldr(wzr, HeapOperandFrom(obj, Offset(0)));
- codegen_->RecordPcInfo(instruction, instruction->GetDexPc());
+ RecordPcInfo(instruction, instruction->GetDexPc());
}
-void InstructionCodeGeneratorARM64::GenerateExplicitNullCheck(HNullCheck* instruction) {
+void CodeGeneratorARM64::GenerateExplicitNullCheck(HNullCheck* instruction) {
SlowPathCodeARM64* slow_path = new (GetGraph()->GetArena()) NullCheckSlowPathARM64(instruction);
- codegen_->AddSlowPath(slow_path);
+ AddSlowPath(slow_path);
LocationSummary* locations = instruction->GetLocations();
Location obj = locations->InAt(0);
@@ -4215,11 +4223,7 @@ void InstructionCodeGeneratorARM64::GenerateExplicitNullCheck(HNullCheck* instru
}
void InstructionCodeGeneratorARM64::VisitNullCheck(HNullCheck* instruction) {
- if (codegen_->IsImplicitNullCheckAllowed(instruction)) {
- GenerateImplicitNullCheck(instruction);
- } else {
- GenerateExplicitNullCheck(instruction);
- }
+ codegen_->GenerateNullCheck(instruction);
}
void LocationsBuilderARM64::VisitOr(HOr* instruction) {
diff --git a/compiler/optimizing/code_generator_arm64.h b/compiler/optimizing/code_generator_arm64.h
index 10f1e7f008..a1f686e39d 100644
--- a/compiler/optimizing/code_generator_arm64.h
+++ b/compiler/optimizing/code_generator_arm64.h
@@ -258,8 +258,6 @@ class InstructionCodeGeneratorARM64 : public InstructionCodeGenerator {
uint32_t offset);
void HandleShift(HBinaryOperation* instr);
- void GenerateImplicitNullCheck(HNullCheck* instruction);
- void GenerateExplicitNullCheck(HNullCheck* instruction);
void GenerateTestAndBranch(HInstruction* instruction,
size_t condition_input_index,
vixl::Label* true_target,
@@ -537,6 +535,9 @@ class CodeGeneratorARM64 : public CodeGenerator {
void GenerateNop();
+ void GenerateImplicitNullCheck(HNullCheck* instruction);
+ void GenerateExplicitNullCheck(HNullCheck* instruction);
+
private:
// Factored implementation of GenerateFieldLoadWithBakerReadBarrier
// and GenerateArrayLoadWithBakerReadBarrier.
diff --git a/compiler/optimizing/code_generator_mips.cc b/compiler/optimizing/code_generator_mips.cc
index f3c12efd8d..3c2c0b05fa 100644
--- a/compiler/optimizing/code_generator_mips.cc
+++ b/compiler/optimizing/code_generator_mips.cc
@@ -2070,6 +2070,10 @@ void LocationsBuilderMIPS::VisitCompare(HCompare* compare) {
new (GetGraph()->GetArena()) LocationSummary(compare, LocationSummary::kNoCall);
switch (in_type) {
+ case Primitive::kPrimBoolean:
+ case Primitive::kPrimByte:
+ case Primitive::kPrimShort:
+ case Primitive::kPrimChar:
case Primitive::kPrimInt:
case Primitive::kPrimLong:
locations->SetInAt(0, Location::RequiresRegister());
@@ -2100,6 +2104,10 @@ void InstructionCodeGeneratorMIPS::VisitCompare(HCompare* instruction) {
// 1 if: left > right
// -1 if: left < right
switch (in_type) {
+ case Primitive::kPrimBoolean:
+ case Primitive::kPrimByte:
+ case Primitive::kPrimShort:
+ case Primitive::kPrimChar:
case Primitive::kPrimInt: {
Register lhs = locations->InAt(0).AsRegister<Register>();
Register rhs = locations->InAt(1).AsRegister<Register>();
@@ -2530,6 +2538,7 @@ void InstructionCodeGeneratorMIPS::VisitDivZeroCheck(HDivZeroCheck* instruction)
Primitive::Type type = instruction->GetType();
switch (type) {
+ case Primitive::kPrimBoolean:
case Primitive::kPrimByte:
case Primitive::kPrimChar:
case Primitive::kPrimShort:
@@ -4395,19 +4404,19 @@ void LocationsBuilderMIPS::VisitNullCheck(HNullCheck* instruction) {
}
}
-void InstructionCodeGeneratorMIPS::GenerateImplicitNullCheck(HNullCheck* instruction) {
- if (codegen_->CanMoveNullCheckToUser(instruction)) {
+void CodeGeneratorMIPS::GenerateImplicitNullCheck(HNullCheck* instruction) {
+ if (CanMoveNullCheckToUser(instruction)) {
return;
}
Location obj = instruction->GetLocations()->InAt(0);
__ Lw(ZERO, obj.AsRegister<Register>(), 0);
- codegen_->RecordPcInfo(instruction, instruction->GetDexPc());
+ RecordPcInfo(instruction, instruction->GetDexPc());
}
-void InstructionCodeGeneratorMIPS::GenerateExplicitNullCheck(HNullCheck* instruction) {
+void CodeGeneratorMIPS::GenerateExplicitNullCheck(HNullCheck* instruction) {
SlowPathCodeMIPS* slow_path = new (GetGraph()->GetArena()) NullCheckSlowPathMIPS(instruction);
- codegen_->AddSlowPath(slow_path);
+ AddSlowPath(slow_path);
Location obj = instruction->GetLocations()->InAt(0);
@@ -4415,11 +4424,7 @@ void InstructionCodeGeneratorMIPS::GenerateExplicitNullCheck(HNullCheck* instruc
}
void InstructionCodeGeneratorMIPS::VisitNullCheck(HNullCheck* instruction) {
- if (codegen_->IsImplicitNullCheckAllowed(instruction)) {
- GenerateImplicitNullCheck(instruction);
- } else {
- GenerateExplicitNullCheck(instruction);
- }
+ codegen_->GenerateNullCheck(instruction);
}
void LocationsBuilderMIPS::VisitOr(HOr* instruction) {
diff --git a/compiler/optimizing/code_generator_mips.h b/compiler/optimizing/code_generator_mips.h
index 605c794421..b720573897 100644
--- a/compiler/optimizing/code_generator_mips.h
+++ b/compiler/optimizing/code_generator_mips.h
@@ -226,8 +226,6 @@ class InstructionCodeGeneratorMIPS : public InstructionCodeGenerator {
void HandleShift(HBinaryOperation* operation);
void HandleFieldSet(HInstruction* instruction, const FieldInfo& field_info, uint32_t dex_pc);
void HandleFieldGet(HInstruction* instruction, const FieldInfo& field_info, uint32_t dex_pc);
- void GenerateImplicitNullCheck(HNullCheck* instruction);
- void GenerateExplicitNullCheck(HNullCheck* instruction);
void GenerateIntCompare(IfCondition cond, LocationSummary* locations);
void GenerateIntCompareAndBranch(IfCondition cond,
LocationSummary* locations,
@@ -362,6 +360,8 @@ class CodeGeneratorMIPS : public CodeGenerator {
}
void GenerateNop();
+ void GenerateImplicitNullCheck(HNullCheck* instruction);
+ void GenerateExplicitNullCheck(HNullCheck* instruction);
private:
// Labels for each block that will be compiled.
diff --git a/compiler/optimizing/code_generator_mips64.cc b/compiler/optimizing/code_generator_mips64.cc
index c2b84b4335..ddc873d440 100644
--- a/compiler/optimizing/code_generator_mips64.cc
+++ b/compiler/optimizing/code_generator_mips64.cc
@@ -1691,6 +1691,10 @@ void LocationsBuilderMIPS64::VisitCompare(HCompare* compare) {
LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(compare);
switch (in_type) {
+ case Primitive::kPrimBoolean:
+ case Primitive::kPrimByte:
+ case Primitive::kPrimShort:
+ case Primitive::kPrimChar:
case Primitive::kPrimInt:
case Primitive::kPrimLong:
locations->SetInAt(0, Location::RequiresRegister());
@@ -1719,6 +1723,10 @@ void InstructionCodeGeneratorMIPS64::VisitCompare(HCompare* instruction) {
// 1 if: left > right
// -1 if: left < right
switch (in_type) {
+ case Primitive::kPrimBoolean:
+ case Primitive::kPrimByte:
+ case Primitive::kPrimShort:
+ case Primitive::kPrimChar:
case Primitive::kPrimInt:
case Primitive::kPrimLong: {
GpuRegister lhs = locations->InAt(0).AsRegister<GpuRegister>();
@@ -1726,17 +1734,17 @@ void InstructionCodeGeneratorMIPS64::VisitCompare(HCompare* instruction) {
bool use_imm = rhs_location.IsConstant();
GpuRegister rhs = ZERO;
if (use_imm) {
- if (in_type == Primitive::kPrimInt) {
- int32_t value = CodeGenerator::GetInt32ValueOf(rhs_location.GetConstant()->AsConstant());
+ if (in_type == Primitive::kPrimLong) {
+ int64_t value = CodeGenerator::GetInt64ValueOf(rhs_location.GetConstant()->AsConstant());
if (value != 0) {
rhs = AT;
- __ LoadConst32(rhs, value);
+ __ LoadConst64(rhs, value);
}
} else {
- int64_t value = CodeGenerator::GetInt64ValueOf(rhs_location.GetConstant()->AsConstant());
+ int32_t value = CodeGenerator::GetInt32ValueOf(rhs_location.GetConstant()->AsConstant());
if (value != 0) {
rhs = AT;
- __ LoadConst64(rhs, value);
+ __ LoadConst32(rhs, value);
}
}
} else {
@@ -2172,8 +2180,8 @@ void InstructionCodeGeneratorMIPS64::VisitDivZeroCheck(HDivZeroCheck* instructio
Primitive::Type type = instruction->GetType();
- if ((type == Primitive::kPrimBoolean) || !Primitive::IsIntegralType(type)) {
- LOG(FATAL) << "Unexpected type " << type << " for DivZeroCheck.";
+ if (!Primitive::IsIntegralType(type)) {
+ LOG(FATAL) << "Unexpected type " << type << " for DivZeroCheck.";
return;
}
@@ -3550,19 +3558,19 @@ void LocationsBuilderMIPS64::VisitNullCheck(HNullCheck* instruction) {
}
}
-void InstructionCodeGeneratorMIPS64::GenerateImplicitNullCheck(HNullCheck* instruction) {
- if (codegen_->CanMoveNullCheckToUser(instruction)) {
+void CodeGeneratorMIPS64::GenerateImplicitNullCheck(HNullCheck* instruction) {
+ if (CanMoveNullCheckToUser(instruction)) {
return;
}
Location obj = instruction->GetLocations()->InAt(0);
__ Lw(ZERO, obj.AsRegister<GpuRegister>(), 0);
- codegen_->RecordPcInfo(instruction, instruction->GetDexPc());
+ RecordPcInfo(instruction, instruction->GetDexPc());
}
-void InstructionCodeGeneratorMIPS64::GenerateExplicitNullCheck(HNullCheck* instruction) {
+void CodeGeneratorMIPS64::GenerateExplicitNullCheck(HNullCheck* instruction) {
SlowPathCodeMIPS64* slow_path = new (GetGraph()->GetArena()) NullCheckSlowPathMIPS64(instruction);
- codegen_->AddSlowPath(slow_path);
+ AddSlowPath(slow_path);
Location obj = instruction->GetLocations()->InAt(0);
@@ -3570,11 +3578,7 @@ void InstructionCodeGeneratorMIPS64::GenerateExplicitNullCheck(HNullCheck* instr
}
void InstructionCodeGeneratorMIPS64::VisitNullCheck(HNullCheck* instruction) {
- if (codegen_->IsImplicitNullCheckAllowed(instruction)) {
- GenerateImplicitNullCheck(instruction);
- } else {
- GenerateExplicitNullCheck(instruction);
- }
+ codegen_->GenerateNullCheck(instruction);
}
void LocationsBuilderMIPS64::VisitOr(HOr* instruction) {
diff --git a/compiler/optimizing/code_generator_mips64.h b/compiler/optimizing/code_generator_mips64.h
index ba9eaff46f..9464a140ad 100644
--- a/compiler/optimizing/code_generator_mips64.h
+++ b/compiler/optimizing/code_generator_mips64.h
@@ -228,8 +228,6 @@ class InstructionCodeGeneratorMIPS64 : public InstructionCodeGenerator {
const FieldInfo& field_info,
bool value_can_be_null);
void HandleFieldGet(HInstruction* instruction, const FieldInfo& field_info);
- void GenerateImplicitNullCheck(HNullCheck* instruction);
- void GenerateExplicitNullCheck(HNullCheck* instruction);
void GenerateTestAndBranch(HInstruction* instruction,
size_t condition_input_index,
Mips64Label* true_target,
@@ -354,6 +352,8 @@ class CodeGeneratorMIPS64 : public CodeGenerator {
}
void GenerateNop();
+ void GenerateImplicitNullCheck(HNullCheck* instruction);
+ void GenerateExplicitNullCheck(HNullCheck* instruction);
private:
// Labels for each block that will be compiled.
diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc
index 6b4a18c688..9acaa1d000 100644
--- a/compiler/optimizing/code_generator_x86.cc
+++ b/compiler/optimizing/code_generator_x86.cc
@@ -3662,6 +3662,7 @@ void LocationsBuilderX86::VisitDivZeroCheck(HDivZeroCheck* instruction) {
: LocationSummary::kNoCall;
LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction, call_kind);
switch (instruction->GetType()) {
+ case Primitive::kPrimBoolean:
case Primitive::kPrimByte:
case Primitive::kPrimChar:
case Primitive::kPrimShort:
@@ -3692,6 +3693,7 @@ void InstructionCodeGeneratorX86::VisitDivZeroCheck(HDivZeroCheck* instruction)
Location value = locations->InAt(0);
switch (instruction->GetType()) {
+ case Primitive::kPrimBoolean:
case Primitive::kPrimByte:
case Primitive::kPrimChar:
case Primitive::kPrimShort:
@@ -4184,6 +4186,10 @@ void LocationsBuilderX86::VisitCompare(HCompare* compare) {
LocationSummary* locations =
new (GetGraph()->GetArena()) LocationSummary(compare, LocationSummary::kNoCall);
switch (compare->InputAt(0)->GetType()) {
+ case Primitive::kPrimBoolean:
+ case Primitive::kPrimByte:
+ case Primitive::kPrimShort:
+ case Primitive::kPrimChar:
case Primitive::kPrimInt:
case Primitive::kPrimLong: {
locations->SetInAt(0, Location::RequiresRegister());
@@ -4219,6 +4225,10 @@ void InstructionCodeGeneratorX86::VisitCompare(HCompare* compare) {
Condition less_cond = kLess;
switch (compare->InputAt(0)->GetType()) {
+ case Primitive::kPrimBoolean:
+ case Primitive::kPrimByte:
+ case Primitive::kPrimShort:
+ case Primitive::kPrimChar:
case Primitive::kPrimInt: {
GenerateIntCompare(left, right);
break;
@@ -4971,20 +4981,20 @@ void LocationsBuilderX86::VisitNullCheck(HNullCheck* instruction) {
}
}
-void InstructionCodeGeneratorX86::GenerateImplicitNullCheck(HNullCheck* instruction) {
- if (codegen_->CanMoveNullCheckToUser(instruction)) {
+void CodeGeneratorX86::GenerateImplicitNullCheck(HNullCheck* instruction) {
+ if (CanMoveNullCheckToUser(instruction)) {
return;
}
LocationSummary* locations = instruction->GetLocations();
Location obj = locations->InAt(0);
__ testl(EAX, Address(obj.AsRegister<Register>(), 0));
- codegen_->RecordPcInfo(instruction, instruction->GetDexPc());
+ RecordPcInfo(instruction, instruction->GetDexPc());
}
-void InstructionCodeGeneratorX86::GenerateExplicitNullCheck(HNullCheck* instruction) {
+void CodeGeneratorX86::GenerateExplicitNullCheck(HNullCheck* instruction) {
SlowPathCode* slow_path = new (GetGraph()->GetArena()) NullCheckSlowPathX86(instruction);
- codegen_->AddSlowPath(slow_path);
+ AddSlowPath(slow_path);
LocationSummary* locations = instruction->GetLocations();
Location obj = locations->InAt(0);
@@ -5003,11 +5013,7 @@ void InstructionCodeGeneratorX86::GenerateExplicitNullCheck(HNullCheck* instruct
}
void InstructionCodeGeneratorX86::VisitNullCheck(HNullCheck* instruction) {
- if (codegen_->IsImplicitNullCheckAllowed(instruction)) {
- GenerateImplicitNullCheck(instruction);
- } else {
- GenerateExplicitNullCheck(instruction);
- }
+ codegen_->GenerateNullCheck(instruction);
}
void LocationsBuilderX86::VisitArrayGet(HArrayGet* instruction) {
diff --git a/compiler/optimizing/code_generator_x86.h b/compiler/optimizing/code_generator_x86.h
index 0795f3b530..c397899892 100644
--- a/compiler/optimizing/code_generator_x86.h
+++ b/compiler/optimizing/code_generator_x86.h
@@ -271,8 +271,6 @@ class InstructionCodeGeneratorX86 : public InstructionCodeGenerator {
void PushOntoFPStack(Location source, uint32_t temp_offset,
uint32_t stack_adjustment, bool is_fp, bool is_wide);
- void GenerateImplicitNullCheck(HNullCheck* instruction);
- void GenerateExplicitNullCheck(HNullCheck* instruction);
template<class LabelType>
void GenerateTestAndBranch(HInstruction* instruction,
size_t condition_input_index,
@@ -541,6 +539,8 @@ class CodeGeneratorX86 : public CodeGenerator {
}
void GenerateNop();
+ void GenerateImplicitNullCheck(HNullCheck* instruction);
+ void GenerateExplicitNullCheck(HNullCheck* instruction);
private:
// Factored implementation of GenerateFieldLoadWithBakerReadBarrier
diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc
index c132663016..51bc8c204a 100644
--- a/compiler/optimizing/code_generator_x86_64.cc
+++ b/compiler/optimizing/code_generator_x86_64.cc
@@ -1860,6 +1860,10 @@ void LocationsBuilderX86_64::VisitCompare(HCompare* compare) {
LocationSummary* locations =
new (GetGraph()->GetArena()) LocationSummary(compare, LocationSummary::kNoCall);
switch (compare->InputAt(0)->GetType()) {
+ case Primitive::kPrimBoolean:
+ case Primitive::kPrimByte:
+ case Primitive::kPrimShort:
+ case Primitive::kPrimChar:
case Primitive::kPrimInt:
case Primitive::kPrimLong: {
locations->SetInAt(0, Location::RequiresRegister());
@@ -1890,6 +1894,10 @@ void InstructionCodeGeneratorX86_64::VisitCompare(HCompare* compare) {
Condition less_cond = kLess;
switch (type) {
+ case Primitive::kPrimBoolean:
+ case Primitive::kPrimByte:
+ case Primitive::kPrimShort:
+ case Primitive::kPrimChar:
case Primitive::kPrimInt: {
CpuRegister left_reg = left.AsRegister<CpuRegister>();
if (right.IsConstant()) {
@@ -3713,6 +3721,7 @@ void InstructionCodeGeneratorX86_64::VisitDivZeroCheck(HDivZeroCheck* instructio
Location value = locations->InAt(0);
switch (instruction->GetType()) {
+ case Primitive::kPrimBoolean:
case Primitive::kPrimByte:
case Primitive::kPrimChar:
case Primitive::kPrimShort:
@@ -4487,20 +4496,20 @@ void LocationsBuilderX86_64::VisitNullCheck(HNullCheck* instruction) {
}
}
-void InstructionCodeGeneratorX86_64::GenerateImplicitNullCheck(HNullCheck* instruction) {
- if (codegen_->CanMoveNullCheckToUser(instruction)) {
+void CodeGeneratorX86_64::GenerateImplicitNullCheck(HNullCheck* instruction) {
+ if (CanMoveNullCheckToUser(instruction)) {
return;
}
LocationSummary* locations = instruction->GetLocations();
Location obj = locations->InAt(0);
__ testl(CpuRegister(RAX), Address(obj.AsRegister<CpuRegister>(), 0));
- codegen_->RecordPcInfo(instruction, instruction->GetDexPc());
+ RecordPcInfo(instruction, instruction->GetDexPc());
}
-void InstructionCodeGeneratorX86_64::GenerateExplicitNullCheck(HNullCheck* instruction) {
+void CodeGeneratorX86_64::GenerateExplicitNullCheck(HNullCheck* instruction) {
SlowPathCode* slow_path = new (GetGraph()->GetArena()) NullCheckSlowPathX86_64(instruction);
- codegen_->AddSlowPath(slow_path);
+ AddSlowPath(slow_path);
LocationSummary* locations = instruction->GetLocations();
Location obj = locations->InAt(0);
@@ -4519,11 +4528,7 @@ void InstructionCodeGeneratorX86_64::GenerateExplicitNullCheck(HNullCheck* instr
}
void InstructionCodeGeneratorX86_64::VisitNullCheck(HNullCheck* instruction) {
- if (codegen_->IsImplicitNullCheckAllowed(instruction)) {
- GenerateImplicitNullCheck(instruction);
- } else {
- GenerateExplicitNullCheck(instruction);
- }
+ codegen_->GenerateNullCheck(instruction);
}
void LocationsBuilderX86_64::VisitArrayGet(HArrayGet* instruction) {
diff --git a/compiler/optimizing/code_generator_x86_64.h b/compiler/optimizing/code_generator_x86_64.h
index b3d27e194a..c3fce6e824 100644
--- a/compiler/optimizing/code_generator_x86_64.h
+++ b/compiler/optimizing/code_generator_x86_64.h
@@ -260,8 +260,6 @@ class InstructionCodeGeneratorX86_64 : public InstructionCodeGenerator {
CpuRegister obj,
uint32_t offset);
- void GenerateImplicitNullCheck(HNullCheck* instruction);
- void GenerateExplicitNullCheck(HNullCheck* instruction);
void PushOntoFPStack(Location source, uint32_t temp_offset,
uint32_t stack_adjustment, bool is_float);
void GenerateCompareTest(HCondition* condition);
@@ -514,6 +512,8 @@ class CodeGeneratorX86_64 : public CodeGenerator {
}
void GenerateNop();
+ void GenerateImplicitNullCheck(HNullCheck* instruction);
+ void GenerateExplicitNullCheck(HNullCheck* instruction);
private:
// Factored implementation of GenerateFieldLoadWithBakerReadBarrier
diff --git a/compiler/optimizing/graph_checker.cc b/compiler/optimizing/graph_checker.cc
index 4a49c83611..1fbb2d5277 100644
--- a/compiler/optimizing/graph_checker.cc
+++ b/compiler/optimizing/graph_checker.cc
@@ -661,19 +661,6 @@ void GraphChecker::HandleLoop(HBasicBlock* loop_header) {
}
}
-static Primitive::Type PrimitiveKind(Primitive::Type type) {
- switch (type) {
- case Primitive::kPrimBoolean:
- case Primitive::kPrimByte:
- case Primitive::kPrimShort:
- case Primitive::kPrimChar:
- case Primitive::kPrimInt:
- return Primitive::kPrimInt;
- default:
- return type;
- }
-}
-
static bool IsSameSizeConstant(HInstruction* insn1, HInstruction* insn2) {
return insn1->IsConstant()
&& insn2->IsConstant()
@@ -716,10 +703,10 @@ void GraphChecker::VisitPhi(HPhi* phi) {
// Ensure that the inputs have the same primitive kind as the phi.
for (size_t i = 0, e = phi->InputCount(); i < e; ++i) {
HInstruction* input = phi->InputAt(i);
- if (PrimitiveKind(input->GetType()) != PrimitiveKind(phi->GetType())) {
+ if (Primitive::PrimitiveKind(input->GetType()) != Primitive::PrimitiveKind(phi->GetType())) {
AddError(StringPrintf(
"Input %d at index %zu of phi %d from block %d does not have the "
- "same type as the phi: %s versus %s",
+ "same kind as the phi: %s versus %s",
input->GetId(), i, phi->GetId(), phi->GetBlock()->GetBlockId(),
Primitive::PrettyDescriptor(input->GetType()),
Primitive::PrettyDescriptor(phi->GetType())));
@@ -910,9 +897,9 @@ void GraphChecker::VisitCondition(HCondition* op) {
}
HInstruction* lhs = op->InputAt(0);
HInstruction* rhs = op->InputAt(1);
- if (PrimitiveKind(lhs->GetType()) != PrimitiveKind(rhs->GetType())) {
+ if (Primitive::PrimitiveKind(lhs->GetType()) != Primitive::PrimitiveKind(rhs->GetType())) {
AddError(StringPrintf(
- "Condition %s %d has inputs of different types: %s, and %s.",
+ "Condition %s %d has inputs of different kinds: %s, and %s.",
op->DebugName(), op->GetId(),
Primitive::PrettyDescriptor(lhs->GetType()),
Primitive::PrettyDescriptor(rhs->GetType())));
@@ -932,42 +919,39 @@ void GraphChecker::VisitCondition(HCondition* op) {
void GraphChecker::VisitBinaryOperation(HBinaryOperation* op) {
VisitInstruction(op);
+ Primitive::Type lhs_type = op->InputAt(0)->GetType();
+ Primitive::Type rhs_type = op->InputAt(1)->GetType();
+ Primitive::Type result_type = op->GetType();
if (op->IsUShr() || op->IsShr() || op->IsShl() || op->IsRor()) {
- if (PrimitiveKind(op->InputAt(1)->GetType()) != Primitive::kPrimInt) {
- AddError(StringPrintf(
- "Shift operation %s %d has a non-int kind second input: "
- "%s of type %s.",
- op->DebugName(), op->GetId(),
- op->InputAt(1)->DebugName(),
- Primitive::PrettyDescriptor(op->InputAt(1)->GetType())));
+ if (Primitive::PrimitiveKind(rhs_type) != Primitive::kPrimInt) {
+ AddError(StringPrintf("Shift operation %s %d has a non-int kind second input: %s of type %s.",
+ op->DebugName(), op->GetId(),
+ op->InputAt(1)->DebugName(),
+ Primitive::PrettyDescriptor(rhs_type)));
}
} else {
- if (PrimitiveKind(op->InputAt(0)->GetType()) != PrimitiveKind(op->InputAt(1)->GetType())) {
- AddError(StringPrintf(
- "Binary operation %s %d has inputs of different types: "
- "%s, and %s.",
- op->DebugName(), op->GetId(),
- Primitive::PrettyDescriptor(op->InputAt(0)->GetType()),
- Primitive::PrettyDescriptor(op->InputAt(1)->GetType())));
+ if (Primitive::PrimitiveKind(lhs_type) != Primitive::PrimitiveKind(rhs_type)) {
+ AddError(StringPrintf("Binary operation %s %d has inputs of different kinds: %s, and %s.",
+ op->DebugName(), op->GetId(),
+ Primitive::PrettyDescriptor(lhs_type),
+ Primitive::PrettyDescriptor(rhs_type)));
}
}
if (op->IsCompare()) {
- if (op->GetType() != Primitive::kPrimInt) {
- AddError(StringPrintf(
- "Compare operation %d has a non-int result type: %s.",
- op->GetId(),
- Primitive::PrettyDescriptor(op->GetType())));
+ if (result_type != Primitive::kPrimInt) {
+ AddError(StringPrintf("Compare operation %d has a non-int result type: %s.",
+ op->GetId(),
+ Primitive::PrettyDescriptor(result_type)));
}
} else {
// Use the first input, so that we can also make this check for shift operations.
- if (PrimitiveKind(op->GetType()) != PrimitiveKind(op->InputAt(0)->GetType())) {
- AddError(StringPrintf(
- "Binary operation %s %d has a result type different "
- "from its input type: %s vs %s.",
- op->DebugName(), op->GetId(),
- Primitive::PrettyDescriptor(op->GetType()),
- Primitive::PrettyDescriptor(op->InputAt(0)->GetType())));
+ if (Primitive::PrimitiveKind(result_type) != Primitive::PrimitiveKind(lhs_type)) {
+ AddError(StringPrintf("Binary operation %s %d has a result kind different "
+ "from its input kind: %s vs %s.",
+ op->DebugName(), op->GetId(),
+ Primitive::PrettyDescriptor(result_type),
+ Primitive::PrettyDescriptor(lhs_type)));
}
}
}
diff --git a/compiler/optimizing/inliner.cc b/compiler/optimizing/inliner.cc
index d861e39c8b..573b58340c 100644
--- a/compiler/optimizing/inliner.cc
+++ b/compiler/optimizing/inliner.cc
@@ -293,7 +293,11 @@ bool HInliner::TryInline(HInvoke* invoke_instruction) {
}
if (actual_method != nullptr) {
- return TryInlineAndReplace(invoke_instruction, actual_method, /* do_rtp */ true);
+ bool result = TryInlineAndReplace(invoke_instruction, actual_method, /* do_rtp */ true);
+ if (result && !invoke_instruction->IsInvokeStaticOrDirect()) {
+ MaybeRecordStat(kInlinedInvokeVirtualOrInterface);
+ }
+ return result;
}
DCHECK(!invoke_instruction->IsInvokeStaticOrDirect());
@@ -1279,10 +1283,14 @@ void HInliner::FixUpReturnReferenceType(HInvoke* invoke_instruction,
// some functionality from the reference type propagation.
DCHECK(return_replacement->IsPhi());
size_t pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize();
- ReferenceTypeInfo::TypeHandle return_handle =
- handles_->NewHandle(resolved_method->GetReturnType(true /* resolve */, pointer_size));
- return_replacement->SetReferenceTypeInfo(ReferenceTypeInfo::Create(
- return_handle, return_handle->CannotBeAssignedFromOtherTypes() /* is_exact */));
+ mirror::Class* cls = resolved_method->GetReturnType(false /* resolve */, pointer_size);
+ if (cls != nullptr) {
+ ReferenceTypeInfo::TypeHandle return_handle = handles_->NewHandle(cls);
+ return_replacement->SetReferenceTypeInfo(ReferenceTypeInfo::Create(
+ return_handle, return_handle->CannotBeAssignedFromOtherTypes() /* is_exact */));
+ } else {
+ return_replacement->SetReferenceTypeInfo(graph_->GetInexactObjectRti());
+ }
}
if (do_rtp) {
diff --git a/compiler/optimizing/instruction_simplifier.cc b/compiler/optimizing/instruction_simplifier.cc
index 049901b882..dd2977f799 100644
--- a/compiler/optimizing/instruction_simplifier.cc
+++ b/compiler/optimizing/instruction_simplifier.cc
@@ -34,8 +34,12 @@ class InstructionSimplifierVisitor : public HGraphDelegateVisitor {
void RecordSimplification() {
simplification_occurred_ = true;
simplifications_at_current_position_++;
- if (stats_) {
- stats_->RecordStat(kInstructionSimplifications);
+ MaybeRecordStat(kInstructionSimplifications);
+ }
+
+ void MaybeRecordStat(MethodCompilationStat stat) {
+ if (stats_ != nullptr) {
+ stats_->RecordStat(stat);
}
}
@@ -95,7 +99,7 @@ class InstructionSimplifierVisitor : public HGraphDelegateVisitor {
void SimplifyRotate(HInvoke* invoke, bool is_left);
void SimplifySystemArrayCopy(HInvoke* invoke);
void SimplifyStringEquals(HInvoke* invoke);
- void SimplifyCompare(HInvoke* invoke, bool has_zero_op);
+ void SimplifyCompare(HInvoke* invoke, bool is_signum, Primitive::Type type);
void SimplifyIsNaN(HInvoke* invoke);
void SimplifyFP2Int(HInvoke* invoke);
void SimplifyMemBarrier(HInvoke* invoke, MemBarrierKind barrier_kind);
@@ -235,7 +239,10 @@ void InstructionSimplifierVisitor::VisitShift(HBinaryOperation* instruction) {
HInstruction* input_other = instruction->GetLeastConstantLeft();
if (input_cst != nullptr) {
- if (input_cst->IsZero()) {
+ int64_t cst = Int64FromConstant(input_cst);
+ int64_t mask =
+ (input_other->GetType() == Primitive::kPrimLong) ? kMaxLongShiftValue : kMaxIntShiftValue;
+ if ((cst & mask) == 0) {
// Replace code looking like
// SHL dst, src, 0
// with
@@ -463,19 +470,17 @@ void InstructionSimplifierVisitor::VisitCheckCast(HCheckCast* check_cast) {
if (object->IsNullConstant()) {
check_cast->GetBlock()->RemoveInstruction(check_cast);
- if (stats_ != nullptr) {
- stats_->RecordStat(MethodCompilationStat::kRemovedCheckedCast);
- }
+ MaybeRecordStat(MethodCompilationStat::kRemovedCheckedCast);
return;
}
- bool outcome;
+ // Note: The `outcome` is initialized to please valgrind - the compiler can reorder
+ // the return value check with the `outcome` check, b/27651442 .
+ bool outcome = false;
if (TypeCheckHasKnownOutcome(load_class, object, &outcome)) {
if (outcome) {
check_cast->GetBlock()->RemoveInstruction(check_cast);
- if (stats_ != nullptr) {
- stats_->RecordStat(MethodCompilationStat::kRemovedCheckedCast);
- }
+ MaybeRecordStat(MethodCompilationStat::kRemovedCheckedCast);
if (!load_class->HasUses()) {
// We cannot rely on DCE to remove the class because the `HLoadClass` thinks it can throw.
// However, here we know that it cannot because the checkcast was successfull, hence
@@ -505,14 +510,18 @@ void InstructionSimplifierVisitor::VisitInstanceOf(HInstanceOf* instruction) {
HGraph* graph = GetGraph();
if (object->IsNullConstant()) {
+ MaybeRecordStat(kRemovedInstanceOf);
instruction->ReplaceWith(graph->GetIntConstant(0));
instruction->GetBlock()->RemoveInstruction(instruction);
RecordSimplification();
return;
}
- bool outcome;
+ // Note: The `outcome` is initialized to please valgrind - the compiler can reorder
+ // the return value check with the `outcome` check, b/27651442 .
+ bool outcome = false;
if (TypeCheckHasKnownOutcome(load_class, object, &outcome)) {
+ MaybeRecordStat(kRemovedInstanceOf);
if (outcome && can_be_null) {
// Type test will succeed, we just need a null test.
HNotEqual* test = new (graph->GetArena()) HNotEqual(graph->GetNullConstant(), object);
@@ -867,9 +876,7 @@ void InstructionSimplifierVisitor::VisitTypeConversion(HTypeConversion* instruct
return;
}
}
- } else if (input->IsAnd() &&
- Primitive::IsIntegralType(result_type) &&
- input->HasOnlyOneNonEnvironmentUse()) {
+ } else if (input->IsAnd() && Primitive::IsIntegralType(result_type)) {
DCHECK(Primitive::IsIntegralType(input_type));
HAnd* input_and = input->AsAnd();
HConstant* constant = input_and->GetConstantRight();
@@ -879,10 +886,18 @@ void InstructionSimplifierVisitor::VisitTypeConversion(HTypeConversion* instruct
size_t trailing_ones = CTZ(~static_cast<uint64_t>(value));
if (trailing_ones >= kBitsPerByte * Primitive::ComponentSize(result_type)) {
// The `HAnd` is useless, for example in `(byte) (x & 0xff)`, get rid of it.
- input_and->ReplaceWith(input_and->GetLeastConstantLeft());
- input_and->GetBlock()->RemoveInstruction(input_and);
- RecordSimplification();
- return;
+ HInstruction* original_input = input_and->GetLeastConstantLeft();
+ if (IsTypeConversionImplicit(original_input->GetType(), result_type)) {
+ instruction->ReplaceWith(original_input);
+ instruction->GetBlock()->RemoveInstruction(instruction);
+ RecordSimplification();
+ return;
+ } else if (input->HasOnlyOneNonEnvironmentUse()) {
+ input_and->ReplaceWith(original_input);
+ input_and->GetBlock()->RemoveInstruction(input_and);
+ RecordSimplification();
+ return;
+ }
}
}
}
@@ -1604,12 +1619,13 @@ void InstructionSimplifierVisitor::SimplifySystemArrayCopy(HInvoke* instruction)
}
}
-void InstructionSimplifierVisitor::SimplifyCompare(HInvoke* invoke, bool is_signum) {
+void InstructionSimplifierVisitor::SimplifyCompare(HInvoke* invoke,
+ bool is_signum,
+ Primitive::Type type) {
DCHECK(invoke->IsInvokeStaticOrDirect());
uint32_t dex_pc = invoke->GetDexPc();
HInstruction* left = invoke->InputAt(0);
HInstruction* right;
- Primitive::Type type = left->GetType();
if (!is_signum) {
right = invoke->InputAt(1);
} else if (type == Primitive::kPrimLong) {
@@ -1686,12 +1702,16 @@ void InstructionSimplifierVisitor::VisitInvoke(HInvoke* instruction) {
SimplifyRotate(instruction, true);
break;
case Intrinsics::kIntegerCompare:
+ SimplifyCompare(instruction, /* is_signum */ false, Primitive::kPrimInt);
+ break;
case Intrinsics::kLongCompare:
- SimplifyCompare(instruction, /* is_signum */ false);
+ SimplifyCompare(instruction, /* is_signum */ false, Primitive::kPrimLong);
break;
case Intrinsics::kIntegerSignum:
+ SimplifyCompare(instruction, /* is_signum */ true, Primitive::kPrimInt);
+ break;
case Intrinsics::kLongSignum:
- SimplifyCompare(instruction, /* is_signum */ true);
+ SimplifyCompare(instruction, /* is_signum */ true, Primitive::kPrimLong);
break;
case Intrinsics::kFloatIsNaN:
case Intrinsics::kDoubleIsNaN:
diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h
index 1bb5f5df51..e2a54f42ce 100644
--- a/compiler/optimizing/nodes.h
+++ b/compiler/optimizing/nodes.h
@@ -2543,7 +2543,7 @@ class HFloatConstant : public HConstant {
return bit_cast<uint32_t, float>(value_) == bit_cast<uint32_t, float>((-1.0f));
}
bool IsZero() const OVERRIDE {
- return value_ == 0.0f;
+ return bit_cast<uint32_t, float>(value_) == bit_cast<uint32_t, float>(0.0f);
}
bool IsOne() const OVERRIDE {
return bit_cast<uint32_t, float>(value_) == bit_cast<uint32_t, float>(1.0f);
@@ -2585,7 +2585,7 @@ class HDoubleConstant : public HConstant {
return bit_cast<uint64_t, double>(value_) == bit_cast<uint64_t, double>((-1.0));
}
bool IsZero() const OVERRIDE {
- return value_ == 0.0;
+ return bit_cast<uint64_t, double>(value_) == bit_cast<uint64_t, double>((0.0));
}
bool IsOne() const OVERRIDE {
return bit_cast<uint64_t, double>(value_) == bit_cast<uint64_t, double>(1.0);
@@ -3428,7 +3428,10 @@ class HAboveOrEqual : public HCondition {
// Result is 0 if input0 == input1, 1 if input0 > input1, or -1 if input0 < input1.
class HCompare : public HBinaryOperation {
public:
- HCompare(Primitive::Type type,
+ // Note that `comparison_type` is the type of comparison performed
+ // between the comparison's inputs, not the type of the instantiated
+ // HCompare instruction (which is always Primitive::kPrimInt).
+ HCompare(Primitive::Type comparison_type,
HInstruction* first,
HInstruction* second,
ComparisonBias bias,
@@ -3436,11 +3439,13 @@ class HCompare : public HBinaryOperation {
: HBinaryOperation(Primitive::kPrimInt,
first,
second,
- SideEffectsForArchRuntimeCalls(type),
+ SideEffectsForArchRuntimeCalls(comparison_type),
dex_pc) {
SetPackedField<ComparisonBiasField>(bias);
- DCHECK_EQ(type, first->GetType());
- DCHECK_EQ(type, second->GetType());
+ if (kIsDebugBuild) {
+ DCHECK_EQ(comparison_type, Primitive::PrimitiveKind(first->GetType()));
+ DCHECK_EQ(comparison_type, Primitive::PrimitiveKind(second->GetType()));
+ }
}
template <typename T>
@@ -3485,9 +3490,9 @@ class HCompare : public HBinaryOperation {
return GetBias() == ComparisonBias::kGtBias;
}
- static SideEffects SideEffectsForArchRuntimeCalls(Primitive::Type type) {
- // MIPS64 uses a runtime call for FP comparisons.
- return Primitive::IsFloatingPointType(type) ? SideEffects::CanTriggerGC() : SideEffects::None();
+ static SideEffects SideEffectsForArchRuntimeCalls(Primitive::Type type ATTRIBUTE_UNUSED) {
+ // Comparisons do not require a runtime call in any back end.
+ return SideEffects::None();
}
DECLARE_INSTRUCTION(Compare);
diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc
index 7a82063bba..4d2469ca15 100644
--- a/compiler/optimizing/optimizing_compiler.cc
+++ b/compiler/optimizing/optimizing_compiler.cc
@@ -63,6 +63,7 @@
#include "instruction_simplifier_arm.h"
#include "intrinsics.h"
#include "jit/debugger_interface.h"
+#include "jit/jit.h"
#include "jit/jit_code_cache.h"
#include "jni/quick/jni_compiler.h"
#include "licm.h"
@@ -697,7 +698,8 @@ CodeGenerator* OptimizingCompiler::TryCompile(ArenaAllocator* arena,
CodeGenerator::Create(graph,
instruction_set,
*compiler_driver->GetInstructionSetFeatures(),
- compiler_driver->GetCompilerOptions()));
+ compiler_driver->GetCompilerOptions(),
+ compilation_stats_.get()));
if (codegen.get() == nullptr) {
MaybeRecordStat(MethodCompilationStat::kNotCompiledNoCodegen);
return nullptr;
@@ -891,7 +893,7 @@ bool OptimizingCompiler::JitCompile(Thread* self,
}
size_t stack_map_size = codegen->ComputeStackMapsSize();
- uint8_t* stack_map_data = code_cache->ReserveData(self, stack_map_size);
+ uint8_t* stack_map_data = code_cache->ReserveData(self, stack_map_size, method);
if (stack_map_data == nullptr) {
return false;
}
@@ -945,6 +947,8 @@ bool OptimizingCompiler::JitCompile(Thread* self,
elf_file.size());
}
+ Runtime::Current()->GetJit()->AddMemoryUsage(method, arena.BytesUsed());
+
return true;
}
diff --git a/compiler/optimizing/optimizing_compiler_stats.h b/compiler/optimizing/optimizing_compiler_stats.h
index 179004bd40..3717926a97 100644
--- a/compiler/optimizing/optimizing_compiler_stats.h
+++ b/compiler/optimizing/optimizing_compiler_stats.h
@@ -60,6 +60,10 @@ enum MethodCompilationStat {
kIntrinsicRecognized,
kLoopInvariantMoved,
kSelectGenerated,
+ kRemovedInstanceOf,
+ kInlinedInvokeVirtualOrInterface,
+ kImplicitNullCheckGenerated,
+ kExplicitNullCheckGenerated,
kLastStat
};
@@ -133,6 +137,10 @@ class OptimizingCompilerStats {
case kIntrinsicRecognized : name = "IntrinsicRecognized"; break;
case kLoopInvariantMoved : name = "LoopInvariantMoved"; break;
case kSelectGenerated : name = "SelectGenerated"; break;
+ case kRemovedInstanceOf: name = "RemovedInstanceOf"; break;
+ case kInlinedInvokeVirtualOrInterface: name = "InlinedInvokeVirtualOrInterface"; break;
+ case kImplicitNullCheckGenerated: name = "ImplicitNullCheckGenerated"; break;
+ case kExplicitNullCheckGenerated: name = "ExplicitNullCheckGenerated"; break;
case kLastStat:
LOG(FATAL) << "invalid stat "
diff --git a/oatdump/oatdump.cc b/oatdump/oatdump.cc
index 79fde5e366..9ac18e830e 100644
--- a/oatdump/oatdump.cc
+++ b/oatdump/oatdump.cc
@@ -291,6 +291,8 @@ class OatDumperOptions {
bool list_methods,
bool dump_header_only,
const char* export_dex_location,
+ const char* app_image,
+ const char* app_oat,
uint32_t addr2instr)
: dump_raw_mapping_table_(dump_raw_mapping_table),
dump_raw_gc_map_(dump_raw_gc_map),
@@ -304,6 +306,8 @@ class OatDumperOptions {
list_methods_(list_methods),
dump_header_only_(dump_header_only),
export_dex_location_(export_dex_location),
+ app_image_(app_image),
+ app_oat_(app_oat),
addr2instr_(addr2instr),
class_loader_(nullptr) {}
@@ -319,6 +323,8 @@ class OatDumperOptions {
const bool list_methods_;
const bool dump_header_only_;
const char* const export_dex_location_;
+ const char* const app_image_;
+ const char* const app_oat_;
uint32_t addr2instr_;
Handle<mirror::ClassLoader>* class_loader_;
};
@@ -1444,8 +1450,10 @@ class OatDumper {
class ImageDumper {
public:
- ImageDumper(std::ostream* os, gc::space::ImageSpace& image_space,
- const ImageHeader& image_header, OatDumperOptions* oat_dumper_options)
+ ImageDumper(std::ostream* os,
+ gc::space::ImageSpace& image_space,
+ const ImageHeader& image_header,
+ OatDumperOptions* oat_dumper_options)
: os_(os),
vios_(os),
indent1_(&vios_),
@@ -1543,16 +1551,23 @@ class ImageDumper {
os << "OAT LOCATION: " << oat_location;
os << "\n";
std::string error_msg;
- const OatFile* oat_file = runtime->GetOatFileManager().FindOpenedOatFileFromOatLocation(
- oat_location);
+ const OatFile* oat_file = image_space_.GetOatFile();
if (oat_file == nullptr) {
- oat_file = OatFile::Open(oat_location, oat_location,
- nullptr, nullptr, false, nullptr,
+ oat_file = runtime->GetOatFileManager().FindOpenedOatFileFromOatLocation(oat_location);
+ }
+ if (oat_file == nullptr) {
+ oat_file = OatFile::Open(oat_location,
+ oat_location,
+ nullptr,
+ nullptr,
+ false,
+ /*low_4gb*/false,
+ nullptr,
&error_msg);
- if (oat_file == nullptr) {
- os << "NOT FOUND: " << error_msg << "\n";
- return false;
- }
+ }
+ if (oat_file == nullptr) {
+ os << "OAT FILE NOT FOUND: " << error_msg << "\n";
+ return EXIT_FAILURE;
}
os << "\n";
@@ -1603,21 +1618,27 @@ class ImageDumper {
// TODO: Dump fields.
// Dump methods after.
const auto& methods_section = image_header_.GetMethodsSection();
- const size_t pointer_size =
- InstructionSetPointerSize(oat_dumper_->GetOatInstructionSet());
DumpArtMethodVisitor visitor(this);
- methods_section.VisitPackedArtMethods(&visitor, image_space_.Begin(), pointer_size);
+ methods_section.VisitPackedArtMethods(&visitor,
+ image_space_.Begin(),
+ image_header_.GetPointerSize());
// Dump the large objects separately.
heap->GetLargeObjectsSpace()->GetLiveBitmap()->Walk(ImageDumper::Callback, this);
indent_os << "\n";
}
os << "STATS:\n" << std::flush;
std::unique_ptr<File> file(OS::OpenFileForReading(image_filename.c_str()));
- if (file.get() == nullptr) {
+ size_t data_size = image_header_.GetDataSize(); // stored size in file.
+ if (file == nullptr) {
LOG(WARNING) << "Failed to find image in " << image_filename;
- }
- if (file.get() != nullptr) {
+ } else {
stats_.file_bytes = file->GetLength();
+ // If the image is compressed, adjust to decompressed size.
+ size_t uncompressed_size = image_header_.GetImageSize() - sizeof(ImageHeader);
+ if (image_header_.GetStorageMode() == ImageHeader::kStorageModeUncompressed) {
+ DCHECK_EQ(uncompressed_size, data_size) << "Sizes should match for uncompressed image";
+ }
+ stats_.file_bytes += uncompressed_size - data_size;
}
size_t header_bytes = sizeof(ImageHeader);
const auto& object_section = image_header_.GetImageSection(ImageHeader::kSectionObjects);
@@ -1664,10 +1685,10 @@ class ImageDumper {
uint32_t end_intern = intern_section.Offset() + intern_section.Size();
stats_.alignment_bytes += class_table_section.Offset() - end_intern;
- // Add space between class table and bitmap. Expect the bitmap to be page-aligned.
- uint32_t end_ctable = class_table_section.Offset() + class_table_section.Size();
+ // Add space between end of image data and bitmap. Expect the bitmap to be page-aligned.
+ const size_t bitmap_offset = sizeof(ImageHeader) + data_size;
CHECK_ALIGNED(bitmap_section.Offset(), kPageSize);
- stats_.alignment_bytes += bitmap_section.Offset() - end_ctable;
+ stats_.alignment_bytes += RoundUp(bitmap_offset, kPageSize) - bitmap_offset;
stats_.bitmap_bytes += bitmap_section.Size();
stats_.art_field_bytes += field_section.Size();
@@ -1691,7 +1712,7 @@ class ImageDumper {
virtual void Visit(ArtMethod* method) OVERRIDE SHARED_REQUIRES(Locks::mutator_lock_) {
std::ostream& indent_os = image_dumper_->vios_.Stream();
indent_os << method << " " << " ArtMethod: " << PrettyMethod(method) << "\n";
- image_dumper_->DumpMethod(method, image_dumper_, indent_os);
+ image_dumper_->DumpMethod(method, indent_os);
indent_os << "\n";
}
@@ -1784,10 +1805,9 @@ class ImageDumper {
return image_space_.Contains(object);
}
- const void* GetQuickOatCodeBegin(ArtMethod* m)
- SHARED_REQUIRES(Locks::mutator_lock_) {
+ const void* GetQuickOatCodeBegin(ArtMethod* m) SHARED_REQUIRES(Locks::mutator_lock_) {
const void* quick_code = m->GetEntryPointFromQuickCompiledCodePtrSize(
- InstructionSetPointerSize(oat_dumper_->GetOatInstructionSet()));
+ image_header_.GetPointerSize());
if (Runtime::Current()->GetClassLinker()->IsQuickResolutionStub(quick_code)) {
quick_code = oat_dumper_->GetQuickOatCode(m);
}
@@ -1846,8 +1866,7 @@ class ImageDumper {
}
ScopedIndentation indent1(&state->vios_);
DumpFields(os, obj, obj_class);
- const auto image_pointer_size =
- InstructionSetPointerSize(state->oat_dumper_->GetOatInstructionSet());
+ const size_t image_pointer_size = state->image_header_.GetPointerSize();
if (obj->IsObjectArray()) {
auto* obj_array = obj->AsObjectArray<mirror::Object>();
for (int32_t i = 0, length = obj_array->GetLength(); i < length; i++) {
@@ -1892,7 +1911,9 @@ class ImageDumper {
ScopedIndentation indent2(&state->vios_);
auto* resolved_methods = dex_cache->GetResolvedMethods();
for (size_t i = 0, length = dex_cache->NumResolvedMethods(); i < length; ++i) {
- auto* elem = mirror::DexCache::GetElementPtrSize(resolved_methods, i, image_pointer_size);
+ auto* elem = mirror::DexCache::GetElementPtrSize(resolved_methods,
+ i,
+ image_pointer_size);
size_t run = 0;
for (size_t j = i + 1;
j != length && elem == mirror::DexCache::GetElementPtrSize(resolved_methods,
@@ -1954,13 +1975,11 @@ class ImageDumper {
state->stats_.Update(obj_class->GetDescriptor(&temp), object_bytes);
}
- void DumpMethod(ArtMethod* method, ImageDumper* state, std::ostream& indent_os)
+ void DumpMethod(ArtMethod* method, std::ostream& indent_os)
SHARED_REQUIRES(Locks::mutator_lock_) {
DCHECK(method != nullptr);
- const auto image_pointer_size =
- InstructionSetPointerSize(state->oat_dumper_->GetOatInstructionSet());
- const void* quick_oat_code_begin = state->GetQuickOatCodeBegin(method);
- const void* quick_oat_code_end = state->GetQuickOatCodeEnd(method);
+ const void* quick_oat_code_begin = GetQuickOatCodeBegin(method);
+ const void* quick_oat_code_end = GetQuickOatCodeEnd(method);
OatQuickMethodHeader* method_header = reinterpret_cast<OatQuickMethodHeader*>(
reinterpret_cast<uintptr_t>(quick_oat_code_begin) - sizeof(OatQuickMethodHeader));
if (method->IsNative()) {
@@ -1969,13 +1988,13 @@ class ImageDumper {
DCHECK(method_header->GetMappingTable() == nullptr) << PrettyMethod(method);
}
bool first_occurrence;
- uint32_t quick_oat_code_size = state->GetQuickOatCodeSize(method);
- state->ComputeOatSize(quick_oat_code_begin, &first_occurrence);
+ uint32_t quick_oat_code_size = GetQuickOatCodeSize(method);
+ ComputeOatSize(quick_oat_code_begin, &first_occurrence);
if (first_occurrence) {
- state->stats_.native_to_managed_code_bytes += quick_oat_code_size;
+ stats_.native_to_managed_code_bytes += quick_oat_code_size;
}
- if (quick_oat_code_begin !=
- method->GetEntryPointFromQuickCompiledCodePtrSize(image_pointer_size)) {
+ if (quick_oat_code_begin != method->GetEntryPointFromQuickCompiledCodePtrSize(
+ image_header_.GetPointerSize())) {
indent_os << StringPrintf("OAT CODE: %p\n", quick_oat_code_begin);
}
} else if (method->IsAbstract() || method->IsCalleeSaveMethod() ||
@@ -1984,46 +2003,44 @@ class ImageDumper {
} else {
const DexFile::CodeItem* code_item = method->GetCodeItem();
size_t dex_instruction_bytes = code_item->insns_size_in_code_units_ * 2;
- state->stats_.dex_instruction_bytes += dex_instruction_bytes;
+ stats_.dex_instruction_bytes += dex_instruction_bytes;
bool first_occurrence;
- size_t gc_map_bytes = state->ComputeOatSize(
- method_header->GetNativeGcMap(), &first_occurrence);
+ size_t gc_map_bytes = ComputeOatSize(method_header->GetNativeGcMap(), &first_occurrence);
if (first_occurrence) {
- state->stats_.gc_map_bytes += gc_map_bytes;
+ stats_.gc_map_bytes += gc_map_bytes;
}
- size_t pc_mapping_table_bytes = state->ComputeOatSize(
+ size_t pc_mapping_table_bytes = ComputeOatSize(
method_header->GetMappingTable(), &first_occurrence);
if (first_occurrence) {
- state->stats_.pc_mapping_table_bytes += pc_mapping_table_bytes;
+ stats_.pc_mapping_table_bytes += pc_mapping_table_bytes;
}
size_t vmap_table_bytes = 0u;
if (!method_header->IsOptimized()) {
// Method compiled with the optimizing compiler have no vmap table.
- vmap_table_bytes = state->ComputeOatSize(
- method_header->GetVmapTable(), &first_occurrence);
+ vmap_table_bytes = ComputeOatSize(method_header->GetVmapTable(), &first_occurrence);
if (first_occurrence) {
- state->stats_.vmap_table_bytes += vmap_table_bytes;
+ stats_.vmap_table_bytes += vmap_table_bytes;
}
}
- uint32_t quick_oat_code_size = state->GetQuickOatCodeSize(method);
- state->ComputeOatSize(quick_oat_code_begin, &first_occurrence);
+ uint32_t quick_oat_code_size = GetQuickOatCodeSize(method);
+ ComputeOatSize(quick_oat_code_begin, &first_occurrence);
if (first_occurrence) {
- state->stats_.managed_code_bytes += quick_oat_code_size;
+ stats_.managed_code_bytes += quick_oat_code_size;
if (method->IsConstructor()) {
if (method->IsStatic()) {
- state->stats_.class_initializer_code_bytes += quick_oat_code_size;
+ stats_.class_initializer_code_bytes += quick_oat_code_size;
} else if (dex_instruction_bytes > kLargeConstructorDexBytes) {
- state->stats_.large_initializer_code_bytes += quick_oat_code_size;
+ stats_.large_initializer_code_bytes += quick_oat_code_size;
}
} else if (dex_instruction_bytes > kLargeMethodDexBytes) {
- state->stats_.large_method_code_bytes += quick_oat_code_size;
+ stats_.large_method_code_bytes += quick_oat_code_size;
}
}
- state->stats_.managed_code_bytes_ignoring_deduplication += quick_oat_code_size;
+ stats_.managed_code_bytes_ignoring_deduplication += quick_oat_code_size;
uint32_t method_access_flags = method->GetAccessFlags();
@@ -2033,11 +2050,11 @@ class ImageDumper {
method_access_flags);
size_t total_size = dex_instruction_bytes + gc_map_bytes + pc_mapping_table_bytes +
- vmap_table_bytes + quick_oat_code_size + ArtMethod::Size(image_pointer_size);
+ vmap_table_bytes + quick_oat_code_size + ArtMethod::Size(image_header_.GetPointerSize());
double expansion =
static_cast<double>(quick_oat_code_size) / static_cast<double>(dex_instruction_bytes);
- state->stats_.ComputeOutliers(total_size, expansion, method);
+ stats_.ComputeOutliers(total_size, expansion, method);
}
}
@@ -2372,26 +2389,75 @@ class ImageDumper {
DISALLOW_COPY_AND_ASSIGN(ImageDumper);
};
-static int DumpImage(Runtime* runtime, OatDumperOptions* options, std::ostream* os) {
+static int DumpImage(gc::space::ImageSpace* image_space,
+ OatDumperOptions* options,
+ std::ostream* os) SHARED_REQUIRES(Locks::mutator_lock_) {
+ const ImageHeader& image_header = image_space->GetImageHeader();
+ if (!image_header.IsValid()) {
+ fprintf(stderr, "Invalid image header %s\n", image_space->GetImageLocation().c_str());
+ return EXIT_FAILURE;
+ }
+ ImageDumper image_dumper(os, *image_space, image_header, options);
+ if (!image_dumper.Dump()) {
+ return EXIT_FAILURE;
+ }
+ return EXIT_SUCCESS;
+}
+
+static int DumpImages(Runtime* runtime, OatDumperOptions* options, std::ostream* os) {
// Dumping the image, no explicit class loader.
ScopedNullHandle<mirror::ClassLoader> null_class_loader;
options->class_loader_ = &null_class_loader;
ScopedObjectAccess soa(Thread::Current());
- gc::Heap* heap = runtime->GetHeap();
- std::vector<gc::space::ImageSpace*> image_spaces = heap->GetBootImageSpaces();
- CHECK(!image_spaces.empty());
- for (gc::space::ImageSpace* image_space : image_spaces) {
- const ImageHeader& image_header = image_space->GetImageHeader();
- if (!image_header.IsValid()) {
- fprintf(stderr, "Invalid image header %s\n", image_space->GetImageLocation().c_str());
+ if (options->app_image_ != nullptr) {
+ if (options->app_oat_ == nullptr) {
+ LOG(ERROR) << "Can not dump app image without app oat file";
return EXIT_FAILURE;
}
-
- ImageDumper image_dumper(os, *image_space, image_header, options);
- if (!image_dumper.Dump()) {
+ // We can't know if the app image is 32 bits yet, but it contains pointers into the oat file.
+ // We need to map the oat file in the low 4gb or else the fixup wont be able to fit oat file
+ // pointers into 32 bit pointer sized ArtMethods.
+ std::string error_msg;
+ std::unique_ptr<OatFile> oat_file(OatFile::Open(options->app_oat_,
+ options->app_oat_,
+ nullptr,
+ nullptr,
+ false,
+ /*low_4gb*/true,
+ nullptr,
+ &error_msg));
+ if (oat_file == nullptr) {
+ LOG(ERROR) << "Failed to open oat file " << options->app_oat_ << " with error " << error_msg;
return EXIT_FAILURE;
}
+ std::unique_ptr<gc::space::ImageSpace> space(
+ gc::space::ImageSpace::CreateFromAppImage(options->app_image_, oat_file.get(), &error_msg));
+ if (space == nullptr) {
+ LOG(ERROR) << "Failed to open app image " << options->app_image_ << " with error "
+ << error_msg;
+ }
+ // Open dex files for the image.
+ std::vector<std::unique_ptr<const DexFile>> dex_files;
+ if (!runtime->GetClassLinker()->OpenImageDexFiles(space.get(), &dex_files, &error_msg)) {
+ LOG(ERROR) << "Failed to open app image dex files " << options->app_image_ << " with error "
+ << error_msg;
+ }
+ // Dump the actual image.
+ int result = DumpImage(space.get(), options, os);
+ if (result != EXIT_SUCCESS) {
+ return result;
+ }
+ // Fall through to dump the boot images.
+ }
+
+ gc::Heap* heap = runtime->GetHeap();
+ CHECK(heap->HasBootImageSpace()) << "No image spaces";
+ for (gc::space::ImageSpace* image_space : heap->GetBootImageSpaces()) {
+ int result = DumpImage(image_space, options, os);
+ if (result != EXIT_SUCCESS) {
+ return result;
+ }
}
return EXIT_SUCCESS;
}
@@ -2447,8 +2513,14 @@ static int DumpOatWithoutRuntime(OatFile* oat_file, OatDumperOptions* options, s
static int DumpOat(Runtime* runtime, const char* oat_filename, OatDumperOptions* options,
std::ostream* os) {
std::string error_msg;
- OatFile* oat_file = OatFile::Open(oat_filename, oat_filename, nullptr, nullptr, false,
- nullptr, &error_msg);
+ OatFile* oat_file = OatFile::Open(oat_filename,
+ oat_filename,
+ nullptr,
+ nullptr,
+ false,
+ /*low_4gb*/false,
+ nullptr,
+ &error_msg);
if (oat_file == nullptr) {
fprintf(stderr, "Failed to open oat file from '%s': %s\n", oat_filename, error_msg.c_str());
return EXIT_FAILURE;
@@ -2463,8 +2535,14 @@ static int DumpOat(Runtime* runtime, const char* oat_filename, OatDumperOptions*
static int SymbolizeOat(const char* oat_filename, std::string& output_name, bool no_bits) {
std::string error_msg;
- OatFile* oat_file = OatFile::Open(oat_filename, oat_filename, nullptr, nullptr, false,
- nullptr, &error_msg);
+ OatFile* oat_file = OatFile::Open(oat_filename,
+ oat_filename,
+ nullptr,
+ nullptr,
+ false,
+ /*low_4gb*/false,
+ nullptr,
+ &error_msg);
if (oat_file == nullptr) {
fprintf(stderr, "Failed to open oat file from '%s': %s\n", oat_filename, error_msg.c_str());
return EXIT_FAILURE;
@@ -2537,6 +2615,10 @@ struct OatdumpArgs : public CmdlineArgs {
*error_msg = "Address conversion failed";
return kParseError;
}
+ } else if (option.starts_with("--app-image=")) {
+ app_image_ = option.substr(strlen("--app-image=")).data();
+ } else if (option.starts_with("--app-oat=")) {
+ app_oat_ = option.substr(strlen("--app-oat=")).data();
} else {
return kParseUnknownArgument;
}
@@ -2582,6 +2664,13 @@ struct OatdumpArgs : public CmdlineArgs {
"\n"
" --image=<file.art>: specifies an input image location.\n"
" Example: --image=/system/framework/boot.art\n"
+ "\n"
+ " --app-image=<file.art>: specifies an input app image. Must also have a specified\n"
+ " boot image and app oat file.\n"
+ " Example: --app-image=app.art\n"
+ "\n"
+ " --app-oat=<file.odex>: specifies an input app oat.\n"
+ " Example: --app-oat=app.odex\n"
"\n";
usage += Base::GetUsage();
@@ -2655,6 +2744,8 @@ struct OatdumpArgs : public CmdlineArgs {
bool dump_header_only_ = false;
uint32_t addr2instr_ = 0;
const char* export_dex_location_ = nullptr;
+ const char* app_image_ = nullptr;
+ const char* app_oat_ = nullptr;
};
struct OatdumpMain : public CmdlineMain<OatdumpArgs> {
@@ -2664,7 +2755,7 @@ struct OatdumpMain : public CmdlineMain<OatdumpArgs> {
// If we are only doing the oat file, disable absolute_addresses. Keep them for image dumping.
bool absolute_addresses = (args_->oat_filename_ == nullptr);
- oat_dumper_options_ = std::unique_ptr<OatDumperOptions>(new OatDumperOptions(
+ oat_dumper_options_.reset(new OatDumperOptions(
args_->dump_raw_mapping_table_,
args_->dump_raw_gc_map_,
args_->dump_vmap_,
@@ -2677,6 +2768,8 @@ struct OatdumpMain : public CmdlineMain<OatdumpArgs> {
args_->list_methods_,
args_->dump_header_only_,
args_->export_dex_location_,
+ args_->app_image_,
+ args_->app_oat_,
args_->addr2instr_));
return (args_->boot_image_location_ != nullptr || args_->image_location_ != nullptr) &&
@@ -2714,7 +2807,7 @@ struct OatdumpMain : public CmdlineMain<OatdumpArgs> {
args_->os_) == EXIT_SUCCESS;
}
- return DumpImage(runtime, oat_dumper_options_.get(), args_->os_) == EXIT_SUCCESS;
+ return DumpImages(runtime, oat_dumper_options_.get(), args_->os_) == EXIT_SUCCESS;
}
std::unique_ptr<OatDumperOptions> oat_dumper_options_;
diff --git a/runtime/Android.mk b/runtime/Android.mk
index 84660a3195..f70e6964c1 100644
--- a/runtime/Android.mk
+++ b/runtime/Android.mk
@@ -140,7 +140,6 @@ LIBART_COMMON_SRC_FILES := \
native/java_lang_Class.cc \
native/java_lang_DexCache.cc \
native/java_lang_Object.cc \
- native/java_lang_Runtime.cc \
native/java_lang_String.cc \
native/java_lang_StringFactory.cc \
native/java_lang_System.cc \
diff --git a/runtime/art_method-inl.h b/runtime/art_method-inl.h
index ebe89bbbd2..8541210791 100644
--- a/runtime/art_method-inl.h
+++ b/runtime/art_method-inl.h
@@ -473,39 +473,40 @@ void ArtMethod::VisitRoots(RootVisitorType& visitor, size_t pointer_size) {
}
template <typename Visitor>
-inline void ArtMethod::UpdateObjectsForImageRelocation(const Visitor& visitor) {
+inline void ArtMethod::UpdateObjectsForImageRelocation(const Visitor& visitor,
+ size_t pointer_size) {
mirror::Class* old_class = GetDeclaringClassUnchecked<kWithoutReadBarrier>();
mirror::Class* new_class = visitor(old_class);
if (old_class != new_class) {
SetDeclaringClass(new_class);
}
- ArtMethod** old_methods = GetDexCacheResolvedMethods(sizeof(void*));
+ ArtMethod** old_methods = GetDexCacheResolvedMethods(pointer_size);
ArtMethod** new_methods = visitor(old_methods);
if (old_methods != new_methods) {
- SetDexCacheResolvedMethods(new_methods, sizeof(void*));
+ SetDexCacheResolvedMethods(new_methods, pointer_size);
}
- GcRoot<mirror::Class>* old_types = GetDexCacheResolvedTypes(sizeof(void*));
+ GcRoot<mirror::Class>* old_types = GetDexCacheResolvedTypes(pointer_size);
GcRoot<mirror::Class>* new_types = visitor(old_types);
if (old_types != new_types) {
- SetDexCacheResolvedTypes(new_types, sizeof(void*));
+ SetDexCacheResolvedTypes(new_types, pointer_size);
}
}
template <ReadBarrierOption kReadBarrierOption, typename Visitor>
-inline void ArtMethod::UpdateEntrypoints(const Visitor& visitor) {
+inline void ArtMethod::UpdateEntrypoints(const Visitor& visitor, size_t pointer_size) {
if (IsNative<kReadBarrierOption>()) {
- const void* old_native_code = GetEntryPointFromJni();
+ const void* old_native_code = GetEntryPointFromJniPtrSize(pointer_size);
const void* new_native_code = visitor(old_native_code);
if (old_native_code != new_native_code) {
- SetEntryPointFromJni(new_native_code);
+ SetEntryPointFromJniPtrSize(new_native_code, pointer_size);
}
} else {
- DCHECK(GetEntryPointFromJni() == nullptr);
+ DCHECK(GetEntryPointFromJniPtrSize(pointer_size) == nullptr);
}
- const void* old_code = GetEntryPointFromQuickCompiledCode();
+ const void* old_code = GetEntryPointFromQuickCompiledCodePtrSize(pointer_size);
const void* new_code = visitor(old_code);
if (old_code != new_code) {
- SetEntryPointFromQuickCompiledCode(new_code);
+ SetEntryPointFromQuickCompiledCodePtrSize(new_code, pointer_size);
}
}
diff --git a/runtime/art_method.h b/runtime/art_method.h
index ec00a7b900..5ca362ce2c 100644
--- a/runtime/art_method.h
+++ b/runtime/art_method.h
@@ -490,12 +490,12 @@ class ArtMethod FINAL {
// Update heap objects and non-entrypoint pointers by the passed in visitor for image relocation.
// Does not use read barrier.
template <typename Visitor>
- ALWAYS_INLINE void UpdateObjectsForImageRelocation(const Visitor& visitor)
+ ALWAYS_INLINE void UpdateObjectsForImageRelocation(const Visitor& visitor, size_t pointer_size)
SHARED_REQUIRES(Locks::mutator_lock_);
// Update entry points by passing them through the visitor.
template <ReadBarrierOption kReadBarrierOption = kWithReadBarrier, typename Visitor>
- ALWAYS_INLINE void UpdateEntrypoints(const Visitor& visitor);
+ ALWAYS_INLINE void UpdateEntrypoints(const Visitor& visitor, size_t pointer_size);
protected:
// Field order required by test "ValidateFieldOrderOfJavaCppUnionClasses".
diff --git a/runtime/base/histogram-inl.h b/runtime/base/histogram-inl.h
index 03980e3273..c7a0ba2dfd 100644
--- a/runtime/base/histogram-inl.h
+++ b/runtime/base/histogram-inl.h
@@ -26,6 +26,7 @@
#include "base/bit_utils.h"
#include "base/time_utils.h"
+#include "utils.h"
namespace art {
@@ -200,6 +201,13 @@ inline void Histogram<Value>::PrintConfidenceIntervals(std::ostream &os, double
}
template <class Value>
+inline void Histogram<Value>::PrintMemoryUse(std::ostream &os) const {
+ os << Name()
+ << ": Avg: " << PrettySize(Mean()) << " Max: "
+ << PrettySize(Max()) << " Min: " << PrettySize(Min()) << "\n";
+}
+
+template <class Value>
inline void Histogram<Value>::CreateHistogram(CumulativeData* out_data) const {
DCHECK_GT(sample_size_, 0ull);
out_data->freq_.clear();
diff --git a/runtime/base/histogram.h b/runtime/base/histogram.h
index ef3a5d78ca..bcb7b3b769 100644
--- a/runtime/base/histogram.h
+++ b/runtime/base/histogram.h
@@ -59,6 +59,7 @@ template <class Value> class Histogram {
double Percentile(double per, const CumulativeData& data) const;
void PrintConfidenceIntervals(std::ostream& os, double interval,
const CumulativeData& data) const;
+ void PrintMemoryUse(std::ostream& os) const;
void PrintBins(std::ostream& os, const CumulativeData& data) const;
void DumpBins(std::ostream& os) const;
Value GetRange(size_t bucket_idx) const;
diff --git a/runtime/base/length_prefixed_array.h b/runtime/base/length_prefixed_array.h
index d6328717e6..8060263863 100644
--- a/runtime/base/length_prefixed_array.h
+++ b/runtime/base/length_prefixed_array.h
@@ -18,6 +18,7 @@
#define ART_RUNTIME_BASE_LENGTH_PREFIXED_ARRAY_H_
#include <stddef.h> // for offsetof()
+#include <string.h> // for memset()
#include "stride_iterator.h"
#include "base/bit_utils.h"
@@ -84,6 +85,13 @@ class LengthPrefixedArray {
size_ = dchecked_integral_cast<uint32_t>(length);
}
+ // Clear the potentially uninitialized padding between the size_ and actual data.
+ void ClearPadding(size_t element_size = sizeof(T), size_t alignment = alignof(T)) {
+ size_t gap_offset = offsetof(LengthPrefixedArray<T>, data);
+ size_t gap_size = OffsetOfElement(0, element_size, alignment) - gap_offset;
+ memset(reinterpret_cast<uint8_t*>(this) + gap_offset, 0, gap_size);
+ }
+
private:
T& AtUnchecked(size_t index, size_t element_size, size_t alignment) {
return *reinterpret_cast<T*>(
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index d51a1f7ecc..3c69323b20 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -1462,6 +1462,64 @@ class UpdateClassLoaderAndResolvedStringsVisitor {
const bool forward_strings_;
};
+static std::unique_ptr<const DexFile> OpenOatDexFile(const OatFile* oat_file,
+ const char* location,
+ std::string* error_msg)
+ SHARED_REQUIRES(Locks::mutator_lock_) {
+ DCHECK(error_msg != nullptr);
+ std::unique_ptr<const DexFile> dex_file;
+ const OatFile::OatDexFile* oat_dex_file = oat_file->GetOatDexFile(location, nullptr);
+ if (oat_dex_file == nullptr) {
+ *error_msg = StringPrintf("Failed finding oat dex file for %s %s",
+ oat_file->GetLocation().c_str(),
+ location);
+ return std::unique_ptr<const DexFile>();
+ }
+ std::string inner_error_msg;
+ dex_file = oat_dex_file->OpenDexFile(&inner_error_msg);
+ if (dex_file == nullptr) {
+ *error_msg = StringPrintf("Failed to open dex file %s from within oat file %s error '%s'",
+ location,
+ oat_file->GetLocation().c_str(),
+ inner_error_msg.c_str());
+ return std::unique_ptr<const DexFile>();
+ }
+
+ if (dex_file->GetLocationChecksum() != oat_dex_file->GetDexFileLocationChecksum()) {
+ *error_msg = StringPrintf("Checksums do not match for %s: %x vs %x",
+ location,
+ dex_file->GetLocationChecksum(),
+ oat_dex_file->GetDexFileLocationChecksum());
+ return std::unique_ptr<const DexFile>();
+ }
+ return dex_file;
+}
+
+bool ClassLinker::OpenImageDexFiles(gc::space::ImageSpace* space,
+ std::vector<std::unique_ptr<const DexFile>>* out_dex_files,
+ std::string* error_msg) {
+ ScopedAssertNoThreadSuspension nts(Thread::Current(), __FUNCTION__);
+ const ImageHeader& header = space->GetImageHeader();
+ mirror::Object* dex_caches_object = header.GetImageRoot(ImageHeader::kDexCaches);
+ DCHECK(dex_caches_object != nullptr);
+ mirror::ObjectArray<mirror::DexCache>* dex_caches =
+ dex_caches_object->AsObjectArray<mirror::DexCache>();
+ const OatFile* oat_file = space->GetOatFile();
+ for (int32_t i = 0; i < dex_caches->GetLength(); i++) {
+ mirror::DexCache* dex_cache = dex_caches->Get(i);
+ std::string dex_file_location(dex_cache->GetLocation()->ToModifiedUtf8());
+ std::unique_ptr<const DexFile> dex_file = OpenOatDexFile(oat_file,
+ dex_file_location.c_str(),
+ error_msg);
+ if (dex_file == nullptr) {
+ return false;
+ }
+ dex_cache->SetDexFile(dex_file.get());
+ out_dex_files->push_back(std::move(dex_file));
+ }
+ return true;
+}
+
bool ClassLinker::AddImageSpace(
gc::space::ImageSpace* space,
Handle<mirror::ClassLoader> class_loader,
@@ -1528,29 +1586,10 @@ bool ClassLinker::AddImageSpace(
dex_location_path = dex_location_path.substr(0, pos + 1); // Keep trailing '/'
dex_file_location = dex_location_path + dex_file_location;
}
- const OatFile::OatDexFile* oat_dex_file = oat_file->GetOatDexFile(dex_file_location.c_str(),
- nullptr);
- if (oat_dex_file == nullptr) {
- *error_msg = StringPrintf("Failed finding oat dex file for %s %s",
- oat_file->GetLocation().c_str(),
- dex_file_location.c_str());
- return false;
- }
- std::string inner_error_msg;
- std::unique_ptr<const DexFile> dex_file = oat_dex_file->OpenDexFile(&inner_error_msg);
+ std::unique_ptr<const DexFile> dex_file = OpenOatDexFile(oat_file,
+ dex_file_location.c_str(),
+ error_msg);
if (dex_file == nullptr) {
- *error_msg = StringPrintf("Failed to open dex file %s from within oat file %s error '%s'",
- dex_file_location.c_str(),
- oat_file->GetLocation().c_str(),
- inner_error_msg.c_str());
- return false;
- }
-
- if (dex_file->GetLocationChecksum() != oat_dex_file->GetDexFileLocationChecksum()) {
- *error_msg = StringPrintf("Checksums do not match for %s: %x vs %x",
- dex_file_location.c_str(),
- dex_file->GetLocationChecksum(),
- oat_dex_file->GetDexFileLocationChecksum());
return false;
}
@@ -2489,8 +2528,18 @@ uint32_t ClassLinker::SizeOfClassWithoutEmbeddedTables(const DexFile& dex_file,
size_t num_32 = 0;
size_t num_64 = 0;
if (class_data != nullptr) {
+ // We allow duplicate definitions of the same field in a class_data_item
+ // but ignore the repeated indexes here, b/21868015.
+ uint32_t last_field_idx = DexFile::kDexNoIndex;
for (ClassDataItemIterator it(dex_file, class_data); it.HasNextStaticField(); it.Next()) {
- const DexFile::FieldId& field_id = dex_file.GetFieldId(it.GetMemberIndex());
+ uint32_t field_idx = it.GetMemberIndex();
+ // Ordering enforced by DexFileVerifier.
+ DCHECK(last_field_idx == DexFile::kDexNoIndex || last_field_idx <= field_idx);
+ if (UNLIKELY(field_idx == last_field_idx)) {
+ continue;
+ }
+ last_field_idx = field_idx;
+ const DexFile::FieldId& field_id = dex_file.GetFieldId(field_idx);
const char* descriptor = dex_file.GetFieldTypeDescriptor(field_id);
char c = descriptor[0];
switch (c) {
diff --git a/runtime/class_linker.h b/runtime/class_linker.h
index 492a228522..36ed8204a6 100644
--- a/runtime/class_linker.h
+++ b/runtime/class_linker.h
@@ -149,6 +149,12 @@ class ClassLinker {
REQUIRES(!dex_lock_)
SHARED_REQUIRES(Locks::mutator_lock_);
+ bool OpenImageDexFiles(gc::space::ImageSpace* space,
+ std::vector<std::unique_ptr<const DexFile>>* out_dex_files,
+ std::string* error_msg)
+ REQUIRES(!dex_lock_)
+ SHARED_REQUIRES(Locks::mutator_lock_);
+
// Finds a class by its descriptor, loading it if necessary.
// If class_loader is null, searches boot_class_path_.
mirror::Class* FindClass(Thread* self,
diff --git a/runtime/dex_file_verifier.cc b/runtime/dex_file_verifier.cc
index ddf27496d9..9c9b8c5517 100644
--- a/runtime/dex_file_verifier.cc
+++ b/runtime/dex_file_verifier.cc
@@ -230,7 +230,10 @@ bool DexFileVerifier::CheckIndex(uint32_t field, uint32_t limit, const char* lab
return true;
}
-bool DexFileVerifier::CheckValidOffsetAndSize(uint32_t offset, uint32_t size, const char* label) {
+bool DexFileVerifier::CheckValidOffsetAndSize(uint32_t offset,
+ uint32_t size,
+ size_t alignment,
+ const char* label) {
if (size == 0) {
if (offset != 0) {
ErrorStringPrintf("Offset(%d) should be zero when size is zero for %s.", offset, label);
@@ -241,6 +244,10 @@ bool DexFileVerifier::CheckValidOffsetAndSize(uint32_t offset, uint32_t size, co
ErrorStringPrintf("Offset(%d) should be within file size(%zu) for %s.", offset, size_, label);
return false;
}
+ if (alignment != 0 && !IsAlignedParam(offset, alignment)) {
+ ErrorStringPrintf("Offset(%d) should be aligned by %zu for %s.", offset, alignment, label);
+ return false;
+ }
return true;
}
@@ -275,16 +282,43 @@ bool DexFileVerifier::CheckHeader() {
// Check that all offsets are inside the file.
bool result =
- CheckValidOffsetAndSize(header_->link_off_, header_->link_size_, "link") &&
- CheckValidOffsetAndSize(header_->map_off_, header_->map_off_, "map") &&
- CheckValidOffsetAndSize(header_->string_ids_off_, header_->string_ids_size_, "string-ids") &&
- CheckValidOffsetAndSize(header_->type_ids_off_, header_->type_ids_size_, "type-ids") &&
- CheckValidOffsetAndSize(header_->proto_ids_off_, header_->proto_ids_size_, "proto-ids") &&
- CheckValidOffsetAndSize(header_->field_ids_off_, header_->field_ids_size_, "field-ids") &&
- CheckValidOffsetAndSize(header_->method_ids_off_, header_->method_ids_size_, "method-ids") &&
- CheckValidOffsetAndSize(header_->class_defs_off_, header_->class_defs_size_, "class-defs") &&
- CheckValidOffsetAndSize(header_->data_off_, header_->data_size_, "data");
-
+ CheckValidOffsetAndSize(header_->link_off_,
+ header_->link_size_,
+ 0 /* unaligned */,
+ "link") &&
+ CheckValidOffsetAndSize(header_->map_off_,
+ header_->map_off_,
+ 4,
+ "map") &&
+ CheckValidOffsetAndSize(header_->string_ids_off_,
+ header_->string_ids_size_,
+ 4,
+ "string-ids") &&
+ CheckValidOffsetAndSize(header_->type_ids_off_,
+ header_->type_ids_size_,
+ 4,
+ "type-ids") &&
+ CheckValidOffsetAndSize(header_->proto_ids_off_,
+ header_->proto_ids_size_,
+ 4,
+ "proto-ids") &&
+ CheckValidOffsetAndSize(header_->field_ids_off_,
+ header_->field_ids_size_,
+ 4,
+ "field-ids") &&
+ CheckValidOffsetAndSize(header_->method_ids_off_,
+ header_->method_ids_size_,
+ 4,
+ "method-ids") &&
+ CheckValidOffsetAndSize(header_->class_defs_off_,
+ header_->class_defs_size_,
+ 4,
+ "class-defs") &&
+ CheckValidOffsetAndSize(header_->data_off_,
+ header_->data_size_,
+ 0, // Unaligned, spec doesn't talk about it, even though size
+ // is supposed to be a multiple of 4.
+ "data");
return result;
}
@@ -1965,6 +1999,11 @@ bool DexFileVerifier::CheckInterClassDefItem() {
// Check that references in annotations_directory_item are to right class.
if (item->annotations_off_ != 0) {
+ // annotations_off_ is supposed to be aligned by 4.
+ if (!IsAlignedParam(item->annotations_off_, 4)) {
+ ErrorStringPrintf("Invalid annotations_off_, not aligned by 4");
+ return false;
+ }
const uint8_t* data = begin_ + item->annotations_off_;
bool success;
uint16_t annotations_definer = FindFirstAnnotationsDirectoryDefiner(data, &success);
diff --git a/runtime/dex_file_verifier.h b/runtime/dex_file_verifier.h
index ddfeea2305..be0e6d83f8 100644
--- a/runtime/dex_file_verifier.h
+++ b/runtime/dex_file_verifier.h
@@ -48,7 +48,7 @@ class DexFileVerifier {
bool CheckList(size_t element_size, const char* label, const uint8_t* *ptr);
// Checks whether the offset is zero (when size is zero) or that the offset falls within the area
// claimed by the file.
- bool CheckValidOffsetAndSize(uint32_t offset, uint32_t size, const char* label);
+ bool CheckValidOffsetAndSize(uint32_t offset, uint32_t size, size_t alignment, const char* label);
bool CheckIndex(uint32_t field, uint32_t limit, const char* label);
bool CheckHeader();
diff --git a/runtime/dex_file_verifier_test.cc b/runtime/dex_file_verifier_test.cc
index 558a6ed5bf..44cf2eefa3 100644
--- a/runtime/dex_file_verifier_test.cc
+++ b/runtime/dex_file_verifier_test.cc
@@ -1253,4 +1253,63 @@ TEST_F(DexFileVerifierTest, DebugInfoTypeIdxTest) {
"DBG_START_LOCAL type_idx");
}
+TEST_F(DexFileVerifierTest, SectionAlignment) {
+ {
+ // The input dex file should be good before modification. Any file is fine, as long as it
+ // uses all sections.
+ ScratchFile tmp;
+ std::string error_msg;
+ std::unique_ptr<const DexFile> raw(OpenDexFileBase64(kGoodTestDex,
+ tmp.GetFilename().c_str(),
+ &error_msg));
+ ASSERT_TRUE(raw.get() != nullptr) << error_msg;
+ }
+
+ // Modify all section offsets to be unaligned.
+ constexpr size_t kSections = 7;
+ for (size_t i = 0; i < kSections; ++i) {
+ VerifyModification(
+ kGoodTestDex,
+ "section_align",
+ [&](DexFile* dex_file) {
+ DexFile::Header* header = const_cast<DexFile::Header*>(
+ reinterpret_cast<const DexFile::Header*>(dex_file->Begin()));
+ uint32_t* off_ptr;
+ switch (i) {
+ case 0:
+ off_ptr = &header->map_off_;
+ break;
+ case 1:
+ off_ptr = &header->string_ids_off_;
+ break;
+ case 2:
+ off_ptr = &header->type_ids_off_;
+ break;
+ case 3:
+ off_ptr = &header->proto_ids_off_;
+ break;
+ case 4:
+ off_ptr = &header->field_ids_off_;
+ break;
+ case 5:
+ off_ptr = &header->method_ids_off_;
+ break;
+ case 6:
+ off_ptr = &header->class_defs_off_;
+ break;
+
+ static_assert(kSections == 7, "kSections is wrong");
+ default:
+ LOG(FATAL) << "Unexpected section";
+ UNREACHABLE();
+ }
+ ASSERT_TRUE(off_ptr != nullptr);
+ ASSERT_NE(*off_ptr, 0U) << i; // Should already contain a value (in use).
+ (*off_ptr)++; // Add one, which should misalign it (all the sections
+ // above are aligned by 4).
+ },
+ "should be aligned by 4 for");
+ }
+}
+
} // namespace art
diff --git a/runtime/elf_file.cc b/runtime/elf_file.cc
index 52da28bb50..3b4b88d437 100644
--- a/runtime/elf_file.cc
+++ b/runtime/elf_file.cc
@@ -57,9 +57,12 @@ ElfFileImpl<ElfTypes>::ElfFileImpl(File* file, bool writable,
}
template <typename ElfTypes>
-ElfFileImpl<ElfTypes>* ElfFileImpl<ElfTypes>::Open(
- File* file, bool writable, bool program_header_only,
- std::string* error_msg, uint8_t* requested_base) {
+ElfFileImpl<ElfTypes>* ElfFileImpl<ElfTypes>::Open(File* file,
+ bool writable,
+ bool program_header_only,
+ bool low_4gb,
+ std::string* error_msg,
+ uint8_t* requested_base) {
std::unique_ptr<ElfFileImpl<ElfTypes>> elf_file(new ElfFileImpl<ElfTypes>
(file, writable, program_header_only, requested_base));
int prot;
@@ -71,26 +74,29 @@ ElfFileImpl<ElfTypes>* ElfFileImpl<ElfTypes>::Open(
prot = PROT_READ;
flags = MAP_PRIVATE;
}
- if (!elf_file->Setup(prot, flags, error_msg)) {
+ if (!elf_file->Setup(prot, flags, low_4gb, error_msg)) {
return nullptr;
}
return elf_file.release();
}
template <typename ElfTypes>
-ElfFileImpl<ElfTypes>* ElfFileImpl<ElfTypes>::Open(
- File* file, int prot, int flags, std::string* error_msg) {
+ElfFileImpl<ElfTypes>* ElfFileImpl<ElfTypes>::Open(File* file,
+ int prot,
+ int flags,
+ bool low_4gb,
+ std::string* error_msg) {
std::unique_ptr<ElfFileImpl<ElfTypes>> elf_file(new ElfFileImpl<ElfTypes>
(file, (prot & PROT_WRITE) == PROT_WRITE, /*program_header_only*/false,
/*requested_base*/nullptr));
- if (!elf_file->Setup(prot, flags, error_msg)) {
+ if (!elf_file->Setup(prot, flags, low_4gb, error_msg)) {
return nullptr;
}
return elf_file.release();
}
template <typename ElfTypes>
-bool ElfFileImpl<ElfTypes>::Setup(int prot, int flags, std::string* error_msg) {
+bool ElfFileImpl<ElfTypes>::Setup(int prot, int flags, bool low_4gb, std::string* error_msg) {
int64_t temp_file_length = file_->GetLength();
if (temp_file_length < 0) {
errno = -temp_file_length;
@@ -114,7 +120,7 @@ bool ElfFileImpl<ElfTypes>::Setup(int prot, int flags, std::string* error_msg) {
flags,
file_->Fd(),
0,
- /*low4_gb*/false,
+ low_4gb,
file_->GetPath().c_str(),
error_msg),
error_msg)) {
@@ -133,7 +139,7 @@ bool ElfFileImpl<ElfTypes>::Setup(int prot, int flags, std::string* error_msg) {
flags,
file_->Fd(),
0,
- /*low4_gb*/false,
+ low_4gb,
file_->GetPath().c_str(),
error_msg),
error_msg)) {
@@ -147,7 +153,7 @@ bool ElfFileImpl<ElfTypes>::Setup(int prot, int flags, std::string* error_msg) {
flags,
file_->Fd(),
0,
- /*low4_gb*/false,
+ low_4gb,
file_->GetPath().c_str(),
error_msg),
error_msg)) {
@@ -1058,7 +1064,7 @@ bool ElfFileImpl<ElfTypes>::GetLoadedSize(size_t* size, std::string* error_msg)
}
template <typename ElfTypes>
-bool ElfFileImpl<ElfTypes>::Load(bool executable, std::string* error_msg) {
+bool ElfFileImpl<ElfTypes>::Load(bool executable, bool low_4gb, std::string* error_msg) {
CHECK(program_header_only_) << file_->GetPath();
if (executable) {
@@ -1124,7 +1130,10 @@ bool ElfFileImpl<ElfTypes>::Load(bool executable, std::string* error_msg) {
}
std::unique_ptr<MemMap> reserve(MemMap::MapAnonymous(reservation_name.c_str(),
reserve_base_override,
- loaded_size, PROT_NONE, false, false,
+ loaded_size,
+ PROT_NONE,
+ low_4gb,
+ false,
error_msg));
if (reserve.get() == nullptr) {
*error_msg = StringPrintf("Failed to allocate %s: %s",
@@ -1656,7 +1665,11 @@ ElfFile::~ElfFile() {
CHECK_NE(elf32_.get() == nullptr, elf64_.get() == nullptr);
}
-ElfFile* ElfFile::Open(File* file, bool writable, bool program_header_only, std::string* error_msg,
+ElfFile* ElfFile::Open(File* file,
+ bool writable,
+ bool program_header_only,
+ bool low_4gb,
+ std::string* error_msg,
uint8_t* requested_base) {
if (file->GetLength() < EI_NIDENT) {
*error_msg = StringPrintf("File %s is too short to be a valid ELF file",
@@ -1668,7 +1681,7 @@ ElfFile* ElfFile::Open(File* file, bool writable, bool program_header_only, std:
MAP_PRIVATE,
file->Fd(),
0,
- /*low4_gb*/false,
+ low_4gb,
file->GetPath().c_str(),
error_msg));
if (map == nullptr && map->Size() != EI_NIDENT) {
@@ -1676,14 +1689,22 @@ ElfFile* ElfFile::Open(File* file, bool writable, bool program_header_only, std:
}
uint8_t* header = map->Begin();
if (header[EI_CLASS] == ELFCLASS64) {
- ElfFileImpl64* elf_file_impl = ElfFileImpl64::Open(file, writable, program_header_only,
- error_msg, requested_base);
+ ElfFileImpl64* elf_file_impl = ElfFileImpl64::Open(file,
+ writable,
+ program_header_only,
+ low_4gb,
+ error_msg,
+ requested_base);
if (elf_file_impl == nullptr)
return nullptr;
return new ElfFile(elf_file_impl);
} else if (header[EI_CLASS] == ELFCLASS32) {
- ElfFileImpl32* elf_file_impl = ElfFileImpl32::Open(file, writable, program_header_only,
- error_msg, requested_base);
+ ElfFileImpl32* elf_file_impl = ElfFileImpl32::Open(file,
+ writable,
+ program_header_only,
+ low_4gb,
+ error_msg,
+ requested_base);
if (elf_file_impl == nullptr) {
return nullptr;
}
@@ -1698,6 +1719,8 @@ ElfFile* ElfFile::Open(File* file, bool writable, bool program_header_only, std:
}
ElfFile* ElfFile::Open(File* file, int mmap_prot, int mmap_flags, std::string* error_msg) {
+ // low_4gb support not required for this path.
+ constexpr bool low_4gb = false;
if (file->GetLength() < EI_NIDENT) {
*error_msg = StringPrintf("File %s is too short to be a valid ELF file",
file->GetPath().c_str());
@@ -1708,7 +1731,7 @@ ElfFile* ElfFile::Open(File* file, int mmap_prot, int mmap_flags, std::string* e
MAP_PRIVATE,
file->Fd(),
0,
- /*low4_gb*/false,
+ low_4gb,
file->GetPath().c_str(),
error_msg));
if (map == nullptr && map->Size() != EI_NIDENT) {
@@ -1716,13 +1739,21 @@ ElfFile* ElfFile::Open(File* file, int mmap_prot, int mmap_flags, std::string* e
}
uint8_t* header = map->Begin();
if (header[EI_CLASS] == ELFCLASS64) {
- ElfFileImpl64* elf_file_impl = ElfFileImpl64::Open(file, mmap_prot, mmap_flags, error_msg);
+ ElfFileImpl64* elf_file_impl = ElfFileImpl64::Open(file,
+ mmap_prot,
+ mmap_flags,
+ low_4gb,
+ error_msg);
if (elf_file_impl == nullptr) {
return nullptr;
}
return new ElfFile(elf_file_impl);
} else if (header[EI_CLASS] == ELFCLASS32) {
- ElfFileImpl32* elf_file_impl = ElfFileImpl32::Open(file, mmap_prot, mmap_flags, error_msg);
+ ElfFileImpl32* elf_file_impl = ElfFileImpl32::Open(file,
+ mmap_prot,
+ mmap_flags,
+ low_4gb,
+ error_msg);
if (elf_file_impl == nullptr) {
return nullptr;
}
@@ -1744,8 +1775,8 @@ ElfFile* ElfFile::Open(File* file, int mmap_prot, int mmap_flags, std::string* e
return elf32_->func(__VA_ARGS__); \
}
-bool ElfFile::Load(bool executable, std::string* error_msg) {
- DELEGATE_TO_IMPL(Load, executable, error_msg);
+bool ElfFile::Load(bool executable, bool low_4gb, std::string* error_msg) {
+ DELEGATE_TO_IMPL(Load, executable, low_4gb, error_msg);
}
const uint8_t* ElfFile::FindDynamicSymbolAddress(const std::string& symbol_name) const {
@@ -1810,7 +1841,7 @@ bool ElfFile::GetLoadedSize(size_t* size, std::string* error_msg) const {
}
bool ElfFile::Strip(File* file, std::string* error_msg) {
- std::unique_ptr<ElfFile> elf_file(ElfFile::Open(file, true, false, error_msg));
+ std::unique_ptr<ElfFile> elf_file(ElfFile::Open(file, true, false, /*low_4gb*/false, error_msg));
if (elf_file.get() == nullptr) {
return false;
}
diff --git a/runtime/elf_file.h b/runtime/elf_file.h
index 1188c97658..b5229b579c 100644
--- a/runtime/elf_file.h
+++ b/runtime/elf_file.h
@@ -38,15 +38,22 @@ typedef ElfFileImpl<ElfTypes64> ElfFileImpl64;
// ELFObjectFile.
class ElfFile {
public:
- static ElfFile* Open(File* file, bool writable, bool program_header_only, std::string* error_msg,
+ static ElfFile* Open(File* file,
+ bool writable,
+ bool program_header_only,
+ bool low_4gb,
+ std::string* error_msg,
uint8_t* requested_base = nullptr); // TODO: move arg to before error_msg.
// Open with specific mmap flags, Always maps in the whole file, not just the
// program header sections.
- static ElfFile* Open(File* file, int mmap_prot, int mmap_flags, std::string* error_msg);
+ static ElfFile* Open(File* file,
+ int mmap_prot,
+ int mmap_flags,
+ std::string* error_msg);
~ElfFile();
// Load segments into memory based on PT_LOAD program headers
- bool Load(bool executable, std::string* error_msg);
+ bool Load(bool executable, bool low_4gb, std::string* error_msg);
const uint8_t* FindDynamicSymbolAddress(const std::string& symbol_name) const;
diff --git a/runtime/elf_file_impl.h b/runtime/elf_file_impl.h
index 2af31dc554..1cdbedc057 100644
--- a/runtime/elf_file_impl.h
+++ b/runtime/elf_file_impl.h
@@ -48,9 +48,17 @@ class ElfFileImpl {
using Elf_Phdr = typename ElfTypes::Phdr;
using Elf_Dyn = typename ElfTypes::Dyn;
- static ElfFileImpl* Open(File* file, bool writable, bool program_header_only,
- std::string* error_msg, uint8_t* requested_base = nullptr);
- static ElfFileImpl* Open(File* file, int mmap_prot, int mmap_flags, std::string* error_msg);
+ static ElfFileImpl* Open(File* file,
+ bool writable,
+ bool program_header_only,
+ bool low_4gb,
+ std::string* error_msg,
+ uint8_t* requested_base = nullptr);
+ static ElfFileImpl* Open(File* file,
+ int mmap_prot,
+ int mmap_flags,
+ bool low_4gb,
+ std::string* error_msg);
~ElfFileImpl();
const File& GetFile() const {
@@ -111,7 +119,7 @@ class ElfFileImpl {
// Load segments into memory based on PT_LOAD program headers.
// executable is true at run time, false at compile time.
- bool Load(bool executable, std::string* error_msg);
+ bool Load(bool executable, bool low_4gb, std::string* error_msg);
bool Fixup(Elf_Addr base_address);
bool FixupDynamic(Elf_Addr base_address);
@@ -129,7 +137,7 @@ class ElfFileImpl {
private:
ElfFileImpl(File* file, bool writable, bool program_header_only, uint8_t* requested_base);
- bool Setup(int prot, int flags, std::string* error_msg);
+ bool Setup(int prot, int flags, bool low_4gb, std::string* error_msg);
bool SetMap(MemMap* map, std::string* error_msg);
diff --git a/runtime/entrypoints/entrypoint_utils-inl.h b/runtime/entrypoints/entrypoint_utils-inl.h
index 7e7d904a10..3e6b4537f9 100644
--- a/runtime/entrypoints/entrypoint_utils-inl.h
+++ b/runtime/entrypoints/entrypoint_utils-inl.h
@@ -173,7 +173,10 @@ inline mirror::Object* AllocObjectFromCode(uint32_t type_idx,
if (klass == nullptr) {
return nullptr;
}
- return klass->Alloc<kInstrumented>(self, Runtime::Current()->GetHeap()->GetCurrentAllocator());
+ // CheckObjectAlloc can cause thread suspension which means we may now be instrumented.
+ return klass->Alloc</*kInstrumented*/true>(
+ self,
+ Runtime::Current()->GetHeap()->GetCurrentAllocator());
}
DCHECK(klass != nullptr);
return klass->Alloc<kInstrumented>(self, allocator_type);
@@ -194,7 +197,9 @@ inline mirror::Object* AllocObjectFromCodeResolved(mirror::Class* klass,
}
gc::Heap* heap = Runtime::Current()->GetHeap();
// Pass in false since the object cannot be finalizable.
- return klass->Alloc<kInstrumented, false>(self, heap->GetCurrentAllocator());
+ // CheckClassInitializedForObjectAlloc can cause thread suspension which means we may now be
+ // instrumented.
+ return klass->Alloc</*kInstrumented*/true, false>(self, heap->GetCurrentAllocator());
}
// Pass in false since the object cannot be finalizable.
return klass->Alloc<kInstrumented, false>(self, allocator_type);
@@ -265,9 +270,12 @@ inline mirror::Array* AllocArrayFromCode(uint32_t type_idx,
return nullptr;
}
gc::Heap* heap = Runtime::Current()->GetHeap();
- return mirror::Array::Alloc<kInstrumented>(self, klass, component_count,
- klass->GetComponentSizeShift(),
- heap->GetCurrentAllocator());
+ // CheckArrayAlloc can cause thread suspension which means we may now be instrumented.
+ return mirror::Array::Alloc</*kInstrumented*/true>(self,
+ klass,
+ component_count,
+ klass->GetComponentSizeShift(),
+ heap->GetCurrentAllocator());
}
return mirror::Array::Alloc<kInstrumented>(self, klass, component_count,
klass->GetComponentSizeShift(), allocator_type);
diff --git a/runtime/gc/allocation_record.cc b/runtime/gc/allocation_record.cc
index 4672483308..73190455cf 100644
--- a/runtime/gc/allocation_record.cc
+++ b/runtime/gc/allocation_record.cc
@@ -92,7 +92,7 @@ void AllocRecordObjectMap::SetProperties() {
}
AllocRecordObjectMap::~AllocRecordObjectMap() {
- STLDeleteValues(&entries_);
+ Clear();
}
void AllocRecordObjectMap::VisitRoots(RootVisitor* visitor) {
@@ -223,7 +223,11 @@ void AllocRecordObjectMap::SetAllocTrackingEnabled(bool enable) {
if (heap->IsAllocTrackingEnabled()) {
return; // Already enabled, bail.
}
- AllocRecordObjectMap* records = new AllocRecordObjectMap();
+ AllocRecordObjectMap* records = heap->GetAllocationRecords();
+ if (records == nullptr) {
+ records = new AllocRecordObjectMap;
+ heap->SetAllocationRecords(records);
+ }
CHECK(records != nullptr);
records->SetProperties();
std::string self_name;
@@ -237,7 +241,6 @@ void AllocRecordObjectMap::SetAllocTrackingEnabled(bool enable) {
LOG(INFO) << "Enabling alloc tracker (" << records->alloc_record_max_ << " entries of "
<< records->max_stack_depth_ << " frames, taking up to "
<< PrettySize(sz * records->alloc_record_max_) << ")";
- heap->SetAllocationRecords(records);
}
Runtime::Current()->GetInstrumentation()->InstrumentQuickAllocEntryPoints();
{
@@ -247,7 +250,6 @@ void AllocRecordObjectMap::SetAllocTrackingEnabled(bool enable) {
} else {
// Delete outside of the critical section to avoid possible lock violations like the runtime
// shutdown lock.
- std::unique_ptr<AllocRecordObjectMap> map;
{
MutexLock mu(self, *Locks::alloc_tracker_lock_);
if (!heap->IsAllocTrackingEnabled()) {
@@ -255,7 +257,8 @@ void AllocRecordObjectMap::SetAllocTrackingEnabled(bool enable) {
}
heap->SetAllocTrackingEnabled(false);
LOG(INFO) << "Disabling alloc tracker";
- map = heap->ReleaseAllocationRecords();
+ AllocRecordObjectMap* records = heap->GetAllocationRecords();
+ records->Clear();
}
// If an allocation comes in before we uninstrument, we will safely drop it on the floor.
Runtime::Current()->GetInstrumentation()->UninstrumentQuickAllocEntryPoints();
@@ -303,5 +306,10 @@ void AllocRecordObjectMap::RecordAllocation(Thread* self, mirror::Object* obj, m
DCHECK_LE(records->Size(), records->alloc_record_max_);
}
+void AllocRecordObjectMap::Clear() {
+ STLDeleteValues(&entries_);
+ entries_.clear();
+}
+
} // namespace gc
} // namespace art
diff --git a/runtime/gc/allocation_record.h b/runtime/gc/allocation_record.h
index ffdfd31ce6..18cce4d72d 100644
--- a/runtime/gc/allocation_record.h
+++ b/runtime/gc/allocation_record.h
@@ -306,6 +306,8 @@ class AllocRecordObjectMap {
return entries_.rend();
}
+ void Clear() REQUIRES(Locks::alloc_tracker_lock_);
+
private:
static constexpr size_t kDefaultNumAllocRecords = 512 * 1024;
static constexpr size_t kDefaultNumRecentRecords = 64 * 1024 - 1;
diff --git a/runtime/gc/heap-inl.h b/runtime/gc/heap-inl.h
index d7023d8ee0..59fd4a6b44 100644
--- a/runtime/gc/heap-inl.h
+++ b/runtime/gc/heap-inl.h
@@ -174,9 +174,6 @@ inline mirror::Object* Heap::AllocObjectWithAllocator(Thread* self,
} else {
DCHECK(!Runtime::Current()->HasStatsEnabled());
}
- if (AllocatorHasAllocationStack(allocator)) {
- PushOnAllocationStack(self, &obj);
- }
if (kInstrumented) {
if (IsAllocTrackingEnabled()) {
// Use obj->GetClass() instead of klass, because PushOnAllocationStack() could move klass
@@ -185,6 +182,9 @@ inline mirror::Object* Heap::AllocObjectWithAllocator(Thread* self,
} else {
DCHECK(!IsAllocTrackingEnabled());
}
+ if (AllocatorHasAllocationStack(allocator)) {
+ PushOnAllocationStack(self, &obj);
+ }
if (kInstrumented) {
if (gc_stress_mode_) {
CheckGcStressMode(self, &obj);
diff --git a/runtime/gc/heap.cc b/runtime/gc/heap.cc
index 2e5b599940..f4fccee034 100644
--- a/runtime/gc/heap.cc
+++ b/runtime/gc/heap.cc
@@ -3940,10 +3940,6 @@ void Heap::SetAllocationRecords(AllocRecordObjectMap* records) {
allocation_records_.reset(records);
}
-std::unique_ptr<AllocRecordObjectMap> Heap::ReleaseAllocationRecords() {
- return std::move(allocation_records_);
-}
-
void Heap::VisitAllocationRecords(RootVisitor* visitor) const {
if (IsAllocTrackingEnabled()) {
MutexLock mu(Thread::Current(), *Locks::alloc_tracker_lock_);
diff --git a/runtime/gc/heap.h b/runtime/gc/heap.h
index e0a53a0cc8..9eda422902 100644
--- a/runtime/gc/heap.h
+++ b/runtime/gc/heap.h
@@ -766,10 +766,6 @@ class Heap {
return allocation_records_.get();
}
- // Release ownership of the allocation records.
- std::unique_ptr<AllocRecordObjectMap> ReleaseAllocationRecords()
- REQUIRES(Locks::alloc_tracker_lock_);
-
void SetAllocationRecords(AllocRecordObjectMap* records)
REQUIRES(Locks::alloc_tracker_lock_);
diff --git a/runtime/gc/space/image_space.cc b/runtime/gc/space/image_space.cc
index 9ecd391e4d..5ff1cb7a2e 100644
--- a/runtime/gc/space/image_space.cc
+++ b/runtime/gc/space/image_space.cc
@@ -16,7 +16,6 @@
#include "image_space.h"
-#include <dirent.h>
#include <lz4.h>
#include <random>
#include <sys/statvfs.h>
@@ -29,9 +28,9 @@
#include "base/scoped_flock.h"
#include "base/systrace.h"
#include "base/time_utils.h"
-#include "base/unix_file/fd_file.h"
#include "gc/accounting/space_bitmap-inl.h"
#include "image-inl.h"
+#include "image_space_fs.h"
#include "mirror/class-inl.h"
#include "mirror/object-inl.h"
#include "oat_file.h"
@@ -79,105 +78,6 @@ static int32_t ChooseRelocationOffsetDelta(int32_t min_delta, int32_t max_delta)
return r;
}
-// We are relocating or generating the core image. We should get rid of everything. It is all
-// out-of-date. We also don't really care if this fails since it is just a convenience.
-// Adapted from prune_dex_cache(const char* subdir) in frameworks/native/cmds/installd/commands.c
-// Note this should only be used during first boot.
-static void RealPruneDalvikCache(const std::string& cache_dir_path);
-
-static void PruneDalvikCache(InstructionSet isa) {
- CHECK_NE(isa, kNone);
- // Prune the base /data/dalvik-cache.
- RealPruneDalvikCache(GetDalvikCacheOrDie(".", false));
- // Prune /data/dalvik-cache/<isa>.
- RealPruneDalvikCache(GetDalvikCacheOrDie(GetInstructionSetString(isa), false));
-}
-
-static void RealPruneDalvikCache(const std::string& cache_dir_path) {
- if (!OS::DirectoryExists(cache_dir_path.c_str())) {
- return;
- }
- DIR* cache_dir = opendir(cache_dir_path.c_str());
- if (cache_dir == nullptr) {
- PLOG(WARNING) << "Unable to open " << cache_dir_path << " to delete it's contents";
- return;
- }
-
- for (struct dirent* de = readdir(cache_dir); de != nullptr; de = readdir(cache_dir)) {
- const char* name = de->d_name;
- if (strcmp(name, ".") == 0 || strcmp(name, "..") == 0) {
- continue;
- }
- // We only want to delete regular files and symbolic links.
- if (de->d_type != DT_REG && de->d_type != DT_LNK) {
- if (de->d_type != DT_DIR) {
- // We do expect some directories (namely the <isa> for pruning the base dalvik-cache).
- LOG(WARNING) << "Unexpected file type of " << std::hex << de->d_type << " encountered.";
- }
- continue;
- }
- std::string cache_file(cache_dir_path);
- cache_file += '/';
- cache_file += name;
- if (TEMP_FAILURE_RETRY(unlink(cache_file.c_str())) != 0) {
- PLOG(ERROR) << "Unable to unlink " << cache_file;
- continue;
- }
- }
- CHECK_EQ(0, TEMP_FAILURE_RETRY(closedir(cache_dir))) << "Unable to close directory.";
-}
-
-// We write out an empty file to the zygote's ISA specific cache dir at the start of
-// every zygote boot and delete it when the boot completes. If we find a file already
-// present, it usually means the boot didn't complete. We wipe the entire dalvik
-// cache if that's the case.
-static void MarkZygoteStart(const InstructionSet isa, const uint32_t max_failed_boots) {
- const std::string isa_subdir = GetDalvikCacheOrDie(GetInstructionSetString(isa), false);
- const std::string boot_marker = isa_subdir + "/.booting";
- const char* file_name = boot_marker.c_str();
-
- uint32_t num_failed_boots = 0;
- std::unique_ptr<File> file(OS::OpenFileReadWrite(file_name));
- if (file.get() == nullptr) {
- file.reset(OS::CreateEmptyFile(file_name));
-
- if (file.get() == nullptr) {
- PLOG(WARNING) << "Failed to create boot marker.";
- return;
- }
- } else {
- if (!file->ReadFully(&num_failed_boots, sizeof(num_failed_boots))) {
- PLOG(WARNING) << "Failed to read boot marker.";
- file->Erase();
- return;
- }
- }
-
- if (max_failed_boots != 0 && num_failed_boots > max_failed_boots) {
- LOG(WARNING) << "Incomplete boot detected. Pruning dalvik cache";
- RealPruneDalvikCache(isa_subdir);
- }
-
- ++num_failed_boots;
- VLOG(startup) << "Number of failed boots on : " << boot_marker << " = " << num_failed_boots;
-
- if (lseek(file->Fd(), 0, SEEK_SET) == -1) {
- PLOG(WARNING) << "Failed to write boot marker.";
- file->Erase();
- return;
- }
-
- if (!file->WriteFully(&num_failed_boots, sizeof(num_failed_boots))) {
- PLOG(WARNING) << "Failed to write boot marker.";
- file->Erase();
- return;
- }
-
- if (file->FlushCloseOrErase() != 0) {
- PLOG(WARNING) << "Failed to flush boot marker.";
- }
-}
-
static bool GenerateImage(const std::string& image_filename, InstructionSet image_isa,
std::string* error_msg) {
const std::string boot_class_path_string(Runtime::Current()->GetBootClassPathString());
@@ -479,9 +379,33 @@ ImageSpace* ImageSpace::CreateBootImage(const char* image_location,
bool has_cache = false;
bool dalvik_cache_exists = false;
bool is_global_cache = true;
- const bool found_image = FindImageFilename(image_location, image_isa, &system_filename,
- &has_system, &cache_filename, &dalvik_cache_exists,
- &has_cache, &is_global_cache);
+ bool found_image = FindImageFilename(image_location, image_isa, &system_filename,
+ &has_system, &cache_filename, &dalvik_cache_exists,
+ &has_cache, &is_global_cache);
+
+ // If we're starting with the global cache, and we're the zygote, try to see whether there are
+ // OTA artifacts from the A/B OTA preopting to move over.
+ // (It is structurally simpler to check this here, instead of complicating the compile/relocate
+ // logic below.)
+ const bool is_zygote = Runtime::Current()->IsZygote();
+ if (is_global_cache && is_zygote) {
+ VLOG(startup) << "Checking for A/B OTA data.";
+ TryMoveOTAArtifacts(cache_filename, dalvik_cache_exists);
+
+ // Retry. There are two cases where the old info is outdated:
+ // * There wasn't a boot image before (e.g., some failure on boot), but now the OTA preopted
+ // image has been moved in-place.
+ // * There was a boot image before, and we tried to move the OTA preopted image, but a failure
+ // happened and there is no file anymore.
+ found_image = FindImageFilename(image_location,
+ image_isa,
+ &system_filename,
+ &has_system,
+ &cache_filename,
+ &dalvik_cache_exists,
+ &has_cache,
+ &is_global_cache);
+ }
if (Runtime::Current()->IsZygote() && !secondary_image) {
MarkZygoteStart(image_isa, Runtime::Current()->GetZygoteMaxFailedBoots());
@@ -520,8 +444,16 @@ ImageSpace* ImageSpace::CreateBootImage(const char* image_location,
// Whether we can write to the cache.
success = false;
} else if (secondary_image) {
- reason = "Should not have to patch secondary image.";
- success = false;
+ if (Runtime::Current()->IsZygote()) {
+ // Secondary image is out of date. Clear cache and exit to let it retry from scratch.
+ LOG(ERROR) << "Cannot patch secondary image '" << image_location
+ << "', clearing dalvik_cache and restarting zygote.";
+ PruneDalvikCache(image_isa);
+ _exit(1);
+ } else {
+ reason = "Should not have to patch secondary image.";
+ success = false;
+ }
} else {
// Try to relocate.
success = RelocateImage(image_location, cache_filename.c_str(), image_isa, &reason);
@@ -693,7 +625,7 @@ class RelocationRange {
}
// Returns the delta between the dest from the source.
- off_t Delta() const {
+ uintptr_t Delta() const {
return dest_ - source_;
}
@@ -715,6 +647,13 @@ class RelocationRange {
const uintptr_t length_;
};
+std::ostream& operator<<(std::ostream& os, const RelocationRange& reloc) {
+ return os << "(" << reinterpret_cast<const void*>(reloc.Source()) << "-"
+ << reinterpret_cast<const void*>(reloc.Source() + reloc.Length()) << ")->("
+ << reinterpret_cast<const void*>(reloc.Dest()) << "-"
+ << reinterpret_cast<const void*>(reloc.Dest() + reloc.Length()) << ")";
+}
+
class FixupVisitor : public ValueObject {
public:
FixupVisitor(const RelocationRange& boot_image,
@@ -746,7 +685,7 @@ class FixupVisitor : public ValueObject {
ALWAYS_INLINE const void* ForwardCode(const void* src) const {
const uintptr_t uint_src = reinterpret_cast<uintptr_t>(src);
if (boot_oat_.InSource(uint_src)) {
- return reinterpret_cast<const void*>(boot_oat_.ToDest(uint_src));
+ return reinterpret_cast<const void*>(boot_oat_.ToDest(uint_src));
}
if (app_oat_.InSource(uint_src)) {
return reinterpret_cast<const void*>(app_oat_.ToDest(uint_src));
@@ -763,13 +702,6 @@ class FixupVisitor : public ValueObject {
const RelocationRange app_oat_;
};
-std::ostream& operator<<(std::ostream& os, const RelocationRange& reloc) {
- return os << "(" << reinterpret_cast<const void*>(reloc.Source()) << "-"
- << reinterpret_cast<const void*>(reloc.Source() + reloc.Length()) << ")->("
- << reinterpret_cast<const void*>(reloc.Dest()) << "-"
- << reinterpret_cast<const void*>(reloc.Dest() + reloc.Length()) << ")";
-}
-
// Adapt for mirror::Class::FixupNativePointers.
class FixupObjectAdapter : public FixupVisitor {
public:
@@ -832,8 +764,10 @@ class FixupObjectVisitor : public FixupVisitor {
public:
template<typename... Args>
explicit FixupObjectVisitor(gc::accounting::ContinuousSpaceBitmap* pointer_array_visited,
+ const size_t pointer_size,
Args... args)
: FixupVisitor(args...),
+ pointer_size_(pointer_size),
pointer_array_visited_(pointer_array_visited) {}
// Fix up separately since we also need to fix up method entrypoints.
@@ -867,7 +801,7 @@ class FixupObjectVisitor : public FixupVisitor {
if (array != nullptr &&
visitor.IsInAppImage(array) &&
!pointer_array_visited_->Test(array)) {
- array->Fixup<kVerifyNone, kWithoutReadBarrier>(array, sizeof(void*), visitor);
+ array->Fixup<kVerifyNone, kWithoutReadBarrier>(array, pointer_size_, visitor);
pointer_array_visited_->Set(array);
}
}
@@ -889,7 +823,7 @@ class FixupObjectVisitor : public FixupVisitor {
if (obj->IsClass<kVerifyNone, kWithoutReadBarrier>()) {
mirror::Class* klass = obj->AsClass<kVerifyNone, kWithoutReadBarrier>();
FixupObjectAdapter visitor(boot_image_, boot_oat_, app_image_, app_oat_);
- klass->FixupNativePointers<kVerifyNone, kWithoutReadBarrier>(klass, sizeof(void*), visitor);
+ klass->FixupNativePointers<kVerifyNone, kWithoutReadBarrier>(klass, pointer_size_, visitor);
// Deal with the pointer arrays. Use the helper function since multiple classes can reference
// the same arrays.
VisitPointerArray(klass->GetVTable<kVerifyNone, kWithoutReadBarrier>(), visitor);
@@ -908,6 +842,7 @@ class FixupObjectVisitor : public FixupVisitor {
}
private:
+ const size_t pointer_size_;
gc::accounting::ContinuousSpaceBitmap* const pointer_array_visited_;
};
@@ -926,7 +861,8 @@ class ForwardObjectAdapter {
class ForwardCodeAdapter {
public:
- ALWAYS_INLINE ForwardCodeAdapter(const FixupVisitor* visitor) : visitor_(visitor) {}
+ ALWAYS_INLINE ForwardCodeAdapter(const FixupVisitor* visitor)
+ : visitor_(visitor) {}
template <typename T>
ALWAYS_INLINE T* operator()(T* src) const {
@@ -940,19 +876,21 @@ class ForwardCodeAdapter {
class FixupArtMethodVisitor : public FixupVisitor, public ArtMethodVisitor {
public:
template<typename... Args>
- explicit FixupArtMethodVisitor(bool fixup_heap_objects, Args... args)
+ explicit FixupArtMethodVisitor(bool fixup_heap_objects, size_t pointer_size, Args... args)
: FixupVisitor(args...),
- fixup_heap_objects_(fixup_heap_objects) {}
+ fixup_heap_objects_(fixup_heap_objects),
+ pointer_size_(pointer_size) {}
virtual void Visit(ArtMethod* method) NO_THREAD_SAFETY_ANALYSIS {
if (fixup_heap_objects_) {
- method->UpdateObjectsForImageRelocation(ForwardObjectAdapter(this));
+ method->UpdateObjectsForImageRelocation(ForwardObjectAdapter(this), pointer_size_);
}
- method->UpdateEntrypoints<kWithoutReadBarrier>(ForwardCodeAdapter(this));
+ method->UpdateEntrypoints<kWithoutReadBarrier>(ForwardCodeAdapter(this), pointer_size_);
}
private:
const bool fixup_heap_objects_;
+ const size_t pointer_size_;
};
class FixupArtFieldVisitor : public FixupVisitor, public ArtFieldVisitor {
@@ -988,6 +926,7 @@ static bool RelocateInPlace(ImageHeader& image_header,
uint32_t boot_image_end = 0;
uint32_t boot_oat_begin = 0;
uint32_t boot_oat_end = 0;
+ const size_t pointer_size = image_header.GetPointerSize();
gc::Heap* const heap = Runtime::Current()->GetHeap();
heap->GetBootImagesSize(&boot_image_begin, &boot_image_end, &boot_oat_begin, &boot_oat_end);
CHECK_NE(boot_image_begin, boot_image_end)
@@ -1050,6 +989,7 @@ static bool RelocateInPlace(ImageHeader& image_header,
target_base,
image_header.GetImageSize()));
FixupObjectVisitor fixup_object_visitor(visited_bitmap.get(),
+ pointer_size,
boot_image,
boot_oat,
app_image,
@@ -1099,10 +1039,10 @@ static bool RelocateInPlace(ImageHeader& image_header,
dex_cache->SetResolvedMethods(new_methods);
}
for (size_t j = 0, num = dex_cache->NumResolvedMethods(); j != num; ++j) {
- ArtMethod* orig = mirror::DexCache::GetElementPtrSize(new_methods, j, sizeof(void*));
+ ArtMethod* orig = mirror::DexCache::GetElementPtrSize(new_methods, j, pointer_size);
ArtMethod* copy = fixup_adapter.ForwardObject(orig);
if (orig != copy) {
- mirror::DexCache::SetElementPtrSize(new_methods, j, copy, sizeof(void*));
+ mirror::DexCache::SetElementPtrSize(new_methods, j, copy, pointer_size);
}
}
}
@@ -1113,10 +1053,10 @@ static bool RelocateInPlace(ImageHeader& image_header,
dex_cache->SetResolvedFields(new_fields);
}
for (size_t j = 0, num = dex_cache->NumResolvedFields(); j != num; ++j) {
- ArtField* orig = mirror::DexCache::GetElementPtrSize(new_fields, j, sizeof(void*));
+ ArtField* orig = mirror::DexCache::GetElementPtrSize(new_fields, j, pointer_size);
ArtField* copy = fixup_adapter.ForwardObject(orig);
if (orig != copy) {
- mirror::DexCache::SetElementPtrSize(new_fields, j, copy, sizeof(void*));
+ mirror::DexCache::SetElementPtrSize(new_fields, j, copy, pointer_size);
}
}
}
@@ -1125,11 +1065,16 @@ static bool RelocateInPlace(ImageHeader& image_header,
{
// Only touches objects in the app image, no need for mutator lock.
TimingLogger::ScopedTiming timing("Fixup methods", &logger);
- FixupArtMethodVisitor method_visitor(fixup_image, boot_image, boot_oat, app_image, app_oat);
+ FixupArtMethodVisitor method_visitor(fixup_image,
+ pointer_size,
+ boot_image,
+ boot_oat,
+ app_image,
+ app_oat);
image_header.GetImageSection(ImageHeader::kSectionArtMethods).VisitPackedArtMethods(
&method_visitor,
target_base,
- sizeof(void*));
+ pointer_size);
}
if (fixup_image) {
{
@@ -1283,7 +1228,7 @@ ImageSpace* ImageSpace::Init(const char* image_filename,
/*out*/out_error_msg));
if (map != nullptr) {
const size_t stored_size = image_header->GetDataSize();
- const size_t write_offset = sizeof(ImageHeader); // Skip the header.
+ const size_t decompress_offset = sizeof(ImageHeader); // Skip the header.
std::unique_ptr<MemMap> temp_map(MemMap::MapFile(sizeof(ImageHeader) + stored_size,
PROT_READ,
MAP_PRIVATE,
@@ -1302,14 +1247,15 @@ ImageSpace* ImageSpace::Init(const char* image_filename,
TimingLogger::ScopedTiming timing2("LZ4 decompress image", &logger);
const size_t decompressed_size = LZ4_decompress_safe(
reinterpret_cast<char*>(temp_map->Begin()) + sizeof(ImageHeader),
- reinterpret_cast<char*>(map->Begin()) + write_offset,
+ reinterpret_cast<char*>(map->Begin()) + decompress_offset,
stored_size,
- map->Size());
+ map->Size() - decompress_offset);
VLOG(image) << "Decompressing image took " << PrettyDuration(NanoTime() - start);
if (decompressed_size + sizeof(ImageHeader) != image_header->GetImageSize()) {
- *error_msg = StringPrintf("Decompressed size does not match expected image size %zu vs %zu",
- decompressed_size + sizeof(ImageHeader),
- image_header->GetImageSize());
+ *error_msg = StringPrintf(
+ "Decompressed size does not match expected image size %zu vs %zu",
+ decompressed_size + sizeof(ImageHeader),
+ image_header->GetImageSize());
return nullptr;
}
}
@@ -1456,6 +1402,7 @@ OatFile* ImageSpace::OpenOatFile(const char* image_path, std::string* error_msg)
image_header.GetOatDataBegin(),
image_header.GetOatFileBegin(),
!Runtime::Current()->IsAotCompiler(),
+ /*low_4gb*/false,
nullptr,
error_msg);
if (oat_file == nullptr) {
diff --git a/runtime/gc/space/image_space_fs.h b/runtime/gc/space/image_space_fs.h
new file mode 100644
index 0000000000..ec4bf92a2a
--- /dev/null
+++ b/runtime/gc/space/image_space_fs.h
@@ -0,0 +1,307 @@
+/*
+ * 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_RUNTIME_GC_SPACE_IMAGE_SPACE_FS_H_
+#define ART_RUNTIME_GC_SPACE_IMAGE_SPACE_FS_H_
+
+#include <dirent.h>
+#include <dlfcn.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"
+#include "utils.h"
+
+namespace art {
+namespace gc {
+namespace space {
+
+// This file contains helper code for ImageSpace. It has most of the file-system
+// related code, including handling A/B OTA.
+
+namespace impl {
+
+// Delete the directory and its (regular or link) contents. If the recurse flag is true, delete
+// sub-directories recursively.
+static void DeleteDirectoryContents(const std::string& dir, bool recurse) {
+ if (!OS::DirectoryExists(dir.c_str())) {
+ return;
+ }
+ DIR* c_dir = opendir(dir.c_str());
+ if (c_dir == nullptr) {
+ PLOG(WARNING) << "Unable to open " << dir << " to delete it's contents";
+ return;
+ }
+
+ for (struct dirent* de = readdir(c_dir); de != nullptr; de = readdir(c_dir)) {
+ const char* name = de->d_name;
+ if (strcmp(name, ".") == 0 || strcmp(name, "..") == 0) {
+ continue;
+ }
+ // We only want to delete regular files and symbolic links.
+ std::string file = 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) {
+ DeleteDirectoryContents(file, recurse);
+ // Try to rmdir the directory.
+ if (TEMP_FAILURE_RETRY(rmdir(file.c_str())) != 0) {
+ PLOG(ERROR) << "Unable to rmdir " << file;
+ }
+ }
+ } else {
+ LOG(WARNING) << "Unexpected file type of " << std::hex << de->d_type << " encountered.";
+ }
+ } else {
+ // Try to unlink the file.
+ if (TEMP_FAILURE_RETRY(unlink(file.c_str())) != 0) {
+ PLOG(ERROR) << "Unable to unlink " << file;
+ }
+ }
+ }
+ CHECK_EQ(0, TEMP_FAILURE_RETRY(closedir(c_dir))) << "Unable to close directory.";
+}
+
+static bool HasContent(const char* dir) {
+ if (!OS::DirectoryExists(dir)) {
+ return false;
+ }
+ DIR* c_dir = opendir(dir);
+ if (c_dir == nullptr) {
+ PLOG(WARNING) << "Unable to open " << dir << " to delete it if empty";
+ return false;
+ }
+
+ for (struct dirent* de = readdir(c_dir); de != nullptr; de = readdir(c_dir)) {
+ const char* name = de->d_name;
+ if (strcmp(name, ".") == 0 || strcmp(name, "..") == 0) {
+ continue;
+ }
+ // Something here.
+ CHECK_EQ(0, TEMP_FAILURE_RETRY(closedir(c_dir))) << "Unable to close directory.";
+ return true;
+ }
+ CHECK_EQ(0, TEMP_FAILURE_RETRY(closedir(c_dir))) << "Unable to close directory.";
+ return false;
+}
+
+// Delete this directory, if empty. Then repeat with the parents. Skips non-existing directories.
+// If stop_at isn't null, the recursion will stop when a directory with the given name is found.
+static void DeleteEmptyDirectoriesUpTo(const std::string& dir, const char* stop_at) {
+ if (HasContent(dir.c_str())) {
+ return;
+ }
+ if (stop_at != nullptr) {
+ // This check isn't precise, but good enough in practice.
+ if (EndsWith(dir, stop_at)) {
+ return;
+ }
+ }
+ if (OS::DirectoryExists(dir.c_str())) {
+ if (TEMP_FAILURE_RETRY(rmdir(dir.c_str())) != 0) {
+ PLOG(ERROR) << "Unable to rmdir " << dir;
+ return;
+ }
+ }
+ size_t last_slash = dir.rfind('/');
+ if (last_slash != std::string::npos) {
+ DeleteEmptyDirectoriesUpTo(dir.substr(0, last_slash), stop_at);
+ }
+}
+
+static void MoveOTAArtifacts(const char* src, const char* trg) {
+ DCHECK(OS::DirectoryExists(src));
+ DCHECK(OS::DirectoryExists(trg));
+
+ if (HasContent(trg)) {
+ LOG(WARNING) << "We do not support merging caches, but the target isn't empty: " << src
+ << " to " << trg;
+ return;
+ }
+
+ if (TEMP_FAILURE_RETRY(rename(src, trg)) != 0) {
+ PLOG(ERROR) << "Could not rename OTA cache " << src << " to target " << trg;
+ }
+}
+
+// This is some dlopen/dlsym and hardcoded data to avoid a dependency on libselinux. Make sure
+// this stays in sync!
+static bool RelabelOTAFiles(const std::string& dalvik_cache_dir) {
+ // We only expect selinux on devices. Don't even attempt this on the host.
+ if (!kIsTargetBuild) {
+ return true;
+ }
+
+ // Custom deleter, so we can use std::unique_ptr.
+ struct HandleDeleter {
+ void operator()(void* in) {
+ if (in != nullptr && dlclose(in) != 0) {
+ PLOG(ERROR) << "Could not close selinux handle.";
+ }
+ }
+ };
+
+ // Look for selinux library.
+ std::unique_ptr<void, HandleDeleter> selinux_handle(dlopen("libselinux.so", RTLD_NOW));
+ if (selinux_handle == nullptr) {
+ // Assume everything's OK if we can't open the library.
+ return true;
+ }
+ dlerror(); // Clean dlerror string.
+
+ void* restorecon_ptr = dlsym(selinux_handle.get(), "selinux_android_restorecon");
+ if (restorecon_ptr == nullptr) {
+ // Can't find the relabel function. That's bad. Make sure the zygote fails, as we have no
+ // other recourse to make this error obvious.
+ const char* error_string = dlerror();
+ LOG(FATAL) << "Could not find selinux restorecon function: "
+ << ((error_string != nullptr) ? error_string : "(unknown error)");
+ UNREACHABLE();
+ }
+
+ using RestoreconFn = int (*)(const char*, unsigned int);
+ constexpr unsigned int kRecursive = 4U;
+
+ RestoreconFn restorecon_fn = reinterpret_cast<RestoreconFn>(restorecon_ptr);
+ if (restorecon_fn(dalvik_cache_dir.c_str(), kRecursive) != 0) {
+ LOG(ERROR) << "Failed to restorecon " << dalvik_cache_dir;
+ return false;
+ }
+
+ return true;
+}
+
+} // namespace impl
+
+
+// We are relocating or generating the core image. We should get rid of everything. It is all
+// out-of-date. We also don't really care if this fails since it is just a convenience.
+// Adapted from prune_dex_cache(const char* subdir) in frameworks/native/cmds/installd/commands.c
+// Note this should only be used during first boot.
+static void PruneDalvikCache(InstructionSet isa) {
+ CHECK_NE(isa, kNone);
+ // Prune the base /data/dalvik-cache.
+ impl::DeleteDirectoryContents(GetDalvikCacheOrDie(".", false), false);
+ // Prune /data/dalvik-cache/<isa>.
+ impl::DeleteDirectoryContents(GetDalvikCacheOrDie(GetInstructionSetString(isa), false), false);
+}
+
+// We write out an empty file to the zygote's ISA specific cache dir at the start of
+// every zygote boot and delete it when the boot completes. If we find a file already
+// present, it usually means the boot didn't complete. We wipe the entire dalvik
+// cache if that's the case.
+static void MarkZygoteStart(const InstructionSet isa, const uint32_t max_failed_boots) {
+ const std::string isa_subdir = GetDalvikCacheOrDie(GetInstructionSetString(isa), false);
+ const std::string boot_marker = isa_subdir + "/.booting";
+ const char* file_name = boot_marker.c_str();
+
+ uint32_t num_failed_boots = 0;
+ std::unique_ptr<File> file(OS::OpenFileReadWrite(file_name));
+ if (file.get() == nullptr) {
+ file.reset(OS::CreateEmptyFile(file_name));
+
+ if (file.get() == nullptr) {
+ PLOG(WARNING) << "Failed to create boot marker.";
+ return;
+ }
+ } else {
+ if (!file->ReadFully(&num_failed_boots, sizeof(num_failed_boots))) {
+ PLOG(WARNING) << "Failed to read boot marker.";
+ file->Erase();
+ return;
+ }
+ }
+
+ if (max_failed_boots != 0 && num_failed_boots > max_failed_boots) {
+ LOG(WARNING) << "Incomplete boot detected. Pruning dalvik cache";
+ impl::DeleteDirectoryContents(isa_subdir, false);
+ }
+
+ ++num_failed_boots;
+ VLOG(startup) << "Number of failed boots on : " << boot_marker << " = " << num_failed_boots;
+
+ if (lseek(file->Fd(), 0, SEEK_SET) == -1) {
+ PLOG(WARNING) << "Failed to write boot marker.";
+ file->Erase();
+ return;
+ }
+
+ if (!file->WriteFully(&num_failed_boots, sizeof(num_failed_boots))) {
+ PLOG(WARNING) << "Failed to write boot marker.";
+ file->Erase();
+ return;
+ }
+
+ if (file->FlushCloseOrErase() != 0) {
+ PLOG(WARNING) << "Failed to flush boot marker.";
+ }
+}
+
+static void TryMoveOTAArtifacts(const std::string& cache_filename, bool dalvik_cache_exists) {
+ // We really assume here global means /data/dalvik-cache, and we'll inject 'ota.' Make sure
+ // that's true.
+ CHECK(StartsWith(cache_filename, "/data/dalvik-cache")) << cache_filename;
+
+ // Inject ota subdirectory.
+ std::string ota_filename(cache_filename);
+ ota_filename = ota_filename.insert(strlen("/data/"), "ota/");
+ CHECK(StartsWith(ota_filename, "/data/ota/dalvik-cache")) << ota_filename;
+
+ // See if the file exists.
+ if (OS::FileExists(ota_filename.c_str())) {
+ VLOG(startup) << "OTA directory does exist, checking for artifacts";
+
+ size_t last_slash = ota_filename.rfind('/');
+ CHECK_NE(last_slash, std::string::npos);
+ std::string ota_source_dir = ota_filename.substr(0, last_slash);
+
+ // We need the dalvik cache now, really.
+ if (dalvik_cache_exists) {
+ size_t last_cache_slash = cache_filename.rfind('/');
+ DCHECK_NE(last_cache_slash, std::string::npos);
+ std::string dalvik_cache_target_dir = cache_filename.substr(0, last_cache_slash);
+
+ // First clean the target cache.
+ impl::DeleteDirectoryContents(dalvik_cache_target_dir.c_str(), false);
+
+ // Now move things over.
+ impl::MoveOTAArtifacts(ota_source_dir.c_str(), dalvik_cache_target_dir.c_str());
+
+ // Last step: ensure the files have the right selinux label.
+ if (!impl::RelabelOTAFiles(dalvik_cache_target_dir)) {
+ // This isn't good. We potentially moved files, but they have the wrong label. Delete the
+ // files.
+ LOG(WARNING) << "Could not relabel files, must delete dalvik-cache.";
+ impl::DeleteDirectoryContents(dalvik_cache_target_dir.c_str(), false);
+ }
+ }
+
+ // Cleanup.
+ impl::DeleteDirectoryContents(ota_source_dir.c_str(), true);
+ impl::DeleteEmptyDirectoriesUpTo(ota_source_dir, "ota");
+ } else {
+ VLOG(startup) << "No OTA directory.";
+ }
+}
+
+} // namespace space
+} // namespace gc
+} // namespace art
+
+#endif // ART_RUNTIME_GC_SPACE_IMAGE_SPACE_FS_H_
diff --git a/runtime/jit/debugger_interface.cc b/runtime/jit/debugger_interface.cc
index f08a1a9d90..d9d7a19912 100644
--- a/runtime/jit/debugger_interface.cc
+++ b/runtime/jit/debugger_interface.cc
@@ -58,6 +58,10 @@ extern "C" {
__asm__("");
}
+ // Call __jit_debug_register_code indirectly via global variable.
+ // This gives the debugger an easy way to inject custom code to handle the events.
+ void (*__jit_debug_register_code_ptr)() = __jit_debug_register_code;
+
// GDB will inspect contents of this descriptor.
// Static initialization is necessary to prevent GDB from seeing
// uninitialized descriptor.
@@ -85,7 +89,7 @@ static JITCodeEntry* CreateJITCodeEntryInternal(
__jit_debug_descriptor.relevant_entry_ = entry;
__jit_debug_descriptor.action_flag_ = JIT_REGISTER_FN;
- __jit_debug_register_code();
+ (*__jit_debug_register_code_ptr)();
return entry;
}
@@ -102,7 +106,7 @@ static void DeleteJITCodeEntryInternal(JITCodeEntry* entry) REQUIRES(g_jit_debug
__jit_debug_descriptor.relevant_entry_ = entry;
__jit_debug_descriptor.action_flag_ = JIT_UNREGISTER_FN;
- __jit_debug_register_code();
+ (*__jit_debug_register_code_ptr)();
delete[] entry->symfile_addr_;
delete entry;
}
diff --git a/runtime/jit/jit.cc b/runtime/jit/jit.cc
index 5bd9a6b76b..7e73e5c7f1 100644
--- a/runtime/jit/jit.cc
+++ b/runtime/jit/jit.cc
@@ -83,6 +83,8 @@ JitOptions* JitOptions::CreateFromRuntimeArguments(const RuntimeArgumentMap& opt
void Jit::DumpInfo(std::ostream& os) {
code_cache_->Dump(os);
cumulative_timings_.Dump(os);
+ MutexLock mu(Thread::Current(), lock_);
+ memory_use_.PrintMemoryUse(os);
}
void Jit::AddTimingLogger(const TimingLogger& logger) {
@@ -95,6 +97,8 @@ Jit::Jit() : jit_library_handle_(nullptr),
jit_compile_method_(nullptr),
dump_info_on_shutdown_(false),
cumulative_timings_("JIT timings"),
+ memory_use_("Memory used for compilation", 16),
+ lock_("JIT memory use lock"),
save_profiling_info_(false),
generate_debug_info_(false) {
}
@@ -433,5 +437,16 @@ bool Jit::MaybeDoOnStackReplacement(Thread* thread,
return true;
}
+void Jit::AddMemoryUsage(ArtMethod* method, size_t bytes) {
+ if (bytes > 4 * MB) {
+ LOG(INFO) << "Compiler allocated "
+ << PrettySize(bytes)
+ << " to compile "
+ << PrettyMethod(method);
+ }
+ MutexLock mu(Thread::Current(), lock_);
+ memory_use_.AddValue(bytes);
+}
+
} // namespace jit
} // namespace art
diff --git a/runtime/jit/jit.h b/runtime/jit/jit.h
index d5c213416a..37d0bdb129 100644
--- a/runtime/jit/jit.h
+++ b/runtime/jit/jit.h
@@ -17,14 +17,11 @@
#ifndef ART_RUNTIME_JIT_JIT_H_
#define ART_RUNTIME_JIT_JIT_H_
-#include <unordered_map>
-
-#include "atomic.h"
+#include "base/arena_allocator.h"
+#include "base/histogram-inl.h"
#include "base/macros.h"
#include "base/mutex.h"
#include "base/timing_logger.h"
-#include "gc_root.h"
-#include "jni.h"
#include "object_callbacks.h"
#include "offline_profiling_info.h"
#include "thread_pool.h"
@@ -62,9 +59,14 @@ class Jit {
void DeleteThreadPool();
// Dump interesting info: #methods compiled, code vs data size, compile / verify cumulative
// loggers.
- void DumpInfo(std::ostream& os);
+ void DumpInfo(std::ostream& os) REQUIRES(!lock_);
// Add a timing logger to cumulative_timings_.
void AddTimingLogger(const TimingLogger& logger);
+
+ void AddMemoryUsage(ArtMethod* method, size_t bytes)
+ REQUIRES(!lock_)
+ SHARED_REQUIRES(Locks::mutator_lock_);
+
JitInstrumentationCache* GetInstrumentationCache() const {
return instrumentation_cache_.get();
}
@@ -82,7 +84,7 @@ class Jit {
const std::string& app_dir);
void StopProfileSaver();
- void DumpForSigQuit(std::ostream& os) {
+ void DumpForSigQuit(std::ostream& os) REQUIRES(!lock_) {
DumpInfo(os);
}
@@ -125,6 +127,8 @@ class Jit {
// Performance monitoring.
bool dump_info_on_shutdown_;
CumulativeLogger cumulative_timings_;
+ Histogram<uint64_t> memory_use_ GUARDED_BY(lock_);
+ Mutex lock_ DEFAULT_MUTEX_ACQUIRED_AFTER;
std::unique_ptr<jit::JitInstrumentationCache> instrumentation_cache_;
std::unique_ptr<jit::JitCodeCache> code_cache_;
diff --git a/runtime/jit/jit_code_cache.cc b/runtime/jit/jit_code_cache.cc
index af47da63c4..c681ed77f2 100644
--- a/runtime/jit/jit_code_cache.cc
+++ b/runtime/jit/jit_code_cache.cc
@@ -40,6 +40,9 @@ static constexpr int kProtAll = PROT_READ | PROT_WRITE | PROT_EXEC;
static constexpr int kProtData = PROT_READ | PROT_WRITE;
static constexpr int kProtCode = PROT_READ | PROT_EXEC;
+static constexpr size_t kCodeSizeLogThreshold = 50 * KB;
+static constexpr size_t kStackMapSizeLogThreshold = 50 * KB;
+
#define CHECKED_MPROTECT(memory, size, prot) \
do { \
int rc = mprotect(memory, size, prot); \
@@ -134,7 +137,10 @@ JitCodeCache::JitCodeCache(MemMap* code_map,
number_of_compilations_(0),
number_of_osr_compilations_(0),
number_of_deoptimizations_(0),
- number_of_collections_(0) {
+ number_of_collections_(0),
+ histogram_stack_map_memory_use_("Memory used for stack maps", 16),
+ histogram_code_memory_use_("Memory used for compiled code", 16),
+ histogram_profiling_info_memory_use_("Memory used for profiling info", 16) {
DCHECK_GE(max_capacity, initial_code_capacity + initial_data_capacity);
code_mspace_ = create_mspace_with_base(code_map_->Begin(), code_end_, false /*locked*/);
@@ -377,6 +383,13 @@ uint8_t* JitCodeCache::CommitCodeInternal(Thread* self,
<< " dcache_size=" << PrettySize(DataCacheSizeLocked()) << ": "
<< reinterpret_cast<const void*>(method_header->GetEntryPoint()) << ","
<< reinterpret_cast<const void*>(method_header->GetEntryPoint() + method_header->code_size_);
+ histogram_code_memory_use_.AddValue(code_size);
+ if (code_size > kCodeSizeLogThreshold) {
+ LOG(INFO) << "JIT allocated "
+ << PrettySize(code_size)
+ << " for compiled code of "
+ << PrettyMethod(method);
+ }
}
return reinterpret_cast<uint8_t*>(method_header);
@@ -405,7 +418,7 @@ void JitCodeCache::ClearData(Thread* self, void* data) {
FreeData(reinterpret_cast<uint8_t*>(data));
}
-uint8_t* JitCodeCache::ReserveData(Thread* self, size_t size) {
+uint8_t* JitCodeCache::ReserveData(Thread* self, size_t size, ArtMethod* method) {
size = RoundUp(size, sizeof(void*));
uint8_t* result = nullptr;
@@ -425,15 +438,14 @@ uint8_t* JitCodeCache::ReserveData(Thread* self, size_t size) {
result = AllocateData(size);
}
- return result;
-}
-
-uint8_t* JitCodeCache::AddDataArray(Thread* self, const uint8_t* begin, const uint8_t* end) {
- uint8_t* result = ReserveData(self, end - begin);
- if (result == nullptr) {
- return nullptr; // Out of space in the data cache.
+ MutexLock mu(self, lock_);
+ histogram_stack_map_memory_use_.AddValue(size);
+ if (size > kStackMapSizeLogThreshold) {
+ LOG(INFO) << "JIT allocated "
+ << PrettySize(size)
+ << " for stack maps of "
+ << PrettyMethod(method);
}
- std::copy(begin, end, result);
return result;
}
@@ -868,6 +880,7 @@ ProfilingInfo* JitCodeCache::AddProfilingInfoInternal(Thread* self ATTRIBUTE_UNU
method->SetProfilingInfo(info);
profiling_infos_.push_back(info);
+ histogram_profiling_info_memory_use_.AddValue(profile_info_size);
return info;
}
@@ -1021,6 +1034,9 @@ void JitCodeCache::Dump(std::ostream& os) {
<< number_of_osr_compilations_ << "\n"
<< "Total number of deoptimizations: " << number_of_deoptimizations_ << "\n"
<< "Total number of JIT code cache collections: " << number_of_collections_ << std::endl;
+ histogram_stack_map_memory_use_.PrintMemoryUse(os);
+ histogram_code_memory_use_.PrintMemoryUse(os);
+ histogram_profiling_info_memory_use_.PrintMemoryUse(os);
}
} // namespace jit
diff --git a/runtime/jit/jit_code_cache.h b/runtime/jit/jit_code_cache.h
index 98dd70dcf9..a54f04faa4 100644
--- a/runtime/jit/jit_code_cache.h
+++ b/runtime/jit/jit_code_cache.h
@@ -20,6 +20,7 @@
#include "instrumentation.h"
#include "atomic.h"
+#include "base/histogram-inl.h"
#include "base/macros.h"
#include "base/mutex.h"
#include "gc/accounting/bitmap.h"
@@ -109,7 +110,7 @@ class JitCodeCache {
bool ContainsMethod(ArtMethod* method) REQUIRES(!lock_);
// Reserve a region of data of size at least "size". Returns null if there is no more room.
- uint8_t* ReserveData(Thread* self, size_t size)
+ uint8_t* ReserveData(Thread* self, size_t size, ArtMethod* method)
SHARED_REQUIRES(Locks::mutator_lock_)
REQUIRES(!lock_);
@@ -118,12 +119,6 @@ class JitCodeCache {
SHARED_REQUIRES(Locks::mutator_lock_)
REQUIRES(!lock_);
- // Add a data array of size (end - begin) with the associated contents, returns null if there
- // is no more room.
- uint8_t* AddDataArray(Thread* self, const uint8_t* begin, const uint8_t* end)
- SHARED_REQUIRES(Locks::mutator_lock_)
- REQUIRES(!lock_);
-
CodeCacheBitmap* GetLiveBitmap() const {
return live_bitmap_.get();
}
@@ -332,6 +327,15 @@ class JitCodeCache {
// Number of code cache collections done throughout the lifetime of the JIT.
size_t number_of_collections_ GUARDED_BY(lock_);
+ // Histograms for keeping track of stack map size statistics.
+ Histogram<uint64_t> histogram_stack_map_memory_use_ GUARDED_BY(lock_);
+
+ // Histograms for keeping track of code size statistics.
+ Histogram<uint64_t> histogram_code_memory_use_ GUARDED_BY(lock_);
+
+ // Histograms for keeping track of profiling info statistics.
+ Histogram<uint64_t> histogram_profiling_info_memory_use_ GUARDED_BY(lock_);
+
DISALLOW_IMPLICIT_CONSTRUCTORS(JitCodeCache);
};
diff --git a/runtime/native/java_lang_Runtime.cc b/runtime/native/java_lang_Runtime.cc
deleted file mode 100644
index df794e1249..0000000000
--- a/runtime/native/java_lang_Runtime.cc
+++ /dev/null
@@ -1,134 +0,0 @@
-/*
- * Copyright (C) 2008 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 "java_lang_Runtime.h"
-
-#include <dlfcn.h>
-#include <limits.h>
-#include <unistd.h>
-
-#include "base/macros.h"
-#include "gc/heap.h"
-#include "handle_scope-inl.h"
-#include "jni_internal.h"
-#include "mirror/class_loader.h"
-#include "runtime.h"
-#include "scoped_thread_state_change.h"
-#include "ScopedUtfChars.h"
-#include "verify_object-inl.h"
-
-#include <sstream>
-#ifdef __ANDROID__
-// This function is provided by android linker.
-extern "C" void android_update_LD_LIBRARY_PATH(const char* ld_library_path);
-#endif // __ANDROID__
-
-namespace art {
-
-static void Runtime_gc(JNIEnv*, jclass) {
- if (Runtime::Current()->IsExplicitGcDisabled()) {
- LOG(INFO) << "Explicit GC skipped.";
- return;
- }
- Runtime::Current()->GetHeap()->CollectGarbage(false);
-}
-
-NO_RETURN static void Runtime_nativeExit(JNIEnv*, jclass, jint status) {
- LOG(INFO) << "System.exit called, status: " << status;
- Runtime::Current()->CallExitHook(status);
- exit(status);
-}
-
-static void SetLdLibraryPath(JNIEnv* env, jstring javaLdLibraryPath) {
-#ifdef __ANDROID__
- if (javaLdLibraryPath != nullptr) {
- ScopedUtfChars ldLibraryPath(env, javaLdLibraryPath);
- if (ldLibraryPath.c_str() != nullptr) {
- android_update_LD_LIBRARY_PATH(ldLibraryPath.c_str());
- }
- }
-
-#else
- LOG(WARNING) << "android_update_LD_LIBRARY_PATH not found; .so dependencies will not work!";
- UNUSED(javaLdLibraryPath, env);
-#endif
-}
-
-static jstring Runtime_nativeLoad(JNIEnv* env,
- jclass,
- jstring javaFilename,
- jobject javaLoader,
- jstring javaLibrarySearchPath) {
- ScopedUtfChars filename(env, javaFilename);
- if (filename.c_str() == nullptr) {
- return nullptr;
- }
-
- int32_t target_sdk_version = Runtime::Current()->GetTargetSdkVersion();
-
- // Starting with N nativeLoad uses classloader local
- // linker namespace instead of global LD_LIBRARY_PATH
- // (23 is Marshmallow). This call is here to preserve
- // backwards compatibility for the apps targeting sdk
- // version <= 23
- if (target_sdk_version == 0) {
- SetLdLibraryPath(env, javaLibrarySearchPath);
- }
-
- std::string error_msg;
- {
- JavaVMExt* vm = Runtime::Current()->GetJavaVM();
- bool success = vm->LoadNativeLibrary(env,
- filename.c_str(),
- javaLoader,
- javaLibrarySearchPath,
- &error_msg);
- if (success) {
- return nullptr;
- }
- }
-
- // Don't let a pending exception from JNI_OnLoad cause a CheckJNI issue with NewStringUTF.
- env->ExceptionClear();
- return env->NewStringUTF(error_msg.c_str());
-}
-
-static jlong Runtime_maxMemory(JNIEnv*, jclass) {
- return Runtime::Current()->GetHeap()->GetMaxMemory();
-}
-
-static jlong Runtime_totalMemory(JNIEnv*, jclass) {
- return Runtime::Current()->GetHeap()->GetTotalMemory();
-}
-
-static jlong Runtime_freeMemory(JNIEnv*, jclass) {
- return Runtime::Current()->GetHeap()->GetFreeMemory();
-}
-
-static JNINativeMethod gMethods[] = {
- NATIVE_METHOD(Runtime, freeMemory, "!()J"),
- NATIVE_METHOD(Runtime, gc, "()V"),
- NATIVE_METHOD(Runtime, maxMemory, "!()J"),
- NATIVE_METHOD(Runtime, nativeExit, "(I)V"),
- NATIVE_METHOD(Runtime, nativeLoad, "(Ljava/lang/String;Ljava/lang/ClassLoader;Ljava/lang/String;)Ljava/lang/String;"),
- NATIVE_METHOD(Runtime, totalMemory, "!()J"),
-};
-
-void register_java_lang_Runtime(JNIEnv* env) {
- REGISTER_NATIVE_METHODS("java/lang/Runtime");
-}
-
-} // namespace art
diff --git a/runtime/oat_file.cc b/runtime/oat_file.cc
index 7155c79afb..033ea563b4 100644
--- a/runtime/oat_file.cc
+++ b/runtime/oat_file.cc
@@ -89,6 +89,7 @@ class OatFileBase : public OatFile {
uint8_t* oat_file_begin,
bool writable,
bool executable,
+ bool low_4gb,
const char* abs_dex_location,
std::string* error_msg);
@@ -102,6 +103,7 @@ class OatFileBase : public OatFile {
uint8_t* oat_file_begin,
bool writable,
bool executable,
+ bool low_4gb,
std::string* error_msg) = 0;
bool ComputeFields(uint8_t* requested_base,
@@ -133,6 +135,7 @@ OatFileBase* OatFileBase::OpenOatFile(const std::string& elf_filename,
uint8_t* oat_file_begin,
bool writable,
bool executable,
+ bool low_4gb,
const char* abs_dex_location,
std::string* error_msg) {
std::unique_ptr<OatFileBase> ret(new kOatFileBaseSubType(location, executable));
@@ -140,6 +143,7 @@ OatFileBase* OatFileBase::OpenOatFile(const std::string& elf_filename,
oat_file_begin,
writable,
executable,
+ low_4gb,
error_msg)) {
return nullptr;
}
@@ -147,7 +151,6 @@ OatFileBase* OatFileBase::OpenOatFile(const std::string& elf_filename,
if (!ret->ComputeFields(requested_base, elf_filename, error_msg)) {
return nullptr;
}
-
ret->PreSetup(elf_filename);
if (!ret->Setup(abs_dex_location, error_msg)) {
@@ -532,6 +535,7 @@ class DlOpenOatFile FINAL : public OatFileBase {
uint8_t* oat_file_begin,
bool writable,
bool executable,
+ bool low_4gb,
std::string* error_msg) OVERRIDE;
// Ask the linker where it mmaped the file and notify our mmap wrapper of the regions.
@@ -558,6 +562,7 @@ bool DlOpenOatFile::Load(const std::string& elf_filename,
uint8_t* oat_file_begin,
bool writable,
bool executable,
+ bool low_4gb,
std::string* error_msg) {
// Use dlopen only when flagged to do so, and when it's OK to load things executable.
// TODO: Also try when not executable? The issue here could be re-mapping as writable (as
@@ -567,6 +572,10 @@ bool DlOpenOatFile::Load(const std::string& elf_filename,
*error_msg = "DlOpen is disabled.";
return false;
}
+ if (low_4gb) {
+ *error_msg = "DlOpen does not support low 4gb loading.";
+ return false;
+ }
if (writable) {
*error_msg = "DlOpen does not support writable loading.";
return false;
@@ -702,6 +711,7 @@ class ElfOatFile FINAL : public OatFileBase {
uint8_t* oat_file_begin, // Override base if not null
bool writable,
bool executable,
+ bool low_4gb,
const char* abs_dex_location,
std::string* error_msg);
@@ -723,6 +733,7 @@ class ElfOatFile FINAL : public OatFileBase {
uint8_t* oat_file_begin, // Override where the file is loaded to if not null
bool writable,
bool executable,
+ bool low_4gb,
std::string* error_msg) OVERRIDE;
void PreSetup(const std::string& elf_filename ATTRIBUTE_UNUSED) OVERRIDE {
@@ -733,6 +744,7 @@ class ElfOatFile FINAL : public OatFileBase {
uint8_t* oat_file_begin, // Override where the file is loaded to if not null
bool writable,
bool executable,
+ bool low_4gb,
std::string* error_msg);
private:
@@ -748,11 +760,17 @@ ElfOatFile* ElfOatFile::OpenElfFile(File* file,
uint8_t* oat_file_begin, // Override base if not null
bool writable,
bool executable,
+ bool low_4gb,
const char* abs_dex_location,
std::string* error_msg) {
ScopedTrace trace("Open elf file " + location);
std::unique_ptr<ElfOatFile> oat_file(new ElfOatFile(location, executable));
- bool success = oat_file->ElfFileOpen(file, oat_file_begin, writable, executable, error_msg);
+ bool success = oat_file->ElfFileOpen(file,
+ oat_file_begin,
+ writable,
+ low_4gb,
+ executable,
+ error_msg);
if (!success) {
CHECK(!error_msg->empty());
return nullptr;
@@ -792,6 +810,7 @@ bool ElfOatFile::Load(const std::string& elf_filename,
uint8_t* oat_file_begin, // Override where the file is loaded to if not null
bool writable,
bool executable,
+ bool low_4gb,
std::string* error_msg) {
ScopedTrace trace(__PRETTY_FUNCTION__);
std::unique_ptr<File> file(OS::OpenFileForReading(elf_filename.c_str()));
@@ -803,6 +822,7 @@ bool ElfOatFile::Load(const std::string& elf_filename,
oat_file_begin,
writable,
executable,
+ low_4gb,
error_msg);
}
@@ -810,19 +830,21 @@ bool ElfOatFile::ElfFileOpen(File* file,
uint8_t* oat_file_begin,
bool writable,
bool executable,
+ bool low_4gb,
std::string* error_msg) {
ScopedTrace trace(__PRETTY_FUNCTION__);
// TODO: rename requested_base to oat_data_begin
elf_file_.reset(ElfFile::Open(file,
writable,
/*program_header_only*/true,
+ low_4gb,
error_msg,
oat_file_begin));
if (elf_file_ == nullptr) {
DCHECK(!error_msg->empty());
return false;
}
- bool loaded = elf_file_->Load(executable, error_msg);
+ bool loaded = elf_file_->Load(executable, low_4gb, error_msg);
DCHECK(loaded || !error_msg->empty());
return loaded;
}
@@ -870,6 +892,7 @@ OatFile* OatFile::Open(const std::string& filename,
uint8_t* requested_base,
uint8_t* oat_file_begin,
bool executable,
+ bool low_4gb,
const char* abs_dex_location,
std::string* error_msg) {
ScopedTrace trace("Open oat file " + location);
@@ -885,15 +908,15 @@ OatFile* OatFile::Open(const std::string& filename,
oat_file_begin,
false,
executable,
+ low_4gb,
abs_dex_location,
error_msg);
if (with_dlopen != nullptr) {
return with_dlopen;
}
if (kPrintDlOpenErrorMessage) {
- LOG(ERROR) << "Failed to dlopen: " << *error_msg;
+ LOG(ERROR) << "Failed to dlopen: " << filename << " with error " << *error_msg;
}
-
// If we aren't trying to execute, we just use our own ElfFile loader for a couple reasons:
//
// On target, dlopen may fail when compiling due to selinux restrictions on installd.
@@ -913,6 +936,7 @@ OatFile* OatFile::Open(const std::string& filename,
oat_file_begin,
false,
executable,
+ low_4gb,
abs_dex_location,
error_msg);
return with_internal;
@@ -929,6 +953,7 @@ OatFile* OatFile::OpenWritable(File* file,
nullptr,
true,
false,
+ /*low_4gb*/false,
abs_dex_location,
error_msg);
}
@@ -944,6 +969,7 @@ OatFile* OatFile::OpenReadable(File* file,
nullptr,
false,
false,
+ /*low_4gb*/false,
abs_dex_location,
error_msg);
}
diff --git a/runtime/oat_file.h b/runtime/oat_file.h
index 1084253a88..7af77aee8f 100644
--- a/runtime/oat_file.h
+++ b/runtime/oat_file.h
@@ -64,6 +64,7 @@ class OatFile {
uint8_t* requested_base,
uint8_t* oat_file_begin,
bool executable,
+ bool low_4gb,
const char* abs_dex_location,
std::string* error_msg);
diff --git a/runtime/oat_file_assistant.cc b/runtime/oat_file_assistant.cc
index 90712c625c..cbc0ec6d28 100644
--- a/runtime/oat_file_assistant.cc
+++ b/runtime/oat_file_assistant.cc
@@ -815,8 +815,13 @@ const OatFile* OatFileAssistant::GetOdexFile() {
const std::string& odex_file_name = *OdexFileName();
std::string error_msg;
cached_odex_file_.reset(OatFile::Open(odex_file_name.c_str(),
- odex_file_name.c_str(), nullptr, nullptr, load_executable_,
- dex_location_.c_str(), &error_msg));
+ odex_file_name.c_str(),
+ nullptr,
+ nullptr,
+ load_executable_,
+ /*low_4gb*/false,
+ dex_location_.c_str(),
+ &error_msg));
if (cached_odex_file_.get() == nullptr) {
VLOG(oat) << "OatFileAssistant test for existing pre-compiled oat file "
<< odex_file_name << ": " << error_msg;
@@ -846,8 +851,13 @@ const OatFile* OatFileAssistant::GetOatFile() {
const std::string& oat_file_name = *OatFileName();
std::string error_msg;
cached_oat_file_.reset(OatFile::Open(oat_file_name.c_str(),
- oat_file_name.c_str(), nullptr, nullptr, load_executable_,
- dex_location_.c_str(), &error_msg));
+ oat_file_name.c_str(),
+ nullptr,
+ nullptr,
+ load_executable_,
+ /*low_4gb*/false,
+ dex_location_.c_str(),
+ &error_msg));
if (cached_oat_file_.get() == nullptr) {
VLOG(oat) << "OatFileAssistant test for existing oat file "
<< oat_file_name << ": " << error_msg;
diff --git a/runtime/oat_file_assistant_test.cc b/runtime/oat_file_assistant_test.cc
index 4541468cb3..046d8ae779 100644
--- a/runtime/oat_file_assistant_test.cc
+++ b/runtime/oat_file_assistant_test.cc
@@ -218,9 +218,14 @@ class OatFileAssistantTest : public CommonRuntimeTest {
// Verify the odex file was generated as expected and really is
// unrelocated.
- std::unique_ptr<OatFile> odex_file(OatFile::Open(
- odex_location.c_str(), odex_location.c_str(), nullptr, nullptr,
- false, dex_location.c_str(), &error_msg));
+ std::unique_ptr<OatFile> odex_file(OatFile::Open(odex_location.c_str(),
+ odex_location.c_str(),
+ nullptr,
+ nullptr,
+ false,
+ /*low_4gb*/false,
+ dex_location.c_str(),
+ &error_msg));
ASSERT_TRUE(odex_file.get() != nullptr) << error_msg;
const std::vector<gc::space::ImageSpace*> image_spaces =
@@ -252,9 +257,14 @@ class OatFileAssistantTest : public CommonRuntimeTest {
setenv("ANDROID_DATA", android_data_.c_str(), 1);
// Verify the odex file was generated as expected.
- std::unique_ptr<OatFile> odex_file(OatFile::Open(
- odex_location.c_str(), odex_location.c_str(), nullptr, nullptr,
- false, dex_location.c_str(), &error_msg));
+ std::unique_ptr<OatFile> odex_file(OatFile::Open(odex_location.c_str(),
+ odex_location.c_str(),
+ nullptr,
+ nullptr,
+ false,
+ /*low_4gb*/false,
+ dex_location.c_str(),
+ &error_msg));
ASSERT_TRUE(odex_file.get() != nullptr) << error_msg;
EXPECT_TRUE(odex_file->IsPic());
}
@@ -269,9 +279,14 @@ class OatFileAssistantTest : public CommonRuntimeTest {
ASSERT_TRUE(OatFileAssistant::Dex2Oat(args, &error_msg)) << error_msg;
// Verify the odex file was generated as expected.
- std::unique_ptr<OatFile> odex_file(OatFile::Open(
- odex_location.c_str(), odex_location.c_str(), nullptr, nullptr,
- false, dex_location.c_str(), &error_msg));
+ std::unique_ptr<OatFile> odex_file(OatFile::Open(odex_location.c_str(),
+ odex_location.c_str(),
+ nullptr,
+ nullptr,
+ false,
+ /*low_4gb*/false,
+ dex_location.c_str(),
+ &error_msg));
ASSERT_TRUE(odex_file.get() != nullptr) << error_msg;
EXPECT_TRUE(odex_file->IsExtractOnly());
EXPECT_EQ(odex_file->GetOatHeader().GetImageFileLocationOatChecksum(), 0u);
@@ -290,9 +305,14 @@ class OatFileAssistantTest : public CommonRuntimeTest {
ASSERT_TRUE(OatFileAssistant::Dex2Oat(args, &error_msg)) << error_msg;
// Verify the odex file was generated as expected.
- std::unique_ptr<OatFile> odex_file(OatFile::Open(
- odex_location.c_str(), odex_location.c_str(), nullptr, nullptr,
- false, dex_location.c_str(), &error_msg));
+ std::unique_ptr<OatFile> odex_file(OatFile::Open(odex_location.c_str(),
+ odex_location.c_str(),
+ nullptr,
+ nullptr,
+ false,
+ /*low_4gb*/false,
+ dex_location.c_str(),
+ &error_msg));
printf("error %s", error_msg.c_str());
ASSERT_TRUE(odex_file.get() != nullptr) << error_msg;
EXPECT_TRUE(odex_file->IsProfileGuideCompiled());
diff --git a/runtime/openjdkjvm/OpenjdkJvm.cc b/runtime/openjdkjvm/OpenjdkJvm.cc
index d377457eb2..aff9b61f3a 100644
--- a/runtime/openjdkjvm/OpenjdkJvm.cc
+++ b/runtime/openjdkjvm/OpenjdkJvm.cc
@@ -342,15 +342,15 @@ JNIEXPORT jstring JVM_NativeLoad(JNIEnv* env,
// Starting with N nativeLoad uses classloader local
// linker namespace instead of global LD_LIBRARY_PATH
- // (23 is Marshmallow)
- if (target_sdk_version <= 23) {
+ // (23 is Marshmallow). This call is here to preserve
+ // backwards compatibility for the apps targeting sdk
+ // version <= 23
+ if (target_sdk_version == 0) {
SetLdLibraryPath(env, javaLibrarySearchPath);
}
std::string error_msg;
{
- art::ScopedObjectAccess soa(env);
- art::StackHandleScope<1> hs(soa.Self());
art::JavaVMExt* vm = art::Runtime::Current()->GetJavaVM();
bool success = vm->LoadNativeLibrary(env,
filename.c_str(),
diff --git a/runtime/primitive.h b/runtime/primitive.h
index 2454a2117b..9c19ad5772 100644
--- a/runtime/primitive.h
+++ b/runtime/primitive.h
@@ -166,6 +166,20 @@ class Primitive {
return type == kPrimLong || type == kPrimDouble;
}
+ // Return the general kind of `type`, fusing integer-like types as kPrimInt.
+ static Type PrimitiveKind(Type type) {
+ switch (type) {
+ case kPrimBoolean:
+ case kPrimByte:
+ case kPrimShort:
+ case kPrimChar:
+ case kPrimInt:
+ return kPrimInt;
+ default:
+ return type;
+ }
+ }
+
private:
DISALLOW_IMPLICIT_CONSTRUCTORS(Primitive);
};
diff --git a/runtime/runtime.cc b/runtime/runtime.cc
index fd6cc100eb..c8085fbfc7 100644
--- a/runtime/runtime.cc
+++ b/runtime/runtime.cc
@@ -94,7 +94,6 @@
#include "native/java_lang_Class.h"
#include "native/java_lang_DexCache.h"
#include "native/java_lang_Object.h"
-#include "native/java_lang_Runtime.h"
#include "native/java_lang_String.h"
#include "native/java_lang_StringFactory.h"
#include "native/java_lang_System.h"
@@ -810,7 +809,11 @@ static bool OpenDexFilesFromImage(const std::string& image_location,
return false;
}
std::string error_msg;
- std::unique_ptr<ElfFile> elf_file(ElfFile::Open(file.release(), false, false, &error_msg));
+ std::unique_ptr<ElfFile> elf_file(ElfFile::Open(file.release(),
+ false,
+ false,
+ /*low_4gb*/false,
+ &error_msg));
if (elf_file.get() == nullptr) {
return false;
}
@@ -1336,7 +1339,6 @@ void Runtime::RegisterRuntimeNativeMethods(JNIEnv* env) {
register_java_lang_reflect_Method(env);
register_java_lang_reflect_Proxy(env);
register_java_lang_ref_Reference(env);
- register_java_lang_Runtime(env);
register_java_lang_String(env);
register_java_lang_StringFactory(env);
register_java_lang_System(env);
@@ -1866,7 +1868,7 @@ void Runtime::SetFaultMessage(const std::string& message) {
void Runtime::AddCurrentRuntimeFeaturesAsDex2OatArguments(std::vector<std::string>* argv)
const {
- if (GetInstrumentation()->InterpretOnly()) {
+ if (GetInstrumentation()->InterpretOnly() || UseJit()) {
argv->push_back("--compiler-filter=interpret-only");
}
diff --git a/test/458-checker-instruction-simplification/src/Main.java b/test/458-checker-instruction-simplification/src/Main.java
index dd4ffe45e4..53c2e0b820 100644
--- a/test/458-checker-instruction-simplification/src/Main.java
+++ b/test/458-checker-instruction-simplification/src/Main.java
@@ -414,6 +414,23 @@ public class Main {
return arg >> 0;
}
+ /// CHECK-START: long Main.Shr64(long) instruction_simplifier (before)
+ /// CHECK-DAG: <<Arg:j\d+>> ParameterValue
+ /// CHECK-DAG: <<Const64:i\d+>> IntConstant 64
+ /// CHECK-DAG: <<Shr:j\d+>> Shr [<<Arg>>,<<Const64>>]
+ /// CHECK-DAG: Return [<<Shr>>]
+
+ /// CHECK-START: long Main.Shr64(long) instruction_simplifier (after)
+ /// CHECK-DAG: <<Arg:j\d+>> ParameterValue
+ /// CHECK-DAG: Return [<<Arg>>]
+
+ /// CHECK-START: long Main.Shr64(long) instruction_simplifier (after)
+ /// CHECK-NOT: Shr
+
+ public static long Shr64(long arg) {
+ return arg >> 64;
+ }
+
/// CHECK-START: long Main.Sub0(long) instruction_simplifier (before)
/// CHECK-DAG: <<Arg:j\d+>> ParameterValue
/// CHECK-DAG: <<Const0:j\d+>> LongConstant 0
@@ -1601,6 +1618,24 @@ public class Main {
return (short) (value & 0x17fff);
}
+ /// CHECK-START: double Main.shortAnd0xffffToShortToDouble(short) instruction_simplifier (before)
+ /// CHECK-DAG: <<Arg:s\d+>> ParameterValue
+ /// CHECK-DAG: <<Mask:i\d+>> IntConstant 65535
+ /// CHECK-DAG: <<And:i\d+>> And [<<Mask>>,<<Arg>>]
+ /// CHECK-DAG: <<Same:s\d+>> TypeConversion [<<And>>]
+ /// CHECK-DAG: <<Double:d\d+>> TypeConversion [<<Same>>]
+ /// CHECK-DAG: Return [<<Double>>]
+
+ /// CHECK-START: double Main.shortAnd0xffffToShortToDouble(short) instruction_simplifier (after)
+ /// CHECK-DAG: <<Arg:s\d+>> ParameterValue
+ /// CHECK-DAG: <<Double:d\d+>> TypeConversion [<<Arg>>]
+ /// CHECK-DAG: Return [<<Double>>]
+
+ public static double shortAnd0xffffToShortToDouble(short value) {
+ short same = (short) (value & 0xffff);
+ return (double) same;
+ }
+
/// CHECK-START: int Main.intReverseCondition(int) instruction_simplifier (before)
/// CHECK-DAG: <<Arg:i\d+>> ParameterValue
/// CHECK-DAG: <<Const42:i\d+>> IntConstant 42
@@ -1653,6 +1688,7 @@ public static void main(String[] args) {
assertLongEquals(OrSame(arg), arg);
assertIntEquals(Shl0(arg), arg);
assertLongEquals(Shr0(arg), arg);
+ assertLongEquals(Shr64(arg), arg);
assertLongEquals(Sub0(arg), arg);
assertIntEquals(SubAliasNeg(arg), -arg);
assertLongEquals(UShr0(arg), arg);
@@ -1717,56 +1753,63 @@ public static void main(String[] args) {
assertIntEquals(doubleConditionEqualZero(6.0), 54);
assertIntEquals(doubleConditionEqualZero(43.0), 13);
- assertIntEquals(intToDoubleToInt(1234567), 1234567);
- assertIntEquals(intToDoubleToInt(Integer.MIN_VALUE), Integer.MIN_VALUE);
- assertIntEquals(intToDoubleToInt(Integer.MAX_VALUE), Integer.MAX_VALUE);
- assertStringEquals(intToDoubleToIntPrint(7654321), "d=7654321.0, i=7654321");
- assertIntEquals(byteToDoubleToInt((byte) 12), 12);
- assertIntEquals(byteToDoubleToInt(Byte.MIN_VALUE), Byte.MIN_VALUE);
- assertIntEquals(byteToDoubleToInt(Byte.MAX_VALUE), Byte.MAX_VALUE);
- assertIntEquals(floatToDoubleToInt(11.3f), 11);
- assertStringEquals(floatToDoubleToIntPrint(12.25f), "d=12.25, i=12");
- assertIntEquals(byteToDoubleToShort((byte) 123), 123);
- assertIntEquals(byteToDoubleToShort(Byte.MIN_VALUE), Byte.MIN_VALUE);
- assertIntEquals(byteToDoubleToShort(Byte.MAX_VALUE), Byte.MAX_VALUE);
- assertIntEquals(charToDoubleToShort((char) 1234), 1234);
- assertIntEquals(charToDoubleToShort(Character.MIN_VALUE), Character.MIN_VALUE);
- assertIntEquals(charToDoubleToShort(Character.MAX_VALUE), /* sign-extended */ -1);
- assertIntEquals(floatToIntToShort(12345.75f), 12345);
- assertIntEquals(floatToIntToShort((float)(Short.MIN_VALUE - 1)), Short.MAX_VALUE);
- assertIntEquals(floatToIntToShort((float)(Short.MAX_VALUE + 1)), Short.MIN_VALUE);
- assertIntEquals(intToFloatToInt(-54321), -54321);
- assertDoubleEquals(longToIntToDouble(0x1234567812345678L), (double) 0x12345678);
- assertDoubleEquals(longToIntToDouble(Long.MIN_VALUE), 0.0);
- assertDoubleEquals(longToIntToDouble(Long.MAX_VALUE), -1.0);
- assertLongEquals(longToIntToLong(0x1234567812345678L), 0x0000000012345678L);
- assertLongEquals(longToIntToLong(0x1234567887654321L), 0xffffffff87654321L);
- assertLongEquals(longToIntToLong(Long.MIN_VALUE), 0L);
- assertLongEquals(longToIntToLong(Long.MAX_VALUE), -1L);
- assertIntEquals(shortToCharToShort((short) -5678), (short) -5678);
- assertIntEquals(shortToCharToShort(Short.MIN_VALUE), Short.MIN_VALUE);
- assertIntEquals(shortToCharToShort(Short.MAX_VALUE), Short.MAX_VALUE);
- assertIntEquals(shortToLongToInt((short) 5678), 5678);
- assertIntEquals(shortToLongToInt(Short.MIN_VALUE), Short.MIN_VALUE);
- assertIntEquals(shortToLongToInt(Short.MAX_VALUE), Short.MAX_VALUE);
- assertIntEquals(shortToCharToByte((short) 0x1234), 0x34);
- assertIntEquals(shortToCharToByte((short) 0x12f0), -0x10);
- assertIntEquals(shortToCharToByte(Short.MIN_VALUE), 0);
- assertIntEquals(shortToCharToByte(Short.MAX_VALUE), -1);
- assertStringEquals(shortToCharToBytePrint((short) 1025), "c=1025, b=1");
- assertStringEquals(shortToCharToBytePrint((short) 1023), "c=1023, b=-1");
- assertStringEquals(shortToCharToBytePrint((short) -1), "c=65535, b=-1");
-
- assertIntEquals(longAnd0xffToByte(0x1234432112344321L), 0x21);
- assertIntEquals(longAnd0xffToByte(Long.MIN_VALUE), 0);
- assertIntEquals(longAnd0xffToByte(Long.MAX_VALUE), -1);
- assertIntEquals(intAnd0x1ffffToChar(0x43211234), 0x1234);
- assertIntEquals(intAnd0x1ffffToChar(Integer.MIN_VALUE), 0);
- assertIntEquals(intAnd0x1ffffToChar(Integer.MAX_VALUE), Character.MAX_VALUE);
- assertIntEquals(intAnd0x17fffToShort(0x87654321), 0x4321);
- assertIntEquals(intAnd0x17fffToShort(0x88888888), 0x0888);
- assertIntEquals(intAnd0x17fffToShort(Integer.MIN_VALUE), 0);
- assertIntEquals(intAnd0x17fffToShort(Integer.MAX_VALUE), Short.MAX_VALUE);
+ assertIntEquals(1234567, intToDoubleToInt(1234567));
+ assertIntEquals(Integer.MIN_VALUE, intToDoubleToInt(Integer.MIN_VALUE));
+ assertIntEquals(Integer.MAX_VALUE, intToDoubleToInt(Integer.MAX_VALUE));
+ assertStringEquals("d=7654321.0, i=7654321", intToDoubleToIntPrint(7654321));
+ assertIntEquals(12, byteToDoubleToInt((byte) 12));
+ assertIntEquals(Byte.MIN_VALUE, byteToDoubleToInt(Byte.MIN_VALUE));
+ assertIntEquals(Byte.MAX_VALUE, byteToDoubleToInt(Byte.MAX_VALUE));
+ assertIntEquals(11, floatToDoubleToInt(11.3f));
+ assertStringEquals("d=12.25, i=12", floatToDoubleToIntPrint(12.25f));
+ assertIntEquals(123, byteToDoubleToShort((byte) 123));
+ assertIntEquals(Byte.MIN_VALUE, byteToDoubleToShort(Byte.MIN_VALUE));
+ assertIntEquals(Byte.MAX_VALUE, byteToDoubleToShort(Byte.MAX_VALUE));
+ assertIntEquals(1234, charToDoubleToShort((char) 1234));
+ assertIntEquals(Character.MIN_VALUE, charToDoubleToShort(Character.MIN_VALUE));
+ assertIntEquals(/* sign-extended */ -1, charToDoubleToShort(Character.MAX_VALUE));
+ assertIntEquals(12345, floatToIntToShort(12345.75f));
+ assertIntEquals(Short.MAX_VALUE, floatToIntToShort((float)(Short.MIN_VALUE - 1)));
+ assertIntEquals(Short.MIN_VALUE, floatToIntToShort((float)(Short.MAX_VALUE + 1)));
+ assertIntEquals(-54321, intToFloatToInt(-54321));
+ assertDoubleEquals((double) 0x12345678, longToIntToDouble(0x1234567812345678L));
+ assertDoubleEquals(0.0, longToIntToDouble(Long.MIN_VALUE));
+ assertDoubleEquals(-1.0, longToIntToDouble(Long.MAX_VALUE));
+ assertLongEquals(0x0000000012345678L, longToIntToLong(0x1234567812345678L));
+ assertLongEquals(0xffffffff87654321L, longToIntToLong(0x1234567887654321L));
+ assertLongEquals(0L, longToIntToLong(Long.MIN_VALUE));
+ assertLongEquals(-1L, longToIntToLong(Long.MAX_VALUE));
+ assertIntEquals((short) -5678, shortToCharToShort((short) -5678));
+ assertIntEquals(Short.MIN_VALUE, shortToCharToShort(Short.MIN_VALUE));
+ assertIntEquals(Short.MAX_VALUE, shortToCharToShort(Short.MAX_VALUE));
+ assertIntEquals(5678, shortToLongToInt((short) 5678));
+ assertIntEquals(Short.MIN_VALUE, shortToLongToInt(Short.MIN_VALUE));
+ assertIntEquals(Short.MAX_VALUE, shortToLongToInt(Short.MAX_VALUE));
+ assertIntEquals(0x34, shortToCharToByte((short) 0x1234));
+ assertIntEquals(-0x10, shortToCharToByte((short) 0x12f0));
+ assertIntEquals(0, shortToCharToByte(Short.MIN_VALUE));
+ assertIntEquals(-1, shortToCharToByte(Short.MAX_VALUE));
+ assertStringEquals("c=1025, b=1", shortToCharToBytePrint((short) 1025));
+ assertStringEquals("c=1023, b=-1", shortToCharToBytePrint((short) 1023));
+ assertStringEquals("c=65535, b=-1", shortToCharToBytePrint((short) -1));
+
+ assertIntEquals(0x21, longAnd0xffToByte(0x1234432112344321L));
+ assertIntEquals(0, longAnd0xffToByte(Long.MIN_VALUE));
+ assertIntEquals(-1, longAnd0xffToByte(Long.MAX_VALUE));
+ assertIntEquals(0x1234, intAnd0x1ffffToChar(0x43211234));
+ assertIntEquals(0, intAnd0x1ffffToChar(Integer.MIN_VALUE));
+ assertIntEquals(Character.MAX_VALUE, intAnd0x1ffffToChar(Integer.MAX_VALUE));
+ assertIntEquals(0x4321, intAnd0x17fffToShort(0x87654321));
+ assertIntEquals(0x0888, intAnd0x17fffToShort(0x88888888));
+ assertIntEquals(0, intAnd0x17fffToShort(Integer.MIN_VALUE));
+ assertIntEquals(Short.MAX_VALUE, intAnd0x17fffToShort(Integer.MAX_VALUE));
+
+ assertDoubleEquals(0.0, shortAnd0xffffToShortToDouble((short) 0));
+ assertDoubleEquals(1.0, shortAnd0xffffToShortToDouble((short) 1));
+ assertDoubleEquals(-2.0, shortAnd0xffffToShortToDouble((short) -2));
+ assertDoubleEquals(12345.0, shortAnd0xffffToShortToDouble((short) 12345));
+ assertDoubleEquals((double)Short.MAX_VALUE, shortAnd0xffffToShortToDouble(Short.MAX_VALUE));
+ assertDoubleEquals((double)Short.MIN_VALUE, shortAnd0xffffToShortToDouble(Short.MIN_VALUE));
assertIntEquals(intReverseCondition(41), 13);
assertIntEquals(intReverseConditionNaN(-5), 13);
diff --git a/test/529-checker-unresolved/build b/test/529-checker-unresolved/build
deleted file mode 100644
index d85035b669..0000000000
--- a/test/529-checker-unresolved/build
+++ /dev/null
@@ -1,46 +0,0 @@
-#!/bin/bash
-#
-# Copyright (C) 2015 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.
-
-# Stop if something fails.
-set -e
-
-# We can't use src-ex testing infrastructure because src and src-ex are compiled
-# with javac independetely and can't share code (without reflection).
-
-mkdir classes
-${JAVAC} -d classes `find src -name '*.java'`
-
-mkdir classes-ex
-mv classes/UnresolvedClass.class classes-ex
-mv classes/UnresolvedInterface.class classes-ex
-mv classes/UnresolvedSuperClass.class classes-ex
-
-if [ ${USE_JACK} = "true" ]; then
- jar cf classes.jill.jar -C classes .
- jar cf classes-ex.jill.jar -C classes-ex .
-
- ${JACK} --import classes.jill.jar --output-dex .
- zip $TEST_NAME.jar classes.dex
- ${JACK} --import classes-ex.jill.jar --output-dex .
- zip ${TEST_NAME}-ex.jar classes.dex
-else
- if [ ${NEED_DEX} = "true" ]; then
- ${DX} -JXmx256m --debug --dex --dump-to=classes.lst --output=classes.dex --dump-width=1000 classes
- zip $TEST_NAME.jar classes.dex
- ${DX} -JXmx256m --debug --dex --dump-to=classes-ex.lst --output=classes.dex --dump-width=1000 classes-ex
- zip ${TEST_NAME}-ex.jar classes.dex
- fi
-fi
diff --git a/test/529-checker-unresolved/src/Unresolved.java b/test/529-checker-unresolved/src-dex2oat-unresolved/UnresolvedClass.java
index 20ac6e0b89..8b3bb3c7ad 100644
--- a/test/529-checker-unresolved/src/Unresolved.java
+++ b/test/529-checker-unresolved/src-dex2oat-unresolved/UnresolvedClass.java
@@ -14,17 +14,7 @@
* limitations under the License.
*/
-interface UnresolvedInterface {
- void interfaceMethod();
-}
-
-class UnresolvedSuperClass {
- public void superMethod() {
- System.out.println("UnresolvedClass.superMethod()");
- }
-}
-
-class UnresolvedClass extends UnresolvedSuperClass implements UnresolvedInterface {
+public class UnresolvedClass extends UnresolvedSuperClass implements UnresolvedInterface {
static public void staticMethod() {
System.out.println("UnresolvedClass.staticMethod()");
}
diff --git a/runtime/native/java_lang_Runtime.h b/test/529-checker-unresolved/src-dex2oat-unresolved/UnresolvedInterface.java
index ceda06bde9..6e6b14bb7c 100644
--- a/runtime/native/java_lang_Runtime.h
+++ b/test/529-checker-unresolved/src-dex2oat-unresolved/UnresolvedInterface.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2014 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,15 +14,6 @@
* limitations under the License.
*/
-#ifndef ART_RUNTIME_NATIVE_JAVA_LANG_RUNTIME_H_
-#define ART_RUNTIME_NATIVE_JAVA_LANG_RUNTIME_H_
-
-#include <jni.h>
-
-namespace art {
-
-void register_java_lang_Runtime(JNIEnv* env);
-
-} // namespace art
-
-#endif // ART_RUNTIME_NATIVE_JAVA_LANG_RUNTIME_H_
+public interface UnresolvedInterface {
+ void interfaceMethod();
+}
diff --git a/test/529-checker-unresolved/src-dex2oat-unresolved/UnresolvedSuperClass.java b/test/529-checker-unresolved/src-dex2oat-unresolved/UnresolvedSuperClass.java
new file mode 100644
index 0000000000..dd3be00633
--- /dev/null
+++ b/test/529-checker-unresolved/src-dex2oat-unresolved/UnresolvedSuperClass.java
@@ -0,0 +1,21 @@
+/*
+ * Copyright (C) 2015 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.
+ */
+
+public class UnresolvedSuperClass {
+ public void superMethod() {
+ System.out.println("UnresolvedClass.superMethod()");
+ }
+}
diff --git a/test/551-checker-shifter-operand/src/Main.java b/test/551-checker-shifter-operand/src/Main.java
index 9c86154bd4..edb8a68b47 100644
--- a/test/551-checker-shifter-operand/src/Main.java
+++ b/test/551-checker-shifter-operand/src/Main.java
@@ -483,9 +483,7 @@ public class Main {
/// CHECK: Arm64DataProcWithShifterOp
/// CHECK: Arm64DataProcWithShifterOp
/// CHECK: Arm64DataProcWithShifterOp
- /// CHECK: Arm64DataProcWithShifterOp
- /// CHECK: Arm64DataProcWithShifterOp
- /// CHECK: Arm64DataProcWithShifterOp
+ // Note: `b << 32`, `b >> 32` and `b >>> 32` are optimized away by generic simplifier.
/// CHECK-START-ARM64: void Main.$opt$validateShiftInt(int, int) instruction_simplifier_arm64 (after)
/// CHECK-NOT: Shl
diff --git a/test/566-checker-signum/src/Main.java b/test/566-checker-signum/src/Main.java
index 0ad0042326..5f2cf3dc95 100644
--- a/test/566-checker-signum/src/Main.java
+++ b/test/566-checker-signum/src/Main.java
@@ -16,65 +16,213 @@
public class Main {
- /// CHECK-START: int Main.sign32(int) intrinsics_recognition (after)
- /// CHECK-DAG: <<Result:i\d+>> InvokeStaticOrDirect intrinsic:IntegerSignum
- /// CHECK-DAG: Return [<<Result>>]
- private static int sign32(int x) {
+ /// CHECK-START: int Main.signBoolean(boolean) intrinsics_recognition (after)
+ /// CHECK-DAG: <<Method:[ij]\d+>> CurrentMethod
+ /// CHECK-DAG: <<Zero:i\d+>> IntConstant 0
+ /// CHECK-DAG: <<One:i\d+>> IntConstant 1
+ /// CHECK-DAG: <<Phi:i\d+>> Phi [<<One>>,<<Zero>>]
+ /// CHECK-DAG: <<Result:i\d+>> InvokeStaticOrDirect [<<Phi>>,<<Method>>] intrinsic:IntegerSignum
+ /// CHECK-DAG: Return [<<Result>>]
+
+ /// CHECK-START: int Main.signBoolean(boolean) instruction_simplifier (after)
+ /// CHECK-DAG: <<Zero:i\d+>> IntConstant 0
+ /// CHECK-DAG: <<One:i\d+>> IntConstant 1
+ /// CHECK-DAG: <<Phi:i\d+>> Phi [<<One>>,<<Zero>>]
+ /// CHECK-DAG: <<Result:i\d+>> Compare [<<Phi>>,<<Zero>>]
+ /// CHECK-DAG: Return [<<Result>>]
+
+ /// CHECK-START: int Main.signBoolean(boolean) instruction_simplifier (after)
+ /// CHECK-NOT: InvokeStaticOrDirect
+
+ /// CHECK-START: int Main.signBoolean(boolean) select_generator (after)
+ /// CHECK-DAG: <<Arg:z\d+>> ParameterValue
+ /// CHECK-DAG: <<Zero:i\d+>> IntConstant 0
+ /// CHECK-DAG: <<One:i\d+>> IntConstant 1
+ /// CHECK-DAG: <<Sel:i\d+>> Select [<<Zero>>,<<One>>,<<Arg>>]
+ /// CHECK-DAG: <<Result:i\d+>> Compare [<<Sel>>,<<Zero>>]
+ /// CHECK-DAG: Return [<<Result>>]
+
+ /// CHECK-START: int Main.signBoolean(boolean) select_generator (after)
+ /// CHECK-NOT: Phi
+
+ /// CHECK-START: int Main.signBoolean(boolean) instruction_simplifier_after_bce (after)
+ /// CHECK-DAG: <<Arg:z\d+>> ParameterValue
+ /// CHECK-DAG: <<Zero:i\d+>> IntConstant 0
+ /// CHECK-DAG: <<Result:i\d+>> Compare [<<Arg>>,<<Zero>>]
+ /// CHECK-DAG: Return [<<Result>>]
+
+ /// CHECK-START: int Main.signBoolean(boolean) instruction_simplifier_after_bce (after)
+ /// CHECK-NOT: Select
+
+ private static int signBoolean(boolean x) {
+ return Integer.signum(x ? 1 : 0);
+ }
+
+ /// CHECK-START: int Main.signByte(byte) intrinsics_recognition (after)
+ /// CHECK-DAG: <<Result:i\d+>> InvokeStaticOrDirect intrinsic:IntegerSignum
+ /// CHECK-DAG: Return [<<Result>>]
+
+ /// CHECK-START: int Main.signByte(byte) instruction_simplifier (after)
+ /// CHECK-DAG: <<Result:i\d+>> Compare
+ /// CHECK-DAG: Return [<<Result>>]
+
+ /// CHECK-START: int Main.signByte(byte) instruction_simplifier (after)
+ /// CHECK-NOT: InvokeStaticOrDirect
+
+ private static int signByte(byte x) {
return Integer.signum(x);
}
- /// CHECK-START: int Main.sign64(long) intrinsics_recognition (after)
- /// CHECK-DAG: <<Result:i\d+>> InvokeStaticOrDirect intrinsic:LongSignum
- /// CHECK-DAG: Return [<<Result>>]
- private static int sign64(long x) {
+ /// CHECK-START: int Main.signShort(short) intrinsics_recognition (after)
+ /// CHECK-DAG: <<Result:i\d+>> InvokeStaticOrDirect intrinsic:IntegerSignum
+ /// CHECK-DAG: Return [<<Result>>]
+
+ /// CHECK-START: int Main.signShort(short) instruction_simplifier (after)
+ /// CHECK-DAG: <<Result:i\d+>> Compare
+ /// CHECK-DAG: Return [<<Result>>]
+
+ /// CHECK-START: int Main.signShort(short) instruction_simplifier (after)
+ /// CHECK-NOT: InvokeStaticOrDirect
+
+ private static int signShort(short x) {
+ return Integer.signum(x);
+ }
+
+ /// CHECK-START: int Main.signChar(char) intrinsics_recognition (after)
+ /// CHECK-DAG: <<Result:i\d+>> InvokeStaticOrDirect intrinsic:IntegerSignum
+ /// CHECK-DAG: Return [<<Result>>]
+
+ /// CHECK-START: int Main.signChar(char) instruction_simplifier (after)
+ /// CHECK-DAG: <<Result:i\d+>> Compare
+ /// CHECK-DAG: Return [<<Result>>]
+
+ /// CHECK-START: int Main.signChar(char) instruction_simplifier (after)
+ /// CHECK-NOT: InvokeStaticOrDirect
+
+ private static int signChar(char x) {
+ return Integer.signum(x);
+ }
+
+ /// CHECK-START: int Main.signInt(int) intrinsics_recognition (after)
+ /// CHECK-DAG: <<Result:i\d+>> InvokeStaticOrDirect intrinsic:IntegerSignum
+ /// CHECK-DAG: Return [<<Result>>]
+
+ /// CHECK-START: int Main.signInt(int) instruction_simplifier (after)
+ /// CHECK-DAG: <<Result:i\d+>> Compare
+ /// CHECK-DAG: Return [<<Result>>]
+
+ /// CHECK-START: int Main.signInt(int) instruction_simplifier (after)
+ /// CHECK-NOT: InvokeStaticOrDirect
+
+ private static int signInt(int x) {
+ return Integer.signum(x);
+ }
+
+ /// CHECK-START: int Main.signLong(long) intrinsics_recognition (after)
+ /// CHECK-DAG: <<Result:i\d+>> InvokeStaticOrDirect intrinsic:LongSignum
+ /// CHECK-DAG: Return [<<Result>>]
+
+ /// CHECK-START: int Main.signLong(long) instruction_simplifier (after)
+ /// CHECK-DAG: <<Result:i\d+>> Compare
+ /// CHECK-DAG: Return [<<Result>>]
+
+ /// CHECK-START: int Main.signLong(long) instruction_simplifier (after)
+ /// CHECK-NOT: InvokeStaticOrDirect
+
+ private static int signLong(long x) {
return Long.signum(x);
}
- public static void main(String args[]) {
- expectEquals(-1, sign32(Integer.MIN_VALUE));
- expectEquals(-1, sign32(-12345));
- expectEquals(-1, sign32(-1));
- expectEquals(0, sign32(0));
- expectEquals(1, sign32(1));
- expectEquals(1, sign32(12345));
- expectEquals(1, sign32(Integer.MAX_VALUE));
+
+ public static void testSignBoolean() {
+ expectEquals(0, signBoolean(false));
+ expectEquals(1, signBoolean(true));
+ }
+
+ public static void testSignByte() {
+ expectEquals(-1, signByte((byte)Byte.MIN_VALUE));
+ expectEquals(-1, signByte((byte)-64));
+ expectEquals(-1, signByte((byte)-1));
+ expectEquals(0, signByte((byte)0));
+ expectEquals(1, signByte((byte)1));
+ expectEquals(1, signByte((byte)64));
+ expectEquals(1, signByte((byte)Byte.MAX_VALUE));
+ }
+
+ public static void testSignShort() {
+ expectEquals(-1, signShort((short)Short.MIN_VALUE));
+ expectEquals(-1, signShort((short)-12345));
+ expectEquals(-1, signShort((short)-1));
+ expectEquals(0, signShort((short)0));
+ expectEquals(1, signShort((short)1));
+ expectEquals(1, signShort((short)12345));
+ expectEquals(1, signShort((short)Short.MAX_VALUE));
+ }
+
+ public static void testSignChar() {
+ expectEquals(0, signChar((char)0));
+ expectEquals(1, signChar((char)1));
+ expectEquals(1, signChar((char)12345));
+ expectEquals(1, signChar((char)Character.MAX_VALUE));
+ }
+
+ public static void testSignInt() {
+ expectEquals(-1, signInt(Integer.MIN_VALUE));
+ expectEquals(-1, signInt(-12345));
+ expectEquals(-1, signInt(-1));
+ expectEquals(0, signInt(0));
+ expectEquals(1, signInt(1));
+ expectEquals(1, signInt(12345));
+ expectEquals(1, signInt(Integer.MAX_VALUE));
for (int i = -11; i <= 11; i++) {
int expected = 0;
if (i < 0) expected = -1;
else if (i > 0) expected = 1;
- expectEquals(expected, sign32(i));
+ expectEquals(expected, signInt(i));
}
+ }
+
+ public static void testSignLong() {
+ expectEquals(-1, signLong(Long.MIN_VALUE));
+ expectEquals(-1, signLong(-12345L));
+ expectEquals(-1, signLong(-1L));
+ expectEquals(0, signLong(0L));
+ expectEquals(1, signLong(1L));
+ expectEquals(1, signLong(12345L));
+ expectEquals(1, signLong(Long.MAX_VALUE));
- expectEquals(-1, sign64(Long.MIN_VALUE));
- expectEquals(-1, sign64(-12345L));
- expectEquals(-1, sign64(-1L));
- expectEquals(0, sign64(0L));
- expectEquals(1, sign64(1L));
- expectEquals(1, sign64(12345L));
- expectEquals(1, sign64(Long.MAX_VALUE));
-
- expectEquals(-1, sign64(0x800000007FFFFFFFL));
- expectEquals(-1, sign64(0x80000000FFFFFFFFL));
- expectEquals(1, sign64(0x000000007FFFFFFFL));
- expectEquals(1, sign64(0x00000000FFFFFFFFL));
- expectEquals(1, sign64(0x7FFFFFFF7FFFFFFFL));
- expectEquals(1, sign64(0x7FFFFFFFFFFFFFFFL));
+ expectEquals(-1, signLong(0x800000007FFFFFFFL));
+ expectEquals(-1, signLong(0x80000000FFFFFFFFL));
+ expectEquals(1, signLong(0x000000007FFFFFFFL));
+ expectEquals(1, signLong(0x00000000FFFFFFFFL));
+ expectEquals(1, signLong(0x7FFFFFFF7FFFFFFFL));
+ expectEquals(1, signLong(0x7FFFFFFFFFFFFFFFL));
for (long i = -11L; i <= 11L; i++) {
int expected = 0;
if (i < 0) expected = -1;
else if (i > 0) expected = 1;
- expectEquals(expected, sign64(i));
+ expectEquals(expected, signLong(i));
}
for (long i = Long.MIN_VALUE; i <= Long.MIN_VALUE + 11L; i++) {
- expectEquals(-1, sign64(i));
+ expectEquals(-1, signLong(i));
}
for (long i = Long.MAX_VALUE; i >= Long.MAX_VALUE - 11L; i--) {
- expectEquals(1, sign64(i));
+ expectEquals(1, signLong(i));
}
+ }
+
+
+ public static void main(String args[]) {
+ testSignBoolean();
+ testSignByte();
+ testSignShort();
+ testSignChar();
+ testSignInt();
+ testSignLong();
System.out.println("passed");
}
diff --git a/test/567-checker-compare/src/Main.java b/test/567-checker-compare/src/Main.java
index 951d2c7510..f95ff1a7db 100644
--- a/test/567-checker-compare/src/Main.java
+++ b/test/567-checker-compare/src/Main.java
@@ -16,98 +16,902 @@
public class Main {
- /// CHECK-START: int Main.compare32(int, int) intrinsics_recognition (after)
- /// CHECK-DAG: <<Result:i\d+>> InvokeStaticOrDirect intrinsic:IntegerCompare
- /// CHECK-DAG: Return [<<Result>>]
- private static int compare32(int x, int y) {
+ /// CHECK-START: int Main.compareBooleans(boolean, boolean) intrinsics_recognition (after)
+ /// CHECK-DAG: <<Method:[ij]\d+>> CurrentMethod
+ /// CHECK-DAG: <<Zero:i\d+>> IntConstant 0
+ /// CHECK-DAG: <<One:i\d+>> IntConstant 1
+ /// CHECK-DAG: <<PhiX:i\d+>> Phi [<<One>>,<<Zero>>]
+ /// CHECK-DAG: <<PhiY:i\d+>> Phi [<<One>>,<<Zero>>]
+ /// CHECK-DAG: <<Result:i\d+>> InvokeStaticOrDirect [<<PhiX>>,<<PhiY>>,<<Method>>] intrinsic:IntegerCompare
+ /// CHECK-DAG: Return [<<Result>>]
+
+ /// CHECK-START: int Main.compareBooleans(boolean, boolean) instruction_simplifier (after)
+ /// CHECK-DAG: <<Zero:i\d+>> IntConstant 0
+ /// CHECK-DAG: <<One:i\d+>> IntConstant 1
+ /// CHECK-DAG: <<PhiX:i\d+>> Phi [<<One>>,<<Zero>>]
+ /// CHECK-DAG: <<PhiY:i\d+>> Phi [<<One>>,<<Zero>>]
+ /// CHECK-DAG: <<Result:i\d+>> Compare [<<PhiX>>,<<PhiY>>]
+ /// CHECK-DAG: Return [<<Result>>]
+
+ /// CHECK-START: int Main.compareBooleans(boolean, boolean) instruction_simplifier (after)
+ /// CHECK-NOT: InvokeStaticOrDirect
+
+ /// CHECK-START: int Main.compareBooleans(boolean, boolean) select_generator (after)
+ /// CHECK: <<ArgX:z\d+>> ParameterValue
+ /// CHECK: <<ArgY:z\d+>> ParameterValue
+ /// CHECK-DAG: <<Zero:i\d+>> IntConstant 0
+ /// CHECK-DAG: <<One:i\d+>> IntConstant 1
+ /// CHECK-DAG: <<SelX:i\d+>> Select [<<Zero>>,<<One>>,<<ArgX>>]
+ /// CHECK-DAG: <<SelY:i\d+>> Select [<<Zero>>,<<One>>,<<ArgY>>]
+ /// CHECK-DAG: <<Result:i\d+>> Compare [<<SelX>>,<<SelY>>]
+ /// CHECK-DAG: Return [<<Result>>]
+
+ /// CHECK-START: int Main.compareBooleans(boolean, boolean) select_generator (after)
+ /// CHECK-NOT: Phi
+
+ /// CHECK-START: int Main.compareBooleans(boolean, boolean) instruction_simplifier_after_bce (after)
+ /// CHECK: <<ArgX:z\d+>> ParameterValue
+ /// CHECK: <<ArgY:z\d+>> ParameterValue
+ /// CHECK-DAG: <<Result:i\d+>> Compare [<<ArgX>>,<<ArgY>>]
+ /// CHECK-DAG: Return [<<Result>>]
+
+ /// CHECK-START: int Main.compareBooleans(boolean, boolean) instruction_simplifier_after_bce (after)
+ /// CHECK-NOT: Select
+
+ private static int compareBooleans(boolean x, boolean y) {
+ return Integer.compare((x ? 1 : 0), (y ? 1 : 0));
+ }
+
+ /// CHECK-START: int Main.compareBytes(byte, byte) intrinsics_recognition (after)
+ /// CHECK-DAG: <<Result:i\d+>> InvokeStaticOrDirect intrinsic:IntegerCompare
+ /// CHECK-DAG: Return [<<Result>>]
+
+ /// CHECK-START: int Main.compareBytes(byte, byte) instruction_simplifier (after)
+ /// CHECK-DAG: <<Result:i\d+>> Compare
+ /// CHECK-DAG: Return [<<Result>>]
+
+ /// CHECK-START: int Main.compareBytes(byte, byte) instruction_simplifier (after)
+ /// CHECK-NOT: InvokeStaticOrDirect
+
+ private static int compareBytes(byte x, byte y) {
+ return Integer.compare(x, y);
+ }
+
+ /// CHECK-START: int Main.compareShorts(short, short) intrinsics_recognition (after)
+ /// CHECK-DAG: <<Result:i\d+>> InvokeStaticOrDirect intrinsic:IntegerCompare
+ /// CHECK-DAG: Return [<<Result>>]
+
+ /// CHECK-START: int Main.compareShorts(short, short) instruction_simplifier (after)
+ /// CHECK-DAG: <<Result:i\d+>> Compare
+ /// CHECK-DAG: Return [<<Result>>]
+
+ /// CHECK-START: int Main.compareShorts(short, short) instruction_simplifier (after)
+ /// CHECK-NOT: InvokeStaticOrDirect
+
+ private static int compareShorts(short x, short y) {
return Integer.compare(x, y);
}
- /// CHECK-START: int Main.compare64(long, long) intrinsics_recognition (after)
- /// CHECK-DAG: <<Result:i\d+>> InvokeStaticOrDirect intrinsic:LongCompare
- /// CHECK-DAG: Return [<<Result>>]
- private static int compare64(long x, long y) {
+ /// CHECK-START: int Main.compareChars(char, char) intrinsics_recognition (after)
+ /// CHECK-DAG: <<Result:i\d+>> InvokeStaticOrDirect intrinsic:IntegerCompare
+ /// CHECK-DAG: Return [<<Result>>]
+
+ /// CHECK-START: int Main.compareChars(char, char) instruction_simplifier (after)
+ /// CHECK-DAG: <<Result:i\d+>> Compare
+ /// CHECK-DAG: Return [<<Result>>]
+
+ /// CHECK-START: int Main.compareChars(char, char) instruction_simplifier (after)
+ /// CHECK-NOT: InvokeStaticOrDirect
+
+ private static int compareChars(char x, char y) {
+ return Integer.compare(x, y);
+ }
+
+ /// CHECK-START: int Main.compareInts(int, int) intrinsics_recognition (after)
+ /// CHECK-DAG: <<Result:i\d+>> InvokeStaticOrDirect intrinsic:IntegerCompare
+ /// CHECK-DAG: Return [<<Result>>]
+
+ /// CHECK-START: int Main.compareInts(int, int) instruction_simplifier (after)
+ /// CHECK-DAG: <<Result:i\d+>> Compare
+ /// CHECK-DAG: Return [<<Result>>]
+
+ /// CHECK-START: int Main.compareInts(int, int) instruction_simplifier (after)
+ /// CHECK-NOT: InvokeStaticOrDirect
+
+ private static int compareInts(int x, int y) {
+ return Integer.compare(x, y);
+ }
+
+ /// CHECK-START: int Main.compareLongs(long, long) intrinsics_recognition (after)
+ /// CHECK-DAG: <<Result:i\d+>> InvokeStaticOrDirect intrinsic:LongCompare
+ /// CHECK-DAG: Return [<<Result>>]
+
+ /// CHECK-START: int Main.compareLongs(long, long) instruction_simplifier (after)
+ /// CHECK-DAG: <<Result:i\d+>> Compare
+ /// CHECK-DAG: Return [<<Result>>]
+
+ /// CHECK-START: int Main.compareLongs(long, long) instruction_simplifier (after)
+ /// CHECK-NOT: InvokeStaticOrDirect
+
+ private static int compareLongs(long x, long y) {
return Long.compare(x, y);
}
- public static void main(String args[]) {
- expectEquals(-1, compare32(Integer.MIN_VALUE, Integer.MIN_VALUE + 1));
- expectEquals(-1, compare32(Integer.MIN_VALUE, -1));
- expectEquals(-1, compare32(Integer.MIN_VALUE, 0));
- expectEquals(-1, compare32(Integer.MIN_VALUE, 1));
- expectEquals(-1, compare32(Integer.MIN_VALUE, Integer.MAX_VALUE));
- expectEquals(-1, compare32(-1, 0));
- expectEquals(-1, compare32(-1, 1));
- expectEquals(-1, compare32(0, 1));
-
- expectEquals(0, compare32(Integer.MIN_VALUE, Integer.MIN_VALUE));
- expectEquals(0, compare32(-1, -1));
- expectEquals(0, compare32(0, 0));
- expectEquals(0, compare32(1, 1));
- expectEquals(0, compare32(Integer.MAX_VALUE, Integer.MAX_VALUE));
-
- expectEquals(1, compare32(0, -1));
- expectEquals(1, compare32(1, -1));
- expectEquals(1, compare32(1, 0));
- expectEquals(1, compare32(Integer.MAX_VALUE, Integer.MIN_VALUE));
- expectEquals(1, compare32(Integer.MAX_VALUE, -1));
- expectEquals(1, compare32(Integer.MAX_VALUE, 0));
- expectEquals(1, compare32(Integer.MAX_VALUE, 1));
- expectEquals(1, compare32(Integer.MAX_VALUE, Integer.MAX_VALUE - 1));
+
+ /// CHECK-START: int Main.compareByteShort(byte, short) intrinsics_recognition (after)
+ /// CHECK-DAG: <<Result:i\d+>> InvokeStaticOrDirect intrinsic:IntegerCompare
+ /// CHECK-DAG: Return [<<Result>>]
+
+ /// CHECK-START: int Main.compareByteShort(byte, short) instruction_simplifier (after)
+ /// CHECK-DAG: <<Result:i\d+>> Compare
+ /// CHECK-DAG: Return [<<Result>>]
+
+ /// CHECK-START: int Main.compareByteShort(byte, short) instruction_simplifier (after)
+ /// CHECK-NOT: InvokeStaticOrDirect
+
+ public static int compareByteShort(byte x, short y) {
+ return Integer.compare(x, y);
+ }
+
+ /// CHECK-START: int Main.compareByteChar(byte, char) intrinsics_recognition (after)
+ /// CHECK-DAG: <<Result:i\d+>> InvokeStaticOrDirect intrinsic:IntegerCompare
+ /// CHECK-DAG: Return [<<Result>>]
+
+ /// CHECK-START: int Main.compareByteChar(byte, char) instruction_simplifier (after)
+ /// CHECK-DAG: <<Result:i\d+>> Compare
+ /// CHECK-DAG: Return [<<Result>>]
+
+ /// CHECK-START: int Main.compareByteChar(byte, char) instruction_simplifier (after)
+ /// CHECK-NOT: InvokeStaticOrDirect
+
+ public static int compareByteChar(byte x, char y) {
+ return Integer.compare(x, y);
+ }
+
+ /// CHECK-START: int Main.compareByteInt(byte, int) intrinsics_recognition (after)
+ /// CHECK-DAG: <<Result:i\d+>> InvokeStaticOrDirect intrinsic:IntegerCompare
+ /// CHECK-DAG: Return [<<Result>>]
+
+ /// CHECK-START: int Main.compareByteInt(byte, int) instruction_simplifier (after)
+ /// CHECK-DAG: <<Result:i\d+>> Compare
+ /// CHECK-DAG: Return [<<Result>>]
+
+ /// CHECK-START: int Main.compareByteInt(byte, int) instruction_simplifier (after)
+ /// CHECK-NOT: InvokeStaticOrDirect
+
+ public static int compareByteInt(byte x, int y) {
+ return Integer.compare(x, y);
+ }
+
+
+ /// CHECK-START: int Main.compareShortByte(short, byte) intrinsics_recognition (after)
+ /// CHECK-DAG: <<Result:i\d+>> InvokeStaticOrDirect intrinsic:IntegerCompare
+ /// CHECK-DAG: Return [<<Result>>]
+
+ /// CHECK-START: int Main.compareShortByte(short, byte) instruction_simplifier (after)
+ /// CHECK-DAG: <<Result:i\d+>> Compare
+ /// CHECK-DAG: Return [<<Result>>]
+
+ /// CHECK-START: int Main.compareShortByte(short, byte) instruction_simplifier (after)
+ /// CHECK-NOT: InvokeStaticOrDirect
+
+ public static int compareShortByte(short x, byte y) {
+ return Integer.compare(x, y);
+ }
+
+ /// CHECK-START: int Main.compareShortChar(short, char) intrinsics_recognition (after)
+ /// CHECK-DAG: <<Result:i\d+>> InvokeStaticOrDirect intrinsic:IntegerCompare
+ /// CHECK-DAG: Return [<<Result>>]
+
+ /// CHECK-START: int Main.compareShortChar(short, char) instruction_simplifier (after)
+ /// CHECK-DAG: <<Result:i\d+>> Compare
+ /// CHECK-DAG: Return [<<Result>>]
+
+ /// CHECK-START: int Main.compareShortChar(short, char) instruction_simplifier (after)
+ /// CHECK-NOT: InvokeStaticOrDirect
+
+ public static int compareShortChar(short x, char y) {
+ return Integer.compare(x, y);
+ }
+
+ /// CHECK-START: int Main.compareShortInt(short, int) intrinsics_recognition (after)
+ /// CHECK-DAG: <<Result:i\d+>> InvokeStaticOrDirect intrinsic:IntegerCompare
+ /// CHECK-DAG: Return [<<Result>>]
+
+ /// CHECK-START: int Main.compareShortInt(short, int) instruction_simplifier (after)
+ /// CHECK-DAG: <<Result:i\d+>> Compare
+ /// CHECK-DAG: Return [<<Result>>]
+
+ /// CHECK-START: int Main.compareShortInt(short, int) instruction_simplifier (after)
+ /// CHECK-NOT: InvokeStaticOrDirect
+
+ public static int compareShortInt(short x, int y) {
+ return Integer.compare(x, y);
+ }
+
+
+ /// CHECK-START: int Main.compareCharByte(char, byte) intrinsics_recognition (after)
+ /// CHECK-DAG: <<Result:i\d+>> InvokeStaticOrDirect intrinsic:IntegerCompare
+ /// CHECK-DAG: Return [<<Result>>]
+
+ /// CHECK-START: int Main.compareCharByte(char, byte) instruction_simplifier (after)
+ /// CHECK-DAG: <<Result:i\d+>> Compare
+ /// CHECK-DAG: Return [<<Result>>]
+
+ /// CHECK-START: int Main.compareCharByte(char, byte) instruction_simplifier (after)
+ /// CHECK-NOT: InvokeStaticOrDirect
+
+ public static int compareCharByte(char x, byte y) {
+ return Integer.compare(x, y);
+ }
+
+ /// CHECK-START: int Main.compareCharShort(char, short) intrinsics_recognition (after)
+ /// CHECK-DAG: <<Result:i\d+>> InvokeStaticOrDirect intrinsic:IntegerCompare
+ /// CHECK-DAG: Return [<<Result>>]
+
+ /// CHECK-START: int Main.compareCharShort(char, short) instruction_simplifier (after)
+ /// CHECK-DAG: <<Result:i\d+>> Compare
+ /// CHECK-DAG: Return [<<Result>>]
+
+ /// CHECK-START: int Main.compareCharShort(char, short) instruction_simplifier (after)
+ /// CHECK-NOT: InvokeStaticOrDirect
+
+ public static int compareCharShort(char x, short y) {
+ return Integer.compare(x, y);
+ }
+
+ /// CHECK-START: int Main.compareCharInt(char, int) intrinsics_recognition (after)
+ /// CHECK-DAG: <<Result:i\d+>> InvokeStaticOrDirect intrinsic:IntegerCompare
+ /// CHECK-DAG: Return [<<Result>>]
+
+ /// CHECK-START: int Main.compareCharInt(char, int) instruction_simplifier (after)
+ /// CHECK-DAG: <<Result:i\d+>> Compare
+ /// CHECK-DAG: Return [<<Result>>]
+
+ /// CHECK-START: int Main.compareCharInt(char, int) instruction_simplifier (after)
+ /// CHECK-NOT: InvokeStaticOrDirect
+
+ public static int compareCharInt(char x, int y) {
+ return Integer.compare(x, y);
+ }
+
+
+ /// CHECK-START: int Main.compareIntByte(int, byte) intrinsics_recognition (after)
+ /// CHECK-DAG: <<Result:i\d+>> InvokeStaticOrDirect intrinsic:IntegerCompare
+ /// CHECK-DAG: Return [<<Result>>]
+
+ /// CHECK-START: int Main.compareIntByte(int, byte) instruction_simplifier (after)
+ /// CHECK-DAG: <<Result:i\d+>> Compare
+ /// CHECK-DAG: Return [<<Result>>]
+
+ /// CHECK-START: int Main.compareIntByte(int, byte) instruction_simplifier (after)
+ /// CHECK-NOT: InvokeStaticOrDirect
+
+ public static int compareIntByte(int x, byte y) {
+ return Integer.compare(x, y);
+ }
+
+ /// CHECK-START: int Main.compareIntShort(int, short) intrinsics_recognition (after)
+ /// CHECK-DAG: <<Result:i\d+>> InvokeStaticOrDirect intrinsic:IntegerCompare
+ /// CHECK-DAG: Return [<<Result>>]
+
+ /// CHECK-START: int Main.compareIntShort(int, short) instruction_simplifier (after)
+ /// CHECK-DAG: <<Result:i\d+>> Compare
+ /// CHECK-DAG: Return [<<Result>>]
+
+ /// CHECK-START: int Main.compareIntShort(int, short) instruction_simplifier (after)
+ /// CHECK-NOT: InvokeStaticOrDirect
+
+ public static int compareIntShort(int x, short y) {
+ return Integer.compare(x, y);
+ }
+
+ /// CHECK-START: int Main.compareIntChar(int, char) intrinsics_recognition (after)
+ /// CHECK-DAG: <<Result:i\d+>> InvokeStaticOrDirect intrinsic:IntegerCompare
+ /// CHECK-DAG: Return [<<Result>>]
+
+ /// CHECK-START: int Main.compareIntChar(int, char) instruction_simplifier (after)
+ /// CHECK-DAG: <<Result:i\d+>> Compare
+ /// CHECK-DAG: Return [<<Result>>]
+
+ /// CHECK-START: int Main.compareIntChar(int, char) instruction_simplifier (after)
+ /// CHECK-NOT: InvokeStaticOrDirect
+
+ public static int compareIntChar(int x, char y) {
+ return Integer.compare(x, y);
+ }
+
+
+ public static void testCompareBooleans() {
+ expectEquals(-1, compareBooleans(false, true));
+
+ expectEquals(0, compareBooleans(false, false));
+ expectEquals(0, compareBooleans(true, true));
+
+ expectEquals(1, compareBooleans(true, false));
+ }
+
+ public static void testCompareBytes() {
+ expectEquals(-1, compareBytes(Byte.MIN_VALUE, (byte)(Byte.MIN_VALUE + 1)));
+ expectEquals(-1, compareBytes(Byte.MIN_VALUE, (byte)-1));
+ expectEquals(-1, compareBytes(Byte.MIN_VALUE, (byte)0));
+ expectEquals(-1, compareBytes(Byte.MIN_VALUE, (byte)1));
+ expectEquals(-1, compareBytes(Byte.MIN_VALUE, Byte.MAX_VALUE));
+ expectEquals(-1, compareBytes((byte)-1, (byte)0));
+ expectEquals(-1, compareBytes((byte)-1, (byte)1));
+ expectEquals(-1, compareBytes((byte)0, (byte)1));
+
+ expectEquals(0, compareBytes(Byte.MIN_VALUE, Byte.MIN_VALUE));
+ expectEquals(0, compareBytes((byte)-1, (byte)-1));
+ expectEquals(0, compareBytes((byte)0, (byte)0));
+ expectEquals(0, compareBytes((byte)1, (byte)1));
+ expectEquals(0, compareBytes(Byte.MAX_VALUE, Byte.MAX_VALUE));
+
+ expectEquals(1, compareBytes((byte)0, (byte)-1));
+ expectEquals(1, compareBytes((byte)1, (byte)-1));
+ expectEquals(1, compareBytes((byte)1, (byte)0));
+ expectEquals(1, compareBytes(Byte.MAX_VALUE, Byte.MIN_VALUE));
+ expectEquals(1, compareBytes(Byte.MAX_VALUE, (byte)-1));
+ expectEquals(1, compareBytes(Byte.MAX_VALUE, (byte)0));
+ expectEquals(1, compareBytes(Byte.MAX_VALUE, (byte)1));
+ expectEquals(1, compareBytes(Byte.MAX_VALUE, (byte)(Byte.MAX_VALUE - 1)));
+
+ for (byte i = -11; i <= 11; i++) {
+ for (byte j = -11; j <= 11; j++) {
+ int expected = 0;
+ if (i < j) expected = -1;
+ else if (i > j) expected = 1;
+ expectEquals(expected, compareBytes(i, j));
+ }
+ }
+ }
+
+ public static void testCompareShorts() {
+ expectEquals(-1, compareShorts(Short.MIN_VALUE, (short)(Short.MIN_VALUE + 1)));
+ expectEquals(-1, compareShorts(Short.MIN_VALUE, (short)-1));
+ expectEquals(-1, compareShorts(Short.MIN_VALUE, (short)0));
+ expectEquals(-1, compareShorts(Short.MIN_VALUE, (short)1));
+ expectEquals(-1, compareShorts(Short.MIN_VALUE, (short)Short.MAX_VALUE));
+ expectEquals(-1, compareShorts((short)-1, (short)0));
+ expectEquals(-1, compareShorts((short)-1, (short)1));
+ expectEquals(-1, compareShorts((short)0, (short)1));
+
+ expectEquals(0, compareShorts(Short.MIN_VALUE, Short.MIN_VALUE));
+ expectEquals(0, compareShorts((short)-1, (short)-1));
+ expectEquals(0, compareShorts((short)0, (short)0));
+ expectEquals(0, compareShorts((short)1, (short)1));
+ expectEquals(0, compareShorts(Short.MAX_VALUE, Short.MAX_VALUE));
+
+ expectEquals(1, compareShorts((short)0, (short)-1));
+ expectEquals(1, compareShorts((short)1, (short)-1));
+ expectEquals(1, compareShorts((short)1, (short)0));
+ expectEquals(1, compareShorts(Short.MAX_VALUE, Short.MIN_VALUE));
+ expectEquals(1, compareShorts(Short.MAX_VALUE, (short)-1));
+ expectEquals(1, compareShorts(Short.MAX_VALUE, (short)0));
+ expectEquals(1, compareShorts(Short.MAX_VALUE, (short)1));
+ expectEquals(1, compareShorts(Short.MAX_VALUE, (short)(Short.MAX_VALUE - 1)));
+
+ for (short i = -11; i <= 11; i++) {
+ for (short j = -11; j <= 11; j++) {
+ int expected = 0;
+ if (i < j) expected = -1;
+ else if (i > j) expected = 1;
+ expectEquals(expected, compareShorts(i, j));
+ }
+ }
+ }
+
+ public static void testCompareChars() {
+ expectEquals(-1, compareChars((char)0, Character.MAX_VALUE));
+ expectEquals(-1, compareChars((char)0, (char)1));
+
+ expectEquals(0, compareChars((char)0, (char)0));
+ expectEquals(0, compareChars((char)1, (char)1));
+ expectEquals(0, compareChars(Character.MAX_VALUE, Character.MAX_VALUE));
+
+ expectEquals(1, compareChars((char)1, (char)0));
+ expectEquals(1, compareChars(Character.MAX_VALUE, (char)0));
+ expectEquals(1, compareChars(Character.MAX_VALUE, (char)1));
+ expectEquals(1, compareChars(Character.MAX_VALUE, (char)(Character.MAX_VALUE - 1)));
+
+ for (char i = 0; i <= 11; i++) {
+ for (char j = 0; j <= 11; j++) {
+ int expected = 0;
+ if (i < j) expected = -1;
+ else if (i > j) expected = 1;
+ expectEquals(expected, compareChars(i, j));
+ }
+ }
+ }
+
+ public static void testCompareInts() {
+ expectEquals(-1, compareInts(Integer.MIN_VALUE, Integer.MIN_VALUE + 1));
+ expectEquals(-1, compareInts(Integer.MIN_VALUE, -1));
+ expectEquals(-1, compareInts(Integer.MIN_VALUE, 0));
+ expectEquals(-1, compareInts(Integer.MIN_VALUE, 1));
+ expectEquals(-1, compareInts(Integer.MIN_VALUE, Integer.MAX_VALUE));
+ expectEquals(-1, compareInts(-1, 0));
+ expectEquals(-1, compareInts(-1, 1));
+ expectEquals(-1, compareInts(0, 1));
+
+ expectEquals(0, compareInts(Integer.MIN_VALUE, Integer.MIN_VALUE));
+ expectEquals(0, compareInts(-1, -1));
+ expectEquals(0, compareInts(0, 0));
+ expectEquals(0, compareInts(1, 1));
+ expectEquals(0, compareInts(Integer.MAX_VALUE, Integer.MAX_VALUE));
+
+ expectEquals(1, compareInts(0, -1));
+ expectEquals(1, compareInts(1, -1));
+ expectEquals(1, compareInts(1, 0));
+ expectEquals(1, compareInts(Integer.MAX_VALUE, Integer.MIN_VALUE));
+ expectEquals(1, compareInts(Integer.MAX_VALUE, -1));
+ expectEquals(1, compareInts(Integer.MAX_VALUE, 0));
+ expectEquals(1, compareInts(Integer.MAX_VALUE, 1));
+ expectEquals(1, compareInts(Integer.MAX_VALUE, Integer.MAX_VALUE - 1));
for (int i = -11; i <= 11; i++) {
for (int j = -11; j <= 11; j++) {
int expected = 0;
if (i < j) expected = -1;
else if (i > j) expected = 1;
- expectEquals(expected, compare32(i, j));
+ expectEquals(expected, compareInts(i, j));
}
}
+ }
- expectEquals(-1, compare64(Long.MIN_VALUE, Long.MIN_VALUE + 1L));
- expectEquals(-1, compare64(Long.MIN_VALUE, -1L));
- expectEquals(-1, compare64(Long.MIN_VALUE, 0L));
- expectEquals(-1, compare64(Long.MIN_VALUE, 1L));
- expectEquals(-1, compare64(Long.MIN_VALUE, Long.MAX_VALUE));
- expectEquals(-1, compare64(-1L, 0L));
- expectEquals(-1, compare64(-1L, 1L));
- expectEquals(-1, compare64(0L, 1L));
+ public static void testCompareLongs() {
+ expectEquals(-1, compareLongs(Long.MIN_VALUE, Long.MIN_VALUE + 1L));
+ expectEquals(-1, compareLongs(Long.MIN_VALUE, -1L));
+ expectEquals(-1, compareLongs(Long.MIN_VALUE, 0L));
+ expectEquals(-1, compareLongs(Long.MIN_VALUE, 1L));
+ expectEquals(-1, compareLongs(Long.MIN_VALUE, Long.MAX_VALUE));
+ expectEquals(-1, compareLongs(-1L, 0L));
+ expectEquals(-1, compareLongs(-1L, 1L));
+ expectEquals(-1, compareLongs(0L, 1L));
- expectEquals(0, compare64(Long.MIN_VALUE, Long.MIN_VALUE));
- expectEquals(0, compare64(-1L, -1L));
- expectEquals(0, compare64(0L, 0L));
- expectEquals(0, compare64(1L, 1L));
- expectEquals(0, compare64(Long.MAX_VALUE, Long.MAX_VALUE));
+ expectEquals(0, compareLongs(Long.MIN_VALUE, Long.MIN_VALUE));
+ expectEquals(0, compareLongs(-1L, -1L));
+ expectEquals(0, compareLongs(0L, 0L));
+ expectEquals(0, compareLongs(1L, 1L));
+ expectEquals(0, compareLongs(Long.MAX_VALUE, Long.MAX_VALUE));
- expectEquals(1, compare64(0L, -1L));
- expectEquals(1, compare64(1L, -1L));
- expectEquals(1, compare64(1L, 0L));
- expectEquals(1, compare64(Long.MAX_VALUE, Long.MIN_VALUE));
- expectEquals(1, compare64(Long.MAX_VALUE, -1L));
- expectEquals(1, compare64(Long.MAX_VALUE, 0L));
- expectEquals(1, compare64(Long.MAX_VALUE, 1L));
- expectEquals(1, compare64(Long.MAX_VALUE, Long.MAX_VALUE - 1L));
+ expectEquals(1, compareLongs(0L, -1L));
+ expectEquals(1, compareLongs(1L, -1L));
+ expectEquals(1, compareLongs(1L, 0L));
+ expectEquals(1, compareLongs(Long.MAX_VALUE, Long.MIN_VALUE));
+ expectEquals(1, compareLongs(Long.MAX_VALUE, -1L));
+ expectEquals(1, compareLongs(Long.MAX_VALUE, 0L));
+ expectEquals(1, compareLongs(Long.MAX_VALUE, 1L));
+ expectEquals(1, compareLongs(Long.MAX_VALUE, Long.MAX_VALUE - 1L));
- expectEquals(-1, compare64(0x111111117FFFFFFFL, 0x11111111FFFFFFFFL));
- expectEquals(0, compare64(0x111111117FFFFFFFL, 0x111111117FFFFFFFL));
- expectEquals(1, compare64(0x11111111FFFFFFFFL, 0x111111117FFFFFFFL));
+ expectEquals(-1, compareLongs(0x111111117FFFFFFFL, 0x11111111FFFFFFFFL));
+ expectEquals(0, compareLongs(0x111111117FFFFFFFL, 0x111111117FFFFFFFL));
+ expectEquals(1, compareLongs(0x11111111FFFFFFFFL, 0x111111117FFFFFFFL));
for (long i = -11L; i <= 11L; i++) {
for (long j = -11L; j <= 11L; j++) {
int expected = 0;
if (i < j) expected = -1;
else if (i > j) expected = 1;
- expectEquals(expected, compare64(i, j));
+ expectEquals(expected, compareLongs(i, j));
}
}
for (long i = Long.MIN_VALUE; i <= Long.MIN_VALUE + 11L; i++) {
- expectEquals(-1, compare64(i, 0));
+ expectEquals(-1, compareLongs(i, 0));
}
for (long i = Long.MAX_VALUE; i >= Long.MAX_VALUE - 11L; i--) {
- expectEquals(1, compare64(i, 0));
+ expectEquals(1, compareLongs(i, 0));
+ }
+ }
+
+
+ public static void testCompareByteShort() {
+ expectEquals(-1, compareByteShort(Byte.MIN_VALUE, (short)-1));
+ expectEquals(-1, compareByteShort(Byte.MIN_VALUE, (short)0));
+ expectEquals(-1, compareByteShort(Byte.MIN_VALUE, (short)1));
+ expectEquals(-1, compareByteShort(Byte.MIN_VALUE, Short.MAX_VALUE));
+ expectEquals(-1, compareByteShort((byte)-1, (short)0));
+ expectEquals(-1, compareByteShort((byte)-1, (short)1));
+ expectEquals(-1, compareByteShort((byte)0, (short)1));
+ expectEquals(-1, compareByteShort(Byte.MAX_VALUE, (short)(Short.MAX_VALUE - 1)));
+ expectEquals(-1, compareByteShort(Byte.MAX_VALUE, Short.MAX_VALUE));
+
+ expectEquals(0, compareByteShort((byte)-1, (short)-1));
+ expectEquals(0, compareByteShort((byte)0, (short)0));
+ expectEquals(0, compareByteShort((byte)1, (short)1));
+
+ expectEquals(1, compareByteShort(Byte.MIN_VALUE, Short.MIN_VALUE));
+ expectEquals(1, compareByteShort(Byte.MIN_VALUE, (short)(Short.MIN_VALUE + 1)));
+ expectEquals(1, compareByteShort((byte)0, (short)-1));
+ expectEquals(1, compareByteShort((byte)1, (short)-1));
+ expectEquals(1, compareByteShort((byte)1, (short)0));
+ expectEquals(1, compareByteShort(Byte.MAX_VALUE, Short.MIN_VALUE));
+ expectEquals(1, compareByteShort(Byte.MAX_VALUE, (short)-1));
+ expectEquals(1, compareByteShort(Byte.MAX_VALUE, (short)0));
+ expectEquals(1, compareByteShort(Byte.MAX_VALUE, (short)1));
+
+ for (byte i = -11; i <= 11; i++) {
+ for (short j = -11; j <= 11; j++) {
+ int expected = 0;
+ if (i < j) expected = -1;
+ else if (i > j) expected = 1;
+ expectEquals(expected, compareByteShort(i, j));
+ }
+ }
+ }
+
+ public static void testCompareByteChar() {
+ expectEquals(-1, compareByteChar(Byte.MIN_VALUE, (char)0));
+ expectEquals(-1, compareByteChar(Byte.MIN_VALUE, (char)1));
+ expectEquals(-1, compareByteChar(Byte.MIN_VALUE, Character.MAX_VALUE));
+ expectEquals(-1, compareByteChar((byte)-1, (char)0));
+ expectEquals(-1, compareByteChar((byte)-1, (char)1));
+ expectEquals(-1, compareByteChar((byte)0, (char)1));
+ expectEquals(-1, compareByteChar(Byte.MAX_VALUE, (char)(Character.MAX_VALUE - 1)));
+ expectEquals(-1, compareByteChar(Byte.MAX_VALUE, Character.MAX_VALUE));
+
+ expectEquals(0, compareByteChar((byte)0, (char)0));
+ expectEquals(0, compareByteChar((byte)1, (char)1));
+
+ expectEquals(1, compareByteChar((byte)1, (char)0));
+ expectEquals(1, compareByteChar(Byte.MAX_VALUE, (char)0));
+ expectEquals(1, compareByteChar(Byte.MAX_VALUE, (char)1));
+
+ for (byte i = -11; i <= 11; i++) {
+ for (char j = 0; j <= 11; j++) {
+ int expected = 0;
+ if (i < j) expected = -1;
+ else if (i > j) expected = 1;
+ expectEquals(expected, compareByteChar(i, j));
+ }
+ }
+ }
+
+ public static void testCompareByteInt() {
+ expectEquals(-1, compareByteInt(Byte.MIN_VALUE, -1));
+ expectEquals(-1, compareByteInt(Byte.MIN_VALUE, 0));
+ expectEquals(-1, compareByteInt(Byte.MIN_VALUE, 1));
+ expectEquals(-1, compareByteInt(Byte.MIN_VALUE, Integer.MAX_VALUE));
+ expectEquals(-1, compareByteInt((byte)-1, 0));
+ expectEquals(-1, compareByteInt((byte)-1, 1));
+ expectEquals(-1, compareByteInt((byte)0, 1));
+ expectEquals(-1, compareByteInt(Byte.MAX_VALUE, Integer.MAX_VALUE - 1));
+ expectEquals(-1, compareByteInt(Byte.MAX_VALUE, Integer.MAX_VALUE));
+
+ expectEquals(0, compareByteInt((byte)-1, -1));
+ expectEquals(0, compareByteInt((byte)0, 0));
+ expectEquals(0, compareByteInt((byte)1, 1));
+
+ expectEquals(1, compareByteInt(Byte.MIN_VALUE, Integer.MIN_VALUE));
+ expectEquals(1, compareByteInt(Byte.MIN_VALUE, Integer.MIN_VALUE + 1));
+ expectEquals(1, compareByteInt((byte)0, -1));
+ expectEquals(1, compareByteInt((byte)1, -1));
+ expectEquals(1, compareByteInt((byte)1, 0));
+ expectEquals(1, compareByteInt(Byte.MAX_VALUE, Integer.MIN_VALUE));
+ expectEquals(1, compareByteInt(Byte.MAX_VALUE, -1));
+ expectEquals(1, compareByteInt(Byte.MAX_VALUE, 0));
+ expectEquals(1, compareByteInt(Byte.MAX_VALUE, 1));
+
+ for (byte i = -11; i <= 11; i++) {
+ for (int j = -11; j <= 11; j++) {
+ int expected = 0;
+ if (i < j) expected = -1;
+ else if (i > j) expected = 1;
+ expectEquals(expected, compareByteInt(i, j));
+ }
+ }
+ }
+
+
+ public static void testCompareShortByte() {
+ expectEquals(-1, compareShortByte(Short.MIN_VALUE, Byte.MIN_VALUE));
+ expectEquals(-1, compareShortByte(Short.MIN_VALUE, (byte)(Byte.MIN_VALUE + 1)));
+ expectEquals(-1, compareShortByte(Short.MIN_VALUE, (byte)-1));
+ expectEquals(-1, compareShortByte(Short.MIN_VALUE, (byte)0));
+ expectEquals(-1, compareShortByte(Short.MIN_VALUE, (byte)1));
+ expectEquals(-1, compareShortByte(Short.MIN_VALUE, Byte.MAX_VALUE));
+ expectEquals(-1, compareShortByte((short)-1, (byte)0));
+ expectEquals(-1, compareShortByte((short)-1, (byte)1));
+ expectEquals(-1, compareShortByte((short)0, (byte)1));
+
+ expectEquals(0, compareShortByte((short)-1, (byte)-1));
+ expectEquals(0, compareShortByte((short)0, (byte)0));
+ expectEquals(0, compareShortByte((short)1, (byte)1));
+
+ expectEquals(1, compareShortByte((short)0, (byte)-1));
+ expectEquals(1, compareShortByte((short)1, (byte)-1));
+ expectEquals(1, compareShortByte((short)1, (byte)0));
+ expectEquals(1, compareShortByte(Short.MAX_VALUE, Byte.MIN_VALUE));
+ expectEquals(1, compareShortByte(Short.MAX_VALUE, (byte)-1));
+ expectEquals(1, compareShortByte(Short.MAX_VALUE, (byte)0));
+ expectEquals(1, compareShortByte(Short.MAX_VALUE, (byte)1));
+ expectEquals(1, compareShortByte(Short.MAX_VALUE, (byte)(Byte.MAX_VALUE - 1)));
+ expectEquals(1, compareShortByte(Short.MAX_VALUE, Byte.MAX_VALUE));
+
+ for (short i = -11; i <= 11; i++) {
+ for (byte j = -11; j <= 11; j++) {
+ int expected = 0;
+ if (i < j) expected = -1;
+ else if (i > j) expected = 1;
+ expectEquals(expected, compareShortByte(i, j));
+ }
+ }
+ }
+
+ public static void testCompareShortChar() {
+ expectEquals(-1, compareShortChar(Short.MIN_VALUE, (char)0));
+ expectEquals(-1, compareShortChar(Short.MIN_VALUE, (char)1));
+ expectEquals(-1, compareShortChar(Short.MIN_VALUE, Character.MAX_VALUE));
+ expectEquals(-1, compareShortChar((short)-1, (char)0));
+ expectEquals(-1, compareShortChar((short)-1, (char)1));
+ expectEquals(-1, compareShortChar((short)0, (char)1));
+ expectEquals(-1, compareShortChar(Short.MAX_VALUE, (char)(Character.MAX_VALUE - 1)));
+ expectEquals(-1, compareShortChar(Short.MAX_VALUE, Character.MAX_VALUE));
+
+ expectEquals(0, compareShortChar((short)0, (char)0));
+ expectEquals(0, compareShortChar((short)1, (char)1));
+
+ expectEquals(1, compareShortChar((short)1, (char)0));
+ expectEquals(1, compareShortChar(Short.MAX_VALUE, (char)0));
+ expectEquals(1, compareShortChar(Short.MAX_VALUE, (char)1));
+
+ for (short i = -11; i <= 11; i++) {
+ for (char j = 0; j <= 11; j++) {
+ int expected = 0;
+ if (i < j) expected = -1;
+ else if (i > j) expected = 1;
+ expectEquals(expected, compareShortChar(i, j));
+ }
+ }
+ }
+
+ public static void testCompareShortInt() {
+ expectEquals(-1, compareShortInt(Short.MIN_VALUE, -1));
+ expectEquals(-1, compareShortInt(Short.MIN_VALUE, 0));
+ expectEquals(-1, compareShortInt(Short.MIN_VALUE, 1));
+ expectEquals(-1, compareShortInt(Short.MIN_VALUE, Integer.MAX_VALUE));
+ expectEquals(-1, compareShortInt((short)-1, 0));
+ expectEquals(-1, compareShortInt((short)-1, 1));
+ expectEquals(-1, compareShortInt((short)0, 1));
+ expectEquals(-1, compareShortInt(Short.MAX_VALUE, Integer.MAX_VALUE - 1));
+ expectEquals(-1, compareShortInt(Short.MAX_VALUE, Integer.MAX_VALUE));
+
+ expectEquals(0, compareShortInt((short)-1, -1));
+ expectEquals(0, compareShortInt((short)0, 0));
+ expectEquals(0, compareShortInt((short)1, 1));
+
+ expectEquals(1, compareShortInt(Short.MIN_VALUE, Integer.MIN_VALUE));
+ expectEquals(1, compareShortInt(Short.MIN_VALUE, Integer.MIN_VALUE + 1));
+ expectEquals(1, compareShortInt((short)0, -1));
+ expectEquals(1, compareShortInt((short)1, -1));
+ expectEquals(1, compareShortInt((short)1, 0));
+ expectEquals(1, compareShortInt(Short.MAX_VALUE, Integer.MIN_VALUE));
+ expectEquals(1, compareShortInt(Short.MAX_VALUE, -1));
+ expectEquals(1, compareShortInt(Short.MAX_VALUE, 0));
+ expectEquals(1, compareShortInt(Short.MAX_VALUE, 1));
+
+ for (short i = -11; i <= 11; i++) {
+ for (int j = -11; j <= 11; j++) {
+ int expected = 0;
+ if (i < j) expected = -1;
+ else if (i > j) expected = 1;
+ expectEquals(expected, compareShortInt(i, j));
+ }
}
+ }
+
+
+ public static void testCompareCharByte() {
+ expectEquals(-1, compareCharByte((char)0, (byte)1));
+ expectEquals(-1, compareCharByte((char)0, Byte.MAX_VALUE));
+
+ expectEquals(0, compareCharByte((char)0, (byte)0));
+ expectEquals(0, compareCharByte((char)1, (byte)1));
+
+ expectEquals(1, compareCharByte((char)0, Byte.MIN_VALUE));
+ expectEquals(1, compareCharByte((char)0, (byte)(Byte.MIN_VALUE + 1)));
+ expectEquals(1, compareCharByte((char)0, (byte)-1));
+ expectEquals(1, compareCharByte((char)1, (byte)-1));
+ expectEquals(1, compareCharByte((char)1, (byte)0));
+ expectEquals(1, compareCharByte(Character.MAX_VALUE, Byte.MIN_VALUE));
+ expectEquals(1, compareCharByte(Character.MAX_VALUE, (byte)-1));
+ expectEquals(1, compareCharByte(Character.MAX_VALUE, (byte)0));
+ expectEquals(1, compareCharByte(Character.MAX_VALUE, (byte)1));
+ expectEquals(1, compareCharByte(Character.MAX_VALUE, (byte)(Byte.MAX_VALUE - 1)));
+ expectEquals(1, compareCharByte(Character.MAX_VALUE, Byte.MAX_VALUE));
+
+ for (char i = 0; i <= 11; i++) {
+ for (byte j = -11; j <= 11; j++) {
+ int expected = 0;
+ if (i < j) expected = -1;
+ else if (i > j) expected = 1;
+ expectEquals(expected, compareCharByte(i, j));
+ }
+ }
+ }
+
+ public static void testCompareCharShort() {
+ expectEquals(-1, compareCharShort((char)0, (short)1));
+ expectEquals(-1, compareCharShort((char)0, Short.MAX_VALUE));
+
+ expectEquals(0, compareCharShort((char)0, (short)0));
+ expectEquals(0, compareCharShort((char)1, (short)1));
+
+ expectEquals(1, compareCharShort((char)0, Short.MIN_VALUE));
+ expectEquals(1, compareCharShort((char)0, (short)(Short.MIN_VALUE + 1)));
+ expectEquals(1, compareCharShort((char)0, (short)-1));
+ expectEquals(1, compareCharShort((char)1, (short)-1));
+ expectEquals(1, compareCharShort((char)1, (short)0));
+ expectEquals(1, compareCharShort(Character.MAX_VALUE, Short.MIN_VALUE));
+ expectEquals(1, compareCharShort(Character.MAX_VALUE, (short)-1));
+ expectEquals(1, compareCharShort(Character.MAX_VALUE, (short)0));
+ expectEquals(1, compareCharShort(Character.MAX_VALUE, (short)1));
+ expectEquals(1, compareCharShort(Character.MAX_VALUE, (short)(Short.MAX_VALUE - 1)));
+ expectEquals(1, compareCharShort(Character.MAX_VALUE, Short.MAX_VALUE));
+
+ for (char i = 0; i <= 11; i++) {
+ for (short j = -11; j <= 11; j++) {
+ int expected = 0;
+ if (i < j) expected = -1;
+ else if (i > j) expected = 1;
+ expectEquals(expected, compareCharShort(i, j));
+ }
+ }
+ }
+
+ public static void testCompareCharInt() {
+ expectEquals(-1, compareCharInt((char)0, 1));
+ expectEquals(-1, compareCharInt((char)0, Integer.MAX_VALUE));
+ expectEquals(-1, compareCharInt(Character.MAX_VALUE, Integer.MAX_VALUE - 1));
+ expectEquals(-1, compareCharInt(Character.MAX_VALUE, Integer.MAX_VALUE));
+
+ expectEquals(0, compareCharInt((char)0, 0));
+ expectEquals(0, compareCharInt((char)1, 1));
+
+ expectEquals(1, compareCharInt((char)0, Integer.MIN_VALUE));
+ expectEquals(1, compareCharInt((char)0, Integer.MIN_VALUE + 1));
+ expectEquals(1, compareCharInt((char)0, -1));
+ expectEquals(1, compareCharInt((char)1, -1));
+ expectEquals(1, compareCharInt((char)1, 0));
+ expectEquals(1, compareCharInt(Character.MAX_VALUE, Integer.MIN_VALUE));
+ expectEquals(1, compareCharInt(Character.MAX_VALUE, -1));
+ expectEquals(1, compareCharInt(Character.MAX_VALUE, 0));
+ expectEquals(1, compareCharInt(Character.MAX_VALUE, 1));
+
+ for (char i = 0; i <= 11; i++) {
+ for (int j = -11; j <= 11; j++) {
+ int expected = 0;
+ if (i < j) expected = -1;
+ else if (i > j) expected = 1;
+ expectEquals(expected, compareCharInt(i, j));
+ }
+ }
+ }
+
+
+ public static void testCompareIntByte() {
+ expectEquals(-1, compareIntByte(Integer.MIN_VALUE, Byte.MIN_VALUE));
+ expectEquals(-1, compareIntByte(Integer.MIN_VALUE, (byte)(Byte.MIN_VALUE + 1)));
+ expectEquals(-1, compareIntByte(Integer.MIN_VALUE, (byte)-1));
+ expectEquals(-1, compareIntByte(Integer.MIN_VALUE, (byte)0));
+ expectEquals(-1, compareIntByte(Integer.MIN_VALUE, (byte)1));
+ expectEquals(-1, compareIntByte(Integer.MIN_VALUE, Byte.MAX_VALUE));
+ expectEquals(-1, compareIntByte(-1, (byte)0));
+ expectEquals(-1, compareIntByte(-1, (byte)1));
+ expectEquals(-1, compareIntByte(0, (byte)1));
+
+ expectEquals(0, compareIntByte(-1, (byte)-1));
+ expectEquals(0, compareIntByte(0, (byte)0));
+ expectEquals(0, compareIntByte(1, (byte)1));
+
+ expectEquals(1, compareIntByte(0, (byte)-1));
+ expectEquals(1, compareIntByte(1, (byte)-1));
+ expectEquals(1, compareIntByte(1, (byte)0));
+ expectEquals(1, compareIntByte(Integer.MAX_VALUE, Byte.MIN_VALUE));
+ expectEquals(1, compareIntByte(Integer.MAX_VALUE, (byte)-1));
+ expectEquals(1, compareIntByte(Integer.MAX_VALUE, (byte)0));
+ expectEquals(1, compareIntByte(Integer.MAX_VALUE, (byte)1));
+ expectEquals(1, compareIntByte(Integer.MAX_VALUE, (byte)(Byte.MAX_VALUE - 1)));
+ expectEquals(1, compareIntByte(Integer.MAX_VALUE, Byte.MAX_VALUE));
+
+ for (int i = -11; i <= 11; i++) {
+ for (byte j = -11; j <= 11; j++) {
+ int expected = 0;
+ if (i < j) expected = -1;
+ else if (i > j) expected = 1;
+ expectEquals(expected, compareIntByte(i, j));
+ }
+ }
+ }
+
+ public static void testCompareIntShort() {
+ expectEquals(-1, compareIntShort(Integer.MIN_VALUE, Short.MIN_VALUE));
+ expectEquals(-1, compareIntShort(Integer.MIN_VALUE, (short)(Short.MIN_VALUE + 1)));
+ expectEquals(-1, compareIntShort(Integer.MIN_VALUE, (short)-1));
+ expectEquals(-1, compareIntShort(Integer.MIN_VALUE, (short)0));
+ expectEquals(-1, compareIntShort(Integer.MIN_VALUE, (short)1));
+ expectEquals(-1, compareIntShort(Integer.MIN_VALUE, Short.MAX_VALUE));
+ expectEquals(-1, compareIntShort(-1, (short)0));
+ expectEquals(-1, compareIntShort(-1, (short)1));
+ expectEquals(-1, compareIntShort(0, (short)1));
+
+ expectEquals(0, compareIntShort(-1, (short)-1));
+ expectEquals(0, compareIntShort(0, (short)0));
+ expectEquals(0, compareIntShort(1, (short)1));
+
+ expectEquals(1, compareIntShort(0, (short)-1));
+ expectEquals(1, compareIntShort(1, (short)-1));
+ expectEquals(1, compareIntShort(1, (short)0));
+ expectEquals(1, compareIntShort(Integer.MAX_VALUE, Short.MIN_VALUE));
+ expectEquals(1, compareIntShort(Integer.MAX_VALUE, (short)-1));
+ expectEquals(1, compareIntShort(Integer.MAX_VALUE, (short)0));
+ expectEquals(1, compareIntShort(Integer.MAX_VALUE, (short)1));
+ expectEquals(1, compareIntShort(Integer.MAX_VALUE, (short)(Short.MAX_VALUE - 1)));
+ expectEquals(1, compareIntShort(Integer.MAX_VALUE, Short.MAX_VALUE));
+
+ for (int i = -11; i <= 11; i++) {
+ for (short j = -11; j <= 11; j++) {
+ int expected = 0;
+ if (i < j) expected = -1;
+ else if (i > j) expected = 1;
+ expectEquals(expected, compareIntShort(i, j));
+ }
+ }
+ }
+
+ public static void testCompareIntChar() {
+ expectEquals(-1, compareIntChar(Integer.MIN_VALUE, (char)0));
+ expectEquals(-1, compareIntChar(Integer.MIN_VALUE, (char)1));
+ expectEquals(-1, compareIntChar(Integer.MIN_VALUE, Character.MAX_VALUE));
+ expectEquals(-1, compareIntChar(-1, (char)0));
+ expectEquals(-1, compareIntChar(-1, (char)1));
+ expectEquals(-1, compareIntChar(0, (char)1));
+
+ expectEquals(0, compareIntChar(0, (char)0));
+ expectEquals(0, compareIntChar(1, (char)1));
+
+ expectEquals(1, compareIntChar(1, (char)0));
+ expectEquals(1, compareIntChar(Integer.MAX_VALUE, (char)0));
+ expectEquals(1, compareIntChar(Integer.MAX_VALUE, (char)1));
+ expectEquals(1, compareIntChar(Integer.MAX_VALUE, (char)(Character.MAX_VALUE - 1)));
+ expectEquals(1, compareIntChar(Integer.MAX_VALUE, Character.MAX_VALUE));
+
+ for (int i = -11; i <= 11; i++) {
+ for (char j = 0; j <= 11; j++) {
+ int expected = 0;
+ if (i < j) expected = -1;
+ else if (i > j) expected = 1;
+ expectEquals(expected, compareIntChar(i, j));
+ }
+ }
+ }
+
+
+ public static void main(String args[]) {
+ testCompareBooleans();
+ testCompareBytes();
+ testCompareShorts();
+ testCompareChars();
+ testCompareInts();
+ testCompareLongs();
+
+ testCompareByteShort();
+ testCompareByteChar();
+ testCompareByteInt();
+
+ testCompareShortByte();
+ testCompareShortChar();
+ testCompareShortInt();
+
+ testCompareCharByte();
+ testCompareCharShort();
+ testCompareCharInt();
+
+ testCompareIntByte();
+ testCompareIntShort();
+ testCompareIntChar();
System.out.println("passed");
}
diff --git a/test/582-checker-bce-length/expected.txt b/test/582-checker-bce-length/expected.txt
new file mode 100644
index 0000000000..b0aad4deb5
--- /dev/null
+++ b/test/582-checker-bce-length/expected.txt
@@ -0,0 +1 @@
+passed
diff --git a/test/582-checker-bce-length/info.txt b/test/582-checker-bce-length/info.txt
new file mode 100644
index 0000000000..cb826cd488
--- /dev/null
+++ b/test/582-checker-bce-length/info.txt
@@ -0,0 +1 @@
+Regression test on deopt bounds check elimination.
diff --git a/test/582-checker-bce-length/src/Main.java b/test/582-checker-bce-length/src/Main.java
new file mode 100644
index 0000000000..3565b6b59b
--- /dev/null
+++ b/test/582-checker-bce-length/src/Main.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2015 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.
+ */
+
+/**
+ * Regression test on duplicate removal of same bounds check.
+ */
+public class Main {
+
+ /// CHECK-START: void Main.doit1(int[]) BCE (before)
+ /// CHECK-DAG: BoundsCheck
+ /// CHECK-DAG: BoundsCheck
+ /// CHECK-DAG: BoundsCheck
+ /// CHECK-DAG: BoundsCheck
+ //
+ /// CHECK-START: void Main.doit1(int[]) BCE (after)
+ /// CHECK-DAG: BoundsCheck
+ /// CHECK-DAG: BoundsCheck
+ /// CHECK-DAG: BoundsCheck
+ /// CHECK-DAG: BoundsCheck
+ //
+ /// CHECK-START: void Main.doit1(int[]) BCE (after)
+ /// CHECK-NOT: Deoptimize
+ public static void doit1(int[] a) {
+ a[a.length-3] = 1;
+ a[a.length-2] = 2;
+ a[a.length-1] = 3;
+ // This introduces a problematic BoundsCheck(x,x) node
+ // (1) certain OOB, so should be rejected
+ // (2) exposed bug in removing same BC twice if (1) would not be done.
+ a[a.length-0] = 4;
+ }
+
+ /// CHECK-START: void Main.doit2(int[]) BCE (before)
+ /// CHECK-DAG: BoundsCheck
+ /// CHECK-DAG: BoundsCheck
+ /// CHECK-DAG: BoundsCheck
+ /// CHECK-DAG: BoundsCheck
+ //
+ /// CHECK-START: void Main.doit2(int[]) BCE (after)
+ /// CHECK-DAG: Deoptimize
+ /// CHECK-DAG: Deoptimize
+ //
+ /// CHECK-START: void Main.doit2(int[]) BCE (after)
+ /// CHECK-NOT: BoundsCheck
+ public static void doit2(int[] a) {
+ a[a.length-4] = -101;
+ a[a.length-3] = -102;
+ a[a.length-2] = -103;
+ a[a.length-1] = -104;
+ }
+
+ public static void main(String[] args) {
+ int[] a = new int[4];
+
+ int fail = 0;
+ try {
+ doit1(a);
+ } catch (ArrayIndexOutOfBoundsException e) {
+ fail++;
+ }
+ expectEquals(1, fail);
+ expectEquals(0, a[0]);
+ expectEquals(1, a[1]);
+ expectEquals(2, a[2]);
+ expectEquals(3, a[3]);
+
+ try {
+ doit2(a);
+ } catch (ArrayIndexOutOfBoundsException e) {
+ fail++;
+ }
+ expectEquals(1, fail);
+ expectEquals(-101, a[0]);
+ expectEquals(-102, a[1]);
+ expectEquals(-103, a[2]);
+ expectEquals(-104, a[3]);
+
+ System.out.println("passed");
+ }
+
+ private static void expectEquals(int expected, int result) {
+ if (expected != result) {
+ throw new Error("Expected: " + expected + ", found: " + result);
+ }
+ }
+}
diff --git a/test/583-checker-zero/expected.txt b/test/583-checker-zero/expected.txt
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/test/583-checker-zero/expected.txt
diff --git a/test/583-checker-zero/info.txt b/test/583-checker-zero/info.txt
new file mode 100644
index 0000000000..8ec5d4845e
--- /dev/null
+++ b/test/583-checker-zero/info.txt
@@ -0,0 +1,2 @@
+Regression test for optimizing that used to think 0.0 has the same bits
+as -0.0.
diff --git a/test/583-checker-zero/src/Main.java b/test/583-checker-zero/src/Main.java
new file mode 100644
index 0000000000..f1f7f0572d
--- /dev/null
+++ b/test/583-checker-zero/src/Main.java
@@ -0,0 +1,29 @@
+/*
+ * 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.
+ */
+
+public class Main {
+ // Test that by inlining new Float(-0f), we still keep the store of
+ // -0f to the instance field. We used to remove it due to wrong assumptions
+ // around HConstant.IsZero.
+
+ /// CHECK-START: void Main.main(java.lang.String[]) inliner (after)
+ /// CHECK: InstanceFieldSet
+ public static void main(String[] args) {
+ if (new Float(0f).equals(new Float(-0f))) {
+ throw new Error("Expected not equal");
+ }
+ }
+}
diff --git a/test/584-checker-div-bool/expected.txt b/test/584-checker-div-bool/expected.txt
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/test/584-checker-div-bool/expected.txt
diff --git a/test/584-checker-div-bool/info.txt b/test/584-checker-div-bool/info.txt
new file mode 100644
index 0000000000..59650d5663
--- /dev/null
+++ b/test/584-checker-div-bool/info.txt
@@ -0,0 +1,2 @@
+Regression test for optimizing that used to not accept
+HDivZeroCheck taking a boolean.
diff --git a/test/584-checker-div-bool/src/Main.java b/test/584-checker-div-bool/src/Main.java
new file mode 100644
index 0000000000..fadc995d1a
--- /dev/null
+++ b/test/584-checker-div-bool/src/Main.java
@@ -0,0 +1,41 @@
+/*
+ * 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.
+ */
+
+public class Main {
+
+ public static void main(String[] args) {
+ try {
+ foo(intField);
+ throw new Error("Expected ArithmeticException");
+ } catch (ArithmeticException e) {
+ // expected
+ }
+ }
+
+ /// CHECK-START: int Main.foo(int) register (after)
+ /// CHECK: <<BoolField:z\d+>> StaticFieldGet
+ /// CHECK: DivZeroCheck [<<BoolField>>]
+ public static int foo(int a) {
+ return a / bar();
+ }
+
+ public static int bar() {
+ return booleanField ? 1 : 0;
+ }
+
+ public static boolean booleanField;
+ public static int intField;
+}
diff --git a/test/585-inline-unresolved/expected.txt b/test/585-inline-unresolved/expected.txt
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/test/585-inline-unresolved/expected.txt
diff --git a/test/585-inline-unresolved/info.txt b/test/585-inline-unresolved/info.txt
new file mode 100644
index 0000000000..414f638a1c
--- /dev/null
+++ b/test/585-inline-unresolved/info.txt
@@ -0,0 +1,2 @@
+Regression test for optimizing that used to crash when inlining
+a method whose return type is unresolved.
diff --git a/test/585-inline-unresolved/smali/TestCase.smali b/test/585-inline-unresolved/smali/TestCase.smali
new file mode 100644
index 0000000000..f260092847
--- /dev/null
+++ b/test/585-inline-unresolved/smali/TestCase.smali
@@ -0,0 +1,48 @@
+# 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.
+
+.class public LTestCase;
+
+.super Ljava/lang/Object;
+
+.field static private test1:Z
+
+.method public static topLevel()V
+ .registers 1
+ invoke-static {}, LTestCase;->$inline$foo()LUnresolved;
+ return-void
+.end method
+
+# We need multiple returns to trigger the crash.
+.method public static $inline$foo()LUnresolved;
+ .registers 2
+ const v1, 0x0
+ sget-boolean v0, LTestCase;->test1:Z
+ if-eqz v0, :other_return
+ return-object v1
+ :other_return
+ invoke-static {}, LTestCase;->$noinline$bar()LUnresolved;
+ move-result-object v0
+ return-object v0
+.end method
+
+.method public static $noinline$bar()LUnresolved;
+ .registers 2
+ const v1, 0x0
+ sget-boolean v0, LTestCase;->test1:Z
+ if-eqz v0, :return
+ throw v1
+ :return
+ return-object v1
+.end method
diff --git a/test/585-inline-unresolved/src/Main.java b/test/585-inline-unresolved/src/Main.java
new file mode 100644
index 0000000000..67ad4d2263
--- /dev/null
+++ b/test/585-inline-unresolved/src/Main.java
@@ -0,0 +1,22 @@
+/*
+ * 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.
+ */
+
+public class Main {
+ public static void main(String[] args) throws Exception {
+ Class<?> c = Class.forName("TestCase");
+ c.getMethod("topLevel").invoke(null);
+ }
+}
diff --git a/test/Android.run-test.mk b/test/Android.run-test.mk
index 7036bdcaf5..bb7daf821b 100644
--- a/test/Android.run-test.mk
+++ b/test/Android.run-test.mk
@@ -223,12 +223,9 @@ ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),$(RUN_TYPES),
# Disable 097-duplicate-method while investigation (broken by latest Jack release, b/27358065)
# Disable 137-cfi (b/27391690).
-# Disable 536-checker-needs-access-check and 537-checker-inline-and-unverified (b/27425061)
# Disable 577-profile-foreign-dex (b/27454772).
TEST_ART_BROKEN_ALL_TARGET_TESTS := \
097-duplicate-method \
- 536-checker-needs-access-check \
- 537-checker-inline-and-unverified \
577-profile-foreign-dex \
ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),$(RUN_TYPES),$(PREBUILD_TYPES), \
diff --git a/test/etc/default-build b/test/etc/default-build
index 5f78496c3f..d048757a97 100755
--- a/test/etc/default-build
+++ b/test/etc/default-build
@@ -54,6 +54,12 @@ else
HAS_SRC_EX=false
fi
+if [ -d src-dex2oat-unresolved ]; then
+ HAS_SRC_DEX2OAT_UNRESOLVED=true
+else
+ HAS_SRC_DEX2OAT_UNRESOLVED=false
+fi
+
DX_FLAGS=""
SKIP_DX_MERGER="false"
EXPERIMENTAL=""
@@ -116,59 +122,80 @@ if ! [ "${HAS_SRC}" = "true" ] && ! [ "${HAS_SRC2}" = "true" ]; then
SKIP_DX_MERGER="true"
fi
-if [ ${USE_JACK} = "true" ]; then
- # Jack toolchain
- if [ "${HAS_SRC}" = "true" ]; then
- if [ "${HAS_SRC_MULTIDEX}" = "true" ]; then
- # Compile src and src-multidex in the same .jack file. We will apply multidex partitioning
- # when creating the output .dex file.
- ${JACK} ${JACK_ARGS} --output-jack src.jack src src src-multidex
- jack_extra_args="${jack_extra_args} -D jack.dex.output.policy=minimal-multidex"
- jack_extra_args="${jack_extra_args} -D jack.preprocessor=true"
- jack_extra_args="${jack_extra_args} -D jack.preprocessor.file=multidex.jpp"
- else
- ${JACK} ${JACK_ARGS} --output-jack src.jack src
+if [ ${HAS_SRC_DEX2OAT_UNRESOLVED} = "true" ]; then
+ mkdir classes
+ mkdir classes-ex
+ ${JAVAC} ${JAVAC_ARGS} -implicit:none -sourcepath src-dex2oat-unresolved -d classes `find src -name '*.java'`
+ ${JAVAC} ${JAVAC_ARGS} -implicit:none -sourcepath src -d classes-ex `find src-dex2oat-unresolved -name '*.java'`
+ if [ ${USE_JACK} = "true" ]; then
+ jar cf classes.jill.jar -C classes .
+ jar cf classes-ex.jill.jar -C classes-ex .
+
+ ${JACK} --import classes-ex.jill.jar --output-dex .
+ zip ${TEST_NAME}-ex.jar classes.dex
+ ${JACK} --import classes.jill.jar --output-dex .
+ else
+ if [ ${NEED_DEX} = "true" ]; then
+ ${DX} -JXmx256m --debug --dex --dump-to=classes-ex.lst --output=classes.dex --dump-width=1000 classes-ex
+ zip ${TEST_NAME}-ex.jar classes.dex
+ ${DX} -JXmx256m --debug --dex --dump-to=classes.lst --output=classes.dex --dump-width=1000 classes
fi
- jack_extra_args="${jack_extra_args} --import src.jack"
fi
+else
+ if [ ${USE_JACK} = "true" ]; then
+ # Jack toolchain
+ if [ "${HAS_SRC}" = "true" ]; then
+ if [ "${HAS_SRC_MULTIDEX}" = "true" ]; then
+ # Compile src and src-multidex in the same .jack file. We will apply multidex partitioning
+ # when creating the output .dex file.
+ ${JACK} ${JACK_ARGS} --output-jack src.jack src src src-multidex
+ jack_extra_args="${jack_extra_args} -D jack.dex.output.policy=minimal-multidex"
+ jack_extra_args="${jack_extra_args} -D jack.preprocessor=true"
+ jack_extra_args="${jack_extra_args} -D jack.preprocessor.file=multidex.jpp"
+ else
+ ${JACK} ${JACK_ARGS} --output-jack src.jack src
+ fi
+ jack_extra_args="${jack_extra_args} --import src.jack"
+ fi
- if [ "${HAS_SRC2}" = "true" ]; then
- ${JACK} ${JACK_ARGS} --output-jack src2.jack src2
- # In case of duplicate classes, we want to take into account the classes from src2. Therefore
- # we apply the 'keep-first' policy and import src2.jack file *before* the src.jack file.
- jack_extra_args="${jack_extra_args} -D jack.import.type.policy=keep-first"
- jack_extra_args="--import src2.jack ${jack_extra_args}"
- fi
+ if [ "${HAS_SRC2}" = "true" ]; then
+ ${JACK} ${JACK_ARGS} --output-jack src2.jack src2
+ # In case of duplicate classes, we want to take into account the classes from src2. Therefore
+ # we apply the 'keep-first' policy and import src2.jack file *before* the src.jack file.
+ jack_extra_args="${jack_extra_args} -D jack.import.type.policy=keep-first"
+ jack_extra_args="--import src2.jack ${jack_extra_args}"
+ fi
- # Compile jack files into a DEX file.
- if [ "${HAS_SRC}" = "true" ] || [ "${HAS_SRC2}" = "true" ]; then
- ${JACK} ${JACK_ARGS} ${jack_extra_args} --output-dex .
- fi
-else
- # Legacy toolchain with javac+dx
- if [ "${HAS_SRC}" = "true" ]; then
- mkdir classes
- ${JAVAC} ${JAVAC_ARGS} -implicit:none -classpath src-multidex -d classes `find src -name '*.java'`
- fi
+ # Compile jack files into a DEX file.
+ if [ "${HAS_SRC}" = "true" ] || [ "${HAS_SRC2}" = "true" ]; then
+ ${JACK} ${JACK_ARGS} ${jack_extra_args} --output-dex .
+ fi
+ else
+ # Legacy toolchain with javac+dx
+ if [ "${HAS_SRC}" = "true" ]; then
+ mkdir classes
+ ${JAVAC} ${JAVAC_ARGS} -implicit:none -classpath src-multidex -d classes `find src -name '*.java'`
+ fi
- if [ "${HAS_SRC_MULTIDEX}" = "true" ]; then
- mkdir classes2
- ${JAVAC} -implicit:none -classpath src -d classes2 `find src-multidex -name '*.java'`
- if [ ${NEED_DEX} = "true" ]; then
- ${DX} -JXmx256m --debug --dex --dump-to=classes2.lst --output=classes2.dex \
- --dump-width=1000 ${DX_FLAGS} classes2
+ if [ "${HAS_SRC_MULTIDEX}" = "true" ]; then
+ mkdir classes2
+ ${JAVAC} -implicit:none -classpath src -d classes2 `find src-multidex -name '*.java'`
+ if [ ${NEED_DEX} = "true" ]; then
+ ${DX} -JXmx256m --debug --dex --dump-to=classes2.lst --output=classes2.dex \
+ --dump-width=1000 ${DX_FLAGS} classes2
+ fi
fi
- fi
- if [ "${HAS_SRC2}" = "true" ]; then
- mkdir -p classes
- ${JAVAC} ${JAVAC_ARGS} -d classes `find src2 -name '*.java'`
- fi
+ if [ "${HAS_SRC2}" = "true" ]; then
+ mkdir -p classes
+ ${JAVAC} ${JAVAC_ARGS} -d classes `find src2 -name '*.java'`
+ fi
- if [ "${HAS_SRC}" = "true" ] || [ "${HAS_SRC2}" = "true" ]; then
- if [ ${NEED_DEX} = "true" -a ${SKIP_DX_MERGER} = "false" ]; then
- ${DX} -JXmx256m --debug --dex --dump-to=classes.lst --output=classes.dex \
- --dump-width=1000 ${DX_FLAGS} classes
+ if [ "${HAS_SRC}" = "true" ] || [ "${HAS_SRC2}" = "true" ]; then
+ if [ ${NEED_DEX} = "true" -a ${SKIP_DX_MERGER} = "false" ]; then
+ ${DX} -JXmx256m --debug --dex --dump-to=classes.lst --output=classes.dex \
+ --dump-width=1000 ${DX_FLAGS} classes
+ fi
fi
fi
fi
diff --git a/test/etc/run-test-jar b/test/etc/run-test-jar
index 762d9a45ee..2db1e6c947 100755
--- a/test/etc/run-test-jar
+++ b/test/etc/run-test-jar
@@ -329,17 +329,9 @@ if [ "$JIT" = "y" ]; then
INT_OPTS="-Xusejit:true"
if [ "$VERIFY" = "y" ] ; then
COMPILE_FLAGS="${COMPILE_FLAGS} --compiler-filter=verify-at-runtime"
- if [ "$PREBUILD" = "n" ]; then
- # Make sure that if we have noprebuild we still JIT as DexClassLoader will
- # try to compile the dex file.
- INT_OPTS="${INT_OPTS} -Xcompiler-option --compiler-filter=verify-at-runtime"
- fi
else
COMPILE_FLAGS="${COMPILE_FLAGS} --compiler-filter=verify-none"
DEX_VERIFY="${DEX_VERIFY} -Xverify:none"
- if [ "$PREBUILD" = "n" ]; then
- INT_OPTS="${INT_OPTS} -Xcompiler-option --compiler-filter=verify-none"
- fi
fi
fi
diff --git a/test/run-test b/test/run-test
index d0f93b9231..6bb154942c 100755
--- a/test/run-test
+++ b/test/run-test
@@ -45,7 +45,7 @@ export JAVAC="javac -g -source 1.7 -target 1.7 -Xlint:-options"
export RUN="${progdir}/etc/run-test-jar"
export DEX_LOCATION=/data/run-test/${test_dir}
export NEED_DEX="true"
-export USE_JACK="false"
+export USE_JACK="true"
export SMALI_ARGS="--experimental --api-level 23"
# If dx was not set by the environment variable, assume it is in the path.
@@ -73,11 +73,6 @@ if [ -z "$JACK" ]; then
export JACK="jack"
fi
-# If the tree is compiled with Jack, build test with Jack by default.
-if [ "$ANDROID_COMPILE_WITH_JACK" = "true" ]; then
- USE_JACK="true"
-fi
-
# ANDROID_BUILD_TOP is not set in a build environment.
if [ -z "$ANDROID_BUILD_TOP" ]; then
export ANDROID_BUILD_TOP=$oldwd
diff --git a/tools/libcore_failures.txt b/tools/libcore_failures.txt
index 46100ae15c..4c3aea4c23 100644
--- a/tools/libcore_failures.txt
+++ b/tools/libcore_failures.txt
@@ -270,5 +270,10 @@
description: "Only work with --mode=activity",
result: EXEC_FAILED,
names: [ "libcore.java.io.FileTest#testJavaIoTmpdirMutable" ]
+},
+{
+ description: "Wrong junit constructor for test",
+ result: EXEC_FAILED,
+ names: [ "jsr166.CollectionTest#testEmptyMeansEmpty" ]
}
]