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