diff options
69 files changed, 2365 insertions, 1422 deletions
diff --git a/build/Android.gtest.mk b/build/Android.gtest.mk index c27f8dbe4a..ed34a8df5f 100644 --- a/build/Android.gtest.mk +++ b/build/Android.gtest.mk @@ -25,6 +25,7 @@ include art/build/Android.common_build.mk GTEST_DEX_DIRECTORIES := \ AbstractMethod \ AllFields \ + DefaultMethods \ DexToDexDecompiler \ ErroneousA \ ErroneousB \ @@ -104,7 +105,7 @@ ART_GTEST_dex_cache_test_DEX_DEPS := Main Packages MethodTypes ART_GTEST_dex_file_test_DEX_DEPS := GetMethodSignature Main Nested MultiDex ART_GTEST_dex2oat_test_DEX_DEPS := $(ART_GTEST_dex2oat_environment_tests_DEX_DEPS) Statics VerifierDeps ART_GTEST_exception_test_DEX_DEPS := ExceptionHandle -ART_GTEST_image_test_DEX_DEPS := ImageLayoutA ImageLayoutB +ART_GTEST_image_test_DEX_DEPS := ImageLayoutA ImageLayoutB DefaultMethods ART_GTEST_imtable_test_DEX_DEPS := IMTA IMTB ART_GTEST_instrumentation_test_DEX_DEPS := Instrumentation ART_GTEST_jni_compiler_test_DEX_DEPS := MyClassNatives diff --git a/compiler/image_test.cc b/compiler/image_test.cc index 89e8a678b1..7ee494a131 100644 --- a/compiler/image_test.cc +++ b/compiler/image_test.cc @@ -76,7 +76,7 @@ class ImageTest : public CommonCompilerTest { void Compile(ImageHeader::StorageMode storage_mode, CompilationHelper& out_helper, const std::string& extra_dex = "", - const std::string& image_class = ""); + const std::initializer_list<std::string>& image_classes = {}); void SetUpRuntimeOptions(RuntimeOptions* options) OVERRIDE { CommonCompilerTest::SetUpRuntimeOptions(options); @@ -90,6 +90,18 @@ class ImageTest : public CommonCompilerTest { return new std::unordered_set<std::string>(image_classes_); } + ArtMethod* FindCopiedMethod(ArtMethod* origin, mirror::Class* klass) + REQUIRES_SHARED(Locks::mutator_lock_) { + PointerSize pointer_size = class_linker_->GetImagePointerSize(); + for (ArtMethod& m : klass->GetCopiedMethods(pointer_size)) { + if (strcmp(origin->GetName(), m.GetName()) == 0 && + origin->GetSignature() == m.GetSignature()) { + return &m; + } + } + return nullptr; + } + private: std::unordered_set<std::string> image_classes_; }; @@ -345,8 +357,8 @@ void CompilationHelper::Compile(CompilerDriver* driver, void ImageTest::Compile(ImageHeader::StorageMode storage_mode, CompilationHelper& helper, const std::string& extra_dex, - const std::string& image_class) { - if (!image_class.empty()) { + const std::initializer_list<std::string>& image_classes) { + for (const std::string& image_class : image_classes) { image_classes_.insert(image_class); } CreateCompilerDriver(Compiler::kOptimizing, kRuntimeISA, kIsTargetBuild ? 2U : 16U); @@ -358,13 +370,15 @@ void ImageTest::Compile(ImageHeader::StorageMode storage_mode, helper.extra_dex_files = OpenTestDexFiles(extra_dex.c_str()); } helper.Compile(compiler_driver_.get(), storage_mode); - if (!image_class.empty()) { + if (image_classes.begin() != image_classes.end()) { // Make sure the class got initialized. ScopedObjectAccess soa(Thread::Current()); ClassLinker* const class_linker = Runtime::Current()->GetClassLinker(); - mirror::Class* klass = class_linker->FindSystemClass(Thread::Current(), image_class.c_str()); - EXPECT_TRUE(klass != nullptr); - EXPECT_TRUE(klass->IsInitialized()); + for (const std::string& image_class : image_classes) { + mirror::Class* klass = class_linker->FindSystemClass(Thread::Current(), image_class.c_str()); + EXPECT_TRUE(klass != nullptr); + EXPECT_TRUE(klass->IsInitialized()); + } } } @@ -492,7 +506,7 @@ TEST_F(ImageTest, TestImageLayout) { // Compile multi-image with ImageLayoutA being the last image. { CompilationHelper helper; - Compile(ImageHeader::kStorageModeUncompressed, helper, "ImageLayoutA", "LMyClass;"); + Compile(ImageHeader::kStorageModeUncompressed, helper, "ImageLayoutA", {"LMyClass;"}); image_sizes = helper.GetImageObjectSectionSizes(); } TearDown(); @@ -501,7 +515,7 @@ TEST_F(ImageTest, TestImageLayout) { // Compile multi-image with ImageLayoutB being the last image. { CompilationHelper helper; - Compile(ImageHeader::kStorageModeUncompressed, helper, "ImageLayoutB", "LMyClass;"); + Compile(ImageHeader::kStorageModeUncompressed, helper, "ImageLayoutB", {"LMyClass;"}); image_sizes_extra = helper.GetImageObjectSectionSizes(); } // Make sure that the new stuff in the clinit in ImageLayoutB is in the last image and not in the @@ -553,4 +567,63 @@ TEST_F(ImageTest, ImageHeaderIsValid) { ASSERT_FALSE(image_header.IsValid()); } +// Test that pointer to quick code is the same in +// a default method of an interface and in a copied method +// of a class which implements the interface. This should be true +// only if the copied method and the origin method are located in the +// same oat file. +TEST_F(ImageTest, TestDefaultMethods) { + CompilationHelper helper; + Compile(ImageHeader::kStorageModeUncompressed, + helper, + "DefaultMethods", + {"LIface;", "LImpl;", "LIterableBase;"}); + + PointerSize pointer_size = class_linker_->GetImagePointerSize(); + Thread* self = Thread::Current(); + ScopedObjectAccess soa(self); + + // Test the pointer to quick code is the same in origin method + // and in the copied method form the same oat file. + mirror::Class* iface_klass = class_linker_->LookupClass( + self, "LIface;", ObjPtr<mirror::ClassLoader>()); + ASSERT_NE(nullptr, iface_klass); + ArtMethod* origin = iface_klass->FindDeclaredVirtualMethod( + "defaultMethod", "()V", pointer_size); + ASSERT_NE(nullptr, origin); + const void* code = origin->GetEntryPointFromQuickCompiledCodePtrSize(pointer_size); + // The origin method should have a pointer to quick code + ASSERT_NE(nullptr, code); + ASSERT_FALSE(class_linker_->IsQuickToInterpreterBridge(code)); + mirror::Class* impl_klass = class_linker_->LookupClass( + self, "LImpl;", ObjPtr<mirror::ClassLoader>()); + ASSERT_NE(nullptr, impl_klass); + ArtMethod* copied = FindCopiedMethod(origin, impl_klass); + ASSERT_NE(nullptr, copied); + // the copied method should have pointer to the same quick code as the origin method + ASSERT_EQ(code, copied->GetEntryPointFromQuickCompiledCodePtrSize(pointer_size)); + + // Test the origin method has pointer to quick code + // but the copied method has pointer to interpreter + // because these methods are in different oat files. + mirror::Class* iterable_klass = class_linker_->LookupClass( + self, "Ljava/lang/Iterable;", ObjPtr<mirror::ClassLoader>()); + ASSERT_NE(nullptr, iterable_klass); + origin = iterable_klass->FindDeclaredVirtualMethod( + "forEach", "(Ljava/util/function/Consumer;)V", pointer_size); + ASSERT_NE(nullptr, origin); + code = origin->GetEntryPointFromQuickCompiledCodePtrSize(pointer_size); + // the origin method should have a pointer to quick code + ASSERT_NE(nullptr, code); + ASSERT_FALSE(class_linker_->IsQuickToInterpreterBridge(code)); + mirror::Class* iterablebase_klass = class_linker_->LookupClass( + self, "LIterableBase;", ObjPtr<mirror::ClassLoader>()); + ASSERT_NE(nullptr, iterablebase_klass); + copied = FindCopiedMethod(origin, iterablebase_klass); + ASSERT_NE(nullptr, copied); + code = copied->GetEntryPointFromQuickCompiledCodePtrSize(pointer_size); + // the copied method should have a pointer to interpreter + ASSERT_TRUE(class_linker_->IsQuickToInterpreterBridge(code)); +} + } // namespace art diff --git a/compiler/oat_writer.cc b/compiler/oat_writer.cc index 5406ae72d1..8e25aa3421 100644 --- a/compiler/oat_writer.cc +++ b/compiler/oat_writer.cc @@ -1034,18 +1034,63 @@ class OatWriter::InitMethodInfoVisitor : public OatDexMethodVisitor { class OatWriter::InitImageMethodVisitor : public OatDexMethodVisitor { public: - InitImageMethodVisitor(OatWriter* writer, size_t offset) + InitImageMethodVisitor(OatWriter* writer, + size_t offset, + const std::vector<const DexFile*>* dex_files) : OatDexMethodVisitor(writer, offset), - pointer_size_(GetInstructionSetPointerSize(writer_->compiler_driver_->GetInstructionSet())) { + pointer_size_(GetInstructionSetPointerSize(writer_->compiler_driver_->GetInstructionSet())), + dex_files_(dex_files), + class_linker_(Runtime::Current()->GetClassLinker()) { + } + + // Handle copied methods here. Copy pointer to quick code from + // an origin method to a copied method only if they are + // in the same oat file. If the origin and the copied methods are + // in different oat files don't touch the copied method. + // References to other oat files are not supported yet. + bool StartClass(const DexFile* dex_file, size_t class_def_index) + REQUIRES_SHARED(Locks::mutator_lock_) { + OatDexMethodVisitor::StartClass(dex_file, class_def_index); + // Skip classes that are not in the image. + if (!IsImageClass()) { + return true; + } + ScopedObjectAccessUnchecked soa(Thread::Current()); + StackHandleScope<1> hs(soa.Self()); + Handle<mirror::DexCache> dex_cache = hs.NewHandle( + class_linker_->FindDexCache(Thread::Current(), *dex_file)); + const DexFile::ClassDef& class_def = dex_file->GetClassDef(class_def_index); + mirror::Class* klass = dex_cache->GetResolvedType(class_def.class_idx_); + if (klass != nullptr) { + for (ArtMethod& method : klass->GetCopiedMethods(pointer_size_)) { + // Find origin method. Declaring class and dex_method_idx + // in the copied method should be the same as in the origin + // method. + mirror::Class* declaring_class = method.GetDeclaringClass(); + ArtMethod* origin = declaring_class->FindDeclaredVirtualMethod( + declaring_class->GetDexCache(), + method.GetDexMethodIndex(), + pointer_size_); + CHECK(origin != nullptr); + if (IsInOatFile(&declaring_class->GetDexFile())) { + const void* code_ptr = + origin->GetEntryPointFromQuickCompiledCodePtrSize(pointer_size_); + if (code_ptr == nullptr) { + methods_to_process_.push_back(std::make_pair(&method, origin)); + } else { + method.SetEntryPointFromQuickCompiledCodePtrSize( + code_ptr, pointer_size_); + } + } + } + } + return true; } bool VisitMethod(size_t class_def_method_index, const ClassDataItemIterator& it) REQUIRES_SHARED(Locks::mutator_lock_) { - const DexFile::TypeId& type_id = - dex_file_->GetTypeId(dex_file_->GetClassDef(class_def_index_).class_idx_); - const char* class_descriptor = dex_file_->GetTypeDescriptor(type_id); // Skip methods that are not in the image. - if (!writer_->GetCompilerDriver()->IsImageClass(class_descriptor)) { + if (!IsImageClass()) { return true; } @@ -1059,17 +1104,16 @@ class OatWriter::InitImageMethodVisitor : public OatDexMethodVisitor { ++method_offsets_index_; } - ClassLinker* linker = Runtime::Current()->GetClassLinker(); // Unchecked as we hold mutator_lock_ on entry. ScopedObjectAccessUnchecked soa(Thread::Current()); StackHandleScope<1> hs(soa.Self()); - Handle<mirror::DexCache> dex_cache(hs.NewHandle(linker->FindDexCache( + Handle<mirror::DexCache> dex_cache(hs.NewHandle(class_linker_->FindDexCache( Thread::Current(), *dex_file_))); ArtMethod* method; if (writer_->HasBootImage()) { const InvokeType invoke_type = it.GetMethodInvokeType( dex_file_->GetClassDef(class_def_index_)); - method = linker->ResolveMethod<ClassLinker::kNoICCECheckForCache>( + method = class_linker_->ResolveMethod<ClassLinker::kNoICCECheckForCache>( *dex_file_, it.GetMemberIndex(), dex_cache, @@ -1089,7 +1133,8 @@ class OatWriter::InitImageMethodVisitor : public OatDexMethodVisitor { // Should already have been resolved by the compiler, just peek into the dex cache. // It may not be resolved if the class failed to verify, in this case, don't set the // entrypoint. This is not fatal since the dex cache will contain a resolution method. - method = dex_cache->GetResolvedMethod(it.GetMemberIndex(), linker->GetImagePointerSize()); + method = dex_cache->GetResolvedMethod(it.GetMemberIndex(), + class_linker_->GetImagePointerSize()); } if (method != nullptr && compiled_method != nullptr && @@ -1101,8 +1146,38 @@ class OatWriter::InitImageMethodVisitor : public OatDexMethodVisitor { return true; } + // Check whether current class is image class + bool IsImageClass() { + const DexFile::TypeId& type_id = + dex_file_->GetTypeId(dex_file_->GetClassDef(class_def_index_).class_idx_); + const char* class_descriptor = dex_file_->GetTypeDescriptor(type_id); + return writer_->GetCompilerDriver()->IsImageClass(class_descriptor); + } + + // Check whether specified dex file is in the compiled oat file. + bool IsInOatFile(const DexFile* dex_file) { + return ContainsElement(*dex_files_, dex_file); + } + + // Assign a pointer to quick code for copied methods + // not handled in the method StartClass + void Postprocess() { + for (std::pair<ArtMethod*, ArtMethod*>& p : methods_to_process_) { + ArtMethod* method = p.first; + ArtMethod* origin = p.second; + const void* code_ptr = + origin->GetEntryPointFromQuickCompiledCodePtrSize(pointer_size_); + if (code_ptr != nullptr) { + method->SetEntryPointFromQuickCompiledCodePtrSize(code_ptr, pointer_size_); + } + } + } + protected: const PointerSize pointer_size_; + const std::vector<const DexFile*>* dex_files_; + ClassLinker* const class_linker_; + std::vector<std::pair<ArtMethod*, ArtMethod*>> methods_to_process_; }; class OatWriter::WriteCodeMethodVisitor : public OatDexMethodVisitor { @@ -1744,8 +1819,9 @@ size_t OatWriter::InitOatCodeDexFiles(size_t offset) { offset = code_visitor.GetOffset(); if (HasImage()) { - InitImageMethodVisitor image_visitor(this, offset); + InitImageMethodVisitor image_visitor(this, offset, dex_files_); success = VisitDexMethods(&image_visitor); + image_visitor.Postprocess(); DCHECK(success); offset = image_visitor.GetOffset(); } diff --git a/compiler/optimizing/code_generator_arm.cc b/compiler/optimizing/code_generator_arm.cc index e34f116b75..caea250ab6 100644 --- a/compiler/optimizing/code_generator_arm.cc +++ b/compiler/optimizing/code_generator_arm.cc @@ -1875,6 +1875,7 @@ static bool CanGenerateConditionalMove(const Location& out, const Location& src) Label* CodeGeneratorARM::GetFinalLabel(HInstruction* instruction, Label* final_label) { DCHECK(!instruction->IsControlFlow() && !instruction->IsSuspendCheck()); + DCHECK(!instruction->IsInvoke() || !instruction->GetLocations()->CanCall()); const HBasicBlock* const block = instruction->GetBlock(); const HLoopInformation* const info = block->GetLoopInformation(); @@ -2901,16 +2902,20 @@ void InstructionCodeGeneratorARM::HandleCondition(HCondition* cond) { // Convert the jumps into the result. Label done_label; + Label* final_label = codegen_->GetFinalLabel(cond, &done_label); // False case: result = 0. __ Bind(&false_label); __ LoadImmediate(out, 0); - __ b(&done_label); + __ b(final_label); // True case: result = 1. __ Bind(&true_label); __ LoadImmediate(out, 1); - __ Bind(&done_label); + + if (done_label.IsLinked()) { + __ Bind(&done_label); + } } void LocationsBuilderARM::VisitEqual(HEqual* comp) { @@ -4441,7 +4446,8 @@ void InstructionCodeGeneratorARM::HandleIntegerRotate(LocationSummary* locations // rotates by swapping input regs (effectively rotating by the first 32-bits of // a larger rotation) or flipping direction (thus treating larger right/left // rotations as sub-word sized rotations in the other direction) as appropriate. -void InstructionCodeGeneratorARM::HandleLongRotate(LocationSummary* locations) { +void InstructionCodeGeneratorARM::HandleLongRotate(HRor* ror) { + LocationSummary* locations = ror->GetLocations(); Register in_reg_lo = locations->InAt(0).AsRegisterPairLow<Register>(); Register in_reg_hi = locations->InAt(0).AsRegisterPairHigh<Register>(); Location rhs = locations->InAt(1); @@ -4474,6 +4480,7 @@ void InstructionCodeGeneratorARM::HandleLongRotate(LocationSummary* locations) { Register shift_left = locations->GetTemp(1).AsRegister<Register>(); Label end; Label shift_by_32_plus_shift_right; + Label* final_label = codegen_->GetFinalLabel(ror, &end); __ and_(shift_right, rhs.AsRegister<Register>(), ShifterOperand(0x1F)); __ Lsrs(shift_left, rhs.AsRegister<Register>(), 6); @@ -4488,7 +4495,7 @@ void InstructionCodeGeneratorARM::HandleLongRotate(LocationSummary* locations) { __ Lsl(out_reg_lo, in_reg_lo, shift_left); __ Lsr(shift_left, in_reg_hi, shift_right); __ add(out_reg_lo, out_reg_lo, ShifterOperand(shift_left)); - __ b(&end); + __ b(final_label); __ Bind(&shift_by_32_plus_shift_right); // Shift by 32+shift_right. // out_reg_hi = (reg_hi >> shift_right) | (reg_lo << shift_left). @@ -4500,7 +4507,9 @@ void InstructionCodeGeneratorARM::HandleLongRotate(LocationSummary* locations) { __ Lsl(shift_right, in_reg_hi, shift_left); __ add(out_reg_lo, out_reg_lo, ShifterOperand(shift_right)); - __ Bind(&end); + if (end.IsLinked()) { + __ Bind(&end); + } } } @@ -4540,7 +4549,7 @@ void InstructionCodeGeneratorARM::VisitRor(HRor* ror) { break; } case Primitive::kPrimLong: { - HandleLongRotate(locations); + HandleLongRotate(ror); break; } default: @@ -4919,6 +4928,7 @@ void InstructionCodeGeneratorARM::VisitCompare(HCompare* compare) { Location right = locations->InAt(1); Label less, greater, done; + Label* final_label = codegen_->GetFinalLabel(compare, &done); Primitive::Type type = compare->InputAt(0)->GetType(); Condition less_cond; switch (type) { @@ -4958,17 +4968,19 @@ void InstructionCodeGeneratorARM::VisitCompare(HCompare* compare) { UNREACHABLE(); } - __ b(&done, EQ); + __ b(final_label, EQ); __ b(&less, less_cond); __ Bind(&greater); __ LoadImmediate(out, 1); - __ b(&done); + __ b(final_label); __ Bind(&less); __ LoadImmediate(out, -1); - __ Bind(&done); + if (done.IsLinked()) { + __ Bind(&done); + } } void LocationsBuilderARM::VisitPhi(HPhi* instruction) { @@ -5746,6 +5758,7 @@ void InstructionCodeGeneratorARM::VisitArrayGet(HArrayGet* instruction) { int32_t const_index = index.GetConstant()->AsIntConstant()->GetValue(); if (maybe_compressed_char_at) { Label uncompressed_load, done; + Label* final_label = codegen_->GetFinalLabel(instruction, &done); __ Lsrs(length, length, 1u); // LSRS has a 16-bit encoding, TST (immediate) does not. static_assert(static_cast<uint32_t>(mirror::StringCompressionFlag::kCompressed) == 0u, "Expecting 0=compressed, 1=uncompressed"); @@ -5754,13 +5767,15 @@ void InstructionCodeGeneratorARM::VisitArrayGet(HArrayGet* instruction) { out_loc.AsRegister<Register>(), obj, data_offset + const_index); - __ b(&done); + __ b(final_label); __ Bind(&uncompressed_load); __ LoadFromOffset(GetLoadOperandType(Primitive::kPrimChar), out_loc.AsRegister<Register>(), obj, data_offset + (const_index << 1)); - __ Bind(&done); + if (done.IsLinked()) { + __ Bind(&done); + } } else { uint32_t full_offset = data_offset + (const_index << Primitive::ComponentSizeShift(type)); @@ -5784,17 +5799,20 @@ void InstructionCodeGeneratorARM::VisitArrayGet(HArrayGet* instruction) { } if (maybe_compressed_char_at) { Label uncompressed_load, done; + Label* final_label = codegen_->GetFinalLabel(instruction, &done); __ Lsrs(length, length, 1u); // LSRS has a 16-bit encoding, TST (immediate) does not. static_assert(static_cast<uint32_t>(mirror::StringCompressionFlag::kCompressed) == 0u, "Expecting 0=compressed, 1=uncompressed"); __ b(&uncompressed_load, CS); __ ldrb(out_loc.AsRegister<Register>(), Address(temp, index.AsRegister<Register>(), Shift::LSL, 0)); - __ b(&done); + __ b(final_label); __ Bind(&uncompressed_load); __ ldrh(out_loc.AsRegister<Register>(), Address(temp, index.AsRegister<Register>(), Shift::LSL, 1)); - __ Bind(&done); + if (done.IsLinked()) { + __ Bind(&done); + } } else { codegen_->LoadFromShiftedRegOffset(type, out_loc, temp, index.AsRegister<Register>()); } @@ -6019,6 +6037,7 @@ void InstructionCodeGeneratorARM::VisitArraySet(HArraySet* instruction) { uint32_t super_offset = mirror::Class::SuperClassOffset().Int32Value(); uint32_t component_offset = mirror::Class::ComponentTypeOffset().Int32Value(); Label done; + Label* final_label = codegen_->GetFinalLabel(instruction, &done); SlowPathCodeARM* slow_path = nullptr; if (may_need_runtime_call_for_type_check) { @@ -6040,7 +6059,7 @@ void InstructionCodeGeneratorARM::VisitArraySet(HArraySet* instruction) { index.AsRegister<Register>()); } codegen_->MaybeRecordImplicitNullCheck(instruction); - __ b(&done); + __ b(final_label); __ Bind(&non_zero); } @@ -7021,6 +7040,7 @@ void InstructionCodeGeneratorARM::VisitInstanceOf(HInstanceOf* instruction) { uint32_t component_offset = mirror::Class::ComponentTypeOffset().Int32Value(); uint32_t primitive_offset = mirror::Class::PrimitiveTypeOffset().Int32Value(); Label done, zero; + Label* final_label = codegen_->GetFinalLabel(instruction, &done); SlowPathCodeARM* slow_path = nullptr; // Return 0 if `obj` is null. @@ -7042,7 +7062,7 @@ void InstructionCodeGeneratorARM::VisitInstanceOf(HInstanceOf* instruction) { // Classes must be equal for the instanceof to succeed. __ b(&zero, NE); __ LoadImmediate(out, 1); - __ b(&done); + __ b(final_label); break; } @@ -7065,12 +7085,12 @@ void InstructionCodeGeneratorARM::VisitInstanceOf(HInstanceOf* instruction) { maybe_temp_loc, kCompilerReadBarrierOption); // If `out` is null, we use it for the result, and jump to `done`. - __ CompareAndBranchIfZero(out, &done); + __ CompareAndBranchIfZero(out, final_label); __ cmp(out, ShifterOperand(cls)); __ b(&loop, NE); __ LoadImmediate(out, 1); if (zero.IsLinked()) { - __ b(&done); + __ b(final_label); } break; } @@ -7096,11 +7116,11 @@ void InstructionCodeGeneratorARM::VisitInstanceOf(HInstanceOf* instruction) { kCompilerReadBarrierOption); __ CompareAndBranchIfNonZero(out, &loop); // If `out` is null, we use it for the result, and jump to `done`. - __ b(&done); + __ b(final_label); __ Bind(&success); __ LoadImmediate(out, 1); if (zero.IsLinked()) { - __ b(&done); + __ b(final_label); } break; } @@ -7125,13 +7145,13 @@ void InstructionCodeGeneratorARM::VisitInstanceOf(HInstanceOf* instruction) { maybe_temp_loc, kCompilerReadBarrierOption); // If `out` is null, we use it for the result, and jump to `done`. - __ CompareAndBranchIfZero(out, &done); + __ CompareAndBranchIfZero(out, final_label); __ LoadFromOffset(kLoadUnsignedHalfword, out, out, primitive_offset); static_assert(Primitive::kPrimNot == 0, "Expected 0 for kPrimNot"); __ CompareAndBranchIfNonZero(out, &zero); __ Bind(&exact_check); __ LoadImmediate(out, 1); - __ b(&done); + __ b(final_label); break; } @@ -7152,7 +7172,7 @@ void InstructionCodeGeneratorARM::VisitInstanceOf(HInstanceOf* instruction) { __ b(slow_path->GetEntryLabel(), NE); __ LoadImmediate(out, 1); if (zero.IsLinked()) { - __ b(&done); + __ b(final_label); } break; } @@ -7183,7 +7203,7 @@ void InstructionCodeGeneratorARM::VisitInstanceOf(HInstanceOf* instruction) { codegen_->AddSlowPath(slow_path); __ b(slow_path->GetEntryLabel()); if (zero.IsLinked()) { - __ b(&done); + __ b(final_label); } break; } @@ -7269,9 +7289,10 @@ void InstructionCodeGeneratorARM::VisitCheckCast(HCheckCast* instruction) { codegen_->AddSlowPath(type_check_slow_path); Label done; + Label* final_label = codegen_->GetFinalLabel(instruction, &done); // Avoid null check if we know obj is not null. if (instruction->MustDoNullCheck()) { - __ CompareAndBranchIfZero(obj, &done); + __ CompareAndBranchIfZero(obj, final_label); } switch (type_check_kind) { @@ -7335,7 +7356,7 @@ void InstructionCodeGeneratorARM::VisitCheckCast(HCheckCast* instruction) { Label loop; __ Bind(&loop); __ cmp(temp, ShifterOperand(cls)); - __ b(&done, EQ); + __ b(final_label, EQ); // /* HeapReference<Class> */ temp = temp->super_class_ GenerateReferenceLoadOneRegister(instruction, @@ -7363,7 +7384,7 @@ void InstructionCodeGeneratorARM::VisitCheckCast(HCheckCast* instruction) { // Do an exact check. __ cmp(temp, ShifterOperand(cls)); - __ b(&done, EQ); + __ b(final_label, EQ); // Otherwise, we need to check that the object's class is a non-primitive array. // /* HeapReference<Class> */ temp = temp->component_type_ @@ -7433,7 +7454,10 @@ void InstructionCodeGeneratorARM::VisitCheckCast(HCheckCast* instruction) { break; } } - __ Bind(&done); + + if (done.IsLinked()) { + __ Bind(&done); + } __ Bind(type_check_slow_path->GetExitLabel()); } diff --git a/compiler/optimizing/code_generator_arm.h b/compiler/optimizing/code_generator_arm.h index 5b15902ccd..59a7f7c048 100644 --- a/compiler/optimizing/code_generator_arm.h +++ b/compiler/optimizing/code_generator_arm.h @@ -237,7 +237,7 @@ class InstructionCodeGeneratorARM : public InstructionCodeGenerator { void HandleBitwiseOperation(HBinaryOperation* operation); void HandleCondition(HCondition* condition); void HandleIntegerRotate(LocationSummary* locations); - void HandleLongRotate(LocationSummary* locations); + void HandleLongRotate(HRor* ror); void HandleShift(HBinaryOperation* operation); void GenerateWideAtomicStore(Register addr, uint32_t offset, diff --git a/compiler/optimizing/code_generator_arm_vixl.cc b/compiler/optimizing/code_generator_arm_vixl.cc index d75779cef6..2d2d8109a3 100644 --- a/compiler/optimizing/code_generator_arm_vixl.cc +++ b/compiler/optimizing/code_generator_arm_vixl.cc @@ -1950,6 +1950,7 @@ static bool CanGenerateConditionalMove(const Location& out, const Location& src) vixl32::Label* CodeGeneratorARMVIXL::GetFinalLabel(HInstruction* instruction, vixl32::Label* final_label) { DCHECK(!instruction->IsControlFlow() && !instruction->IsSuspendCheck()); + DCHECK(!instruction->IsInvoke() || !instruction->GetLocations()->CanCall()); const HBasicBlock* const block = instruction->GetBlock(); const HLoopInformation* const info = block->GetLoopInformation(); @@ -2925,16 +2926,20 @@ void InstructionCodeGeneratorARMVIXL::HandleCondition(HCondition* cond) { // Convert the jumps into the result. vixl32::Label done_label; + vixl32::Label* final_label = codegen_->GetFinalLabel(cond, &done_label); // False case: result = 0. __ Bind(&false_label); __ Mov(out, 0); - __ B(&done_label); + __ B(final_label); // True case: result = 1. __ Bind(&true_label); __ Mov(out, 1); - __ Bind(&done_label); + + if (done_label.IsReferenced()) { + __ Bind(&done_label); + } } void LocationsBuilderARMVIXL::VisitEqual(HEqual* comp) { @@ -4447,6 +4452,7 @@ void InstructionCodeGeneratorARMVIXL::HandleLongRotate(HRor* ror) { vixl32::Register shift_left = RegisterFrom(locations->GetTemp(1)); vixl32::Label end; vixl32::Label shift_by_32_plus_shift_right; + vixl32::Label* final_label = codegen_->GetFinalLabel(ror, &end); __ And(shift_right, RegisterFrom(rhs), 0x1F); __ Lsrs(shift_left, RegisterFrom(rhs), 6); @@ -4461,7 +4467,7 @@ void InstructionCodeGeneratorARMVIXL::HandleLongRotate(HRor* ror) { __ Lsl(out_reg_lo, in_reg_lo, shift_left); __ Lsr(shift_left, in_reg_hi, shift_right); __ Add(out_reg_lo, out_reg_lo, shift_left); - __ B(&end); + __ B(final_label); __ Bind(&shift_by_32_plus_shift_right); // Shift by 32+shift_right. // out_reg_hi = (reg_hi >> shift_right) | (reg_lo << shift_left). @@ -4473,7 +4479,9 @@ void InstructionCodeGeneratorARMVIXL::HandleLongRotate(HRor* ror) { __ Lsl(shift_right, in_reg_hi, shift_left); __ Add(out_reg_lo, out_reg_lo, shift_right); - __ Bind(&end); + if (end.IsReferenced()) { + __ Bind(&end); + } } } @@ -4906,6 +4914,7 @@ void InstructionCodeGeneratorARMVIXL::VisitCompare(HCompare* compare) { Location right = locations->InAt(1); vixl32::Label less, greater, done; + vixl32::Label* final_label = codegen_->GetFinalLabel(compare, &done); Primitive::Type type = compare->InputAt(0)->GetType(); vixl32::Condition less_cond = vixl32::Condition(kNone); switch (type) { @@ -4944,17 +4953,19 @@ void InstructionCodeGeneratorARMVIXL::VisitCompare(HCompare* compare) { UNREACHABLE(); } - __ B(eq, &done, /* far_target */ false); + __ B(eq, final_label, /* far_target */ false); __ B(less_cond, &less, /* far_target */ false); __ Bind(&greater); __ Mov(out, 1); - __ B(&done); + __ B(final_label); __ Bind(&less); __ Mov(out, -1); - __ Bind(&done); + if (done.IsReferenced()) { + __ Bind(&done); + } } void LocationsBuilderARMVIXL::VisitPhi(HPhi* instruction) { @@ -5746,6 +5757,7 @@ void InstructionCodeGeneratorARMVIXL::VisitArrayGet(HArrayGet* instruction) { int32_t const_index = Int32ConstantFrom(index); if (maybe_compressed_char_at) { vixl32::Label uncompressed_load, done; + vixl32::Label* final_label = codegen_->GetFinalLabel(instruction, &done); __ Lsrs(length, length, 1u); // LSRS has a 16-bit encoding, TST (immediate) does not. static_assert(static_cast<uint32_t>(mirror::StringCompressionFlag::kCompressed) == 0u, "Expecting 0=compressed, 1=uncompressed"); @@ -5754,13 +5766,15 @@ void InstructionCodeGeneratorARMVIXL::VisitArrayGet(HArrayGet* instruction) { RegisterFrom(out_loc), obj, data_offset + const_index); - __ B(&done); + __ B(final_label); __ Bind(&uncompressed_load); GetAssembler()->LoadFromOffset(GetLoadOperandType(Primitive::kPrimChar), RegisterFrom(out_loc), obj, data_offset + (const_index << 1)); - __ Bind(&done); + if (done.IsReferenced()) { + __ Bind(&done); + } } else { uint32_t full_offset = data_offset + (const_index << Primitive::ComponentSizeShift(type)); @@ -5785,15 +5799,18 @@ void InstructionCodeGeneratorARMVIXL::VisitArrayGet(HArrayGet* instruction) { } if (maybe_compressed_char_at) { vixl32::Label uncompressed_load, done; + vixl32::Label* final_label = codegen_->GetFinalLabel(instruction, &done); __ Lsrs(length, length, 1u); // LSRS has a 16-bit encoding, TST (immediate) does not. static_assert(static_cast<uint32_t>(mirror::StringCompressionFlag::kCompressed) == 0u, "Expecting 0=compressed, 1=uncompressed"); __ B(cs, &uncompressed_load, /* far_target */ false); __ Ldrb(RegisterFrom(out_loc), MemOperand(temp, RegisterFrom(index), vixl32::LSL, 0)); - __ B(&done); + __ B(final_label); __ Bind(&uncompressed_load); __ Ldrh(RegisterFrom(out_loc), MemOperand(temp, RegisterFrom(index), vixl32::LSL, 1)); - __ Bind(&done); + if (done.IsReferenced()) { + __ Bind(&done); + } } else { codegen_->LoadFromShiftedRegOffset(type, out_loc, temp, RegisterFrom(index)); } @@ -6032,6 +6049,7 @@ void InstructionCodeGeneratorARMVIXL::VisitArraySet(HArraySet* instruction) { uint32_t super_offset = mirror::Class::SuperClassOffset().Int32Value(); uint32_t component_offset = mirror::Class::ComponentTypeOffset().Int32Value(); vixl32::Label done; + vixl32::Label* final_label = codegen_->GetFinalLabel(instruction, &done); SlowPathCodeARMVIXL* slow_path = nullptr; if (may_need_runtime_call_for_type_check) { @@ -6054,7 +6072,7 @@ void InstructionCodeGeneratorARMVIXL::VisitArraySet(HArraySet* instruction) { // TODO(VIXL): Use a scope to ensure we record the pc info immediately after the preceding // store instruction. codegen_->MaybeRecordImplicitNullCheck(instruction); - __ B(&done); + __ B(final_label); __ Bind(&non_zero); } @@ -7062,6 +7080,7 @@ void InstructionCodeGeneratorARMVIXL::VisitInstanceOf(HInstanceOf* instruction) uint32_t component_offset = mirror::Class::ComponentTypeOffset().Int32Value(); uint32_t primitive_offset = mirror::Class::PrimitiveTypeOffset().Int32Value(); vixl32::Label done, zero; + vixl32::Label* final_label = codegen_->GetFinalLabel(instruction, &done); SlowPathCodeARMVIXL* slow_path = nullptr; // Return 0 if `obj` is null. @@ -7083,7 +7102,7 @@ void InstructionCodeGeneratorARMVIXL::VisitInstanceOf(HInstanceOf* instruction) // Classes must be equal for the instanceof to succeed. __ B(ne, &zero, /* far_target */ false); __ Mov(out, 1); - __ B(&done); + __ B(final_label); break; } @@ -7106,12 +7125,12 @@ void InstructionCodeGeneratorARMVIXL::VisitInstanceOf(HInstanceOf* instruction) maybe_temp_loc, kCompilerReadBarrierOption); // If `out` is null, we use it for the result, and jump to `done`. - __ CompareAndBranchIfZero(out, &done, /* far_target */ false); + __ CompareAndBranchIfZero(out, final_label, /* far_target */ false); __ Cmp(out, cls); __ B(ne, &loop, /* far_target */ false); __ Mov(out, 1); if (zero.IsReferenced()) { - __ B(&done); + __ B(final_label); } break; } @@ -7137,11 +7156,11 @@ void InstructionCodeGeneratorARMVIXL::VisitInstanceOf(HInstanceOf* instruction) kCompilerReadBarrierOption); __ CompareAndBranchIfNonZero(out, &loop); // If `out` is null, we use it for the result, and jump to `done`. - __ B(&done); + __ B(final_label); __ Bind(&success); __ Mov(out, 1); if (zero.IsReferenced()) { - __ B(&done); + __ B(final_label); } break; } @@ -7166,13 +7185,13 @@ void InstructionCodeGeneratorARMVIXL::VisitInstanceOf(HInstanceOf* instruction) maybe_temp_loc, kCompilerReadBarrierOption); // If `out` is null, we use it for the result, and jump to `done`. - __ CompareAndBranchIfZero(out, &done, /* far_target */ false); + __ CompareAndBranchIfZero(out, final_label, /* far_target */ false); GetAssembler()->LoadFromOffset(kLoadUnsignedHalfword, out, out, primitive_offset); static_assert(Primitive::kPrimNot == 0, "Expected 0 for kPrimNot"); __ CompareAndBranchIfNonZero(out, &zero, /* far_target */ false); __ Bind(&exact_check); __ Mov(out, 1); - __ B(&done); + __ B(final_label); break; } @@ -7193,7 +7212,7 @@ void InstructionCodeGeneratorARMVIXL::VisitInstanceOf(HInstanceOf* instruction) __ B(ne, slow_path->GetEntryLabel()); __ Mov(out, 1); if (zero.IsReferenced()) { - __ B(&done); + __ B(final_label); } break; } @@ -7224,7 +7243,7 @@ void InstructionCodeGeneratorARMVIXL::VisitInstanceOf(HInstanceOf* instruction) codegen_->AddSlowPath(slow_path); __ B(slow_path->GetEntryLabel()); if (zero.IsReferenced()) { - __ B(&done); + __ B(final_label); } break; } @@ -7310,9 +7329,10 @@ void InstructionCodeGeneratorARMVIXL::VisitCheckCast(HCheckCast* instruction) { codegen_->AddSlowPath(type_check_slow_path); vixl32::Label done; + vixl32::Label* final_label = codegen_->GetFinalLabel(instruction, &done); // Avoid null check if we know obj is not null. if (instruction->MustDoNullCheck()) { - __ CompareAndBranchIfZero(obj, &done, /* far_target */ false); + __ CompareAndBranchIfZero(obj, final_label, /* far_target */ false); } switch (type_check_kind) { @@ -7376,7 +7396,7 @@ void InstructionCodeGeneratorARMVIXL::VisitCheckCast(HCheckCast* instruction) { vixl32::Label loop; __ Bind(&loop); __ Cmp(temp, cls); - __ B(eq, &done, /* far_target */ false); + __ B(eq, final_label, /* far_target */ false); // /* HeapReference<Class> */ temp = temp->super_class_ GenerateReferenceLoadOneRegister(instruction, @@ -7404,7 +7424,7 @@ void InstructionCodeGeneratorARMVIXL::VisitCheckCast(HCheckCast* instruction) { // Do an exact check. __ Cmp(temp, cls); - __ B(eq, &done, /* far_target */ false); + __ B(eq, final_label, /* far_target */ false); // Otherwise, we need to check that the object's class is a non-primitive array. // /* HeapReference<Class> */ temp = temp->component_type_ @@ -7472,7 +7492,9 @@ void InstructionCodeGeneratorARMVIXL::VisitCheckCast(HCheckCast* instruction) { break; } } - __ Bind(&done); + if (done.IsReferenced()) { + __ Bind(&done); + } __ Bind(type_check_slow_path->GetExitLabel()); } diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc index 0b50619a66..958c1a6fdb 100644 --- a/compiler/optimizing/code_generator_x86.cc +++ b/compiler/optimizing/code_generator_x86.cc @@ -183,10 +183,13 @@ class SuspendCheckSlowPathX86 : public SlowPathCode { : SlowPathCode(instruction), successor_(successor) {} void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { + LocationSummary* locations = instruction_->GetLocations(); CodeGeneratorX86* x86_codegen = down_cast<CodeGeneratorX86*>(codegen); __ Bind(GetEntryLabel()); + SaveLiveRegisters(codegen, locations); // only saves full width XMM for SIMD x86_codegen->InvokeRuntime(kQuickTestSuspend, instruction_, instruction_->GetDexPc(), this); CheckEntrypointTypes<kQuickTestSuspend, void, void>(); + RestoreLiveRegisters(codegen, locations); // only saves full width XMM for SIMD if (successor_ == nullptr) { __ jmp(GetReturnLabel()); } else { @@ -963,12 +966,20 @@ size_t CodeGeneratorX86::RestoreCoreRegister(size_t stack_index, uint32_t reg_id } size_t CodeGeneratorX86::SaveFloatingPointRegister(size_t stack_index, uint32_t reg_id) { - __ movsd(Address(ESP, stack_index), XmmRegister(reg_id)); + if (GetGraph()->HasSIMD()) { + __ movupd(Address(ESP, stack_index), XmmRegister(reg_id)); + } else { + __ movsd(Address(ESP, stack_index), XmmRegister(reg_id)); + } return GetFloatingPointSpillSlotSize(); } size_t CodeGeneratorX86::RestoreFloatingPointRegister(size_t stack_index, uint32_t reg_id) { - __ movsd(XmmRegister(reg_id), Address(ESP, stack_index)); + if (GetGraph()->HasSIMD()) { + __ movupd(XmmRegister(reg_id), Address(ESP, stack_index)); + } else { + __ movsd(XmmRegister(reg_id), Address(ESP, stack_index)); + } return GetFloatingPointSpillSlotSize(); } @@ -5699,7 +5710,12 @@ void InstructionCodeGeneratorX86::VisitParallelMove(HParallelMove* instruction) void LocationsBuilderX86::VisitSuspendCheck(HSuspendCheck* instruction) { LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCallOnSlowPath); - locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty()); // No caller-save registers. + // In suspend check slow path, usually there are no caller-save registers at all. + // If SIMD instructions are present, however, we force spilling all live SIMD + // registers in full width (since the runtime only saves/restores lower part). + locations->SetCustomSlowPathCallerSaves(GetGraph()->HasSIMD() + ? RegisterSet::AllFpu() + : RegisterSet::Empty()); } void InstructionCodeGeneratorX86::VisitSuspendCheck(HSuspendCheck* instruction) { diff --git a/compiler/optimizing/code_generator_x86.h b/compiler/optimizing/code_generator_x86.h index 65ee383b54..ca3a9eadd2 100644 --- a/compiler/optimizing/code_generator_x86.h +++ b/compiler/optimizing/code_generator_x86.h @@ -348,8 +348,9 @@ class CodeGeneratorX86 : public CodeGenerator { } size_t GetFloatingPointSpillSlotSize() const OVERRIDE { - // 8 bytes == 2 words for each spill. - return 2 * kX86WordSize; + return GetGraph()->HasSIMD() + ? 4 * kX86WordSize // 16 bytes == 4 words for each spill + : 2 * kX86WordSize; // 8 bytes == 2 words for each spill } HGraphVisitor* GetLocationBuilder() OVERRIDE { diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc index 08f1adfcff..c106d9b06e 100644 --- a/compiler/optimizing/code_generator_x86_64.cc +++ b/compiler/optimizing/code_generator_x86_64.cc @@ -140,10 +140,13 @@ class SuspendCheckSlowPathX86_64 : public SlowPathCode { : SlowPathCode(instruction), successor_(successor) {} void EmitNativeCode(CodeGenerator* codegen) OVERRIDE { + LocationSummary* locations = instruction_->GetLocations(); CodeGeneratorX86_64* x86_64_codegen = down_cast<CodeGeneratorX86_64*>(codegen); __ Bind(GetEntryLabel()); + SaveLiveRegisters(codegen, locations); // only saves full width XMM for SIMD x86_64_codegen->InvokeRuntime(kQuickTestSuspend, instruction_, instruction_->GetDexPc(), this); CheckEntrypointTypes<kQuickTestSuspend, void, void>(); + RestoreLiveRegisters(codegen, locations); // only saves full width XMM for SIMD if (successor_ == nullptr) { __ jmp(GetReturnLabel()); } else { @@ -1158,13 +1161,21 @@ size_t CodeGeneratorX86_64::RestoreCoreRegister(size_t stack_index, uint32_t reg } size_t CodeGeneratorX86_64::SaveFloatingPointRegister(size_t stack_index, uint32_t reg_id) { - __ movsd(Address(CpuRegister(RSP), stack_index), XmmRegister(reg_id)); - return kX86_64WordSize; + if (GetGraph()->HasSIMD()) { + __ movupd(Address(CpuRegister(RSP), stack_index), XmmRegister(reg_id)); + } else { + __ movsd(Address(CpuRegister(RSP), stack_index), XmmRegister(reg_id)); + } + return GetFloatingPointSpillSlotSize(); } size_t CodeGeneratorX86_64::RestoreFloatingPointRegister(size_t stack_index, uint32_t reg_id) { - __ movsd(XmmRegister(reg_id), Address(CpuRegister(RSP), stack_index)); - return kX86_64WordSize; + if (GetGraph()->HasSIMD()) { + __ movupd(XmmRegister(reg_id), Address(CpuRegister(RSP), stack_index)); + } else { + __ movsd(XmmRegister(reg_id), Address(CpuRegister(RSP), stack_index)); + } + return GetFloatingPointSpillSlotSize(); } void CodeGeneratorX86_64::InvokeRuntime(QuickEntrypointEnum entrypoint, @@ -5152,7 +5163,12 @@ void InstructionCodeGeneratorX86_64::VisitParallelMove(HParallelMove* instructio void LocationsBuilderX86_64::VisitSuspendCheck(HSuspendCheck* instruction) { LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kCallOnSlowPath); - locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty()); // No caller-save registers. + // In suspend check slow path, usually there are no caller-save registers at all. + // If SIMD instructions are present, however, we force spilling all live SIMD + // registers in full width (since the runtime only saves/restores lower part). + locations->SetCustomSlowPathCallerSaves(GetGraph()->HasSIMD() + ? RegisterSet::AllFpu() + : RegisterSet::Empty()); } void InstructionCodeGeneratorX86_64::VisitSuspendCheck(HSuspendCheck* instruction) { diff --git a/compiler/optimizing/code_generator_x86_64.h b/compiler/optimizing/code_generator_x86_64.h index 376c3ce381..c8336dabd9 100644 --- a/compiler/optimizing/code_generator_x86_64.h +++ b/compiler/optimizing/code_generator_x86_64.h @@ -326,7 +326,9 @@ class CodeGeneratorX86_64 : public CodeGenerator { } size_t GetFloatingPointSpillSlotSize() const OVERRIDE { - return kX86_64WordSize; + return GetGraph()->HasSIMD() + ? 2 * kX86_64WordSize // 16 bytes == 2 x86_64 words for each spill + : 1 * kX86_64WordSize; // 8 bytes == 1 x86_64 words for each spill } HGraphVisitor* GetLocationBuilder() OVERRIDE { diff --git a/compiler/optimizing/inliner.cc b/compiler/optimizing/inliner.cc index 62f5114e59..9550a53333 100644 --- a/compiler/optimizing/inliner.cc +++ b/compiler/optimizing/inliner.cc @@ -292,7 +292,18 @@ ArtMethod* HInliner::TryCHADevirtualization(ArtMethod* resolved_method) { return nullptr; } PointerSize pointer_size = caller_compilation_unit_.GetClassLinker()->GetImagePointerSize(); - return resolved_method->GetSingleImplementation(pointer_size); + ArtMethod* single_impl = resolved_method->GetSingleImplementation(pointer_size); + if (single_impl == nullptr) { + return nullptr; + } + if (single_impl->IsProxyMethod()) { + // Proxy method is a generic invoker that's not worth + // devirtualizing/inlining. It also causes issues when the proxy + // method is in another dex file if we try to rewrite invoke-interface to + // invoke-virtual because a proxy method doesn't have a real dex file. + return nullptr; + } + return single_impl; } bool HInliner::TryInline(HInvoke* invoke_instruction) { @@ -1021,11 +1032,23 @@ bool HInliner::TryInlineAndReplace(HInvoke* invoke_instruction, HBasicBlock* bb_cursor = invoke_instruction->GetBlock(); if (!TryBuildAndInline(invoke_instruction, method, receiver_type, &return_replacement)) { if (invoke_instruction->IsInvokeInterface()) { + DCHECK(!method->IsProxyMethod()); // Turn an invoke-interface into an invoke-virtual. An invoke-virtual is always // better than an invoke-interface because: // 1) In the best case, the interface call has one more indirection (to fetch the IMT). // 2) We will not go to the conflict trampoline with an invoke-virtual. // TODO: Consider sharpening once it is not dependent on the compiler driver. + + if (method->IsDefault() && !method->IsCopied()) { + // Changing to invoke-virtual cannot be done on an original default method + // since it's not in any vtable. Devirtualization by exact type/inline-cache + // always uses a method in the iftable which is never an original default + // method. + // On the other hand, inlining an original default method by CHA is fine. + DCHECK(cha_devirtualize); + return false; + } + const DexFile& caller_dex_file = *caller_compilation_unit_.GetDexFile(); uint32_t dex_method_index = FindMethodIndexIn( method, caller_dex_file, invoke_instruction->GetDexMethodIndex()); diff --git a/compiler/optimizing/intrinsics_arm.cc b/compiler/optimizing/intrinsics_arm.cc index 98b80f5d3c..1006a776f0 100644 --- a/compiler/optimizing/intrinsics_arm.cc +++ b/compiler/optimizing/intrinsics_arm.cc @@ -270,9 +270,11 @@ static void CreateFPToFPLocations(ArenaAllocator* arena, HInvoke* invoke) { locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap); } -static void GenNumberOfLeadingZeros(LocationSummary* locations, +static void GenNumberOfLeadingZeros(HInvoke* invoke, Primitive::Type type, - ArmAssembler* assembler) { + CodeGeneratorARM* codegen) { + ArmAssembler* assembler = codegen->GetAssembler(); + LocationSummary* locations = invoke->GetLocations(); Location in = locations->InAt(0); Register out = locations->Out().AsRegister<Register>(); @@ -282,11 +284,14 @@ static void GenNumberOfLeadingZeros(LocationSummary* locations, Register in_reg_lo = in.AsRegisterPairLow<Register>(); Register in_reg_hi = in.AsRegisterPairHigh<Register>(); Label end; + Label* final_label = codegen->GetFinalLabel(invoke, &end); __ clz(out, in_reg_hi); - __ CompareAndBranchIfNonZero(in_reg_hi, &end); + __ CompareAndBranchIfNonZero(in_reg_hi, final_label); __ clz(out, in_reg_lo); __ AddConstant(out, 32); - __ Bind(&end); + if (end.IsLinked()) { + __ Bind(&end); + } } else { __ clz(out, in.AsRegister<Register>()); } @@ -297,7 +302,7 @@ void IntrinsicLocationsBuilderARM::VisitIntegerNumberOfLeadingZeros(HInvoke* inv } void IntrinsicCodeGeneratorARM::VisitIntegerNumberOfLeadingZeros(HInvoke* invoke) { - GenNumberOfLeadingZeros(invoke->GetLocations(), Primitive::kPrimInt, GetAssembler()); + GenNumberOfLeadingZeros(invoke, Primitive::kPrimInt, codegen_); } void IntrinsicLocationsBuilderARM::VisitLongNumberOfLeadingZeros(HInvoke* invoke) { @@ -309,27 +314,32 @@ void IntrinsicLocationsBuilderARM::VisitLongNumberOfLeadingZeros(HInvoke* invoke } void IntrinsicCodeGeneratorARM::VisitLongNumberOfLeadingZeros(HInvoke* invoke) { - GenNumberOfLeadingZeros(invoke->GetLocations(), Primitive::kPrimLong, GetAssembler()); + GenNumberOfLeadingZeros(invoke, Primitive::kPrimLong, codegen_); } -static void GenNumberOfTrailingZeros(LocationSummary* locations, +static void GenNumberOfTrailingZeros(HInvoke* invoke, Primitive::Type type, - ArmAssembler* assembler) { + CodeGeneratorARM* codegen) { DCHECK((type == Primitive::kPrimInt) || (type == Primitive::kPrimLong)); + ArmAssembler* assembler = codegen->GetAssembler(); + LocationSummary* locations = invoke->GetLocations(); Register out = locations->Out().AsRegister<Register>(); if (type == Primitive::kPrimLong) { Register in_reg_lo = locations->InAt(0).AsRegisterPairLow<Register>(); Register in_reg_hi = locations->InAt(0).AsRegisterPairHigh<Register>(); Label end; + Label* final_label = codegen->GetFinalLabel(invoke, &end); __ rbit(out, in_reg_lo); __ clz(out, out); - __ CompareAndBranchIfNonZero(in_reg_lo, &end); + __ CompareAndBranchIfNonZero(in_reg_lo, final_label); __ rbit(out, in_reg_hi); __ clz(out, out); __ AddConstant(out, 32); - __ Bind(&end); + if (end.IsLinked()) { + __ Bind(&end); + } } else { Register in = locations->InAt(0).AsRegister<Register>(); __ rbit(out, in); @@ -346,7 +356,7 @@ void IntrinsicLocationsBuilderARM::VisitIntegerNumberOfTrailingZeros(HInvoke* in } void IntrinsicCodeGeneratorARM::VisitIntegerNumberOfTrailingZeros(HInvoke* invoke) { - GenNumberOfTrailingZeros(invoke->GetLocations(), Primitive::kPrimInt, GetAssembler()); + GenNumberOfTrailingZeros(invoke, Primitive::kPrimInt, codegen_); } void IntrinsicLocationsBuilderARM::VisitLongNumberOfTrailingZeros(HInvoke* invoke) { @@ -358,7 +368,7 @@ void IntrinsicLocationsBuilderARM::VisitLongNumberOfTrailingZeros(HInvoke* invok } void IntrinsicCodeGeneratorARM::VisitLongNumberOfTrailingZeros(HInvoke* invoke) { - GenNumberOfTrailingZeros(invoke->GetLocations(), Primitive::kPrimLong, GetAssembler()); + GenNumberOfTrailingZeros(invoke, Primitive::kPrimLong, codegen_); } static void MathAbsFP(LocationSummary* locations, bool is64bit, ArmAssembler* assembler) { @@ -1355,6 +1365,7 @@ void IntrinsicCodeGeneratorARM::VisitStringEquals(HInvoke* invoke) { Label end; Label return_true; Label return_false; + Label* final_label = codegen_->GetFinalLabel(invoke, &end); // Get offsets of count, value, and class fields within a string object. const uint32_t count_offset = mirror::String::CountOffset().Uint32Value(); @@ -1428,12 +1439,15 @@ void IntrinsicCodeGeneratorARM::VisitStringEquals(HInvoke* invoke) { // If loop does not result in returning false, we return true. __ Bind(&return_true); __ LoadImmediate(out, 1); - __ b(&end); + __ b(final_label); // Return false and exit the function. __ Bind(&return_false); __ LoadImmediate(out, 0); - __ Bind(&end); + + if (end.IsLinked()) { + __ Bind(&end); + } } static void GenerateVisitStringIndexOf(HInvoke* invoke, @@ -2491,13 +2505,14 @@ void IntrinsicCodeGeneratorARM::VisitStringGetCharsNoCheck(HInvoke* invoke) { Register dst_ptr = locations->GetTemp(2).AsRegister<Register>(); Label done, compressed_string_loop; + Label* final_label = codegen_->GetFinalLabel(invoke, &done); // dst to be copied. __ add(dst_ptr, dstObj, ShifterOperand(data_offset)); __ add(dst_ptr, dst_ptr, ShifterOperand(dstBegin, LSL, 1)); __ subs(num_chr, srcEnd, ShifterOperand(srcBegin)); // Early out for valid zero-length retrievals. - __ b(&done, EQ); + __ b(final_label, EQ); // src range to copy. __ add(src_ptr, srcObj, ShifterOperand(value_offset)); @@ -2534,7 +2549,7 @@ void IntrinsicCodeGeneratorARM::VisitStringGetCharsNoCheck(HInvoke* invoke) { __ b(&loop, GE); __ adds(num_chr, num_chr, ShifterOperand(4)); - __ b(&done, EQ); + __ b(final_label, EQ); // Main loop for < 4 character case and remainder handling. Loads and stores one // 16-bit Java character at a time. @@ -2545,7 +2560,7 @@ void IntrinsicCodeGeneratorARM::VisitStringGetCharsNoCheck(HInvoke* invoke) { __ b(&remainder, GT); if (mirror::kUseStringCompression) { - __ b(&done); + __ b(final_label); const size_t c_char_size = Primitive::ComponentSize(Primitive::kPrimByte); DCHECK_EQ(c_char_size, 1u); @@ -2559,7 +2574,9 @@ void IntrinsicCodeGeneratorARM::VisitStringGetCharsNoCheck(HInvoke* invoke) { __ b(&compressed_string_loop, GT); } - __ Bind(&done); + if (done.IsLinked()) { + __ Bind(&done); + } } void IntrinsicLocationsBuilderARM::VisitFloatIsInfinite(HInvoke* invoke) { diff --git a/compiler/optimizing/intrinsics_arm_vixl.cc b/compiler/optimizing/intrinsics_arm_vixl.cc index 19ff49c6ce..b25bad7170 100644 --- a/compiler/optimizing/intrinsics_arm_vixl.cc +++ b/compiler/optimizing/intrinsics_arm_vixl.cc @@ -333,9 +333,11 @@ static void CreateFPToFPLocations(ArenaAllocator* arena, HInvoke* invoke) { locations->SetOut(Location::RequiresFpuRegister(), Location::kNoOutputOverlap); } -static void GenNumberOfLeadingZeros(LocationSummary* locations, +static void GenNumberOfLeadingZeros(HInvoke* invoke, Primitive::Type type, - ArmVIXLAssembler* assembler) { + CodeGeneratorARMVIXL* codegen) { + ArmVIXLAssembler* assembler = codegen->GetAssembler(); + LocationSummary* locations = invoke->GetLocations(); Location in = locations->InAt(0); vixl32::Register out = RegisterFrom(locations->Out()); @@ -345,11 +347,14 @@ static void GenNumberOfLeadingZeros(LocationSummary* locations, vixl32::Register in_reg_lo = LowRegisterFrom(in); vixl32::Register in_reg_hi = HighRegisterFrom(in); vixl32::Label end; + vixl32::Label* final_label = codegen->GetFinalLabel(invoke, &end); __ Clz(out, in_reg_hi); - __ CompareAndBranchIfNonZero(in_reg_hi, &end, /* far_target */ false); + __ CompareAndBranchIfNonZero(in_reg_hi, final_label, /* far_target */ false); __ Clz(out, in_reg_lo); __ Add(out, out, 32); - __ Bind(&end); + if (end.IsReferenced()) { + __ Bind(&end); + } } else { __ Clz(out, RegisterFrom(in)); } @@ -360,7 +365,7 @@ void IntrinsicLocationsBuilderARMVIXL::VisitIntegerNumberOfLeadingZeros(HInvoke* } void IntrinsicCodeGeneratorARMVIXL::VisitIntegerNumberOfLeadingZeros(HInvoke* invoke) { - GenNumberOfLeadingZeros(invoke->GetLocations(), Primitive::kPrimInt, GetAssembler()); + GenNumberOfLeadingZeros(invoke, Primitive::kPrimInt, codegen_); } void IntrinsicLocationsBuilderARMVIXL::VisitLongNumberOfLeadingZeros(HInvoke* invoke) { @@ -372,27 +377,32 @@ void IntrinsicLocationsBuilderARMVIXL::VisitLongNumberOfLeadingZeros(HInvoke* in } void IntrinsicCodeGeneratorARMVIXL::VisitLongNumberOfLeadingZeros(HInvoke* invoke) { - GenNumberOfLeadingZeros(invoke->GetLocations(), Primitive::kPrimLong, GetAssembler()); + GenNumberOfLeadingZeros(invoke, Primitive::kPrimLong, codegen_); } -static void GenNumberOfTrailingZeros(LocationSummary* locations, +static void GenNumberOfTrailingZeros(HInvoke* invoke, Primitive::Type type, - ArmVIXLAssembler* assembler) { + CodeGeneratorARMVIXL* codegen) { DCHECK((type == Primitive::kPrimInt) || (type == Primitive::kPrimLong)); + ArmVIXLAssembler* assembler = codegen->GetAssembler(); + LocationSummary* locations = invoke->GetLocations(); vixl32::Register out = RegisterFrom(locations->Out()); if (type == Primitive::kPrimLong) { vixl32::Register in_reg_lo = LowRegisterFrom(locations->InAt(0)); vixl32::Register in_reg_hi = HighRegisterFrom(locations->InAt(0)); vixl32::Label end; + vixl32::Label* final_label = codegen->GetFinalLabel(invoke, &end); __ Rbit(out, in_reg_lo); __ Clz(out, out); - __ CompareAndBranchIfNonZero(in_reg_lo, &end, /* far_target */ false); + __ CompareAndBranchIfNonZero(in_reg_lo, final_label, /* far_target */ false); __ Rbit(out, in_reg_hi); __ Clz(out, out); __ Add(out, out, 32); - __ Bind(&end); + if (end.IsReferenced()) { + __ Bind(&end); + } } else { vixl32::Register in = RegisterFrom(locations->InAt(0)); __ Rbit(out, in); @@ -409,7 +419,7 @@ void IntrinsicLocationsBuilderARMVIXL::VisitIntegerNumberOfTrailingZeros(HInvoke } void IntrinsicCodeGeneratorARMVIXL::VisitIntegerNumberOfTrailingZeros(HInvoke* invoke) { - GenNumberOfTrailingZeros(invoke->GetLocations(), Primitive::kPrimInt, GetAssembler()); + GenNumberOfTrailingZeros(invoke, Primitive::kPrimInt, codegen_); } void IntrinsicLocationsBuilderARMVIXL::VisitLongNumberOfTrailingZeros(HInvoke* invoke) { @@ -421,7 +431,7 @@ void IntrinsicLocationsBuilderARMVIXL::VisitLongNumberOfTrailingZeros(HInvoke* i } void IntrinsicCodeGeneratorARMVIXL::VisitLongNumberOfTrailingZeros(HInvoke* invoke) { - GenNumberOfTrailingZeros(invoke->GetLocations(), Primitive::kPrimLong, GetAssembler()); + GenNumberOfTrailingZeros(invoke, Primitive::kPrimLong, codegen_); } static void MathAbsFP(HInvoke* invoke, ArmVIXLAssembler* assembler) { @@ -502,7 +512,8 @@ void IntrinsicCodeGeneratorARMVIXL::VisitMathAbsLong(HInvoke* invoke) { GenAbsInteger(invoke->GetLocations(), /* is64bit */ true, GetAssembler()); } -static void GenMinMaxFloat(HInvoke* invoke, bool is_min, ArmVIXLAssembler* assembler) { +static void GenMinMaxFloat(HInvoke* invoke, bool is_min, CodeGeneratorARMVIXL* codegen) { + ArmVIXLAssembler* assembler = codegen->GetAssembler(); Location op1_loc = invoke->GetLocations()->InAt(0); Location op2_loc = invoke->GetLocations()->InAt(1); Location out_loc = invoke->GetLocations()->Out(); @@ -520,6 +531,7 @@ static void GenMinMaxFloat(HInvoke* invoke, bool is_min, ArmVIXLAssembler* assem const vixl32::Register temp1 = temps.Acquire(); vixl32::Register temp2 = RegisterFrom(invoke->GetLocations()->GetTemp(0)); vixl32::Label nan, done; + vixl32::Label* final_label = codegen->GetFinalLabel(invoke, &done); DCHECK(op1.Is(out)); @@ -536,7 +548,8 @@ static void GenMinMaxFloat(HInvoke* invoke, bool is_min, ArmVIXLAssembler* assem __ it(cond); __ vmov(cond, F32, out, op2); } - __ B(ne, &done, /* far_target */ false); // for <>(not equal), we've done min/max calculation. + // for <>(not equal), we've done min/max calculation. + __ B(ne, final_label, /* far_target */ false); // handle op1 == op2, max(+0.0,-0.0), min(+0.0,-0.0). __ Vmov(temp1, op1); @@ -547,14 +560,16 @@ static void GenMinMaxFloat(HInvoke* invoke, bool is_min, ArmVIXLAssembler* assem __ And(temp1, temp1, temp2); } __ Vmov(out, temp1); - __ B(&done); + __ B(final_label); // handle NaN input. __ Bind(&nan); __ Movt(temp1, High16Bits(kNanFloat)); // 0x7FC0xxxx is a NaN. __ Vmov(out, temp1); - __ Bind(&done); + if (done.IsReferenced()) { + __ Bind(&done); + } } static void CreateFPFPToFPLocations(ArenaAllocator* arena, HInvoke* invoke) { @@ -572,7 +587,7 @@ void IntrinsicLocationsBuilderARMVIXL::VisitMathMinFloatFloat(HInvoke* invoke) { } void IntrinsicCodeGeneratorARMVIXL::VisitMathMinFloatFloat(HInvoke* invoke) { - GenMinMaxFloat(invoke, /* is_min */ true, GetAssembler()); + GenMinMaxFloat(invoke, /* is_min */ true, codegen_); } void IntrinsicLocationsBuilderARMVIXL::VisitMathMaxFloatFloat(HInvoke* invoke) { @@ -581,10 +596,11 @@ void IntrinsicLocationsBuilderARMVIXL::VisitMathMaxFloatFloat(HInvoke* invoke) { } void IntrinsicCodeGeneratorARMVIXL::VisitMathMaxFloatFloat(HInvoke* invoke) { - GenMinMaxFloat(invoke, /* is_min */ false, GetAssembler()); + GenMinMaxFloat(invoke, /* is_min */ false, codegen_); } -static void GenMinMaxDouble(HInvoke* invoke, bool is_min, ArmVIXLAssembler* assembler) { +static void GenMinMaxDouble(HInvoke* invoke, bool is_min, CodeGeneratorARMVIXL* codegen) { + ArmVIXLAssembler* assembler = codegen->GetAssembler(); Location op1_loc = invoke->GetLocations()->InAt(0); Location op2_loc = invoke->GetLocations()->InAt(1); Location out_loc = invoke->GetLocations()->Out(); @@ -599,6 +615,7 @@ static void GenMinMaxDouble(HInvoke* invoke, bool is_min, ArmVIXLAssembler* asse vixl32::DRegister op2 = DRegisterFrom(op2_loc); vixl32::DRegister out = OutputDRegister(invoke); vixl32::Label handle_nan_eq, done; + vixl32::Label* final_label = codegen->GetFinalLabel(invoke, &done); DCHECK(op1.Is(out)); @@ -615,19 +632,22 @@ static void GenMinMaxDouble(HInvoke* invoke, bool is_min, ArmVIXLAssembler* asse __ it(cond); __ vmov(cond, F64, out, op2); } - __ B(ne, &done, /* far_target */ false); // for <>(not equal), we've done min/max calculation. + // for <>(not equal), we've done min/max calculation. + __ B(ne, final_label, /* far_target */ false); // handle op1 == op2, max(+0.0,-0.0). if (!is_min) { __ Vand(F64, out, op1, op2); - __ B(&done); + __ B(final_label); } // handle op1 == op2, min(+0.0,-0.0), NaN input. __ Bind(&handle_nan_eq); __ Vorr(F64, out, op1, op2); // assemble op1/-0.0/NaN. - __ Bind(&done); + if (done.IsReferenced()) { + __ Bind(&done); + } } void IntrinsicLocationsBuilderARMVIXL::VisitMathMinDoubleDouble(HInvoke* invoke) { @@ -635,7 +655,7 @@ void IntrinsicLocationsBuilderARMVIXL::VisitMathMinDoubleDouble(HInvoke* invoke) } void IntrinsicCodeGeneratorARMVIXL::VisitMathMinDoubleDouble(HInvoke* invoke) { - GenMinMaxDouble(invoke, /* is_min */ true , GetAssembler()); + GenMinMaxDouble(invoke, /* is_min */ true , codegen_); } void IntrinsicLocationsBuilderARMVIXL::VisitMathMaxDoubleDouble(HInvoke* invoke) { @@ -643,7 +663,7 @@ void IntrinsicLocationsBuilderARMVIXL::VisitMathMaxDoubleDouble(HInvoke* invoke) } void IntrinsicCodeGeneratorARMVIXL::VisitMathMaxDoubleDouble(HInvoke* invoke) { - GenMinMaxDouble(invoke, /* is_min */ false, GetAssembler()); + GenMinMaxDouble(invoke, /* is_min */ false, codegen_); } static void GenMinMaxLong(HInvoke* invoke, bool is_min, ArmVIXLAssembler* assembler) { @@ -1670,6 +1690,7 @@ void IntrinsicCodeGeneratorARMVIXL::VisitStringEquals(HInvoke* invoke) { vixl32::Label end; vixl32::Label return_true; vixl32::Label return_false; + vixl32::Label* final_label = codegen_->GetFinalLabel(invoke, &end); // Get offsets of count, value, and class fields within a string object. const uint32_t count_offset = mirror::String::CountOffset().Uint32Value(); @@ -1746,12 +1767,15 @@ void IntrinsicCodeGeneratorARMVIXL::VisitStringEquals(HInvoke* invoke) { // If loop does not result in returning false, we return true. __ Bind(&return_true); __ Mov(out, 1); - __ B(&end); + __ B(final_label); // Return false and exit the function. __ Bind(&return_false); __ Mov(out, 0); - __ Bind(&end); + + if (end.IsReferenced()) { + __ Bind(&end); + } } static void GenerateVisitStringIndexOf(HInvoke* invoke, @@ -2789,13 +2813,14 @@ void IntrinsicCodeGeneratorARMVIXL::VisitStringGetCharsNoCheck(HInvoke* invoke) vixl32::Register dst_ptr = RegisterFrom(locations->GetTemp(2)); vixl32::Label done, compressed_string_loop; + vixl32::Label* final_label = codegen_->GetFinalLabel(invoke, &done); // dst to be copied. __ Add(dst_ptr, dstObj, data_offset); __ Add(dst_ptr, dst_ptr, Operand(dstBegin, vixl32::LSL, 1)); __ Subs(num_chr, srcEnd, srcBegin); // Early out for valid zero-length retrievals. - __ B(eq, &done, /* far_target */ false); + __ B(eq, final_label, /* far_target */ false); // src range to copy. __ Add(src_ptr, srcObj, value_offset); @@ -2839,7 +2864,7 @@ void IntrinsicCodeGeneratorARMVIXL::VisitStringGetCharsNoCheck(HInvoke* invoke) __ B(ge, &loop, /* far_target */ false); __ Adds(num_chr, num_chr, 4); - __ B(eq, &done, /* far_target */ false); + __ B(eq, final_label, /* far_target */ false); // Main loop for < 4 character case and remainder handling. Loads and stores one // 16-bit Java character at a time. @@ -2852,7 +2877,7 @@ void IntrinsicCodeGeneratorARMVIXL::VisitStringGetCharsNoCheck(HInvoke* invoke) __ B(gt, &remainder, /* far_target */ false); if (mirror::kUseStringCompression) { - __ B(&done); + __ B(final_label); const size_t c_char_size = Primitive::ComponentSize(Primitive::kPrimByte); DCHECK_EQ(c_char_size, 1u); @@ -2868,7 +2893,9 @@ void IntrinsicCodeGeneratorARMVIXL::VisitStringGetCharsNoCheck(HInvoke* invoke) __ B(gt, &compressed_string_loop, /* far_target */ false); } - __ Bind(&done); + if (done.IsReferenced()) { + __ Bind(&done); + } } void IntrinsicLocationsBuilderARMVIXL::VisitFloatIsInfinite(HInvoke* invoke) { diff --git a/compiler/optimizing/locations.h b/compiler/optimizing/locations.h index 091b58a63d..d391f6913c 100644 --- a/compiler/optimizing/locations.h +++ b/compiler/optimizing/locations.h @@ -417,6 +417,7 @@ std::ostream& operator<<(std::ostream& os, const Location::Policy& rhs); class RegisterSet : public ValueObject { public: static RegisterSet Empty() { return RegisterSet(); } + static RegisterSet AllFpu() { return RegisterSet(0, -1); } void Add(Location loc) { if (loc.IsRegister()) { @@ -462,6 +463,7 @@ class RegisterSet : public ValueObject { private: RegisterSet() : core_registers_(0), floating_point_registers_(0) {} + RegisterSet(uint32_t core, uint32_t fp) : core_registers_(core), floating_point_registers_(fp) {} uint32_t core_registers_; uint32_t floating_point_registers_; diff --git a/compiler/optimizing/nodes.cc b/compiler/optimizing/nodes.cc index 020e4463d4..ec706e6694 100644 --- a/compiler/optimizing/nodes.cc +++ b/compiler/optimizing/nodes.cc @@ -2046,6 +2046,9 @@ HInstruction* HGraph::InlineInto(HGraph* outer_graph, HInvoke* invoke) { if (HasTryCatch()) { outer_graph->SetHasTryCatch(true); } + if (HasSIMD()) { + outer_graph->SetHasSIMD(true); + } HInstruction* return_value = nullptr; if (GetBlocks().size() == 3) { diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h index 542b218cf8..6881d8f6ae 100644 --- a/compiler/optimizing/nodes.h +++ b/compiler/optimizing/nodes.h @@ -323,6 +323,7 @@ class HGraph : public ArenaObject<kArenaAllocGraph> { temporaries_vreg_slots_(0), has_bounds_checks_(false), has_try_catch_(false), + has_simd_(false), has_loops_(false), has_irreducible_loops_(false), debuggable_(debuggable), @@ -560,6 +561,9 @@ class HGraph : public ArenaObject<kArenaAllocGraph> { bool HasTryCatch() const { return has_try_catch_; } void SetHasTryCatch(bool value) { has_try_catch_ = value; } + bool HasSIMD() const { return has_simd_; } + void SetHasSIMD(bool value) { has_simd_ = value; } + bool HasLoops() const { return has_loops_; } void SetHasLoops(bool value) { has_loops_ = value; } @@ -652,6 +656,11 @@ class HGraph : public ArenaObject<kArenaAllocGraph> { // false positives. bool has_try_catch_; + // Flag whether SIMD instructions appear in the graph. If true, the + // code generators may have to be more careful spilling the wider + // contents of SIMD registers. + bool has_simd_; + // Flag whether there are any loops in the graph. We can skip loop // optimization if it's false. It's only best effort to keep it up // to date in the presence of code elimination so there might be false diff --git a/compiler/optimizing/register_allocation_resolver.cc b/compiler/optimizing/register_allocation_resolver.cc index 8a9c1ccaff..0d33b49fdb 100644 --- a/compiler/optimizing/register_allocation_resolver.cc +++ b/compiler/optimizing/register_allocation_resolver.cc @@ -299,11 +299,13 @@ void RegisterAllocationResolver::ConnectSiblings(LiveInterval* interval) { // Currently, we spill unconditionnally the current method in the code generators. && !interval->GetDefinedBy()->IsCurrentMethod()) { // We spill eagerly, so move must be at definition. - InsertMoveAfter(interval->GetDefinedBy(), - interval->ToLocation(), - interval->NeedsTwoSpillSlots() - ? Location::DoubleStackSlot(interval->GetParent()->GetSpillSlot()) - : Location::StackSlot(interval->GetParent()->GetSpillSlot())); + Location loc; + switch (interval->NumberOfSpillSlotsNeeded()) { + case 1: loc = Location::StackSlot(interval->GetParent()->GetSpillSlot()); break; + case 2: loc = Location::DoubleStackSlot(interval->GetParent()->GetSpillSlot()); break; + default: LOG(FATAL) << "Unexpected number of spill slots"; UNREACHABLE(); + } + InsertMoveAfter(interval->GetDefinedBy(), interval->ToLocation(), loc); } UsePosition* use = current->GetFirstUse(); EnvUsePosition* env_use = current->GetFirstEnvironmentUse(); @@ -459,9 +461,11 @@ void RegisterAllocationResolver::ConnectSplitSiblings(LiveInterval* interval, location_source = defined_by->GetLocations()->Out(); } else { DCHECK(defined_by->IsCurrentMethod()); - location_source = parent->NeedsTwoSpillSlots() - ? Location::DoubleStackSlot(parent->GetSpillSlot()) - : Location::StackSlot(parent->GetSpillSlot()); + switch (parent->NumberOfSpillSlotsNeeded()) { + case 1: location_source = Location::StackSlot(parent->GetSpillSlot()); break; + case 2: location_source = Location::DoubleStackSlot(parent->GetSpillSlot()); break; + default: LOG(FATAL) << "Unexpected number of spill slots"; UNREACHABLE(); + } } } else { DCHECK(source != nullptr); diff --git a/compiler/optimizing/register_allocator_graph_color.cc b/compiler/optimizing/register_allocator_graph_color.cc index 9064f865c3..87f709f63d 100644 --- a/compiler/optimizing/register_allocator_graph_color.cc +++ b/compiler/optimizing/register_allocator_graph_color.cc @@ -1029,7 +1029,7 @@ void RegisterAllocatorGraphColor::AllocateSpillSlotForCatchPhi(HInstruction* ins interval->SetSpillSlot(previous_phi->GetLiveInterval()->GetSpillSlot()); } else { interval->SetSpillSlot(catch_phi_spill_slot_counter_); - catch_phi_spill_slot_counter_ += interval->NeedsTwoSpillSlots() ? 2 : 1; + catch_phi_spill_slot_counter_ += interval->NumberOfSpillSlotsNeeded(); } } } @@ -1996,43 +1996,48 @@ void RegisterAllocatorGraphColor::ColorSpillSlots(ArenaVector<LiveInterval*>* in bool is_interval_beginning; size_t position; std::tie(position, is_interval_beginning, parent_interval) = *it; - - bool needs_two_slots = parent_interval->NeedsTwoSpillSlots(); + size_t number_of_spill_slots_needed = parent_interval->NumberOfSpillSlotsNeeded(); if (is_interval_beginning) { DCHECK(!parent_interval->HasSpillSlot()); DCHECK_EQ(position, parent_interval->GetStart()); - // Find a free stack slot. + // Find first available free stack slot(s). size_t slot = 0; - for (; taken.IsBitSet(slot) || (needs_two_slots && taken.IsBitSet(slot + 1)); ++slot) { - // Skip taken slots. + for (; ; ++slot) { + bool found = true; + for (size_t s = slot, u = slot + number_of_spill_slots_needed; s < u; s++) { + if (taken.IsBitSet(s)) { + found = false; + break; // failure + } + } + if (found) { + break; // success + } } + parent_interval->SetSpillSlot(slot); - *num_stack_slots_used = std::max(*num_stack_slots_used, - needs_two_slots ? slot + 1 : slot + 2); - if (needs_two_slots && *num_stack_slots_used % 2 != 0) { + *num_stack_slots_used = std::max(*num_stack_slots_used, slot + number_of_spill_slots_needed); + if (number_of_spill_slots_needed > 1 && *num_stack_slots_used % 2 != 0) { // The parallel move resolver requires that there be an even number of spill slots // allocated for pair value types. ++(*num_stack_slots_used); } - taken.SetBit(slot); - if (needs_two_slots) { - taken.SetBit(slot + 1); + for (size_t s = slot, u = slot + number_of_spill_slots_needed; s < u; s++) { + taken.SetBit(s); } } else { DCHECK_EQ(position, parent_interval->GetLastSibling()->GetEnd()); DCHECK(parent_interval->HasSpillSlot()); - // Free up the stack slot used by this interval. + // Free up the stack slot(s) used by this interval. size_t slot = parent_interval->GetSpillSlot(); - DCHECK(taken.IsBitSet(slot)); - DCHECK(!needs_two_slots || taken.IsBitSet(slot + 1)); - taken.ClearBit(slot); - if (needs_two_slots) { - taken.ClearBit(slot + 1); + for (size_t s = slot, u = slot + number_of_spill_slots_needed; s < u; s++) { + DCHECK(taken.IsBitSet(s)); + taken.ClearBit(s); } } } diff --git a/compiler/optimizing/register_allocator_linear_scan.cc b/compiler/optimizing/register_allocator_linear_scan.cc index 6354e76ec8..ab8d540359 100644 --- a/compiler/optimizing/register_allocator_linear_scan.cc +++ b/compiler/optimizing/register_allocator_linear_scan.cc @@ -1125,36 +1125,31 @@ void RegisterAllocatorLinearScan::AllocateSpillSlotFor(LiveInterval* interval) { LOG(FATAL) << "Unexpected type for interval " << interval->GetType(); } - // Find an available spill slot. + // Find first available spill slots. + size_t number_of_spill_slots_needed = parent->NumberOfSpillSlotsNeeded(); size_t slot = 0; for (size_t e = spill_slots->size(); slot < e; ++slot) { - if ((*spill_slots)[slot] <= parent->GetStart()) { - if (!parent->NeedsTwoSpillSlots()) { - // One spill slot is sufficient. - break; - } - if (slot == e - 1 || (*spill_slots)[slot + 1] <= parent->GetStart()) { - // Two spill slots are available. + bool found = true; + for (size_t s = slot, u = std::min(slot + number_of_spill_slots_needed, e); s < u; s++) { + if ((*spill_slots)[s] > parent->GetStart()) { + found = false; // failure break; } } + if (found) { + break; // success + } } + // Need new spill slots? + size_t upper = slot + number_of_spill_slots_needed; + if (upper > spill_slots->size()) { + spill_slots->resize(upper); + } + // Set slots to end. size_t end = interval->GetLastSibling()->GetEnd(); - if (parent->NeedsTwoSpillSlots()) { - if (slot + 2u > spill_slots->size()) { - // We need a new spill slot. - spill_slots->resize(slot + 2u, end); - } - (*spill_slots)[slot] = end; - (*spill_slots)[slot + 1] = end; - } else { - if (slot == spill_slots->size()) { - // We need a new spill slot. - spill_slots->push_back(end); - } else { - (*spill_slots)[slot] = end; - } + for (size_t s = slot; s < upper; s++) { + (*spill_slots)[s] = end; } // Note that the exact spill slot location will be computed when we resolve, @@ -1180,7 +1175,7 @@ void RegisterAllocatorLinearScan::AllocateSpillSlotForCatchPhi(HPhi* phi) { // TODO: Reuse spill slots when intervals of phis from different catch // blocks do not overlap. interval->SetSpillSlot(catch_phi_spill_slots_); - catch_phi_spill_slots_ += interval->NeedsTwoSpillSlots() ? 2 : 1; + catch_phi_spill_slots_ += interval->NumberOfSpillSlotsNeeded(); } } diff --git a/compiler/optimizing/ssa_liveness_analysis.cc b/compiler/optimizing/ssa_liveness_analysis.cc index e8e12e1a55..c0a045c33e 100644 --- a/compiler/optimizing/ssa_liveness_analysis.cc +++ b/compiler/optimizing/ssa_liveness_analysis.cc @@ -469,8 +469,8 @@ bool LiveInterval::SameRegisterKind(Location other) const { } } -bool LiveInterval::NeedsTwoSpillSlots() const { - return type_ == Primitive::kPrimLong || type_ == Primitive::kPrimDouble; +size_t LiveInterval::NumberOfSpillSlotsNeeded() const { + return (type_ == Primitive::kPrimLong || type_ == Primitive::kPrimDouble) ? 2 : 1; } Location LiveInterval::ToLocation() const { @@ -494,10 +494,10 @@ Location LiveInterval::ToLocation() const { if (defined_by->IsConstant()) { return defined_by->GetLocations()->Out(); } else if (GetParent()->HasSpillSlot()) { - if (NeedsTwoSpillSlots()) { - return Location::DoubleStackSlot(GetParent()->GetSpillSlot()); - } else { - return Location::StackSlot(GetParent()->GetSpillSlot()); + switch (NumberOfSpillSlotsNeeded()) { + case 1: return Location::StackSlot(GetParent()->GetSpillSlot()); + case 2: return Location::DoubleStackSlot(GetParent()->GetSpillSlot()); + default: LOG(FATAL) << "Unexpected number of spill slots"; UNREACHABLE(); } } else { return Location(); diff --git a/compiler/optimizing/ssa_liveness_analysis.h b/compiler/optimizing/ssa_liveness_analysis.h index 340d0ccefe..e9dffc1fac 100644 --- a/compiler/optimizing/ssa_liveness_analysis.h +++ b/compiler/optimizing/ssa_liveness_analysis.h @@ -762,9 +762,9 @@ class LiveInterval : public ArenaObject<kArenaAllocSsaLiveness> { // Returns kNoRegister otherwise. int FindHintAtDefinition() const; - // Returns whether the interval needs two (Dex virtual register size `kVRegSize`) - // slots for spilling. - bool NeedsTwoSpillSlots() const; + // Returns the number of required spilling slots (measured as a multiple of the + // Dex virtual register size `kVRegSize`). + size_t NumberOfSpillSlotsNeeded() const; bool IsFloatingPoint() const { return type_ == Primitive::kPrimFloat || type_ == Primitive::kPrimDouble; diff --git a/dex2oat/Android.bp b/dex2oat/Android.bp index 0924aec7f1..048f36d76c 100644 --- a/dex2oat/Android.bp +++ b/dex2oat/Android.bp @@ -14,6 +14,12 @@ // limitations under the License. // +cc_library_headers { + name: "dex2oat_headers", + host_supported: true, + export_include_dirs: ["include"], +} + cc_defaults { name: "dex2oat-defaults", host_supported: true, @@ -40,6 +46,7 @@ cc_defaults { include_dirs: [ "art/cmdline", ], + header_libs: ["dex2oat_headers"], } art_cc_binary { @@ -132,4 +139,5 @@ art_cc_test { "art_gtest_defaults", ], srcs: ["dex2oat_test.cc"], + header_libs: ["dex2oat_headers"], } diff --git a/dex2oat/dex2oat.cc b/dex2oat/dex2oat.cc index 92a12c8d07..e80be8172a 100644 --- a/dex2oat/dex2oat.cc +++ b/dex2oat/dex2oat.cc @@ -54,6 +54,7 @@ #include "debug/method_debug_info.h" #include "dex/quick_compiler_callbacks.h" #include "dex/verification_results.h" +#include "dex2oat_return_codes.h" #include "dex_file-inl.h" #include "driver/compiler_driver.h" #include "driver/compiler_options.h" @@ -1442,11 +1443,11 @@ class Dex2Oat FINAL { // Set up the environment for compilation. Includes starting the runtime and loading/opening the // boot class path. - bool Setup() { + dex2oat::ReturnCode Setup() { TimingLogger::ScopedTiming t("dex2oat Setup", timings_); if (!PrepareImageClasses() || !PrepareCompiledClasses() || !PrepareCompiledMethods()) { - return false; + return dex2oat::ReturnCode::kOther; } verification_results_.reset(new VerificationResults(compiler_options_.get())); @@ -1458,12 +1459,12 @@ class Dex2Oat FINAL { RuntimeArgumentMap runtime_options; if (!PrepareRuntimeOptions(&runtime_options)) { - return false; + return dex2oat::ReturnCode::kOther; } CreateOatWriters(); if (!AddDexFileSources()) { - return false; + return dex2oat::ReturnCode::kOther; } if (IsBootImage() && image_filenames_.size() > 1) { @@ -1479,7 +1480,7 @@ class Dex2Oat FINAL { // When compiling an app, create the runtime early to retrieve // the image location key needed for the oat header. if (!CreateRuntime(std::move(runtime_options))) { - return false; + return dex2oat::ReturnCode::kCreateRuntime; } if (CompilerFilter::DependsOnImageChecksum(compiler_options_->GetCompilerFilter())) { @@ -1550,7 +1551,7 @@ class Dex2Oat FINAL { update_input_vdex_, &opened_dex_files_map, &opened_dex_files)) { - return false; + return dex2oat::ReturnCode::kOther; } dex_files_per_oat_file_.push_back(MakeNonOwningPointerVector(opened_dex_files)); if (opened_dex_files_map != nullptr) { @@ -1602,7 +1603,7 @@ class Dex2Oat FINAL { // Note: Runtime acquires ownership of these dex files. runtime_options.Set(RuntimeArgumentMap::BootClassPathDexList, &opened_dex_files_); if (!CreateRuntime(std::move(runtime_options))) { - return false; + return dex2oat::ReturnCode::kOther; } } @@ -1636,7 +1637,7 @@ class Dex2Oat FINAL { for (const std::unique_ptr<MemMap>& map : opened_dex_files_maps_) { if (!map->Protect(PROT_READ | PROT_WRITE)) { PLOG(ERROR) << "Failed to make .dex files writeable."; - return false; + return dex2oat::ReturnCode::kOther; } } @@ -1651,14 +1652,14 @@ class Dex2Oat FINAL { soa.Self()->AssertPendingException(); soa.Self()->ClearException(); PLOG(ERROR) << "Failed to register dex file."; - return false; + return dex2oat::ReturnCode::kOther; } // Pre-register dex files so that we can access verification results without locks during // compilation and verification. verification_results_->AddDexFile(dex_file); } - return true; + return dex2oat::ReturnCode::kNoFailure; } // If we need to keep the oat file open for the image writer. @@ -2789,13 +2790,13 @@ static void b13564922() { #endif } -static int CompileImage(Dex2Oat& dex2oat) { +static dex2oat::ReturnCode CompileImage(Dex2Oat& dex2oat) { dex2oat.LoadClassProfileDescriptors(); dex2oat.Compile(); if (!dex2oat.WriteOutputFiles()) { dex2oat.EraseOutputFiles(); - return EXIT_FAILURE; + return dex2oat::ReturnCode::kOther; } // Flush boot.oat. We always expect the output file by name, and it will be re-opened from the @@ -2804,46 +2805,46 @@ static int CompileImage(Dex2Oat& dex2oat) { if (dex2oat.ShouldKeepOatFileOpen()) { if (!dex2oat.FlushOutputFiles()) { dex2oat.EraseOutputFiles(); - return EXIT_FAILURE; + return dex2oat::ReturnCode::kOther; } } else if (!dex2oat.FlushCloseOutputFiles()) { - return EXIT_FAILURE; + return dex2oat::ReturnCode::kOther; } // Creates the boot.art and patches the oat files. if (!dex2oat.HandleImage()) { - return EXIT_FAILURE; + return dex2oat::ReturnCode::kOther; } // When given --host, finish early without stripping. if (dex2oat.IsHost()) { if (!dex2oat.FlushCloseOutputFiles()) { - return EXIT_FAILURE; + return dex2oat::ReturnCode::kOther; } dex2oat.DumpTiming(); - return EXIT_SUCCESS; + return dex2oat::ReturnCode::kNoFailure; } // Copy stripped to unstripped location, if necessary. if (!dex2oat.CopyStrippedToUnstripped()) { - return EXIT_FAILURE; + return dex2oat::ReturnCode::kOther; } // FlushClose again, as stripping might have re-opened the oat files. if (!dex2oat.FlushCloseOutputFiles()) { - return EXIT_FAILURE; + return dex2oat::ReturnCode::kOther; } dex2oat.DumpTiming(); - return EXIT_SUCCESS; + return dex2oat::ReturnCode::kNoFailure; } -static int CompileApp(Dex2Oat& dex2oat) { +static dex2oat::ReturnCode CompileApp(Dex2Oat& dex2oat) { dex2oat.Compile(); if (!dex2oat.WriteOutputFiles()) { dex2oat.EraseOutputFiles(); - return EXIT_FAILURE; + return dex2oat::ReturnCode::kOther; } // Do not close the oat files here. We might have gotten the output file by file descriptor, @@ -2852,29 +2853,29 @@ static int CompileApp(Dex2Oat& dex2oat) { // When given --host, finish early without stripping. if (dex2oat.IsHost()) { if (!dex2oat.FlushCloseOutputFiles()) { - return EXIT_FAILURE; + return dex2oat::ReturnCode::kOther; } dex2oat.DumpTiming(); - return EXIT_SUCCESS; + return dex2oat::ReturnCode::kNoFailure; } // Copy stripped to unstripped location, if necessary. This will implicitly flush & close the // stripped versions. If this is given, we expect to be able to open writable files by name. if (!dex2oat.CopyStrippedToUnstripped()) { - return EXIT_FAILURE; + return dex2oat::ReturnCode::kOther; } // Flush and close the files. if (!dex2oat.FlushCloseOutputFiles()) { - return EXIT_FAILURE; + return dex2oat::ReturnCode::kOther; } dex2oat.DumpTiming(); - return EXIT_SUCCESS; + return dex2oat::ReturnCode::kNoFailure; } -static int dex2oat(int argc, char** argv) { +static dex2oat::ReturnCode Dex2oat(int argc, char** argv) { b13564922(); TimingLogger timings("compiler", false, false); @@ -2893,14 +2894,14 @@ static int dex2oat(int argc, char** argv) { if (dex2oat->UseProfile()) { if (!dex2oat->LoadProfile()) { LOG(ERROR) << "Failed to process profile file"; - return EXIT_FAILURE; + return dex2oat::ReturnCode::kOther; } } if (dex2oat->DoDexLayoutOptimizations()) { if (dex2oat->HasInputVdexFile()) { LOG(ERROR) << "Dexlayout is incompatible with an input VDEX"; - return EXIT_FAILURE; + return dex2oat::ReturnCode::kOther; } } @@ -2908,7 +2909,7 @@ static int dex2oat(int argc, char** argv) { // Check early that the result of compilation can be written if (!dex2oat->OpenFile()) { - return EXIT_FAILURE; + return dex2oat::ReturnCode::kOther; } // Print the complete line when any of the following is true: @@ -2923,16 +2924,17 @@ static int dex2oat(int argc, char** argv) { LOG(INFO) << StrippedCommandLine(); } - if (!dex2oat->Setup()) { + dex2oat::ReturnCode setup_code = dex2oat->Setup(); + if (setup_code != dex2oat::ReturnCode::kNoFailure) { dex2oat->EraseOutputFiles(); - return EXIT_FAILURE; + return setup_code; } // Helps debugging on device. Can be used to determine which dalvikvm instance invoked a dex2oat // instance. Used by tools/bisection_search/bisection_search.py. VLOG(compiler) << "Running dex2oat (parent PID = " << getppid() << ")"; - bool result; + dex2oat::ReturnCode result; if (dex2oat->IsImage()) { result = CompileImage(*dex2oat); } else { @@ -2945,7 +2947,7 @@ static int dex2oat(int argc, char** argv) { } // namespace art int main(int argc, char** argv) { - int result = art::dex2oat(argc, argv); + int result = static_cast<int>(art::Dex2oat(argc, argv)); // Everything was done, do an explicit exit here to avoid running Runtime destructors that take // time (bug 10645725) unless we're a debug build or running on valgrind. Note: The Dex2Oat class // should not destruct the runtime in this case. diff --git a/dex2oat/dex2oat_test.cc b/dex2oat/dex2oat_test.cc index 289b8ab50a..8c14b50094 100644 --- a/dex2oat/dex2oat_test.cc +++ b/dex2oat/dex2oat_test.cc @@ -30,6 +30,7 @@ #include "base/macros.h" #include "dex_file-inl.h" #include "dex2oat_environment_test.h" +#include "dex2oat_return_codes.h" #include "jit/profile_compilation_info.h" #include "oat.h" #include "oat_file.h" @@ -50,12 +51,12 @@ class Dex2oatTest : public Dex2oatEnvironmentTest { } protected: - void GenerateOdexForTest(const std::string& dex_location, - const std::string& odex_location, - CompilerFilter::Filter filter, - const std::vector<std::string>& extra_args = {}, - bool expect_success = true, - bool use_fd = false) { + int GenerateOdexForTestWithStatus(const std::string& dex_location, + const std::string& odex_location, + CompilerFilter::Filter filter, + std::string* error_msg, + const std::vector<std::string>& extra_args = {}, + bool use_fd = false) { std::unique_ptr<File> oat_file; std::vector<std::string> args; args.push_back("--dex-file=" + dex_location); @@ -73,12 +74,27 @@ class Dex2oatTest : public Dex2oatEnvironmentTest { args.insert(args.end(), extra_args.begin(), extra_args.end()); - std::string error_msg; - bool success = Dex2Oat(args, &error_msg); + int status = Dex2Oat(args, error_msg); if (oat_file != nullptr) { - ASSERT_EQ(oat_file->FlushClose(), 0) << "Could not flush and close oat file"; + CHECK_EQ(oat_file->FlushClose(), 0) << "Could not flush and close oat file"; } + return status; + } + void GenerateOdexForTest(const std::string& dex_location, + const std::string& odex_location, + CompilerFilter::Filter filter, + const std::vector<std::string>& extra_args = {}, + bool expect_success = true, + bool use_fd = false) { + std::string error_msg; + int status = GenerateOdexForTestWithStatus(dex_location, + odex_location, + filter, + &error_msg, + extra_args, + use_fd); + bool success = (status == 0); if (expect_success) { ASSERT_TRUE(success) << error_msg << std::endl << output_; @@ -118,7 +134,7 @@ class Dex2oatTest : public Dex2oatEnvironmentTest { EXPECT_EQ(expected, actual); } - bool Dex2Oat(const std::vector<std::string>& dex2oat_args, std::string* error_msg) { + int Dex2Oat(const std::vector<std::string>& dex2oat_args, std::string* error_msg) { Runtime* runtime = Runtime::Current(); const std::vector<gc::space::ImageSpace*>& image_spaces = @@ -196,6 +212,7 @@ class Dex2oatTest : public Dex2oatEnvironmentTest { c_args.push_back(nullptr); execv(c_args[0], const_cast<char* const*>(c_args.data())); exit(1); + UNREACHABLE(); } else { close(link[1]); char buffer[128]; @@ -206,12 +223,12 @@ class Dex2oatTest : public Dex2oatEnvironmentTest { output_ += std::string(buffer, bytes_read); } close(link[0]); - int status = 0; + int status = -1; if (waitpid(pid, &status, 0) != -1) { success_ = (status == 0); } + return status; } - return success_; } std::string output_ = ""; @@ -845,4 +862,30 @@ TEST_F(Dex2oatWatchdogTest, TestWatchdogTrigger) { RunTest(false, { "--watchdog-timeout=10" }); } +class Dex2oatReturnCodeTest : public Dex2oatTest { + protected: + int RunTest(const std::vector<std::string>& extra_args = {}) { + std::string dex_location = GetScratchDir() + "/Dex2OatSwapTest.jar"; + std::string odex_location = GetOdexDir() + "/Dex2OatSwapTest.odex"; + + Copy(GetTestDexFileName(), dex_location); + + std::string error_msg; + return GenerateOdexForTestWithStatus(dex_location, + odex_location, + CompilerFilter::kSpeed, + &error_msg, + extra_args); + } + + std::string GetTestDexFileName() { + return GetDexSrc1(); + } +}; + +TEST_F(Dex2oatReturnCodeTest, TestCreateRuntime) { + int status = RunTest({ "--boot-image=/this/does/not/exist/yolo.oat" }); + EXPECT_EQ(static_cast<int>(dex2oat::ReturnCode::kCreateRuntime), WEXITSTATUS(status)) << output_; +} + } // namespace art diff --git a/dex2oat/include/dex2oat_return_codes.h b/dex2oat/include/dex2oat_return_codes.h new file mode 100644 index 0000000000..cc5400fc27 --- /dev/null +++ b/dex2oat/include/dex2oat_return_codes.h @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef ART_DEX2OAT_INCLUDE_DEX2OAT_RETURN_CODES_H_ +#define ART_DEX2OAT_INCLUDE_DEX2OAT_RETURN_CODES_H_ + +namespace art { +namespace dex2oat { + +enum class ReturnCode : int { + kNoFailure = 0, + kOther = 1, + kCreateRuntime = 2, +}; + +} // namespace dex2oat +} // namespace art + +#endif // ART_DEX2OAT_INCLUDE_DEX2OAT_RETURN_CODES_H_ diff --git a/disassembler/disassembler_x86.cc b/disassembler/disassembler_x86.cc index a289433af5..77ed3c6a22 100644 --- a/disassembler/disassembler_x86.cc +++ b/disassembler/disassembler_x86.cc @@ -832,6 +832,24 @@ DISASSEMBLER_ENTRY(cmp, store = true; immediate_bytes = 1; break; + case 0x74: + case 0x75: + case 0x76: + if (prefix[2] == 0x66) { + src_reg_file = dst_reg_file = SSE; + prefix[2] = 0; // clear prefix now it's served its purpose as part of the opcode + } else { + src_reg_file = dst_reg_file = MMX; + } + switch (*instr) { + case 0x74: opcode1 = "pcmpeqb"; break; + case 0x75: opcode1 = "pcmpeqw"; break; + case 0x76: opcode1 = "pcmpeqd"; break; + } + prefix[2] = 0; + has_modrm = true; + load = true; + break; case 0x7C: if (prefix[0] == 0xF2) { opcode1 = "haddps"; diff --git a/profman/profile_assistant_test.cc b/profman/profile_assistant_test.cc index 52f3b52ee2..1a8a614a4a 100644 --- a/profman/profile_assistant_test.cc +++ b/profman/profile_assistant_test.cc @@ -22,6 +22,7 @@ #include "exec_utils.h" #include "jit/profile_compilation_info.h" #include "mirror/class-inl.h" +#include "obj_ptr-inl.h" #include "profile_assistant.h" #include "scoped_thread_state_change-inl.h" #include "utils.h" @@ -140,7 +141,8 @@ class ProfileAssistantTest : public CommonRuntimeTest { return true; } - bool CreateAndDump(const std::string& input_file_contents, std::string* output_file_contents) { + bool CreateAndDump(const std::string& input_file_contents, + std::string* output_file_contents) { ScratchFile profile_file; EXPECT_TRUE(CreateProfile(input_file_contents, profile_file.GetFilename(), @@ -156,7 +158,7 @@ class ProfileAssistantTest : public CommonRuntimeTest { ScopedObjectAccess soa(self); StackHandleScope<1> hs(self); Handle<mirror::ClassLoader> h_loader( - hs.NewHandle(self->DecodeJObject(class_loader)->AsClassLoader())); + hs.NewHandle(ObjPtr<mirror::ClassLoader>::DownCast(self->DecodeJObject(class_loader)))); return class_linker->FindClass(self, clazz.c_str(), h_loader); } @@ -442,6 +444,44 @@ TEST_F(ProfileAssistantTest, TestProfileCreationAllMatch) { ASSERT_EQ(output_file_contents, expected_contents); } +TEST_F(ProfileAssistantTest, TestProfileCreationGenerateMethods) { + // Class names put here need to be in sorted order. + std::vector<std::string> class_names = { + "Ljava/lang/Math;->*", + }; + std::string input_file_contents; + std::string expected_contents; + for (std::string& class_name : class_names) { + input_file_contents += class_name + std::string("\n"); + expected_contents += DescriptorToDot(class_name.c_str()) + + std::string("\n"); + } + std::string output_file_contents; + ScratchFile profile_file; + EXPECT_TRUE(CreateProfile(input_file_contents, + profile_file.GetFilename(), + GetLibCoreDexFileNames()[0])); + ProfileCompilationInfo info; + profile_file.GetFile()->ResetOffset(); + ASSERT_TRUE(info.Load(GetFd(profile_file))); + // Verify that the profile has matching methods. + ScopedObjectAccess soa(Thread::Current()); + ObjPtr<mirror::Class> klass = GetClass(nullptr, "Ljava/lang/Math;"); + ASSERT_TRUE(klass != nullptr); + size_t method_count = 0; + for (ArtMethod& method : klass->GetMethods(kRuntimePointerSize)) { + if (!method.IsCopied() && method.GetCodeItem() != nullptr) { + ++method_count; + ProfileCompilationInfo::OfflineProfileMethodInfo pmi; + ASSERT_TRUE(info.GetMethod(method.GetDexFile()->GetLocation(), + method.GetDexFile()->GetLocationChecksum(), + method.GetDexMethodIndex(), + &pmi)); + } + } + EXPECT_GT(method_count, 0u); +} + TEST_F(ProfileAssistantTest, TestProfileCreationOneNotMatched) { // Class names put here need to be in sorted order. std::vector<std::string> class_names = { diff --git a/profman/profman.cc b/profman/profman.cc index f7316cc129..fdb9a75a6f 100644 --- a/profman/profman.cc +++ b/profman/profman.cc @@ -120,7 +120,6 @@ NO_RETURN static void Usage(const char *fmt, ...) { UsageError(""); UsageError(" --create-profile-from=<filename>: creates a profile from a list of classes."); UsageError(""); - UsageError(""); UsageError(" --dex-location=<string>: location string to use with corresponding"); UsageError(" apk-fd to find dex files"); UsageError(""); @@ -140,6 +139,7 @@ static constexpr uint16_t kDefaultTestProfileClassRatio = 5; // Separators used when parsing human friendly representation of profiles. static const std::string kMethodSep = "->"; static const std::string kMissingTypesMarker = "missing_types"; +static const std::string kClassAllMethods = "*"; static constexpr char kProfileParsingInlineChacheSep = '+'; static constexpr char kProfileParsingTypeSep = ','; static constexpr char kProfileParsingFirstCharInSignature = '('; @@ -630,6 +630,7 @@ class ProfMan FINAL { // "LTestInline;->inlinePolymorphic(LSuper;)I+LSubA;,LSubB;,LSubC;". // "LTestInline;->inlineMissingTypes(LSuper;)I+missing_types". // "LTestInline;->inlineNoInlineCaches(LSuper;)I". + // "LTestInline;->*". // The method and classes are searched only in the given dex files. bool ProcessLine(const std::vector<std::unique_ptr<const DexFile>>& dex_files, const std::string& line, @@ -650,8 +651,8 @@ class ProfMan FINAL { return false; } - if (method_str.empty()) { - // No method to add. Just add the class. + if (method_str.empty() || method_str == kClassAllMethods) { + // Start by adding the class. std::set<DexCacheResolvedClasses> resolved_class_set; const DexFile* dex_file = class_ref.dex_file; const auto& dex_resolved_classes = resolved_class_set.emplace( @@ -659,7 +660,27 @@ class ProfMan FINAL { dex_file->GetBaseLocation(), dex_file->GetLocationChecksum()); dex_resolved_classes.first->AddClass(class_ref.type_index); - profile->AddMethodsAndClasses(std::vector<ProfileMethodInfo>(), resolved_class_set); + std::vector<ProfileMethodInfo> methods; + if (method_str == kClassAllMethods) { + // Add all of the methods. + const DexFile::ClassDef* class_def = dex_file->FindClassDef(class_ref.type_index); + const uint8_t* class_data = dex_file->GetClassData(*class_def); + if (class_data != nullptr) { + ClassDataItemIterator it(*dex_file, class_data); + while (it.HasNextStaticField() || it.HasNextInstanceField()) { + it.Next(); + } + while (it.HasNextDirectMethod() || it.HasNextVirtualMethod()) { + if (it.GetMethodCodeItemOffset() != 0) { + // Add all of the methods that have code to the profile. + const uint32_t method_idx = it.GetMemberIndex(); + methods.push_back(ProfileMethodInfo(dex_file, method_idx)); + } + it.Next(); + } + } + } + profile->AddMethodsAndClasses(methods, resolved_class_set); return true; } diff --git a/runtime/arch/mips64/instruction_set_features_mips64_test.cc b/runtime/arch/mips64/instruction_set_features_mips64_test.cc index 563200ff76..0ba0bd4c15 100644 --- a/runtime/arch/mips64/instruction_set_features_mips64_test.cc +++ b/runtime/arch/mips64/instruction_set_features_mips64_test.cc @@ -20,7 +20,7 @@ namespace art { -TEST(Mips64InstructionSetFeaturesTest, Mips64Features) { +TEST(Mips64InstructionSetFeaturesTest, Mips64FeaturesFromDefaultVariant) { std::string error_msg; std::unique_ptr<const InstructionSetFeatures> mips64_features( InstructionSetFeatures::FromVariant(kMips64, "default", &error_msg)); @@ -31,4 +31,20 @@ TEST(Mips64InstructionSetFeaturesTest, Mips64Features) { EXPECT_EQ(mips64_features->AsBitmap(), 1U); } +TEST(Mips64InstructionSetFeaturesTest, Mips64FeaturesFromR6Variant) { + std::string error_msg; + std::unique_ptr<const InstructionSetFeatures> mips64r6_features( + InstructionSetFeatures::FromVariant(kMips64, "mips64r6", &error_msg)); + ASSERT_TRUE(mips64r6_features.get() != nullptr) << error_msg; + EXPECT_EQ(mips64r6_features->GetInstructionSet(), kMips64); + EXPECT_TRUE(mips64r6_features->Equals(mips64r6_features.get())); + EXPECT_STREQ("msa", mips64r6_features->GetFeatureString().c_str()); + EXPECT_EQ(mips64r6_features->AsBitmap(), 1U); + + std::unique_ptr<const InstructionSetFeatures> mips64_default_features( + InstructionSetFeatures::FromVariant(kMips64, "default", &error_msg)); + ASSERT_TRUE(mips64_default_features.get() != nullptr) << error_msg; + EXPECT_TRUE(mips64r6_features->Equals(mips64_default_features.get())); +} + } // namespace art diff --git a/runtime/art_method.h b/runtime/art_method.h index 2248c3bd9d..8f09cc6d03 100644 --- a/runtime/art_method.h +++ b/runtime/art_method.h @@ -691,7 +691,7 @@ class ArtMethod FINAL { // Pointer to JNI function registered to this method, or a function to resolve the JNI function, // or the profiling data for non-native methods, or an ImtConflictTable, or the - // single-implementation of an abstract method. + // single-implementation of an abstract/interface method. void* data_; // Method dispatch from quick compiled code invokes this pointer which may cause bridging into diff --git a/runtime/base/scoped_flock.cc b/runtime/base/scoped_flock.cc index d4bb56b62a..5394e53fa3 100644 --- a/runtime/base/scoped_flock.cc +++ b/runtime/base/scoped_flock.cc @@ -116,7 +116,10 @@ ScopedFlock::ScopedFlock() { } ScopedFlock::~ScopedFlock() { if (file_.get() != nullptr) { int flock_result = TEMP_FAILURE_RETRY(flock(file_->Fd(), LOCK_UN)); - CHECK_EQ(0, flock_result); + if (flock_result != 0) { + PLOG(FATAL) << "Unable to unlock file " << file_->GetPath(); + UNREACHABLE(); + } int close_result = -1; if (file_->ReadOnlyMode()) { close_result = file_->Close(); diff --git a/runtime/cha.cc b/runtime/cha.cc index eaba01b2ce..7948c29e5d 100644 --- a/runtime/cha.cc +++ b/runtime/cha.cc @@ -210,7 +210,7 @@ void ClassHierarchyAnalysis::VerifyNonSingleImplementation(mirror::Class* verify } } -void ClassHierarchyAnalysis::CheckSingleImplementationInfo( +void ClassHierarchyAnalysis::CheckVirtualMethodSingleImplementationInfo( Handle<mirror::Class> klass, ArtMethod* virtual_method, ArtMethod* method_in_super, @@ -290,8 +290,9 @@ void ClassHierarchyAnalysis::CheckSingleImplementationInfo( // A non-abstract method overrides an abstract method. if (method_in_super->GetSingleImplementation(pointer_size) == nullptr) { // Abstract method_in_super has no implementation yet. - // We need to grab cha_lock_ for further checking/updating due to possible - // races. + // We need to grab cha_lock_ since there may be multiple class linking + // going on that can check/modify the single-implementation flag/method + // of method_in_super. MutexLock cha_mu(Thread::Current(), *Locks::cha_lock_); if (!method_in_super->HasSingleImplementation()) { return; @@ -362,6 +363,55 @@ void ClassHierarchyAnalysis::CheckSingleImplementationInfo( } } +void ClassHierarchyAnalysis::CheckInterfaceMethodSingleImplementationInfo( + Handle<mirror::Class> klass, + ArtMethod* interface_method, + ArtMethod* implementation_method, + std::unordered_set<ArtMethod*>& invalidated_single_impl_methods, + PointerSize pointer_size) { + DCHECK(klass->IsInstantiable()); + DCHECK(interface_method->IsAbstract() || interface_method->IsDefault()); + + if (!interface_method->HasSingleImplementation()) { + return; + } + + if (implementation_method->IsAbstract()) { + // An instantiable class doesn't supply an implementation for + // interface_method. Invoking the interface method on the class will throw + // AbstractMethodError. This is an uncommon case, so we simply treat + // interface_method as not having single-implementation. + invalidated_single_impl_methods.insert(interface_method); + return; + } + + // We need to grab cha_lock_ since there may be multiple class linking going + // on that can check/modify the single-implementation flag/method of + // interface_method. + MutexLock cha_mu(Thread::Current(), *Locks::cha_lock_); + // Do this check again after we grab cha_lock_. + if (!interface_method->HasSingleImplementation()) { + return; + } + + ArtMethod* single_impl = interface_method->GetSingleImplementation(pointer_size); + if (single_impl == nullptr) { + // implementation_method becomes the first implementation for + // interface_method. + interface_method->SetSingleImplementation(implementation_method, pointer_size); + // Keep interface_method's single-implementation status. + return; + } + DCHECK(!single_impl->IsAbstract()); + if (single_impl->GetDeclaringClass() == implementation_method->GetDeclaringClass()) { + // Same implementation. Since implementation_method may be a copy of a default + // method, we need to check the declaring class for equality. + return; + } + // Another implementation for interface_method. + invalidated_single_impl_methods.insert(interface_method); +} + void ClassHierarchyAnalysis::InitSingleImplementationFlag(Handle<mirror::Class> klass, ArtMethod* method, PointerSize pointer_size) { @@ -382,6 +432,7 @@ void ClassHierarchyAnalysis::InitSingleImplementationFlag(Handle<mirror::Class> // Rare case, but we do accept it (such as 800-smali/smali/b_26143249.smali). // Do not attempt to devirtualize it. method->SetHasSingleImplementation(false); + DCHECK(method->GetSingleImplementation(pointer_size) == nullptr); } else { // Abstract method starts with single-implementation flag set and null // implementation method. @@ -396,9 +447,15 @@ void ClassHierarchyAnalysis::InitSingleImplementationFlag(Handle<mirror::Class> } void ClassHierarchyAnalysis::UpdateAfterLoadingOf(Handle<mirror::Class> klass) { + PointerSize image_pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize(); if (klass->IsInterface()) { + for (ArtMethod& method : klass->GetDeclaredVirtualMethods(image_pointer_size)) { + DCHECK(method.IsAbstract() || method.IsDefault()); + InitSingleImplementationFlag(klass, &method, image_pointer_size); + } return; } + mirror::Class* super_class = klass->GetSuperClass(); if (super_class == nullptr) { return; @@ -408,7 +465,6 @@ void ClassHierarchyAnalysis::UpdateAfterLoadingOf(Handle<mirror::Class> klass) { // is invalidated by linking `klass`. std::unordered_set<ArtMethod*> invalidated_single_impl_methods; - PointerSize image_pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize(); // Do an entry-by-entry comparison of vtable contents with super's vtable. for (int32_t i = 0; i < super_class->GetVTableLength(); ++i) { ArtMethod* method = klass->GetVTableEntry(i, image_pointer_size); @@ -418,33 +474,59 @@ void ClassHierarchyAnalysis::UpdateAfterLoadingOf(Handle<mirror::Class> klass) { if (method->IsAbstract() && klass->IsInstantiable()) { // An instantiable class that inherits an abstract method is treated as // supplying an implementation that throws AbstractMethodError. - CheckSingleImplementationInfo(klass, - method, - method_in_super, - invalidated_single_impl_methods, - image_pointer_size); + CheckVirtualMethodSingleImplementationInfo(klass, + method, + method_in_super, + invalidated_single_impl_methods, + image_pointer_size); } continue; } InitSingleImplementationFlag(klass, method, image_pointer_size); - CheckSingleImplementationInfo(klass, - method, - method_in_super, - invalidated_single_impl_methods, - image_pointer_size); + CheckVirtualMethodSingleImplementationInfo(klass, + method, + method_in_super, + invalidated_single_impl_methods, + image_pointer_size); } - // For new virtual methods that don't override. for (int32_t i = super_class->GetVTableLength(); i < klass->GetVTableLength(); ++i) { ArtMethod* method = klass->GetVTableEntry(i, image_pointer_size); InitSingleImplementationFlag(klass, method, image_pointer_size); } - Runtime* const runtime = Runtime::Current(); + if (klass->IsInstantiable()) { + auto* iftable = klass->GetIfTable(); + const size_t ifcount = klass->GetIfTableCount(); + for (size_t i = 0; i < ifcount; ++i) { + mirror::Class* interface = iftable->GetInterface(i); + for (size_t j = 0, count = iftable->GetMethodArrayCount(i); j < count; ++j) { + ArtMethod* interface_method = interface->GetVirtualMethod(j, image_pointer_size); + mirror::PointerArray* method_array = iftable->GetMethodArray(i); + ArtMethod* implementation_method = + method_array->GetElementPtrSize<ArtMethod*>(j, image_pointer_size); + DCHECK(implementation_method != nullptr) << klass->PrettyClass(); + CheckInterfaceMethodSingleImplementationInfo(klass, + interface_method, + implementation_method, + invalidated_single_impl_methods, + image_pointer_size); + } + } + } + + InvalidateSingleImplementationMethods(invalidated_single_impl_methods); +} + +void ClassHierarchyAnalysis::InvalidateSingleImplementationMethods( + std::unordered_set<ArtMethod*>& invalidated_single_impl_methods) { if (!invalidated_single_impl_methods.empty()) { + Runtime* const runtime = Runtime::Current(); Thread *self = Thread::Current(); // Method headers for compiled code to be invalidated. std::unordered_set<OatQuickMethodHeader*> dependent_method_headers; + PointerSize image_pointer_size = + Runtime::Current()->GetClassLinker()->GetImagePointerSize(); { // We do this under cha_lock_. Committing code also grabs this lock to diff --git a/runtime/cha.h b/runtime/cha.h index a56a752d8c..99c49d2bca 100644 --- a/runtime/cha.h +++ b/runtime/cha.h @@ -117,11 +117,13 @@ class ClassHierarchyAnalysis { PointerSize pointer_size) REQUIRES_SHARED(Locks::mutator_lock_); + // Check/update single-implementation info when one virtual method + // overrides another. // `virtual_method` in `klass` overrides `method_in_super`. - // This will invalidate some assumptions on single-implementation. + // This may invalidate some assumptions on single-implementation. // Append methods that should have their single-implementation flag invalidated // to `invalidated_single_impl_methods`. - void CheckSingleImplementationInfo( + void CheckVirtualMethodSingleImplementationInfo( Handle<mirror::Class> klass, ArtMethod* virtual_method, ArtMethod* method_in_super, @@ -129,6 +131,23 @@ class ClassHierarchyAnalysis { PointerSize pointer_size) REQUIRES_SHARED(Locks::mutator_lock_); + // Check/update single-implementation info when one method + // implements an interface method. + // `implementation_method` in `klass` implements `interface_method`. + // Append `interface_method` to `invalidated_single_impl_methods` + // if `interface_method` gets a new implementation. + void CheckInterfaceMethodSingleImplementationInfo( + Handle<mirror::Class> klass, + ArtMethod* interface_method, + ArtMethod* implementation_method, + std::unordered_set<ArtMethod*>& invalidated_single_impl_methods, + PointerSize pointer_size) + REQUIRES_SHARED(Locks::mutator_lock_); + + void InvalidateSingleImplementationMethods( + std::unordered_set<ArtMethod*>& invalidated_single_impl_methods) + REQUIRES_SHARED(Locks::mutator_lock_); + // For all methods in vtable slot at `verify_index` of `verify_class` and its // superclasses, single-implementation status should be false, except if the // method is `excluded_method`. diff --git a/runtime/gc/collector/concurrent_copying.cc b/runtime/gc/collector/concurrent_copying.cc index 7136f101aa..d2ab41d409 100644 --- a/runtime/gc/collector/concurrent_copying.cc +++ b/runtime/gc/collector/concurrent_copying.cc @@ -2171,9 +2171,12 @@ mirror::Object* ConcurrentCopying::Copy(mirror::Object* from_ref) { fall_back_to_non_moving = true; to_ref = heap_->non_moving_space_->Alloc(Thread::Current(), obj_size, &non_moving_space_bytes_allocated, nullptr, &dummy); - CHECK(to_ref != nullptr) << "Fall-back non-moving space allocation failed for a " - << obj_size << " byte object in region type " - << region_space_->GetRegionType(from_ref); + if (UNLIKELY(to_ref == nullptr)) { + LOG(FATAL_WITHOUT_ABORT) << "Fall-back non-moving space allocation failed for a " + << obj_size << " byte object in region type " + << region_space_->GetRegionType(from_ref); + LOG(FATAL) << "Object address=" << from_ref << " type=" << from_ref->PrettyTypeOf(); + } bytes_allocated = non_moving_space_bytes_allocated; // Mark it in the mark bitmap. accounting::ContinuousSpaceBitmap* mark_bitmap = diff --git a/runtime/thread.cc b/runtime/thread.cc index 30a4046d73..008c388229 100644 --- a/runtime/thread.cc +++ b/runtime/thread.cc @@ -16,6 +16,10 @@ #include "thread.h" +#if !defined(__APPLE__) +#include <sched.h> +#endif + #include <pthread.h> #include <signal.h> #include <sys/resource.h> @@ -1591,8 +1595,21 @@ void Thread::DumpState(std::ostream& os, const Thread* thread, pid_t tid) { if (thread != nullptr) { int policy; sched_param sp; +#if !defined(__APPLE__) + // b/36445592 Don't use pthread_getschedparam since pthread may have exited. + policy = sched_getscheduler(tid); + if (policy == -1) { + PLOG(WARNING) << "sched_getscheduler(" << tid << ")"; + } + int sched_getparam_result = sched_getparam(tid, &sp); + if (sched_getparam_result == -1) { + PLOG(WARNING) << "sched_getparam(" << tid << ", &sp)"; + sp.sched_priority = -1; + } +#else CHECK_PTHREAD_CALL(pthread_getschedparam, (thread->tlsPtr_.pthread_self, &policy, &sp), __FUNCTION__); +#endif os << " sched=" << policy << "/" << sp.sched_priority << " handle=" << reinterpret_cast<void*>(thread->tlsPtr_.pthread_self); } diff --git a/test.py b/test.py new file mode 100755 index 0000000000..414d7790f8 --- /dev/null +++ b/test.py @@ -0,0 +1,74 @@ +#!/usr/bin/env python +# +# Copyright 2017, 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. + +# --run-test : To run run-test +# --gtest : To run gtest +# -j : Number of jobs +# --host: for host tests +# --target: for target tests +# All the other arguments will be passed to the run-test testrunner. +import sys +import subprocess +import os +import argparse + +ANDROID_BUILD_TOP = os.environ.get('ANDROID_BUILD_TOP', os.getcwd()) + +parser = argparse.ArgumentParser() +parser.add_argument('-j', default='', dest='n_threads') +parser.add_argument('--run-test', '-r', action='store_true', dest='run_test') +parser.add_argument('--gtest', '-g', action='store_true', dest='gtest') +parser.add_argument('--target', action='store_true', dest='target') +parser.add_argument('--host', action='store_true', dest='host') +options, unknown = parser.parse_known_args() + +if options.run_test or not options.gtest: + testrunner = os.path.join('./', + ANDROID_BUILD_TOP, + 'art/test/testrunner/testrunner.py') + run_test_args = [] + for arg in sys.argv[1:]: + if arg == '--run-test' or arg == '--gtest' \ + or arg == '-r' or arg == '-g': + continue + run_test_args.append(arg) + + test_runner_cmd = [testrunner] + run_test_args + print test_runner_cmd + if subprocess.call(test_runner_cmd): + sys.exit(1) + +if options.gtest or not options.run_test: + build_target = '' + if options.host or not options.target: + build_target += ' test-art-host-gtest' + if options.target or not options.host: + build_target += ' test-art-target-gtest' + + build_command = 'make' + build_command += ' -j' + str(options.n_threads) + + build_command += ' -C ' + ANDROID_BUILD_TOP + build_command += ' ' + build_target + # Add 'dist' to avoid Jack issues b/36169180. + build_command += ' dist' + + print build_command + + if subprocess.call(build_command.split()): + sys.exit(1) + +sys.exit(0) diff --git a/test/051-thread/expected.txt b/test/051-thread/expected.txt index c6cd4f8bea..3fc34929eb 100644 --- a/test/051-thread/expected.txt +++ b/test/051-thread/expected.txt @@ -1,6 +1,6 @@ JNI_OnLoad called thread test starting -testThreadCapacity thread count: 512 +testThreadCapacity thread count: 128 testThreadDaemons starting thread 'TestDaemonThread' testThreadDaemons @ Thread running testThreadDaemons @ Got expected setDaemon exception diff --git a/test/051-thread/src/Main.java b/test/051-thread/src/Main.java index 2e26b22265..82fc0d471b 100644 --- a/test/051-thread/src/Main.java +++ b/test/051-thread/src/Main.java @@ -35,8 +35,8 @@ public class Main { * Simple thread capacity test. */ private static void testThreadCapacity() throws Exception { - TestCapacityThread[] threads = new TestCapacityThread[512]; - for (int i = 0; i < 512; i++) { + TestCapacityThread[] threads = new TestCapacityThread[128]; + for (int i = 0; i < threads.length; i++) { threads[i] = new TestCapacityThread(); } diff --git a/test/080-oom-throw/run b/test/080-oom-throw/run new file mode 100644 index 0000000000..eb473782a5 --- /dev/null +++ b/test/080-oom-throw/run @@ -0,0 +1,17 @@ +#!/bin/bash +# +# Copyright (C) 2017 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. + +exec ${RUN} $@ --runtime-option -Xmx16m diff --git a/test/080-oom-throw/src/Main.java b/test/080-oom-throw/src/Main.java index a6c18b75fc..3d5d0629f3 100644 --- a/test/080-oom-throw/src/Main.java +++ b/test/080-oom-throw/src/Main.java @@ -114,13 +114,13 @@ public class Main { static Object[] holder; public static void blowup() throws Exception { - int size = 32 * 1024 * 1024; + int size = 2 * 1024 * 1024; for (int i = 0; i < holder.length; ) { try { holder[i] = new char[size]; i++; } catch (OutOfMemoryError oome) { - size = size / 2; + size = size / 16; if (size == 0) { break; } diff --git a/test/527-checker-array-access-split/src/Main.java b/test/527-checker-array-access-split/src/Main.java index 3de900a3a9..a5caa7bce0 100644 --- a/test/527-checker-array-access-split/src/Main.java +++ b/test/527-checker-array-access-split/src/Main.java @@ -327,17 +327,17 @@ public class Main { // check. /// CHECK-START-ARM64: int Main.canMergeAfterBCE1() instruction_simplifier_arm64 (before) - /// CHECK: <<Const1:i\d+>> IntConstant 1 + /// CHECK: <<Const7:i\d+>> IntConstant 7 /// CHECK: <<Array:l\d+>> NewArray /// CHECK: <<Index:i\d+>> Phi /// CHECK: If // -------------- Loop /// CHECK: <<ArrayGet:i\d+>> ArrayGet [<<Array>>,<<Index>>] - /// CHECK: <<Add:i\d+>> Add [<<ArrayGet>>,<<Const1>>] - /// CHECK: ArraySet [<<Array>>,<<Index>>,<<Add>>] + /// CHECK: <<Div:i\d+>> Div [<<ArrayGet>>,<<Const7>>] + /// CHECK: ArraySet [<<Array>>,<<Index>>,<<Div>>] /// CHECK-START-ARM64: int Main.canMergeAfterBCE1() instruction_simplifier_arm64 (after) - /// CHECK-DAG: <<Const1:i\d+>> IntConstant 1 + /// CHECK-DAG: <<Const7:i\d+>> IntConstant 7 /// CHECK-DAG: <<DataOffset:i\d+>> IntConstant 12 /// CHECK: <<Array:l\d+>> NewArray /// CHECK: <<Index:i\d+>> Phi @@ -345,12 +345,12 @@ public class Main { // -------------- Loop /// CHECK: <<Address1:i\d+>> IntermediateAddress [<<Array>>,<<DataOffset>>] /// CHECK-NEXT: <<ArrayGet:i\d+>> ArrayGet [<<Address1>>,<<Index>>] - /// CHECK: <<Add:i\d+>> Add [<<ArrayGet>>,<<Const1>>] + /// CHECK: <<Div:i\d+>> Div [<<ArrayGet>>,<<Const7>>] /// CHECK: <<Address2:i\d+>> IntermediateAddress [<<Array>>,<<DataOffset>>] - /// CHECK-NEXT: ArraySet [<<Address2>>,<<Index>>,<<Add>>] + /// CHECK-NEXT: ArraySet [<<Address2>>,<<Index>>,<<Div>>] /// CHECK-START-ARM64: int Main.canMergeAfterBCE1() GVN$after_arch (after) - /// CHECK-DAG: <<Const1:i\d+>> IntConstant 1 + /// CHECK-DAG: <<Const7:i\d+>> IntConstant 7 /// CHECK-DAG: <<DataOffset:i\d+>> IntConstant 12 /// CHECK: <<Array:l\d+>> NewArray /// CHECK: <<Index:i\d+>> Phi @@ -358,23 +358,23 @@ public class Main { // -------------- Loop /// CHECK: <<Address:i\d+>> IntermediateAddress [<<Array>>,<<DataOffset>>] /// CHECK: <<ArrayGet:i\d+>> ArrayGet [<<Address>>,<<Index>>] - /// CHECK: <<Add:i\d+>> Add [<<ArrayGet>>,<<Const1>>] + /// CHECK: <<Div:i\d+>> Div [<<ArrayGet>>,<<Const7>>] /// CHECK-NOT: IntermediateAddress - /// CHECK: ArraySet [<<Address>>,<<Index>>,<<Add>>] + /// CHECK: ArraySet [<<Address>>,<<Index>>,<<Div>>] /// CHECK-START-ARM: int Main.canMergeAfterBCE1() instruction_simplifier_arm (before) - /// CHECK: <<Const1:i\d+>> IntConstant 1 + /// CHECK: <<Const7:i\d+>> IntConstant 7 /// CHECK: <<Array:l\d+>> NewArray /// CHECK: <<Index:i\d+>> Phi /// CHECK: If // -------------- Loop /// CHECK: <<ArrayGet:i\d+>> ArrayGet [<<Array>>,<<Index>>] - /// CHECK: <<Add:i\d+>> Add [<<ArrayGet>>,<<Const1>>] - /// CHECK: ArraySet [<<Array>>,<<Index>>,<<Add>>] + /// CHECK: <<Div:i\d+>> Div [<<ArrayGet>>,<<Const7>>] + /// CHECK: ArraySet [<<Array>>,<<Index>>,<<Div>>] /// CHECK-START-ARM: int Main.canMergeAfterBCE1() instruction_simplifier_arm (after) - /// CHECK-DAG: <<Const1:i\d+>> IntConstant 1 + /// CHECK-DAG: <<Const7:i\d+>> IntConstant 7 /// CHECK-DAG: <<DataOffset:i\d+>> IntConstant 12 /// CHECK: <<Array:l\d+>> NewArray /// CHECK: <<Index:i\d+>> Phi @@ -382,12 +382,12 @@ public class Main { // -------------- Loop /// CHECK: <<Address1:i\d+>> IntermediateAddress [<<Array>>,<<DataOffset>>] /// CHECK-NEXT: <<ArrayGet:i\d+>> ArrayGet [<<Address1>>,<<Index>>] - /// CHECK: <<Add:i\d+>> Add [<<ArrayGet>>,<<Const1>>] + /// CHECK: <<Div:i\d+>> Div [<<ArrayGet>>,<<Const7>>] /// CHECK: <<Address2:i\d+>> IntermediateAddress [<<Array>>,<<DataOffset>>] - /// CHECK-NEXT: ArraySet [<<Address2>>,<<Index>>,<<Add>>] + /// CHECK-NEXT: ArraySet [<<Address2>>,<<Index>>,<<Div>>] /// CHECK-START-ARM: int Main.canMergeAfterBCE1() GVN$after_arch (after) - /// CHECK-DAG: <<Const1:i\d+>> IntConstant 1 + /// CHECK-DAG: <<Const7:i\d+>> IntConstant 7 /// CHECK-DAG: <<DataOffset:i\d+>> IntConstant 12 /// CHECK: <<Array:l\d+>> NewArray /// CHECK: <<Index:i\d+>> Phi @@ -395,14 +395,14 @@ public class Main { // -------------- Loop /// CHECK: <<Address:i\d+>> IntermediateAddress [<<Array>>,<<DataOffset>>] /// CHECK: <<ArrayGet:i\d+>> ArrayGet [<<Address>>,<<Index>>] - /// CHECK: <<Add:i\d+>> Add [<<ArrayGet>>,<<Const1>>] + /// CHECK: <<Div:i\d+>> Div [<<ArrayGet>>,<<Const7>>] /// CHECK-NOT: IntermediateAddress - /// CHECK: ArraySet [<<Address>>,<<Index>>,<<Add>>] + /// CHECK: ArraySet [<<Address>>,<<Index>>,<<Div>>] public static int canMergeAfterBCE1() { - int[] array = {0, 1, 2, 3}; + int[] array = {0, 7, 14, 21}; for (int i = 0; i < array.length; i++) { - array[i] = array[i] + 1; + array[i] = array[i] / 7; } return array[array.length - 1]; } @@ -421,8 +421,8 @@ public class Main { /// CHECK-DAG: <<Index1:i\d+>> Add [<<Index>>,<<Const1>>] /// CHECK-DAG: <<ArrayGetI:i\d+>> ArrayGet [<<Array>>,<<Index>>] /// CHECK-DAG: <<ArrayGetI1:i\d+>> ArrayGet [<<Array>>,<<Index1>>] - /// CHECK: <<Add:i\d+>> Add [<<ArrayGetI>>,<<ArrayGetI1>>] - /// CHECK: ArraySet [<<Array>>,<<Index1>>,<<Add>>] + /// CHECK: <<Shl:i\d+>> Shl [<<ArrayGetI>>,<<ArrayGetI1>>] + /// CHECK: ArraySet [<<Array>>,<<Index1>>,<<Shl>>] // Note that we do not care that the `DataOffset` is `12`. But if we do not // specify it and any other `IntConstant` appears before that instruction, @@ -441,9 +441,9 @@ public class Main { /// CHECK-DAG: <<ArrayGetI:i\d+>> ArrayGet [<<Address1>>,<<Index>>] /// CHECK-DAG: <<Address2:i\d+>> IntermediateAddress [<<Array>>,<<DataOffset>>] /// CHECK-DAG: <<ArrayGetI1:i\d+>> ArrayGet [<<Address2>>,<<Index1>>] - /// CHECK: <<Add:i\d+>> Add [<<ArrayGetI>>,<<ArrayGetI1>>] + /// CHECK: <<Shl:i\d+>> Shl [<<ArrayGetI>>,<<ArrayGetI1>>] /// CHECK: <<Address3:i\d+>> IntermediateAddress [<<Array>>,<<DataOffset>>] - /// CHECK: ArraySet [<<Address3>>,<<Index1>>,<<Add>>] + /// CHECK: ArraySet [<<Address3>>,<<Index1>>,<<Shl>>] /// CHECK-START-ARM64: int Main.canMergeAfterBCE2() GVN$after_arch (after) /// CHECK-DAG: <<Const1:i\d+>> IntConstant 1 @@ -456,8 +456,8 @@ public class Main { /// CHECK-DAG: <<Address:i\d+>> IntermediateAddress [<<Array>>,<<DataOffset>>] /// CHECK-DAG: <<ArrayGetI:i\d+>> ArrayGet [<<Address>>,<<Index>>] /// CHECK-DAG: <<ArrayGetI1:i\d+>> ArrayGet [<<Address>>,<<Index1>>] - /// CHECK: <<Add:i\d+>> Add [<<ArrayGetI>>,<<ArrayGetI1>>] - /// CHECK: ArraySet [<<Address>>,<<Index1>>,<<Add>>] + /// CHECK: <<Shl:i\d+>> Shl [<<ArrayGetI>>,<<ArrayGetI1>>] + /// CHECK: ArraySet [<<Address>>,<<Index1>>,<<Shl>>] // There should be only one intermediate address computation in the loop. @@ -475,8 +475,8 @@ public class Main { /// CHECK-DAG: <<Index1:i\d+>> Add [<<Index>>,<<Const1>>] /// CHECK-DAG: <<ArrayGetI:i\d+>> ArrayGet [<<Array>>,<<Index>>] /// CHECK-DAG: <<ArrayGetI1:i\d+>> ArrayGet [<<Array>>,<<Index1>>] - /// CHECK: <<Add:i\d+>> Add [<<ArrayGetI>>,<<ArrayGetI1>>] - /// CHECK: ArraySet [<<Array>>,<<Index1>>,<<Add>>] + /// CHECK: <<Shl:i\d+>> Shl [<<ArrayGetI>>,<<ArrayGetI1>>] + /// CHECK: ArraySet [<<Array>>,<<Index1>>,<<Shl>>] /// CHECK-START-ARM: int Main.canMergeAfterBCE2() instruction_simplifier_arm (after) /// CHECK-DAG: <<Const1:i\d+>> IntConstant 1 @@ -490,9 +490,9 @@ public class Main { /// CHECK-DAG: <<ArrayGetI:i\d+>> ArrayGet [<<Address1>>,<<Index>>] /// CHECK-DAG: <<Address2:i\d+>> IntermediateAddress [<<Array>>,<<DataOffset>>] /// CHECK-DAG: <<ArrayGetI1:i\d+>> ArrayGet [<<Address2>>,<<Index1>>] - /// CHECK: <<Add:i\d+>> Add [<<ArrayGetI>>,<<ArrayGetI1>>] + /// CHECK: <<Shl:i\d+>> Shl [<<ArrayGetI>>,<<ArrayGetI1>>] /// CHECK: <<Address3:i\d+>> IntermediateAddress [<<Array>>,<<DataOffset>>] - /// CHECK: ArraySet [<<Address3>>,<<Index1>>,<<Add>>] + /// CHECK: ArraySet [<<Address3>>,<<Index1>>,<<Shl>>] /// CHECK-START-ARM: int Main.canMergeAfterBCE2() GVN$after_arch (after) /// CHECK-DAG: <<Const1:i\d+>> IntConstant 1 @@ -505,17 +505,17 @@ public class Main { /// CHECK-DAG: <<Address:i\d+>> IntermediateAddress [<<Array>>,<<DataOffset>>] /// CHECK-DAG: <<ArrayGetI:i\d+>> ArrayGet [<<Address>>,<<Index>>] /// CHECK-DAG: <<ArrayGetI1:i\d+>> ArrayGet [<<Address>>,<<Index1>>] - /// CHECK: <<Add:i\d+>> Add [<<ArrayGetI>>,<<ArrayGetI1>>] - /// CHECK: ArraySet [<<Address>>,<<Index1>>,<<Add>>] + /// CHECK: <<Shl:i\d+>> Shl [<<ArrayGetI>>,<<ArrayGetI1>>] + /// CHECK: ArraySet [<<Address>>,<<Index1>>,<<Shl>>] /// CHECK-START-ARM: int Main.canMergeAfterBCE2() GVN$after_arch (after) /// CHECK: IntermediateAddress /// CHECK-NOT: IntermediateAddress public static int canMergeAfterBCE2() { - int[] array = {0, 1, 2, 3}; + int[] array = {64, 8, 4, 2 }; for (int i = 0; i < array.length - 1; i++) { - array[i + 1] = array[i] + array[i + 1]; + array[i + 1] = array[i] << array[i + 1]; } return array[array.length - 1]; } @@ -571,8 +571,8 @@ public class Main { accrossGC(array, 0); assertIntEquals(125, array[0]); - assertIntEquals(4, canMergeAfterBCE1()); - assertIntEquals(6, canMergeAfterBCE2()); + assertIntEquals(3, canMergeAfterBCE1()); + assertIntEquals(1048576, canMergeAfterBCE2()); assertIntEquals(18, checkLongFloatDouble()); } diff --git a/test/616-cha-abstract/src/Main.java b/test/616-cha-abstract/src/Main.java index e1d7db170d..b33f575dec 100644 --- a/test/616-cha-abstract/src/Main.java +++ b/test/616-cha-abstract/src/Main.java @@ -39,8 +39,8 @@ class Main2 extends Main1 { } public class Main { - static Main1 sMain1; - static Main1 sMain2; + static Base sMain1; + static Base sMain2; static boolean sIsOptimizing = true; static boolean sHasJIT = true; diff --git a/test/616-cha-interface-default/expected.txt b/test/616-cha-interface-default/expected.txt new file mode 100644 index 0000000000..6a5618ebc6 --- /dev/null +++ b/test/616-cha-interface-default/expected.txt @@ -0,0 +1 @@ +JNI_OnLoad called diff --git a/test/616-cha-interface-default/info.txt b/test/616-cha-interface-default/info.txt new file mode 100644 index 0000000000..11baa1f0f2 --- /dev/null +++ b/test/616-cha-interface-default/info.txt @@ -0,0 +1,2 @@ +Test for Class Hierarchy Analysis (CHA) on interface method. +Test it under multidex configuration to check cross-dex inlining. diff --git a/test/616-cha-interface-default/multidex.jpp b/test/616-cha-interface-default/multidex.jpp new file mode 100644 index 0000000000..b0d200ea38 --- /dev/null +++ b/test/616-cha-interface-default/multidex.jpp @@ -0,0 +1,3 @@ +Main: + @@com.android.jack.annotations.ForceInMainDex + class Main diff --git a/test/616-cha-interface-default/run b/test/616-cha-interface-default/run new file mode 100644 index 0000000000..d8b4f0d26c --- /dev/null +++ b/test/616-cha-interface-default/run @@ -0,0 +1,18 @@ +#!/bin/bash +# +# Copyright (C) 2017 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. + +# Run without an app image to prevent the classes to be loaded at startup. +exec ${RUN} "${@}" --no-app-image diff --git a/test/616-cha-interface-default/src-multidex/Base.java b/test/616-cha-interface-default/src-multidex/Base.java new file mode 100644 index 0000000000..2cbcb500c4 --- /dev/null +++ b/test/616-cha-interface-default/src-multidex/Base.java @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2017 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. + */ + +interface Base { + default public int foo(int i) { + if (i != 1) { + return -2; + } + return i + 10; + } + + // Test default method that's not inlined. + default public int $noinline$bar() { + System.out.print(""); + System.out.print(""); + System.out.print(""); + System.out.print(""); + System.out.print(""); + System.out.print(""); + System.out.print(""); + System.out.print(""); + return -1; + } + + default void printError(String msg) { + System.out.println(msg); + } +} diff --git a/test/616-cha-interface-default/src/Main.java b/test/616-cha-interface-default/src/Main.java new file mode 100644 index 0000000000..951607d2cf --- /dev/null +++ b/test/616-cha-interface-default/src/Main.java @@ -0,0 +1,176 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +class Main1 implements Base { +} + +class Main2 extends Main1 { + public void foobar() {} +} + +class Main3 implements Base { + public int foo(int i) { + if (i != 3) { + printError("error3"); + } + return -(i + 10); + } +} + +public class Main { + static Base sMain1; + static Base sMain2; + static Base sMain3; + + static boolean sIsOptimizing = true; + static boolean sHasJIT = true; + static volatile boolean sOtherThreadStarted; + + private static void assertSingleImplementation(Class<?> clazz, String method_name, boolean b) { + if (hasSingleImplementation(clazz, method_name) != b) { + System.out.println(clazz + "." + method_name + + " doesn't have single implementation value of " + b); + } + } + + static int getValue(Class<?> cls) { + if (cls == Main1.class || cls == Main2.class) { + return 1; + } + return 3; + } + + // sMain1.foo()/sMain2.foo() will be always be Base.foo() before Main3 is loaded/linked. + // So sMain1.foo() can be devirtualized to Base.foo() and be inlined. + // After Dummy.createMain3() which links in Main3, live testImplement() on stack + // should be deoptimized. + static void testImplement(boolean createMain3, boolean wait, boolean setHasJIT) { + if (setHasJIT) { + if (isInterpreted()) { + sHasJIT = false; + } + return; + } + + if (createMain3 && (sIsOptimizing || sHasJIT)) { + assertIsManaged(); + } + + if (sMain1.foo(getValue(sMain1.getClass())) != 11) { + System.out.println("11 expected."); + } + if (sMain1.$noinline$bar() != -1) { + System.out.println("-1 expected."); + } + if (sMain2.foo(getValue(sMain2.getClass())) != 11) { + System.out.println("11 expected."); + } + + if (createMain3) { + // Wait for the other thread to start. + while (!sOtherThreadStarted); + // Create an Main2 instance and assign it to sMain2. + // sMain1 is kept the same. + sMain3 = Dummy.createMain3(); + // Wake up the other thread. + synchronized(Main.class) { + Main.class.notify(); + } + } else if (wait) { + // This is the other thread. + synchronized(Main.class) { + sOtherThreadStarted = true; + // Wait for Main2 to be linked and deoptimization is triggered. + try { + Main.class.wait(); + } catch (Exception e) { + } + } + } + + // There should be a deoptimization here right after Main3 is linked by + // calling Dummy.createMain3(), even though sMain1 didn't change. + // The behavior here would be different if inline-cache is used, which + // doesn't deoptimize since sMain1 still hits the type cache. + if (sMain1.foo(getValue(sMain1.getClass())) != 11) { + System.out.println("11 expected."); + } + if ((createMain3 || wait) && sHasJIT && !sIsOptimizing) { + // This method should be deoptimized right after Main3 is created. + assertIsInterpreted(); + } + + if (sMain3 != null) { + if (sMain3.foo(getValue(sMain3.getClass())) != -13) { + System.out.println("-13 expected."); + } + } + } + + // Test scenarios under which CHA-based devirtualization happens, + // and class loading that implements a method can invalidate compiled code. + public static void main(String[] args) { + System.loadLibrary(args[0]); + + if (isInterpreted()) { + sIsOptimizing = false; + } + + // sMain1 is an instance of Main1. + // sMain2 is an instance of Main2. + // Neither Main1 nor Main2 override default method Base.foo(). + // Main3 hasn't bee loaded yet. + sMain1 = new Main1(); + sMain2 = new Main2(); + + ensureJitCompiled(Main.class, "testImplement"); + testImplement(false, false, true); + + if (sHasJIT && !sIsOptimizing) { + assertSingleImplementation(Base.class, "foo", true); + assertSingleImplementation(Main1.class, "foo", true); + } else { + // Main3 is verified ahead-of-time so it's linked in already. + } + + // Create another thread that also calls sMain1.foo(). + // Try to test suspend and deopt another thread. + new Thread() { + public void run() { + testImplement(false, true, false); + } + }.start(); + + // This will create Main3 instance in the middle of testImplement(). + testImplement(true, false, false); + assertSingleImplementation(Base.class, "foo", false); + assertSingleImplementation(Main1.class, "foo", true); + assertSingleImplementation(sMain3.getClass(), "foo", true); + } + + private static native void ensureJitCompiled(Class<?> itf, String method_name); + private static native void assertIsInterpreted(); + private static native void assertIsManaged(); + private static native boolean isInterpreted(); + private static native boolean hasSingleImplementation(Class<?> clazz, String method_name); +} + +// Put createMain3() in another class to avoid class loading due to verifier. +class Dummy { + static Base createMain3() { + return new Main3(); + } +} diff --git a/test/616-cha-interface/expected.txt b/test/616-cha-interface/expected.txt new file mode 100644 index 0000000000..6a5618ebc6 --- /dev/null +++ b/test/616-cha-interface/expected.txt @@ -0,0 +1 @@ +JNI_OnLoad called diff --git a/test/616-cha-interface/info.txt b/test/616-cha-interface/info.txt new file mode 100644 index 0000000000..1fd330afd4 --- /dev/null +++ b/test/616-cha-interface/info.txt @@ -0,0 +1 @@ +Test for Class Hierarchy Analysis (CHA) on interface method. diff --git a/test/616-cha-interface/run b/test/616-cha-interface/run new file mode 100644 index 0000000000..d8b4f0d26c --- /dev/null +++ b/test/616-cha-interface/run @@ -0,0 +1,18 @@ +#!/bin/bash +# +# Copyright (C) 2017 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. + +# Run without an app image to prevent the classes to be loaded at startup. +exec ${RUN} "${@}" --no-app-image diff --git a/test/616-cha-interface/src/Main.java b/test/616-cha-interface/src/Main.java new file mode 100644 index 0000000000..3c9349663d --- /dev/null +++ b/test/616-cha-interface/src/Main.java @@ -0,0 +1,173 @@ +/* + * Copyright (C) 2017 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. + */ + +interface Base { + void foo(int i); + void $noinline$bar(); +} + +class Main1 implements Base { + public void foo(int i) { + if (i != 1) { + printError("error1"); + } + } + + // Test rewriting invoke-interface into invoke-virtual when inlining fails. + public void $noinline$bar() { + System.out.print(""); + System.out.print(""); + System.out.print(""); + System.out.print(""); + System.out.print(""); + System.out.print(""); + System.out.print(""); + System.out.print(""); + } + + void printError(String msg) { + System.out.println(msg); + } +} + +class Main2 extends Main1 { + public void foo(int i) { + if (i != 2) { + printError("error2"); + } + } +} + +public class Main { + static Base sMain1; + static Base sMain2; + + static boolean sIsOptimizing = true; + static boolean sHasJIT = true; + static volatile boolean sOtherThreadStarted; + + private static void assertSingleImplementation(Class<?> clazz, String method_name, boolean b) { + if (hasSingleImplementation(clazz, method_name) != b) { + System.out.println(clazz + "." + method_name + + " doesn't have single implementation value of " + b); + } + } + + // sMain1.foo() will be always be Main1.foo() before Main2 is loaded/linked. + // So sMain1.foo() can be devirtualized to Main1.foo() and be inlined. + // After Dummy.createMain2() which links in Main2, live testImplement() on stack + // should be deoptimized. + static void testImplement(boolean createMain2, boolean wait, boolean setHasJIT) { + if (setHasJIT) { + if (isInterpreted()) { + sHasJIT = false; + } + return; + } + + if (createMain2 && (sIsOptimizing || sHasJIT)) { + assertIsManaged(); + } + + sMain1.foo(sMain1.getClass() == Main1.class ? 1 : 2); + sMain1.$noinline$bar(); + + if (createMain2) { + // Wait for the other thread to start. + while (!sOtherThreadStarted); + // Create an Main2 instance and assign it to sMain2. + // sMain1 is kept the same. + sMain2 = Dummy.createMain2(); + // Wake up the other thread. + synchronized(Main.class) { + Main.class.notify(); + } + } else if (wait) { + // This is the other thread. + synchronized(Main.class) { + sOtherThreadStarted = true; + // Wait for Main2 to be linked and deoptimization is triggered. + try { + Main.class.wait(); + } catch (Exception e) { + } + } + } + + // There should be a deoptimization here right after Main2 is linked by + // calling Dummy.createMain2(), even though sMain1 didn't change. + // The behavior here would be different if inline-cache is used, which + // doesn't deoptimize since sMain1 still hits the type cache. + sMain1.foo(sMain1.getClass() == Main1.class ? 1 : 2); + if ((createMain2 || wait) && sHasJIT && !sIsOptimizing) { + // This method should be deoptimized right after Main2 is created. + assertIsInterpreted(); + } + + if (sMain2 != null) { + sMain2.foo(sMain2.getClass() == Main1.class ? 1 : 2); + } + } + + // Test scenarios under which CHA-based devirtualization happens, + // and class loading that overrides a method can invalidate compiled code. + public static void main(String[] args) { + System.loadLibrary(args[0]); + + if (isInterpreted()) { + sIsOptimizing = false; + } + + // sMain1 is an instance of Main1. Main2 hasn't bee loaded yet. + sMain1 = new Main1(); + + ensureJitCompiled(Main.class, "testImplement"); + testImplement(false, false, true); + + if (sHasJIT && !sIsOptimizing) { + assertSingleImplementation(Base.class, "foo", true); + assertSingleImplementation(Main1.class, "foo", true); + } else { + // Main2 is verified ahead-of-time so it's linked in already. + } + + // Create another thread that also calls sMain1.foo(). + // Try to test suspend and deopt another thread. + new Thread() { + public void run() { + testImplement(false, true, false); + } + }.start(); + + // This will create Main2 instance in the middle of testImplement(). + testImplement(true, false, false); + assertSingleImplementation(Base.class, "foo", false); + assertSingleImplementation(Main1.class, "foo", false); + } + + private static native void ensureJitCompiled(Class<?> itf, String method_name); + private static native void assertIsInterpreted(); + private static native void assertIsManaged(); + private static native boolean isInterpreted(); + private static native boolean hasSingleImplementation(Class<?> clazz, String method_name); +} + +// Put createMain2() in another class to avoid class loading due to verifier. +class Dummy { + static Main1 createMain2() { + return new Main2(); + } +} diff --git a/test/616-cha-miranda/expected.txt b/test/616-cha-miranda/expected.txt new file mode 100644 index 0000000000..6a5618ebc6 --- /dev/null +++ b/test/616-cha-miranda/expected.txt @@ -0,0 +1 @@ +JNI_OnLoad called diff --git a/test/616-cha-miranda/info.txt b/test/616-cha-miranda/info.txt new file mode 100644 index 0000000000..c46f33f613 --- /dev/null +++ b/test/616-cha-miranda/info.txt @@ -0,0 +1 @@ +Test for Class Hierarchy Analysis (CHA) on miranda method. diff --git a/test/616-cha-miranda/run b/test/616-cha-miranda/run new file mode 100644 index 0000000000..d8b4f0d26c --- /dev/null +++ b/test/616-cha-miranda/run @@ -0,0 +1,18 @@ +#!/bin/bash +# +# Copyright (C) 2017 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. + +# Run without an app image to prevent the classes to be loaded at startup. +exec ${RUN} "${@}" --no-app-image diff --git a/test/616-cha-miranda/src/Main.java b/test/616-cha-miranda/src/Main.java new file mode 100644 index 0000000000..e548482eb3 --- /dev/null +++ b/test/616-cha-miranda/src/Main.java @@ -0,0 +1,163 @@ +/* + * Copyright (C) 2017 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. + */ + +interface Iface { + public void foo(int i); +} + +abstract class Base implements Iface { + // Iface.foo(int) will be added as a miranda method. + + void printError(String msg) { + System.out.println(msg); + } +} + +class Main1 extends Base { + public void foo(int i) { + if (i != 1) { + printError("error1"); + } + } +} + +class Main2 extends Main1 { + public void foo(int i) { + if (i != 2) { + printError("error2"); + } + } +} + +public class Main { + static Base sMain1; + static Base sMain2; + + static boolean sIsOptimizing = true; + static boolean sHasJIT = true; + static volatile boolean sOtherThreadStarted; + + private static void assertSingleImplementation(Class<?> clazz, String method_name, boolean b) { + if (hasSingleImplementation(clazz, method_name) != b) { + System.out.println(clazz + "." + method_name + + " doesn't have single implementation value of " + b); + } + } + + // sMain1.foo() will be always be Main1.foo() before Main2 is loaded/linked. + // So sMain1.foo() can be devirtualized to Main1.foo() and be inlined. + // After Dummy.createMain2() which links in Main2, live testOverride() on stack + // should be deoptimized. + static void testOverride(boolean createMain2, boolean wait, boolean setHasJIT) { + if (setHasJIT) { + if (isInterpreted()) { + sHasJIT = false; + } + return; + } + + if (createMain2 && (sIsOptimizing || sHasJIT)) { + assertIsManaged(); + } + + sMain1.foo(sMain1.getClass() == Main1.class ? 1 : 2); + + if (createMain2) { + // Wait for the other thread to start. + while (!sOtherThreadStarted); + // Create an Main2 instance and assign it to sMain2. + // sMain1 is kept the same. + sMain2 = Dummy.createMain2(); + // Wake up the other thread. + synchronized(Main.class) { + Main.class.notify(); + } + } else if (wait) { + // This is the other thread. + synchronized(Main.class) { + sOtherThreadStarted = true; + // Wait for Main2 to be linked and deoptimization is triggered. + try { + Main.class.wait(); + } catch (Exception e) { + } + } + } + + // There should be a deoptimization here right after Main2 is linked by + // calling Dummy.createMain2(), even though sMain1 didn't change. + // The behavior here would be different if inline-cache is used, which + // doesn't deoptimize since sMain1 still hits the type cache. + sMain1.foo(sMain1.getClass() == Main1.class ? 1 : 2); + if ((createMain2 || wait) && sHasJIT && !sIsOptimizing) { + // This method should be deoptimized right after Main2 is created. + assertIsInterpreted(); + } + + if (sMain2 != null) { + sMain2.foo(sMain2.getClass() == Main1.class ? 1 : 2); + } + } + + // Test scenarios under which CHA-based devirtualization happens, + // and class loading that overrides a method can invalidate compiled code. + public static void main(String[] args) { + System.loadLibrary(args[0]); + + if (isInterpreted()) { + sIsOptimizing = false; + } + + // sMain1 is an instance of Main1. Main2 hasn't bee loaded yet. + sMain1 = new Main1(); + + ensureJitCompiled(Main.class, "testOverride"); + testOverride(false, false, true); + + if (sHasJIT && !sIsOptimizing) { + assertSingleImplementation(Base.class, "foo", true); + assertSingleImplementation(Main1.class, "foo", true); + } else { + // Main2 is verified ahead-of-time so it's linked in already. + } + + // Create another thread that also calls sMain1.foo(). + // Try to test suspend and deopt another thread. + new Thread() { + public void run() { + testOverride(false, true, false); + } + }.start(); + + // This will create Main2 instance in the middle of testOverride(). + testOverride(true, false, false); + assertSingleImplementation(Base.class, "foo", false); + assertSingleImplementation(Main1.class, "foo", false); + } + + private static native void ensureJitCompiled(Class<?> itf, String method_name); + private static native void assertIsInterpreted(); + private static native void assertIsManaged(); + private static native boolean isInterpreted(); + private static native boolean hasSingleImplementation(Class<?> clazz, String method_name); +} + +// Put createMain2() in another class to avoid class loading due to verifier. +class Dummy { + static Main1 createMain2() { + return new Main2(); + } +} diff --git a/test/616-cha-proxy-method-inline/expected.txt b/test/616-cha-proxy-method-inline/expected.txt new file mode 100644 index 0000000000..6a5618ebc6 --- /dev/null +++ b/test/616-cha-proxy-method-inline/expected.txt @@ -0,0 +1 @@ +JNI_OnLoad called diff --git a/test/616-cha-proxy-method-inline/info.txt b/test/616-cha-proxy-method-inline/info.txt new file mode 100644 index 0000000000..012685547c --- /dev/null +++ b/test/616-cha-proxy-method-inline/info.txt @@ -0,0 +1 @@ +Test for Class Hierarchy Analysis (CHA) on inlining a cross-dex proxy method. diff --git a/test/616-cha-proxy-method-inline/multidex.jpp b/test/616-cha-proxy-method-inline/multidex.jpp new file mode 100644 index 0000000000..b0d200ea38 --- /dev/null +++ b/test/616-cha-proxy-method-inline/multidex.jpp @@ -0,0 +1,3 @@ +Main: + @@com.android.jack.annotations.ForceInMainDex + class Main diff --git a/test/616-cha-proxy-method-inline/run b/test/616-cha-proxy-method-inline/run new file mode 100644 index 0000000000..d8b4f0d26c --- /dev/null +++ b/test/616-cha-proxy-method-inline/run @@ -0,0 +1,18 @@ +#!/bin/bash +# +# Copyright (C) 2017 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. + +# Run without an app image to prevent the classes to be loaded at startup. +exec ${RUN} "${@}" --no-app-image diff --git a/test/616-cha-proxy-method-inline/src-multidex/Foo.java b/test/616-cha-proxy-method-inline/src-multidex/Foo.java new file mode 100644 index 0000000000..9deca3e646 --- /dev/null +++ b/test/616-cha-proxy-method-inline/src-multidex/Foo.java @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2017 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. + */ + +interface Foo { + public Object bar(Object obj); +} diff --git a/test/616-cha-proxy-method-inline/src/Main.java b/test/616-cha-proxy-method-inline/src/Main.java new file mode 100644 index 0000000000..be7bc820b3 --- /dev/null +++ b/test/616-cha-proxy-method-inline/src/Main.java @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import java.lang.reflect.Method; +import java.lang.reflect.InvocationTargetException; + +class DebugProxy implements java.lang.reflect.InvocationHandler { + private Object obj; + static Class<?>[] interfaces = {Foo.class}; + + public static Object newInstance(Object obj) { + return java.lang.reflect.Proxy.newProxyInstance( + Foo.class.getClassLoader(), + interfaces, + new DebugProxy(obj)); + } + + private DebugProxy(Object obj) { + this.obj = obj; + } + + public Object invoke(Object proxy, Method m, Object[] args) throws Throwable { + Object result; + if (obj == null) { + return null; + } + try { + System.out.println("before invoking method " + m.getName()); + result = m.invoke(obj, args); + } catch (InvocationTargetException e) { + throw e.getTargetException(); + } catch (Exception e) { + throw new RuntimeException("unexpected invocation exception: " + e.getMessage()); + } finally { + System.out.println("after invoking method " + m.getName()); + } + return result; + } +} + +public class Main { + public static void call(Foo foo) { + if (foo == null) { + return; + } + foo.bar(null); + } + + public static void main(String[] args) { + System.loadLibrary(args[0]); + Foo foo = (Foo)DebugProxy.newInstance(null); + ensureJitCompiled(Main.class, "call"); + call(foo); + } + + private static native void ensureJitCompiled(Class<?> itf, String method_name); +} diff --git a/test/Android.run-test.mk b/test/Android.run-test.mk index 2b57de679c..703b911f0f 100644 --- a/test/Android.run-test.mk +++ b/test/Android.run-test.mk @@ -43,18 +43,6 @@ TEST_ART_RUN_TEST_DEPENDENCIES := \ TEST_ART_RUN_TEST_ORDERONLY_DEPENDENCIES := setup-jack-server -ifeq ($(ART_TEST_DEBUG_GC),true) - ART_TEST_WITH_STRACE := true -endif - -ifeq ($(ART_TEST_BISECTION),true) - # Need to keep rebuilding the test to bisection search it. - ART_TEST_RUN_TEST_NO_PREBUILD := true - ART_TEST_RUN_TEST_PREBUILD := false - # Bisection search writes to standard output. - ART_TEST_QUIET := false -endif - # Helper to create individual build targets for tests. Must be called with $(eval). # $(1): the test number define define-build-art-run-test @@ -97,707 +85,11 @@ LOCAL_PICKUP_FILES := $(art_run_tests_install_dir) include $(BUILD_PHONY_PACKAGE) -# Clear temp vars. -art_run_tests_build_dir := -art_run_tests_install_dir := -define-build-art-run-test := -TEST_ART_RUN_TEST_BUILD_RULES := - -######################################################################## -# General rules to build and run a run-test. - -TARGET_TYPES := host target -PREBUILD_TYPES := -ifeq ($(ART_TEST_RUN_TEST_PREBUILD),true) - PREBUILD_TYPES += prebuild -endif -ifeq ($(ART_TEST_RUN_TEST_NO_PREBUILD),true) - PREBUILD_TYPES += no-prebuild -endif -ifeq ($(ART_TEST_RUN_TEST_NO_DEX2OAT),true) - PREBUILD_TYPES += no-dex2oat -endif -COMPILER_TYPES := -ifeq ($(ART_TEST_INTERPRETER_ACCESS_CHECKS),true) - COMPILER_TYPES += interp-ac -endif -ifeq ($(ART_TEST_INTERPRETER),true) - COMPILER_TYPES += interpreter -endif -ifeq ($(ART_TEST_JIT),true) - COMPILER_TYPES += jit -endif -OPTIMIZING_COMPILER_TYPES := -ifeq ($(ART_TEST_OPTIMIZING),true) - COMPILER_TYPES += optimizing - OPTIMIZING_COMPILER_TYPES += optimizing -endif -ifeq ($(ART_TEST_OPTIMIZING_GRAPH_COLOR),true) - COMPILER_TYPES += regalloc_gc - OPTIMIZING_COMPILER_TYPES += regalloc_gc -endif -RELOCATE_TYPES := no-relocate -ifeq ($(ART_TEST_RUN_TEST_RELOCATE),true) - RELOCATE_TYPES += relocate -endif -ifeq ($(ART_TEST_RUN_TEST_RELOCATE_NO_PATCHOAT),true) - RELOCATE_TYPES += relocate-npatchoat -endif -TRACE_TYPES := ntrace -ifeq ($(ART_TEST_TRACE),true) - TRACE_TYPES += trace -endif -ifeq ($(ART_TEST_TRACE_STREAM),true) - TRACE_TYPES += stream -endif -GC_TYPES := cms -ifeq ($(ART_TEST_GC_STRESS),true) - GC_TYPES += gcstress -endif -ifeq ($(ART_TEST_GC_VERIFY),true) - GC_TYPES += gcverify -endif -JNI_TYPES := checkjni -ifeq ($(ART_TEST_JNI_FORCECOPY),true) - JNI_TYPES += forcecopy -endif -ifeq ($(ART_TEST_RUN_TEST_IMAGE),true) -IMAGE_TYPES := picimage -endif -ifeq ($(ART_TEST_RUN_TEST_NO_IMAGE),true) - IMAGE_TYPES += no-image -endif -ifeq ($(ART_TEST_RUN_TEST_MULTI_IMAGE),true) - IMAGE_TYPES := multipicimage -endif -PICTEST_TYPES := npictest -ifeq ($(ART_TEST_PIC_TEST),true) - PICTEST_TYPES += pictest -endif -RUN_TYPES := -ifeq ($(ART_TEST_RUN_TEST_DEBUG),true) - RUN_TYPES += debug -endif -ifeq ($(ART_TEST_RUN_TEST_NDEBUG),true) - RUN_TYPES += ndebug -endif -DEBUGGABLE_TYPES := ndebuggable -ifeq ($(ART_TEST_RUN_TEST_DEBUGGABLE),true) -DEBUGGABLE_TYPES += debuggable -endif -ADDRESS_SIZES_TARGET := $(ART_PHONY_TEST_TARGET_SUFFIX) -ADDRESS_SIZES_HOST := $(ART_PHONY_TEST_HOST_SUFFIX) -ifeq ($(ART_TEST_RUN_TEST_2ND_ARCH),true) - ADDRESS_SIZES_TARGET += $(2ND_ART_PHONY_TEST_TARGET_SUFFIX) - ADDRESS_SIZES_HOST += $(2ND_ART_PHONY_TEST_HOST_SUFFIX) -endif -ALL_ADDRESS_SIZES := 64 32 - -# List all run test names with number arguments agreeing with the comment above. -define all-run-test-names - $(foreach target, $(1), \ - $(foreach run-type, $(2), \ - $(foreach prebuild, $(3), \ - $(foreach compiler, $(4), \ - $(foreach relocate, $(5), \ - $(foreach trace, $(6), \ - $(foreach gc, $(7), \ - $(foreach jni, $(8), \ - $(foreach image, $(9), \ - $(foreach pictest, $(10), \ - $(foreach debuggable, $(11), \ - $(foreach test, $(12), \ - $(foreach address_size, $(13), \ - test-art-$(target)-run-test-$(run-type)-$(prebuild)-$(compiler)-$(relocate)-$(trace)-$(gc)-$(jni)-$(image)-$(pictest)-$(debuggable)-$(test)$(address_size) \ - ))))))))))))) -endef # all-run-test-names - -# To generate a full list or tests: -# $(call all-run-test-names,$(TARGET_TYPES),$(RUN_TYPES),$(PREBUILD_TYPES),$(COMPILER_TYPES), \ -# $(RELOCATE_TYPES),$(TRACE_TYPES),$(GC_TYPES),$(JNI_TYPES),$(IMAGE_TYPES), \ -# $(PICTEST_TYPES),$(DEBUGGABLE_TYPES),$(TEST_ART_RUN_TESTS),$(ALL_ADDRESS_SIZES)) - # Convert's a rule name to the form used in variables, e.g. no-relocate to NO_RELOCATE define name-to-var $(shell echo $(1) | tr '[:lower:]' '[:upper:]' | tr '-' '_') endef # name-to-var -# Disable 115-native-bridge, it fails when run through make b/35984597. -# Disable 153-reference-stress temporarily until a fix arrives. b/33389022. -# Disable 080-oom-fragmentation due to flakes. b/33795328 -# Disable 497-inlining-and-class-loader and 542-unresolved-access-check until -# they are rewritten. These tests use a broken class loader that tries to -# register a dex file that's already registered with a different loader. -# b/34193123 -# Disable 638-checker-inline-caches until b/36371709 is fixed. -ART_TEST_RUN_TEST_SKIP += \ - 115-native-bridge \ - 153-reference-stress \ - 080-oom-fragmentation \ - 497-inlining-and-class-loader \ - 542-unresolved-access-check \ - 638-checker-inline-caches - -ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),$(RUN_TYPES),$(PREBUILD_TYPES), \ - $(COMPILER_TYPES),$(RELOCATE_TYPES),$(TRACE_TYPES),$(GC_TYPES),$(JNI_TYPES), \ - $(IMAGE_TYPES), $(PICTEST_TYPES), $(DEBUGGABLE_TYPES), $(ART_TEST_RUN_TEST_SKIP), $(ALL_ADDRESS_SIZES)) - - -# Disable 149-suspend-all-stress, its output is flaky (b/28988206). -TEST_ART_BROKEN_ALL_TARGET_TESTS := \ - 149-suspend-all-stress \ - -ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),$(RUN_TYPES),$(PREBUILD_TYPES), \ - $(COMPILER_TYPES), $(RELOCATE_TYPES),$(TRACE_TYPES),$(GC_TYPES),$(JNI_TYPES), \ - $(IMAGE_TYPES),$(PICTEST_TYPES),$(DEBUGGABLE_TYPES), $(TEST_ART_BROKEN_ALL_TARGET_TESTS), \ - $(ALL_ADDRESS_SIZES)) - -TEST_ART_BROKEN_ALL_TARGET_TESTS := - -# Tests that are timing sensitive and flaky on heavily loaded systems. -TEST_ART_TIMING_SENSITIVE_RUN_TESTS := \ - 002-sleep \ - 053-wait-some \ - 055-enum-performance \ - 133-static-invoke-super - -# disable timing sensitive tests on "dist" builds. -ifdef dist_goal - ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),$(RUN_TYPES),$(PREBUILD_TYPES), \ - $(COMPILER_TYPES),$(RELOCATE_TYPES),$(TRACE_TYPES),$(GC_TYPES),$(JNI_TYPES), \ - $(IMAGE_TYPES), $(PICTEST_TYPES), $(DEBUGGABLE_TYPES), $(TEST_ART_TIMING_SENSITIVE_RUN_TESTS), $(ALL_ADDRESS_SIZES)) -endif - -# 147-stripped-dex-fallback isn't supported on device because --strip-dex -# requires the zip command. -# 569-checker-pattern-replacement tests behaviour present only on host. -TEST_ART_BROKEN_TARGET_TESTS := \ - 147-stripped-dex-fallback \ - 569-checker-pattern-replacement - -ifneq (,$(filter target,$(TARGET_TYPES))) - 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_TARGET_TESTS), $(ALL_ADDRESS_SIZES)) -endif - -TEST_ART_BROKEN_TARGET_TESTS := - -# Tests that require python3. -TEST_ART_PYTHON3_DEPENDENCY_RUN_TESTS := \ - 960-default-smali \ - 961-default-iface-resolution-gen \ - 964-default-iface-init-gen \ - 968-default-partial-compile-gen \ - 969-iface-super \ - 970-iface-super-resolution-gen \ - 971-iface-super - -# Check if we have python3 to run our tests. -ifeq ($(wildcard /usr/bin/python3),) - $(warning "No python3 found. Disabling tests: $(TEST_ART_PYTHON3_DEPENDENCY_RUN_TESTS)") - - # Currently disable tests requiring python3 when it is not installed. - ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),$(RUN_TYPES),$(PREBUILD_TYPES), \ - $(COMPILER_TYPES),$(RELOCATE_TYPES),$(TRACE_TYPES),$(GC_TYPES),$(JNI_TYPES), \ - $(IMAGE_TYPES), $(PICTEST_TYPES), $(DEBUGGABLE_TYPES), $(TEST_ART_PYTHON3_DEPENDENCY_RUN_TESTS), $(ALL_ADDRESS_SIZES)) -endif - -TEST_ART_TIMING_SENSITIVE_RUN_TESTS := - -# Note 116-nodex2oat is not broken per-se it just doesn't (and isn't meant to) work with --prebuild. -TEST_ART_BROKEN_PREBUILD_RUN_TESTS := \ - 116-nodex2oat \ - 118-noimage-dex2oat \ - 134-nodex2oat-nofallback - -ifneq (,$(filter prebuild,$(PREBUILD_TYPES))) - ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),$(RUN_TYPES),prebuild, \ - $(COMPILER_TYPES),$(RELOCATE_TYPES),$(TRACE_TYPES),$(GC_TYPES),$(JNI_TYPES), \ - $(IMAGE_TYPES), $(PICTEST_TYPES), $(DEBUGGABLE_TYPES), $(TEST_ART_BROKEN_PREBUILD_RUN_TESTS), $(ALL_ADDRESS_SIZES)) -endif - -TEST_ART_BROKEN_PREBUILD_RUN_TESTS := - -# 554-jit-profile-file is disabled because it needs a primary oat file to know what it should save. -# 529 and 555: b/27784033 -TEST_ART_BROKEN_NO_PREBUILD_TESTS := \ - 117-nopatchoat \ - 147-stripped-dex-fallback \ - 554-jit-profile-file \ - 529-checker-unresolved \ - 555-checker-regression-x86const \ - 608-checker-unresolved-lse - -ifneq (,$(filter no-prebuild,$(PREBUILD_TYPES))) - ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),$(RUN_TYPES),no-prebuild, \ - $(COMPILER_TYPES),$(RELOCATE_TYPES),$(TRACE_TYPES),$(GC_TYPES),$(JNI_TYPES), \ - $(IMAGE_TYPES), $(PICTEST_TYPES), $(DEBUGGABLE_TYPES), $(TEST_ART_BROKEN_NO_PREBUILD_TESTS), $(ALL_ADDRESS_SIZES)) -endif - -TEST_ART_BROKEN_NO_PREBUILD_TESTS := - -# Note 117-nopatchoat is not broken per-se it just doesn't work (and isn't meant to) without -# --prebuild --relocate -TEST_ART_BROKEN_NO_RELOCATE_TESTS := \ - 117-nopatchoat \ - 118-noimage-dex2oat \ - 119-noimage-patchoat \ - 554-jit-profile-file - -ifneq (,$(filter no-relocate,$(RELOCATE_TYPES))) - ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),$(RUN_TYPES),$(PREBUILD_TYPES), \ - $(COMPILER_TYPES), no-relocate,$(TRACE_TYPES),$(GC_TYPES),$(JNI_TYPES), \ - $(IMAGE_TYPES), $(PICTEST_TYPES), $(DEBUGGABLE_TYPES), $(TEST_ART_BROKEN_NO_RELOCATE_TESTS), $(ALL_ADDRESS_SIZES)) -endif - -TEST_ART_BROKEN_NO_RELOCATE_TESTS := - -# Temporarily disable some broken tests when forcing access checks in interpreter b/22414682 -# 629 requires compilation. -# 030, 080 and 530: b/36377828 -TEST_ART_BROKEN_INTERPRETER_ACCESS_CHECK_TESTS := \ - 137-cfi \ - 030-bad-finalizer \ - 530-checker-lse \ - 530-checker-lse2 \ - 080-oom-throw \ - 629-vdex-speed - -ifneq (,$(filter interp-ac,$(COMPILER_TYPES))) - ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),$(RUN_TYPES),$(PREBUILD_TYPES), \ - interp-ac,$(RELOCATE_TYPES),$(TRACE_TYPES),$(GC_TYPES),$(JNI_TYPES), \ - $(IMAGE_TYPES), $(PICTEST_TYPES), $(DEBUGGABLE_TYPES), $(TEST_ART_BROKEN_INTERPRETER_ACCESS_CHECK_TESTS), $(ALL_ADDRESS_SIZES)) -endif - -TEST_ART_BROKEN_INTERPRETER_ACCESS_CHECK_TESTS := - -# Tests that are broken with GC stress. -# * 137-cfi needs to unwind a second forked process. We're using a primitive sleep to wait till we -# hope the second process got into the expected state. The slowness of gcstress makes this bad. -# * 152-dead-large-object requires a heap larger than what gcstress uses. -# * 908-gc-start-finish expects GCs only to be run at clear points. The reduced heap size makes -# this non-deterministic. Same for 913. -# * 961-default-iface-resolution-gen and 964-default-iface-init-genare very long tests that often -# will take more than the timeout to run when gcstress is enabled. This is because gcstress -# slows down allocations significantly which these tests do a lot. -TEST_ART_BROKEN_GCSTRESS_RUN_TESTS := \ - 137-cfi \ - 152-dead-large-object \ - 154-gc-loop \ - 908-gc-start-finish \ - 913-heaps \ - 961-default-iface-resolution-gen \ - 964-default-iface-init-gen \ - -ifneq (,$(filter gcstress,$(GC_TYPES))) - ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),$(RUN_TYPES),$(PREBUILD_TYPES), \ - $(COMPILER_TYPES),$(RELOCATE_TYPES),$(TRACE_TYPES),gcstress,$(JNI_TYPES), \ - $(IMAGE_TYPES), $(PICTEST_TYPES), $(DEBUGGABLE_TYPES), $(TEST_ART_BROKEN_GCSTRESS_RUN_TESTS), $(ALL_ADDRESS_SIZES)) -endif - -TEST_ART_BROKEN_GCSTRESS_RUN_TESTS := - -# 115-native-bridge setup is complicated. Need to implement it correctly for the target. -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), 115-native-bridge, \ - $(ALL_ADDRESS_SIZES)) - -# 130-hprof dumps the heap and runs hprof-conv to check whether the file is somewhat readable. This -# is only possible on the host. -# TODO: Turn off all the other combinations, this is more about testing actual ART code. A gtest is -# very hard to write here, as (for a complete test) JDWP must be set up. -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),130-hprof,$(ALL_ADDRESS_SIZES)) - -# 131 is an old test. The functionality has been implemented at an earlier stage and is checked -# in tests 138. Blacklisted for debug builds since these builds have duplicate classes checks which -# punt to interpreter. -ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),debug,$(PREBUILD_TYPES), \ - $(COMPILER_TYPES),$(RELOCATE_TYPES),$(TRACE_TYPES),$(GC_TYPES),$(JNI_TYPES),$(IMAGE_TYPES), \ - $(PICTEST_TYPES),$(DEBUGGABLE_TYPES),131-structural-change,$(ALL_ADDRESS_SIZES)) - -# 138-duplicate-classes-check. Turned on for debug builds since debug builds have duplicate classes -# checks enabled, b/2133391. -ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),ndebug,$(PREBUILD_TYPES), \ - $(COMPILER_TYPES),$(RELOCATE_TYPES),$(TRACE_TYPES),$(GC_TYPES),$(JNI_TYPES),$(IMAGE_TYPES), \ - $(PICTEST_TYPES),$(DEBUGGABLE_TYPES),138-duplicate-classes-check,$(ALL_ADDRESS_SIZES)) - -# All these tests check that we have sane behavior if we don't have a patchoat or dex2oat. -# Therefore we shouldn't run them in situations where we actually don't have these since they -# explicitly test for them. These all also assume we have an image. -# 147-stripped-dex-fallback is disabled because it requires --prebuild. -# 554-jit-profile-file is disabled because it needs a primary oat file to know what it should save. -# 629-vdex-speed requires compiled code. -TEST_ART_BROKEN_FALLBACK_RUN_TESTS := \ - 116-nodex2oat \ - 117-nopatchoat \ - 118-noimage-dex2oat \ - 119-noimage-patchoat \ - 137-cfi \ - 138-duplicate-classes-check2 \ - 147-stripped-dex-fallback \ - 554-jit-profile-file \ - 616-cha \ - 616-cha-abstract \ - 912-classes \ - 629-vdex-speed - -# This test fails without an image. -# 018, 961, 964, 968 often time out. b/34369284 -# 508: b/36365552 -# 597: b/36467228 -TEST_ART_BROKEN_NO_IMAGE_RUN_TESTS := \ - 137-cfi \ - 138-duplicate-classes-check \ - 018-stack-overflow \ - 476-clinit-inline-static-invoke \ - 496-checker-inlining-class-loader \ - 508-referrer-method \ - 597-deopt-new-string \ - 637-checker-throw-inline \ - 616-cha \ - 616-cha-abstract \ - 912-classes \ - 961-default-iface-resolution-gen \ - 964-default-iface-init \ - 968-default-partial-compile-gen \ - -ifneq (,$(filter no-dex2oat,$(PREBUILD_TYPES))) - ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),$(RUN_TYPES),no-dex2oat, \ - $(COMPILER_TYPES),$(RELOCATE_TYPES),$(TRACE_TYPES),$(GC_TYPES),$(JNI_TYPES),$(IMAGE_TYPES), \ - $(PICTEST_TYPES),$(DEBUGGABLE_TYPES), $(TEST_ART_BROKEN_FALLBACK_RUN_TESTS),$(ALL_ADDRESS_SIZES)) -endif - - -ifneq (,$(filter no-image,$(IMAGE_TYPES))) - ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),$(RUN_TYPES),$(PREBUILD_TYPES), \ - $(COMPILER_TYPES), $(RELOCATE_TYPES),$(TRACE_TYPES),$(GC_TYPES),$(JNI_TYPES),no-image, \ - $(PICTEST_TYPES), $(DEBUGGABLE_TYPES), $(TEST_ART_BROKEN_FALLBACK_RUN_TESTS),$(ALL_ADDRESS_SIZES)) - ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),$(RUN_TYPES),$(PREBUILD_TYPES), \ - $(COMPILER_TYPES), $(RELOCATE_TYPES),$(TRACE_TYPES),$(GC_TYPES),$(JNI_TYPES),no-image, \ - $(PICTEST_TYPES), $(DEBUGGABLE_TYPES), $(TEST_ART_BROKEN_NO_IMAGE_RUN_TESTS),$(ALL_ADDRESS_SIZES)) -endif - -ifneq (,$(filter relocate-npatchoat,$(RELOCATE_TYPES))) - ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),$(RUN_TYPES),$(PREBUILD_TYPES), \ - $(COMPILER_TYPES), relocate-npatchoat,$(TRACE_TYPES),$(GC_TYPES),$(JNI_TYPES), \ - $(IMAGE_TYPES),$(PICTEST_TYPES),$(DEBUGGABLE_TYPES), $(TEST_ART_BROKEN_FALLBACK_RUN_TESTS),$(ALL_ADDRESS_SIZES)) -endif - -TEST_ART_BROKEN_FALLBACK_RUN_TESTS := - -# 137: -# This test unrolls and expects managed frames, but tracing means we run the interpreter. -# 802 and 570-checker-osr: -# This test dynamically enables tracing to force a deoptimization. This makes the test meaningless -# when already tracing, and writes an error message that we do not want to check for. -# 130 occasional timeout b/32383962. -# 629 requires compilation. -TEST_ART_BROKEN_TRACING_RUN_TESTS := \ - 087-gc-after-link \ - 130-hprof \ - 137-cfi \ - 141-class-unload \ - 570-checker-osr \ - 629-vdex-speed \ - 802-deoptimization - -ifneq (,$(filter trace stream,$(TRACE_TYPES))) - ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),$(RUN_TYPES),$(PREBUILD_TYPES), \ - $(COMPILER_TYPES),$(RELOCATE_TYPES),trace stream,$(GC_TYPES),$(JNI_TYPES),$(IMAGE_TYPES), \ - $(PICTEST_TYPES),$(DEBUGGABLE_TYPES), $(TEST_ART_BROKEN_TRACING_RUN_TESTS),$(ALL_ADDRESS_SIZES)) -endif - -TEST_ART_BROKEN_TRACING_RUN_TESTS := - -# These tests expect JIT compilation, which is suppressed when tracing. -TEST_ART_BROKEN_JIT_TRACING_RUN_TESTS := \ - 604-hot-static-interface \ - 612-jit-dex-cache \ - 613-inlining-dex-cache \ - 616-cha \ - 626-set-resolved-string \ - -ifneq (,$(filter trace stream,$(TRACE_TYPES))) - ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),$(RUN_TYPES),$(PREBUILD_TYPES), \ - jit,$(RELOCATE_TYPES),trace stream,$(GC_TYPES),$(JNI_TYPES),$(IMAGE_TYPES), \ - $(PICTEST_TYPES),$(DEBUGGABLE_TYPES), $(TEST_ART_BROKEN_JIT_TRACING_RUN_TESTS),$(ALL_ADDRESS_SIZES)) -endif - -TEST_ART_BROKEN_JIT_TRACING_RUN_TESTS := - -# Known broken tests for the interpreter. -# CFI unwinding expects managed frames. -# 629 requires compilation. -TEST_ART_BROKEN_INTERPRETER_RUN_TESTS := \ - 137-cfi \ - 554-jit-profile-file \ - 629-vdex-speed - -ifneq (,$(filter interpreter,$(COMPILER_TYPES))) - ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),$(RUN_TYPES),$(PREBUILD_TYPES), \ - interpreter,$(RELOCATE_TYPES),$(TRACE_TYPES),$(GC_TYPES),$(JNI_TYPES), \ - $(IMAGE_TYPES),$(PICTEST_TYPES),$(DEBUGGABLE_TYPES),$(TEST_ART_BROKEN_INTERPRETER_RUN_TESTS),$(ALL_ADDRESS_SIZES)) -endif - -TEST_ART_BROKEN_INTERPRETER_RUN_TESTS := - -# Known broken tests for the JIT. -# CFI unwinding expects managed frames, and the test does not iterate enough to even compile. JIT -# also uses Generic JNI instead of the JNI compiler. -# 154-gc-loop requires more deterministic GC behavior than what JIT does. -# Test 906 iterates the heap filtering with different options. No instances should be created -# between those runs to be able to have precise checks. -# Test 629 requires compilation. -# 912: b/34655682 -TEST_ART_BROKEN_JIT_RUN_TESTS := \ - 137-cfi \ - 154-gc-loop \ - 629-vdex-speed \ - 904-object-allocation \ - 906-iterate-heap \ - -ifneq (,$(filter jit,$(COMPILER_TYPES))) - ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),$(RUN_TYPES),$(PREBUILD_TYPES), \ - jit,$(RELOCATE_TYPES),$(TRACE_TYPES),$(GC_TYPES),$(JNI_TYPES), \ - $(IMAGE_TYPES),$(PICTEST_TYPES),$(DEBUGGABLE_TYPES),$(TEST_ART_BROKEN_JIT_RUN_TESTS),$(ALL_ADDRESS_SIZES)) -endif - -TEST_ART_BROKEN_JIT_RUN_TESTS := - -# Known broken tests for the graph coloring register allocator. -# These tests were based on the linear scan allocator, which makes different decisions than -# the graph coloring allocator. (These attempt to test for code quality, not correctness.) -TEST_ART_BROKEN_OPTIMIZING_GRAPH_COLOR := \ - 570-checker-select \ - 484-checker-register-hints - -ifneq (,$(filter regalloc_gc,$(COMPILER_TYPES))) - ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),$(RUN_TYPES),$(PREBUILD_TYPES), \ - regalloc_gc,$(RELOCATE_TYPES),$(TRACE_TYPES),$(GC_TYPES),$(JNI_TYPES), \ - $(IMAGE_TYPES),$(PICTEST_TYPES),$(DEBUGGABLE_TYPES), \ - $(TEST_ART_BROKEN_OPTIMIZING_GRAPH_COLOR),$(ALL_ADDRESS_SIZES)) -endif - -# Known broken tests for the mips32 optimizing compiler backend. -TEST_ART_BROKEN_OPTIMIZING_MIPS_RUN_TESTS := \ - -ifeq (mips,$(TARGET_ARCH)) - ifneq (,$(filter $(OPTIMIZING_COMPILER_TYPES),$(COMPILER_TYPES))) - ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,target,$(RUN_TYPES),$(PREBUILD_TYPES), \ - $(OPTIMIZING_COMPILER_TYPES),$(RELOCATE_TYPES),$(TRACE_TYPES),$(GC_TYPES),$(JNI_TYPES), \ - $(IMAGE_TYPES),$(PICTEST_TYPES),$(DEBUGGABLE_TYPES), \ - $(TEST_ART_BROKEN_OPTIMIZING_MIPS_RUN_TESTS),$(ALL_ADDRESS_SIZES)) - endif -endif - -TEST_ART_BROKEN_OPTIMIZING_MIPS_RUN_TESTS := - -# Known broken tests for the mips64 optimizing compiler backend. -TEST_ART_BROKEN_OPTIMIZING_MIPS64_RUN_TESTS := \ - -ifeq (mips64,$(TARGET_ARCH)) - ifneq (,$(filter $(OPTIMIZING_COMPILER_TYPES),$(COMPILER_TYPES))) - ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,target,$(RUN_TYPES),$(PREBUILD_TYPES), \ - $(OPTIMIZING_COMPILER_TYPES),$(RELOCATE_TYPES),$(TRACE_TYPES),$(GC_TYPES),$(JNI_TYPES), \ - $(IMAGE_TYPES),$(PICTEST_TYPES),$(DEBUGGABLE_TYPES), \ - $(TEST_ART_BROKEN_OPTIMIZING_MIPS64_RUN_TESTS),$(ALL_ADDRESS_SIZES)) - endif -endif - -TEST_ART_BROKEN_OPTIMIZING_MIPS64_RUN_TESTS := - -# Tests that should fail when the optimizing compiler compiles them non-debuggable. -TEST_ART_BROKEN_OPTIMIZING_NONDEBUGGABLE_RUN_TESTS := \ - 454-get-vreg \ - 457-regs \ - 602-deoptimizeable - -ifneq (,$(filter $(OPTIMIZING_COMPILER_TYPES),$(COMPILER_TYPES))) - ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),$(RUN_TYPES),$(PREBUILD_TYPES), \ - $(OPTIMIZING_COMPILER_TYPES),$(RELOCATE_TYPES),$(TRACE_TYPES),$(GC_TYPES),$(JNI_TYPES), \ - $(IMAGE_TYPES),$(PICTEST_TYPES),ndebuggable,$(TEST_ART_BROKEN_OPTIMIZING_NONDEBUGGABLE_RUN_TESTS),$(ALL_ADDRESS_SIZES)) -endif - -TEST_ART_BROKEN_OPTIMIZING_NONDEBUGGABLE_RUN_TESTS := - -# Tests that should fail when the optimizing compiler compiles them debuggable. -TEST_ART_BROKEN_OPTIMIZING_DEBUGGABLE_RUN_TESTS := \ - -ifneq (,$(filter $(OPTIMIZING_COMPILER_TYPES),$(COMPILER_TYPES))) - ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),$(RUN_TYPES),$(PREBUILD_TYPES), \ - $(OPTIMIZING_COMPILER_TYPES),$(RELOCATE_TYPES),$(TRACE_TYPES),$(GC_TYPES),$(JNI_TYPES), \ - $(IMAGE_TYPES),$(PICTEST_TYPES),debuggable,$(TEST_ART_BROKEN_OPTIMIZING_DEBUGGABLE_RUN_TESTS),$(ALL_ADDRESS_SIZES)) -endif - -TEST_ART_BROKEN_OPTIMIZING_DEBUGGABLE_RUN_TESTS := - -# Tests that should fail in the read barrier configuration with the interpreter. -TEST_ART_BROKEN_INTERPRETER_READ_BARRIER_RUN_TESTS := - -# Tests that should fail in the read barrier configuration with the Optimizing compiler (AOT). -TEST_ART_BROKEN_OPTIMIZING_READ_BARRIER_RUN_TESTS := - -# Tests that should fail in the read barrier configuration with JIT (Optimizing compiler). -TEST_ART_BROKEN_JIT_READ_BARRIER_RUN_TESTS := - -# Tests failing in non-Baker read barrier configurations with the Optimizing compiler (AOT). -# 537 and 641: Expect an array copy to be intrinsified, but calling-on-slowpath intrinsics are not yet -# handled in non-Baker read barrier configurations. -TEST_ART_BROKEN_OPTIMIZING_NON_BAKER_READ_BARRIER_RUN_TESTS := \ - 537-checker-arraycopy \ - 641-checker-arraycopy - -# Tests failing in non-Baker read barrier configurations with JIT (Optimizing compiler). -# 537 and 641: Expect an array copy to be intrinsified, but calling-on-slowpath intrinsics are not yet -# handled in non-Baker read barrier configurations. -TEST_ART_BROKEN_JIT_NON_BAKER_READ_BARRIER_RUN_TESTS := \ - 537-checker-arraycopy \ - 641-checker-arraycopy - -ifeq ($(ART_USE_READ_BARRIER),true) - ifneq (,$(filter interpreter,$(COMPILER_TYPES))) - ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),$(RUN_TYPES), \ - $(PREBUILD_TYPES),interpreter,$(RELOCATE_TYPES),$(TRACE_TYPES),$(GC_TYPES), \ - $(JNI_TYPES),$(IMAGE_TYPES),$(PICTEST_TYPES),$(DEBUGGABLE_TYPES), \ - $(TEST_ART_BROKEN_INTERPRETER_READ_BARRIER_RUN_TESTS),$(ALL_ADDRESS_SIZES)) - endif - - ifneq (,$(filter $(OPTIMIZING_COMPILER_TYPES),$(COMPILER_TYPES))) - ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),$(RUN_TYPES), \ - $(PREBUILD_TYPES),$(OPTIMIZING_COMPILER_TYPES),$(RELOCATE_TYPES),$(TRACE_TYPES), \ - $(GC_TYPES),$(JNI_TYPES),$(IMAGE_TYPES),$(PICTEST_TYPES),$(DEBUGGABLE_TYPES), \ - $(TEST_ART_BROKEN_OPTIMIZING_READ_BARRIER_RUN_TESTS),$(ALL_ADDRESS_SIZES)) - ifneq ($(ART_READ_BARRIER_TYPE),BAKER) - ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),$(RUN_TYPES), \ - $(PREBUILD_TYPES),$(OPTIMIZING_COMPILER_TYPES),$(RELOCATE_TYPES),$(TRACE_TYPES), \ - $(GC_TYPES),$(JNI_TYPES),$(IMAGE_TYPES),$(PICTEST_TYPES),$(DEBUGGABLE_TYPES), \ - $(TEST_ART_BROKEN_OPTIMIZING_NON_BAKER_READ_BARRIER_RUN_TESTS),$(ALL_ADDRESS_SIZES)) - endif - endif - - ifneq (,$(filter jit,$(COMPILER_TYPES))) - ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),$(RUN_TYPES), \ - $(PREBUILD_TYPES),jit,$(RELOCATE_TYPES),$(TRACE_TYPES),$(GC_TYPES), \ - $(JNI_TYPES),$(IMAGE_TYPES),$(PICTEST_TYPES),$(DEBUGGABLE_TYPES), \ - $(TEST_ART_BROKEN_JIT_READ_BARRIER_RUN_TESTS),$(ALL_ADDRESS_SIZES)) - ifneq ($(ART_READ_BARRIER_TYPE),BAKER) - ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),$(RUN_TYPES), \ - $(PREBUILD_TYPES),jit,$(RELOCATE_TYPES),$(TRACE_TYPES),$(GC_TYPES), \ - $(JNI_TYPES),$(IMAGE_TYPES),$(PICTEST_TYPES),$(DEBUGGABLE_TYPES), \ - $(TEST_ART_BROKEN_JIT_NON_BAKER_READ_BARRIER_RUN_TESTS),$(ALL_ADDRESS_SIZES)) - endif - endif -endif - -TEST_ART_BROKEN_OPTIMIZING_READ_BARRIER_RUN_TESTS := -TEST_ART_BROKEN_JIT_READ_BARRIER_RUN_TESTS := - -TEST_ART_BROKEN_NPIC_RUN_TESTS := 596-app-images -ifneq (,$(filter npictest,$(PICTEST_TYPES))) - ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),$(RUN_TYPES),$(PREBUILD_TYPES), \ - ${COMPILER_TYPES},$(RELOCATE_TYPES),$(TRACE_TYPES),$(GC_TYPES),$(JNI_TYPES), \ - $(IMAGE_TYPES),npictest,$(DEBUGGABLE_TYPES),$(TEST_ART_BROKEN_NPIC_RUN_TESTS),$(ALL_ADDRESS_SIZES)) -endif - -# Tests that should fail in the heap poisoning configuration with the Optimizing compiler. -# 055: Exceeds run time limits due to heap poisoning instrumentation (on ARM and ARM64 devices). -TEST_ART_BROKEN_OPTIMIZING_HEAP_POISONING_RUN_TESTS := \ - 055-enum-performance - -ifeq ($(ART_HEAP_POISONING),true) - ifneq (,$(filter $(OPTIMIZING_COMPILER_TYPES),$(COMPILER_TYPES))) - ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),$(RUN_TYPES), \ - $(PREBUILD_TYPES),$(OPTIMIZING_COMPILER_TYPES),$(RELOCATE_TYPES),$(TRACE_TYPES),$(GC_TYPES),$(JNI_TYPES), \ - $(IMAGE_TYPES),$(PICTEST_TYPES),$(DEBUGGABLE_TYPES), \ - $(TEST_ART_BROKEN_OPTIMIZING_HEAP_POISONING_RUN_TESTS),$(ALL_ADDRESS_SIZES)) - endif -endif - -TEST_ART_BROKEN_OPTIMIZING_HEAP_POISONING_RUN_TESTS := - -# 909: Tests that check semantics for a non-debuggable app. -# 137: relies on AOT code and debuggable makes us JIT always. -TEST_ART_BROKEN_DEBUGGABLE_RUN_TESTS := \ - 137-cfi \ - 909-attach-agent \ - -ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),$(RUN_TYPES),$(PREBUILD_TYPES), \ - $(COMPILER_TYPES),$(RELOCATE_TYPES),$(TRACE_TYPES),$(GC_TYPES),$(JNI_TYPES), \ - $(IMAGE_TYPES),$(PICTEST_TYPES),debuggable,$(TEST_ART_BROKEN_DEBUGGABLE_RUN_TESTS),$(ALL_ADDRESS_SIZES)) - -TEST_ART_BROKEN_DEBUGGABLE_RUN_TESTS := - -# Tests incompatible with bisection bug search. Sorted by incompatibility reason. -# 000 through 595 do not compile anything. 089 tests a build failure. 018 through 137 -# run dalvikvm more than once. 115 and 088 assume they are always compiled. -# 055 tests performance which is degraded during bisecting. -TEST_ART_INCOMPATIBLE_BISECTION_SEARCH_RUN_TESTS := \ - 000-nop \ - 134-nodex2oat-nofallback \ - 147-stripped-dex-fallback \ - 595-profile-saving \ - \ - 089-many-methods \ - \ - 018-stack-overflow \ - 116-nodex2oat \ - 117-nopatchoat \ - 118-noimage-dex2oat \ - 119-noimage-patchoat \ - 126-miranda-multidex \ - 137-cfi \ - \ - 115-native-bridge \ - 088-monitor-verification \ - \ - 055-enum-performance - -ifeq ($(ART_TEST_BISECTION),true) - ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),$(RUN_TYPES), \ - $(PREBUILD_TYPES),$(OPTIMIZING_COMPILER_TYPES),$(RELOCATE_TYPES),$(TRACE_TYPES),$(GC_TYPES),$(JNI_TYPES), \ - $(IMAGE_TYPES),$(PICTEST_TYPES),$(DEBUGGABLE_TYPES), \ - $(TEST_ART_INCOMPATIBLE_BISECTION_SEARCH_RUN_TESTS),$(ALL_ADDRESS_SIZES)) -endif - -# Clear variables ahead of appending to them when defining tests. -$(foreach target, $(TARGET_TYPES), $(eval ART_RUN_TEST_$(call name-to-var,$(target))_RULES :=)) -$(foreach target, $(TARGET_TYPES), \ - $(foreach prebuild, $(PREBUILD_TYPES), \ - $(eval ART_RUN_TEST_$(call name-to-var,$(target))_$(call name-to-var,$(prebuild))_RULES :=))) -$(foreach target, $(TARGET_TYPES), \ - $(foreach compiler, $(COMPILER_TYPES), \ - $(eval ART_RUN_TEST_$(call name-to-var,$(target))_$(call name-to-var,$(compiler))_RULES :=))) -$(foreach target, $(TARGET_TYPES), \ - $(foreach relocate, $(RELOCATE_TYPES), \ - $(eval ART_RUN_TEST_$(call name-to-var,$(target))_$(call name-to-var,$(relocate))_RULES :=))) -$(foreach target, $(TARGET_TYPES), \ - $(foreach trace, $(TRACE_TYPES), \ - $(eval ART_RUN_TEST_$(call name-to-var,$(target))_$(call name-to-var,$(trace))_RULES :=))) -$(foreach target, $(TARGET_TYPES), \ - $(foreach gc, $(GC_TYPES), \ - $(eval ART_RUN_TEST_$(call name-to-var,$(target))_$(call name-to-var,$(gc))_RULES :=))) -$(foreach target, $(TARGET_TYPES), \ - $(foreach jni, $(JNI_TYPES), \ - $(eval ART_RUN_TEST_$(call name-to-var,$(target))_$(call name-to-var,$(jni))_RULES :=))) -$(foreach target, $(TARGET_TYPES), \ - $(foreach image, $(IMAGE_TYPES), \ - $(eval ART_RUN_TEST_$(call name-to-var,$(target))_$(call name-to-var,$(image))_RULES :=))) -$(foreach target, $(TARGET_TYPES), \ - $(foreach test, $(TEST_ART_RUN_TESTS), \ - $(eval ART_RUN_TEST_$(call name-to-var,$(target))_$(call name-to-var,$(test))_RULES :=))) -$(foreach target, $(TARGET_TYPES), \ - $(foreach address_size, $(ALL_ADDRESS_SIZES), \ - $(eval ART_RUN_TEST_$(call name-to-var,$(target))_$(call name-to-var,$(address_size))_RULES :=))) -$(foreach target, $(TARGET_TYPES), \ - $(foreach run_type, $(RUN_TYPES), \ - $(eval ART_RUN_TEST_$(call name-to-var,$(target))_$(call name-to-var,$(run_type))_RULES :=))) -$(foreach target, $(TARGET_TYPES), \ - $(foreach debuggable_type, $(DEBUGGABLE_TYPES), \ - $(eval ART_RUN_TEST_$(call name-to-var,$(target))_$(call name-to-var,$(debuggable_type))_RULES :=))) - # We need dex2oat and dalvikvm on the target as well as the core images (all images as we sync # only once). TEST_ART_TARGET_SYNC_DEPS += $(ART_TARGET_EXECUTABLES) $(TARGET_CORE_IMG_OUTS) @@ -916,423 +208,62 @@ define core-image-dependencies endif endef -COMPILER_TYPES_2 := optimizing -COMPILER_TYPES_2 += interpreter -COMPILER_TYPES_2 += jit -COMPILER_TYPES_2 += regalloc_gc -COMPILER_TYPES_2 += interp-ac -ALL_ADDRESS_SIZES_2 := 32 64 -IMAGE_TYPES_2 := picimage -IMAGE_TYPES_2 += no-image -IMAGE_TYPES_2 += npicimage -IMAGE_TYPES_2 += multinpicimage -IMAGE_TYPES_2 += multipicimage +TARGET_TYPES := host target +COMPILER_TYPES := jit interpreter optimizing regalloc_gc jit interp-ac +IMAGE_TYPES := picimage no-image multipicimage +ALL_ADDRESS_SIZES := 64 32 # Add core image dependencies required for given target - HOST or TARGET, # IMAGE_TYPE, COMPILER_TYPE and ADDRESS_SIZE to the prereq_rules. $(foreach target, $(TARGET_TYPES), \ - $(foreach image, $(IMAGE_TYPES_2), \ - $(foreach compiler, $(COMPILER_TYPES_2), \ - $(foreach address_size, $(ALL_ADDRESS_SIZES_2), $(eval \ + $(foreach image, $(IMAGE_TYPES), \ + $(foreach compiler, $(COMPILER_TYPES), \ + $(foreach address_size, $(ALL_ADDRESS_SIZES), $(eval \ $(call core-image-dependencies,$(target),$(image),$(compiler),$(address_size))))))) test-art-host-run-test-dependencies : $(host_prereq_rules) test-art-target-run-test-dependencies : $(target_prereq_rules) test-art-run-test-dependencies : test-art-host-run-test-dependencies test-art-target-run-test-dependencies -host_prereq_rules := -target_prereq_rules := - -# Create a rule to build and run a tests following the form: -# test-art-{1: host or target}-run-test-{2: debug ndebug}-{3: prebuild no-prebuild no-dex2oat}- -# {4: interpreter optimizing jit interp-ac}- -# {5: relocate nrelocate relocate-npatchoat}- -# {6: trace or ntrace}-{7: gcstress gcverify cms}-{8: forcecopy checkjni jni}- -# {9: no-image image picimage}-{10: pictest npictest}- -# {11: ndebuggable debuggable}-{12: test name}{13: 32 or 64} -define define-test-art-run-test - run_test_options := - prereq_rule := - test_groups := - uc_host_or_target := - jack_classpath := - ifeq ($(ART_TEST_WITH_STRACE),true) - run_test_options += --strace - endif - ifeq ($(ART_TEST_RUN_TEST_ALWAYS_CLEAN),true) - run_test_options += --always-clean - endif - ifeq ($(ART_TEST_BISECTION),true) - run_test_options += --bisection-search - endif - ifeq ($(1),host) - uc_host_or_target := HOST - test_groups := ART_RUN_TEST_HOST_RULES - run_test_options += --host - prereq_rule := $(ART_TEST_HOST_RUN_TEST_DEPENDENCIES) $(HOST_JACK_CLASSPATH_DEPENDENCIES) - jack_classpath := $(HOST_JACK_CLASSPATH) - else - ifeq ($(1),target) - uc_host_or_target := TARGET - test_groups := ART_RUN_TEST_TARGET_RULES - prereq_rule := test-art-target-sync $(TARGET_JACK_CLASSPATH_DEPENDENCIES) - jack_classpath := $(TARGET_JACK_CLASSPATH) - else - $$(error found $(1) expected $(TARGET_TYPES)) - endif - endif - ifeq ($(2),debug) - test_groups += ART_RUN_TEST_$$(uc_host_or_target)_DEBUG_RULES - else - ifeq ($(2),ndebug) - test_groups += ART_RUN_TEST_$$(uc_host_or_target)_RELEASE_RULES - run_test_options += -O - else - $$(error found $(2) expected $(RUN_TYPES)) - endif - endif - ifeq ($(3),prebuild) - test_groups += ART_RUN_TEST_$$(uc_host_or_target)_PREBUILD_RULES - run_test_options += --prebuild - else - ifeq ($(3),no-prebuild) - test_groups += ART_RUN_TEST_$$(uc_host_or_target)_NO_PREBUILD_RULES - run_test_options += --no-prebuild - else - ifeq ($(3),no-dex2oat) - test_groups += ART_RUN_TEST_$$(uc_host_or_target)_NO_DEX2OAT_RULES - run_test_options += --no-prebuild --no-dex2oat - else - $$(error found $(3) expected $(PREBUILD_TYPES)) - endif - endif - endif - ifeq ($(4),optimizing) - test_groups += ART_RUN_TEST_$$(uc_host_or_target)_OPTIMIZING_RULES - run_test_options += --optimizing - else ifeq ($(4),regalloc_gc) - test_groups += ART_RUN_TEST_$$(uc_host_or_target)_OPTIMIZING_GRAPH_COLOR_RULES - run_test_options += --optimizing -Xcompiler-option --register-allocation-strategy=graph-color - else - ifeq ($(4),interpreter) - test_groups += ART_RUN_TEST_$$(uc_host_or_target)_INTERPRETER_RULES - run_test_options += --interpreter - else ifeq ($(4),interp-ac) - test_groups += ART_RUN_TEST_$$(uc_host_or_target)_INTERPRETER_ACCESS_CHECKS_RULES - run_test_options += --interpreter --verify-soft-fail - else - ifeq ($(4),jit) - test_groups += ART_RUN_TEST_$$(uc_host_or_target)_JIT_RULES - run_test_options += --jit - else - $$(error found $(4) expected $(COMPILER_TYPES)) - endif - endif - endif - - ifeq ($(5),relocate) - test_groups += ART_RUN_TEST_$$(uc_host_or_target)_RELOCATE_RULES - run_test_options += --relocate - else - ifeq ($(5),no-relocate) - test_groups += ART_RUN_TEST_$$(uc_host_or_target)_NO_RELOCATE_RULES - run_test_options += --no-relocate - else - ifeq ($(5),relocate-npatchoat) - test_groups += ART_RUN_TEST_$$(uc_host_or_target)_RELOCATE_NO_PATCHOAT_RULES - run_test_options += --relocate --no-patchoat - else - $$(error found $(5) expected $(RELOCATE_TYPES)) - endif - endif - endif - ifeq ($(6),trace) - test_groups += ART_RUN_TEST_$$(uc_host_or_target)_TRACE_RULES - run_test_options += --trace - else - ifeq ($(6),ntrace) - test_groups += ART_RUN_TEST_$$(uc_host_or_target)_NO_TRACE_RULES - else - ifeq ($(6),stream) - # Group streaming under normal tracing rules. - test_groups += ART_RUN_TEST_$$(uc_host_or_target)_TRACE_RULES - run_test_options += --trace --stream - else - $$(error found $(6) expected $(TRACE_TYPES)) - endif - endif - endif - ifeq ($(7),gcverify) - test_groups += ART_RUN_TEST_$$(uc_host_or_target)_GCVERIFY_RULES - run_test_options += --gcverify - else - ifeq ($(7),gcstress) - test_groups += ART_RUN_TEST_$$(uc_host_or_target)_GCSTRESS_RULES - run_test_options += --gcstress - else - ifeq ($(7),cms) - test_groups += ART_RUN_TEST_$$(uc_host_or_target)_CMS_RULES - else - $$(error found $(7) expected $(GC_TYPES)) - endif - endif - endif - ifeq ($(8),forcecopy) - test_groups += ART_RUN_TEST_$$(uc_host_or_target)_FORCECOPY_RULES - run_test_options += --runtime-option -Xjniopts:forcecopy - ifneq ($$(ART_TEST_JNI_FORCECOPY),true) - skip_test := true - endif - else - ifeq ($(8),checkjni) - test_groups += ART_RUN_TEST_$$(uc_host_or_target)_CHECKJNI_RULES - run_test_options += --runtime-option -Xcheck:jni - else - ifeq ($(8),jni) - test_groups += ART_RUN_TEST_$$(uc_host_or_target)_JNI_RULES - else - $$(error found $(8) expected $(JNI_TYPES)) - endif - endif - endif - image_suffix := $(4) - ifeq ($(4),regalloc_gc) - # Graph coloring tests share the image_suffix with optimizing tests. - image_suffix := optimizing - else - ifeq ($(4),jit) - # JIT tests share the image_suffix with interpreter tests. - image_suffix := interpreter - endif - endif - ifeq ($(9),no-image) - test_groups += ART_RUN_TEST_$$(uc_host_or_target)_NO_IMAGE_RULES - run_test_options += --no-image - # Add the core dependency. This is required for pre-building. - # Use the PIC image, as it is the default in run-test, to match dependencies. - ifeq ($(1),host) - prereq_rule += $$(HOST_CORE_IMAGE_$$(image_suffix)_$(13)) - else - prereq_rule += $$(TARGET_CORE_IMAGE_$$(image_suffix)_$(13)) - endif - else - ifeq ($(9),picimage) - test_groups += ART_RUN_TEST_$$(uc_host_or_target)_PICIMAGE_RULES - ifeq ($(1),host) - prereq_rule += $$(HOST_CORE_IMAGE_$$(image_suffix)_$(13)) - else - prereq_rule += $$(TARGET_CORE_IMAGE_$$(image_suffix)_$(13)) - endif - else - ifeq ($(9),multipicimage) - test_groups += ART_RUN_TEST_$$(uc_host_or_target)_PICIMAGE_RULES - run_test_options += --multi-image - ifeq ($(1),host) - prereq_rule += $$(HOST_CORE_IMAGE_$$(image_suffix)_multi_$(13)) - else - prereq_rule += $$(TARGET_CORE_IMAGE_$$(image_suffix)_multi_$(13)) - endif - else - $$(error found $(9) expected $(IMAGE_TYPES)) - endif - endif - endif - ifeq ($(10),pictest) - run_test_options += --pic-test - else - ifeq ($(10),npictest) - # Nothing to be done. - else - $$(error found $(10) expected $(PICTEST_TYPES)) - endif - endif - ifeq ($(11),debuggable) - test_groups += ART_RUN_TEST_$$(uc_host_or_target)_DEBUGGABLE_RULES - run_test_options += --debuggable - else - ifeq ($(11),ndebuggable) - test_groups += ART_RUN_TEST_$$(uc_host_or_target)_NONDEBUGGABLE_RULES - # Nothing to be done. - else - $$(error found $(11) expected $(DEBUGGABLE_TYPES)) - endif - endif - # $(12) is the test name. - test_groups += ART_RUN_TEST_$$(uc_host_or_target)_$(call name-to-var,$(12))_RULES - ifeq ($(13),64) - test_groups += ART_RUN_TEST_$$(uc_host_or_target)_64_RULES - run_test_options += --64 - else - ifeq ($(13),32) - test_groups += ART_RUN_TEST_$$(uc_host_or_target)_32_RULES - else - $$(error found $(13) expected $(ALL_ADDRESS_SIZES)) - endif - endif - # Override of host instruction-set-features. Required to test advanced x86 intrinsics. The - # conditionals aren't really correct, they will fail to do the right thing on a 32-bit only - # host. However, this isn't common enough to worry here and make the conditions complicated. - ifneq ($(DEX2OAT_HOST_INSTRUCTION_SET_FEATURES),) - ifeq ($(13),64) - run_test_options += --instruction-set-features $(DEX2OAT_HOST_INSTRUCTION_SET_FEATURES) - endif - endif - ifneq ($($(HOST_2ND_ARCH_VAR_PREFIX)DEX2OAT_HOST_INSTRUCTION_SET_FEATURES),) - ifeq ($(13),32) - run_test_options += --instruction-set-features $($(HOST_2ND_ARCH_VAR_PREFIX)DEX2OAT_HOST_INSTRUCTION_SET_FEATURES) - endif - endif - run_test_rule_name := test-art-$(1)-run-test-$(2)-$(3)-$(4)-$(5)-$(6)-$(7)-$(8)-$(9)-$(10)-$(11)-$(12)$(13) - run_test_options := --output-path $(ART_HOST_TEST_DIR)/run-test-output/$$(run_test_rule_name) \ - $$(run_test_options) - ifneq ($(ART_TEST_ANDROID_ROOT),) - run_test_options := --android-root $(ART_TEST_ANDROID_ROOT) $$(run_test_options) - endif - ifeq ($(ART_TEST_QUIET),true) - run_test_options += --quiet - endif -$$(run_test_rule_name): PRIVATE_RUN_TEST_OPTIONS := $$(run_test_options) -$$(run_test_rule_name): PRIVATE_JACK_CLASSPATH := $$(jack_classpath) -.PHONY: $$(run_test_rule_name) -$$(run_test_rule_name): $(TEST_ART_RUN_TEST_DEPENDENCIES) $(HOST_OUT_EXECUTABLES)/hprof-conv $$(prereq_rule) | $(TEST_ART_RUN_TEST_ORDERONLY_DEPENDENCIES) - $(hide) $$(call ART_TEST_SKIP,$$@) && \ - DX=$(abspath $(DX)) \ - JASMIN=$(abspath $(HOST_OUT_EXECUTABLES)/jasmin) \ - SMALI=$(abspath $(HOST_OUT_EXECUTABLES)/smali) \ - DXMERGER=$(abspath $(HOST_OUT_EXECUTABLES)/dexmerger) \ - JACK_VERSION=$(JACK_DEFAULT_VERSION) \ - JACK=$(abspath $(JACK)) \ - JACK_VERSION=$(JACK_DEFAULT_VERSION) \ - JACK_CLASSPATH=$$(PRIVATE_JACK_CLASSPATH) \ - art/test/run-test $$(PRIVATE_RUN_TEST_OPTIONS) $(12) \ - && $$(call ART_TEST_PASSED,$$@) || $$(call ART_TEST_FAILED,$$@) - $$(hide) (echo $(MAKECMDGOALS) | grep -q $$@ && \ - echo "run-test run as top-level target, removing test directory $(ART_HOST_TEST_DIR)" && \ - rm -r $(ART_HOST_TEST_DIR)) || true - - $$(foreach test_group,$$(test_groups), $$(eval $$(value test_group) += $$(run_test_rule_name))) - - # Clear locally defined variables. - uc_host_or_target := - test_groups := - run_test_options := - run_test_rule_name := - prereq_rule := - jack_classpath := -endef # define-test-art-run-test - +# Generate list of dependencies required for given target - HOST or TARGET, IMAGE_TYPE, +# COMPILER_TYPE and ADDRESS_SIZE. $(foreach target, $(TARGET_TYPES), \ - $(foreach test, $(TEST_ART_RUN_TESTS), \ - $(foreach run_type, $(RUN_TYPES), \ - $(foreach address_size, $(ADDRESS_SIZES_$(call name-to-var,$(target))), \ - $(foreach prebuild, $(PREBUILD_TYPES), \ - $(foreach compiler, $(COMPILER_TYPES), \ - $(foreach relocate, $(RELOCATE_TYPES), \ - $(foreach trace, $(TRACE_TYPES), \ - $(foreach gc, $(GC_TYPES), \ - $(foreach jni, $(JNI_TYPES), \ - $(foreach image, $(IMAGE_TYPES), \ - $(foreach pictest, $(PICTEST_TYPES), \ - $(foreach debuggable, $(DEBUGGABLE_TYPES), \ - $(eval $(call define-test-art-run-test,$(target),$(run_type),$(prebuild),$(compiler),$(relocate),$(trace),$(gc),$(jni),$(image),$(pictest),$(debuggable),$(test),$(address_size))) \ - ))))))))))))) -define-test-art-run-test := + $(foreach image, $(IMAGE_TYPES), \ + $(foreach compiler, $(COMPILER_TYPES), \ + $(foreach address_size, $(ALL_ADDRESS_SIZES), $(eval \ + $(call core-image-dependencies,$(target),$(image),$(compiler),$(address_size))))))) -# Define a phony rule whose purpose is to test its prerequisites. -# $(1): host or target -# $(2): list of prerequisites -define define-test-art-run-test-group -.PHONY: $(1) -$(1): $(2) - $(hide) $$(call ART_TEST_PREREQ_FINISHED,$$@) +test-art-host-run-test-dependencies : $(host_prereq_rules) +test-art-target-run-test-dependencies : $(target_prereq_rules) +test-art-run-test-dependencies : test-art-host-run-test-dependencies test-art-target-run-test-dependencies -endef # define-test-art-run-test-group +# Create a rule to build and run a test group of the following form: +# test-art-{1: host target}-run-test +define define-test-art-host-or-target-run-test-group + build_target := test-art-$(1)-run-test + .PHONY: $$(build_target) + $$(build_target) : args := --$(1) --verbose + $$(build_target) : test-art-$(1)-run-test-dependencies + ./art/test/testrunner/testrunner.py $$(args) + build_target := + args := +endef # define-test-art-host-or-target-run-test-group $(foreach target, $(TARGET_TYPES), $(eval \ - $(call define-test-art-run-test-group,test-art-$(target)-run-test,$(ART_RUN_TEST_$(call name-to-var,$(target))_RULES)))) -$(foreach target, $(TARGET_TYPES), \ - $(foreach prebuild, $(PREBUILD_TYPES), $(eval \ - $(call define-test-art-run-test-group,test-art-$(target)-run-test-$(prebuild),$(ART_RUN_TEST_$(call name-to-var,$(target))_$(call name-to-var,$(prebuild))_RULES))))) -$(foreach target, $(TARGET_TYPES), \ - $(foreach run-type, $(RUN_TYPES), $(eval \ - $(call define-test-art-run-test-group,test-art-$(target)-run-test-$(run-type),$(ART_RUN_TEST_$(call name-to-var,$(target))_$(call name-to-var,$(run-type))_RULES))))) -$(foreach target, $(TARGET_TYPES), \ - $(foreach compiler, $(COMPILER_TYPES), $(eval \ - $(call define-test-art-run-test-group,test-art-$(target)-run-test-$(compiler),$(ART_RUN_TEST_$(call name-to-var,$(target))_$(call name-to-var,$(compiler))_RULES))))) -$(foreach target, $(TARGET_TYPES), \ - $(foreach relocate, $(RELOCATE_TYPES), $(eval \ - $(call define-test-art-run-test-group,test-art-$(target)-run-test-$(relocate),$(ART_RUN_TEST_$(call name-to-var,$(target))_$(call name-to-var,$(relocate))_RULES))))) -$(foreach target, $(TARGET_TYPES), \ - $(foreach trace, $(TRACE_TYPES), $(eval \ - $(call define-test-art-run-test-group,test-art-$(target)-run-test-$(trace),$(ART_RUN_TEST_$(call name-to-var,$(target))_$(call name-to-var,$(trace))_RULES))))) -$(foreach target, $(TARGET_TYPES), \ - $(foreach gc, $(GC_TYPES), $(eval \ - $(call define-test-art-run-test-group,test-art-$(target)-run-test-$(gc),$(ART_RUN_TEST_$(call name-to-var,$(target))_$(call name-to-var,$(gc))_RULES))))) -$(foreach target, $(TARGET_TYPES), \ - $(foreach jni, $(JNI_TYPES), $(eval \ - $(call define-test-art-run-test-group,test-art-$(target)-run-test-$(jni),$(ART_RUN_TEST_$(call name-to-var,$(target))_$(call name-to-var,$(jni))_RULES))))) -$(foreach target, $(TARGET_TYPES), \ - $(foreach debuggable, $(DEBUGGABLE_TYPES), $(eval \ - $(call define-test-art-run-test-group,test-art-$(target)-run-test-$(debuggable),$(ART_RUN_TEST_$(call name-to-var,$(target))_$(call name-to-var,$(debuggable))_RULES))))) -$(foreach target, $(TARGET_TYPES), \ - $(foreach image, $(IMAGE_TYPES), $(eval \ - $(call define-test-art-run-test-group,test-art-$(target)-run-test-$(image),$(ART_RUN_TEST_$(call name-to-var,$(target))_$(call name-to-var,$(image))_RULES))))) -$(foreach target, $(TARGET_TYPES), \ - $(foreach test, $(TEST_ART_RUN_TESTS), $(eval \ - $(call define-test-art-run-test-group,test-art-$(target)-run-test-$(test),$(ART_RUN_TEST_$(call name-to-var,$(target))_$(call name-to-var,$(test))_RULES))))) -$(foreach target, $(TARGET_TYPES), \ - $(foreach address_size, $(ADDRESS_SIZES_$(call name-to-var,$(target))), $(eval \ - $(call define-test-art-run-test-group,test-art-$(target)-run-test$(address_size),$(ART_RUN_TEST_$(call name-to-var,$(target))_$(address_size)_RULES))))) + $(call define-test-art-host-or-target-run-test-group,$(target)))) -# Clear variables now we're finished with them. -$(foreach target, $(TARGET_TYPES), $(eval ART_RUN_TEST_$(call name-to-var,$(target))_RULES :=)) -$(foreach target, $(TARGET_TYPES), \ - $(foreach prebuild, $(PREBUILD_TYPES), \ - $(eval ART_RUN_TEST_$(call name-to-var,$(target))_$(call name-to-var,$(prebuild))_RULES :=))) -$(foreach target, $(TARGET_TYPES), \ - $(foreach compiler, $(COMPILER_TYPES), \ - $(eval ART_RUN_TEST_$(call name-to-var,$(target))_$(call name-to-var,$(compiler))_RULES :=))) -$(foreach target, $(TARGET_TYPES), \ - $(foreach relocate, $(RELOCATE_TYPES), \ - $(eval ART_RUN_TEST_$(call name-to-var,$(target))_$(call name-to-var,$(relocate))_RULES :=))) -$(foreach target, $(TARGET_TYPES), \ - $(foreach trace, $(TRACE_TYPES), \ - $(eval ART_RUN_TEST_$(call name-to-var,$(target))_$(call name-to-var,$(trace))_RULES :=))) -$(foreach target, $(TARGET_TYPES), \ - $(foreach gc, $(GC_TYPES), \ - $(eval ART_RUN_TEST_$(call name-to-var,$(target))_$(call name-to-var,$(gc))_RULES :=))) -$(foreach target, $(TARGET_TYPES), \ - $(foreach jni, $(JNI_TYPES), \ - $(eval ART_RUN_TEST_$(call name-to-var,$(target))_$(call name-to-var,$(jni))_RULES :=))) -$(foreach target, $(TARGET_TYPES), \ - $(foreach debuggable, $(DEBUGGABLE_TYPES), \ - $(eval ART_RUN_TEST_$(call name-to-var,$(target))_$(call name-to-var,$(debuggable))_RULES :=))) -$(foreach target, $(TARGET_TYPES), \ - $(foreach image, $(IMAGE_TYPES), \ - $(eval ART_RUN_TEST_$(call name-to-var,$(target))_$(call name-to-var,$(image))_RULES :=))) -$(foreach target, $(TARGET_TYPES), \ - $(foreach test, $(TEST_ART_RUN_TESTS), \ - $(eval ART_RUN_TEST_$(call name-to-var,$(target))_$(call name-to-var,$(test))_RULES :=))) -$(foreach target, $(TARGET_TYPES), \ - $(foreach address_size, $(ALL_ADDRESS_SIZES), \ - $(eval ART_RUN_TEST_$(call name-to-var,$(target))_$(call name-to-var,$(address_size))_RULES :=))) -$(foreach target, $(TARGET_TYPES), \ - $(foreach run_type, $(RUN_TYPES), \ - $(eval ART_RUN_TEST_$(call name-to-var,$(target))_$(call name-to-var,$(run_type))_RULES :=))) -define-test-art-run-test-group := +test-art-run-test : test-art-host-run-test test-art-target-run-test + +host_prereq_rules := +target_prereq_rules := +core-image-dependencies := +name-to-var := +ART_TEST_HOST_RUN_TEST_DEPENDENCIES := +TEST_ART_TARGET_SYNC_DEPS := +define-test-art-host-or-target-run-test-group := TARGET_TYPES := -PREBUILD_TYPES := COMPILER_TYPES := -RELOCATE_TYPES := -TRACE_TYPES := -GC_TYPES := -JNI_TYPES := IMAGE_TYPES := -ADDRESS_SIZES_TARGET := -ADDRESS_SIZES_HOST := ALL_ADDRESS_SIZES := -RUN_TYPES := -DEBUGGABLE_TYPES := - LOCAL_PATH := diff --git a/test/DefaultMethods/IterableBase.java b/test/DefaultMethods/IterableBase.java new file mode 100644 index 0000000000..4cefdefb67 --- /dev/null +++ b/test/DefaultMethods/IterableBase.java @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2017 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. + */ + +interface Iface { + default void defaultMethod() { + } +} + +class Impl implements Iface { +} + +abstract class IterableBase implements Iterable { +} + diff --git a/test/testrunner/run_build_test_target.py b/test/testrunner/run_build_test_target.py index 4c519ae7f7..835b678cd6 100755 --- a/test/testrunner/run_build_test_target.py +++ b/test/testrunner/run_build_test_target.py @@ -46,7 +46,6 @@ custom_env['SOONG_ALLOW_MISSING_DEPENDENCIES'] = 'true' print custom_env os.environ.update(custom_env) - if target.get('target'): build_command = 'make' build_command += ' -j' + str(n_threads) @@ -56,7 +55,7 @@ if target.get('target'): if subprocess.call(build_command.split()): sys.exit(1) -else: +if target.get('run-tests'): run_test_command = [os.path.join(env.ANDROID_BUILD_TOP, 'art/test/testrunner/testrunner.py')] run_test_command += target.get('flags', []) diff --git a/test/testrunner/target_config.py b/test/testrunner/target_config.py index 1af2ae7a63..5a6ecffd44 100644 --- a/test/testrunner/target_config.py +++ b/test/testrunner/target_config.py @@ -1,29 +1,35 @@ target_config = { 'art-test' : { + 'target' : 'test-art-host-gtest', + 'run-tests' : True, 'flags' : [], 'env' : { 'ART_USE_READ_BARRIER' : 'false' } }, 'art-interpreter' : { + 'run-tests' : True, 'flags' : ['--interpreter'], 'env' : { 'ART_USE_READ_BARRIER' : 'false' } }, 'art-interpreter-access-checks' : { + 'run-tests' : True, 'flags' : ['--interp-ac'], 'env' : { 'ART_USE_READ_BARRIER' : 'false' } }, 'art-jit' : { + 'run-tests' : True, 'flags' : ['--jit'], 'env' : { 'ART_USE_READ_BARRIER' : 'false' } }, 'art-gcstress-gcverify': { + 'run-tests' : True, 'flags' : ['--gcstress', '--gcverify'], 'env' : { @@ -32,6 +38,7 @@ target_config = { } }, 'art-interpreter-gcstress' : { + 'run-tests' : True, 'flags': ['--interpreter', '--gcstress'], 'env' : { @@ -40,6 +47,7 @@ target_config = { } }, 'art-optimizing-gcstress' : { + 'run-tests' : True, 'flags': ['--gcstress', '--optimizing'], 'env' : { @@ -48,6 +56,7 @@ target_config = { } }, 'art-jit-gcstress' : { + 'run-tests' : True, 'flags': ['--jit', '--gcstress'], 'env' : { @@ -56,6 +65,7 @@ target_config = { } }, 'art-read-barrier' : { + 'run-tests' : True, 'flags': ['--interpreter', '--optimizing'], 'env' : { @@ -64,6 +74,7 @@ target_config = { } }, 'art-read-barrier-gcstress' : { + 'run-tests' : True, 'flags' : ['--interpreter', '--optimizing', '--gcstress'], @@ -73,6 +84,7 @@ target_config = { } }, 'art-read-barrier-table-lookup' : { + 'run-tests' : True, 'flags' : ['--interpreter', '--optimizing'], 'env' : { @@ -82,6 +94,7 @@ target_config = { } }, 'art-debug-gc' : { + 'run-tests' : True, 'flags' : ['--interpreter', '--optimizing'], 'env' : { @@ -90,6 +103,7 @@ target_config = { } }, 'art-ss-gc' : { + 'run-tests' : True, 'flags' : ['--interpreter', '--optimizing', '--jit'], @@ -99,6 +113,7 @@ target_config = { } }, 'art-gss-gc' : { + 'run-tests' : True, 'flags' : ['--interpreter', '--optimizing', '--jit'], @@ -108,6 +123,7 @@ target_config = { } }, 'art-ss-gc-tlab' : { + 'run-tests' : True, 'flags' : ['--interpreter', '--optimizing', '--jit'], @@ -118,6 +134,7 @@ target_config = { } }, 'art-gss-gc-tlab' : { + 'run-tests' : True, 'flags' : ['--interpreter', '--optimizing', '--jit'], @@ -128,12 +145,14 @@ target_config = { } }, 'art-tracing' : { + 'run-tests' : True, 'flags' : ['--trace'], 'env' : { 'ART_USE_READ_BARRIER' : 'false' } }, 'art-interpreter-tracing' : { + 'run-tests' : True, 'flags' : ['--interpreter', '--trace'], 'env' : { @@ -141,24 +160,28 @@ target_config = { } }, 'art-forcecopy' : { + 'run-tests' : True, 'flags' : ['--forcecopy'], 'env' : { 'ART_USE_READ_BARRIER' : 'false', } }, 'art-no-prebuild' : { + 'run-tests' : True, 'flags' : ['--no-prebuild'], 'env' : { 'ART_USE_READ_BARRIER' : 'false', } }, 'art-no-image' : { + 'run-tests' : True, 'flags' : ['--no-image'], 'env' : { 'ART_USE_READ_BARRIER' : 'false', } }, 'art-interpreter-no-image' : { + 'run-tests' : True, 'flags' : ['--interpreter', '--no-image'], 'env' : { @@ -166,18 +189,21 @@ target_config = { } }, 'art-relocate-no-patchoat' : { + 'run-tests' : True, 'flags' : ['--relocate-npatchoat'], 'env' : { 'ART_USE_READ_BARRIER' : 'false', } }, 'art-no-dex2oat' : { + 'run-tests' : True, 'flags' : ['--no-dex2oat'], 'env' : { 'ART_USE_READ_BARRIER' : 'false', } }, 'art-heap-poisoning' : { + 'run-tests' : True, 'flags' : ['--interpreter', '--optimizing'], 'env' : { diff --git a/test/testrunner/testrunner.py b/test/testrunner/testrunner.py index 0b9a6e6457..9b9997004b 100755 --- a/test/testrunner/testrunner.py +++ b/test/testrunner/testrunner.py @@ -48,6 +48,7 @@ import argparse import fnmatch import itertools import json +import multiprocessing import os import re import subprocess @@ -115,7 +116,7 @@ failed_tests = [] skipped_tests = [] # Flags -n_thread = 1 +n_thread = -1 test_count = 0 total_test_count = 0 verbose = False @@ -257,6 +258,13 @@ def setup_test_env(): ADDRESS_SIZES_TARGET['host'] = ADDRESS_SIZES_TARGET['host'].union(ADDRESS_SIZES) ADDRESS_SIZES_TARGET['target'] = ADDRESS_SIZES_TARGET['target'].union(ADDRESS_SIZES) + global n_thread + if n_thread is -1: + if 'target' in TARGET_TYPES: + n_thread = get_default_threads('target') + else: + n_thread = get_default_threads('host') + global semaphore semaphore = threading.Semaphore(n_thread) @@ -774,6 +782,15 @@ def setup_env_for_build_target(build_target, parser, options): return target_options +def get_default_threads(target): + if target is 'target': + adb_command = 'adb shell cat /sys/devices/system/cpu/present' + cpu_info_proc = subprocess.Popen(adb_command.split(), stdout=subprocess.PIPE) + cpu_info = cpu_info_proc.stdout.read() + return int(cpu_info.split('-')[1]) + else: + return multiprocessing.cpu_count() + def parse_option(): global verbose global dry_run @@ -908,9 +925,11 @@ def main(): if 'target' in TARGET_TYPES: build_targets += 'test-art-target-run-test-dependencies' build_command = 'make' - build_command += ' -j' + str(n_thread) + build_command += ' -j' build_command += ' -C ' + env.ANDROID_BUILD_TOP build_command += ' ' + build_targets + # Add 'dist' to avoid Jack issues b/36169180. + build_command += ' dist' if subprocess.call(build_command.split()): sys.exit(1) if user_requested_test: diff --git a/tools/golem/build-target.sh b/tools/golem/build-target.sh new file mode 100755 index 0000000000..8d8e2bbe6f --- /dev/null +++ b/tools/golem/build-target.sh @@ -0,0 +1,384 @@ +#!/bin/bash +# +# Copyright (C) 2017 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. + +if [[ ! -d art ]]; then + echo "Script needs to be run at the root of the android tree" + exit 1 +fi + +ALL_CONFIGS=(linux-ia32 linux-x64 linux-armv8 linux-armv7 android-armv8 android-armv7) + +usage() { + local config + local golem_target + + (cat << EOF + Usage: $(basename "${BASH_SOURCE[0]}") [--golem=<target>] --machine-type=MACHINE_TYPE + [--tarball[=<target>.tar.gz]] + + Build minimal art binaries required to run golem benchmarks either + locally or on the golem servers. + + Creates the \$MACHINE_TYPE binaries in your \$OUT_DIR, and if --tarball was specified, + it also tars the results of the build together into your <target.tar.gz> file. + -------------------------------------------------------- + Required Flags: + --machine-type=MT Specify the machine type that will be built. + + Optional Flags": + --golem=<target> Builds with identical commands that Golem servers use. + --tarball[=o.tgz] Tar/gz the results. File name defaults to <machine_type>.tar.gz + -j<num> Specify how many jobs to use for parallelism. + --help Print this help listing. + --showcommands Show commands as they are being executed. + --simulate Print commands only, don't execute commands. +EOF + ) | sed -e 's/^[[:space:]][[:space:]]//g' >&2 # Strip leading whitespace from heredoc. + + echo >&2 "Available machine types:" + for config in "${ALL_CONFIGS[@]}"; do + echo >&2 " $config" + done + + echo >&2 + echo >&2 "Available Golem targets:" + while IFS='' read -r golem_target; do + echo >&2 " $golem_target" + done < <("$(thisdir)/env" --list-targets) +} + +# Check if $1 element is in array $2 +contains_element() { + local e + for e in "${@:2}"; do [[ "$e" == "$1" ]] && return 0; done + return 1 +} + +# Display a command, but don't execute it, if --showcommands was set. +show_command() { + if [[ $showcommands == "showcommands" ]]; then + echo "$@" + fi +} + +# Execute a command, displaying it if --showcommands was set. +# If --simulate is used, command is not executed. +execute() { + show_command "$@" + execute_noshow "$@" +} + +# Execute a command unless --simulate was used. +execute_noshow() { + if [[ $simulate == "simulate" ]]; then + return 0 + fi + + local prog="$1" + shift + "$prog" "$@" +} + +# Export environment variable, echoing it to screen. +setenv() { + local name="$1" + local value="$2" + + export $name="$value" + echo export $name="$value" +} + +# Export environment variable, echoing $3 to screen ($3 is meant to be unevaluated). +setenv_escape() { + local name="$1" + local value="$2" + local escaped_value="$3" + + export $name="$value" + echo export $name="$escaped_value" +} + +log_usage_error() { + echo >&2 "ERROR: " "$@" + echo >&2 " See --help for the correct usage information." + exit 1 +} + +log_fatal() { + echo >&2 "FATAL: " "$@" + exit 2 +} + +# Get the directory of this script. +thisdir() { + (\cd "$(dirname "${BASH_SOURCE[0]}")" && pwd ) +} + +# Get the path to the top of the Android source tree. +gettop() { + if [[ "x$ANDROID_BUILD_TOP" != "x" ]]; then + echo "$ANDROID_BUILD_TOP"; + else + echo "$(thisdir)/../../.." + fi +} + +# Get a build variable from the Android build system. +get_build_var() { + local varname="$1" + + # include the desired target product/build-variant + # which won't be set in our env if neither we nor the user first executed + # source build/envsetup.sh (e.g. if simulating from a fresh shell). + local extras + [[ -n $target_product ]] && extras+=" TARGET_PRODUCT=$target_product" + [[ -n $target_build_variant ]] && extras+=" TARGET_BUILD_VARIANT=$target_build_variant" + + # call dumpvar-$name from the makefile system. + (\cd "$(gettop)"; + CALLED_FROM_SETUP=true BUILD_SYSTEM=build/core \ + command make --no-print-directory -f build/core/config.mk \ + $extras \ + dumpvar-$varname) +} + +# Defaults from command-line. + +mode="" # blank or 'golem' if --golem was specified. +golem_target="" # --golem=$golem_target +config="" # --machine-type=$config +j_arg="-j8" +showcommands="" +simulate="" +make_tarball="" +tarball="" + +# Parse command line arguments + +while [[ "$1" != "" ]]; do + case "$1" in + --help) + usage + exit 1 + ;; + --golem=*) + mode="golem" + golem_target="${1##--golem=}" + + if [[ "x$golem_target" == x ]]; then + log_usage_error "Missing --golem target type." + fi + + shift + ;; + --machine-type=*) + config="${1##--machine-type=}" + if ! contains_element "$config" "${ALL_CONFIGS[@]}"; then + log_usage_error "Invalid --machine-type value '$config'" + fi + shift + ;; + --tarball) + tarball="" # reuse the machine type name. + make_tarball="make_tarball" + shift + ;; + --tarball=*) + tarball="${1##--tarball=}" + make_tarball="make_tarball" + shift + ;; + -j*) + j_arg="$1" + shift + ;; + --showcommands) + showcommands="showcommands" + shift + ;; + --simulate) + simulate="simulate" + shift + ;; + *) + log_usage_error "Unknown options $1" + ;; + esac +done + +################################### +################################### +################################### + +if [[ -z $config ]]; then + log_usage_error "--machine-type option is required." +fi + +# --tarball defaults to the --machine-type value with .tar.gz. +tarball="${tarball:-$config.tar.gz}" + +target_product="$TARGET_PRODUCT" +target_build_variant="$TARGET_BUILD_VARIANT" + +# If not using --golem, use whatever the user had lunch'd prior to this script. +if [[ $mode == "golem" ]]; then + # This section is intended solely to be executed by a golem build server. + + target_build_variant=eng + case "$config" in + *-armv7) + target_product="arm_krait" + ;; + *-armv8) + target_product="armv8" + ;; + *) + target_product="sdk" + ;; + esac + + if [[ $target_product = arm* ]]; then + # If using the regular manifest, e.g. 'master' + # The lunch command for arm will assuredly fail because we don't have device/generic/art. + # + # Print a human-readable error message instead of trying to lunch and failing there. + if ! [[ -d "$(gettop)/device/generic/art" ]]; then + log_fatal "Missing device/generic/art directory. Perhaps try master-art repo manifest?\n" \ + " Cannot build ARM targets (arm_krait, armv8) for Golem." >&2 + fi + # We could try to keep on simulating but it seems brittle because we won't have the proper + # build variables to output the right strings. + fi + + # Get this particular target's environment variables (e.g. ART read barrier on/off). + source "$(thisdir)"/env "$golem_target" || exit 1 + + lunch_target="$target_product-$target_build_variant" + + execute 'source' build/envsetup.sh + # Build generic targets (as opposed to something specific like aosp_angler-eng). + execute lunch "$lunch_target" + setenv JACK_SERVER false + setenv_escape JACK_REPOSITORY "$PWD/prebuilts/sdk/tools/jacks" '$PWD/prebuilts/sdk/tools/jacks' + # Golem uses master-art repository which is missing a lot of other libraries. + setenv SOONG_ALLOW_MISSING_DEPENDENCIES true + # Golem may be missing tools such as javac from its path. + setenv_escape PATH "/usr/lib/jvm/java-8-openjdk-amd64/bin/:$PATH" '/usr/lib/jvm/java-8-openjdk-amd64/bin/:$PATH' +else + # Look up the default variables from the build system if they weren't set already. + [[ -z $target_product ]] && target_product="$(get_build_var TARGET_PRODUCT)" + [[ -z $target_build_variant ]] && target_build_variant="$(get_build_var TARGET_BUILD_VARIANT)" +fi + +# Defaults for all machine types. +make_target="build-art-target-golem" +out_dir="out/x86_64" +root_dir_var="PRODUCT_OUT" +strip_symbols=false +bit64_suffix="" +tar_directories=(system data/art-test) + +# Per-machine type overrides +if [[ $config == linux-arm* ]]; then + setenv ART_TARGET_LINUX true +fi + +case "$config" in + linux-ia32|linux-x64) + root_dir_var="HOST_OUT" + # Android strips target builds automatically, but not host builds. + strip_symbols=true + make_target="build-art-host-golem" + + if [[ $config == linux-ia32 ]]; then + out_dir="out/x86" + setenv HOST_PREFER_32_BIT true + else + bit64_suffix="64" + fi + + tar_directories=(bin framework usr lib${bit64_suffix}) + ;; + *-armv8) + bit64_suffix="64" + ;; + *-armv7) + ;; + *) + log_fatal "Unsupported machine-type '$config'" +esac + +# Golem benchmark run commands expect a certain $OUT_DIR to be set, +# so specify it here. +# +# Note: It is questionable if we want to customize this since users +# could alternatively probably use their own build directly (and forgo this script). +setenv OUT_DIR "$out_dir" +root_dir="$(get_build_var "$root_dir_var")" + +if [[ $mode == "golem" ]]; then + # For golem-style running only. + # Sets the DT_INTERP to this path in every .so we can run the + # non-system version of dalvikvm with our own copies of the dependencies (e.g. our own libc++). + if [[ $config == android-* ]]; then + # TODO: the linker can be relative to the binaries + # (which is what we do for linux-armv8 and linux-armv7) + golem_run_path="/data/local/tmp/runner/" + else + golem_run_path="" + fi + + # Only do this for target builds. Host doesn't need this. + if [[ $config == *-arm* ]]; then + setenv CUSTOM_TARGET_LINKER "${golem_run_path}${root_dir}/system/bin/linker${bit64_suffix}" + fi +fi + +# +# Main command execution below here. +# (everything prior to this just sets up environment variables, +# and maybe calls lunch). +# + +execute make "${j_arg}" "${make_target}" + +if $strip_symbols; then + # Further reduce size by stripping symbols. + execute_noshow strip $root_dir/bin/* || true + show_command strip $root_dir/bin/'*' '|| true' + execute_noshow strip $root_dir/lib${bit64_suffix}/'*' + show_command strip $root_dir/lib${bit64_suffix}/'*' +fi + +if [[ "$make_tarball" == "make_tarball" ]]; then + # Create a tarball which is required for the golem build resource. + # (In particular, each golem benchmark's run commands depend on a list of resource files + # in order to have all the files it needs to actually execute, + # and this tarball would satisfy that particular target+machine-type's requirements). + dirs_rooted=() + for tar_dir in "${tar_directories[@]}"; do + dirs_rooted+=("$root_dir/$tar_dir") + done + + execute tar -czf "${tarball}" "${dirs_rooted[@]}" --exclude .git --exclude .gitignore + tar_result=$? + if [[ $tar_result -ne 0 ]]; then + [[ -f $tarball ]] && rm $tarball + fi + + show_command '[[ $? -ne 0 ]] && rm' "$tarball" +fi + diff --git a/tools/golem/env b/tools/golem/env new file mode 100755 index 0000000000..187ba3a01f --- /dev/null +++ b/tools/golem/env @@ -0,0 +1,117 @@ +#!/bin/bash +# +# Copyright (C) 2017 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. + +# +# Export some environment variables used by ART's Android.mk/Android.bp +# build systems to configure ART [to use a different implementation]. +# +# Currently only varies on ART_USE_READ_BARRIER for a concurrent/non-concurrent +# flavor of the ART garbage collector. +# +# Only meant for golem use since when building ART directly, one can/should set +# these environment flags themselves. +# +# These environment flags are not really meant here to be for "correctness", +# but rather telling the ART C++ to use alternative algorithms. +# In other words, the same exact binary build with a different "target" +# should run in the same context (e.g. it does not change arch or the OS it's built for). +# + +setenv() { + local name="$1" + local value="$2" + + export $name="$value" + echo export $name="$value" +} + +# Enforce specified target-name is one of these. +# Perhaps we should be less strict? +ALL_TARGETS=(art-interpreter art-opt art-jit art-jit-cc art-opt-cc art-opt-debuggable art-vdex) + +usage() { + echo >&2 "Usage: $(basename $0) (--list-targets | <target-name>)" + echo >&2 + echo >&2 "Exports the necessary ART environment variables" + echo >&2 "to pass to the Golem build to correctly configure ART." + echo >&2 "--------------------------------------------------------" + echo >&2 "Required Arguments:" + echo >&2 " <target-name> Specify the golem target to get environment variables for." + echo >&2 + echo >&2 "Optional Flags": + echo >&2 " --list-targets Display all the targets. Do not require the main target-name." + echo >&2 " --help Print this help listing." + echo >&2 + echo >&2 "Available Targets:" + + list_targets 2 " " +} + +list_targets() { + local out_fd="${1:-1}" # defaults to 1 if no param was set + local prefix="$2" + + for target in "${ALL_TARGETS[@]}"; do + echo >&$out_fd "${prefix}${target}" + done +} + + +# Check if $1 element is in array $2 +contains_element() { + local e + for e in "${@:2}"; do [[ "$e" == "$1" ]] && return 0; done + return 1 +} + +main() { + if [[ $# -lt 1 ]]; then + usage + exit 1 + fi + + if [[ "$1" == "--help" ]]; then + usage + exit 1 + fi + + if [[ "$1" == "--list-targets" ]]; then + list_targets + exit 0 + fi + + local selected_target="$1" + if ! contains_element "$selected_target" "${ALL_TARGETS[@]}"; then + echo "ERROR: Invalid target value '$selected_target'" >&2 + exit 1 + fi + + case "$selected_target" in + *-cc) + setenv ART_USE_READ_BARRIER true + ;; + *) + setenv ART_USE_READ_BARRIER false + ;; + esac + + # Make smaller .tar.gz files by excluding debug targets. + setenv ART_BUILD_TARGET_DEBUG false + setenv ART_BUILD_HOST_DEBUG false + setenv USE_DEX2OAT_DEBUG false +} + +main "$@" |