diff options
| -rw-r--r-- | build/Android.gtest.mk | 3 | ||||
| -rw-r--r-- | compiler/image_test.cc | 91 | ||||
| -rw-r--r-- | compiler/oat_writer.cc | 98 | ||||
| -rw-r--r-- | compiler/optimizing/code_generator_arm.cc | 78 | ||||
| -rw-r--r-- | compiler/optimizing/code_generator_arm.h | 2 | ||||
| -rw-r--r-- | compiler/optimizing/code_generator_arm_vixl.cc | 72 | ||||
| -rw-r--r-- | compiler/optimizing/intrinsics_arm.cc | 53 | ||||
| -rw-r--r-- | compiler/optimizing/intrinsics_arm_vixl.cc | 87 | ||||
| -rw-r--r-- | dex2oat/Android.bp | 8 | ||||
| -rw-r--r-- | dex2oat/dex2oat.cc | 72 | ||||
| -rw-r--r-- | dex2oat/dex2oat_test.cc | 67 | ||||
| -rw-r--r-- | dex2oat/include/dex2oat_return_codes.h | 32 | ||||
| -rw-r--r-- | runtime/class_linker.cc | 26 | ||||
| -rw-r--r-- | runtime/thread.cc | 17 | ||||
| -rw-r--r-- | runtime/well_known_classes.cc | 2 | ||||
| -rw-r--r-- | runtime/well_known_classes.h | 1 | ||||
| -rw-r--r-- | test/Android.run-test.mk | 4 | ||||
| -rw-r--r-- | test/DefaultMethods/IterableBase.java | 27 | ||||
| -rw-r--r-- | test/knownfailures.json | 11 | ||||
| -rwxr-xr-x | test/testrunner/run_build_test_target.py | 3 | ||||
| -rw-r--r-- | test/testrunner/target_config.py | 26 | ||||
| -rwxr-xr-x | tools/golem/build-target.sh | 384 | ||||
| -rwxr-xr-x | tools/golem/env | 117 |
23 files changed, 1095 insertions, 186 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/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/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/runtime/class_linker.cc b/runtime/class_linker.cc index 746cace8a6..fa87c8ce25 100644 --- a/runtime/class_linker.cc +++ b/runtime/class_linker.cc @@ -6732,10 +6732,11 @@ static void CheckClassOwnsVTableEntries(Thread* self, auto is_same_method = [m] (const ArtMethod& meth) { return &meth == m; }; - CHECK((super_vtable_length > i && superclass->GetVTableEntry(i, pointer_size) == m) || - std::find_if(virtuals.begin(), virtuals.end(), is_same_method) != virtuals.end()) - << m->PrettyMethod() << " does not seem to be owned by current class " - << klass->PrettyClass() << " or any of its superclasses!"; + if (!((super_vtable_length > i && superclass->GetVTableEntry(i, pointer_size) == m) || + std::find_if(virtuals.begin(), virtuals.end(), is_same_method) != virtuals.end())) { + LOG(WARNING) << m->PrettyMethod() << " does not seem to be owned by current class " + << klass->PrettyClass() << " or any of its superclasses!"; + } } } @@ -6763,14 +6764,15 @@ static void CheckVTableHasNoDuplicates(Thread* self, other_entry->GetAccessFlags())) { continue; } - CHECK(vtable_entry != other_entry && - !name_comparator.HasSameNameAndSignature( - other_entry->GetInterfaceMethodIfProxy(pointer_size))) - << "vtable entries " << i << " and " << j << " are identical for " - << klass->PrettyClass() << " in method " << vtable_entry->PrettyMethod() << " (0x" - << std::hex << reinterpret_cast<uintptr_t>(vtable_entry) << ") and " - << other_entry->PrettyMethod() << " (0x" << std::hex - << reinterpret_cast<uintptr_t>(other_entry) << ")"; + if (vtable_entry == other_entry || + name_comparator.HasSameNameAndSignature( + other_entry->GetInterfaceMethodIfProxy(pointer_size))) { + LOG(WARNING) << "vtable entries " << i << " and " << j << " are identical for " + << klass->PrettyClass() << " in method " << vtable_entry->PrettyMethod() + << " (0x" << std::hex << reinterpret_cast<uintptr_t>(vtable_entry) << ") and " + << other_entry->PrettyMethod() << " (0x" << std::hex + << reinterpret_cast<uintptr_t>(other_entry) << ")"; + } } } } 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/runtime/well_known_classes.cc b/runtime/well_known_classes.cc index 54cce98e8c..5aef062728 100644 --- a/runtime/well_known_classes.cc +++ b/runtime/well_known_classes.cc @@ -34,7 +34,6 @@ namespace art { -jclass WellKnownClasses::com_android_dex_Dex; jclass WellKnownClasses::dalvik_annotation_optimization_CriticalNative; jclass WellKnownClasses::dalvik_annotation_optimization_FastNative; jclass WellKnownClasses::dalvik_system_BaseDexClassLoader; @@ -267,7 +266,6 @@ uint32_t WellKnownClasses::StringInitToEntryPoint(ArtMethod* string_init) { #undef STRING_INIT_LIST void WellKnownClasses::Init(JNIEnv* env) { - com_android_dex_Dex = CacheClass(env, "com/android/dex/Dex"); dalvik_annotation_optimization_CriticalNative = CacheClass(env, "dalvik/annotation/optimization/CriticalNative"); dalvik_annotation_optimization_FastNative = CacheClass(env, "dalvik/annotation/optimization/FastNative"); diff --git a/runtime/well_known_classes.h b/runtime/well_known_classes.h index af4dbbf076..c18473197b 100644 --- a/runtime/well_known_classes.h +++ b/runtime/well_known_classes.h @@ -44,7 +44,6 @@ struct WellKnownClasses { static ObjPtr<mirror::Class> ToClass(jclass global_jclass) REQUIRES_SHARED(Locks::mutator_lock_); - static jclass com_android_dex_Dex; static jclass dalvik_annotation_optimization_CriticalNative; static jclass dalvik_annotation_optimization_FastNative; static jclass dalvik_system_BaseDexClassLoader; diff --git a/test/Android.run-test.mk b/test/Android.run-test.mk index 9a3bd886eb..2b57de679c 100644 --- a/test/Android.run-test.mk +++ b/test/Android.run-test.mk @@ -446,12 +446,16 @@ TEST_ART_BROKEN_FALLBACK_RUN_TESTS := \ # 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 \ 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/knownfailures.json b/test/knownfailures.json index e03bd68463..fd2a3178aa 100644 --- a/test/knownfailures.json +++ b/test/knownfailures.json @@ -339,11 +339,17 @@ { "tests": ["476-clinit-inline-static-invoke", "496-checker-inlining-class-loader", + "508-referrer-method", "637-checker-throw-inline"], "bug": "http://b/36365552", "variant": "no-image & jit" }, { + "tests": ["597-deopt-new-string"], + "bug": "http://b/36467228", + "variant": "no-image & jit" + }, + { "tests": ["530-checker-lse", "530-checker-lse2", "030-bad-finalizer", @@ -357,4 +363,9 @@ "arrives."], "bug": "http://b/36371709" }, + { + "tests": "080-oom-throw", + "bug": "http://b/36501991", + "variant": "interpreter & target" + } ] 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/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 "$@" |