diff options
Diffstat (limited to 'compiler/optimizing')
-rw-r--r-- | compiler/optimizing/instruction_builder.cc | 101 | ||||
-rw-r--r-- | compiler/optimizing/instruction_builder.h | 4 | ||||
-rw-r--r-- | compiler/optimizing/licm.cc | 15 | ||||
-rw-r--r-- | compiler/optimizing/licm_test.cc | 12 | ||||
-rw-r--r-- | compiler/optimizing/nodes.h | 33 | ||||
-rw-r--r-- | compiler/optimizing/side_effects_test.cc | 34 | ||||
-rw-r--r-- | compiler/optimizing/ssa_liveness_analysis.h | 56 |
7 files changed, 156 insertions, 99 deletions
diff --git a/compiler/optimizing/instruction_builder.cc b/compiler/optimizing/instruction_builder.cc index 06b39680b2..f5e49c2235 100644 --- a/compiler/optimizing/instruction_builder.cc +++ b/compiler/optimizing/instruction_builder.cc @@ -889,8 +889,15 @@ bool HInstructionBuilder::BuildInvoke(const Instruction& instruction, } bool HInstructionBuilder::BuildNewInstance(uint16_t type_index, uint32_t dex_pc) { + ScopedObjectAccess soa(Thread::Current()); + StackHandleScope<1> hs(soa.Self()); + Handle<mirror::DexCache> dex_cache = dex_compilation_unit_->GetDexCache(); + Handle<mirror::Class> resolved_class(hs.NewHandle(dex_cache->GetResolvedType(type_index))); + const DexFile& outer_dex_file = *outer_compilation_unit_->GetDexFile(); + Handle<mirror::DexCache> outer_dex_cache = outer_compilation_unit_->GetDexCache(); + bool finalizable; - bool can_throw = NeedsAccessCheck(type_index, &finalizable); + bool can_throw = NeedsAccessCheck(type_index, dex_cache, &finalizable); // Only the non-resolved entrypoint handles the finalizable class case. If we // need access checks, then we haven't resolved the method and the class may @@ -899,16 +906,6 @@ bool HInstructionBuilder::BuildNewInstance(uint16_t type_index, uint32_t dex_pc) ? kQuickAllocObject : kQuickAllocObjectInitialized; - ScopedObjectAccess soa(Thread::Current()); - StackHandleScope<3> hs(soa.Self()); - Handle<mirror::DexCache> dex_cache(hs.NewHandle( - dex_compilation_unit_->GetClassLinker()->FindDexCache( - soa.Self(), *dex_compilation_unit_->GetDexFile()))); - Handle<mirror::Class> resolved_class(hs.NewHandle(dex_cache->GetResolvedType(type_index))); - const DexFile& outer_dex_file = *outer_compilation_unit_->GetDexFile(); - Handle<mirror::DexCache> outer_dex_cache(hs.NewHandle( - outer_compilation_unit_->GetClassLinker()->FindDexCache(soa.Self(), outer_dex_file))); - if (outer_dex_cache.Get() != dex_cache.Get()) { // We currently do not support inlining allocations across dex files. return false; @@ -921,7 +918,7 @@ bool HInstructionBuilder::BuildNewInstance(uint16_t type_index, uint32_t dex_pc) IsOutermostCompilingClass(type_index), dex_pc, /*needs_access_check*/ can_throw, - compiler_driver_->CanAssumeTypeIsPresentInDexCache(outer_dex_file, type_index)); + compiler_driver_->CanAssumeTypeIsPresentInDexCache(outer_dex_cache, type_index)); AppendInstruction(load_class); HInstruction* cls = load_class; @@ -979,13 +976,9 @@ HClinitCheck* HInstructionBuilder::ProcessClinitCheckForInvoke( HInvokeStaticOrDirect::ClinitCheckRequirement* clinit_check_requirement) { const DexFile& outer_dex_file = *outer_compilation_unit_->GetDexFile(); Thread* self = Thread::Current(); - StackHandleScope<4> hs(self); - Handle<mirror::DexCache> dex_cache(hs.NewHandle( - dex_compilation_unit_->GetClassLinker()->FindDexCache( - self, *dex_compilation_unit_->GetDexFile()))); - Handle<mirror::DexCache> outer_dex_cache(hs.NewHandle( - outer_compilation_unit_->GetClassLinker()->FindDexCache( - self, outer_dex_file))); + StackHandleScope<2> hs(self); + Handle<mirror::DexCache> dex_cache = dex_compilation_unit_->GetDexCache(); + Handle<mirror::DexCache> outer_dex_cache = outer_compilation_unit_->GetDexCache(); Handle<mirror::Class> outer_class(hs.NewHandle(GetOutermostCompilingClass())); Handle<mirror::Class> resolved_method_class(hs.NewHandle(resolved_method->GetDeclaringClass())); @@ -1016,7 +1009,7 @@ HClinitCheck* HInstructionBuilder::ProcessClinitCheckForInvoke( is_outer_class, dex_pc, /*needs_access_check*/ false, - compiler_driver_->CanAssumeTypeIsPresentInDexCache(outer_dex_file, storage_index)); + compiler_driver_->CanAssumeTypeIsPresentInDexCache(outer_dex_cache, storage_index)); AppendInstruction(load_class); clinit_check = new (arena_) HClinitCheck(load_class, dex_pc); AppendInstruction(clinit_check); @@ -1261,12 +1254,10 @@ bool HInstructionBuilder::BuildInstanceFieldAccess(const Instruction& instructio static mirror::Class* GetClassFrom(CompilerDriver* driver, const DexCompilationUnit& compilation_unit) { ScopedObjectAccess soa(Thread::Current()); - StackHandleScope<2> hs(soa.Self()); - const DexFile& dex_file = *compilation_unit.GetDexFile(); + StackHandleScope<1> hs(soa.Self()); Handle<mirror::ClassLoader> class_loader(hs.NewHandle( soa.Decode<mirror::ClassLoader*>(compilation_unit.GetClassLoader()))); - Handle<mirror::DexCache> dex_cache(hs.NewHandle( - compilation_unit.GetClassLinker()->FindDexCache(soa.Self(), dex_file))); + Handle<mirror::DexCache> dex_cache = compilation_unit.GetDexCache(); return driver->ResolveCompilingMethodsClass(soa, dex_cache, class_loader, &compilation_unit); } @@ -1281,10 +1272,8 @@ mirror::Class* HInstructionBuilder::GetCompilingClass() const { bool HInstructionBuilder::IsOutermostCompilingClass(uint16_t type_index) const { ScopedObjectAccess soa(Thread::Current()); - StackHandleScope<4> hs(soa.Self()); - Handle<mirror::DexCache> dex_cache(hs.NewHandle( - dex_compilation_unit_->GetClassLinker()->FindDexCache( - soa.Self(), *dex_compilation_unit_->GetDexFile()))); + StackHandleScope<3> hs(soa.Self()); + Handle<mirror::DexCache> dex_cache = dex_compilation_unit_->GetDexCache(); Handle<mirror::ClassLoader> class_loader(hs.NewHandle( soa.Decode<mirror::ClassLoader*>(dex_compilation_unit_->GetClassLoader()))); Handle<mirror::Class> cls(hs.NewHandle(compiler_driver_->ResolveClass( @@ -1324,10 +1313,8 @@ bool HInstructionBuilder::BuildStaticFieldAccess(const Instruction& instruction, uint16_t field_index = instruction.VRegB_21c(); ScopedObjectAccess soa(Thread::Current()); - StackHandleScope<5> hs(soa.Self()); - Handle<mirror::DexCache> dex_cache(hs.NewHandle( - dex_compilation_unit_->GetClassLinker()->FindDexCache( - soa.Self(), *dex_compilation_unit_->GetDexFile()))); + StackHandleScope<3> hs(soa.Self()); + Handle<mirror::DexCache> dex_cache = dex_compilation_unit_->GetDexCache(); Handle<mirror::ClassLoader> class_loader(hs.NewHandle( soa.Decode<mirror::ClassLoader*>(dex_compilation_unit_->GetClassLoader()))); ArtField* resolved_field = compiler_driver_->ResolveField( @@ -1342,8 +1329,7 @@ bool HInstructionBuilder::BuildStaticFieldAccess(const Instruction& instruction, Primitive::Type field_type = resolved_field->GetTypeAsPrimitiveType(); const DexFile& outer_dex_file = *outer_compilation_unit_->GetDexFile(); - Handle<mirror::DexCache> outer_dex_cache(hs.NewHandle( - outer_compilation_unit_->GetClassLinker()->FindDexCache(soa.Self(), outer_dex_file))); + Handle<mirror::DexCache> outer_dex_cache = outer_compilation_unit_->GetDexCache(); Handle<mirror::Class> outer_class(hs.NewHandle(GetOutermostCompilingClass())); // The index at which the field's class is stored in the DexCache's type array. @@ -1371,7 +1357,7 @@ bool HInstructionBuilder::BuildStaticFieldAccess(const Instruction& instruction, } bool is_in_cache = - compiler_driver_->CanAssumeTypeIsPresentInDexCache(outer_dex_file, storage_index); + compiler_driver_->CanAssumeTypeIsPresentInDexCache(outer_dex_cache, storage_index); HLoadClass* constant = new (arena_) HLoadClass(graph_->GetCurrentMethod(), storage_index, outer_dex_file, @@ -1634,22 +1620,17 @@ void HInstructionBuilder::BuildTypeCheck(const Instruction& instruction, uint8_t reference, uint16_t type_index, uint32_t dex_pc) { - bool type_known_final, type_known_abstract, use_declaring_class; - bool can_access = compiler_driver_->CanAccessTypeWithoutChecks( - dex_compilation_unit_->GetDexMethodIndex(), - *dex_compilation_unit_->GetDexFile(), - type_index, - &type_known_final, - &type_known_abstract, - &use_declaring_class); - ScopedObjectAccess soa(Thread::Current()); - StackHandleScope<2> hs(soa.Self()); + StackHandleScope<1> hs(soa.Self()); const DexFile& dex_file = *dex_compilation_unit_->GetDexFile(); - Handle<mirror::DexCache> dex_cache(hs.NewHandle( - dex_compilation_unit_->GetClassLinker()->FindDexCache(soa.Self(), dex_file))); + Handle<mirror::DexCache> dex_cache = dex_compilation_unit_->GetDexCache(); Handle<mirror::Class> resolved_class(hs.NewHandle(dex_cache->GetResolvedType(type_index))); + bool can_access = compiler_driver_->CanAccessTypeWithoutChecks( + dex_compilation_unit_->GetDexMethodIndex(), + dex_cache, + type_index); + HInstruction* object = LoadLocal(reference, Primitive::kPrimNot); HLoadClass* cls = new (arena_) HLoadClass( graph_->GetCurrentMethod(), @@ -1658,7 +1639,7 @@ void HInstructionBuilder::BuildTypeCheck(const Instruction& instruction, IsOutermostCompilingClass(type_index), dex_pc, !can_access, - compiler_driver_->CanAssumeTypeIsPresentInDexCache(dex_file, type_index)); + compiler_driver_->CanAssumeTypeIsPresentInDexCache(dex_cache, type_index)); AppendInstruction(cls); TypeCheckKind check_kind = ComputeTypeCheckKind(resolved_class); @@ -1676,9 +1657,17 @@ void HInstructionBuilder::BuildTypeCheck(const Instruction& instruction, } } -bool HInstructionBuilder::NeedsAccessCheck(uint32_t type_index, bool* finalizable) const { +bool HInstructionBuilder::NeedsAccessCheck(uint32_t type_index, + Handle<mirror::DexCache> dex_cache, + bool* finalizable) const { return !compiler_driver_->CanAccessInstantiableTypeWithoutChecks( - dex_compilation_unit_->GetDexMethodIndex(), *dex_file_, type_index, finalizable); + dex_compilation_unit_->GetDexMethodIndex(), dex_cache, type_index, finalizable); +} + +bool HInstructionBuilder::NeedsAccessCheck(uint32_t type_index, bool* finalizable) const { + ScopedObjectAccess soa(Thread::Current()); + Handle<mirror::DexCache> dex_cache = dex_compilation_unit_->GetDexCache(); + return NeedsAccessCheck(type_index, dex_cache, finalizable); } bool HInstructionBuilder::CanDecodeQuickenedInfo() const { @@ -2612,16 +2601,16 @@ bool HInstructionBuilder::ProcessDexInstruction(const Instruction& instruction, case Instruction::CONST_CLASS: { uint16_t type_index = instruction.VRegB_21c(); - bool type_known_final; - bool type_known_abstract; - bool dont_use_is_referrers_class; // `CanAccessTypeWithoutChecks` will tell whether the method being // built is trying to access its own class, so that the generated // code can optimize for this case. However, the optimization does not // work for inlining, so we use `IsOutermostCompilingClass` instead. + ScopedObjectAccess soa(Thread::Current()); + Handle<mirror::DexCache> dex_cache = dex_compilation_unit_->GetDexCache(); bool can_access = compiler_driver_->CanAccessTypeWithoutChecks( - dex_compilation_unit_->GetDexMethodIndex(), *dex_file_, type_index, - &type_known_final, &type_known_abstract, &dont_use_is_referrers_class); + dex_compilation_unit_->GetDexMethodIndex(), dex_cache, type_index); + bool is_in_dex_cache = + compiler_driver_->CanAssumeTypeIsPresentInDexCache(dex_cache, type_index); AppendInstruction(new (arena_) HLoadClass( graph_->GetCurrentMethod(), type_index, @@ -2629,7 +2618,7 @@ bool HInstructionBuilder::ProcessDexInstruction(const Instruction& instruction, IsOutermostCompilingClass(type_index), dex_pc, !can_access, - compiler_driver_->CanAssumeTypeIsPresentInDexCache(*dex_file_, type_index))); + is_in_dex_cache)); UpdateLocal(instruction.VRegA_21c(), current_block_->GetLastInstruction()); break; } diff --git a/compiler/optimizing/instruction_builder.h b/compiler/optimizing/instruction_builder.h index f480b70062..070f7da80e 100644 --- a/compiler/optimizing/instruction_builder.h +++ b/compiler/optimizing/instruction_builder.h @@ -97,6 +97,10 @@ class HInstructionBuilder : public ValueObject { // Returns whether the current method needs access check for the type. // Output parameter finalizable is set to whether the type is finalizable. + bool NeedsAccessCheck(uint32_t type_index, + Handle<mirror::DexCache> dex_cache, + /*out*/bool* finalizable) const + SHARED_REQUIRES(Locks::mutator_lock_); bool NeedsAccessCheck(uint32_t type_index, /*out*/bool* finalizable) const; template<typename T> diff --git a/compiler/optimizing/licm.cc b/compiler/optimizing/licm.cc index 7a1e06b951..5a0b89c90a 100644 --- a/compiler/optimizing/licm.cc +++ b/compiler/optimizing/licm.cc @@ -79,8 +79,15 @@ static void UpdateLoopPhisIn(HEnvironment* environment, HLoopInformation* info) void LICM::Run() { DCHECK(side_effects_.HasRun()); + // Only used during debug. - ArenaBitVector visited(graph_->GetArena(), graph_->GetBlocks().size(), false, kArenaAllocLICM); + ArenaBitVector* visited = nullptr; + if (kIsDebugBuild) { + visited = new (graph_->GetArena()) ArenaBitVector(graph_->GetArena(), + graph_->GetBlocks().size(), + false, + kArenaAllocLICM); + } // Post order visit to visit inner loops before outer loops. for (HPostOrderIterator it(*graph_); !it.Done(); it.Advance()) { @@ -109,10 +116,12 @@ void LICM::Run() { DCHECK(inner->IsInLoop()); if (inner->GetLoopInformation() != loop_info) { // Thanks to post order visit, inner loops were already visited. - DCHECK(visited.IsBitSet(inner->GetBlockId())); + DCHECK(visited->IsBitSet(inner->GetBlockId())); continue; } - visited.SetBit(inner->GetBlockId()); + if (kIsDebugBuild) { + visited->SetBit(inner->GetBlockId()); + } if (contains_irreducible_loop) { // We cannot licm in an irreducible loop, or in a natural loop containing an diff --git a/compiler/optimizing/licm_test.cc b/compiler/optimizing/licm_test.cc index 2a62643465..d446539700 100644 --- a/compiler/optimizing/licm_test.cc +++ b/compiler/optimizing/licm_test.cc @@ -169,11 +169,13 @@ TEST_F(LICMTest, ArrayHoisting) { BuildLoop(); // Populate the loop with instructions: set/get array with different types. + // ArrayGet is typed as kPrimByte and ArraySet given a float value in order to + // avoid SsaBuilder's typing of ambiguous array operations from reference type info. HInstruction* get_array = new (&allocator_) HArrayGet( - parameter_, int_constant_, Primitive::kPrimInt, 0); + parameter_, int_constant_, Primitive::kPrimByte, 0); loop_body_->InsertInstructionBefore(get_array, loop_body_->GetLastInstruction()); HInstruction* set_array = new (&allocator_) HArraySet( - parameter_, int_constant_, float_constant_, Primitive::kPrimFloat, 0); + parameter_, int_constant_, float_constant_, Primitive::kPrimShort, 0); loop_body_->InsertInstructionBefore(set_array, loop_body_->GetLastInstruction()); EXPECT_EQ(get_array->GetBlock(), loop_body_); @@ -187,11 +189,13 @@ TEST_F(LICMTest, NoArrayHoisting) { BuildLoop(); // Populate the loop with instructions: set/get array with same types. + // ArrayGet is typed as kPrimByte and ArraySet given a float value in order to + // avoid SsaBuilder's typing of ambiguous array operations from reference type info. HInstruction* get_array = new (&allocator_) HArrayGet( - parameter_, int_constant_, Primitive::kPrimFloat, 0); + parameter_, int_constant_, Primitive::kPrimByte, 0); loop_body_->InsertInstructionBefore(get_array, loop_body_->GetLastInstruction()); HInstruction* set_array = new (&allocator_) HArraySet( - parameter_, get_array, float_constant_, Primitive::kPrimFloat, 0); + parameter_, get_array, float_constant_, Primitive::kPrimByte, 0); loop_body_->InsertInstructionBefore(set_array, loop_body_->GetLastInstruction()); EXPECT_EQ(get_array->GetBlock(), loop_body_); diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h index 6f3e536d05..dc5a8fa9cb 100644 --- a/compiler/optimizing/nodes.h +++ b/compiler/optimizing/nodes.h @@ -1551,21 +1551,21 @@ class SideEffects : public ValueObject { static SideEffects FieldWriteOfType(Primitive::Type type, bool is_volatile) { return is_volatile ? AllWritesAndReads() - : SideEffects(TypeFlag(type, kFieldWriteOffset)); + : SideEffects(TypeFlagWithAlias(type, kFieldWriteOffset)); } static SideEffects ArrayWriteOfType(Primitive::Type type) { - return SideEffects(TypeFlag(type, kArrayWriteOffset)); + return SideEffects(TypeFlagWithAlias(type, kArrayWriteOffset)); } static SideEffects FieldReadOfType(Primitive::Type type, bool is_volatile) { return is_volatile ? AllWritesAndReads() - : SideEffects(TypeFlag(type, kFieldReadOffset)); + : SideEffects(TypeFlagWithAlias(type, kFieldReadOffset)); } static SideEffects ArrayReadOfType(Primitive::Type type) { - return SideEffects(TypeFlag(type, kArrayReadOffset)); + return SideEffects(TypeFlagWithAlias(type, kArrayReadOffset)); } static SideEffects CanTriggerGC() { @@ -1692,6 +1692,23 @@ class SideEffects : public ValueObject { static constexpr uint64_t kAllReads = ((1ULL << (kLastBitForReads + 1 - kFieldReadOffset)) - 1) << kFieldReadOffset; + // Work around the fact that HIR aliases I/F and J/D. + // TODO: remove this interceptor once HIR types are clean + static uint64_t TypeFlagWithAlias(Primitive::Type type, int offset) { + switch (type) { + case Primitive::kPrimInt: + case Primitive::kPrimFloat: + return TypeFlag(Primitive::kPrimInt, offset) | + TypeFlag(Primitive::kPrimFloat, offset); + case Primitive::kPrimLong: + case Primitive::kPrimDouble: + return TypeFlag(Primitive::kPrimLong, offset) | + TypeFlag(Primitive::kPrimDouble, offset); + default: + return TypeFlag(type, offset); + } + } + // Translates type to bit flag. static uint64_t TypeFlag(Primitive::Type type, int offset) { CHECK_NE(type, Primitive::kPrimVoid); @@ -5179,8 +5196,10 @@ class HArraySet : public HTemplateInstruction<3> { uint32_t dex_pc, SideEffects additional_side_effects = SideEffects::None()) : HTemplateInstruction( - SideEffectsForArchRuntimeCalls(value->GetType()).Union(additional_side_effects), - dex_pc) { + SideEffects::ArrayWriteOfType(expected_component_type).Union( + SideEffectsForArchRuntimeCalls(value->GetType())).Union( + additional_side_effects), + dex_pc) { SetPackedField<ExpectedComponentTypeField>(expected_component_type); SetPackedFlag<kFlagNeedsTypeCheck>(value->GetType() == Primitive::kPrimNot); SetPackedFlag<kFlagValueCanBeNull>(true); @@ -5188,8 +5207,6 @@ class HArraySet : public HTemplateInstruction<3> { SetRawInputAt(0, array); SetRawInputAt(1, index); SetRawInputAt(2, value); - // We can now call component type logic to set correct type-based side effects. - AddSideEffects(SideEffects::ArrayWriteOfType(GetComponentType())); } bool NeedsEnvironment() const OVERRIDE { diff --git a/compiler/optimizing/side_effects_test.cc b/compiler/optimizing/side_effects_test.cc index b01bc1ca0d..9bbc354290 100644 --- a/compiler/optimizing/side_effects_test.cc +++ b/compiler/optimizing/side_effects_test.cc @@ -148,19 +148,19 @@ TEST(SideEffectsTest, VolatileDependences) { EXPECT_FALSE(any_write.MayDependOn(volatile_read)); } -TEST(SideEffectsTest, SameWidthTypesNoAlias) { +TEST(SideEffectsTest, SameWidthTypes) { // Type I/F. - testNoWriteAndReadDependence( + testWriteAndReadDependence( SideEffects::FieldWriteOfType(Primitive::kPrimInt, /* is_volatile */ false), SideEffects::FieldReadOfType(Primitive::kPrimFloat, /* is_volatile */ false)); - testNoWriteAndReadDependence( + testWriteAndReadDependence( SideEffects::ArrayWriteOfType(Primitive::kPrimInt), SideEffects::ArrayReadOfType(Primitive::kPrimFloat)); // Type L/D. - testNoWriteAndReadDependence( + testWriteAndReadDependence( SideEffects::FieldWriteOfType(Primitive::kPrimLong, /* is_volatile */ false), SideEffects::FieldReadOfType(Primitive::kPrimDouble, /* is_volatile */ false)); - testNoWriteAndReadDependence( + testWriteAndReadDependence( SideEffects::ArrayWriteOfType(Primitive::kPrimLong), SideEffects::ArrayReadOfType(Primitive::kPrimDouble)); } @@ -216,32 +216,14 @@ TEST(SideEffectsTest, BitStrings) { "||||||L|", SideEffects::FieldWriteOfType(Primitive::kPrimNot, false).ToString().c_str()); EXPECT_STREQ( - "||DFJISCBZL|DFJISCBZL||DFJISCBZL|DFJISCBZL|", - SideEffects::FieldWriteOfType(Primitive::kPrimNot, true).ToString().c_str()); - EXPECT_STREQ( "|||||Z||", SideEffects::ArrayWriteOfType(Primitive::kPrimBoolean).ToString().c_str()); EXPECT_STREQ( - "|||||C||", - SideEffects::ArrayWriteOfType(Primitive::kPrimChar).ToString().c_str()); - EXPECT_STREQ( - "|||||S||", - SideEffects::ArrayWriteOfType(Primitive::kPrimShort).ToString().c_str()); - EXPECT_STREQ( "|||B||||", SideEffects::FieldReadOfType(Primitive::kPrimByte, false).ToString().c_str()); EXPECT_STREQ( - "||D|||||", + "||DJ|||||", // note: DJ alias SideEffects::ArrayReadOfType(Primitive::kPrimDouble).ToString().c_str()); - EXPECT_STREQ( - "||J|||||", - SideEffects::ArrayReadOfType(Primitive::kPrimLong).ToString().c_str()); - EXPECT_STREQ( - "||F|||||", - SideEffects::ArrayReadOfType(Primitive::kPrimFloat).ToString().c_str()); - EXPECT_STREQ( - "||I|||||", - SideEffects::ArrayReadOfType(Primitive::kPrimInt).ToString().c_str()); SideEffects s = SideEffects::None(); s = s.Union(SideEffects::FieldWriteOfType(Primitive::kPrimChar, /* is_volatile */ false)); s = s.Union(SideEffects::FieldWriteOfType(Primitive::kPrimLong, /* is_volatile */ false)); @@ -249,7 +231,9 @@ TEST(SideEffectsTest, BitStrings) { s = s.Union(SideEffects::FieldReadOfType(Primitive::kPrimInt, /* is_volatile */ false)); s = s.Union(SideEffects::ArrayReadOfType(Primitive::kPrimFloat)); s = s.Union(SideEffects::ArrayReadOfType(Primitive::kPrimDouble)); - EXPECT_STREQ("||DF|I||S|JC|", s.ToString().c_str()); + EXPECT_STREQ( + "||DFJI|FI||S|DJC|", // note: DJ/FI alias. + s.ToString().c_str()); } } // namespace art diff --git a/compiler/optimizing/ssa_liveness_analysis.h b/compiler/optimizing/ssa_liveness_analysis.h index 97f2aeeb1e..719feec468 100644 --- a/compiler/optimizing/ssa_liveness_analysis.h +++ b/compiler/optimizing/ssa_liveness_analysis.h @@ -969,6 +969,38 @@ class LiveInterval : public ArenaObject<kArenaAllocSsaLiveness> { return false; } + bool IsLinearOrderWellFormed(const HGraph& graph) { + for (HBasicBlock* header : graph.GetBlocks()) { + if (!header->IsLoopHeader()) { + continue; + } + + HLoopInformation* loop = header->GetLoopInformation(); + size_t num_blocks = loop->GetBlocks().NumSetBits(); + size_t found_blocks = 0u; + + for (HLinearOrderIterator it(graph); !it.Done(); it.Advance()) { + HBasicBlock* current = it.Current(); + if (loop->Contains(*current)) { + found_blocks++; + if (found_blocks == 1u && current != header) { + // First block is not the header. + return false; + } else if (found_blocks == num_blocks && !loop->IsBackEdge(*current)) { + // Last block is not a back edge. + return false; + } + } else if (found_blocks != 0u && found_blocks != num_blocks) { + // Blocks are not adjacent. + return false; + } + } + DCHECK_EQ(found_blocks, num_blocks); + } + + return true; + } + void AddBackEdgeUses(const HBasicBlock& block_at_use) { DCHECK(block_at_use.IsInLoop()); // Add synthesized uses at the back edge of loops to help the register allocator. @@ -995,12 +1027,30 @@ class LiveInterval : public ArenaObject<kArenaAllocSsaLiveness> { if ((first_use_ != nullptr) && (first_use_->GetPosition() <= back_edge_use_position)) { // There was a use already seen in this loop. Therefore the previous call to `AddUse` // already inserted the backedge use. We can stop going outward. - DCHECK(HasSynthesizeUseAt(back_edge_use_position)); + if (kIsDebugBuild) { + if (!HasSynthesizeUseAt(back_edge_use_position)) { + // There exists a use prior to `back_edge_use_position` but there is + // no synthesized use at the back edge. This can happen in the presence + // of irreducible loops, when blocks of the loop are not adjacent in + // linear order, i.e. when there is an out-of-loop block between + // `block_at_use` and `back_edge_position` that uses this interval. + DCHECK(block_at_use.GetGraph()->HasIrreducibleLoops()); + DCHECK(!IsLinearOrderWellFormed(*block_at_use.GetGraph())); + } + } break; } - DCHECK(last_in_new_list == nullptr - || back_edge_use_position > last_in_new_list->GetPosition()); + if (last_in_new_list != nullptr && + back_edge_use_position <= last_in_new_list->GetPosition()) { + // Loops are not properly nested in the linear order, i.e. the back edge + // of an outer loop preceeds blocks of an inner loop. This can happen + // in the presence of irreducible loops. + DCHECK(block_at_use.GetGraph()->HasIrreducibleLoops()); + DCHECK(!IsLinearOrderWellFormed(*block_at_use.GetGraph())); + // We must bail out, otherwise we would generate an unsorted use list. + break; + } UsePosition* new_use = new (allocator_) UsePosition( /* user */ nullptr, |