diff options
Diffstat (limited to 'compiler/optimizing')
-rw-r--r-- | compiler/optimizing/code_generator.cc | 7 | ||||
-rw-r--r-- | compiler/optimizing/code_generator.h | 5 | ||||
-rw-r--r-- | compiler/optimizing/code_generator_arm.cc | 2 | ||||
-rw-r--r-- | compiler/optimizing/code_generator_arm64.cc | 4 | ||||
-rw-r--r-- | compiler/optimizing/code_generator_mips.cc | 2 | ||||
-rw-r--r-- | compiler/optimizing/code_generator_mips64.cc | 2 | ||||
-rw-r--r-- | compiler/optimizing/code_generator_x86.cc | 2 | ||||
-rw-r--r-- | compiler/optimizing/code_generator_x86_64.cc | 2 | ||||
-rw-r--r-- | compiler/optimizing/graph_visualizer.cc | 5 | ||||
-rw-r--r-- | compiler/optimizing/instruction_simplifier.cc | 26 | ||||
-rw-r--r-- | compiler/optimizing/intrinsics.cc | 6 | ||||
-rw-r--r-- | compiler/optimizing/intrinsics.h | 2 | ||||
-rw-r--r-- | compiler/optimizing/intrinsics_list.h | 2 | ||||
-rw-r--r-- | compiler/optimizing/nodes.h | 13 |
14 files changed, 69 insertions, 11 deletions
diff --git a/compiler/optimizing/code_generator.cc b/compiler/optimizing/code_generator.cc index e7fa4e472b..51fbaea519 100644 --- a/compiler/optimizing/code_generator.cc +++ b/compiler/optimizing/code_generator.cc @@ -50,6 +50,7 @@ #include "mirror/array-inl.h" #include "mirror/object_array-inl.h" #include "mirror/object_reference.h" +#include "mirror/string.h" #include "parallel_move_resolver.h" #include "ssa_liveness_analysis.h" #include "utils/assembler.h" @@ -139,6 +140,12 @@ size_t CodeGenerator::GetCachePointerOffset(uint32_t index) { return pointer_size * index; } +uint32_t CodeGenerator::GetArrayLengthOffset(HArrayLength* array_length) { + return array_length->IsStringLength() + ? mirror::String::CountOffset().Uint32Value() + : mirror::Array::LengthOffset().Uint32Value(); +} + bool CodeGenerator::GoesToNextBlock(HBasicBlock* current, HBasicBlock* next) const { DCHECK_EQ((*block_order_)[current_block_index_], current); return GetNextBlockToEmit() == FirstNonEmptyBlock(next); diff --git a/compiler/optimizing/code_generator.h b/compiler/optimizing/code_generator.h index d69c41055b..6e75e3bb2e 100644 --- a/compiler/optimizing/code_generator.h +++ b/compiler/optimizing/code_generator.h @@ -340,6 +340,11 @@ class CodeGenerator : public DeletableArenaObject<kArenaAllocCodeGenerator> { // Pointer variant for ArtMethod and ArtField arrays. size_t GetCachePointerOffset(uint32_t index); + // Helper that returns the offset of the array's length field. + // Note: Besides the normal arrays, we also use the HArrayLength for + // accessing the String's `count` field in String intrinsics. + static uint32_t GetArrayLengthOffset(HArrayLength* array_length); + void EmitParallelMoves(Location from1, Location to1, Primitive::Type type1, diff --git a/compiler/optimizing/code_generator_arm.cc b/compiler/optimizing/code_generator_arm.cc index 197e473473..e0106628c6 100644 --- a/compiler/optimizing/code_generator_arm.cc +++ b/compiler/optimizing/code_generator_arm.cc @@ -4742,7 +4742,7 @@ void LocationsBuilderARM::VisitArrayLength(HArrayLength* instruction) { void InstructionCodeGeneratorARM::VisitArrayLength(HArrayLength* instruction) { LocationSummary* locations = instruction->GetLocations(); - uint32_t offset = mirror::Array::LengthOffset().Uint32Value(); + uint32_t offset = CodeGenerator::GetArrayLengthOffset(instruction); Register obj = locations->InAt(0).AsRegister<Register>(); Register out = locations->Out().AsRegister<Register>(); __ LoadFromOffset(kLoadWord, out, obj, offset); diff --git a/compiler/optimizing/code_generator_arm64.cc b/compiler/optimizing/code_generator_arm64.cc index 9680f2bf45..261c04f062 100644 --- a/compiler/optimizing/code_generator_arm64.cc +++ b/compiler/optimizing/code_generator_arm64.cc @@ -2118,9 +2118,9 @@ void LocationsBuilderARM64::VisitArrayLength(HArrayLength* instruction) { } void InstructionCodeGeneratorARM64::VisitArrayLength(HArrayLength* instruction) { + uint32_t offset = CodeGenerator::GetArrayLengthOffset(instruction); BlockPoolsScope block_pools(GetVIXLAssembler()); - __ Ldr(OutputRegister(instruction), - HeapOperand(InputRegisterAt(instruction, 0), mirror::Array::LengthOffset())); + __ Ldr(OutputRegister(instruction), HeapOperand(InputRegisterAt(instruction, 0), offset)); codegen_->MaybeRecordImplicitNullCheck(instruction); } diff --git a/compiler/optimizing/code_generator_mips.cc b/compiler/optimizing/code_generator_mips.cc index 12d1164d03..fb50680c91 100644 --- a/compiler/optimizing/code_generator_mips.cc +++ b/compiler/optimizing/code_generator_mips.cc @@ -1803,7 +1803,7 @@ void LocationsBuilderMIPS::VisitArrayLength(HArrayLength* instruction) { void InstructionCodeGeneratorMIPS::VisitArrayLength(HArrayLength* instruction) { LocationSummary* locations = instruction->GetLocations(); - uint32_t offset = mirror::Array::LengthOffset().Uint32Value(); + uint32_t offset = CodeGenerator::GetArrayLengthOffset(instruction); Register obj = locations->InAt(0).AsRegister<Register>(); Register out = locations->Out().AsRegister<Register>(); __ LoadFromOffset(kLoadWord, out, obj, offset); diff --git a/compiler/optimizing/code_generator_mips64.cc b/compiler/optimizing/code_generator_mips64.cc index 56ac38ef84..e67d8d0dc5 100644 --- a/compiler/optimizing/code_generator_mips64.cc +++ b/compiler/optimizing/code_generator_mips64.cc @@ -1426,7 +1426,7 @@ void LocationsBuilderMIPS64::VisitArrayLength(HArrayLength* instruction) { void InstructionCodeGeneratorMIPS64::VisitArrayLength(HArrayLength* instruction) { LocationSummary* locations = instruction->GetLocations(); - uint32_t offset = mirror::Array::LengthOffset().Uint32Value(); + uint32_t offset = CodeGenerator::GetArrayLengthOffset(instruction); GpuRegister obj = locations->InAt(0).AsRegister<GpuRegister>(); GpuRegister out = locations->Out().AsRegister<GpuRegister>(); __ LoadFromOffset(kLoadWord, out, obj, offset); diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc index 6dc480bbee..50892a9d48 100644 --- a/compiler/optimizing/code_generator_x86.cc +++ b/compiler/optimizing/code_generator_x86.cc @@ -5480,7 +5480,7 @@ void LocationsBuilderX86::VisitArrayLength(HArrayLength* instruction) { void InstructionCodeGeneratorX86::VisitArrayLength(HArrayLength* instruction) { LocationSummary* locations = instruction->GetLocations(); - uint32_t offset = mirror::Array::LengthOffset().Uint32Value(); + uint32_t offset = CodeGenerator::GetArrayLengthOffset(instruction); Register obj = locations->InAt(0).AsRegister<Register>(); Register out = locations->Out().AsRegister<Register>(); __ movl(out, Address(obj, offset)); diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc index 96ec09c2a8..56c5b06945 100644 --- a/compiler/optimizing/code_generator_x86_64.cc +++ b/compiler/optimizing/code_generator_x86_64.cc @@ -4956,7 +4956,7 @@ void LocationsBuilderX86_64::VisitArrayLength(HArrayLength* instruction) { void InstructionCodeGeneratorX86_64::VisitArrayLength(HArrayLength* instruction) { LocationSummary* locations = instruction->GetLocations(); - uint32_t offset = mirror::Array::LengthOffset().Uint32Value(); + uint32_t offset = CodeGenerator::GetArrayLengthOffset(instruction); CpuRegister obj = locations->InAt(0).AsRegister<CpuRegister>(); CpuRegister out = locations->Out().AsRegister<CpuRegister>(); __ movl(out, Address(obj, offset)); diff --git a/compiler/optimizing/graph_visualizer.cc b/compiler/optimizing/graph_visualizer.cc index 9efc13f61b..568b8a8df6 100644 --- a/compiler/optimizing/graph_visualizer.cc +++ b/compiler/optimizing/graph_visualizer.cc @@ -389,6 +389,11 @@ class HGraphVisualizerPrinter : public HGraphDelegateVisitor { << instance_of->MustDoNullCheck() << std::noboolalpha; } + void VisitArrayLength(HArrayLength* array_length) OVERRIDE { + StartAttributeStream("is_string_length") << std::boolalpha + << array_length->IsStringLength() << std::noboolalpha; + } + void VisitArraySet(HArraySet* array_set) OVERRIDE { StartAttributeStream("value_can_be_null") << std::boolalpha << array_set->GetValueCanBeNull() << std::noboolalpha; diff --git a/compiler/optimizing/instruction_simplifier.cc b/compiler/optimizing/instruction_simplifier.cc index d7b3856bf4..fd79901ffc 100644 --- a/compiler/optimizing/instruction_simplifier.cc +++ b/compiler/optimizing/instruction_simplifier.cc @@ -101,6 +101,7 @@ class InstructionSimplifierVisitor : public HGraphDelegateVisitor { void SimplifyCompare(HInvoke* invoke, bool is_signum, Primitive::Type type); void SimplifyIsNaN(HInvoke* invoke); void SimplifyFP2Int(HInvoke* invoke); + void SimplifyStringIsEmptyOrLength(HInvoke* invoke); void SimplifyMemBarrier(HInvoke* invoke, MemBarrierKind barrier_kind); OptimizingCompilerStats* stats_; @@ -1673,6 +1674,27 @@ void InstructionSimplifierVisitor::SimplifyFP2Int(HInvoke* invoke) { invoke->ReplaceWithExceptInReplacementAtIndex(select, 0); // false at index 0 } +void InstructionSimplifierVisitor::SimplifyStringIsEmptyOrLength(HInvoke* invoke) { + HInstruction* str = invoke->InputAt(0); + uint32_t dex_pc = invoke->GetDexPc(); + // We treat String as an array to allow DCE and BCE to seamlessly work on strings, + // so create the HArrayLength. + HArrayLength* length = new (GetGraph()->GetArena()) HArrayLength(str, dex_pc); + length->MarkAsStringLength(); + HInstruction* replacement; + if (invoke->GetIntrinsic() == Intrinsics::kStringIsEmpty) { + // For String.isEmpty(), create the `HEqual` representing the `length == 0`. + invoke->GetBlock()->InsertInstructionBefore(length, invoke); + HIntConstant* zero = GetGraph()->GetIntConstant(0); + HEqual* equal = new (GetGraph()->GetArena()) HEqual(length, zero, dex_pc); + replacement = equal; + } else { + DCHECK_EQ(invoke->GetIntrinsic(), Intrinsics::kStringLength); + replacement = length; + } + invoke->GetBlock()->ReplaceAndRemoveInstructionWith(invoke, replacement); +} + void InstructionSimplifierVisitor::SimplifyMemBarrier(HInvoke* invoke, MemBarrierKind barrier_kind) { uint32_t dex_pc = invoke->GetDexPc(); HMemoryBarrier* mem_barrier = new (GetGraph()->GetArena()) HMemoryBarrier(barrier_kind, dex_pc); @@ -1719,6 +1741,10 @@ void InstructionSimplifierVisitor::VisitInvoke(HInvoke* instruction) { case Intrinsics::kDoubleDoubleToLongBits: SimplifyFP2Int(instruction); break; + case Intrinsics::kStringIsEmpty: + case Intrinsics::kStringLength: + SimplifyStringIsEmptyOrLength(instruction); + break; case Intrinsics::kUnsafeLoadFence: SimplifyMemBarrier(instruction, MemBarrierKind::kLoadAny); break; diff --git a/compiler/optimizing/intrinsics.cc b/compiler/optimizing/intrinsics.cc index 5d4c4e2950..418d59c6cb 100644 --- a/compiler/optimizing/intrinsics.cc +++ b/compiler/optimizing/intrinsics.cc @@ -388,10 +388,8 @@ static Intrinsics GetIntrinsic(InlineMethod method) { case kIntrinsicGetCharsNoCheck: return Intrinsics::kStringGetCharsNoCheck; case kIntrinsicIsEmptyOrLength: - // The inliner can handle these two cases - and this is the preferred approach - // since after inlining the call is no longer visible (as opposed to waiting - // until codegen to handle intrinsic). - return Intrinsics::kNone; + return ((method.d.data & kIntrinsicFlagIsEmpty) == 0) ? + Intrinsics::kStringLength : Intrinsics::kStringIsEmpty; case kIntrinsicIndexOf: return ((method.d.data & kIntrinsicFlagBase0) == 0) ? Intrinsics::kStringIndexOfAfter : Intrinsics::kStringIndexOf; diff --git a/compiler/optimizing/intrinsics.h b/compiler/optimizing/intrinsics.h index 39a1313ba0..214250f337 100644 --- a/compiler/optimizing/intrinsics.h +++ b/compiler/optimizing/intrinsics.h @@ -239,6 +239,8 @@ UNREACHABLE_INTRINSIC(Arch, IntegerCompare) \ UNREACHABLE_INTRINSIC(Arch, LongCompare) \ UNREACHABLE_INTRINSIC(Arch, IntegerSignum) \ UNREACHABLE_INTRINSIC(Arch, LongSignum) \ +UNREACHABLE_INTRINSIC(Arch, StringIsEmpty) \ +UNREACHABLE_INTRINSIC(Arch, StringLength) \ UNREACHABLE_INTRINSIC(Arch, UnsafeLoadFence) \ UNREACHABLE_INTRINSIC(Arch, UnsafeStoreFence) \ UNREACHABLE_INTRINSIC(Arch, UnsafeFullFence) diff --git a/compiler/optimizing/intrinsics_list.h b/compiler/optimizing/intrinsics_list.h index dd9294d486..db60238fb4 100644 --- a/compiler/optimizing/intrinsics_list.h +++ b/compiler/optimizing/intrinsics_list.h @@ -107,6 +107,8 @@ V(StringGetCharsNoCheck, kDirect, kNeedsEnvironmentOrCache, kReadSideEffects, kCanThrow) \ V(StringIndexOf, kDirect, kNeedsEnvironmentOrCache, kReadSideEffects, kCanThrow) \ V(StringIndexOfAfter, kDirect, kNeedsEnvironmentOrCache, kReadSideEffects, kCanThrow) \ + V(StringIsEmpty, kDirect, kNeedsEnvironmentOrCache, kReadSideEffects, kNoThrow) \ + V(StringLength, kDirect, kNeedsEnvironmentOrCache, kReadSideEffects, kNoThrow) \ V(StringNewStringFromBytes, kStatic, kNeedsEnvironmentOrCache, kAllSideEffects, kCanThrow) \ V(StringNewStringFromChars, kStatic, kNeedsEnvironmentOrCache, kAllSideEffects, kCanThrow) \ V(StringNewStringFromString, kStatic, kNeedsEnvironmentOrCache, kAllSideEffects, kCanThrow) \ diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h index afb995d438..88eba4c4fb 100644 --- a/compiler/optimizing/nodes.h +++ b/compiler/optimizing/nodes.h @@ -5226,9 +5226,22 @@ class HArrayLength : public HExpression<1> { return obj == InputAt(0); } + void MarkAsStringLength() { SetPackedFlag<kFlagIsStringLength>(); } + bool IsStringLength() const { return GetPackedFlag<kFlagIsStringLength>(); } + DECLARE_INSTRUCTION(ArrayLength); private: + // We treat a String as an array, creating the HArrayLength from String.length() + // or String.isEmpty() intrinsic in the instruction simplifier. We can always + // determine whether a particular HArrayLength is actually a String.length() by + // looking at the type of the input but that requires holding the mutator lock, so + // we prefer to use a flag, so that code generators don't need to do the locking. + static constexpr size_t kFlagIsStringLength = kNumberOfExpressionPackedBits; + static constexpr size_t kNumberOfArrayLengthPackedBits = kFlagIsStringLength + 1; + static_assert(kNumberOfArrayLengthPackedBits <= HInstruction::kMaxNumberOfPackedBits, + "Too many packed fields."); + DISALLOW_COPY_AND_ASSIGN(HArrayLength); }; |