diff options
Diffstat (limited to 'compiler')
| -rw-r--r-- | compiler/driver/compiler_driver.cc | 93 | ||||
| -rw-r--r-- | compiler/driver/compiler_driver.h | 25 | ||||
| -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 |
9 files changed, 175 insertions, 198 deletions
diff --git a/compiler/driver/compiler_driver.cc b/compiler/driver/compiler_driver.cc index 8bdff21c70..be82956e76 100644 --- a/compiler/driver/compiler_driver.cc +++ b/compiler/driver/compiler_driver.cc @@ -1283,14 +1283,13 @@ bool CompilerDriver::CanAssumeClassIsLoaded(mirror::Class* klass) { return IsImageClass(descriptor); } -bool CompilerDriver::CanAssumeTypeIsPresentInDexCache(const DexFile& dex_file, uint32_t type_idx) { +bool CompilerDriver::CanAssumeTypeIsPresentInDexCache(Handle<mirror::DexCache> dex_cache, + uint32_t type_idx) { bool result = false; if ((IsBootImage() && - IsImageClass(dex_file.StringDataByIdx(dex_file.GetTypeId(type_idx).descriptor_idx_))) || + IsImageClass(dex_cache->GetDexFile()->StringDataByIdx( + dex_cache->GetDexFile()->GetTypeId(type_idx).descriptor_idx_))) || Runtime::Current()->UseJit()) { - ScopedObjectAccess soa(Thread::Current()); - mirror::DexCache* dex_cache = Runtime::Current()->GetClassLinker()->FindDexCache( - soa.Self(), dex_file, false); mirror::Class* resolved_class = dex_cache->GetResolvedType(type_idx); result = (resolved_class != nullptr); } @@ -1332,32 +1331,16 @@ bool CompilerDriver::CanAssumeStringIsPresentInDexCache(const DexFile& dex_file, return result; } -bool CompilerDriver::CanAccessTypeWithoutChecks(uint32_t referrer_idx, const DexFile& dex_file, - uint32_t type_idx, - bool* type_known_final, bool* type_known_abstract, - bool* equals_referrers_class) { - if (type_known_final != nullptr) { - *type_known_final = false; - } - if (type_known_abstract != nullptr) { - *type_known_abstract = false; - } - if (equals_referrers_class != nullptr) { - *equals_referrers_class = false; - } - ScopedObjectAccess soa(Thread::Current()); - mirror::DexCache* dex_cache = Runtime::Current()->GetClassLinker()->FindDexCache( - soa.Self(), dex_file, false); +bool CompilerDriver::CanAccessTypeWithoutChecks(uint32_t referrer_idx, + Handle<mirror::DexCache> dex_cache, + uint32_t type_idx) { // Get type from dex cache assuming it was populated by the verifier mirror::Class* resolved_class = dex_cache->GetResolvedType(type_idx); if (resolved_class == nullptr) { stats_->TypeNeedsAccessCheck(); return false; // Unknown class needs access checks. } - const DexFile::MethodId& method_id = dex_file.GetMethodId(referrer_idx); - if (equals_referrers_class != nullptr) { - *equals_referrers_class = (method_id.class_idx_ == type_idx); - } + const DexFile::MethodId& method_id = dex_cache->GetDexFile()->GetMethodId(referrer_idx); bool is_accessible = resolved_class->IsPublic(); // Public classes are always accessible. if (!is_accessible) { mirror::Class* referrer_class = dex_cache->GetResolvedType(method_id.class_idx_); @@ -1371,12 +1354,6 @@ bool CompilerDriver::CanAccessTypeWithoutChecks(uint32_t referrer_idx, const Dex } if (is_accessible) { stats_->TypeDoesntNeedAccessCheck(); - if (type_known_final != nullptr) { - *type_known_final = resolved_class->IsFinal() && !resolved_class->IsArrayClass(); - } - if (type_known_abstract != nullptr) { - *type_known_abstract = resolved_class->IsAbstract() && !resolved_class->IsArrayClass(); - } } else { stats_->TypeNeedsAccessCheck(); } @@ -1384,12 +1361,9 @@ bool CompilerDriver::CanAccessTypeWithoutChecks(uint32_t referrer_idx, const Dex } bool CompilerDriver::CanAccessInstantiableTypeWithoutChecks(uint32_t referrer_idx, - const DexFile& dex_file, + Handle<mirror::DexCache> dex_cache, uint32_t type_idx, bool* finalizable) { - ScopedObjectAccess soa(Thread::Current()); - mirror::DexCache* dex_cache = Runtime::Current()->GetClassLinker()->FindDexCache( - soa.Self(), dex_file, false); // Get type from dex cache assuming it was populated by the verifier. mirror::Class* resolved_class = dex_cache->GetResolvedType(type_idx); if (resolved_class == nullptr) { @@ -1399,7 +1373,7 @@ bool CompilerDriver::CanAccessInstantiableTypeWithoutChecks(uint32_t referrer_id return false; // Unknown class needs access checks. } *finalizable = resolved_class->IsFinalizable(); - const DexFile::MethodId& method_id = dex_file.GetMethodId(referrer_idx); + const DexFile::MethodId& method_id = dex_cache->GetDexFile()->GetMethodId(referrer_idx); bool is_accessible = resolved_class->IsPublic(); // Public classes are always accessible. if (!is_accessible) { mirror::Class* referrer_class = dex_cache->GetResolvedType(method_id.class_idx_); @@ -1587,53 +1561,6 @@ bool CompilerDriver::ComputeInstanceFieldInfo(uint32_t field_idx, const DexCompi } } -bool CompilerDriver::ComputeStaticFieldInfo(uint32_t field_idx, const DexCompilationUnit* mUnit, - bool is_put, MemberOffset* field_offset, - uint32_t* storage_index, bool* is_referrers_class, - bool* is_volatile, bool* is_initialized, - Primitive::Type* type) { - ScopedObjectAccess soa(Thread::Current()); - // Try to resolve the field and compiling method's class. - ArtField* resolved_field; - mirror::Class* referrer_class; - Handle<mirror::DexCache> dex_cache(mUnit->GetDexCache()); - { - StackHandleScope<1> hs(soa.Self()); - Handle<mirror::ClassLoader> class_loader_handle( - hs.NewHandle(soa.Decode<mirror::ClassLoader*>(mUnit->GetClassLoader()))); - resolved_field = - ResolveField(soa, dex_cache, class_loader_handle, mUnit, field_idx, true); - referrer_class = resolved_field != nullptr - ? ResolveCompilingMethodsClass(soa, dex_cache, class_loader_handle, mUnit) : nullptr; - } - bool result = false; - if (resolved_field != nullptr && referrer_class != nullptr) { - *is_volatile = IsFieldVolatile(resolved_field); - std::pair<bool, bool> fast_path = IsFastStaticField( - dex_cache.Get(), referrer_class, resolved_field, field_idx, storage_index); - result = is_put ? fast_path.second : fast_path.first; - } - if (result) { - *field_offset = GetFieldOffset(resolved_field); - *is_referrers_class = IsStaticFieldInReferrerClass(referrer_class, resolved_field); - // *is_referrers_class == true implies no worrying about class initialization. - *is_initialized = (*is_referrers_class) || - (IsStaticFieldsClassInitialized(referrer_class, resolved_field) && - CanAssumeTypeIsPresentInDexCache(*mUnit->GetDexFile(), *storage_index)); - *type = resolved_field->GetTypeAsPrimitiveType(); - } else { - // Conservative defaults. - *is_volatile = true; - *field_offset = MemberOffset(static_cast<size_t>(-1)); - *storage_index = -1; - *is_referrers_class = false; - *is_initialized = false; - *type = Primitive::kPrimVoid; - } - ProcessedStaticField(result, *is_referrers_class); - return result; -} - void CompilerDriver::GetCodeAndMethodForDirectCall(InvokeType* type, InvokeType sharp_type, bool no_guarantee_of_dex_cache_entry, const mirror::Class* referrer_class, diff --git a/compiler/driver/compiler_driver.h b/compiler/driver/compiler_driver.h index 4308eac4af..d63dffa49a 100644 --- a/compiler/driver/compiler_driver.h +++ b/compiler/driver/compiler_driver.h @@ -195,25 +195,26 @@ class CompilerDriver { // Callbacks from compiler to see what runtime checks must be generated. - bool CanAssumeTypeIsPresentInDexCache(const DexFile& dex_file, uint32_t type_idx); + bool CanAssumeTypeIsPresentInDexCache(Handle<mirror::DexCache> dex_cache, + uint32_t type_idx) + SHARED_REQUIRES(Locks::mutator_lock_); bool CanAssumeStringIsPresentInDexCache(const DexFile& dex_file, uint32_t string_idx) REQUIRES(!Locks::mutator_lock_); // Are runtime access checks necessary in the compiled code? - bool CanAccessTypeWithoutChecks(uint32_t referrer_idx, const DexFile& dex_file, - uint32_t type_idx, bool* type_known_final = nullptr, - bool* type_known_abstract = nullptr, - bool* equals_referrers_class = nullptr) - REQUIRES(!Locks::mutator_lock_); + bool CanAccessTypeWithoutChecks(uint32_t referrer_idx, + Handle<mirror::DexCache> dex_cache, + uint32_t type_idx) + SHARED_REQUIRES(Locks::mutator_lock_); // Are runtime access and instantiable checks necessary in the code? // out_is_finalizable is set to whether the type is finalizable. bool CanAccessInstantiableTypeWithoutChecks(uint32_t referrer_idx, - const DexFile& dex_file, + Handle<mirror::DexCache> dex_cache, uint32_t type_idx, bool* out_is_finalizable) - REQUIRES(!Locks::mutator_lock_); + SHARED_REQUIRES(Locks::mutator_lock_); bool CanEmbedTypeInCode(const DexFile& dex_file, uint32_t type_idx, bool* is_type_initialized, bool* use_direct_type_ptr, @@ -368,14 +369,6 @@ class CompilerDriver { SHARED_REQUIRES(Locks::mutator_lock_); - // Can we fastpath static field access? Computes field's offset, volatility and whether the - // field is within the referrer (which can avoid checking class initialization). - bool ComputeStaticFieldInfo(uint32_t field_idx, const DexCompilationUnit* mUnit, bool is_put, - MemberOffset* field_offset, uint32_t* storage_index, - bool* is_referrers_class, bool* is_volatile, bool* is_initialized, - Primitive::Type* type) - REQUIRES(!Locks::mutator_lock_); - // Can we fastpath a interface, super class or virtual method call? Computes method's vtable // index. bool ComputeInvokeInfo(const DexCompilationUnit* mUnit, const uint32_t dex_pc, 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, |