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