diff options
Diffstat (limited to 'compiler')
| -rw-r--r-- | compiler/common_compiler_test.cc | 9 | ||||
| -rw-r--r-- | compiler/common_compiler_test.h | 2 | ||||
| -rw-r--r-- | compiler/dex/quick/arm64/int_arm64.cc | 1 | ||||
| -rw-r--r-- | compiler/image_writer.cc | 6 | ||||
| -rw-r--r-- | compiler/oat_test.cc | 4 | ||||
| -rw-r--r-- | compiler/oat_writer.cc | 32 | ||||
| -rw-r--r-- | compiler/optimizing/code_generator_x86_64.cc | 2 | ||||
| -rw-r--r-- | compiler/optimizing/graph_checker.cc | 68 | ||||
| -rw-r--r-- | compiler/optimizing/graph_checker.h | 18 | ||||
| -rw-r--r-- | compiler/optimizing/graph_visualizer.h | 1 | ||||
| -rw-r--r-- | compiler/optimizing/gvn.h | 21 | ||||
| -rw-r--r-- | compiler/optimizing/live_ranges_test.cc | 10 | ||||
| -rw-r--r-- | compiler/optimizing/optimization.cc | 6 | ||||
| -rw-r--r-- | compiler/optimizing/optimization.h | 1 | ||||
| -rw-r--r-- | compiler/optimizing/optimizing_compiler.cc | 24 | ||||
| -rw-r--r-- | compiler/optimizing/ssa_builder.cc | 14 | ||||
| -rw-r--r-- | compiler/optimizing/ssa_builder.h | 15 | ||||
| -rw-r--r-- | compiler/optimizing/ssa_phi_elimination.cc | 8 | ||||
| -rw-r--r-- | compiler/optimizing/ssa_test.cc | 36 | ||||
| -rw-r--r-- | compiler/utils/arm/assembler_arm.cc | 3 |
20 files changed, 204 insertions, 77 deletions
diff --git a/compiler/common_compiler_test.cc b/compiler/common_compiler_test.cc index 97387a1c9c..a3d9a0bd6d 100644 --- a/compiler/common_compiler_test.cc +++ b/compiler/common_compiler_test.cc @@ -60,14 +60,18 @@ void CommonCompilerTest::MakeExecutable(mirror::ArtMethod* method) { const std::vector<uint8_t>& mapping_table = compiled_method->GetMappingTable(); uint32_t mapping_table_offset = mapping_table.empty() ? 0u : sizeof(OatQuickMethodHeader) + vmap_table.size() + mapping_table.size(); - OatQuickMethodHeader method_header(mapping_table_offset, vmap_table_offset, + const std::vector<uint8_t>& gc_map = *compiled_method->GetGcMap(); + uint32_t gc_map_offset = gc_map.empty() ? 0u + : sizeof(OatQuickMethodHeader) + vmap_table.size() + mapping_table.size() + gc_map.size(); + OatQuickMethodHeader method_header(mapping_table_offset, vmap_table_offset, gc_map_offset, compiled_method->GetFrameSizeInBytes(), compiled_method->GetCoreSpillMask(), compiled_method->GetFpSpillMask(), code_size); header_code_and_maps_chunks_.push_back(std::vector<uint8_t>()); std::vector<uint8_t>* chunk = &header_code_and_maps_chunks_.back(); - size_t size = sizeof(method_header) + code_size + vmap_table.size() + mapping_table.size(); + size_t size = sizeof(method_header) + code_size + vmap_table.size() + mapping_table.size() + + gc_map.size(); size_t code_offset = compiled_method->AlignCode(size - code_size); size_t padding = code_offset - (size - code_size); chunk->reserve(padding + size); @@ -75,6 +79,7 @@ void CommonCompilerTest::MakeExecutable(mirror::ArtMethod* method) { memcpy(&(*chunk)[0], &method_header, sizeof(method_header)); chunk->insert(chunk->begin(), vmap_table.begin(), vmap_table.end()); chunk->insert(chunk->begin(), mapping_table.begin(), mapping_table.end()); + chunk->insert(chunk->begin(), gc_map.begin(), gc_map.end()); chunk->insert(chunk->begin(), padding, 0); chunk->insert(chunk->end(), code->begin(), code->end()); CHECK_EQ(padding + size, chunk->size()); diff --git a/compiler/common_compiler_test.h b/compiler/common_compiler_test.h index 20b750c55b..9cffbc86f3 100644 --- a/compiler/common_compiler_test.h +++ b/compiler/common_compiler_test.h @@ -42,7 +42,7 @@ class CommonCompilerTest : public CommonRuntimeTest { ~CommonCompilerTest(); // Create an OatMethod based on pointers (for unit tests). - OatFile::OatMethod CreateOatMethod(const void* code, const uint8_t* gc_map); + OatFile::OatMethod CreateOatMethod(const void* code); void MakeExecutable(mirror::ArtMethod* method) SHARED_LOCKS_REQUIRED(Locks::mutator_lock_); diff --git a/compiler/dex/quick/arm64/int_arm64.cc b/compiler/dex/quick/arm64/int_arm64.cc index b12fc0a313..57e67d534b 100644 --- a/compiler/dex/quick/arm64/int_arm64.cc +++ b/compiler/dex/quick/arm64/int_arm64.cc @@ -483,6 +483,7 @@ bool Arm64Mir2Lir::SmallLiteralDivRem64(Instruction::Code dalvik_opcode, bool is } else { reconstructed_imm = base + 1; } + DCHECK_EQ(reconstructed_imm, magic_table[lit].magic64) << " for literal " << lit; } // Load the magic constant in two instructions. diff --git a/compiler/image_writer.cc b/compiler/image_writer.cc index 4f5026dee3..03899cc755 100644 --- a/compiler/image_writer.cc +++ b/compiler/image_writer.cc @@ -1125,7 +1125,6 @@ void ImageWriter::FixupMethod(ArtMethod* orig, ArtMethod* copy) { copy->SetEntryPointFromJniPtrSize<kVerifyNone>(orig->GetEntryPointFromJni(), target_ptr_size_); copy->SetEntryPointFromQuickCompiledCodePtrSize<kVerifyNone>( orig->GetEntryPointFromQuickCompiledCode(), target_ptr_size_); - copy->SetNativeGcMapPtrSize<kVerifyNone>(orig->GetNativeGcMap(), target_ptr_size_); // The resolution method has a special trampoline to call. Runtime* runtime = Runtime::Current(); @@ -1186,11 +1185,6 @@ void ImageWriter::FixupMethod(ArtMethod* orig, ArtMethod* copy) { // Note this is not the code_ pointer, that is handled above. copy->SetEntryPointFromJniPtrSize<kVerifyNone>(GetOatAddress(jni_dlsym_lookup_offset_), target_ptr_size_); - } else { - // Normal (non-abstract non-native) methods have various tables to relocate. - uint32_t native_gc_map_offset = orig->GetOatNativeGcMapOffset(); - const uint8_t* native_gc_map = GetOatAddress(native_gc_map_offset); - copy->SetNativeGcMapPtrSize<kVerifyNone>(native_gc_map, target_ptr_size_); } // Interpreter entrypoint: diff --git a/compiler/oat_test.cc b/compiler/oat_test.cc index ce4ed6d22b..9fe98e3663 100644 --- a/compiler/oat_test.cc +++ b/compiler/oat_test.cc @@ -190,8 +190,8 @@ TEST_F(OatTest, OatHeaderSizeCheck) { // If this test is failing and you have to update these constants, // it is time to update OatHeader::kOatVersion EXPECT_EQ(84U, sizeof(OatHeader)); - EXPECT_EQ(8U, sizeof(OatMethodOffsets)); - EXPECT_EQ(24U, sizeof(OatQuickMethodHeader)); + EXPECT_EQ(4U, sizeof(OatMethodOffsets)); + EXPECT_EQ(28U, sizeof(OatQuickMethodHeader)); EXPECT_EQ(91 * GetInstructionSetPointerSize(kRuntimeISA), sizeof(QuickEntryPoints)); } diff --git a/compiler/oat_writer.cc b/compiler/oat_writer.cc index c6beb36178..a57f892c58 100644 --- a/compiler/oat_writer.cc +++ b/compiler/oat_writer.cc @@ -510,15 +510,18 @@ struct OatWriter::GcMapDataAccess { } static uint32_t GetOffset(OatClass* oat_class, size_t method_offsets_index) ALWAYS_INLINE { - return oat_class->method_offsets_[method_offsets_index].gc_map_offset_; + uint32_t offset = oat_class->method_headers_[method_offsets_index].gc_map_offset_; + return offset == 0u ? 0u : + (oat_class->method_offsets_[method_offsets_index].code_offset_ & ~1) - offset; } static void SetOffset(OatClass* oat_class, size_t method_offsets_index, uint32_t offset) ALWAYS_INLINE { - oat_class->method_offsets_[method_offsets_index].gc_map_offset_ = offset; + oat_class->method_headers_[method_offsets_index].gc_map_offset_ = + (oat_class->method_offsets_[method_offsets_index].code_offset_ & ~1) - offset; } - static const char* Name() ALWAYS_INLINE { + static const char* Name() { return "GC map"; } }; @@ -540,7 +543,7 @@ struct OatWriter::MappingTableDataAccess { (oat_class->method_offsets_[method_offsets_index].code_offset_ & ~1) - offset; } - static const char* Name() ALWAYS_INLINE { + static const char* Name() { return "mapping table"; } }; @@ -562,7 +565,7 @@ struct OatWriter::VmapTableDataAccess { (oat_class->method_offsets_[method_offsets_index].code_offset_ & ~1) - offset; } - static const char* Name() ALWAYS_INLINE { + static const char* Name() { return "vmap table"; } }; @@ -764,6 +767,7 @@ class OatWriter::InitCodeMethodVisitor : public OatDexMethodVisitor { OatQuickMethodHeader* method_header = &oat_class->method_headers_[method_offsets_index_]; uint32_t mapping_table_offset = method_header->mapping_table_offset_; uint32_t vmap_table_offset = method_header->vmap_table_offset_; + uint32_t gc_map_offset = method_header->gc_map_offset_; // The code offset was 0 when the mapping/vmap table offset was set, so it's set // to 0-offset and we need to adjust it by code_offset. uint32_t code_offset = quick_code_offset - thumb_offset; @@ -775,12 +779,16 @@ class OatWriter::InitCodeMethodVisitor : public OatDexMethodVisitor { vmap_table_offset += code_offset; DCHECK_LT(vmap_table_offset, code_offset); } + if (gc_map_offset != 0u) { + gc_map_offset += code_offset; + DCHECK_LT(gc_map_offset, code_offset); + } uint32_t frame_size_in_bytes = compiled_method->GetFrameSizeInBytes(); uint32_t core_spill_mask = compiled_method->GetCoreSpillMask(); uint32_t fp_spill_mask = compiled_method->GetFpSpillMask(); *method_header = OatQuickMethodHeader(mapping_table_offset, vmap_table_offset, - frame_size_in_bytes, core_spill_mask, fp_spill_mask, - code_size); + gc_map_offset, frame_size_in_bytes, core_spill_mask, + fp_spill_mask, code_size); if (!deduped) { // Update offsets. (Checksum is updated when writing.) @@ -909,7 +917,7 @@ class OatWriter::InitImageMethodVisitor : public OatDexMethodVisitor { OatClass* oat_class = writer_->oat_classes_[oat_class_index_]; CompiledMethod* compiled_method = oat_class->GetCompiledMethod(class_def_method_index); - OatMethodOffsets offsets(0u, 0u); + OatMethodOffsets offsets(0u); if (compiled_method != nullptr) { DCHECK_LT(method_offsets_index_, oat_class->method_offsets_.size()); offsets = oat_class->method_offsets_[method_offsets_index_]; @@ -920,7 +928,7 @@ class OatWriter::InitImageMethodVisitor : public OatDexMethodVisitor { InvokeType invoke_type = it.GetMethodInvokeType(dex_file_->GetClassDef(class_def_index_)); // Unchecked as we hold mutator_lock_ on entry. ScopedObjectAccessUnchecked soa(Thread::Current()); - StackHandleScope<2> hs(soa.Self()); + StackHandleScope<1> hs(soa.Self()); Handle<mirror::DexCache> dex_cache(hs.NewHandle(linker->FindDexCache(*dex_file_))); mirror::ArtMethod* method = linker->ResolveMethod(*dex_file_, it.GetMemberIndex(), dex_cache, NullHandle<mirror::ClassLoader>(), @@ -936,7 +944,6 @@ class OatWriter::InitImageMethodVisitor : public OatDexMethodVisitor { } // Portable code offsets are set by ElfWriterMclinker::FixupCompiledCodeOffset after linking. method->SetQuickOatCodeOffset(offsets.code_offset_); - method->SetOatNativeGcMapOffset(offsets.gc_map_offset_); return true; } @@ -1157,7 +1164,7 @@ template <typename DataAccess> class OatWriter::WriteMapMethodVisitor : public OatDexMethodVisitor { public: WriteMapMethodVisitor(OatWriter* writer, OutputStream* out, const size_t file_offset, - size_t relative_offset) + size_t relative_offset) : OatDexMethodVisitor(writer, relative_offset), out_(out), file_offset_(file_offset) { @@ -1179,7 +1186,8 @@ class OatWriter::WriteMapMethodVisitor : public OatDexMethodVisitor { size_t map_size = map == nullptr ? 0 : map->size() * sizeof((*map)[0]); DCHECK((map_size == 0u && map_offset == 0u) || (map_size != 0u && map_offset != 0u && map_offset <= offset_)) - << PrettyMethod(it.GetMemberIndex(), *dex_file_); + << map_size << " " << map_offset << " " << offset_ << " " + << PrettyMethod(it.GetMemberIndex(), *dex_file_) << " for " << DataAccess::Name(); if (map_size != 0u && map_offset == offset_) { if (UNLIKELY(!out->WriteFully(&(*map)[0], map_size))) { ReportWriteFailure(it); diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc index 69f031aab1..83d04b1a58 100644 --- a/compiler/optimizing/code_generator_x86_64.cc +++ b/compiler/optimizing/code_generator_x86_64.cc @@ -2896,7 +2896,7 @@ void ParallelMoveResolverX86_64::EmitMove(size_t index) { __ movss(Address(CpuRegister(RSP), destination.GetStackIndex()), source.As<XmmRegister>()); } else { - DCHECK(destination.IsDoubleStackSlot()); + DCHECK(destination.IsDoubleStackSlot()) << destination; __ movsd(Address(CpuRegister(RSP), destination.GetStackIndex()), source.As<XmmRegister>()); } diff --git a/compiler/optimizing/graph_checker.cc b/compiler/optimizing/graph_checker.cc index 1953241a2a..5d712feb2b 100644 --- a/compiler/optimizing/graph_checker.cc +++ b/compiler/optimizing/graph_checker.cc @@ -342,4 +342,72 @@ void SSAChecker::VisitPhi(HPhi* phi) { } } +static Primitive::Type PrimitiveKind(Primitive::Type type) { + switch (type) { + case Primitive::kPrimBoolean: + case Primitive::kPrimByte: + case Primitive::kPrimShort: + case Primitive::kPrimChar: + case Primitive::kPrimInt: + return Primitive::kPrimInt; + default: + return type; + } +} + +void SSAChecker::VisitCondition(HCondition* op) { + VisitInstruction(op); + // TODO: check inputs types, and special case the `null` check. + if (op->GetType() != Primitive::kPrimBoolean) { + std::stringstream error; + error << "Condition " << op->DebugName() << " " << op->GetId() + << " has a non-boolean result type: " + << op->GetType() << "."; + errors_.push_back(error.str()); + } +} + +void SSAChecker::VisitBinaryOperation(HBinaryOperation* op) { + VisitInstruction(op); + if (op->IsUShr() || op->IsShr() || op->IsShl()) { + if (PrimitiveKind(op->InputAt(1)->GetType()) != Primitive::kPrimInt) { + std::stringstream error; + error << "Shift operation " << op->DebugName() << " " << op->GetId() + << " has a non-int kind second input: " + << op->InputAt(1)->DebugName() << " of type " << op->InputAt(1)->GetType() + << "."; + errors_.push_back(error.str()); + } + } else { + if (PrimitiveKind(op->InputAt(1)->GetType()) != PrimitiveKind(op->InputAt(0)->GetType())) { + std::stringstream error; + error << "Binary operation " << op->DebugName() << " " << op->GetId() + << " has inputs of different type: " + << op->InputAt(0)->GetType() << ", and " << op->InputAt(1)->GetType() + << "."; + errors_.push_back(error.str()); + } + } + + if (op->IsCompare()) { + if (op->GetType() != Primitive::kPrimInt) { + std::stringstream error; + error << "Compare operation " << op->GetId() + << " has a non-int result type: " + << op->GetType() << "."; + errors_.push_back(error.str()); + } + } else { + // Use the first input, so that we can also make this check for shift operations. + if (PrimitiveKind(op->GetType()) != PrimitiveKind(op->InputAt(0)->GetType())) { + std::stringstream error; + error << "Binary operation " << op->DebugName() << " " << op->GetId() + << " has a result type different than its input type: " + << op->GetType() << ", and " << op->InputAt(1)->GetType() + << "."; + errors_.push_back(error.str()); + } + } +} + } // namespace art diff --git a/compiler/optimizing/graph_checker.h b/compiler/optimizing/graph_checker.h index 8ba8cb16b1..b6c9f1720c 100644 --- a/compiler/optimizing/graph_checker.h +++ b/compiler/optimizing/graph_checker.h @@ -24,11 +24,11 @@ namespace art { // A control-flow graph visitor performing various checks. -class GraphChecker : public HGraphVisitor { +class GraphChecker : public HGraphDelegateVisitor { public: GraphChecker(ArenaAllocator* allocator, HGraph* graph, const char* dump_prefix = "art::GraphChecker: ") - : HGraphVisitor(graph), + : HGraphDelegateVisitor(graph), allocator_(allocator), dump_prefix_(dump_prefix) {} @@ -36,10 +36,10 @@ class GraphChecker : public HGraphVisitor { virtual void Run() { VisitInsertionOrder(); } // Check `block`. - virtual void VisitBasicBlock(HBasicBlock* block) OVERRIDE; + void VisitBasicBlock(HBasicBlock* block) OVERRIDE; // Check `instruction`. - virtual void VisitInstruction(HInstruction* instruction) OVERRIDE; + void VisitInstruction(HInstruction* instruction) OVERRIDE; // Was the last visit of the graph valid? bool IsValid() const { @@ -82,7 +82,7 @@ class SSAChecker : public GraphChecker { : GraphChecker(allocator, graph, "art::SSAChecker: ") {} // Check the whole graph (in reverse post-order). - virtual void Run() { + void Run() OVERRIDE { // VisitReversePostOrder is used instead of VisitInsertionOrder, // as the latter might visit dead blocks removed by the dominator // computation. @@ -90,13 +90,15 @@ class SSAChecker : public GraphChecker { } // Perform SSA form checks on `block`. - virtual void VisitBasicBlock(HBasicBlock* block) OVERRIDE; + void VisitBasicBlock(HBasicBlock* block) OVERRIDE; // Loop-related checks from block `loop_header`. void CheckLoop(HBasicBlock* loop_header); // Perform SSA form checks on instructions. - virtual void VisitInstruction(HInstruction* instruction) OVERRIDE; - virtual void VisitPhi(HPhi* phi) OVERRIDE; + void VisitInstruction(HInstruction* instruction) OVERRIDE; + void VisitPhi(HPhi* phi) OVERRIDE; + void VisitBinaryOperation(HBinaryOperation* op) OVERRIDE; + void VisitCondition(HCondition* op) OVERRIDE; private: DISALLOW_COPY_AND_ASSIGN(SSAChecker); diff --git a/compiler/optimizing/graph_visualizer.h b/compiler/optimizing/graph_visualizer.h index 4d8bec2422..60d996ba88 100644 --- a/compiler/optimizing/graph_visualizer.h +++ b/compiler/optimizing/graph_visualizer.h @@ -30,7 +30,6 @@ class HGraph; // TODO: Create an analysis/optimization abstraction. static const char* kLivenessPassName = "liveness"; static const char* kRegisterAllocatorPassName = "register"; -static const char* kGVNPassName = "gvn"; /** * If enabled, emits compilation information suitable for the c1visualizer tool diff --git a/compiler/optimizing/gvn.h b/compiler/optimizing/gvn.h index a841d5f65a..8e739cb6d3 100644 --- a/compiler/optimizing/gvn.h +++ b/compiler/optimizing/gvn.h @@ -166,10 +166,10 @@ class ValueSet : public ArenaObject<kArenaAllocMisc> { /** * Optimization phase that removes redundant instruction. */ -class GlobalValueNumberer : public HOptimization { +class GlobalValueNumberer : public ValueObject { public: GlobalValueNumberer(ArenaAllocator* allocator, HGraph* graph) - : HOptimization(graph, true, "GVN"), + : graph_(graph), allocator_(allocator), block_effects_(allocator, graph->GetBlocks().Size()), loop_effects_(allocator, graph->GetBlocks().Size()), @@ -187,7 +187,7 @@ class GlobalValueNumberer : public HOptimization { } } - void Run() OVERRIDE; + void Run(); private: // Per-block GVN. Will also update the ValueSet of the dominated and @@ -202,6 +202,8 @@ class GlobalValueNumberer : public HOptimization { SideEffects GetLoopEffects(HBasicBlock* block) const; SideEffects GetBlockEffects(HBasicBlock* block) const; + HGraph* graph_; + ArenaAllocator* const allocator_; // Side effects of individual blocks, that is the union of the side effects @@ -224,6 +226,19 @@ class GlobalValueNumberer : public HOptimization { DISALLOW_COPY_AND_ASSIGN(GlobalValueNumberer); }; +class GVNOptimization : public HOptimization { + public: + explicit GVNOptimization(HGraph* graph) : HOptimization(graph, true, "GVN") {} + + void Run() OVERRIDE { + GlobalValueNumberer gvn(graph_->GetArena(), graph_); + gvn.Run(); + } + + private: + DISALLOW_COPY_AND_ASSIGN(GVNOptimization); +}; + } // namespace art #endif // ART_COMPILER_OPTIMIZING_GVN_H_ diff --git a/compiler/optimizing/live_ranges_test.cc b/compiler/optimizing/live_ranges_test.cc index 89c949563b..e3c6fec23b 100644 --- a/compiler/optimizing/live_ranges_test.cc +++ b/compiler/optimizing/live_ranges_test.cc @@ -386,7 +386,7 @@ TEST(LiveRangesTest, CFG4) { Instruction::ADD_INT, 1 << 8, Instruction::GOTO | 0x300, Instruction::ADD_INT, 1 << 8, - Instruction::RETURN | 1 << 8); + Instruction::RETURN); ArenaPool pool; ArenaAllocator allocator(&pool); @@ -410,7 +410,10 @@ TEST(LiveRangesTest, CFG4) { interval = liveness.GetInstructionFromSsaIndex(1)->GetLiveInterval(); range = interval->GetFirstRange(); ASSERT_EQ(4u, range->GetStart()); - ASSERT_EQ(28u, range->GetEnd()); + ASSERT_EQ(17u, range->GetEnd()); + range = range->GetNext(); + ASSERT_EQ(20u, range->GetStart()); + ASSERT_EQ(23u, range->GetEnd()); ASSERT_TRUE(range->GetNext() == nullptr); // Test for the first add. @@ -429,9 +432,8 @@ TEST(LiveRangesTest, CFG4) { ASSERT_EQ(26u, range->GetEnd()); ASSERT_TRUE(range->GetNext() == nullptr); - // Test for the phi, which is unused. HPhi* phi = liveness.GetInstructionFromSsaIndex(4)->AsPhi(); - ASSERT_EQ(phi->NumberOfUses(), 0u); + ASSERT_EQ(phi->NumberOfUses(), 1u); interval = phi->GetLiveInterval(); range = interval->GetFirstRange(); ASSERT_EQ(26u, range->GetStart()); diff --git a/compiler/optimizing/optimization.cc b/compiler/optimizing/optimization.cc index d1178d5798..b99f6784f7 100644 --- a/compiler/optimizing/optimization.cc +++ b/compiler/optimizing/optimization.cc @@ -27,13 +27,15 @@ void HOptimization::Check() { SSAChecker checker(graph_->GetArena(), graph_); checker.Run(); if (!checker.IsValid()) { - LOG(FATAL) << Dumpable<SSAChecker>(checker); + LOG(FATAL) << "Error after " << GetPassName() << ": " + << Dumpable<SSAChecker>(checker); } } else { GraphChecker checker(graph_->GetArena(), graph_); checker.Run(); if (!checker.IsValid()) { - LOG(FATAL) << Dumpable<GraphChecker>(checker); + LOG(FATAL) << "Error after " << GetPassName() << ": " + << Dumpable<GraphChecker>(checker); } } } diff --git a/compiler/optimizing/optimization.h b/compiler/optimizing/optimization.h index d281248f4a..e36ef198b6 100644 --- a/compiler/optimizing/optimization.h +++ b/compiler/optimizing/optimization.h @@ -17,7 +17,6 @@ #ifndef ART_COMPILER_OPTIMIZING_OPTIMIZATION_H_ #define ART_COMPILER_OPTIMIZING_OPTIMIZATION_H_ -#include "graph_visualizer.h" #include "nodes.h" namespace art { diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc index 42ac77d1d8..d8533eb8bf 100644 --- a/compiler/optimizing/optimizing_compiler.cc +++ b/compiler/optimizing/optimizing_compiler.cc @@ -35,6 +35,7 @@ #include "nodes.h" #include "prepare_for_register_allocation.h" #include "register_allocator.h" +#include "ssa_builder.h" #include "ssa_phi_elimination.h" #include "ssa_liveness_analysis.h" #include "utils/arena_allocator.h" @@ -191,21 +192,31 @@ static bool CanOptimize(const DexFile::CodeItem& code_item) { } static void RunOptimizations(HGraph* graph, const HGraphVisualizer& visualizer) { + TransformToSsa ssa(graph); HDeadCodeElimination opt1(graph); HConstantFolding opt2(graph); SsaRedundantPhiElimination opt3(graph); SsaDeadPhiElimination opt4(graph); InstructionSimplifier opt5(graph); - GlobalValueNumberer opt6(graph->GetArena(), graph); + GVNOptimization opt6(graph); InstructionSimplifier opt7(graph); - HOptimization* optimizations[] = { &opt1, &opt2, &opt3, &opt4, &opt5, &opt6, &opt7 }; + HOptimization* optimizations[] = { + &ssa, + &opt1, + &opt2, + &opt3, + &opt4, + &opt5, + &opt6, + &opt7 + }; for (size_t i = 0; i < arraysize(optimizations); ++i) { HOptimization* optimization = optimizations[i]; optimization->Run(); - optimization->Check(); visualizer.DumpGraph(optimization->GetPassName()); + optimization->Check(); } } @@ -271,11 +282,6 @@ CompiledMethod* OptimizingCompiler::Compile(const DexFile::CodeItem* code_item, && CanOptimize(*code_item) && RegisterAllocator::CanAllocateRegistersFor(*graph, instruction_set)) { optimized_compiled_methods_++; - graph->BuildDominatorTree(); - graph->TransformToSSA(); - visualizer.DumpGraph("ssa"); - graph->FindNaturalLoops(); - RunOptimizations(graph, visualizer); PrepareForRegisterAllocation(graph).Run(); @@ -321,7 +327,7 @@ CompiledMethod* OptimizingCompiler::Compile(const DexFile::CodeItem* code_item, graph->FindNaturalLoops(); SsaRedundantPhiElimination(graph).Run(); SsaDeadPhiElimination(graph).Run(); - GlobalValueNumberer(graph->GetArena(), graph).Run(); + GVNOptimization(graph).Run(); SsaLivenessAnalysis liveness(*graph, codegen); liveness.Analyze(); visualizer.DumpGraph(kLivenessPassName); diff --git a/compiler/optimizing/ssa_builder.cc b/compiler/optimizing/ssa_builder.cc index b2cc11996e..edfafcdd83 100644 --- a/compiler/optimizing/ssa_builder.cc +++ b/compiler/optimizing/ssa_builder.cc @@ -18,6 +18,7 @@ #include "nodes.h" #include "ssa_type_propagation.h" +#include "ssa_phi_elimination.h" namespace art { @@ -41,11 +42,20 @@ void SsaBuilder::BuildSsa() { } } - // 3) Propagate types of phis. + // 3) Remove dead phis. This will remove phis that are only used by environments: + // at the DEX level, the type of these phis does not need to be consistent, but + // our code generator will complain if the inputs of a phi do not have the same + // type (modulo the special case of `null`). + SsaDeadPhiElimination dead_phis(GetGraph()); + dead_phis.Run(); + + // 4) Propagate types of phis. At this point, phis are typed void in the general + // case, or float or double when we created a floating-point equivalent. So we + // need to propagate the types across phis to give them a correct type. SsaTypePropagation type_propagation(GetGraph()); type_propagation.Run(); - // 4) Clear locals. + // 5) Clear locals. // TODO: Move this to a dead code eliminator phase. for (HInstructionIterator it(GetGraph()->GetEntryBlock()->GetInstructions()); !it.Done(); diff --git a/compiler/optimizing/ssa_builder.h b/compiler/optimizing/ssa_builder.h index 2207cd6bfa..5ab328fe23 100644 --- a/compiler/optimizing/ssa_builder.h +++ b/compiler/optimizing/ssa_builder.h @@ -18,9 +18,24 @@ #define ART_COMPILER_OPTIMIZING_SSA_BUILDER_H_ #include "nodes.h" +#include "optimization.h" namespace art { +class TransformToSsa : public HOptimization { + public: + explicit TransformToSsa(HGraph* graph) : HOptimization(graph, true, "ssa transform") {} + + void Run() OVERRIDE { + graph_->BuildDominatorTree(); + graph_->TransformToSSA(); + graph_->FindNaturalLoops(); + } + + private: + DISALLOW_COPY_AND_ASSIGN(TransformToSsa); +}; + static constexpr int kDefaultNumberOfLoops = 2; class SsaBuilder : public HGraphVisitor { diff --git a/compiler/optimizing/ssa_phi_elimination.cc b/compiler/optimizing/ssa_phi_elimination.cc index 56979e1c6a..58cea771b9 100644 --- a/compiler/optimizing/ssa_phi_elimination.cc +++ b/compiler/optimizing/ssa_phi_elimination.cc @@ -24,6 +24,8 @@ void SsaDeadPhiElimination::Run() { HBasicBlock* block = it.Current(); for (HInstructionIterator inst_it(block->GetPhis()); !inst_it.Done(); inst_it.Advance()) { HPhi* phi = inst_it.Current()->AsPhi(); + // Set dead ahead of running through uses. The phi may have no use. + phi->SetDead(); for (HUseIterator<HInstruction> use_it(phi->GetUses()); !use_it.Done(); use_it.Advance()) { HUseListNode<HInstruction>* current = use_it.Current(); HInstruction* user = current->GetUser(); @@ -31,8 +33,6 @@ void SsaDeadPhiElimination::Run() { worklist_.Add(phi); phi->SetLive(); break; - } else { - phi->SetDead(); } } } @@ -65,8 +65,8 @@ void SsaDeadPhiElimination::Run() { use_it.Advance()) { HUseListNode<HInstruction>* user_node = use_it.Current(); HInstruction* user = user_node->GetUser(); - DCHECK(user->IsLoopHeaderPhi()); - DCHECK(user->AsPhi()->IsDead()); + DCHECK(user->IsLoopHeaderPhi()) << user->GetId(); + DCHECK(user->AsPhi()->IsDead()) << user->GetId(); // Just put itself as an input. The phi will be removed in this loop anyway. user->SetRawInputAt(user_node->GetIndex(), user); current->RemoveUser(user, user_node->GetIndex()); diff --git a/compiler/optimizing/ssa_test.cc b/compiler/optimizing/ssa_test.cc index fffe5c2b44..6174dd49a1 100644 --- a/compiler/optimizing/ssa_test.cc +++ b/compiler/optimizing/ssa_test.cc @@ -199,29 +199,31 @@ TEST(SsaTest, Loop1) { // Test that we create a phi for an initialized local at entry of a loop. const char* expected = "BasicBlock 0, succ: 1\n" - " 0: IntConstant 0 [6, 4, 2, 2]\n" - " 1: Goto\n" - "BasicBlock 1, pred: 0, succ: 5, 6\n" - " 2: Equal(0, 0) [3]\n" - " 3: If(2)\n" - "BasicBlock 2, pred: 3, 6, succ: 3\n" - " 4: Phi(6, 0) [6]\n" + " 0: IntConstant 0 [6, 3, 3]\n" + " 1: IntConstant 4 [6]\n" + " 2: Goto\n" + "BasicBlock 1, pred: 0, succ: 4, 2\n" + " 3: Equal(0, 0) [4]\n" + " 4: If(3)\n" + "BasicBlock 2, pred: 1, succ: 3\n" " 5: Goto\n" - "BasicBlock 3, pred: 5, 2, succ: 2\n" - " 6: Phi(0, 4) [4]\n" + "BasicBlock 3, pred: 2, 4, succ: 5\n" + " 6: Phi(1, 0) [9]\n" " 7: Goto\n" - "BasicBlock 4\n" - // Synthesized blocks to avoid critical edge. - "BasicBlock 5, pred: 1, succ: 3\n" + "BasicBlock 4, pred: 1, succ: 3\n" " 8: Goto\n" - "BasicBlock 6, pred: 1, succ: 2\n" - " 9: Goto\n"; + "BasicBlock 5, pred: 3, succ: 6\n" + " 9: Return(6)\n" + "BasicBlock 6, pred: 5\n" + " 10: Exit\n"; const uint16_t data[] = ONE_REGISTER_CODE_ITEM( Instruction::CONST_4 | 0 | 0, - Instruction::IF_EQ, 3, - Instruction::GOTO | 0x100, - Instruction::GOTO | 0xFF00); + Instruction::IF_EQ, 4, + Instruction::CONST_4 | 4 << 12 | 0, + Instruction::GOTO | 0x200, + Instruction::GOTO | 0xFF00, + Instruction::RETURN | 0 << 8); TestCode(data, expected); } diff --git a/compiler/utils/arm/assembler_arm.cc b/compiler/utils/arm/assembler_arm.cc index 9c84bc1e37..0f28591775 100644 --- a/compiler/utils/arm/assembler_arm.cc +++ b/compiler/utils/arm/assembler_arm.cc @@ -205,10 +205,9 @@ uint32_t Address::encodingArm() const { encoding = am_ | offset_; } } else { - uint32_t imm5 = offset_; uint32_t shift = shift_; if (shift == RRX) { - imm5 = 0; + CHECK_EQ(offset_, 0); shift = ROR; } encoding = am_ | static_cast<uint32_t>(rm_) | shift << 5 | offset_ << 7 | B25; |