diff options
41 files changed, 622 insertions, 282 deletions
diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk index 7283710bfa..8c618719e7 100644 --- a/build/Android.gtest.mk +++ b/build/Android.gtest.mk @@ -171,6 +171,7 @@ RUNTIME_GTEST_COMMON_SRC_FILES := \ runtime/oat_file_test.cc \ runtime/oat_file_assistant_test.cc \ runtime/parsed_options_test.cc \ + runtime/prebuilt_tools_test.cc \ runtime/reference_table_test.cc \ runtime/thread_pool_test.cc \ runtime/transaction_test.cc \ diff --git a/compiler/common_compiler_test.cc b/compiler/common_compiler_test.cc index 96d90bb443..8ffc86ea3f 100644 --- a/compiler/common_compiler_test.cc +++ b/compiler/common_compiler_test.cc @@ -165,7 +165,7 @@ void CommonCompilerTest::SetUp() { method_inliner_map_.get(), compiler_kind, instruction_set, instruction_set_features_.get(), - true, new std::set<std::string>, nullptr, + true, new std::unordered_set<std::string>, nullptr, 2, true, true, "", timer_.get(), -1, "")); } // We typically don't generate an image in unit tests, disable this optimization by default. diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc index 538d969c12..1832647f4e 100644 --- a/compiler/driver/compiler_driver.cc +++ b/compiler/driver/compiler_driver.cc @@ -76,6 +76,10 @@ static constexpr bool kTimeCompileMethod = !kIsDebugBuild; // Whether to produce 64-bit ELF files for 64-bit targets. Leave this off for now. static constexpr bool kProduce64BitELFFiles = false; +// Whether classes-to-compile is only applied to the boot image, or, when given, too all +// compilations. +static constexpr bool kRestrictCompilationFiltersToImage = true; + static double Percentage(size_t x, size_t y) { return 100.0 * (static_cast<double>(x)) / (static_cast<double>(x + y)); } @@ -343,9 +347,9 @@ CompilerDriver::CompilerDriver(const CompilerOptions* compiler_options, Compiler::Kind compiler_kind, InstructionSet instruction_set, const InstructionSetFeatures* instruction_set_features, - bool image, std::set<std::string>* image_classes, - std::set<std::string>* compiled_classes, size_t thread_count, - bool dump_stats, bool dump_passes, + bool image, std::unordered_set<std::string>* image_classes, + std::unordered_set<std::string>* compiled_classes, + size_t thread_count, bool dump_stats, bool dump_passes, const std::string& dump_cfg_file_name, CumulativeLogger* timer, int swap_fd, const std::string& profile_file) : swap_space_(swap_fd == -1 ? nullptr : new SwapSpace(swap_fd, 10 * MB)), @@ -656,14 +660,14 @@ bool CompilerDriver::IsImageClass(const char* descriptor) const { } bool CompilerDriver::IsClassToCompile(const char* descriptor) const { - if (!IsImage()) { + if (kRestrictCompilationFiltersToImage && !IsImage()) { + return true; + } + + if (classes_to_compile_ == nullptr) { return true; - } else { - if (classes_to_compile_ == nullptr) { - return true; - } - return classes_to_compile_->find(descriptor) != classes_to_compile_->end(); } + return classes_to_compile_->find(descriptor) != classes_to_compile_->end(); } static void ResolveExceptionsForMethod(MutableHandle<mirror::ArtMethod> method_handle, @@ -723,7 +727,8 @@ static bool ResolveCatchBlockExceptionsClassVisitor(mirror::Class* c, void* arg) static bool RecordImageClassesVisitor(mirror::Class* klass, void* arg) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { - std::set<std::string>* image_classes = reinterpret_cast<std::set<std::string>*>(arg); + std::unordered_set<std::string>* image_classes = + reinterpret_cast<std::unordered_set<std::string>*>(arg); std::string temp; image_classes->insert(klass->GetDescriptor(&temp)); return true; @@ -795,7 +800,8 @@ void CompilerDriver::LoadImageClasses(TimingLogger* timings) CHECK_NE(image_classes_->size(), 0U); } -static void MaybeAddToImageClasses(Handle<mirror::Class> c, std::set<std::string>* image_classes) +static void MaybeAddToImageClasses(Handle<mirror::Class> c, + std::unordered_set<std::string>* image_classes) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) { Thread* self = Thread::Current(); StackHandleScope<1> hs(self); @@ -804,7 +810,8 @@ static void MaybeAddToImageClasses(Handle<mirror::Class> c, std::set<std::string std::string temp; while (!klass->IsObjectClass()) { const char* descriptor = klass->GetDescriptor(&temp); - std::pair<std::set<std::string>::iterator, bool> result = image_classes->insert(descriptor); + std::pair<std::unordered_set<std::string>::iterator, bool> result = + image_classes->insert(descriptor); if (!result.second) { // Previously inserted. break; } @@ -826,8 +833,8 @@ static void MaybeAddToImageClasses(Handle<mirror::Class> c, std::set<std::string // Note: we can use object pointers because we suspend all threads. class ClinitImageUpdate { public: - static ClinitImageUpdate* Create(std::set<std::string>* image_class_descriptors, Thread* self, - ClassLinker* linker, std::string* error_msg) { + static ClinitImageUpdate* Create(std::unordered_set<std::string>* image_class_descriptors, + Thread* self, ClassLinker* linker, std::string* error_msg) { std::unique_ptr<ClinitImageUpdate> res(new ClinitImageUpdate(image_class_descriptors, self, linker)); if (res->art_method_class_ == nullptr) { @@ -867,7 +874,7 @@ class ClinitImageUpdate { } private: - ClinitImageUpdate(std::set<std::string>* image_class_descriptors, Thread* self, + ClinitImageUpdate(std::unordered_set<std::string>* image_class_descriptors, Thread* self, ClassLinker* linker) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_) : image_class_descriptors_(image_class_descriptors), self_(self) { @@ -933,7 +940,7 @@ class ClinitImageUpdate { } mutable std::unordered_set<mirror::Object*> marked_objects_; - std::set<std::string>* const image_class_descriptors_; + std::unordered_set<std::string>* const image_class_descriptors_; std::vector<mirror::Class*> image_classes_; const mirror::Class* art_method_class_; const mirror::Class* dex_cache_class_; diff --git a/compiler/driver/compiler_driver.h b/compiler/driver/compiler_driver.h index 95534fb554..ce13a17792 100644 --- a/compiler/driver/compiler_driver.h +++ b/compiler/driver/compiler_driver.h @@ -19,6 +19,7 @@ #include <set> #include <string> +#include <unordered_set> #include <vector> #include "arch/instruction_set.h" @@ -101,8 +102,8 @@ class CompilerDriver { Compiler::Kind compiler_kind, InstructionSet instruction_set, const InstructionSetFeatures* instruction_set_features, - bool image, std::set<std::string>* image_classes, - std::set<std::string>* compiled_classes, + bool image, std::unordered_set<std::string>* image_classes, + std::unordered_set<std::string>* compiled_classes, size_t thread_count, bool dump_stats, bool dump_passes, const std::string& dump_cfg_file_name, CumulativeLogger* timer, int swap_fd, @@ -154,7 +155,7 @@ class CompilerDriver { return image_; } - const std::set<std::string>* GetImageClasses() const { + const std::unordered_set<std::string>* GetImageClasses() const { return image_classes_.get(); } @@ -424,7 +425,7 @@ class CompilerDriver { // Checks if class specified by type_idx is one of the image_classes_ bool IsImageClass(const char* descriptor) const; - // Checks if the provided class should be compiled, i.e., is in classes_to_compile_. + // Checks whether the provided class should be compiled, i.e., is in classes_to_compile_. bool IsClassToCompile(const char* descriptor) const; void RecordClassStatus(ClassReference ref, mirror::Class::Status status) @@ -589,12 +590,12 @@ class CompilerDriver { // If image_ is true, specifies the classes that will be included in // the image. Note if image_classes_ is nullptr, all classes are // included in the image. - std::unique_ptr<std::set<std::string>> image_classes_; + std::unique_ptr<std::unordered_set<std::string>> image_classes_; - // If image_ is true, specifies the classes that will be compiled in - // the image. Note if classes_to_compile_ is nullptr, all classes are - // included in the image. - std::unique_ptr<std::set<std::string>> classes_to_compile_; + // Specifies the classes that will be compiled. Note that if classes_to_compile_ is nullptr, + // all classes are eligible for compilation (duplication filters etc. will still apply). + // This option may be restricted to the boot image, depending on a flag in the implementation. + std::unique_ptr<std::unordered_set<std::string>> classes_to_compile_; bool had_hard_verifier_failure_; diff --git a/compiler/dwarf/dwarf_test.h b/compiler/dwarf/dwarf_test.h index dd5e0c286e..5a97c3b07e 100644 --- a/compiler/dwarf/dwarf_test.h +++ b/compiler/dwarf/dwarf_test.h @@ -55,36 +55,6 @@ class DwarfTest : public CommonRuntimeTest { expected_lines_.push_back(ExpectedLine {substr, next, at_file, at_line}); } - static std::string GetObjdumpPath() { - const char* android_build_top = getenv("ANDROID_BUILD_TOP"); - if (android_build_top != nullptr) { - std::string host_prebuilts = std::string(android_build_top) + - "/prebuilts/gcc/linux-x86/host/"; - // Read the content of the directory. - std::set<std::string> entries; - DIR* dir = opendir(host_prebuilts.c_str()); - if (dir != nullptr) { - struct dirent* entry; - while ((entry = readdir(dir)) != nullptr) { - if (strstr(entry->d_name, "linux-glibc")) { - entries.insert(host_prebuilts + entry->d_name); - } - } - closedir(dir); - } - // Strings are sorted so the last one should be the most recent version. - if (!entries.empty()) { - std::string path = *entries.rbegin() + "/x86_64-linux/bin/objdump"; - struct stat st; - if (stat(path.c_str(), &st) == 0) { - return path; // File exists. - } - } - } - ADD_FAILURE() << "Can not find prebuild objdump."; - return "objdump"; // Use the system objdump as fallback. - } - // Pretty-print the generated DWARF data using objdump. template<typename Elf_Word, typename Elf_Sword, typename Elf_Addr, typename Elf_Dyn, typename Elf_Sym, typename Elf_Ehdr, typename Elf_Phdr, typename Elf_Shdr> @@ -130,8 +100,8 @@ class DwarfTest : public CommonRuntimeTest { // Read the elf file back using objdump. std::vector<std::string> lines; - std::string cmd = GetObjdumpPath(); - cmd = cmd + " " + args + " " + file.GetFilename() + " 2>&1"; + std::string cmd = GetAndroidHostToolsDir(); + cmd = cmd + "objdump " + args + " " + file.GetFilename() + " 2>&1"; FILE* output = popen(cmd.data(), "r"); char buffer[1024]; const char* line; diff --git a/compiler/image_test.cc b/compiler/image_test.cc index cfd525cc5c..8016831e37 100644 --- a/compiler/image_test.cc +++ b/compiler/image_test.cc @@ -124,7 +124,7 @@ TEST_F(ImageTest, WriteRead) { } ASSERT_TRUE(compiler_driver_->GetImageClasses() != NULL); - std::set<std::string> image_classes(*compiler_driver_->GetImageClasses()); + std::unordered_set<std::string> image_classes(*compiler_driver_->GetImageClasses()); // Need to delete the compiler since it has worker threads which are attached to runtime. compiler_driver_.reset(); diff --git a/compiler/image_writer.cc b/compiler/image_writer.cc index 670c897e2d..a99ef3470d 100644 --- a/compiler/image_writer.cc +++ b/compiler/image_writer.cc @@ -776,7 +776,7 @@ void ImageWriter::CheckNonImageClassesRemovedCallback(Object* obj, void* arg) { } void ImageWriter::DumpImageClasses() { - const std::set<std::string>* image_classes = compiler_driver_.GetImageClasses(); + auto image_classes = compiler_driver_.GetImageClasses(); CHECK(image_classes != NULL); for (const std::string& image_class : *image_classes) { LOG(INFO) << " " << image_class; diff --git a/compiler/jit/jit_compiler.cc b/compiler/jit/jit_compiler.cc index be2c8c61b0..9ff7ab80e0 100644 --- a/compiler/jit/jit_compiler.cc +++ b/compiler/jit/jit_compiler.cc @@ -94,7 +94,7 @@ JitCompiler::JitCompiler() : total_time_(0) { compiler_driver_.reset(new CompilerDriver( compiler_options_.get(), verification_results_.get(), method_inliner_map_.get(), Compiler::kQuick, instruction_set, instruction_set_features_.get(), false, - nullptr, new std::set<std::string>, 1, false, true, + nullptr, nullptr, 1, false, true, std::string(), cumulative_logger_.get(), -1, std::string())); // Disable dedupe so we can remove compiled methods. compiler_driver_->SetDedupeEnabled(false); diff --git a/compiler/optimizing/code_generator_arm64.cc b/compiler/optimizing/code_generator_arm64.cc index 76e2ec6f42..23ba339a98 100644 --- a/compiler/optimizing/code_generator_arm64.cc +++ b/compiler/optimizing/code_generator_arm64.cc @@ -1353,16 +1353,16 @@ void InstructionCodeGeneratorARM64::VisitArrayLength(HArrayLength* instruction) } void LocationsBuilderARM64::VisitArraySet(HArraySet* instruction) { - Primitive::Type value_type = instruction->GetComponentType(); - bool is_object = value_type == Primitive::kPrimNot; - LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary( - instruction, is_object ? LocationSummary::kCall : LocationSummary::kNoCall); - if (is_object) { + if (instruction->NeedsTypeCheck()) { + LocationSummary* locations = + new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCall); InvokeRuntimeCallingConvention calling_convention; locations->SetInAt(0, LocationFrom(calling_convention.GetRegisterAt(0))); locations->SetInAt(1, LocationFrom(calling_convention.GetRegisterAt(1))); locations->SetInAt(2, LocationFrom(calling_convention.GetRegisterAt(2))); } else { + LocationSummary* locations = + new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall); locations->SetInAt(0, Location::RequiresRegister()); locations->SetInAt(1, Location::RegisterOrConstant(instruction->InputAt(1))); if (Primitive::IsFloatingPointType(instruction->InputAt(2)->GetType())) { @@ -1375,33 +1375,42 @@ void LocationsBuilderARM64::VisitArraySet(HArraySet* instruction) { void InstructionCodeGeneratorARM64::VisitArraySet(HArraySet* instruction) { Primitive::Type value_type = instruction->GetComponentType(); - if (value_type == Primitive::kPrimNot) { + LocationSummary* locations = instruction->GetLocations(); + bool needs_runtime_call = locations->WillCall(); + + if (needs_runtime_call) { codegen_->InvokeRuntime( QUICK_ENTRY_POINT(pAputObject), instruction, instruction->GetDexPc(), nullptr); CheckEntrypointTypes<kQuickAputObject, void, mirror::Array*, int32_t, mirror::Object*>(); } else { - LocationSummary* locations = instruction->GetLocations(); Register obj = InputRegisterAt(instruction, 0); CPURegister value = InputCPURegisterAt(instruction, 2); Location index = locations->InAt(1); size_t offset = mirror::Array::DataOffset(Primitive::ComponentSize(value_type)).Uint32Value(); MemOperand destination = HeapOperand(obj); MacroAssembler* masm = GetVIXLAssembler(); - UseScratchRegisterScope temps(masm); BlockPoolsScope block_pools(masm); + { + // We use a block to end the scratch scope before the write barrier, thus + // freeing the temporary registers so they can be used in `MarkGCCard`. + UseScratchRegisterScope temps(masm); + + if (index.IsConstant()) { + offset += Int64ConstantFrom(index) << Primitive::ComponentSizeShift(value_type); + destination = HeapOperand(obj, offset); + } else { + Register temp = temps.AcquireSameSizeAs(obj); + Register index_reg = InputRegisterAt(instruction, 1); + __ Add(temp, obj, Operand(index_reg, LSL, Primitive::ComponentSizeShift(value_type))); + destination = HeapOperand(temp, offset); + } - if (index.IsConstant()) { - offset += Int64ConstantFrom(index) << Primitive::ComponentSizeShift(value_type); - destination = HeapOperand(obj, offset); - } else { - Register temp = temps.AcquireSameSizeAs(obj); - Register index_reg = InputRegisterAt(instruction, 1); - __ Add(temp, obj, Operand(index_reg, LSL, Primitive::ComponentSizeShift(value_type))); - destination = HeapOperand(temp, offset); + codegen_->Store(value_type, value, destination); + codegen_->MaybeRecordImplicitNullCheck(instruction); + } + if (CodeGenerator::StoreNeedsWriteBarrier(value_type, instruction->GetValue())) { + codegen_->MarkGCCard(obj, value.W()); } - - codegen_->Store(value_type, value, destination); - codegen_->MaybeRecordImplicitNullCheck(instruction); } } diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc index 86e84ac66a..3dcfca6a0c 100644 --- a/compiler/optimizing/code_generator_x86.cc +++ b/compiler/optimizing/code_generator_x86.cc @@ -1556,10 +1556,8 @@ void LocationsBuilderX86::VisitTypeConversion(HTypeConversion* conversion) { case Primitive::kPrimLong: // Processing a Dex `long-to-float' instruction. - locations->SetInAt(0, Location::RequiresRegister()); - locations->SetOut(Location::RequiresFpuRegister()); - locations->AddTemp(Location::RequiresFpuRegister()); - locations->AddTemp(Location::RequiresFpuRegister()); + locations->SetInAt(0, Location::Any()); + locations->SetOut(Location::Any()); break; case Primitive::kPrimDouble: @@ -1589,10 +1587,8 @@ void LocationsBuilderX86::VisitTypeConversion(HTypeConversion* conversion) { case Primitive::kPrimLong: // Processing a Dex `long-to-double' instruction. - locations->SetInAt(0, Location::RequiresRegister()); - locations->SetOut(Location::RequiresFpuRegister()); - locations->AddTemp(Location::RequiresFpuRegister()); - locations->AddTemp(Location::RequiresFpuRegister()); + locations->SetInAt(0, Location::Any()); + locations->SetOut(Location::Any()); break; case Primitive::kPrimFloat: @@ -1813,37 +1809,31 @@ void InstructionCodeGeneratorX86::VisitTypeConversion(HTypeConversion* conversio case Primitive::kPrimLong: { // Processing a Dex `long-to-float' instruction. - Register low = in.AsRegisterPairLow<Register>(); - Register high = in.AsRegisterPairHigh<Register>(); - XmmRegister result = out.AsFpuRegister<XmmRegister>(); - XmmRegister temp = locations->GetTemp(0).AsFpuRegister<XmmRegister>(); - XmmRegister constant = locations->GetTemp(1).AsFpuRegister<XmmRegister>(); - - // Operations use doubles for precision reasons (each 32-bit - // half of a long fits in the 53-bit mantissa of a double, - // but not in the 24-bit mantissa of a float). This is - // especially important for the low bits. The result is - // eventually converted to float. - - // low = low - 2^31 (to prevent bit 31 of `low` to be - // interpreted as a sign bit) - __ subl(low, Immediate(0x80000000)); - // temp = int-to-double(high) - __ cvtsi2sd(temp, high); - // temp = temp * 2^32 - __ LoadLongConstant(constant, k2Pow32EncodingForDouble); - __ mulsd(temp, constant); - // result = int-to-double(low) - __ cvtsi2sd(result, low); - // result = result + 2^31 (restore the original value of `low`) - __ LoadLongConstant(constant, k2Pow31EncodingForDouble); - __ addsd(result, constant); - // result = result + temp - __ addsd(result, temp); - // result = double-to-float(result) - __ cvtsd2ss(result, result); - // Restore low. - __ addl(low, Immediate(0x80000000)); + size_t adjustment = 0; + + // Create stack space for the call to + // InstructionCodeGeneratorX86::PushOntoFPStack and/or X86Assembler::fstps below. + // TODO: enhance register allocator to ask for stack temporaries. + if (!in.IsDoubleStackSlot() || !out.IsStackSlot()) { + adjustment = Primitive::ComponentSize(Primitive::kPrimLong); + __ subl(ESP, Immediate(adjustment)); + } + + // Load the value to the FP stack, using temporaries if needed. + PushOntoFPStack(in, 0, adjustment, false, true); + + if (out.IsStackSlot()) { + __ fstps(Address(ESP, out.GetStackIndex() + adjustment)); + } else { + __ fstps(Address(ESP, 0)); + Location stack_temp = Location::StackSlot(0); + codegen_->Move32(out, stack_temp); + } + + // Remove the temporary stack space we allocated. + if (adjustment != 0) { + __ addl(ESP, Immediate(adjustment)); + } break; } @@ -1872,29 +1862,31 @@ void InstructionCodeGeneratorX86::VisitTypeConversion(HTypeConversion* conversio case Primitive::kPrimLong: { // Processing a Dex `long-to-double' instruction. - Register low = in.AsRegisterPairLow<Register>(); - Register high = in.AsRegisterPairHigh<Register>(); - XmmRegister result = out.AsFpuRegister<XmmRegister>(); - XmmRegister temp = locations->GetTemp(0).AsFpuRegister<XmmRegister>(); - XmmRegister constant = locations->GetTemp(1).AsFpuRegister<XmmRegister>(); - - // low = low - 2^31 (to prevent bit 31 of `low` to be - // interpreted as a sign bit) - __ subl(low, Immediate(0x80000000)); - // temp = int-to-double(high) - __ cvtsi2sd(temp, high); - // temp = temp * 2^32 - __ LoadLongConstant(constant, k2Pow32EncodingForDouble); - __ mulsd(temp, constant); - // result = int-to-double(low) - __ cvtsi2sd(result, low); - // result = result + 2^31 (restore the original value of `low`) - __ LoadLongConstant(constant, k2Pow31EncodingForDouble); - __ addsd(result, constant); - // result = result + temp - __ addsd(result, temp); - // Restore low. - __ addl(low, Immediate(0x80000000)); + size_t adjustment = 0; + + // Create stack space for the call to + // InstructionCodeGeneratorX86::PushOntoFPStack and/or X86Assembler::fstpl below. + // TODO: enhance register allocator to ask for stack temporaries. + if (!in.IsDoubleStackSlot() || !out.IsDoubleStackSlot()) { + adjustment = Primitive::ComponentSize(Primitive::kPrimLong); + __ subl(ESP, Immediate(adjustment)); + } + + // Load the value to the FP stack, using temporaries if needed. + PushOntoFPStack(in, 0, adjustment, false, true); + + if (out.IsDoubleStackSlot()) { + __ fstpl(Address(ESP, out.GetStackIndex() + adjustment)); + } else { + __ fstpl(Address(ESP, 0)); + Location stack_temp = Location::DoubleStackSlot(0); + codegen_->Move64(out, stack_temp); + } + + // Remove the temporary stack space we allocated. + if (adjustment != 0) { + __ addl(ESP, Immediate(adjustment)); + } break; } @@ -2234,24 +2226,43 @@ void InstructionCodeGeneratorX86::VisitMul(HMul* mul) { } } -void InstructionCodeGeneratorX86::PushOntoFPStack(Location source, uint32_t temp_offset, - uint32_t stack_adjustment, bool is_float) { +void InstructionCodeGeneratorX86::PushOntoFPStack(Location source, + uint32_t temp_offset, + uint32_t stack_adjustment, + bool is_fp, + bool is_wide) { if (source.IsStackSlot()) { - DCHECK(is_float); - __ flds(Address(ESP, source.GetStackIndex() + stack_adjustment)); + DCHECK(!is_wide); + if (is_fp) { + __ flds(Address(ESP, source.GetStackIndex() + stack_adjustment)); + } else { + __ filds(Address(ESP, source.GetStackIndex() + stack_adjustment)); + } } else if (source.IsDoubleStackSlot()) { - DCHECK(!is_float); - __ fldl(Address(ESP, source.GetStackIndex() + stack_adjustment)); + DCHECK(is_wide); + if (is_fp) { + __ fldl(Address(ESP, source.GetStackIndex() + stack_adjustment)); + } else { + __ fildl(Address(ESP, source.GetStackIndex() + stack_adjustment)); + } } else { // Write the value to the temporary location on the stack and load to FP stack. - if (is_float) { + if (!is_wide) { Location stack_temp = Location::StackSlot(temp_offset); codegen_->Move32(stack_temp, source); - __ flds(Address(ESP, temp_offset)); + if (is_fp) { + __ flds(Address(ESP, temp_offset)); + } else { + __ filds(Address(ESP, temp_offset)); + } } else { Location stack_temp = Location::DoubleStackSlot(temp_offset); codegen_->Move64(stack_temp, source); - __ fldl(Address(ESP, temp_offset)); + if (is_fp) { + __ fldl(Address(ESP, temp_offset)); + } else { + __ fildl(Address(ESP, temp_offset)); + } } } } @@ -2270,8 +2281,9 @@ void InstructionCodeGeneratorX86::GenerateRemFP(HRem *rem) { __ subl(ESP, Immediate(2 * elem_size)); // Load the values to the FP stack in reverse order, using temporaries if needed. - PushOntoFPStack(second, elem_size, 2 * elem_size, is_float); - PushOntoFPStack(first, 0, 2 * elem_size, is_float); + const bool is_wide = !is_float; + PushOntoFPStack(second, elem_size, 2 * elem_size, /* is_fp */ true, is_wide); + PushOntoFPStack(first, 0, 2 * elem_size, /* is_fp */ true, is_wide); // Loop doing FPREM until we stabilize. Label retry; diff --git a/compiler/optimizing/code_generator_x86.h b/compiler/optimizing/code_generator_x86.h index 07476c6850..8bd3cd3585 100644 --- a/compiler/optimizing/code_generator_x86.h +++ b/compiler/optimizing/code_generator_x86.h @@ -174,8 +174,10 @@ class InstructionCodeGeneratorX86 : public HGraphVisitor { void GenerateMemoryBarrier(MemBarrierKind kind); void HandleFieldSet(HInstruction* instruction, const FieldInfo& field_info); void HandleFieldGet(HInstruction* instruction, const FieldInfo& field_info); + // Push value to FPU stack. `is_fp` specifies whether the value is floating point or not. + // `is_wide` specifies whether it is long/double or not. void PushOntoFPStack(Location source, uint32_t temp_offset, - uint32_t stack_adjustment, bool is_float); + uint32_t stack_adjustment, bool is_fp, bool is_wide); void GenerateImplicitNullCheck(HNullCheck* instruction); void GenerateExplicitNullCheck(HNullCheck* instruction); diff --git a/compiler/optimizing/dead_code_elimination.cc b/compiler/optimizing/dead_code_elimination.cc index 8045cc5027..94990402e5 100644 --- a/compiler/optimizing/dead_code_elimination.cc +++ b/compiler/optimizing/dead_code_elimination.cc @@ -41,7 +41,6 @@ void HDeadCodeElimination::Run() { && !inst->IsMemoryBarrier() // If we added an explicit barrier then we should keep it. && !inst->HasUses()) { block->RemoveInstruction(inst); - MaybeRecordStat(MethodCompilationStat::kRemovedDeadInstruction); } } } diff --git a/compiler/optimizing/dead_code_elimination.h b/compiler/optimizing/dead_code_elimination.h index 3f309c514d..3db2c3ff3f 100644 --- a/compiler/optimizing/dead_code_elimination.h +++ b/compiler/optimizing/dead_code_elimination.h @@ -19,7 +19,6 @@ #include "nodes.h" #include "optimization.h" -#include "optimizing_compiler_stats.h" namespace art { @@ -29,8 +28,8 @@ namespace art { */ class HDeadCodeElimination : public HOptimization { public: - HDeadCodeElimination(HGraph* graph, OptimizingCompilerStats* stats) - : HOptimization(graph, true, kDeadCodeEliminationPassName, stats) {} + explicit HDeadCodeElimination(HGraph* graph) + : HOptimization(graph, true, kDeadCodeEliminationPassName) {} void Run() OVERRIDE; diff --git a/compiler/optimizing/graph_checker.cc b/compiler/optimizing/graph_checker.cc index 2216cecc2b..e743d8eca8 100644 --- a/compiler/optimizing/graph_checker.cc +++ b/compiler/optimizing/graph_checker.cc @@ -276,6 +276,17 @@ void SSAChecker::CheckLoop(HBasicBlock* loop_header) { id)); } } + + // If this is a nested loop, ensure the outer loops contain a superset of the blocks. + for (HLoopInformationOutwardIterator it(*loop_header); !it.Done(); it.Advance()) { + HLoopInformation* outer_info = it.Current(); + if (!loop_blocks.IsSubsetOf(&outer_info->GetBlocks())) { + AddError(StringPrintf("Blocks of loop defined by header %d are not a subset of blocks of " + "an outer loop defined by header %d.", + loop_header->GetBlockId(), + outer_info->GetHeader()->GetBlockId())); + } + } } void SSAChecker::VisitInstruction(HInstruction* instruction) { diff --git a/compiler/optimizing/inliner.cc b/compiler/optimizing/inliner.cc index bffd639e83..6d2a8d77e2 100644 --- a/compiler/optimizing/inliner.cc +++ b/compiler/optimizing/inliner.cc @@ -190,7 +190,7 @@ bool HInliner::TryBuildAndInline(Handle<mirror::ArtMethod> resolved_method, } // Run simple optimizations on the graph. - HDeadCodeElimination dce(callee_graph, stats_); + HDeadCodeElimination dce(callee_graph); HConstantFolding fold(callee_graph); InstructionSimplifier simplify(callee_graph, stats_); diff --git a/compiler/optimizing/instruction_simplifier.cc b/compiler/optimizing/instruction_simplifier.cc index b8ae1f6369..f30c9a6cef 100644 --- a/compiler/optimizing/instruction_simplifier.cc +++ b/compiler/optimizing/instruction_simplifier.cc @@ -467,7 +467,8 @@ void InstructionSimplifierVisitor::VisitNeg(HNeg* instruction) { return; } - if (input->IsSub() && input->HasOnlyOneNonEnvironmentUse()) { + if (input->IsSub() && input->HasOnlyOneNonEnvironmentUse() && + !Primitive::IsFloatingPointType(input->GetType())) { // Replace code looking like // SUB tmp, a, b // NEG dst, tmp @@ -478,6 +479,7 @@ void InstructionSimplifierVisitor::VisitNeg(HNeg* instruction) { // worse code. In particular, we do not want the live ranges of `a` and `b` // to be extended if we are not sure the initial 'SUB' instruction can be // removed. + // We do not perform optimization for fp because we could lose the sign of zero. HSub* sub = input->AsSub(); HSub* new_sub = new (GetGraph()->GetArena()) HSub(instruction->GetType(), sub->GetRight(), sub->GetLeft()); diff --git a/compiler/optimizing/intrinsics_x86.cc b/compiler/optimizing/intrinsics_x86.cc index 3c7a2660db..95ab90de23 100644 --- a/compiler/optimizing/intrinsics_x86.cc +++ b/compiler/optimizing/intrinsics_x86.cc @@ -828,7 +828,7 @@ void IntrinsicLocationsBuilderX86::VisitMathRoundFloat(HInvoke* invoke) { LocationSummary::kNoCall, kIntrinsified); locations->SetInAt(0, Location::RequiresFpuRegister()); - locations->SetOut(Location::RequiresFpuRegister()); + locations->SetOut(Location::RequiresRegister()); locations->AddTemp(Location::RequiresFpuRegister()); locations->AddTemp(Location::RequiresFpuRegister()); return; diff --git a/compiler/optimizing/nodes.cc b/compiler/optimizing/nodes.cc index 5fca4fab22..4b9d4fc26b 100644 --- a/compiler/optimizing/nodes.cc +++ b/compiler/optimizing/nodes.cc @@ -1064,8 +1064,10 @@ void HGraph::InlineInto(HGraph* outer_graph, HInvoke* invoke) { outer_graph->AddBlock(current); outer_graph->reverse_post_order_.Put(++index_of_at, current); if (info != nullptr) { - info->Add(current); current->SetLoopInformation(info); + for (HLoopInformationOutwardIterator loop_it(*at); !loop_it.Done(); loop_it.Advance()) { + loop_it.Current()->Add(current); + } } } } @@ -1075,8 +1077,10 @@ void HGraph::InlineInto(HGraph* outer_graph, HInvoke* invoke) { outer_graph->AddBlock(to); outer_graph->reverse_post_order_.Put(++index_of_at, to); if (info != nullptr) { - info->Add(to); to->SetLoopInformation(info); + for (HLoopInformationOutwardIterator loop_it(*at); !loop_it.Done(); loop_it.Advance()) { + loop_it.Current()->Add(to); + } if (info->IsBackEdge(*at)) { // Only `at` can become a back edge, as the inlined blocks // are predecessors of `at`. diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h index d9d15c4b18..1565f58977 100644 --- a/compiler/optimizing/nodes.h +++ b/compiler/optimizing/nodes.h @@ -2170,7 +2170,7 @@ class HInvoke : public HInstruction { uint32_t GetDexMethodIndex() const { return dex_method_index_; } - Intrinsics GetIntrinsic() { + Intrinsics GetIntrinsic() const { return intrinsic_; } diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc index ab752c3655..2ec8536cdf 100644 --- a/compiler/optimizing/optimizing_compiler.cc +++ b/compiler/optimizing/optimizing_compiler.cc @@ -320,8 +320,7 @@ static void RunOptimizations(HGraph* graph, const DexCompilationUnit& dex_compilation_unit, PassInfoPrinter* pass_info_printer, StackHandleScopeCollection* handles) { - HDeadCodeElimination dce1(graph, stats); - HDeadCodeElimination dce2(graph, stats); + HDeadCodeElimination dce(graph); HConstantFolding fold1(graph); InstructionSimplifier simplify1(graph, stats); HBooleanSimplifier boolean_not(graph); @@ -340,7 +339,7 @@ static void RunOptimizations(HGraph* graph, HOptimization* optimizations[] = { &intrinsics, - &dce1, + &dce, &fold1, &simplify1, // BooleanSimplifier depends on the InstructionSimplifier removing redundant @@ -353,8 +352,7 @@ static void RunOptimizations(HGraph* graph, &licm, &bce, &type_propagation, - &simplify2, - &dce2, + &simplify2 }; RunOptimizations(optimizations, arraysize(optimizations), pass_info_printer); diff --git a/compiler/optimizing/optimizing_compiler_stats.h b/compiler/optimizing/optimizing_compiler_stats.h index e6508c9851..9bfa543401 100644 --- a/compiler/optimizing/optimizing_compiler_stats.h +++ b/compiler/optimizing/optimizing_compiler_stats.h @@ -29,7 +29,6 @@ enum MethodCompilationStat { kCompiledBaseline, kCompiledOptimized, kCompiledQuick, - kInstructionSimplifications, kInlinedInvoke, kNotCompiledUnsupportedIsa, kNotCompiledPathological, @@ -49,8 +48,8 @@ enum MethodCompilationStat { kNotCompiledVerifyAtRuntime, kNotCompiledClassNotVerified, kRemovedCheckedCast, - kRemovedDeadInstruction, kRemovedNullCheck, + kInstructionSimplifications, kLastStat }; @@ -97,7 +96,6 @@ class OptimizingCompilerStats { case kCompiledOptimized : return "kCompiledOptimized"; case kCompiledQuick : return "kCompiledQuick"; case kInlinedInvoke : return "kInlinedInvoke"; - case kInstructionSimplifications: return "kInstructionSimplifications"; case kNotCompiledUnsupportedIsa : return "kNotCompiledUnsupportedIsa"; case kNotCompiledPathological : return "kNotCompiledPathological"; case kNotCompiledHugeMethod : return "kNotCompiledHugeMethod"; @@ -116,8 +114,8 @@ class OptimizingCompilerStats { case kNotCompiledVerifyAtRuntime : return "kNotCompiledVerifyAtRuntime"; case kNotCompiledClassNotVerified : return "kNotCompiledClassNotVerified"; case kRemovedCheckedCast: return "kRemovedCheckedCast"; - case kRemovedDeadInstruction: return "kRemovedDeadInstruction"; case kRemovedNullCheck: return "kRemovedNullCheck"; + case kInstructionSimplifications: return "kInstructionSimplifications"; default: LOG(FATAL) << "invalid stat"; } return ""; diff --git a/compiler/optimizing/register_allocator.cc b/compiler/optimizing/register_allocator.cc index 6350b35ca1..f8e00f6873 100644 --- a/compiler/optimizing/register_allocator.cc +++ b/compiler/optimizing/register_allocator.cc @@ -903,6 +903,10 @@ bool RegisterAllocator::AllocateBlockedReg(LiveInterval* current) { return false; } + // We use the first use to compare with other intervals. If this interval + // is used after any active intervals, we will spill this interval. + size_t first_use = current->FirstUseAfter(current->GetStart()); + // First set all registers as not being used. size_t* next_use = registers_array_; for (size_t i = 0; i < number_of_registers_; ++i) { @@ -917,7 +921,7 @@ bool RegisterAllocator::AllocateBlockedReg(LiveInterval* current) { if (active->IsFixed()) { next_use[active->GetRegister()] = current->GetStart(); } else { - size_t use = active->FirstRegisterUseAfter(current->GetStart()); + size_t use = active->FirstUseAfter(current->GetStart()); if (use != kNoLifetime) { next_use[active->GetRegister()] = use; } @@ -945,7 +949,7 @@ bool RegisterAllocator::AllocateBlockedReg(LiveInterval* current) { next_use[inactive->GetRegister()] = std::min(next_intersection, next_use[inactive->GetRegister()]); } else { - size_t use = inactive->FirstRegisterUseAfter(current->GetStart()); + size_t use = inactive->FirstUseAfter(current->GetStart()); if (use != kNoLifetime) { next_use[inactive->GetRegister()] = std::min(use, next_use[inactive->GetRegister()]); } @@ -959,16 +963,16 @@ bool RegisterAllocator::AllocateBlockedReg(LiveInterval* current) { DCHECK(current->IsHighInterval()); reg = current->GetRegister(); // When allocating the low part, we made sure the high register was available. - DCHECK_LT(first_register_use, next_use[reg]); + DCHECK_LT(first_use, next_use[reg]); } else if (current->IsLowInterval()) { reg = FindAvailableRegisterPair(next_use, first_register_use); // We should spill if both registers are not available. - should_spill = (first_register_use >= next_use[reg]) - || (first_register_use >= next_use[GetHighForLowRegister(reg)]); + should_spill = (first_use >= next_use[reg]) + || (first_use >= next_use[GetHighForLowRegister(reg)]); } else { DCHECK(!current->IsHighInterval()); reg = FindAvailableRegister(next_use); - should_spill = (first_register_use >= next_use[reg]); + should_spill = (first_use >= next_use[reg]); } DCHECK_NE(reg, kNoRegister); @@ -998,10 +1002,12 @@ bool RegisterAllocator::AllocateBlockedReg(LiveInterval* current) { DumpInterval(std::cerr, current); DumpAllIntervals(std::cerr); // This situation has the potential to infinite loop, so we make it a non-debug CHECK. + HInstruction* at = liveness_.GetInstructionFromPosition(first_register_use / 2); CHECK(false) << "There is not enough registers available for " << split->GetParent()->GetDefinedBy()->DebugName() << " " << split->GetParent()->GetDefinedBy()->GetId() - << " at " << first_register_use - 1; + << " at " << first_register_use - 1 << " " + << (at == nullptr ? "" : at->DebugName()); } AddSorted(unhandled_, split); } diff --git a/compiler/optimizing/ssa_liveness_analysis.h b/compiler/optimizing/ssa_liveness_analysis.h index 8eb98a186b..03f5545755 100644 --- a/compiler/optimizing/ssa_liveness_analysis.h +++ b/compiler/optimizing/ssa_liveness_analysis.h @@ -131,6 +131,9 @@ class UsePosition : public ArenaObject<kArenaAllocMisc> { void Dump(std::ostream& stream) const { stream << position_; + if (is_environment_) { + stream << " (env)"; + } } UsePosition* Dup(ArenaAllocator* allocator) const { @@ -366,6 +369,10 @@ class LiveInterval : public ArenaObject<kArenaAllocMisc> { LiveInterval* GetParent() const { return parent_; } + // Returns whether this interval is the parent interval, that is, the interval + // that starts where the HInstruction is defined. + bool IsParent() const { return parent_ == this; } + LiveRange* GetFirstRange() const { return first_range_; } LiveRange* GetLastRange() const { return last_range_; } @@ -442,7 +449,7 @@ class LiveInterval : public ArenaObject<kArenaAllocMisc> { if (is_temp_) { return position == GetStart() ? position : kNoLifetime; } - if (position == GetStart() && defined_by_ != nullptr) { + if (position == GetStart() && IsParent()) { LocationSummary* locations = defined_by_->GetLocations(); Location location = locations->Out(); // This interval is the first interval of the instruction. If the output @@ -491,12 +498,19 @@ class LiveInterval : public ArenaObject<kArenaAllocMisc> { return position == GetStart() ? position : kNoLifetime; } + if (position == GetStart() && IsParent()) { + if (defined_by_->GetLocations()->Out().IsValid()) { + return position; + } + } + UsePosition* use = first_use_; size_t end = GetEnd(); while (use != nullptr && use->GetPosition() <= end) { if (!use->GetIsEnvironment()) { + Location location = use->GetUser()->GetLocations()->InAt(use->GetInputIndex()); size_t use_position = use->GetPosition(); - if (use_position > position) { + if (use_position > position && location.IsValid()) { return use_position; } } @@ -725,7 +739,7 @@ class LiveInterval : public ArenaObject<kArenaAllocMisc> { } void AddHighInterval(bool is_temp = false) { - DCHECK_EQ(GetParent(), this); + DCHECK(IsParent()); DCHECK(!HasHighInterval()); DCHECK(!HasLowInterval()); high_or_low_interval_ = new (allocator_) LiveInterval( diff --git a/compiler/utils/assembler_thumb_test.cc b/compiler/utils/assembler_thumb_test.cc index a171e59d98..772fa9aa4b 100644 --- a/compiler/utils/assembler_thumb_test.cc +++ b/compiler/utils/assembler_thumb_test.cc @@ -43,8 +43,6 @@ namespace arm { static constexpr bool kPrintResults = false; #endif -static const char* TOOL_PREFIX = "arm-linux-androideabi-"; - void SetAndroidData() { const char* data = getenv("ANDROID_DATA"); if (data == nullptr) { @@ -65,87 +63,6 @@ int CompareIgnoringSpace(const char* s1, const char* s2) { return *s1 - *s2; } -std::string GetAndroidToolsDir() { - std::string root; - const char* android_build_top = getenv("ANDROID_BUILD_TOP"); - if (android_build_top != nullptr) { - root += android_build_top; - } else { - // Not set by build server, so default to current directory - char* cwd = getcwd(nullptr, 0); - setenv("ANDROID_BUILD_TOP", cwd, 1); - root += cwd; - free(cwd); - } - - // Look for "prebuilts" - std::string toolsdir = root; - struct stat st; - while (toolsdir != "") { - std::string prebuilts = toolsdir + "/prebuilts"; - if (stat(prebuilts.c_str(), &st) == 0) { - // Found prebuilts. - toolsdir += "/prebuilts/gcc/linux-x86/arm"; - break; - } - // Not present, move up one dir. - size_t slash = toolsdir.rfind('/'); - if (slash == std::string::npos) { - toolsdir = ""; - } else { - toolsdir = toolsdir.substr(0, slash-1); - } - } - bool statok = stat(toolsdir.c_str(), &st) == 0; - if (!statok) { - return ""; // Use path. - } - - DIR* dir = opendir(toolsdir.c_str()); - if (dir == nullptr) { - return ""; // Use path. - } - - struct dirent* entry; - std::string founddir; - double maxversion = 0; - - // Find the latest version of the arm-eabi tools (biggest version number). - // Suffix on toolsdir will be something like "arm-eabi-4.8" - while ((entry = readdir(dir)) != nullptr) { - std::string subdir = toolsdir + std::string("/") + std::string(entry->d_name); - size_t eabi = subdir.find(TOOL_PREFIX); - if (eabi != std::string::npos) { - // Check if "bin/{as,objcopy,objdump}" exist under this folder. - struct stat exec_st; - std::string exec_path; - exec_path = subdir + "/bin/" + TOOL_PREFIX + "as"; - if (stat(exec_path.c_str(), &exec_st) != 0) - continue; - exec_path = subdir + "/bin/" + TOOL_PREFIX + "objcopy"; - if (stat(exec_path.c_str(), &exec_st) != 0) - continue; - exec_path = subdir + "/bin/" + TOOL_PREFIX + "objdump"; - if (stat(exec_path.c_str(), &exec_st) != 0) - continue; - - std::string suffix = subdir.substr(eabi + strlen(TOOL_PREFIX)); - double version = strtod(suffix.c_str(), nullptr); - if (version > maxversion) { - maxversion = version; - founddir = subdir; - } - } - } - closedir(dir); - bool found = founddir != ""; - if (!found) { - return ""; // Use path. - } - - return founddir + "/bin/"; -} - void dump(std::vector<uint8_t>& code, const char* testname) { // This will only work on the host. There is no as, objcopy or objdump on the // device. @@ -155,7 +72,7 @@ void dump(std::vector<uint8_t>& code, const char* testname) { if (!results_ok) { setup_results(); - toolsdir = GetAndroidToolsDir(); + toolsdir = CommonRuntimeTest::GetAndroidTargetToolsDir(kThumb2); SetAndroidData(); results_ok = true; } @@ -187,19 +104,18 @@ void dump(std::vector<uint8_t>& code, const char* testname) { char cmd[1024]; // Assemble the .S - snprintf(cmd, sizeof(cmd), "%s%sas %s -o %s.o", toolsdir.c_str(), TOOL_PREFIX, filename, filename); + snprintf(cmd, sizeof(cmd), "%sas %s -o %s.o", toolsdir.c_str(), filename, filename); system(cmd); // Remove the $d symbols to prevent the disassembler dumping the instructions // as .word - snprintf(cmd, sizeof(cmd), "%s%sobjcopy -N '$d' %s.o %s.oo", toolsdir.c_str(), TOOL_PREFIX, - filename, filename); + snprintf(cmd, sizeof(cmd), "%sobjcopy -N '$d' %s.o %s.oo", toolsdir.c_str(), filename, filename); system(cmd); // Disassemble. - snprintf(cmd, sizeof(cmd), "%s%sobjdump -d %s.oo | grep '^ *[0-9a-f][0-9a-f]*:'", - toolsdir.c_str(), TOOL_PREFIX, filename); + snprintf(cmd, sizeof(cmd), "%sobjdump -d %s.oo | grep '^ *[0-9a-f][0-9a-f]*:'", + toolsdir.c_str(), filename); if (kPrintResults) { // Print the results only, don't check. This is used to generate new output for inserting // into the .inc file. diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc index 7e32b43e66..70b4213d56 100644 --- a/dex2oat/dex2oat.cc +++ b/dex2oat/dex2oat.cc @@ -23,6 +23,7 @@ #include <iostream> #include <sstream> #include <string> +#include <unordered_set> #include <vector> #if defined(__linux__) && defined(__arm__) @@ -1102,7 +1103,7 @@ class Dex2Oat FINAL { return false; } } else if (image_) { - image_classes_.reset(new std::set<std::string>); + image_classes_.reset(new std::unordered_set<std::string>); } // If --compiled-classes was specified, calculate the full list of classes to compile in the // image. @@ -1615,20 +1616,22 @@ class Dex2Oat FINAL { } // Reads the class names (java.lang.Object) and returns a set of descriptors (Ljava/lang/Object;) - static std::set<std::string>* ReadImageClassesFromFile(const char* image_classes_filename) { + static std::unordered_set<std::string>* ReadImageClassesFromFile( + const char* image_classes_filename) { std::unique_ptr<std::ifstream> image_classes_file(new std::ifstream(image_classes_filename, std::ifstream::in)); if (image_classes_file.get() == nullptr) { LOG(ERROR) << "Failed to open image classes file " << image_classes_filename; return nullptr; } - std::unique_ptr<std::set<std::string>> result(ReadImageClasses(*image_classes_file)); + std::unique_ptr<std::unordered_set<std::string>> result(ReadImageClasses(*image_classes_file)); image_classes_file->close(); return result.release(); } - static std::set<std::string>* ReadImageClasses(std::istream& image_classes_stream) { - std::unique_ptr<std::set<std::string>> image_classes(new std::set<std::string>); + static std::unordered_set<std::string>* ReadImageClasses(std::istream& image_classes_stream) { + std::unique_ptr<std::unordered_set<std::string>> image_classes( + new std::unordered_set<std::string>); while (image_classes_stream.good()) { std::string dot; std::getline(image_classes_stream, dot); @@ -1642,9 +1645,10 @@ class Dex2Oat FINAL { } // Reads the class names (java.lang.Object) and returns a set of descriptors (Ljava/lang/Object;) - static std::set<std::string>* ReadImageClassesFromZip(const char* zip_filename, - const char* image_classes_filename, - std::string* error_msg) { + static std::unordered_set<std::string>* ReadImageClassesFromZip( + const char* zip_filename, + const char* image_classes_filename, + std::string* error_msg) { std::unique_ptr<ZipArchive> zip_archive(ZipArchive::Open(zip_filename, error_msg)); if (zip_archive.get() == nullptr) { return nullptr; @@ -1720,8 +1724,8 @@ class Dex2Oat FINAL { const char* image_classes_filename_; const char* compiled_classes_zip_filename_; const char* compiled_classes_filename_; - std::unique_ptr<std::set<std::string>> image_classes_; - std::unique_ptr<std::set<std::string>> compiled_classes_; + std::unique_ptr<std::unordered_set<std::string>> image_classes_; + std::unique_ptr<std::unordered_set<std::string>> compiled_classes_; bool image_; std::unique_ptr<ImageWriter> image_writer_; bool is_host_; diff --git a/runtime/base/bit_vector.cc b/runtime/base/bit_vector.cc index c3e24a7912..65cb02839a 100644 --- a/runtime/base/bit_vector.cc +++ b/runtime/base/bit_vector.cc @@ -79,6 +79,32 @@ bool BitVector::SameBitsSet(const BitVector *src) const { return (memcmp(storage_, src->GetRawStorage(), our_highest_index * kWordBytes) == 0); } +bool BitVector::IsSubsetOf(const BitVector *other) const { + int this_highest = GetHighestBitSet(); + int other_highest = other->GetHighestBitSet(); + + // If the highest bit set is -1, this is empty and a trivial subset. + if (this_highest < 0) { + return true; + } + + // If the highest bit set is higher, this cannot be a subset. + if (this_highest > other_highest) { + return false; + } + + // Compare each 32-bit word. + size_t this_highest_index = BitsToWords(this_highest + 1); + for (size_t i = 0; i < this_highest_index; ++i) { + uint32_t this_storage = storage_[i]; + uint32_t other_storage = other->storage_[i]; + if ((this_storage | other_storage) != other_storage) { + return false; + } + } + return true; +} + void BitVector::Intersect(const BitVector* src) { uint32_t src_storage_size = src->storage_size_; diff --git a/runtime/base/bit_vector.h b/runtime/base/bit_vector.h index 557a2ec110..be4d363bf5 100644 --- a/runtime/base/bit_vector.h +++ b/runtime/base/bit_vector.h @@ -173,6 +173,8 @@ class BitVector { */ bool SameBitsSet(const BitVector *src) const; + bool IsSubsetOf(const BitVector *other) const; + // Count the number of bits that are set. uint32_t NumSetBits() const; diff --git a/runtime/base/bit_vector_test.cc b/runtime/base/bit_vector_test.cc index fe3313d122..c51b9b0570 100644 --- a/runtime/base/bit_vector_test.cc +++ b/runtime/base/bit_vector_test.cc @@ -167,4 +167,48 @@ TEST(BitVector, UnionIfNotIn) { } } +TEST(BitVector, Subset) { + { + BitVector first(2, true, Allocator::GetMallocAllocator()); + BitVector second(5, true, Allocator::GetMallocAllocator()); + + EXPECT_TRUE(first.IsSubsetOf(&second)); + second.SetBit(4); + EXPECT_TRUE(first.IsSubsetOf(&second)); + } + + { + BitVector first(5, true, Allocator::GetMallocAllocator()); + BitVector second(5, true, Allocator::GetMallocAllocator()); + + first.SetBit(5); + EXPECT_FALSE(first.IsSubsetOf(&second)); + second.SetBit(4); + EXPECT_FALSE(first.IsSubsetOf(&second)); + } + + { + BitVector first(5, true, Allocator::GetMallocAllocator()); + BitVector second(5, true, Allocator::GetMallocAllocator()); + + first.SetBit(16); + first.SetBit(32); + first.SetBit(48); + second.SetBit(16); + second.SetBit(32); + second.SetBit(48); + + EXPECT_TRUE(first.IsSubsetOf(&second)); + second.SetBit(8); + EXPECT_TRUE(first.IsSubsetOf(&second)); + second.SetBit(40); + EXPECT_TRUE(first.IsSubsetOf(&second)); + second.SetBit(52); + EXPECT_TRUE(first.IsSubsetOf(&second)); + + first.SetBit(9); + EXPECT_FALSE(first.IsSubsetOf(&second)); + } +} + } // namespace art diff --git a/runtime/common_runtime_test.cc b/runtime/common_runtime_test.cc index 60b7fa218f..e17b885f21 100644 --- a/runtime/common_runtime_test.cc +++ b/runtime/common_runtime_test.cc @@ -16,6 +16,7 @@ #include "common_runtime_test.h" +#include <cstdio> #include <dirent.h> #include <dlfcn.h> #include <fcntl.h> @@ -188,6 +189,82 @@ void CommonRuntimeTest::TearDownAndroidData(const std::string& android_data, boo } } +// Helper - find directory with the following format: +// ${ANDROID_BUILD_TOP}/${subdir1}/${subdir2}-${version}/${subdir3}/bin/ +static std::string GetAndroidToolsDir(const std::string& subdir1, + const std::string& subdir2, + const std::string& subdir3) { + std::string root; + const char* android_build_top = getenv("ANDROID_BUILD_TOP"); + if (android_build_top != nullptr) { + root = android_build_top; + } else { + // Not set by build server, so default to current directory + char* cwd = getcwd(nullptr, 0); + setenv("ANDROID_BUILD_TOP", cwd, 1); + root = cwd; + free(cwd); + } + + std::string toolsdir = root + "/" + subdir1; + std::string founddir; + DIR* dir; + if ((dir = opendir(toolsdir.c_str())) != nullptr) { + float maxversion = 0; + struct dirent* entry; + while ((entry = readdir(dir)) != nullptr) { + std::string format = subdir2 + "-%f"; + float version; + if (std::sscanf(entry->d_name, format.c_str(), &version) == 1) { + if (version > maxversion) { + maxversion = version; + founddir = toolsdir + "/" + entry->d_name + "/" + subdir3 + "/bin/"; + } + } + } + closedir(dir); + } + + if (founddir.empty()) { + ADD_FAILURE() << "Can not find Android tools directory."; + } + return founddir; +} + +std::string CommonRuntimeTest::GetAndroidHostToolsDir() { + return GetAndroidToolsDir("prebuilts/gcc/linux-x86/host", + "x86_64-linux-glibc2.15", + "x86_64-linux"); +} + +std::string CommonRuntimeTest::GetAndroidTargetToolsDir(InstructionSet isa) { + switch (isa) { + case kArm: + case kThumb2: + return GetAndroidToolsDir("prebuilts/gcc/linux-x86/arm", + "arm-linux-androideabi", + "arm-linux-androideabi"); + case kArm64: + return GetAndroidToolsDir("prebuilts/gcc/linux-x86/aarch64", + "aarch64-linux-android", + "aarch64-linux-android"); + case kX86: + case kX86_64: + return GetAndroidToolsDir("prebuilts/gcc/linux-x86/x86", + "x86_64-linux-android", + "x86_64-linux-android"); + case kMips: + case kMips64: + return GetAndroidToolsDir("prebuilts/gcc/linux-x86/mips", + "mips64el-linux-android", + "mips64el-linux-android"); + case kNone: + break; + } + ADD_FAILURE() << "Invalid isa " << isa; + return ""; +} + std::string CommonRuntimeTest::GetCoreArtLocation() { return GetCoreFileLocation("art"); } diff --git a/runtime/common_runtime_test.h b/runtime/common_runtime_test.h index 5fbc2ee680..991737893a 100644 --- a/runtime/common_runtime_test.h +++ b/runtime/common_runtime_test.h @@ -22,6 +22,7 @@ #include <string> +#include "arch/instruction_set.h" #include "base/mutex.h" #include "globals.h" #include "os.h" @@ -79,6 +80,12 @@ class CommonRuntimeTest : public testing::Test { // Gets the path of the libcore dex file. static std::string GetLibCoreDexFileName(); + // Returns bin directory which contains host's prebuild tools. + static std::string GetAndroidHostToolsDir(); + + // Returns bin directory which contains target's prebuild tools. + static std::string GetAndroidTargetToolsDir(InstructionSet isa); + protected: static bool IsHost() { return !kIsTargetBuild; diff --git a/runtime/prebuilt_tools_test.cc b/runtime/prebuilt_tools_test.cc new file mode 100644 index 0000000000..453c0dac5f --- /dev/null +++ b/runtime/prebuilt_tools_test.cc @@ -0,0 +1,65 @@ +/* + * 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. + */ + +#include "common_runtime_test.h" + +#include <cstdio> + +#include "gtest/gtest.h" + +namespace art { + +// Run the tests only on host. +#ifndef HAVE_ANDROID_OS + +class PrebuiltToolsTest : public CommonRuntimeTest { +}; + +static void CheckToolsExist(const std::string& tools_dir) { + const char* tools[] { "as", "objcopy", "objdump" }; // NOLINT + for (const char* tool : tools) { + struct stat exec_st; + std::string exec_path = tools_dir + tool; + if (stat(exec_path.c_str(), &exec_st) != 0) { + ADD_FAILURE() << "Can not find " << tool << " in " << tools_dir; + } + } +} + +TEST_F(PrebuiltToolsTest, CheckHostTools) { + std::string tools_dir = GetAndroidHostToolsDir(); + if (tools_dir.empty()) { + ADD_FAILURE() << "Can not find Android tools directory for host"; + } else { + CheckToolsExist(tools_dir); + } +} + +TEST_F(PrebuiltToolsTest, CheckTargetTools) { + InstructionSet isas[] = { kArm, kArm64, kThumb2, kX86, kX86_64, kMips, kMips64 }; // NOLINT + for (InstructionSet isa : isas) { + std::string tools_dir = GetAndroidTargetToolsDir(isa); + if (tools_dir.empty()) { + ADD_FAILURE() << "Can not find Android tools directory for " << isa; + } else { + CheckToolsExist(tools_dir); + } + } +} + +#endif // HAVE_ANDROID_OS + +} // namespace art diff --git a/test/474-fp-sub-neg/expected.txt b/test/474-fp-sub-neg/expected.txt new file mode 100644 index 0000000000..e6ffe0d430 --- /dev/null +++ b/test/474-fp-sub-neg/expected.txt @@ -0,0 +1,2 @@ +-0.0 +-0.0 diff --git a/test/474-fp-sub-neg/info.txt b/test/474-fp-sub-neg/info.txt new file mode 100644 index 0000000000..eced93fef5 --- /dev/null +++ b/test/474-fp-sub-neg/info.txt @@ -0,0 +1,5 @@ +Regression check for optimizing simplify instruction pass. +A pair (sub, neg) should not be transforemd to (sub) for +fp calculation because we can lose the sign of zero for +the following expression: + - ( A - B ) != B - A ; if B == A diff --git a/test/474-fp-sub-neg/src/Main.java b/test/474-fp-sub-neg/src/Main.java new file mode 100644 index 0000000000..e6bce6793f --- /dev/null +++ b/test/474-fp-sub-neg/src/Main.java @@ -0,0 +1,45 @@ +/* + * 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 Main { + public static void floatTest() { + float f = 0; + float fc = 1f; + for (int i = 0; i < 2; i++) { + f -= fc; + f = -f; + } + + System.out.println(f); + } + + public static void doubleTest() { + double d = 0; + double dc = 1f; + for (int i = 0; i < 2; i++) { + d -= dc; + d = -d; + } + + System.out.println(d); + } + + public static void main(String[] args) { + doubleTest(); + floatTest(); + } + +} diff --git a/test/477-long-to-float-conversion-precision/expected.txt b/test/477-long-to-float-conversion-precision/expected.txt new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/test/477-long-to-float-conversion-precision/expected.txt diff --git a/test/477-long-to-float-conversion-precision/info.txt b/test/477-long-to-float-conversion-precision/info.txt new file mode 100644 index 0000000000..d9d41d70ba --- /dev/null +++ b/test/477-long-to-float-conversion-precision/info.txt @@ -0,0 +1 @@ +Tests for type conversions precision. diff --git a/test/477-long-to-float-conversion-precision/src/Main.java b/test/477-long-to-float-conversion-precision/src/Main.java new file mode 100644 index 0000000000..bc17053e20 --- /dev/null +++ b/test/477-long-to-float-conversion-precision/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. + */ + +// Note that $opt$ is a marker for the optimizing compiler to ensure +// it does compile the method. +public class Main { + + public static void assertFloatEquals(float expected, float result) { + if (expected != result) { + throw new Error("Expected: " + expected + ", found: " + result); + } + } + + public static void main(String[] args) { + // Generate, compile and check long-to-float Dex instructions. + longToFloat(); + } + + private static void longToFloat() { + // The result for this test case is slightly less accurate on ARM, + // due to the implementation of long-to-float type conversions for + // this architecture (both in Quick and Optimizing). + assertFloatEquals(Float.intBitsToFloat(-555858671), $opt$LongToFloat(-8008112895877447681L)); + } + + // This method produces a long-to-float Dex instruction. + static float $opt$LongToFloat(long a) { return (float)a; } +} diff --git a/test/478-checker-inliner-nested-loop/expected.txt b/test/478-checker-inliner-nested-loop/expected.txt new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/test/478-checker-inliner-nested-loop/expected.txt diff --git a/test/478-checker-inliner-nested-loop/info.txt b/test/478-checker-inliner-nested-loop/info.txt new file mode 100644 index 0000000000..c221e37285 --- /dev/null +++ b/test/478-checker-inliner-nested-loop/info.txt @@ -0,0 +1,2 @@ +Tests inlining into a nested loop. SSAChecker should verify that +loop information was updated correctly. diff --git a/test/478-checker-inliner-nested-loop/src/Main.java b/test/478-checker-inliner-nested-loop/src/Main.java new file mode 100644 index 0000000000..df583d9302 --- /dev/null +++ b/test/478-checker-inliner-nested-loop/src/Main.java @@ -0,0 +1,57 @@ +/* +* 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 Main { + + public static void assertIntEquals(int expected, int result) { + if (expected != result) { + throw new Error("Expected: " + expected + ", found: " + result); + } + } + + public static int Inline(int x, int y) { + int result; + if (x <= y) { + result = x * y; + } else { + result = 0; + } + return result; + } + + // CHECK-START: int Main.NestedLoop(int, int) inliner (before) + // CHECK-NOT: Mul + + // CHECK-START: int Main.NestedLoop(int, int) inliner (after) + // CHECK: Mul + // CHECK-NOT: Mul + + public static int NestedLoop(int max_x, int max_y) { + int total = 0; + for (int x = 0; x < max_x; ++x) { + for (int y = 0; y < max_y; ++y) { + total += Inline(x, y); + } + } + return total; + } + + public static void main(String[] args) { + assertIntEquals(0, NestedLoop(1, 1)); + assertIntEquals(3, NestedLoop(2, 3)); + } +} diff --git a/test/Android.run-test.mk b/test/Android.run-test.mk index 39afc6785b..731c040027 100644 --- a/test/Android.run-test.mk +++ b/test/Android.run-test.mk @@ -335,6 +335,19 @@ endif TEST_ART_BROKEN_DEFAULT_RUN_TESTS := +# Known broken tests for Quick's and Optimizing's ARM back ends. +TEST_ART_BROKEN_ARM_RUN_TESTS := 477-long-to-float-conversion-precision # b/20413424 + +ifeq ($(TARGET_ARCH),arm) + ifneq (,$(filter 32,$(ALL_ADDRESS_SIZES))) + ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,target,$(RUN_TYPES),$(PREBUILD_TYPES), \ + $(COMPILER_TYPES),$(RELOCATE_TYPES),$(TRACE_TYPES),$(GC_TYPES),$(JNI_TYPES), \ + $(IMAGE_TYPES),$(PICTEST_TYPES),$(DEBUGGABLE_TYPES),$(TEST_ART_BROKEN_ARM_RUN_TESTS),32) + endif +endif + +TEST_ART_BROKEN_ARM_RUN_TESTS := + # Known broken tests for the arm64 optimizing compiler backend. TEST_ART_BROKEN_OPTIMIZING_ARM64_RUN_TESTS := |