diff options
24 files changed, 336 insertions, 66 deletions
diff --git a/build/Android.common_build.mk b/build/Android.common_build.mk index 5dd9f1534c..63469703f2 100644 --- a/build/Android.common_build.mk +++ b/build/Android.common_build.mk @@ -259,16 +259,21 @@ ifeq ($(HOST_OS),linux) art_target_non_debug_cflags += -Wframe-larger-than=1728 endif -ifndef LIBART_IMG_HOST_BASE_ADDRESS - $(error LIBART_IMG_HOST_BASE_ADDRESS unset) -endif -ART_HOST_CFLAGS += $(art_cflags) -DART_BASE_ADDRESS=$(LIBART_IMG_HOST_BASE_ADDRESS) -ART_HOST_CFLAGS += -DART_DEFAULT_INSTRUCTION_SET_FEATURES=default +# DALVIK_VM_LIB will be empty for VM-less builds. Avoid $(error) calls here because +# LIBART_IMG_XXX variables won't be defined. +ifneq ($(DALVIK_VM_LIB),) + ifndef LIBART_IMG_HOST_BASE_ADDRESS + $(error LIBART_IMG_HOST_BASE_ADDRESS unset) + endif + ART_HOST_CFLAGS += $(art_cflags) -DART_BASE_ADDRESS=$(LIBART_IMG_HOST_BASE_ADDRESS) + ART_HOST_CFLAGS += -DART_DEFAULT_INSTRUCTION_SET_FEATURES=default -ifndef LIBART_IMG_TARGET_BASE_ADDRESS - $(error LIBART_IMG_TARGET_BASE_ADDRESS unset) + ifndef LIBART_IMG_TARGET_BASE_ADDRESS + $(error LIBART_IMG_TARGET_BASE_ADDRESS unset) + endif + ART_TARGET_CFLAGS += $(art_cflags) \ + -DART_TARGET -DART_BASE_ADDRESS=$(LIBART_IMG_TARGET_BASE_ADDRESS) endif -ART_TARGET_CFLAGS += $(art_cflags) -DART_TARGET -DART_BASE_ADDRESS=$(LIBART_IMG_TARGET_BASE_ADDRESS) ART_HOST_NON_DEBUG_CFLAGS := $(art_host_non_debug_cflags) ART_TARGET_NON_DEBUG_CFLAGS := $(art_target_non_debug_cflags) diff --git a/build/Android.oat.mk b/build/Android.oat.mk index e8b363ba26..523d14345c 100644 --- a/build/Android.oat.mk +++ b/build/Android.oat.mk @@ -44,6 +44,9 @@ define create-core-oat-host-rules core_pic_infix := core_dex2oat_dependency := $(DEX2OAT_DEPENDENCY) + ifeq ($(1),default) + core_compile_options += --compiler-backend=Quick + endif ifeq ($(1),optimizing) core_compile_options += --compiler-backend=Optimizing # With the optimizing compiler, we want to rerun dex2oat whenever there is @@ -137,6 +140,9 @@ define create-core-oat-target-rules core_pic_infix := core_dex2oat_dependency := $(DEX2OAT_DEPENDENCY) + ifeq ($(1),default) + core_compile_options += --compiler-backend=Quick + endif ifeq ($(1),optimizing) ifeq ($($(3)TARGET_ARCH),arm64) # TODO: Enable image generation on arm64 once the backend diff --git a/compiler/dex/quick/codegen_util.cc b/compiler/dex/quick/codegen_util.cc index 58bcee2124..00217549b0 100644 --- a/compiler/dex/quick/codegen_util.cc +++ b/compiler/dex/quick/codegen_util.cc @@ -775,6 +775,10 @@ void Mir2Lir::CreateNativeGcMap() { ": " << PrettyMethod(cu_->method_idx, *cu_->dex_file); native_gc_map_builder.AddEntry(native_offset, references); } + + // Maybe not necessary, but this could help prevent errors where we access the verified method + // after it has been deleted. + mir_graph_->GetCurrentDexCompilationUnit()->ClearVerifiedMethod(); } /* Determine the offset of each literal field */ diff --git a/compiler/dex/verification_results.cc b/compiler/dex/verification_results.cc index 4929b5b2b0..932a532e56 100644 --- a/compiler/dex/verification_results.cc +++ b/compiler/dex/verification_results.cc @@ -84,6 +84,15 @@ const VerifiedMethod* VerificationResults::GetVerifiedMethod(MethodReference ref return (it != verified_methods_.end()) ? it->second : nullptr; } +void VerificationResults::RemoveVerifiedMethod(MethodReference ref) { + WriterMutexLock mu(Thread::Current(), verified_methods_lock_); + auto it = verified_methods_.find(ref); + if (it != verified_methods_.end()) { + delete it->second; + verified_methods_.erase(it); + } +} + void VerificationResults::AddRejectedClass(ClassReference ref) { { WriterMutexLock mu(Thread::Current(), rejected_classes_lock_); diff --git a/compiler/dex/verification_results.h b/compiler/dex/verification_results.h index 0e7923fbc3..7fc2a2363d 100644 --- a/compiler/dex/verification_results.h +++ b/compiler/dex/verification_results.h @@ -48,6 +48,7 @@ class VerificationResults { const VerifiedMethod* GetVerifiedMethod(MethodReference ref) LOCKS_EXCLUDED(verified_methods_lock_); + void RemoveVerifiedMethod(MethodReference ref) LOCKS_EXCLUDED(verified_methods_lock_); void AddRejectedClass(ClassReference ref) LOCKS_EXCLUDED(rejected_classes_lock_); bool IsClassRejected(ClassReference ref) LOCKS_EXCLUDED(rejected_classes_lock_); diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc index ab9f41a4b3..e4274712d4 100644 --- a/compiler/driver/compiler_driver.cc +++ b/compiler/driver/compiler_driver.cc @@ -23,6 +23,10 @@ #include <vector> #include <unistd.h> +#ifndef __APPLE__ +#include <malloc.h> // For mallinfo +#endif + #include "base/stl_util.h" #include "base/timing_logger.h" #include "class_linker.h" @@ -497,6 +501,7 @@ void CompilerDriver::CompileAll(jobject class_loader, TimingLogger* timings) { DCHECK(!Runtime::Current()->IsStarted()); std::unique_ptr<ThreadPool> thread_pool(new ThreadPool("Compiler driver thread pool", thread_count_ - 1)); + VLOG(compiler) << "Before precompile " << GetMemoryUsageString(); PreCompile(class_loader, dex_files, thread_pool.get(), timings); Compile(class_loader, dex_files, thread_pool.get(), timings); if (dump_stats_) { @@ -593,20 +598,25 @@ void CompilerDriver::Resolve(jobject class_loader, const std::vector<const DexFi void CompilerDriver::PreCompile(jobject class_loader, const std::vector<const DexFile*>& dex_files, ThreadPool* thread_pool, TimingLogger* timings) { LoadImageClasses(timings); + VLOG(compiler) << "LoadImageClasses: " << GetMemoryUsageString(); Resolve(class_loader, dex_files, thread_pool, timings); + VLOG(compiler) << "Resolve: " << GetMemoryUsageString(); if (!compiler_options_->IsVerificationEnabled()) { - LOG(INFO) << "Verify none mode specified, skipping verification."; + VLOG(compiler) << "Verify none mode specified, skipping verification."; SetVerified(class_loader, dex_files, thread_pool, timings); return; } Verify(class_loader, dex_files, thread_pool, timings); + VLOG(compiler) << "Verify: " << GetMemoryUsageString(); InitializeClasses(class_loader, dex_files, thread_pool, timings); + VLOG(compiler) << "InitializeClasses: " << GetMemoryUsageString(); UpdateImageClasses(timings); + VLOG(compiler) << "UpdateImageClasses: " << GetMemoryUsageString(); } bool CompilerDriver::IsImageClass(const char* descriptor) const { @@ -2002,6 +2012,7 @@ void CompilerDriver::Compile(jobject class_loader, const std::vector<const DexFi CHECK(dex_file != nullptr); CompileDexFile(class_loader, *dex_file, dex_files, thread_pool, timings); } + VLOG(compiler) << "Compile: " << GetMemoryUsageString(); } void CompilerDriver::CompileClass(const ParallelCompilationManager* manager, size_t class_def_index) { @@ -2128,6 +2139,7 @@ void CompilerDriver::CompileMethod(const DexFile::CodeItem* code_item, uint32_t bool compilation_enabled) { CompiledMethod* compiled_method = nullptr; uint64_t start_ns = kTimeCompileMethod ? NanoTime() : 0; + MethodReference method_ref(&dex_file, method_idx); if ((access_flags & kAccNative) != 0) { // Are we interpreting only and have support for generic JNI down calls? @@ -2141,7 +2153,6 @@ void CompilerDriver::CompileMethod(const DexFile::CodeItem* code_item, uint32_t } else if ((access_flags & kAccAbstract) != 0) { // Abstract methods don't have code. } else { - MethodReference method_ref(&dex_file, method_idx); bool compile = compilation_enabled && verification_results_->IsCandidateForCompilation(method_ref, access_flags); if (compile) { @@ -2178,16 +2189,18 @@ void CompilerDriver::CompileMethod(const DexFile::CodeItem* code_item, uint32_t // When compiling with PIC, there should be zero non-relative linker patches CHECK(!compile_pic || non_relative_linker_patch_count == 0u); - MethodReference ref(&dex_file, method_idx); - DCHECK(GetCompiledMethod(ref) == nullptr) << PrettyMethod(method_idx, dex_file); + DCHECK(GetCompiledMethod(method_ref) == nullptr) << PrettyMethod(method_idx, dex_file); { MutexLock mu(self, compiled_methods_lock_); - compiled_methods_.Put(ref, compiled_method); + compiled_methods_.Put(method_ref, compiled_method); non_relative_linker_patch_count_ += non_relative_linker_patch_count; } - DCHECK(GetCompiledMethod(ref) != nullptr) << PrettyMethod(method_idx, dex_file); + DCHECK(GetCompiledMethod(method_ref) != nullptr) << PrettyMethod(method_idx, dex_file); } + // Done compiling, delete the verified method to reduce native memory usage. + verification_results_->RemoveVerifiedMethod(method_ref); + if (self->IsExceptionPending()) { ScopedObjectAccess soa(self); LOG(FATAL) << "Unexpected exception compiling: " << PrettyMethod(method_idx, dex_file) << "\n" @@ -2337,4 +2350,21 @@ bool CompilerDriver::SkipCompilation(const std::string& method_name) { } return !compile; } + +std::string CompilerDriver::GetMemoryUsageString() const { + std::ostringstream oss; + const ArenaPool* arena_pool = GetArenaPool(); + gc::Heap* heap = Runtime::Current()->GetHeap(); + oss << "arena alloc=" << PrettySize(arena_pool->GetBytesAllocated()); + oss << " java alloc=" << PrettySize(heap->GetBytesAllocated()); +#ifdef HAVE_MALLOC_H + struct mallinfo info = mallinfo(); + const size_t allocated_space = static_cast<size_t>(info.uordblks); + const size_t free_space = static_cast<size_t>(info.fordblks); + oss << " native alloc=" << PrettySize(allocated_space) << " free=" + << PrettySize(free_space); +#endif + return oss.str(); +} + } // namespace art diff --git a/compiler/driver/compiler_driver.h b/compiler/driver/compiler_driver.h index d837dbc9cd..615e0d0db4 100644 --- a/compiler/driver/compiler_driver.h +++ b/compiler/driver/compiler_driver.h @@ -39,6 +39,7 @@ #include "thread_pool.h" #include "utils/arena_allocator.h" #include "utils/dedupe_set.h" +#include "dex/verified_method.h" namespace art { @@ -398,6 +399,9 @@ class CompilerDriver { // Should the compiler run on this method given profile information? bool SkipCompilation(const std::string& method_name); + // Get memory usage during compilation. + std::string GetMemoryUsageString() const; + private: // These flags are internal to CompilerDriver for collecting INVOKE resolution statistics. // The only external contract is that unresolved method has flags 0 and resolved non-0. diff --git a/compiler/driver/dex_compilation_unit.h b/compiler/driver/dex_compilation_unit.h index 84f57991c3..03ae489da1 100644 --- a/compiler/driver/dex_compilation_unit.h +++ b/compiler/driver/dex_compilation_unit.h @@ -102,6 +102,10 @@ class DexCompilationUnit { return verified_method_; } + void ClearVerifiedMethod() { + verified_method_ = nullptr; + } + const std::string& GetSymbol(); private: @@ -117,7 +121,7 @@ class DexCompilationUnit { const uint16_t class_def_idx_; const uint32_t dex_method_idx_; const uint32_t access_flags_; - const VerifiedMethod* const verified_method_; + const VerifiedMethod* verified_method_; std::string symbol_; }; diff --git a/compiler/image_writer.cc b/compiler/image_writer.cc index 3b1d914f6e..ab5c6c77de 100644 --- a/compiler/image_writer.cc +++ b/compiler/image_writer.cc @@ -77,6 +77,7 @@ bool ImageWriter::PrepareImageAddressSpace() { Thread::Current()->TransitionFromSuspendedToRunnable(); PruneNonImageClasses(); // Remove junk ComputeLazyFieldsForImageClasses(); // Add useful information + ProcessStrings(); Thread::Current()->TransitionFromRunnableToSuspended(kNative); } gc::Heap* heap = Runtime::Current()->GetHeap(); @@ -561,9 +562,9 @@ void ImageWriter::ProcessStrings() { bool is_prefix = false; if (it != existing_strings.end()) { CHECK_LE(length, it->second); - is_prefix = std::equal(combined_chars.begin() + it->first, - combined_chars.begin() + it->first + it->second, - combined_chars.begin() + new_string.first); + is_prefix = std::equal(combined_chars.begin() + new_string.first, + combined_chars.begin() + new_string.first + new_string.second, + combined_chars.begin() + it->first); } if (is_prefix) { // Shares a prefix, set the offset to where the new offset will be. diff --git a/compiler/optimizing/builder.cc b/compiler/optimizing/builder.cc index 23c3b390c0..eb6181c711 100644 --- a/compiler/optimizing/builder.cc +++ b/compiler/optimizing/builder.cc @@ -1108,6 +1108,11 @@ bool HGraphBuilder::AnalyzeDexInstruction(const Instruction& instruction, uint32 break; } + case Instruction::FLOAT_TO_INT: { + Conversion_12x(instruction, Primitive::kPrimFloat, Primitive::kPrimInt); + break; + } + case Instruction::INT_TO_BYTE: { Conversion_12x(instruction, Primitive::kPrimInt, Primitive::kPrimByte); break; diff --git a/compiler/optimizing/code_generator.h b/compiler/optimizing/code_generator.h index 4c0d3ea960..7c8f6a2d29 100644 --- a/compiler/optimizing/code_generator.h +++ b/compiler/optimizing/code_generator.h @@ -35,6 +35,9 @@ static int64_t constexpr k2Pow32EncodingForDouble = INT64_C(0x41F0000000000000); // Binary encoding of 2^31 for type double. static int64_t constexpr k2Pow31EncodingForDouble = INT64_C(0x41E0000000000000); +// Maximum value for a primitive integer. +static int32_t constexpr kPrimIntMax = 0x7fffffff; + class Assembler; class CodeGenerator; class DexCompilationUnit; diff --git a/compiler/optimizing/code_generator_arm.cc b/compiler/optimizing/code_generator_arm.cc index 5b2be2e9a1..448a5a0707 100644 --- a/compiler/optimizing/code_generator_arm.cc +++ b/compiler/optimizing/code_generator_arm.cc @@ -1406,6 +1406,12 @@ void LocationsBuilderARM::VisitTypeConversion(HTypeConversion* conversion) { break; case Primitive::kPrimFloat: + // Processing a Dex `float-to-int' instruction. + locations->SetInAt(0, Location::RequiresFpuRegister()); + locations->SetOut(Location::RequiresRegister()); + locations->AddTemp(Location::RequiresFpuRegister()); + break; + case Primitive::kPrimDouble: LOG(FATAL) << "Type conversion from " << input_type << " to " << result_type << " not yet implemented"; @@ -1580,7 +1586,15 @@ void InstructionCodeGeneratorARM::VisitTypeConversion(HTypeConversion* conversio } break; - case Primitive::kPrimFloat: + case Primitive::kPrimFloat: { + // Processing a Dex `float-to-int' instruction. + SRegister temp = locations->GetTemp(0).AsFpuRegisterPairLow<SRegister>(); + __ vmovs(temp, in.AsFpuRegister<SRegister>()); + __ vcvtis(temp, temp); + __ vmovrs(out.AsRegister<Register>(), temp); + break; + } + case Primitive::kPrimDouble: LOG(FATAL) << "Type conversion from " << input_type << " to " << result_type << " not yet implemented"; diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc index fd794f95d1..6f83d9faf4 100644 --- a/compiler/optimizing/code_generator_x86.cc +++ b/compiler/optimizing/code_generator_x86.cc @@ -1373,6 +1373,12 @@ void LocationsBuilderX86::VisitTypeConversion(HTypeConversion* conversion) { break; case Primitive::kPrimFloat: + // Processing a Dex `float-to-int' instruction. + locations->SetInAt(0, Location::RequiresFpuRegister()); + locations->SetOut(Location::RequiresRegister()); + locations->AddTemp(Location::RequiresFpuRegister()); + break; + case Primitive::kPrimDouble: LOG(FATAL) << "Type conversion from " << input_type << " to " << result_type << " not yet implemented"; @@ -1559,7 +1565,31 @@ void InstructionCodeGeneratorX86::VisitTypeConversion(HTypeConversion* conversio } break; - case Primitive::kPrimFloat: + case Primitive::kPrimFloat: { + // Processing a Dex `float-to-int' instruction. + XmmRegister input = in.AsFpuRegister<XmmRegister>(); + Register output = out.AsRegister<Register>(); + XmmRegister temp = locations->GetTemp(0).AsFpuRegister<XmmRegister>(); + Label done, nan; + + __ movl(output, Immediate(kPrimIntMax)); + // temp = int-to-float(output) + __ cvtsi2ss(temp, output); + // if input >= temp goto done + __ comiss(input, temp); + __ j(kAboveEqual, &done); + // if input == NaN goto nan + __ j(kUnordered, &nan); + // output = float-to-int-truncate(input) + __ cvttss2si(output, input); + __ jmp(&done); + __ Bind(&nan); + // output = 0 + __ xorl(output, output); + __ Bind(&done); + break; + } + case Primitive::kPrimDouble: LOG(FATAL) << "Type conversion from " << input_type << " to " << result_type << " not yet implemented"; diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc index 4d70efcf38..47fd304969 100644 --- a/compiler/optimizing/code_generator_x86_64.cc +++ b/compiler/optimizing/code_generator_x86_64.cc @@ -1363,6 +1363,12 @@ void LocationsBuilderX86_64::VisitTypeConversion(HTypeConversion* conversion) { break; case Primitive::kPrimFloat: + // Processing a Dex `float-to-int' instruction. + locations->SetInAt(0, Location::RequiresFpuRegister()); + locations->SetOut(Location::RequiresRegister()); + locations->AddTemp(Location::RequiresFpuRegister()); + break; + case Primitive::kPrimDouble: LOG(FATAL) << "Type conversion from " << input_type << " to " << result_type << " not yet implemented"; @@ -1550,7 +1556,31 @@ void InstructionCodeGeneratorX86_64::VisitTypeConversion(HTypeConversion* conver } break; - case Primitive::kPrimFloat: + case Primitive::kPrimFloat: { + // Processing a Dex `float-to-int' instruction. + XmmRegister input = in.AsFpuRegister<XmmRegister>(); + CpuRegister output = out.AsRegister<CpuRegister>(); + XmmRegister temp = locations->GetTemp(0).AsFpuRegister<XmmRegister>(); + Label done, nan; + + __ movl(output, Immediate(kPrimIntMax)); + // temp = int-to-float(output) + __ cvtsi2ss(temp, output); + // if input >= temp goto done + __ comiss(input, temp); + __ j(kAboveEqual, &done); + // if input == NaN goto nan + __ j(kUnordered, &nan); + // output = float-to-int-truncate(input) + __ cvttss2si(output, input); + __ jmp(&done); + __ Bind(&nan); + // output = 0 + __ xorl(output, output); + __ Bind(&done); + break; + } + case Primitive::kPrimDouble: LOG(FATAL) << "Type conversion from " << input_type << " to " << result_type << " not yet implemented"; diff --git a/compiler/optimizing/nodes.cc b/compiler/optimizing/nodes.cc index 7584f1bac1..ba4dccf598 100644 --- a/compiler/optimizing/nodes.cc +++ b/compiler/optimizing/nodes.cc @@ -30,6 +30,36 @@ void HGraph::FindBackEdges(ArenaBitVector* visited) { VisitBlockForBackEdges(entry_block_, visited, &visiting); } +static void RemoveAsUser(HInstruction* instruction) { + for (size_t i = 0; i < instruction->InputCount(); i++) { + instruction->InputAt(i)->RemoveUser(instruction, i); + } + + HEnvironment* environment = instruction->GetEnvironment(); + if (environment != nullptr) { + for (size_t i = 0, e = environment->Size(); i < e; ++i) { + HInstruction* vreg = environment->GetInstructionAt(i); + if (vreg != nullptr) { + vreg->RemoveEnvironmentUser(environment, i); + } + } + } +} + +void HGraph::RemoveInstructionsAsUsersFromDeadBlocks(const ArenaBitVector& visited) const { + for (size_t i = 0; i < blocks_.Size(); ++i) { + if (!visited.IsBitSet(i)) { + HBasicBlock* block = blocks_.Get(i); + for (HInstructionIterator it(block->GetPhis()); !it.Done(); it.Advance()) { + RemoveAsUser(it.Current()); + } + for (HInstructionIterator it(block->GetInstructions()); !it.Done(); it.Advance()) { + RemoveAsUser(it.Current()); + } + } + } +} + void HGraph::RemoveDeadBlocks(const ArenaBitVector& visited) const { for (size_t i = 0; i < blocks_.Size(); ++i) { if (!visited.IsBitSet(i)) { @@ -72,16 +102,21 @@ void HGraph::BuildDominatorTree() { // (1) Find the back edges in the graph doing a DFS traversal. FindBackEdges(&visited); - // (2) Remove blocks not visited during the initial DFS. - // Step (3) requires dead blocks to be removed from the + // (2) Remove instructions and phis from blocks not visited during + // the initial DFS as users from other instructions, so that + // users can be safely removed before uses later. + RemoveInstructionsAsUsersFromDeadBlocks(visited); + + // (3) Remove blocks not visited during the initial DFS. + // Step (4) requires dead blocks to be removed from the // predecessors list of live blocks. RemoveDeadBlocks(visited); - // (3) Simplify the CFG now, so that we don't need to recompute + // (4) Simplify the CFG now, so that we don't need to recompute // dominators and the reverse post order. SimplifyCFG(); - // (4) Compute the immediate dominator of each block. We visit + // (5) Compute the immediate dominator of each block. We visit // the successors of a block only when all its forward branches // have been processed. GrowableArray<size_t> visits(arena_, blocks_.Size()); @@ -391,19 +426,7 @@ static void Remove(HInstructionList* instruction_list, instruction->SetBlock(nullptr); instruction_list->RemoveInstruction(instruction); - for (size_t i = 0; i < instruction->InputCount(); i++) { - instruction->InputAt(i)->RemoveUser(instruction, i); - } - - HEnvironment* environment = instruction->GetEnvironment(); - if (environment != nullptr) { - for (size_t i = 0, e = environment->Size(); i < e; ++i) { - HInstruction* vreg = environment->GetInstructionAt(i); - if (vreg != nullptr) { - vreg->RemoveEnvironmentUser(environment, i); - } - } - } + RemoveAsUser(instruction); } void HBasicBlock::RemoveInstruction(HInstruction* instruction) { diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h index 4706b3beab..3908a61910 100644 --- a/compiler/optimizing/nodes.h +++ b/compiler/optimizing/nodes.h @@ -173,6 +173,7 @@ class HGraph : public ArenaObject<kArenaAllocMisc> { void VisitBlockForBackEdges(HBasicBlock* block, ArenaBitVector* visited, ArenaBitVector* visiting); + void RemoveInstructionsAsUsersFromDeadBlocks(const ArenaBitVector& visited) const; void RemoveDeadBlocks(const ArenaBitVector& visited) const; ArenaAllocator* const arena_; diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc index a1ac2f0761..d7669e1104 100644 --- a/dex2oat/dex2oat.cc +++ b/dex2oat/dex2oat.cc @@ -25,10 +25,6 @@ #include <string> #include <vector> -#ifndef __APPLE__ -#include <malloc.h> // For mallinfo -#endif - #if defined(__linux__) && defined(__arm__) #include <sys/personality.h> #include <sys/utsname.h> @@ -987,6 +983,12 @@ class Dex2Oat FINAL { return true; } + void EraseOatFile() { + DCHECK(oat_file_.get() != nullptr); + oat_file_->Erase(); + oat_file_.reset(); + } + // Set up the environment for compilation. Includes starting the runtime and loading/opening the // boot class path. bool Setup() { @@ -1301,7 +1303,6 @@ class Dex2Oat FINAL { if (!driver_->WriteElf(android_root_, is_host_, dex_files_, oat_writer.get(), oat_file_.get())) { LOG(ERROR) << "Failed to write ELF file " << oat_file_->GetPath(); - oat_file_->Erase(); return false; } } @@ -1609,20 +1610,9 @@ class Dex2Oat FINAL { } void LogCompletionTime() { - std::ostringstream mallinfostr; -#ifdef HAVE_MALLOC_H - struct mallinfo info = mallinfo(); - const size_t allocated_space = static_cast<size_t>(info.uordblks); - const size_t free_space = static_cast<size_t>(info.fordblks); - mallinfostr << " native alloc=" << PrettySize(allocated_space) << " free=" - << PrettySize(free_space); -#endif - const ArenaPool* arena_pool = driver_->GetArenaPool(); - gc::Heap* heap = Runtime::Current()->GetHeap(); LOG(INFO) << "dex2oat took " << PrettyDuration(NanoTime() - start_ns_) - << " (threads: " << thread_count_ << ")" - << " arena alloc=" << PrettySize(arena_pool->GetBytesAllocated()) - << " java alloc=" << PrettySize(heap->GetBytesAllocated()) << mallinfostr.str(); + << " (threads: " << thread_count_ << ") " + << driver_->GetMemoryUsageString(); } std::unique_ptr<CompilerOptions> compiler_options_; @@ -1712,6 +1702,7 @@ static int CompileImage(Dex2Oat& dex2oat) { // Create the boot.oat. if (!dex2oat.CreateOatFile()) { + dex2oat.EraseOatFile(); return EXIT_FAILURE; } @@ -1756,6 +1747,7 @@ static int CompileApp(Dex2Oat& dex2oat) { // Create the app oat. if (!dex2oat.CreateOatFile()) { + dex2oat.EraseOatFile(); return EXIT_FAILURE; } @@ -1813,6 +1805,7 @@ static int dex2oat(int argc, char** argv) { LOG(INFO) << CommandLine(); if (!dex2oat.Setup()) { + dex2oat.EraseOatFile(); return EXIT_FAILURE; } diff --git a/test/004-JniTest/jni_test.cc b/test/004-JniTest/jni_test.cc index c2877be02e..544cbc503e 100644 --- a/test/004-JniTest/jni_test.cc +++ b/test/004-JniTest/jni_test.cc @@ -294,20 +294,20 @@ static void testShallowGetCallingClassLoader(JNIEnv* env) { assert(!env->ExceptionCheck()); // Create a string object. - jobject library_string = env->NewStringUTF("arttest"); + jobject library_string = env->NewStringUTF("non_existing_library"); assert(library_string != nullptr); assert(!env->ExceptionCheck()); env->CallStaticVoidMethod(system_clazz, loadLibraryMethodId, library_string); - if (env->ExceptionCheck()) { - // At most we expect UnsatisfiedLinkError. - jthrowable thrown = env->ExceptionOccurred(); - env->ExceptionClear(); - - jclass unsatisfied_link_error_clazz = env->FindClass("java/lang/UnsatisfiedLinkError"); - jclass thrown_class = env->GetObjectClass(thrown); - assert(env->IsSameObject(unsatisfied_link_error_clazz, thrown_class)); - } + assert(env->ExceptionCheck()); + + // We expect UnsatisfiedLinkError. + jthrowable thrown = env->ExceptionOccurred(); + env->ExceptionClear(); + + jclass unsatisfied_link_error_clazz = env->FindClass("java/lang/UnsatisfiedLinkError"); + jclass thrown_class = env->GetObjectClass(thrown); + assert(env->IsSameObject(unsatisfied_link_error_clazz, thrown_class)); } } diff --git a/test/422-type-conversion/src/Main.java b/test/422-type-conversion/src/Main.java index c434db37c9..e7dbe2463f 100644 --- a/test/422-type-conversion/src/Main.java +++ b/test/422-type-conversion/src/Main.java @@ -91,6 +91,9 @@ public class Main { // Generate, compile and check long-to-double Dex instructions. longToDouble(); + // Generate, compile and check float-to-int Dex instructions. + floatToInt(); + // Generate, compile and check int-to-byte Dex instructions. shortToByte(); intToByte(); @@ -313,6 +316,32 @@ public class Main { assertDoubleEquals(-9223372036854775808D, $opt$LongToDouble(-9223372036854775808L)); // -(2^63) } + private static void floatToInt() { + assertIntEquals(1, $opt$FloatToInt(1F)); + assertIntEquals(0, $opt$FloatToInt(0F)); + assertIntEquals(0, $opt$FloatToInt(-0F)); + assertIntEquals(-1, $opt$FloatToInt(-1F)); + assertIntEquals(51, $opt$FloatToInt(51F)); + assertIntEquals(-51, $opt$FloatToInt(-51F)); + assertIntEquals(0, $opt$FloatToInt(0.5F)); + assertIntEquals(0, $opt$FloatToInt(0.4999999F)); + assertIntEquals(0, $opt$FloatToInt(-0.4999999F)); + assertIntEquals(0, $opt$FloatToInt(-0.5F)); + assertIntEquals(42, $opt$FloatToInt(42.199F)); + assertIntEquals(-42, $opt$FloatToInt(-42.199F)); + assertIntEquals(2147483647, $opt$FloatToInt(2147483647F)); // 2^31 - 1 + assertIntEquals(-2147483648, $opt$FloatToInt(-2147483647F)); // -(2^31 - 1) + assertIntEquals(-2147483648, $opt$FloatToInt(-2147483648F)); // -(2^31) + assertIntEquals(2147483647, $opt$FloatToInt(2147483648F)); // (2^31) + assertIntEquals(-2147483648, $opt$FloatToInt(-2147483649F)); // -(2^31 + 1) + assertIntEquals(2147483647, $opt$FloatToInt(9223372036854775807F)); // 2^63 - 1 + assertIntEquals(-2147483648, $opt$FloatToInt(-9223372036854775807F)); // -(2^63 - 1) + assertIntEquals(-2147483648, $opt$FloatToInt(-9223372036854775808F)); // -(2^63) + assertIntEquals(0, $opt$FloatToInt(Float.NaN)); + assertIntEquals(2147483647, $opt$FloatToInt(Float.POSITIVE_INFINITY)); + assertIntEquals(-2147483648, $opt$FloatToInt(Float.NEGATIVE_INFINITY)); + } + private static void shortToByte() { assertByteEquals((byte)1, $opt$ShortToByte((short)1)); assertByteEquals((byte)0, $opt$ShortToByte((short)0)); @@ -468,6 +497,9 @@ public class Main { // This method produces a long-to-double Dex instruction. static double $opt$LongToDouble(long a){ return (double)a; } + // This method produces a float-to-int Dex instruction. + static int $opt$FloatToInt(float a){ return (int)a; } + // These methods produce int-to-byte Dex instructions. static byte $opt$ShortToByte(short a){ return (byte)a; } static byte $opt$IntToByte(int a){ return (byte)a; } diff --git a/test/435-try-finally-without-catch/expected.txt b/test/435-try-finally-without-catch/expected.txt new file mode 100644 index 0000000000..8a67802d72 --- /dev/null +++ b/test/435-try-finally-without-catch/expected.txt @@ -0,0 +1,3 @@ +In finally +In finally +In finally diff --git a/test/435-try-finally-without-catch/info.txt b/test/435-try-finally-without-catch/info.txt new file mode 100644 index 0000000000..46217c516b --- /dev/null +++ b/test/435-try-finally-without-catch/info.txt @@ -0,0 +1,26 @@ +Exercise a method containing a `try' statement with several +instructions with a `finally' clause but without any `catch' block, +enclosed in a loop. + +When dx processes an integer division (or modulo) enclosing a `try' +block and whose result is assigned to a local value, it is smart +enough not to emit a `div-int' (or `rem-int') instruction when the +divisor is non-null, as it wouldn't be used. However, dx is not +that clever regarding exception handling: if the divisor is known to +be non-null at compile-time (as is the case in this test), it will +still emit a block with the exception catching and rethrowing +mechanism, even if it is not used. + +This used to be a problem for a `try' block followed by a `finally' +clause but with no `catch' block: in that case, the generated Dex code +item would list zero catch block for this method (see +art::CodeItem::tries_size_) and the optimizing compiler would have no +clue that it contains a `try' statement, which it cannot optimize +(yet). With no hint that this method might contain one (or several) +special block(s) related to `catch'-less `try' statement(s), the +optimizing compiler considered this (these) as dead block(s) and +improperly tried to remove its (their) instructions, sometimes +removing instructions used by others instructions, thus triggering +assertions. The optimizing compiler was thus adjusted to remove these +instructions in a proper fashion, by removing them as users first, and +then by suppressing them for good. diff --git a/test/435-try-finally-without-catch/src/Main.java b/test/435-try-finally-without-catch/src/Main.java new file mode 100644 index 0000000000..3c29ce8476 --- /dev/null +++ b/test/435-try-finally-without-catch/src/Main.java @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2014 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){ + foo(); + } + + // Reduced test case inspired by constantPropagationTest() from + // test/083-compiler-regressions. + static void foo() { + int a = 0; + int b = 1; + + for (int i = 0; i < 3; i++) { + try { + a = 1; + // Would throw an ArithmeticException if b were null (hence + // the enclosing `try' statement). + int c = a % b; + } + finally { + System.out.println("In finally"); + } + } + } +} diff --git a/test/Android.run-test.mk b/test/Android.run-test.mk index 3c959fbcb3..b85685ba76 100644 --- a/test/Android.run-test.mk +++ b/test/Android.run-test.mk @@ -472,6 +472,7 @@ define define-test-art-run-test else ifeq ($(4),default) test_groups += ART_RUN_TEST_$$(uc_host_or_target)_DEFAULT_RULES + run_test_options += --quick else $$(error found $(4) expected $(COMPILER_TYPES)) endif diff --git a/test/run-test b/test/run-test index 6b8f00748b..2abc1fa262 100755 --- a/test/run-test +++ b/test/run-test @@ -194,6 +194,9 @@ while true; do run_args="${run_args} -Xcompiler-option --compiler-backend=Optimizing" image_suffix="-optimizing" shift + elif [ "x$1" = "x--quick" ]; then + run_args="${run_args} -Xcompiler-option --compiler-backend=Quick" + shift elif [ "x$1" = "x--no-verify" ]; then run_args="${run_args} --no-verify" shift @@ -421,6 +424,7 @@ if [ "$usage" = "yes" ]; then echo " --build-only Build test files only (off by default)." echo " --interpreter Enable interpreter only mode (off by default)." echo " --optimizing Enable optimizing compiler (off by default)." + echo " --quick Use Quick compiler (default)." echo " --no-verify Turn off verification (on by default)." echo " --no-optimize Turn off optimization (on by default)." echo " --no-precise Turn off precise GC (on by default)." |