diff options
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" ] } ] |