diff options
Diffstat (limited to 'compiler')
| -rw-r--r-- | compiler/Android.mk | 1 | ||||
| -rw-r--r-- | compiler/dex/quick/dex_file_method_inliner.cc | 9 | ||||
| -rw-r--r-- | compiler/driver/compiler_driver-inl.h | 14 | ||||
| -rw-r--r-- | compiler/oat_writer.cc | 77 | ||||
| -rw-r--r-- | compiler/oat_writer.h | 15 | ||||
| -rw-r--r-- | compiler/optimizing/code_generator_mips.cc | 24 | ||||
| -rw-r--r-- | compiler/optimizing/constant_folding.cc | 53 | ||||
| -rw-r--r-- | compiler/optimizing/constant_folding_test.cc | 232 | ||||
| -rw-r--r-- | compiler/optimizing/induction_var_analysis.cc | 15 | ||||
| -rw-r--r-- | compiler/optimizing/intrinsics_mips.cc | 230 | ||||
| -rw-r--r-- | compiler/optimizing/intrinsics_mips.h | 83 | ||||
| -rw-r--r-- | compiler/optimizing/intrinsics_mips64.cc | 88 | ||||
| -rw-r--r-- | compiler/optimizing/nodes.cc | 11 | ||||
| -rw-r--r-- | compiler/optimizing/nodes.h | 6 |
14 files changed, 762 insertions, 96 deletions
diff --git a/compiler/Android.mk b/compiler/Android.mk index aaac126c4a..e74a68f608 100644 --- a/compiler/Android.mk +++ b/compiler/Android.mk @@ -155,6 +155,7 @@ LIBART_COMPILER_SRC_FILES_mips := \ dex/quick/mips/utility_mips.cc \ jni/quick/mips/calling_convention_mips.cc \ optimizing/code_generator_mips.cc \ + optimizing/intrinsics_mips.cc \ utils/mips/assembler_mips.cc \ utils/mips/managed_register_mips.cc \ diff --git a/compiler/dex/quick/dex_file_method_inliner.cc b/compiler/dex/quick/dex_file_method_inliner.cc index e1a2838f3e..eaf2408763 100644 --- a/compiler/dex/quick/dex_file_method_inliner.cc +++ b/compiler/dex/quick/dex_file_method_inliner.cc @@ -756,14 +756,7 @@ uint32_t DexFileMethodInliner::FindClassIndex(const DexFile* dex_file, IndexCach return *class_index; } - const DexFile::StringId* string_id = dex_file->FindStringId(kClassCacheNames[index]); - if (string_id == nullptr) { - *class_index = kIndexNotFound; - return *class_index; - } - uint32_t string_index = dex_file->GetIndexForStringId(*string_id); - - const DexFile::TypeId* type_id = dex_file->FindTypeId(string_index); + const DexFile::TypeId* type_id = dex_file->FindTypeId(kClassCacheNames[index]); if (type_id == nullptr) { *class_index = kIndexNotFound; return *class_index; diff --git a/compiler/driver/compiler_driver-inl.h b/compiler/driver/compiler_driver-inl.h index 1a7dbe3a9f..14ba81d193 100644 --- a/compiler/driver/compiler_driver-inl.h +++ b/compiler/driver/compiler_driver-inl.h @@ -187,15 +187,11 @@ inline std::pair<bool, bool> CompilerDriver::IsClassOfStaticMemberAvailableToRef // Search dex file for localized ssb index, may fail if member's class is a parent // of the class mentioned in the dex file and there is no dex cache entry. std::string temp; - const DexFile::StringId* string_id = - dex_file->FindStringId(resolved_member->GetDeclaringClass()->GetDescriptor(&temp)); - if (string_id != nullptr) { - const DexFile::TypeId* type_id = - dex_file->FindTypeId(dex_file->GetIndexForStringId(*string_id)); - if (type_id != nullptr) { - // medium path, needs check of static storage base being initialized - storage_idx = dex_file->GetIndexForTypeId(*type_id); - } + const DexFile::TypeId* type_id = + dex_file->FindTypeId(resolved_member->GetDeclaringClass()->GetDescriptor(&temp)); + if (type_id != nullptr) { + // medium path, needs check of static storage base being initialized + storage_idx = dex_file->GetIndexForTypeId(*type_id); } } if (storage_idx != DexFile::kDexNoIndex) { diff --git a/compiler/oat_writer.cc b/compiler/oat_writer.cc index dcb23bf1ec..c7b8884214 100644 --- a/compiler/oat_writer.cc +++ b/compiler/oat_writer.cc @@ -33,6 +33,7 @@ #include "driver/compiler_options.h" #include "gc/space/image_space.h" #include "gc/space/space.h" +#include "handle_scope-inl.h" #include "image_writer.h" #include "linker/relative_patcher.h" #include "mirror/array.h" @@ -44,7 +45,7 @@ #include "output_stream.h" #include "safe_map.h" #include "scoped_thread_state_change.h" -#include "handle_scope-inl.h" +#include "type_lookup_table.h" #include "utils/dex_cache_arrays_layout-inl.h" #include "verifier/method_verifier.h" @@ -107,6 +108,9 @@ OatWriter::OatWriter(const std::vector<const DexFile*>& dex_files, size_oat_class_status_(0), size_oat_class_method_bitmaps_(0), size_oat_class_method_offsets_(0), + size_oat_lookup_table_alignment_(0), + size_oat_lookup_table_offset_(0), + size_oat_lookup_table_(0), method_offset_map_() { CHECK(key_value_store != nullptr); @@ -129,6 +133,10 @@ OatWriter::OatWriter(const std::vector<const DexFile*>& dex_files, offset = InitDexFiles(offset); } { + TimingLogger::ScopedTiming split("InitLookupTables", timings); + offset = InitLookupTables(offset); + } + { TimingLogger::ScopedTiming split("InitOatClasses", timings); offset = InitOatClasses(offset); } @@ -322,7 +330,8 @@ class OatWriter::InitOatClassesMethodVisitor : public DexMethodVisitor { return true; } - bool VisitMethod(size_t class_def_method_index ATTRIBUTE_UNUSED, const ClassDataItemIterator& it) { + bool VisitMethod(size_t class_def_method_index ATTRIBUTE_UNUSED, + const ClassDataItemIterator& it) { // Fill in the compiled_methods_ array for methods that have a // CompiledMethod. We track the number of non-null entries in // num_non_null_compiled_methods_ since we only want to allocate @@ -1043,11 +1052,29 @@ size_t OatWriter::InitDexFiles(size_t offset) { oat_dex_files_[i]->dex_file_offset_ = offset; const DexFile* dex_file = (*dex_files_)[i]; + + // Initialize type lookup table + oat_dex_files_[i]->lookup_table_ = dex_file->GetTypeLookupTable(); + offset += dex_file->GetHeader().file_size_; } return offset; } +size_t OatWriter::InitLookupTables(size_t offset) { + for (OatDexFile* oat_dex_file : oat_dex_files_) { + if (oat_dex_file->lookup_table_ != nullptr) { + uint32_t aligned_offset = RoundUp(offset, 4); + oat_dex_file->lookup_table_offset_ = aligned_offset; + size_oat_lookup_table_alignment_ += aligned_offset - offset; + offset = aligned_offset + oat_dex_file->lookup_table_->RawDataLength(); + } else { + oat_dex_file->lookup_table_offset_ = 0; + } + } + return offset; +} + size_t OatWriter::InitOatClasses(size_t offset) { // calculate the offsets within OatDexFiles to OatClasses InitOatClassesMethodVisitor visitor(this, offset); @@ -1256,6 +1283,9 @@ bool OatWriter::WriteCode(OutputStream* out) { DO_STAT(size_oat_class_status_); DO_STAT(size_oat_class_method_bitmaps_); DO_STAT(size_oat_class_method_offsets_); + DO_STAT(size_oat_lookup_table_alignment_); + DO_STAT(size_oat_lookup_table_offset_); + DO_STAT(size_oat_lookup_table_); #undef DO_STAT VLOG(compiler) << "size_total=" << PrettySize(size_total) << " (" << size_total << "B)"; \ @@ -1309,6 +1339,9 @@ bool OatWriter::WriteTables(OutputStream* out, const size_t file_offset) { } size_dex_file_ += dex_file->GetHeader().file_size_; } + if (!WriteLookupTables(out, file_offset)) { + return false; + } for (size_t i = 0; i != oat_classes_.size(); ++i) { if (!oat_classes_[i]->Write(this, out, file_offset)) { PLOG(ERROR) << "Failed to write oat methods information to " << out->GetLocation(); @@ -1318,6 +1351,35 @@ bool OatWriter::WriteTables(OutputStream* out, const size_t file_offset) { return true; } +bool OatWriter::WriteLookupTables(OutputStream* out, const size_t file_offset) { + for (size_t i = 0; i < oat_dex_files_.size(); ++i) { + const uint32_t lookup_table_offset = oat_dex_files_[i]->lookup_table_offset_; + const TypeLookupTable* table = oat_dex_files_[i]->lookup_table_; + DCHECK_EQ(lookup_table_offset == 0, table == nullptr); + if (lookup_table_offset == 0) { + continue; + } + const uint32_t expected_offset = file_offset + lookup_table_offset; + off_t actual_offset = out->Seek(expected_offset, kSeekSet); + if (static_cast<uint32_t>(actual_offset) != expected_offset) { + const DexFile* dex_file = (*dex_files_)[i]; + PLOG(ERROR) << "Failed to seek to lookup table section. Actual: " << actual_offset + << " Expected: " << expected_offset << " File: " << dex_file->GetLocation(); + return false; + } + if (table != nullptr) { + if (!out->WriteFully(table->RawData(), table->RawDataLength())) { + const DexFile* dex_file = (*dex_files_)[i]; + PLOG(ERROR) << "Failed to write lookup table for " << dex_file->GetLocation() + << " to " << out->GetLocation(); + return false; + } + size_oat_lookup_table_ += table->RawDataLength(); + } + } + return true; +} + size_t OatWriter::WriteMaps(OutputStream* out, const size_t file_offset, size_t relative_offset) { #define VISIT(VisitorType) \ do { \ @@ -1425,6 +1487,7 @@ OatWriter::OatDexFile::OatDexFile(size_t offset, const DexFile& dex_file) { dex_file_location_data_ = reinterpret_cast<const uint8_t*>(location.data()); dex_file_location_checksum_ = dex_file.GetLocationChecksum(); dex_file_offset_ = 0; + lookup_table_offset_ = 0; methods_offsets_.resize(dex_file.NumClassDefs()); } @@ -1433,6 +1496,7 @@ size_t OatWriter::OatDexFile::SizeOf() const { + dex_file_location_size_ + sizeof(dex_file_location_checksum_) + sizeof(dex_file_offset_) + + sizeof(lookup_table_offset_) + (sizeof(methods_offsets_[0]) * methods_offsets_.size()); } @@ -1441,6 +1505,10 @@ void OatWriter::OatDexFile::UpdateChecksum(OatHeader* oat_header) const { oat_header->UpdateChecksum(dex_file_location_data_, dex_file_location_size_); oat_header->UpdateChecksum(&dex_file_location_checksum_, sizeof(dex_file_location_checksum_)); oat_header->UpdateChecksum(&dex_file_offset_, sizeof(dex_file_offset_)); + oat_header->UpdateChecksum(&lookup_table_offset_, sizeof(lookup_table_offset_)); + if (lookup_table_ != nullptr) { + oat_header->UpdateChecksum(lookup_table_->RawData(), lookup_table_->RawDataLength()); + } oat_header->UpdateChecksum(&methods_offsets_[0], sizeof(methods_offsets_[0]) * methods_offsets_.size()); } @@ -1469,6 +1537,11 @@ bool OatWriter::OatDexFile::Write(OatWriter* oat_writer, return false; } oat_writer->size_oat_dex_file_offset_ += sizeof(dex_file_offset_); + if (!out->WriteFully(&lookup_table_offset_, sizeof(lookup_table_offset_))) { + PLOG(ERROR) << "Failed to write lookup table offset to " << out->GetLocation(); + return false; + } + oat_writer->size_oat_lookup_table_offset_ += sizeof(lookup_table_offset_); if (!out->WriteFully(&methods_offsets_[0], sizeof(methods_offsets_[0]) * methods_offsets_.size())) { PLOG(ERROR) << "Failed to write methods offsets to " << out->GetLocation(); diff --git a/compiler/oat_writer.h b/compiler/oat_writer.h index d6cb65bd64..f2fe048174 100644 --- a/compiler/oat_writer.h +++ b/compiler/oat_writer.h @@ -24,8 +24,8 @@ #include "linker/relative_patcher.h" // For linker::RelativePatcherTargetProvider. #include "mem_map.h" #include "method_reference.h" -#include "oat.h" #include "mirror/class.h" +#include "oat.h" #include "safe_map.h" namespace art { @@ -36,6 +36,7 @@ class CompilerDriver; class ImageWriter; class OutputStream; class TimingLogger; +class TypeLookupTable; // OatHeader variable length with count of D OatDexFiles // @@ -49,6 +50,11 @@ class TimingLogger; // ... // Dex[D] // +// TypeLookupTable[0] one descriptor to class def index hash table for each OatDexFile. +// TypeLookupTable[1] +// ... +// TypeLookupTable[D] +// // OatClass[0] one variable sized OatClass for each of C DexFile::ClassDefs // OatClass[1] contains OatClass entries with class status, offsets to code, etc. // ... @@ -168,6 +174,7 @@ class OatWriter { size_t InitOatHeader(); size_t InitOatDexFiles(size_t offset); + size_t InitLookupTables(size_t offset); size_t InitDexFiles(size_t offset); size_t InitOatClasses(size_t offset); size_t InitOatMaps(size_t offset); @@ -177,6 +184,7 @@ class OatWriter { SHARED_REQUIRES(Locks::mutator_lock_); bool WriteTables(OutputStream* out, const size_t file_offset); + bool WriteLookupTables(OutputStream* out, const size_t file_offset); size_t WriteMaps(OutputStream* out, const size_t file_offset, size_t relative_offset); size_t WriteCode(OutputStream* out, const size_t file_offset, size_t relative_offset); size_t WriteCodeDexFiles(OutputStream* out, const size_t file_offset, size_t relative_offset); @@ -199,6 +207,8 @@ class OatWriter { const uint8_t* dex_file_location_data_; uint32_t dex_file_location_checksum_; uint32_t dex_file_offset_; + uint32_t lookup_table_offset_; + TypeLookupTable* lookup_table_; // Owned by the dex file. std::vector<uint32_t> methods_offsets_; private: @@ -333,6 +343,9 @@ class OatWriter { uint32_t size_oat_class_status_; uint32_t size_oat_class_method_bitmaps_; uint32_t size_oat_class_method_offsets_; + uint32_t size_oat_lookup_table_alignment_; + uint32_t size_oat_lookup_table_offset_; + uint32_t size_oat_lookup_table_; std::unique_ptr<linker::RelativePatcher> relative_patcher_; diff --git a/compiler/optimizing/code_generator_mips.cc b/compiler/optimizing/code_generator_mips.cc index e6b9273d24..29d08beb97 100644 --- a/compiler/optimizing/code_generator_mips.cc +++ b/compiler/optimizing/code_generator_mips.cc @@ -19,10 +19,12 @@ #include "arch/mips/entrypoints_direct_mips.h" #include "arch/mips/instruction_set_features_mips.h" #include "art_method.h" +#include "code_generator_utils.h" #include "entrypoints/quick/quick_entrypoints.h" #include "entrypoints/quick/quick_entrypoints_enum.h" #include "gc/accounting/card_table.h" #include "intrinsics.h" +#include "intrinsics_mips.h" #include "mirror/array-inl.h" #include "mirror/class-inl.h" #include "offsets.h" @@ -2933,7 +2935,11 @@ void InstructionCodeGeneratorMIPS::VisitInvokeInterface(HInvokeInterface* invoke } void LocationsBuilderMIPS::VisitInvokeVirtual(HInvokeVirtual* invoke) { - // TODO: intrinsic function. + IntrinsicLocationsBuilderMIPS intrinsic(codegen_); + if (intrinsic.TryDispatch(invoke)) { + return; + } + HandleInvoke(invoke); } @@ -2942,13 +2948,18 @@ void LocationsBuilderMIPS::VisitInvokeStaticOrDirect(HInvokeStaticOrDirect* invo // invokes must have been pruned by art::PrepareForRegisterAllocation. DCHECK(codegen_->IsBaseline() || !invoke->IsStaticWithExplicitClinitCheck()); - // TODO: intrinsic function. + IntrinsicLocationsBuilderMIPS intrinsic(codegen_); + if (intrinsic.TryDispatch(invoke)) { + return; + } + HandleInvoke(invoke); } -static bool TryGenerateIntrinsicCode(HInvoke* invoke, CodeGeneratorMIPS* codegen ATTRIBUTE_UNUSED) { +static bool TryGenerateIntrinsicCode(HInvoke* invoke, CodeGeneratorMIPS* codegen) { if (invoke->GetLocations()->Intrinsified()) { - // TODO: intrinsic function. + IntrinsicCodeGeneratorMIPS intrinsic(codegen); + intrinsic.Dispatch(invoke); return true; } return false; @@ -3088,7 +3099,10 @@ void InstructionCodeGeneratorMIPS::VisitInvokeStaticOrDirect(HInvokeStaticOrDire } void InstructionCodeGeneratorMIPS::VisitInvokeVirtual(HInvokeVirtual* invoke) { - // TODO: Try to generate intrinsics code. + if (TryGenerateIntrinsicCode(invoke, codegen_)) { + return; + } + LocationSummary* locations = invoke->GetLocations(); Location receiver = locations->InAt(0); Register temp = invoke->GetLocations()->GetTemp(0).AsRegister<Register>(); diff --git a/compiler/optimizing/constant_folding.cc b/compiler/optimizing/constant_folding.cc index e0aa4ff489..57452cc076 100644 --- a/compiler/optimizing/constant_folding.cc +++ b/compiler/optimizing/constant_folding.cc @@ -27,6 +27,11 @@ class InstructionWithAbsorbingInputSimplifier : public HGraphVisitor { private: void VisitShift(HBinaryOperation* shift); + void VisitAbove(HAbove* instruction) OVERRIDE; + void VisitAboveOrEqual(HAboveOrEqual* instruction) OVERRIDE; + void VisitBelow(HBelow* instruction) OVERRIDE; + void VisitBelowOrEqual(HBelowOrEqual* instruction) OVERRIDE; + void VisitAnd(HAnd* instruction) OVERRIDE; void VisitCompare(HCompare* instruction) OVERRIDE; void VisitMul(HMul* instruction) OVERRIDE; @@ -105,6 +110,54 @@ void InstructionWithAbsorbingInputSimplifier::VisitShift(HBinaryOperation* instr } } +void InstructionWithAbsorbingInputSimplifier::VisitAbove(HAbove* instruction) { + if (instruction->GetLeft()->IsConstant() && + instruction->GetLeft()->AsConstant()->IsZero()) { + // Replace code looking like + // ABOVE dst, 0, src // unsigned 0 > src is always false + // with + // CONSTANT false + instruction->ReplaceWith(GetGraph()->GetConstant(Primitive::kPrimBoolean, 0)); + instruction->GetBlock()->RemoveInstruction(instruction); + } +} + +void InstructionWithAbsorbingInputSimplifier::VisitAboveOrEqual(HAboveOrEqual* instruction) { + if (instruction->GetRight()->IsConstant() && + instruction->GetRight()->AsConstant()->IsZero()) { + // Replace code looking like + // ABOVE_OR_EQUAL dst, src, 0 // unsigned src >= 0 is always true + // with + // CONSTANT true + instruction->ReplaceWith(GetGraph()->GetConstant(Primitive::kPrimBoolean, 1)); + instruction->GetBlock()->RemoveInstruction(instruction); + } +} + +void InstructionWithAbsorbingInputSimplifier::VisitBelow(HBelow* instruction) { + if (instruction->GetRight()->IsConstant() && + instruction->GetRight()->AsConstant()->IsZero()) { + // Replace code looking like + // BELOW dst, src, 0 // unsigned src < 0 is always false + // with + // CONSTANT false + instruction->ReplaceWith(GetGraph()->GetConstant(Primitive::kPrimBoolean, 0)); + instruction->GetBlock()->RemoveInstruction(instruction); + } +} + +void InstructionWithAbsorbingInputSimplifier::VisitBelowOrEqual(HBelowOrEqual* instruction) { + if (instruction->GetLeft()->IsConstant() && + instruction->GetLeft()->AsConstant()->IsZero()) { + // Replace code looking like + // BELOW_OR_EQUAL dst, 0, src // unsigned 0 <= src is always true + // with + // CONSTANT true + instruction->ReplaceWith(GetGraph()->GetConstant(Primitive::kPrimBoolean, 1)); + instruction->GetBlock()->RemoveInstruction(instruction); + } +} + void InstructionWithAbsorbingInputSimplifier::VisitAnd(HAnd* instruction) { HConstant* input_cst = instruction->GetConstantRight(); if ((input_cst != nullptr) && input_cst->IsZero()) { diff --git a/compiler/optimizing/constant_folding_test.cc b/compiler/optimizing/constant_folding_test.cc index 2feb75cc9f..e469c8d6d0 100644 --- a/compiler/optimizing/constant_folding_test.cc +++ b/compiler/optimizing/constant_folding_test.cc @@ -29,50 +29,70 @@ namespace art { -static void TestCode(const uint16_t* data, - const std::string& expected_before, - const std::string& expected_after_cf, - const std::string& expected_after_dce, - std::function<void(HGraph*)> check_after_cf, - Primitive::Type return_type = Primitive::kPrimInt) { - ArenaPool pool; - ArenaAllocator allocator(&pool); - HGraph* graph = CreateCFG(&allocator, data, return_type); - ASSERT_NE(graph, nullptr); - - graph->TryBuildingSsa(); - - StringPrettyPrinter printer_before(graph); - printer_before.VisitInsertionOrder(); - std::string actual_before = printer_before.str(); - ASSERT_EQ(expected_before, actual_before); - - std::unique_ptr<const X86InstructionSetFeatures> features_x86( - X86InstructionSetFeatures::FromCppDefines()); - x86::CodeGeneratorX86 codegenX86(graph, *features_x86.get(), CompilerOptions()); - HConstantFolding(graph).Run(); - SSAChecker ssa_checker_cf(graph); - ssa_checker_cf.Run(); - ASSERT_TRUE(ssa_checker_cf.IsValid()); - - StringPrettyPrinter printer_after_cf(graph); - printer_after_cf.VisitInsertionOrder(); - std::string actual_after_cf = printer_after_cf.str(); - ASSERT_EQ(expected_after_cf, actual_after_cf); - - check_after_cf(graph); - - HDeadCodeElimination(graph).Run(); - SSAChecker ssa_checker_dce(graph); - ssa_checker_dce.Run(); - ASSERT_TRUE(ssa_checker_dce.IsValid()); - - StringPrettyPrinter printer_after_dce(graph); - printer_after_dce.VisitInsertionOrder(); - std::string actual_after_dce = printer_after_dce.str(); - ASSERT_EQ(expected_after_dce, actual_after_dce); -} - +/** + * Fixture class for the constant folding and dce tests. + */ +class ConstantFoldingTest : public testing::Test { + public: + ConstantFoldingTest() : pool_(), allocator_(&pool_) { + graph_ = CreateGraph(&allocator_); + } + + void TestCode(const uint16_t* data, + const std::string& expected_before, + const std::string& expected_after_cf, + const std::string& expected_after_dce, + std::function<void(HGraph*)> check_after_cf, + Primitive::Type return_type = Primitive::kPrimInt) { + graph_ = CreateCFG(&allocator_, data, return_type); + TestCodeOnReadyGraph(expected_before, + expected_after_cf, + expected_after_dce, + check_after_cf); + } + + void TestCodeOnReadyGraph(const std::string& expected_before, + const std::string& expected_after_cf, + const std::string& expected_after_dce, + std::function<void(HGraph*)> check_after_cf) { + ASSERT_NE(graph_, nullptr); + graph_->TryBuildingSsa(); + + StringPrettyPrinter printer_before(graph_); + printer_before.VisitInsertionOrder(); + std::string actual_before = printer_before.str(); + EXPECT_EQ(expected_before, actual_before); + + std::unique_ptr<const X86InstructionSetFeatures> features_x86( + X86InstructionSetFeatures::FromCppDefines()); + x86::CodeGeneratorX86 codegenX86(graph_, *features_x86.get(), CompilerOptions()); + HConstantFolding(graph_).Run(); + SSAChecker ssa_checker_cf(graph_); + ssa_checker_cf.Run(); + ASSERT_TRUE(ssa_checker_cf.IsValid()); + + StringPrettyPrinter printer_after_cf(graph_); + printer_after_cf.VisitInsertionOrder(); + std::string actual_after_cf = printer_after_cf.str(); + EXPECT_EQ(expected_after_cf, actual_after_cf); + + check_after_cf(graph_); + + HDeadCodeElimination(graph_).Run(); + SSAChecker ssa_checker_dce(graph_); + ssa_checker_dce.Run(); + ASSERT_TRUE(ssa_checker_dce.IsValid()); + + StringPrettyPrinter printer_after_dce(graph_); + printer_after_dce.VisitInsertionOrder(); + std::string actual_after_dce = printer_after_dce.str(); + EXPECT_EQ(expected_after_dce, actual_after_dce); + } + + ArenaPool pool_; + ArenaAllocator allocator_; + HGraph* graph_; +}; /** * Tiny three-register program exercising int constant folding on negation. @@ -84,7 +104,7 @@ static void TestCode(const uint16_t* data, * v1 <- -v0 1. neg-int v1, v0 * return v1 2. return v1 */ -TEST(ConstantFolding, IntConstantFoldingNegation) { +TEST_F(ConstantFoldingTest, IntConstantFoldingNegation) { const uint16_t data[] = TWO_REGISTERS_CODE_ITEM( Instruction::CONST_4 | 0 << 8 | 1 << 12, Instruction::NEG_INT | 1 << 8 | 0 << 12, @@ -141,7 +161,7 @@ TEST(ConstantFolding, IntConstantFoldingNegation) { * (v2, v3) <- -(v0, v1) 1. neg-long v2, v0 * return (v2, v3) 2. return-wide v2 */ -TEST(ConstantFolding, LongConstantFoldingNegation) { +TEST_F(ConstantFoldingTest, LongConstantFoldingNegation) { const int64_t input = INT64_C(4294967296); // 2^32 const uint16_t word0 = Low16Bits(Low32Bits(input)); // LSW. const uint16_t word1 = High16Bits(Low32Bits(input)); @@ -205,7 +225,7 @@ TEST(ConstantFolding, LongConstantFoldingNegation) { * v2 <- v0 + v1 2. add-int v2, v0, v1 * return v2 4. return v2 */ -TEST(ConstantFolding, IntConstantFoldingOnAddition1) { +TEST_F(ConstantFoldingTest, IntConstantFoldingOnAddition1) { const uint16_t data[] = THREE_REGISTERS_CODE_ITEM( Instruction::CONST_4 | 0 << 8 | 1 << 12, Instruction::CONST_4 | 1 << 8 | 2 << 12, @@ -271,7 +291,7 @@ TEST(ConstantFolding, IntConstantFoldingOnAddition1) { * v2 <- v0 + v1 6. add-int v2, v0, v1 * return v2 8. return v2 */ -TEST(ConstantFolding, IntConstantFoldingOnAddition2) { +TEST_F(ConstantFoldingTest, IntConstantFoldingOnAddition2) { const uint16_t data[] = THREE_REGISTERS_CODE_ITEM( Instruction::CONST_4 | 0 << 8 | 1 << 12, Instruction::CONST_4 | 1 << 8 | 2 << 12, @@ -357,7 +377,7 @@ TEST(ConstantFolding, IntConstantFoldingOnAddition2) { * v2 <- v0 - v1 2. sub-int v2, v0, v1 * return v2 4. return v2 */ -TEST(ConstantFolding, IntConstantFoldingOnSubtraction) { +TEST_F(ConstantFoldingTest, IntConstantFoldingOnSubtraction) { const uint16_t data[] = THREE_REGISTERS_CODE_ITEM( Instruction::CONST_4 | 0 << 8 | 3 << 12, Instruction::CONST_4 | 1 << 8 | 2 << 12, @@ -421,7 +441,7 @@ TEST(ConstantFolding, IntConstantFoldingOnSubtraction) { * (v0, v1) + (v1, v2) 4. add-long v4, v0, v2 * return (v4, v5) 6. return-wide v4 */ -TEST(ConstantFolding, LongConstantFoldingOnAddition) { +TEST_F(ConstantFoldingTest, LongConstantFoldingOnAddition) { const uint16_t data[] = SIX_REGISTERS_CODE_ITEM( Instruction::CONST_WIDE_16 | 0 << 8, 1, Instruction::CONST_WIDE_16 | 2 << 8, 2, @@ -486,7 +506,7 @@ TEST(ConstantFolding, LongConstantFoldingOnAddition) { * (v0, v1) - (v1, v2) 4. sub-long v4, v0, v2 * return (v4, v5) 6. return-wide v4 */ -TEST(ConstantFolding, LongConstantFoldingOnSubtraction) { +TEST_F(ConstantFoldingTest, LongConstantFoldingOnSubtraction) { const uint16_t data[] = SIX_REGISTERS_CODE_ITEM( Instruction::CONST_WIDE_16 | 0 << 8, 3, Instruction::CONST_WIDE_16 | 2 << 8, 2, @@ -560,7 +580,7 @@ TEST(ConstantFolding, LongConstantFoldingOnSubtraction) { * L3: v2 <- v1 + 8 11. add-int/lit16 v2, v1, #+8 * return v2 13. return v2 */ -TEST(ConstantFolding, IntConstantFoldingAndJumps) { +TEST_F(ConstantFoldingTest, IntConstantFoldingAndJumps) { const uint16_t data[] = THREE_REGISTERS_CODE_ITEM( Instruction::CONST_4 | 0 << 8 | 1 << 12, Instruction::CONST_4 | 1 << 8 | 2 << 12, @@ -656,7 +676,6 @@ TEST(ConstantFolding, IntConstantFoldingAndJumps) { check_after_cf); } - /** * Three-register program with a constant (static) condition. * @@ -670,7 +689,7 @@ TEST(ConstantFolding, IntConstantFoldingAndJumps) { * L1: v2 <- v0 + v1 5. add-int v2, v0, v1 * return-void 7. return */ -TEST(ConstantFolding, ConstantCondition) { +TEST_F(ConstantFoldingTest, ConstantCondition) { const uint16_t data[] = THREE_REGISTERS_CODE_ITEM( Instruction::CONST_4 | 1 << 8 | 1 << 12, Instruction::CONST_4 | 0 << 8 | 0 << 12, @@ -732,4 +751,109 @@ TEST(ConstantFolding, ConstantCondition) { check_after_cf); } +/** + * Unsigned comparisons with zero. Since these instructions are not present + * in the bytecode, we need to set up the graph explicitly. + */ +TEST_F(ConstantFoldingTest, UnsignedComparisonsWithZero) { + graph_ = CreateGraph(&allocator_); + HBasicBlock* entry_block = new (&allocator_) HBasicBlock(graph_); + graph_->AddBlock(entry_block); + graph_->SetEntryBlock(entry_block); + HBasicBlock* block = new (&allocator_) HBasicBlock(graph_); + graph_->AddBlock(block); + HBasicBlock* exit_block = new (&allocator_) HBasicBlock(graph_); + graph_->AddBlock(exit_block); + graph_->SetExitBlock(exit_block); + entry_block->AddSuccessor(block); + block->AddSuccessor(exit_block); + + // Make various unsigned comparisons with zero against a parameter. + HInstruction* parameter = new (&allocator_) HParameterValue( + graph_->GetDexFile(), 0, 0, Primitive::kPrimInt, true); + entry_block->AddInstruction(parameter); + HInstruction* zero = graph_->GetIntConstant(0); + HInstruction* last; + block->AddInstruction(last = new (&allocator_) HAbove(zero, parameter)); + block->AddInstruction(new (&allocator_) HDeoptimize(last, 0)); + block->AddInstruction(last = new (&allocator_) HAbove(parameter, zero)); + block->AddInstruction(new (&allocator_) HDeoptimize(last, 0)); + block->AddInstruction(last = new (&allocator_) HAboveOrEqual(zero, parameter)); + block->AddInstruction(new (&allocator_) HDeoptimize(last, 0)); + block->AddInstruction(last = new (&allocator_) HAboveOrEqual(parameter, zero)); + block->AddInstruction(new (&allocator_) HDeoptimize(last, 0)); + block->AddInstruction(last = new (&allocator_) HBelow(zero, parameter)); + block->AddInstruction(new (&allocator_) HDeoptimize(last, 0)); + block->AddInstruction(last = new (&allocator_) HBelow(parameter, zero)); + block->AddInstruction(new (&allocator_) HDeoptimize(last, 0)); + block->AddInstruction(last = new (&allocator_) HBelowOrEqual(zero, parameter)); + block->AddInstruction(new (&allocator_) HDeoptimize(last, 0)); + block->AddInstruction(last = new (&allocator_) HBelowOrEqual(parameter, zero)); + block->AddInstruction(new (&allocator_) HDeoptimize(last, 0)); + + entry_block->AddInstruction(new (&allocator_) HGoto()); + block->AddInstruction(new (&allocator_) HReturn(zero)); + exit_block->AddInstruction(new (&allocator_) HExit()); + + const std::string expected_before = + "BasicBlock 0, succ: 1\n" + " 0: ParameterValue [16, 14, 12, 10, 8, 6, 4, 2]\n" + " 1: IntConstant [19, 16, 14, 12, 10, 8, 6, 4, 2]\n" + " 18: Goto 1\n" + "BasicBlock 1, pred: 0, succ: 2\n" + " 2: Above(1, 0) [3]\n" + " 3: Deoptimize(2)\n" + " 4: Above(0, 1) [5]\n" + " 5: Deoptimize(4)\n" + " 6: AboveOrEqual(1, 0) [7]\n" + " 7: Deoptimize(6)\n" + " 8: AboveOrEqual(0, 1) [9]\n" + " 9: Deoptimize(8)\n" + " 10: Below(1, 0) [11]\n" + " 11: Deoptimize(10)\n" + " 12: Below(0, 1) [13]\n" + " 13: Deoptimize(12)\n" + " 14: BelowOrEqual(1, 0) [15]\n" + " 15: Deoptimize(14)\n" + " 16: BelowOrEqual(0, 1) [17]\n" + " 17: Deoptimize(16)\n" + " 19: Return(1)\n" + "BasicBlock 2, pred: 1\n" + " 20: Exit\n"; + + const std::string expected_after_cf = + "BasicBlock 0, succ: 1\n" + " 0: ParameterValue [16, 10, 6, 4]\n" + " 1: IntConstant [13, 3, 19, 16, 10, 6, 4]\n" + " 21: IntConstant [15, 9]\n" + " 18: Goto 1\n" + "BasicBlock 1, pred: 0, succ: 2\n" + " 3: Deoptimize(1)\n" + " 4: Above(0, 1) [5]\n" + " 5: Deoptimize(4)\n" + " 6: AboveOrEqual(1, 0) [7]\n" + " 7: Deoptimize(6)\n" + " 9: Deoptimize(21)\n" + " 10: Below(1, 0) [11]\n" + " 11: Deoptimize(10)\n" + " 13: Deoptimize(1)\n" + " 15: Deoptimize(21)\n" + " 16: BelowOrEqual(0, 1) [17]\n" + " 17: Deoptimize(16)\n" + " 19: Return(1)\n" + "BasicBlock 2, pred: 1\n" + " 20: Exit\n"; + + const std::string expected_after_dce = expected_after_cf; + + auto check_after_cf = [](HGraph* graph) { + CHECK(graph != nullptr); + }; + + TestCodeOnReadyGraph(expected_before, + expected_after_cf, + expected_after_dce, + check_after_cf); +} + } // namespace art diff --git a/compiler/optimizing/induction_var_analysis.cc b/compiler/optimizing/induction_var_analysis.cc index b6220fc133..fdf8cc9c1f 100644 --- a/compiler/optimizing/induction_var_analysis.cc +++ b/compiler/optimizing/induction_var_analysis.cc @@ -20,19 +20,6 @@ namespace art { /** - * Returns true if instruction is invariant within the given loop - * (instruction is not defined in same or more inner loop). - */ -static bool IsLoopInvariant(HLoopInformation* loop, HInstruction* instruction) { - HLoopInformation* other_loop = instruction->GetBlock()->GetLoopInformation(); - if (other_loop != loop && (other_loop == nullptr || !other_loop->IsIn(*loop))) { - DCHECK(instruction->GetBlock()->Dominates(loop->GetHeader())); - return true; - } - return false; -} - -/** * Since graph traversal may enter a SCC at any position, an initial representation may be rotated, * along dependences, viz. any of (a, b, c, d), (d, a, b, c) (c, d, a, b), (b, c, d, a) assuming * a chain of dependences (mutual independent items may occur in arbitrary order). For proper @@ -718,7 +705,7 @@ HInductionVarAnalysis::InductionInfo* HInductionVarAnalysis::LookupInfo(HLoopInf return loop_it->second; } } - if (IsLoopInvariant(loop, instruction)) { + if (loop->IsLoopInvariant(instruction, true)) { InductionInfo* info = CreateInvariantFetch(instruction); AssignInfo(loop, instruction, info); return info; diff --git a/compiler/optimizing/intrinsics_mips.cc b/compiler/optimizing/intrinsics_mips.cc new file mode 100644 index 0000000000..5efcf4eadf --- /dev/null +++ b/compiler/optimizing/intrinsics_mips.cc @@ -0,0 +1,230 @@ +/* + * 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 "intrinsics_mips.h" + +#include "arch/mips/instruction_set_features_mips.h" +#include "art_method.h" +#include "code_generator_mips.h" +#include "entrypoints/quick/quick_entrypoints.h" +#include "intrinsics.h" +#include "mirror/array-inl.h" +#include "mirror/string.h" +#include "thread.h" +#include "utils/mips/assembler_mips.h" +#include "utils/mips/constants_mips.h" + +namespace art { + +namespace mips { + +IntrinsicLocationsBuilderMIPS::IntrinsicLocationsBuilderMIPS(CodeGeneratorMIPS* codegen) + : arena_(codegen->GetGraph()->GetArena()) { +} + +MipsAssembler* IntrinsicCodeGeneratorMIPS::GetAssembler() { + return reinterpret_cast<MipsAssembler*>(codegen_->GetAssembler()); +} + +ArenaAllocator* IntrinsicCodeGeneratorMIPS::GetAllocator() { + return codegen_->GetGraph()->GetArena(); +} + +#define __ codegen->GetAssembler()-> + +static void MoveFromReturnRegister(Location trg, + Primitive::Type type, + CodeGeneratorMIPS* codegen) { + if (!trg.IsValid()) { + DCHECK_EQ(type, Primitive::kPrimVoid); + return; + } + + DCHECK_NE(type, Primitive::kPrimVoid); + + if (Primitive::IsIntegralType(type) || type == Primitive::kPrimNot) { + Register trg_reg = trg.AsRegister<Register>(); + if (trg_reg != V0) { + __ Move(V0, trg_reg); + } + } else { + FRegister trg_reg = trg.AsFpuRegister<FRegister>(); + if (trg_reg != F0) { + if (type == Primitive::kPrimFloat) { + __ MovS(F0, trg_reg); + } else { + __ MovD(F0, trg_reg); + } + } + } +} + +static void MoveArguments(HInvoke* invoke, CodeGeneratorMIPS* codegen) { + InvokeDexCallingConventionVisitorMIPS calling_convention_visitor; + IntrinsicVisitor::MoveArguments(invoke, codegen, &calling_convention_visitor); +} + +// Slow-path for fallback (calling the managed code to handle the +// intrinsic) in an intrinsified call. This will copy the arguments +// into the positions for a regular call. +// +// Note: The actual parameters are required to be in the locations +// given by the invoke's location summary. If an intrinsic +// modifies those locations before a slowpath call, they must be +// restored! +class IntrinsicSlowPathMIPS : public SlowPathCodeMIPS { + public: + explicit IntrinsicSlowPathMIPS(HInvoke* invoke) : invoke_(invoke) { } + + void EmitNativeCode(CodeGenerator* codegen_in) OVERRIDE { + CodeGeneratorMIPS* codegen = down_cast<CodeGeneratorMIPS*>(codegen_in); + + __ Bind(GetEntryLabel()); + + SaveLiveRegisters(codegen, invoke_->GetLocations()); + + MoveArguments(invoke_, codegen); + + if (invoke_->IsInvokeStaticOrDirect()) { + codegen->GenerateStaticOrDirectCall(invoke_->AsInvokeStaticOrDirect(), + Location::RegisterLocation(A0)); + codegen->RecordPcInfo(invoke_, invoke_->GetDexPc(), this); + } else { + UNIMPLEMENTED(FATAL) << "Non-direct intrinsic slow-path not yet implemented"; + UNREACHABLE(); + } + + // Copy the result back to the expected output. + Location out = invoke_->GetLocations()->Out(); + if (out.IsValid()) { + DCHECK(out.IsRegister()); // TODO: Replace this when we support output in memory. + DCHECK(!invoke_->GetLocations()->GetLiveRegisters()->ContainsCoreRegister(out.reg())); + MoveFromReturnRegister(out, invoke_->GetType(), codegen); + } + + RestoreLiveRegisters(codegen, invoke_->GetLocations()); + __ B(GetExitLabel()); + } + + const char* GetDescription() const OVERRIDE { return "IntrinsicSlowPathMIPS"; } + + private: + // The instruction where this slow path is happening. + HInvoke* const invoke_; + + DISALLOW_COPY_AND_ASSIGN(IntrinsicSlowPathMIPS); +}; + +#undef __ + +bool IntrinsicLocationsBuilderMIPS::TryDispatch(HInvoke* invoke) { + Dispatch(invoke); + LocationSummary* res = invoke->GetLocations(); + return res != nullptr && res->Intrinsified(); +} + +#define __ assembler-> + +// Unimplemented intrinsics. + +#define UNIMPLEMENTED_INTRINSIC(Name) \ +void IntrinsicLocationsBuilderMIPS::Visit ## Name(HInvoke* invoke ATTRIBUTE_UNUSED) { \ +} \ +void IntrinsicCodeGeneratorMIPS::Visit ## Name(HInvoke* invoke ATTRIBUTE_UNUSED) { \ +} + +UNIMPLEMENTED_INTRINSIC(IntegerReverse) +UNIMPLEMENTED_INTRINSIC(LongReverse) +UNIMPLEMENTED_INTRINSIC(ShortReverseBytes) +UNIMPLEMENTED_INTRINSIC(IntegerReverseBytes) +UNIMPLEMENTED_INTRINSIC(LongReverseBytes) +UNIMPLEMENTED_INTRINSIC(LongNumberOfLeadingZeros) +UNIMPLEMENTED_INTRINSIC(IntegerNumberOfLeadingZeros) +UNIMPLEMENTED_INTRINSIC(FloatIntBitsToFloat) +UNIMPLEMENTED_INTRINSIC(DoubleLongBitsToDouble) +UNIMPLEMENTED_INTRINSIC(FloatFloatToRawIntBits) +UNIMPLEMENTED_INTRINSIC(DoubleDoubleToRawLongBits) +UNIMPLEMENTED_INTRINSIC(MathAbsDouble) +UNIMPLEMENTED_INTRINSIC(MathAbsFloat) +UNIMPLEMENTED_INTRINSIC(MathAbsInt) +UNIMPLEMENTED_INTRINSIC(MathAbsLong) +UNIMPLEMENTED_INTRINSIC(MathMinDoubleDouble) +UNIMPLEMENTED_INTRINSIC(MathMinFloatFloat) +UNIMPLEMENTED_INTRINSIC(MathMaxDoubleDouble) +UNIMPLEMENTED_INTRINSIC(MathMaxFloatFloat) +UNIMPLEMENTED_INTRINSIC(MathMinIntInt) +UNIMPLEMENTED_INTRINSIC(MathMinLongLong) +UNIMPLEMENTED_INTRINSIC(MathMaxIntInt) +UNIMPLEMENTED_INTRINSIC(MathMaxLongLong) +UNIMPLEMENTED_INTRINSIC(MathSqrt) +UNIMPLEMENTED_INTRINSIC(MathCeil) +UNIMPLEMENTED_INTRINSIC(MathFloor) +UNIMPLEMENTED_INTRINSIC(MathRint) +UNIMPLEMENTED_INTRINSIC(MathRoundDouble) +UNIMPLEMENTED_INTRINSIC(MathRoundFloat) +UNIMPLEMENTED_INTRINSIC(MemoryPeekByte) +UNIMPLEMENTED_INTRINSIC(MemoryPeekIntNative) +UNIMPLEMENTED_INTRINSIC(MemoryPeekLongNative) +UNIMPLEMENTED_INTRINSIC(MemoryPeekShortNative) +UNIMPLEMENTED_INTRINSIC(MemoryPokeByte) +UNIMPLEMENTED_INTRINSIC(MemoryPokeIntNative) +UNIMPLEMENTED_INTRINSIC(MemoryPokeLongNative) +UNIMPLEMENTED_INTRINSIC(MemoryPokeShortNative) +UNIMPLEMENTED_INTRINSIC(ThreadCurrentThread) +UNIMPLEMENTED_INTRINSIC(UnsafeGet) +UNIMPLEMENTED_INTRINSIC(UnsafeGetVolatile) +UNIMPLEMENTED_INTRINSIC(UnsafeGetLong) +UNIMPLEMENTED_INTRINSIC(UnsafeGetLongVolatile) +UNIMPLEMENTED_INTRINSIC(UnsafeGetObject) +UNIMPLEMENTED_INTRINSIC(UnsafeGetObjectVolatile) +UNIMPLEMENTED_INTRINSIC(UnsafePut) +UNIMPLEMENTED_INTRINSIC(UnsafePutOrdered) +UNIMPLEMENTED_INTRINSIC(UnsafePutVolatile) +UNIMPLEMENTED_INTRINSIC(UnsafePutObject) +UNIMPLEMENTED_INTRINSIC(UnsafePutObjectOrdered) +UNIMPLEMENTED_INTRINSIC(UnsafePutObjectVolatile) +UNIMPLEMENTED_INTRINSIC(UnsafePutLong) +UNIMPLEMENTED_INTRINSIC(UnsafePutLongOrdered) +UNIMPLEMENTED_INTRINSIC(UnsafePutLongVolatile) +UNIMPLEMENTED_INTRINSIC(UnsafeCASInt) +UNIMPLEMENTED_INTRINSIC(UnsafeCASLong) +UNIMPLEMENTED_INTRINSIC(UnsafeCASObject) +UNIMPLEMENTED_INTRINSIC(StringCharAt) +UNIMPLEMENTED_INTRINSIC(StringCompareTo) +UNIMPLEMENTED_INTRINSIC(StringEquals) +UNIMPLEMENTED_INTRINSIC(StringIndexOf) +UNIMPLEMENTED_INTRINSIC(StringIndexOfAfter) +UNIMPLEMENTED_INTRINSIC(StringNewStringFromBytes) +UNIMPLEMENTED_INTRINSIC(StringNewStringFromChars) +UNIMPLEMENTED_INTRINSIC(StringNewStringFromString) +UNIMPLEMENTED_INTRINSIC(LongRotateLeft) +UNIMPLEMENTED_INTRINSIC(LongRotateRight) +UNIMPLEMENTED_INTRINSIC(LongNumberOfTrailingZeros) +UNIMPLEMENTED_INTRINSIC(IntegerRotateLeft) +UNIMPLEMENTED_INTRINSIC(IntegerRotateRight) +UNIMPLEMENTED_INTRINSIC(IntegerNumberOfTrailingZeros) + +UNIMPLEMENTED_INTRINSIC(ReferenceGetReferent) +UNIMPLEMENTED_INTRINSIC(StringGetCharsNoCheck) +UNIMPLEMENTED_INTRINSIC(SystemArrayCopyChar) +UNIMPLEMENTED_INTRINSIC(SystemArrayCopy) + +#undef UNIMPLEMENTED_INTRINSIC + +#undef __ + +} // namespace mips +} // namespace art diff --git a/compiler/optimizing/intrinsics_mips.h b/compiler/optimizing/intrinsics_mips.h new file mode 100644 index 0000000000..c71b3c68b7 --- /dev/null +++ b/compiler/optimizing/intrinsics_mips.h @@ -0,0 +1,83 @@ +/* + * 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. + */ + +#ifndef ART_COMPILER_OPTIMIZING_INTRINSICS_MIPS_H_ +#define ART_COMPILER_OPTIMIZING_INTRINSICS_MIPS_H_ + +#include "intrinsics.h" + +namespace art { + +class ArenaAllocator; +class HInvokeStaticOrDirect; +class HInvokeVirtual; + +namespace mips { + +class CodeGeneratorMIPS; +class MipsAssembler; + +class IntrinsicLocationsBuilderMIPS FINAL : public IntrinsicVisitor { + public: + explicit IntrinsicLocationsBuilderMIPS(CodeGeneratorMIPS* codegen); + + // Define visitor methods. + +#define OPTIMIZING_INTRINSICS(Name, IsStatic, NeedsEnvironmentOrCache) \ + void Visit ## Name(HInvoke* invoke) OVERRIDE; +#include "intrinsics_list.h" +INTRINSICS_LIST(OPTIMIZING_INTRINSICS) +#undef INTRINSICS_LIST +#undef OPTIMIZING_INTRINSICS + + // Check whether an invoke is an intrinsic, and if so, create a location summary. Returns whether + // a corresponding LocationSummary with the intrinsified_ flag set was generated and attached to + // the invoke. + bool TryDispatch(HInvoke* invoke); + + private: + ArenaAllocator* arena_; + + DISALLOW_COPY_AND_ASSIGN(IntrinsicLocationsBuilderMIPS); +}; + +class IntrinsicCodeGeneratorMIPS FINAL : public IntrinsicVisitor { + public: + explicit IntrinsicCodeGeneratorMIPS(CodeGeneratorMIPS* codegen) : codegen_(codegen) {} + + // Define visitor methods. + +#define OPTIMIZING_INTRINSICS(Name, IsStatic, NeedsEnvironmentOrCache) \ + void Visit ## Name(HInvoke* invoke) OVERRIDE; +#include "intrinsics_list.h" +INTRINSICS_LIST(OPTIMIZING_INTRINSICS) +#undef INTRINSICS_LIST +#undef OPTIMIZING_INTRINSICS + + private: + MipsAssembler* GetAssembler(); + + ArenaAllocator* GetAllocator(); + + CodeGeneratorMIPS* codegen_; + + DISALLOW_COPY_AND_ASSIGN(IntrinsicCodeGeneratorMIPS); +}; + +} // namespace mips +} // namespace art + +#endif // ART_COMPILER_OPTIMIZING_INTRINSICS_MIPS_H_ diff --git a/compiler/optimizing/intrinsics_mips64.cc b/compiler/optimizing/intrinsics_mips64.cc index 0ab0b80396..05c7eb02d9 100644 --- a/compiler/optimizing/intrinsics_mips64.cc +++ b/compiler/optimizing/intrinsics_mips64.cc @@ -1227,6 +1227,91 @@ void IntrinsicCodeGeneratorMIPS64::VisitUnsafePutLongVolatile(HInvoke* invoke) { GenUnsafePut(invoke->GetLocations(), Primitive::kPrimLong, true, false, codegen_); } +static void CreateIntIntIntIntIntToInt(ArenaAllocator* arena, HInvoke* invoke) { + LocationSummary* locations = new (arena) LocationSummary(invoke, + LocationSummary::kNoCall, + kIntrinsified); + locations->SetInAt(0, Location::NoLocation()); // Unused receiver. + locations->SetInAt(1, Location::RequiresRegister()); + locations->SetInAt(2, Location::RequiresRegister()); + locations->SetInAt(3, Location::RequiresRegister()); + locations->SetInAt(4, Location::RequiresRegister()); + + locations->SetOut(Location::RequiresRegister()); +} + +static void GenCas(LocationSummary* locations, Primitive::Type type, CodeGeneratorMIPS64* codegen) { + Mips64Assembler* assembler = codegen->GetAssembler(); + GpuRegister base = locations->InAt(1).AsRegister<GpuRegister>(); + GpuRegister offset = locations->InAt(2).AsRegister<GpuRegister>(); + GpuRegister expected = locations->InAt(3).AsRegister<GpuRegister>(); + GpuRegister value = locations->InAt(4).AsRegister<GpuRegister>(); + GpuRegister out = locations->Out().AsRegister<GpuRegister>(); + + DCHECK_NE(base, out); + DCHECK_NE(offset, out); + DCHECK_NE(expected, out); + + // do { + // tmp_value = [tmp_ptr] - expected; + // } while (tmp_value == 0 && failure([tmp_ptr] <- r_new_value)); + // result = tmp_value != 0; + + Label loop_head, exit_loop; + __ Daddu(TMP, base, offset); + __ Sync(0); + __ Bind(&loop_head); + if (type == Primitive::kPrimLong) { + __ Lld(out, TMP); + } else { + __ Ll(out, TMP); + } + __ Dsubu(out, out, expected); // If we didn't get the 'expected' + __ Sltiu(out, out, 1); // value, set 'out' to false, and + __ Beqzc(out, &exit_loop); // return. + __ Move(out, value); // Use 'out' for the 'store conditional' instruction. + // If we use 'value' directly, we would lose 'value' + // in the case that the store fails. Whether the + // store succeeds, or fails, it will load the + // correct boolean value into the 'out' register. + if (type == Primitive::kPrimLong) { + __ Scd(out, TMP); + } else { + __ Sc(out, TMP); + } + __ Beqzc(out, &loop_head); // If we couldn't do the read-modify-write + // cycle atomically then retry. + __ Bind(&exit_loop); + __ Sync(0); +} + +// boolean sun.misc.Unsafe.compareAndSwapInt(Object o, long offset, int expected, int x) +void IntrinsicLocationsBuilderMIPS64::VisitUnsafeCASInt(HInvoke* invoke) { + CreateIntIntIntIntIntToInt(arena_, invoke); +} + +void IntrinsicCodeGeneratorMIPS64::VisitUnsafeCASInt(HInvoke* invoke) { + GenCas(invoke->GetLocations(), Primitive::kPrimInt, codegen_); +} + +// boolean sun.misc.Unsafe.compareAndSwapLong(Object o, long offset, long expected, long x) +void IntrinsicLocationsBuilderMIPS64::VisitUnsafeCASLong(HInvoke* invoke) { + CreateIntIntIntIntIntToInt(arena_, invoke); +} + +void IntrinsicCodeGeneratorMIPS64::VisitUnsafeCASLong(HInvoke* invoke) { + GenCas(invoke->GetLocations(), Primitive::kPrimLong, codegen_); +} + +// boolean sun.misc.Unsafe.compareAndSwapObject(Object o, long offset, Object expected, Object x) +void IntrinsicLocationsBuilderMIPS64::VisitUnsafeCASObject(HInvoke* invoke) { + CreateIntIntIntIntIntToInt(arena_, invoke); +} + +void IntrinsicCodeGeneratorMIPS64::VisitUnsafeCASObject(HInvoke* invoke) { + GenCas(invoke->GetLocations(), Primitive::kPrimNot, codegen_); +} + // char java.lang.String.charAt(int index) void IntrinsicLocationsBuilderMIPS64::VisitStringCharAt(HInvoke* invoke) { LocationSummary* locations = new (arena_) LocationSummary(invoke, @@ -1502,9 +1587,6 @@ void IntrinsicCodeGeneratorMIPS64::Visit ## Name(HInvoke* invoke ATTRIBUTE_UNUSE UNIMPLEMENTED_INTRINSIC(MathRoundDouble) UNIMPLEMENTED_INTRINSIC(MathRoundFloat) -UNIMPLEMENTED_INTRINSIC(UnsafeCASInt) -UNIMPLEMENTED_INTRINSIC(UnsafeCASLong) -UNIMPLEMENTED_INTRINSIC(UnsafeCASObject) UNIMPLEMENTED_INTRINSIC(StringEquals) UNIMPLEMENTED_INTRINSIC(ReferenceGetReferent) diff --git a/compiler/optimizing/nodes.cc b/compiler/optimizing/nodes.cc index 348026551e..8b28ff91d4 100644 --- a/compiler/optimizing/nodes.cc +++ b/compiler/optimizing/nodes.cc @@ -574,6 +574,17 @@ bool HLoopInformation::IsIn(const HLoopInformation& other) const { return other.blocks_.IsBitSet(header_->GetBlockId()); } +bool HLoopInformation::IsLoopInvariant(HInstruction* instruction, bool must_dominate) const { + HLoopInformation* other_loop = instruction->GetBlock()->GetLoopInformation(); + if (other_loop != this && (other_loop == nullptr || !other_loop->IsIn(*this))) { + if (must_dominate) { + return instruction->GetBlock()->Dominates(GetHeader()); + } + return true; + } + return false; +} + size_t HLoopInformation::GetLifetimeEnd() const { size_t last_position = 0; for (HBasicBlock* back_edge : GetBackEdges()) { diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h index 6028d4b6fa..7df586692b 100644 --- a/compiler/optimizing/nodes.h +++ b/compiler/optimizing/nodes.h @@ -551,6 +551,12 @@ class HLoopInformation : public ArenaObject<kArenaAllocLoopInfo> { // Note that `other` *must* be populated before entering this function. bool IsIn(const HLoopInformation& other) const; + // Returns true if instruction is not defined within this loop or any loop nested inside + // this loop. If must_dominate is set, only definitions that actually dominate the loop + // header can be invariant. Otherwise, any definition outside the loop, including + // definitions that appear after the loop, is invariant. + bool IsLoopInvariant(HInstruction* instruction, bool must_dominate) const; + const ArenaBitVector& GetBlocks() const { return blocks_; } void Add(HBasicBlock* block); |