diff options
author | 2016-07-06 16:23:04 +0100 | |
---|---|---|
committer | 2016-07-21 09:16:43 +0000 | |
commit | 328429ff48d06e2cad4ebdd3568ab06de916a10a (patch) | |
tree | 6290ac8afc3e93488382727f6765f548a2cfff04 | |
parent | 79e73245140f4115039a7284b3797d701f368fe6 (diff) |
ARM: Port instr simplification of array accesses.
After changing the addressing mode for array accesses (in
https://android-review.googlesource.com/248406) the 'add'
instruction that calculates the base address for the array can be
shared across accesses to the same array.
Before https://android-review.googlesource.com/248406:
add IP, r[Array], r[Index0], LSL #2
ldr r0, [IP, #12]
add IP, r[Array], r[Index1], LSL #2
ldr r0, [IP, #12]
Before this CL:
add IP. r[Array], #12
ldr r0, [IP, r[Index0], LSL #2]
add IP. r[Array], #12
ldr r0, [IP, r[Index1], LSL #2]
After this CL:
add IP. r[Array], #12
ldr r0, [IP, r[Index0], LSL #2]
ldr r0, [IP, r[Index1], LSL #2]
Link to the original optimization:
https://android-review.googlesource.com/#/c/127310/
Test: Run ART test suite on Nexus 6.
Change-Id: Iee26f9a0a7ca46abb90e3f60d19d22dc8dee4d8f
-rw-r--r-- | compiler/optimizing/code_generator_arm.cc | 93 | ||||
-rw-r--r-- | compiler/optimizing/code_generator_arm64.cc | 40 | ||||
-rw-r--r-- | compiler/optimizing/common_arm64.h | 2 | ||||
-rw-r--r-- | compiler/optimizing/instruction_simplifier_arm.cc | 42 | ||||
-rw-r--r-- | compiler/optimizing/instruction_simplifier_arm.h | 2 | ||||
-rw-r--r-- | compiler/optimizing/instruction_simplifier_arm64.cc | 70 | ||||
-rw-r--r-- | compiler/optimizing/instruction_simplifier_arm64.h | 4 | ||||
-rw-r--r-- | compiler/optimizing/instruction_simplifier_shared.cc | 55 | ||||
-rw-r--r-- | compiler/optimizing/instruction_simplifier_shared.h | 5 | ||||
-rw-r--r-- | compiler/optimizing/nodes.h | 6 | ||||
-rw-r--r-- | compiler/optimizing/nodes_arm64.h | 26 | ||||
-rw-r--r-- | compiler/optimizing/nodes_shared.h | 28 | ||||
-rw-r--r-- | compiler/optimizing/optimizing_compiler.cc | 4 | ||||
-rw-r--r-- | test/527-checker-array-access-split/src/Main.java | 298 | ||||
-rw-r--r-- | test/Android.run-test.mk | 2 |
15 files changed, 526 insertions, 151 deletions
diff --git a/compiler/optimizing/code_generator_arm.cc b/compiler/optimizing/code_generator_arm.cc index 105b7b49d3..395db32f93 100644 --- a/compiler/optimizing/code_generator_arm.cc +++ b/compiler/optimizing/code_generator_arm.cc @@ -4437,6 +4437,10 @@ void InstructionCodeGeneratorARM::VisitArrayGet(HArrayGet* instruction) { Location out_loc = locations->Out(); uint32_t data_offset = CodeGenerator::GetArrayDataOffset(instruction); Primitive::Type type = instruction->GetType(); + HInstruction* array_instr = instruction->GetArray(); + bool has_intermediate_address = array_instr->IsIntermediateAddress(); + // The read barrier instrumentation does not support the HIntermediateAddress instruction yet. + DCHECK(!(has_intermediate_address && kEmitCompilerReadBarrier)); switch (type) { case Primitive::kPrimBoolean: @@ -4451,8 +4455,21 @@ void InstructionCodeGeneratorARM::VisitArrayGet(HArrayGet* instruction) { LoadOperandType load_type = GetLoadOperandType(type); __ LoadFromOffset(load_type, out_loc.AsRegister<Register>(), obj, full_offset); } else { - __ add(IP, obj, ShifterOperand(data_offset)); - codegen_->LoadFromShiftedRegOffset(type, out_loc, IP, index.AsRegister<Register>()); + Register temp = IP; + + if (has_intermediate_address) { + // We do not need to compute the intermediate address from the array: the + // input instruction has done it already. See the comment in + // `TryExtractArrayAccessAddress()`. + if (kIsDebugBuild) { + HIntermediateAddress* tmp = array_instr->AsIntermediateAddress(); + DCHECK_EQ(tmp->GetOffset()->AsIntConstant()->GetValueAsUint64(), data_offset); + } + temp = obj; + } else { + __ add(temp, obj, ShifterOperand(data_offset)); + } + codegen_->LoadFromShiftedRegOffset(type, out_loc, temp, index.AsRegister<Register>()); } break; } @@ -4481,8 +4498,21 @@ void InstructionCodeGeneratorARM::VisitArrayGet(HArrayGet* instruction) { // reference, if heap poisoning is enabled). codegen_->MaybeGenerateReadBarrierSlow(instruction, out_loc, out_loc, obj_loc, offset); } else { - __ add(IP, obj, ShifterOperand(data_offset)); - codegen_->LoadFromShiftedRegOffset(type, out_loc, IP, index.AsRegister<Register>()); + Register temp = IP; + + if (has_intermediate_address) { + // We do not need to compute the intermediate address from the array: the + // input instruction has done it already. See the comment in + // `TryExtractArrayAccessAddress()`. + if (kIsDebugBuild) { + HIntermediateAddress* tmp = array_instr->AsIntermediateAddress(); + DCHECK_EQ(tmp->GetOffset()->AsIntConstant()->GetValueAsUint64(), data_offset); + } + temp = obj; + } else { + __ add(temp, obj, ShifterOperand(data_offset)); + } + codegen_->LoadFromShiftedRegOffset(type, out_loc, temp, index.AsRegister<Register>()); codegen_->MaybeRecordImplicitNullCheck(instruction); // If read barriers are enabled, emit read barriers other than @@ -4585,6 +4615,10 @@ void InstructionCodeGeneratorARM::VisitArraySet(HArraySet* instruction) { uint32_t data_offset = mirror::Array::DataOffset(Primitive::ComponentSize(value_type)).Uint32Value(); Location value_loc = locations->InAt(2); + HInstruction* array_instr = instruction->GetArray(); + bool has_intermediate_address = array_instr->IsIntermediateAddress(); + // The read barrier instrumentation does not support the HIntermediateAddress instruction yet. + DCHECK(!(has_intermediate_address && kEmitCompilerReadBarrier)); switch (value_type) { case Primitive::kPrimBoolean: @@ -4599,10 +4633,23 @@ void InstructionCodeGeneratorARM::VisitArraySet(HArraySet* instruction) { StoreOperandType store_type = GetStoreOperandType(value_type); __ StoreToOffset(store_type, value_loc.AsRegister<Register>(), array, full_offset); } else { - __ add(IP, array, ShifterOperand(data_offset)); + Register temp = IP; + + if (has_intermediate_address) { + // We do not need to compute the intermediate address from the array: the + // input instruction has done it already. See the comment in + // `TryExtractArrayAccessAddress()`. + if (kIsDebugBuild) { + HIntermediateAddress* tmp = array_instr->AsIntermediateAddress(); + DCHECK(tmp->GetOffset()->AsIntConstant()->GetValueAsUint64() == data_offset); + } + temp = array; + } else { + __ add(temp, array, ShifterOperand(data_offset)); + } codegen_->StoreToShiftedRegOffset(value_type, value_loc, - IP, + temp, index.AsRegister<Register>()); } break; @@ -4610,6 +4657,9 @@ void InstructionCodeGeneratorARM::VisitArraySet(HArraySet* instruction) { case Primitive::kPrimNot: { Register value = value_loc.AsRegister<Register>(); + // TryExtractArrayAccessAddress optimization is never applied for non-primitive ArraySet. + // See the comment in instruction_simplifier_shared.cc. + DCHECK(!has_intermediate_address); if (instruction->InputAt(2)->IsNullConstant()) { // Just setting null. @@ -4832,6 +4882,37 @@ void InstructionCodeGeneratorARM::VisitArrayLength(HArrayLength* instruction) { codegen_->MaybeRecordImplicitNullCheck(instruction); } +void LocationsBuilderARM::VisitIntermediateAddress(HIntermediateAddress* instruction) { + // The read barrier instrumentation does not support the HIntermediateAddress instruction yet. + DCHECK(!kEmitCompilerReadBarrier); + LocationSummary* locations = + new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall); + + locations->SetInAt(0, Location::RequiresRegister()); + locations->SetInAt(1, Location::RegisterOrConstant(instruction->GetOffset())); + locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); +} + +void InstructionCodeGeneratorARM::VisitIntermediateAddress(HIntermediateAddress* instruction) { + LocationSummary* locations = instruction->GetLocations(); + Location out = locations->Out(); + Location first = locations->InAt(0); + Location second = locations->InAt(1); + + // The read barrier instrumentation does not support the HIntermediateAddress instruction yet. + DCHECK(!kEmitCompilerReadBarrier); + + if (second.IsRegister()) { + __ add(out.AsRegister<Register>(), + first.AsRegister<Register>(), + ShifterOperand(second.AsRegister<Register>())); + } else { + __ AddConstant(out.AsRegister<Register>(), + first.AsRegister<Register>(), + second.GetConstant()->AsIntConstant()->GetValue()); + } +} + void LocationsBuilderARM::VisitBoundsCheck(HBoundsCheck* instruction) { LocationSummary::CallKind call_kind = instruction->CanThrowIntoCatchBlock() ? LocationSummary::kCallOnSlowPath diff --git a/compiler/optimizing/code_generator_arm64.cc b/compiler/optimizing/code_generator_arm64.cc index 07b7823571..c7b24ba1a3 100644 --- a/compiler/optimizing/code_generator_arm64.cc +++ b/compiler/optimizing/code_generator_arm64.cc @@ -690,10 +690,9 @@ class ReadBarrierForHeapReferenceSlowPathARM64 : public SlowPathCodeARM64 { instruction_->GetLocations()->Intrinsified())) << "Unexpected instruction in read barrier for heap reference slow path: " << instruction_->DebugName(); - // The read barrier instrumentation does not support the - // HArm64IntermediateAddress instruction yet. + // The read barrier instrumentation does not support the HIntermediateAddress instruction yet. DCHECK(!(instruction_->IsArrayGet() && - instruction_->AsArrayGet()->GetArray()->IsArm64IntermediateAddress())); + instruction_->AsArrayGet()->GetArray()->IsIntermediateAddress())); __ Bind(GetEntryLabel()); @@ -1983,9 +1982,8 @@ void InstructionCodeGeneratorARM64::VisitArm64DataProcWithShifterOp( } } -void LocationsBuilderARM64::VisitArm64IntermediateAddress(HArm64IntermediateAddress* instruction) { - // The read barrier instrumentation does not support the - // HArm64IntermediateAddress instruction yet. +void LocationsBuilderARM64::VisitIntermediateAddress(HIntermediateAddress* instruction) { + // The read barrier instrumentation does not support the HIntermediateAddress instruction yet. DCHECK(!kEmitCompilerReadBarrier); LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instruction, LocationSummary::kNoCall); @@ -1994,10 +1992,9 @@ void LocationsBuilderARM64::VisitArm64IntermediateAddress(HArm64IntermediateAddr locations->SetOut(Location::RequiresRegister()); } -void InstructionCodeGeneratorARM64::VisitArm64IntermediateAddress( - HArm64IntermediateAddress* instruction) { - // The read barrier instrumentation does not support the - // HArm64IntermediateAddress instruction yet. +void InstructionCodeGeneratorARM64::VisitIntermediateAddress( + HIntermediateAddress* instruction) { + // The read barrier instrumentation does not support the HIntermediateAddress instruction yet. DCHECK(!kEmitCompilerReadBarrier); __ Add(OutputRegister(instruction), InputRegisterAt(instruction, 0), @@ -2097,9 +2094,8 @@ void InstructionCodeGeneratorARM64::VisitArrayGet(HArrayGet* instruction) { if (type == Primitive::kPrimNot && kEmitCompilerReadBarrier && kUseBakerReadBarrier) { // Object ArrayGet with Baker's read barrier case. Register temp = temps.AcquireW(); - // The read barrier instrumentation does not support the - // HArm64IntermediateAddress instruction yet. - DCHECK(!instruction->GetArray()->IsArm64IntermediateAddress()); + // The read barrier instrumentation does not support the HIntermediateAddress instruction yet. + DCHECK(!instruction->GetArray()->IsIntermediateAddress()); // Note that a potential implicit null check is handled in the // CodeGeneratorARM64::GenerateArrayLoadWithBakerReadBarrier call. codegen_->GenerateArrayLoadWithBakerReadBarrier( @@ -2112,15 +2108,15 @@ void InstructionCodeGeneratorARM64::VisitArrayGet(HArrayGet* instruction) { source = HeapOperand(obj, offset); } else { Register temp = temps.AcquireSameSizeAs(obj); - if (instruction->GetArray()->IsArm64IntermediateAddress()) { + if (instruction->GetArray()->IsIntermediateAddress()) { // The read barrier instrumentation does not support the - // HArm64IntermediateAddress instruction yet. + // HIntermediateAddress instruction yet. DCHECK(!kEmitCompilerReadBarrier); // We do not need to compute the intermediate address from the array: the // input instruction has done it already. See the comment in - // `InstructionSimplifierArm64::TryExtractArrayAccessAddress()`. + // `TryExtractArrayAccessAddress()`. if (kIsDebugBuild) { - HArm64IntermediateAddress* tmp = instruction->GetArray()->AsArm64IntermediateAddress(); + HIntermediateAddress* tmp = instruction->GetArray()->AsIntermediateAddress(); DCHECK_EQ(tmp->GetOffset()->AsIntConstant()->GetValueAsUint64(), offset); } temp = obj; @@ -2204,15 +2200,15 @@ void InstructionCodeGeneratorARM64::VisitArraySet(HArraySet* instruction) { } else { UseScratchRegisterScope temps(masm); Register temp = temps.AcquireSameSizeAs(array); - if (instruction->GetArray()->IsArm64IntermediateAddress()) { + if (instruction->GetArray()->IsIntermediateAddress()) { // The read barrier instrumentation does not support the - // HArm64IntermediateAddress instruction yet. + // HIntermediateAddress instruction yet. DCHECK(!kEmitCompilerReadBarrier); // We do not need to compute the intermediate address from the array: the // input instruction has done it already. See the comment in - // `InstructionSimplifierArm64::TryExtractArrayAccessAddress()`. + // `TryExtractArrayAccessAddress()`. if (kIsDebugBuild) { - HArm64IntermediateAddress* tmp = instruction->GetArray()->AsArm64IntermediateAddress(); + HIntermediateAddress* tmp = instruction->GetArray()->AsIntermediateAddress(); DCHECK(tmp->GetOffset()->AsIntConstant()->GetValueAsUint64() == offset); } temp = array; @@ -2228,7 +2224,7 @@ void InstructionCodeGeneratorARM64::VisitArraySet(HArraySet* instruction) { codegen_->MaybeRecordImplicitNullCheck(instruction); } else { DCHECK(needs_write_barrier); - DCHECK(!instruction->GetArray()->IsArm64IntermediateAddress()); + DCHECK(!instruction->GetArray()->IsIntermediateAddress()); vixl::aarch64::Label done; SlowPathCodeARM64* slow_path = nullptr; { diff --git a/compiler/optimizing/common_arm64.h b/compiler/optimizing/common_arm64.h index d2afa5b914..af0ee4e197 100644 --- a/compiler/optimizing/common_arm64.h +++ b/compiler/optimizing/common_arm64.h @@ -227,7 +227,7 @@ static bool CanEncodeConstantAsImmediate(HConstant* constant, HInstruction* inst return vixl::aarch64::Assembler::IsImmMovn(value, vixl::aarch64::kXRegSize); } else { DCHECK(instr->IsAdd() || - instr->IsArm64IntermediateAddress() || + instr->IsIntermediateAddress() || instr->IsBoundsCheck() || instr->IsCompare() || instr->IsCondition() || diff --git a/compiler/optimizing/instruction_simplifier_arm.cc b/compiler/optimizing/instruction_simplifier_arm.cc index cd026b8770..495f3fd232 100644 --- a/compiler/optimizing/instruction_simplifier_arm.cc +++ b/compiler/optimizing/instruction_simplifier_arm.cc @@ -14,8 +14,10 @@ * limitations under the License. */ +#include "code_generator.h" #include "instruction_simplifier_arm.h" #include "instruction_simplifier_shared.h" +#include "mirror/array-inl.h" namespace art { namespace arm { @@ -38,6 +40,46 @@ void InstructionSimplifierArmVisitor::VisitAnd(HAnd* instruction) { } } +void InstructionSimplifierArmVisitor::VisitArrayGet(HArrayGet* instruction) { + size_t data_offset = CodeGenerator::GetArrayDataOffset(instruction); + Primitive::Type type = instruction->GetType(); + + if (type == Primitive::kPrimLong + || type == Primitive::kPrimFloat + || type == Primitive::kPrimDouble) { + // T32 doesn't support ShiftedRegOffset mem address mode for these types + // to enable optimization. + return; + } + + if (TryExtractArrayAccessAddress(instruction, + instruction->GetArray(), + instruction->GetIndex(), + data_offset)) { + RecordSimplification(); + } +} + +void InstructionSimplifierArmVisitor::VisitArraySet(HArraySet* instruction) { + size_t access_size = Primitive::ComponentSize(instruction->GetComponentType()); + size_t data_offset = mirror::Array::DataOffset(access_size).Uint32Value(); + Primitive::Type type = instruction->GetComponentType(); + + if (type == Primitive::kPrimLong + || type == Primitive::kPrimFloat + || type == Primitive::kPrimDouble) { + // T32 doesn't support ShiftedRegOffset mem address mode for these types + // to enable optimization. + return; + } + + if (TryExtractArrayAccessAddress(instruction, + instruction->GetArray(), + instruction->GetIndex(), + data_offset)) { + RecordSimplification(); + } +} } // namespace arm } // namespace art diff --git a/compiler/optimizing/instruction_simplifier_arm.h b/compiler/optimizing/instruction_simplifier_arm.h index 14c940eb21..3d297dacc0 100644 --- a/compiler/optimizing/instruction_simplifier_arm.h +++ b/compiler/optimizing/instruction_simplifier_arm.h @@ -38,6 +38,8 @@ class InstructionSimplifierArmVisitor : public HGraphVisitor { void VisitMul(HMul* instruction) OVERRIDE; void VisitOr(HOr* instruction) OVERRIDE; void VisitAnd(HAnd* instruction) OVERRIDE; + void VisitArrayGet(HArrayGet* instruction) OVERRIDE; + void VisitArraySet(HArraySet* instruction) OVERRIDE; OptimizingCompilerStats* stats_; }; diff --git a/compiler/optimizing/instruction_simplifier_arm64.cc b/compiler/optimizing/instruction_simplifier_arm64.cc index 983d31d168..6d107d571f 100644 --- a/compiler/optimizing/instruction_simplifier_arm64.cc +++ b/compiler/optimizing/instruction_simplifier_arm64.cc @@ -28,56 +28,6 @@ using helpers::CanFitInShifterOperand; using helpers::HasShifterOperand; using helpers::ShifterOperandSupportsExtension; -void InstructionSimplifierArm64Visitor::TryExtractArrayAccessAddress(HInstruction* access, - HInstruction* array, - HInstruction* index, - size_t data_offset) { - if (kEmitCompilerReadBarrier) { - // The read barrier instrumentation does not support the - // HArm64IntermediateAddress instruction yet. - // - // TODO: Handle this case properly in the ARM64 code generator and - // re-enable this optimization; otherwise, remove this TODO. - // b/26601270 - return; - } - if (index->IsConstant() || - (index->IsBoundsCheck() && index->AsBoundsCheck()->GetIndex()->IsConstant())) { - // When the index is a constant all the addressing can be fitted in the - // memory access instruction, so do not split the access. - return; - } - if (access->IsArraySet() && - access->AsArraySet()->GetValue()->GetType() == Primitive::kPrimNot) { - // The access may require a runtime call or the original array pointer. - return; - } - - // Proceed to extract the base address computation. - ArenaAllocator* arena = GetGraph()->GetArena(); - - HIntConstant* offset = GetGraph()->GetIntConstant(data_offset); - HArm64IntermediateAddress* address = - new (arena) HArm64IntermediateAddress(array, offset, kNoDexPc); - address->SetReferenceTypeInfo(array->GetReferenceTypeInfo()); - access->GetBlock()->InsertInstructionBefore(address, access); - access->ReplaceInput(address, 0); - // Both instructions must depend on GC to prevent any instruction that can - // trigger GC to be inserted between the two. - access->AddSideEffects(SideEffects::DependsOnGC()); - DCHECK(address->GetSideEffects().Includes(SideEffects::DependsOnGC())); - DCHECK(access->GetSideEffects().Includes(SideEffects::DependsOnGC())); - // TODO: Code generation for HArrayGet and HArraySet will check whether the input address - // is an HArm64IntermediateAddress and generate appropriate code. - // We would like to replace the `HArrayGet` and `HArraySet` with custom instructions (maybe - // `HArm64Load` and `HArm64Store`). We defer these changes because these new instructions would - // not bring any advantages yet. - // Also see the comments in - // `InstructionCodeGeneratorARM64::VisitArrayGet()` and - // `InstructionCodeGeneratorARM64::VisitArraySet()`. - RecordSimplification(); -} - bool InstructionSimplifierArm64Visitor::TryMergeIntoShifterOperand(HInstruction* use, HInstruction* bitfield_op, bool do_merge) { @@ -190,19 +140,23 @@ void InstructionSimplifierArm64Visitor::VisitAnd(HAnd* instruction) { void InstructionSimplifierArm64Visitor::VisitArrayGet(HArrayGet* instruction) { size_t data_offset = CodeGenerator::GetArrayDataOffset(instruction); - TryExtractArrayAccessAddress(instruction, - instruction->GetArray(), - instruction->GetIndex(), - data_offset); + if (TryExtractArrayAccessAddress(instruction, + instruction->GetArray(), + instruction->GetIndex(), + data_offset)) { + RecordSimplification(); + } } void InstructionSimplifierArm64Visitor::VisitArraySet(HArraySet* instruction) { size_t access_size = Primitive::ComponentSize(instruction->GetComponentType()); size_t data_offset = mirror::Array::DataOffset(access_size).Uint32Value(); - TryExtractArrayAccessAddress(instruction, - instruction->GetArray(), - instruction->GetIndex(), - data_offset); + if (TryExtractArrayAccessAddress(instruction, + instruction->GetArray(), + instruction->GetIndex(), + data_offset)) { + RecordSimplification(); + } } void InstructionSimplifierArm64Visitor::VisitMul(HMul* instruction) { diff --git a/compiler/optimizing/instruction_simplifier_arm64.h b/compiler/optimizing/instruction_simplifier_arm64.h index 4735f85ab0..28648b3bea 100644 --- a/compiler/optimizing/instruction_simplifier_arm64.h +++ b/compiler/optimizing/instruction_simplifier_arm64.h @@ -35,10 +35,6 @@ class InstructionSimplifierArm64Visitor : public HGraphVisitor { } } - void TryExtractArrayAccessAddress(HInstruction* access, - HInstruction* array, - HInstruction* index, - size_t data_offset); bool TryMergeIntoUsersShifterOperand(HInstruction* instruction); bool TryMergeIntoShifterOperand(HInstruction* use, HInstruction* bitfield_op, diff --git a/compiler/optimizing/instruction_simplifier_shared.cc b/compiler/optimizing/instruction_simplifier_shared.cc index dab1ebc16d..8f7778fe68 100644 --- a/compiler/optimizing/instruction_simplifier_shared.cc +++ b/compiler/optimizing/instruction_simplifier_shared.cc @@ -226,4 +226,59 @@ bool TryMergeNegatedInput(HBinaryOperation* op) { return false; } + +bool TryExtractArrayAccessAddress(HInstruction* access, + HInstruction* array, + HInstruction* index, + size_t data_offset) { + if (kEmitCompilerReadBarrier) { + // The read barrier instrumentation does not support the + // HIntermediateAddress instruction yet. + // + // TODO: Handle this case properly in the ARM64 and ARM code generator and + // re-enable this optimization; otherwise, remove this TODO. + // b/26601270 + return false; + } + if (index->IsConstant() || + (index->IsBoundsCheck() && index->AsBoundsCheck()->GetIndex()->IsConstant())) { + // When the index is a constant all the addressing can be fitted in the + // memory access instruction, so do not split the access. + return false; + } + if (access->IsArraySet() && + access->AsArraySet()->GetValue()->GetType() == Primitive::kPrimNot) { + // The access may require a runtime call or the original array pointer. + return false; + } + + // Proceed to extract the base address computation. + HGraph* graph = access->GetBlock()->GetGraph(); + ArenaAllocator* arena = graph->GetArena(); + + HIntConstant* offset = graph->GetIntConstant(data_offset); + HIntermediateAddress* address = + new (arena) HIntermediateAddress(array, offset, kNoDexPc); + address->SetReferenceTypeInfo(array->GetReferenceTypeInfo()); + access->GetBlock()->InsertInstructionBefore(address, access); + access->ReplaceInput(address, 0); + // Both instructions must depend on GC to prevent any instruction that can + // trigger GC to be inserted between the two. + access->AddSideEffects(SideEffects::DependsOnGC()); + DCHECK(address->GetSideEffects().Includes(SideEffects::DependsOnGC())); + DCHECK(access->GetSideEffects().Includes(SideEffects::DependsOnGC())); + // TODO: Code generation for HArrayGet and HArraySet will check whether the input address + // is an HIntermediateAddress and generate appropriate code. + // We would like to replace the `HArrayGet` and `HArraySet` with custom instructions (maybe + // `HArm64Load` and `HArm64Store`,`HArmLoad` and `HArmStore`). We defer these changes + // because these new instructions would not bring any advantages yet. + // Also see the comments in + // `InstructionCodeGeneratorARM::VisitArrayGet()` + // `InstructionCodeGeneratorARM::VisitArraySet()` + // `InstructionCodeGeneratorARM64::VisitArrayGet()` + // `InstructionCodeGeneratorARM64::VisitArraySet()`. + return true; +} + + } // namespace art diff --git a/compiler/optimizing/instruction_simplifier_shared.h b/compiler/optimizing/instruction_simplifier_shared.h index b1fe8f4756..56804f5e90 100644 --- a/compiler/optimizing/instruction_simplifier_shared.h +++ b/compiler/optimizing/instruction_simplifier_shared.h @@ -26,6 +26,11 @@ bool TryCombineMultiplyAccumulate(HMul* mul, InstructionSet isa); // a negated bitwise instruction. bool TryMergeNegatedInput(HBinaryOperation* op); +bool TryExtractArrayAccessAddress(HInstruction* access, + HInstruction* array, + HInstruction* index, + size_t data_offset); + } // namespace art #endif // ART_COMPILER_OPTIMIZING_INSTRUCTION_SIMPLIFIER_SHARED_H_ diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h index 0f0ef26ea9..23ac457568 100644 --- a/compiler/optimizing/nodes.h +++ b/compiler/optimizing/nodes.h @@ -1289,7 +1289,8 @@ class HLoopInformationOutwardIterator : public ValueObject { #else #define FOR_EACH_CONCRETE_INSTRUCTION_SHARED(M) \ M(BitwiseNegatedRight, Instruction) \ - M(MultiplyAccumulate, Instruction) + M(MultiplyAccumulate, Instruction) \ + M(IntermediateAddress, Instruction) #endif #ifndef ART_ENABLE_CODEGEN_arm @@ -1303,8 +1304,7 @@ class HLoopInformationOutwardIterator : public ValueObject { #define FOR_EACH_CONCRETE_INSTRUCTION_ARM64(M) #else #define FOR_EACH_CONCRETE_INSTRUCTION_ARM64(M) \ - M(Arm64DataProcWithShifterOp, Instruction) \ - M(Arm64IntermediateAddress, Instruction) + M(Arm64DataProcWithShifterOp, Instruction) #endif #ifndef ART_ENABLE_CODEGEN_mips diff --git a/compiler/optimizing/nodes_arm64.h b/compiler/optimizing/nodes_arm64.h index 06b073c3e2..3f88717c2a 100644 --- a/compiler/optimizing/nodes_arm64.h +++ b/compiler/optimizing/nodes_arm64.h @@ -94,32 +94,6 @@ class HArm64DataProcWithShifterOp FINAL : public HExpression<2> { std::ostream& operator<<(std::ostream& os, const HArm64DataProcWithShifterOp::OpKind op); -// This instruction computes an intermediate address pointing in the 'middle' of an object. The -// result pointer cannot be handled by GC, so extra care is taken to make sure that this value is -// never used across anything that can trigger GC. -class HArm64IntermediateAddress FINAL : public HExpression<2> { - public: - HArm64IntermediateAddress(HInstruction* base_address, HInstruction* offset, uint32_t dex_pc) - : HExpression(Primitive::kPrimNot, SideEffects::DependsOnGC(), dex_pc) { - SetRawInputAt(0, base_address); - SetRawInputAt(1, offset); - } - - bool CanBeMoved() const OVERRIDE { return true; } - bool InstructionDataEquals(const HInstruction* other ATTRIBUTE_UNUSED) const OVERRIDE { - return true; - } - bool IsActualObject() const OVERRIDE { return false; } - - HInstruction* GetBaseAddress() const { return InputAt(0); } - HInstruction* GetOffset() const { return InputAt(1); } - - DECLARE_INSTRUCTION(Arm64IntermediateAddress); - - private: - DISALLOW_COPY_AND_ASSIGN(HArm64IntermediateAddress); -}; - } // namespace art #endif // ART_COMPILER_OPTIMIZING_NODES_ARM64_H_ diff --git a/compiler/optimizing/nodes_shared.h b/compiler/optimizing/nodes_shared.h index f2d5cf3253..8bd8667f84 100644 --- a/compiler/optimizing/nodes_shared.h +++ b/compiler/optimizing/nodes_shared.h @@ -113,6 +113,34 @@ class HBitwiseNegatedRight FINAL : public HBinaryOperation { DISALLOW_COPY_AND_ASSIGN(HBitwiseNegatedRight); }; + +// This instruction computes an intermediate address pointing in the 'middle' of an object. The +// result pointer cannot be handled by GC, so extra care is taken to make sure that this value is +// never used across anything that can trigger GC. +class HIntermediateAddress FINAL : public HExpression<2> { + public: + HIntermediateAddress(HInstruction* base_address, HInstruction* offset, uint32_t dex_pc) + : HExpression(Primitive::kPrimNot, SideEffects::DependsOnGC(), dex_pc) { + SetRawInputAt(0, base_address); + SetRawInputAt(1, offset); + } + + bool CanBeMoved() const OVERRIDE { return true; } + bool InstructionDataEquals(const HInstruction* other ATTRIBUTE_UNUSED) const OVERRIDE { + return true; + } + bool IsActualObject() const OVERRIDE { return false; } + + HInstruction* GetBaseAddress() const { return InputAt(0); } + HInstruction* GetOffset() const { return InputAt(1); } + + DECLARE_INSTRUCTION(IntermediateAddress); + + private: + DISALLOW_COPY_AND_ASSIGN(HIntermediateAddress); +}; + + } // namespace art #endif // ART_COMPILER_OPTIMIZING_NODES_SHARED_H_ diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc index 80affc3d66..30a6d0f005 100644 --- a/compiler/optimizing/optimizing_compiler.cc +++ b/compiler/optimizing/optimizing_compiler.cc @@ -448,8 +448,12 @@ static void RunArchOptimizations(InstructionSet instruction_set, arm::DexCacheArrayFixups* fixups = new (arena) arm::DexCacheArrayFixups(graph, stats); arm::InstructionSimplifierArm* simplifier = new (arena) arm::InstructionSimplifierArm(graph, stats); + SideEffectsAnalysis* side_effects = new (arena) SideEffectsAnalysis(graph); + GVNOptimization* gvn = new (arena) GVNOptimization(graph, *side_effects, "GVN_after_arch"); HOptimization* arm_optimizations[] = { simplifier, + side_effects, + gvn, fixups }; RunOptimizations(arm_optimizations, arraysize(arm_optimizations), pass_observer); diff --git a/test/527-checker-array-access-split/src/Main.java b/test/527-checker-array-access-split/src/Main.java index ead94464bf..3366f20cc5 100644 --- a/test/527-checker-array-access-split/src/Main.java +++ b/test/527-checker-array-access-split/src/Main.java @@ -34,9 +34,21 @@ public class Main { /// CHECK-START-ARM64: int Main.constantIndexGet(int[]) instruction_simplifier_arm64 (after) /// CHECK: <<Array:l\d+>> NullCheck /// CHECK: <<Index:i\d+>> BoundsCheck - /// CHECK-NOT: Arm64IntermediateAddress + /// CHECK-NOT: IntermediateAddress /// CHECK: ArrayGet [<<Array>>,<<Index>>] + + /// CHECK-START-ARM: int Main.constantIndexGet(int[]) instruction_simplifier_arm (before) + /// CHECK: <<Array:l\d+>> NullCheck + /// CHECK: <<Index:i\d+>> BoundsCheck + /// CHECK: ArrayGet [<<Array>>,<<Index>>] + + /// CHECK-START-ARM: int Main.constantIndexGet(int[]) instruction_simplifier_arm (after) + /// CHECK: <<Array:l\d+>> NullCheck + /// CHECK: <<Index:i\d+>> BoundsCheck + /// CHECK-NOT: IntermediateAddress + /// CHECK: ArrayGet [<<Array>>,<<Index>>] + public static int constantIndexGet(int array[]) { return array[1]; } @@ -55,9 +67,22 @@ public class Main { /// CHECK: <<Const2:i\d+>> IntConstant 2 /// CHECK: <<Array:l\d+>> NullCheck /// CHECK: <<Index:i\d+>> BoundsCheck - /// CHECK-NOT: Arm64IntermediateAddress + /// CHECK-NOT: IntermediateAddress + /// CHECK: ArraySet [<<Array>>,<<Index>>,<<Const2>>] + + + /// CHECK-START-ARM: void Main.constantIndexSet(int[]) instruction_simplifier_arm (before) + /// CHECK: <<Const2:i\d+>> IntConstant 2 + /// CHECK: <<Array:l\d+>> NullCheck + /// CHECK: <<Index:i\d+>> BoundsCheck /// CHECK: ArraySet [<<Array>>,<<Index>>,<<Const2>>] + /// CHECK-START-ARM: void Main.constantIndexSet(int[]) instruction_simplifier_arm (after) + /// CHECK: <<Const2:i\d+>> IntConstant 2 + /// CHECK: <<Array:l\d+>> NullCheck + /// CHECK: <<Index:i\d+>> BoundsCheck + /// CHECK-NOT: IntermediateAddress + /// CHECK: ArraySet [<<Array>>,<<Index>>,<<Const2>>] public static void constantIndexSet(int array[]) { array[1] = 2; @@ -76,7 +101,20 @@ public class Main { /// CHECK: <<DataOffset:i\d+>> IntConstant /// CHECK: <<Array:l\d+>> NullCheck /// CHECK: <<Index:i\d+>> BoundsCheck - /// CHECK: <<Address:l\d+>> Arm64IntermediateAddress [<<Array>>,<<DataOffset>>] + /// CHECK: <<Address:l\d+>> IntermediateAddress [<<Array>>,<<DataOffset>>] + /// CHECK-NEXT: ArrayGet [<<Address>>,<<Index>>] + + + /// CHECK-START-ARM: int Main.get(int[], int) instruction_simplifier_arm (before) + /// CHECK: <<Array:l\d+>> NullCheck + /// CHECK: <<Index:i\d+>> BoundsCheck + /// CHECK: ArrayGet [<<Array>>,<<Index>>] + + /// CHECK-START-ARM: int Main.get(int[], int) instruction_simplifier_arm (after) + /// CHECK: <<DataOffset:i\d+>> IntConstant + /// CHECK: <<Array:l\d+>> NullCheck + /// CHECK: <<Index:i\d+>> BoundsCheck + /// CHECK: <<Address:l\d+>> IntermediateAddress [<<Array>>,<<DataOffset>>] /// CHECK-NEXT: ArrayGet [<<Address>>,<<Index>>] public static int get(int array[], int index) { @@ -102,7 +140,26 @@ public class Main { /// CHECK: <<DataOffset:i\d+>> IntConstant /// CHECK: <<Array:l\d+>> NullCheck /// CHECK: <<Index:i\d+>> BoundsCheck - /// CHECK: <<Address:l\d+>> Arm64IntermediateAddress [<<Array>>,<<DataOffset>>] + /// CHECK: <<Address:l\d+>> IntermediateAddress [<<Array>>,<<DataOffset>>] + /// CHECK-NEXT: ArraySet [<<Address>>,<<Index>>,<<Arg>>] + + + /// CHECK-START-ARM: void Main.set(int[], int, int) instruction_simplifier_arm (before) + /// CHECK: ParameterValue + /// CHECK: ParameterValue + /// CHECK: <<Arg:i\d+>> ParameterValue + /// CHECK: <<Array:l\d+>> NullCheck + /// CHECK: <<Index:i\d+>> BoundsCheck + /// CHECK: ArraySet [<<Array>>,<<Index>>,<<Arg>>] + + /// CHECK-START-ARM: void Main.set(int[], int, int) instruction_simplifier_arm (after) + /// CHECK: ParameterValue + /// CHECK: ParameterValue + /// CHECK: <<Arg:i\d+>> ParameterValue + /// CHECK: <<DataOffset:i\d+>> IntConstant + /// CHECK: <<Array:l\d+>> NullCheck + /// CHECK: <<Index:i\d+>> BoundsCheck + /// CHECK: <<Address:l\d+>> IntermediateAddress [<<Array>>,<<DataOffset>>] /// CHECK-NEXT: ArraySet [<<Address>>,<<Index>>,<<Arg>>] public static void set(int array[], int index, int value) { @@ -126,10 +183,10 @@ public class Main { /// CHECK-DAG: <<DataOffset:i\d+>> IntConstant /// CHECK: <<Array:l\d+>> NullCheck /// CHECK: <<Index:i\d+>> BoundsCheck - /// CHECK: <<Address1:l\d+>> Arm64IntermediateAddress [<<Array>>,<<DataOffset>>] + /// CHECK: <<Address1:l\d+>> IntermediateAddress [<<Array>>,<<DataOffset>>] /// CHECK-NEXT: <<ArrayGet:i\d+>> ArrayGet [<<Address1>>,<<Index>>] /// CHECK: <<Add:i\d+>> Add [<<ArrayGet>>,<<Const1>>] - /// CHECK: <<Address2:l\d+>> Arm64IntermediateAddress [<<Array>>,<<DataOffset>>] + /// CHECK: <<Address2:l\d+>> IntermediateAddress [<<Array>>,<<DataOffset>>] /// CHECK-NEXT: ArraySet [<<Address2>>,<<Index>>,<<Add>>] /// CHECK-START-ARM64: void Main.getSet(int[], int) GVN_after_arch (after) @@ -137,12 +194,42 @@ public class Main { /// CHECK-DAG: <<DataOffset:i\d+>> IntConstant /// CHECK: <<Array:l\d+>> NullCheck /// CHECK: <<Index:i\d+>> BoundsCheck - /// CHECK: <<Address:l\d+>> Arm64IntermediateAddress [<<Array>>,<<DataOffset>>] + /// CHECK: <<Address:l\d+>> IntermediateAddress [<<Array>>,<<DataOffset>>] /// CHECK: <<ArrayGet:i\d+>> ArrayGet [<<Address>>,<<Index>>] /// CHECK: <<Add:i\d+>> Add [<<ArrayGet>>,<<Const1>>] - /// CHECK-NOT: Arm64IntermediateAddress + /// CHECK-NOT: IntermediateAddress /// CHECK: ArraySet [<<Address>>,<<Index>>,<<Add>>] + + /// CHECK-START-ARM: void Main.getSet(int[], int) instruction_simplifier_arm (before) + /// CHECK: <<Const1:i\d+>> IntConstant 1 + /// CHECK: <<Array:l\d+>> NullCheck + /// CHECK: <<Index:i\d+>> BoundsCheck + /// CHECK: <<ArrayGet:i\d+>> ArrayGet [<<Array>>,<<Index>>] + /// CHECK: <<Add:i\d+>> Add [<<ArrayGet>>,<<Const1>>] + /// CHECK: ArraySet [<<Array>>,<<Index>>,<<Add>>] + + /// CHECK-START-ARM: void Main.getSet(int[], int) instruction_simplifier_arm (after) + /// CHECK-DAG: <<Const1:i\d+>> IntConstant 1 + /// CHECK-DAG: <<DataOffset:i\d+>> IntConstant + /// CHECK: <<Array:l\d+>> NullCheck + /// CHECK: <<Index:i\d+>> BoundsCheck + /// CHECK: <<Address1:l\d+>> IntermediateAddress [<<Array>>,<<DataOffset>>] + /// CHECK-NEXT: <<ArrayGet:i\d+>> ArrayGet [<<Address1>>,<<Index>>] + /// CHECK: <<Add:i\d+>> Add [<<ArrayGet>>,<<Const1>>] + /// CHECK: <<Address2:l\d+>> IntermediateAddress [<<Array>>,<<DataOffset>>] + /// CHECK-NEXT: ArraySet [<<Address2>>,<<Index>>,<<Add>>] + + /// CHECK-START-ARM: void Main.getSet(int[], int) GVN_after_arch (after) + /// CHECK-DAG: <<Const1:i\d+>> IntConstant 1 + /// CHECK-DAG: <<DataOffset:i\d+>> IntConstant + /// CHECK: <<Array:l\d+>> NullCheck + /// CHECK: <<Index:i\d+>> BoundsCheck + /// CHECK: <<Address:l\d+>> IntermediateAddress [<<Array>>,<<DataOffset>>] + /// CHECK: <<ArrayGet:i\d+>> ArrayGet [<<Address>>,<<Index>>] + /// CHECK: <<Add:i\d+>> Add [<<ArrayGet>>,<<Const1>>] + /// CHECK-NOT: IntermediateAddress + /// CHECK: ArraySet [<<Address>>,<<Index>>,<<Add>>] public static void getSet(int array[], int index) { array[index] = array[index] + 1; } @@ -166,11 +253,11 @@ public class Main { /// CHECK-DAG: <<DataOffset:i\d+>> IntConstant /// CHECK: <<Array:l\d+>> NullCheck /// CHECK: <<Index:i\d+>> BoundsCheck - /// CHECK: <<Address1:l\d+>> Arm64IntermediateAddress [<<Array>>,<<DataOffset>>] + /// CHECK: <<Address1:l\d+>> IntermediateAddress [<<Array>>,<<DataOffset>>] /// CHECK-NEXT: <<ArrayGet:i\d+>> ArrayGet [<<Address1>>,<<Index>>] /// CHECK: <<Add:i\d+>> Add [<<ArrayGet>>,<<Const1>>] /// CHECK: NewArray - /// CHECK: <<Address2:l\d+>> Arm64IntermediateAddress [<<Array>>,<<DataOffset>>] + /// CHECK: <<Address2:l\d+>> IntermediateAddress [<<Array>>,<<DataOffset>>] /// CHECK-NEXT: ArraySet [<<Address2>>,<<Index>>,<<Add>>] /// CHECK-START-ARM64: int[] Main.accrossGC(int[], int) GVN_after_arch (after) @@ -178,11 +265,45 @@ public class Main { /// CHECK-DAG: <<DataOffset:i\d+>> IntConstant /// CHECK: <<Array:l\d+>> NullCheck /// CHECK: <<Index:i\d+>> BoundsCheck - /// CHECK: <<Address1:l\d+>> Arm64IntermediateAddress [<<Array>>,<<DataOffset>>] + /// CHECK: <<Address1:l\d+>> IntermediateAddress [<<Array>>,<<DataOffset>>] /// CHECK: <<ArrayGet:i\d+>> ArrayGet [<<Address1>>,<<Index>>] /// CHECK: <<Add:i\d+>> Add [<<ArrayGet>>,<<Const1>>] /// CHECK: NewArray - /// CHECK: <<Address2:l\d+>> Arm64IntermediateAddress [<<Array>>,<<DataOffset>>] + /// CHECK: <<Address2:l\d+>> IntermediateAddress [<<Array>>,<<DataOffset>>] + /// CHECK: ArraySet [<<Address2>>,<<Index>>,<<Add>>] + + + /// CHECK-START-ARM: int[] Main.accrossGC(int[], int) instruction_simplifier_arm (before) + /// CHECK: <<Const1:i\d+>> IntConstant 1 + /// CHECK: <<Array:l\d+>> NullCheck + /// CHECK: <<Index:i\d+>> BoundsCheck + /// CHECK: <<ArrayGet:i\d+>> ArrayGet [<<Array>>,<<Index>>] + /// CHECK: <<Add:i\d+>> Add [<<ArrayGet>>,<<Const1>>] + /// CHECK: NewArray + /// CHECK: ArraySet [<<Array>>,<<Index>>,<<Add>>] + + /// CHECK-START-ARM: int[] Main.accrossGC(int[], int) instruction_simplifier_arm (after) + /// CHECK-DAG: <<Const1:i\d+>> IntConstant 1 + /// CHECK-DAG: <<DataOffset:i\d+>> IntConstant + /// CHECK: <<Array:l\d+>> NullCheck + /// CHECK: <<Index:i\d+>> BoundsCheck + /// CHECK: <<Address1:l\d+>> IntermediateAddress [<<Array>>,<<DataOffset>>] + /// CHECK-NEXT: <<ArrayGet:i\d+>> ArrayGet [<<Address1>>,<<Index>>] + /// CHECK: <<Add:i\d+>> Add [<<ArrayGet>>,<<Const1>>] + /// CHECK: NewArray + /// CHECK: <<Address2:l\d+>> IntermediateAddress [<<Array>>,<<DataOffset>>] + /// CHECK-NEXT: ArraySet [<<Address2>>,<<Index>>,<<Add>>] + + /// CHECK-START-ARM: int[] Main.accrossGC(int[], int) GVN_after_arch (after) + /// CHECK-DAG: <<Const1:i\d+>> IntConstant 1 + /// CHECK-DAG: <<DataOffset:i\d+>> IntConstant + /// CHECK: <<Array:l\d+>> NullCheck + /// CHECK: <<Index:i\d+>> BoundsCheck + /// CHECK: <<Address1:l\d+>> IntermediateAddress [<<Array>>,<<DataOffset>>] + /// CHECK: <<ArrayGet:i\d+>> ArrayGet [<<Address1>>,<<Index>>] + /// CHECK: <<Add:i\d+>> Add [<<ArrayGet>>,<<Const1>>] + /// CHECK: NewArray + /// CHECK: <<Address2:l\d+>> IntermediateAddress [<<Array>>,<<DataOffset>>] /// CHECK: ArraySet [<<Address2>>,<<Index>>,<<Add>>] public static int[] accrossGC(int array[], int index) { @@ -196,6 +317,14 @@ public class Main { * Test that the intermediate address is shared between array accesses after * the bounds check have been removed by BCE. */ + // For checker tests `instruction_simplifier_<arch> (after)` below, by the time we reach + // the architecture-specific instruction simplifier, BCE has removed the bounds checks in + // the loop. + + // Note that we do not care that the `DataOffset` is `12`. But if we do not + // specify it and any other `IntConstant` appears before that instruction, + // checker will match the previous `IntConstant`, and we will thus fail the + // check. /// CHECK-START-ARM64: int Main.canMergeAfterBCE1() instruction_simplifier_arm64 (before) /// CHECK: <<Const1:i\d+>> IntConstant 1 @@ -207,14 +336,6 @@ public class Main { /// CHECK: <<Add:i\d+>> Add [<<ArrayGet>>,<<Const1>>] /// CHECK: ArraySet [<<Array>>,<<Index>>,<<Add>>] - // By the time we reach the architecture-specific instruction simplifier, BCE - // has removed the bounds checks in the loop. - - // Note that we do not care that the `DataOffset` is `12`. But if we do not - // specify it and any other `IntConstant` appears before that instruction, - // checker will match the previous `IntConstant`, and we will thus fail the - // check. - /// CHECK-START-ARM64: int Main.canMergeAfterBCE1() instruction_simplifier_arm64 (after) /// CHECK-DAG: <<Const1:i\d+>> IntConstant 1 /// CHECK-DAG: <<DataOffset:i\d+>> IntConstant 12 @@ -222,10 +343,10 @@ public class Main { /// CHECK: <<Index:i\d+>> Phi /// CHECK: If // -------------- Loop - /// CHECK: <<Address1:l\d+>> Arm64IntermediateAddress [<<Array>>,<<DataOffset>>] + /// CHECK: <<Address1:l\d+>> IntermediateAddress [<<Array>>,<<DataOffset>>] /// CHECK-NEXT: <<ArrayGet:i\d+>> ArrayGet [<<Address1>>,<<Index>>] /// CHECK: <<Add:i\d+>> Add [<<ArrayGet>>,<<Const1>>] - /// CHECK: <<Address2:l\d+>> Arm64IntermediateAddress [<<Array>>,<<DataOffset>>] + /// CHECK: <<Address2:l\d+>> IntermediateAddress [<<Array>>,<<DataOffset>>] /// CHECK-NEXT: ArraySet [<<Address2>>,<<Index>>,<<Add>>] /// CHECK-START-ARM64: int Main.canMergeAfterBCE1() GVN_after_arch (after) @@ -235,10 +356,47 @@ public class Main { /// CHECK: <<Index:i\d+>> Phi /// CHECK: If // -------------- Loop - /// CHECK: <<Address:l\d+>> Arm64IntermediateAddress [<<Array>>,<<DataOffset>>] + /// CHECK: <<Address:l\d+>> IntermediateAddress [<<Array>>,<<DataOffset>>] + /// CHECK: <<ArrayGet:i\d+>> ArrayGet [<<Address>>,<<Index>>] + /// CHECK: <<Add:i\d+>> Add [<<ArrayGet>>,<<Const1>>] + /// CHECK-NOT: IntermediateAddress + /// CHECK: ArraySet [<<Address>>,<<Index>>,<<Add>>] + + + /// CHECK-START-ARM: int Main.canMergeAfterBCE1() instruction_simplifier_arm (before) + /// CHECK: <<Const1:i\d+>> IntConstant 1 + /// CHECK: <<Array:l\d+>> NewArray + /// CHECK: <<Index:i\d+>> Phi + /// CHECK: If + // -------------- Loop + /// CHECK: <<ArrayGet:i\d+>> ArrayGet [<<Array>>,<<Index>>] + /// CHECK: <<Add:i\d+>> Add [<<ArrayGet>>,<<Const1>>] + /// CHECK: ArraySet [<<Array>>,<<Index>>,<<Add>>] + + /// CHECK-START-ARM: int Main.canMergeAfterBCE1() instruction_simplifier_arm (after) + /// CHECK-DAG: <<Const1:i\d+>> IntConstant 1 + /// CHECK-DAG: <<DataOffset:i\d+>> IntConstant 12 + /// CHECK: <<Array:l\d+>> NewArray + /// CHECK: <<Index:i\d+>> Phi + /// CHECK: If + // -------------- Loop + /// CHECK: <<Address1:l\d+>> IntermediateAddress [<<Array>>,<<DataOffset>>] + /// CHECK-NEXT: <<ArrayGet:i\d+>> ArrayGet [<<Address1>>,<<Index>>] + /// CHECK: <<Add:i\d+>> Add [<<ArrayGet>>,<<Const1>>] + /// CHECK: <<Address2:l\d+>> IntermediateAddress [<<Array>>,<<DataOffset>>] + /// CHECK-NEXT: ArraySet [<<Address2>>,<<Index>>,<<Add>>] + + /// CHECK-START-ARM: int Main.canMergeAfterBCE1() GVN_after_arch (after) + /// CHECK-DAG: <<Const1:i\d+>> IntConstant 1 + /// CHECK-DAG: <<DataOffset:i\d+>> IntConstant 12 + /// CHECK: <<Array:l\d+>> NewArray + /// CHECK: <<Index:i\d+>> Phi + /// CHECK: If + // -------------- Loop + /// CHECK: <<Address:l\d+>> IntermediateAddress [<<Array>>,<<DataOffset>>] /// CHECK: <<ArrayGet:i\d+>> ArrayGet [<<Address>>,<<Index>>] /// CHECK: <<Add:i\d+>> Add [<<ArrayGet>>,<<Const1>>] - /// CHECK-NOT: Arm64IntermediateAddress + /// CHECK-NOT: IntermediateAddress /// CHECK: ArraySet [<<Address>>,<<Index>>,<<Add>>] public static int canMergeAfterBCE1() { @@ -279,12 +437,12 @@ public class Main { /// CHECK: If // -------------- Loop /// CHECK-DAG: <<Index1:i\d+>> Add [<<Index>>,<<Const1>>] - /// CHECK-DAG: <<Address1:l\d+>> Arm64IntermediateAddress [<<Array>>,<<DataOffset>>] + /// CHECK-DAG: <<Address1:l\d+>> IntermediateAddress [<<Array>>,<<DataOffset>>] /// CHECK-DAG: <<ArrayGetI:i\d+>> ArrayGet [<<Address1>>,<<Index>>] - /// CHECK-DAG: <<Address2:l\d+>> Arm64IntermediateAddress [<<Array>>,<<DataOffset>>] + /// CHECK-DAG: <<Address2:l\d+>> IntermediateAddress [<<Array>>,<<DataOffset>>] /// CHECK-DAG: <<ArrayGetI1:i\d+>> ArrayGet [<<Address2>>,<<Index1>>] /// CHECK: <<Add:i\d+>> Add [<<ArrayGetI>>,<<ArrayGetI1>>] - /// CHECK: <<Address3:l\d+>> Arm64IntermediateAddress [<<Array>>,<<DataOffset>>] + /// CHECK: <<Address3:l\d+>> IntermediateAddress [<<Array>>,<<DataOffset>>] /// CHECK: ArraySet [<<Address3>>,<<Index1>>,<<Add>>] /// CHECK-START-ARM64: int Main.canMergeAfterBCE2() GVN_after_arch (after) @@ -295,7 +453,7 @@ public class Main { /// CHECK: If // -------------- Loop /// CHECK-DAG: <<Index1:i\d+>> Add [<<Index>>,<<Const1>>] - /// CHECK-DAG: <<Address:l\d+>> Arm64IntermediateAddress [<<Array>>,<<DataOffset>>] + /// CHECK-DAG: <<Address:l\d+>> IntermediateAddress [<<Array>>,<<DataOffset>>] /// CHECK-DAG: <<ArrayGetI:i\d+>> ArrayGet [<<Address>>,<<Index>>] /// CHECK-DAG: <<ArrayGetI1:i\d+>> ArrayGet [<<Address>>,<<Index1>>] /// CHECK: <<Add:i\d+>> Add [<<ArrayGetI>>,<<ArrayGetI1>>] @@ -304,8 +462,55 @@ public class Main { // There should be only one intermediate address computation in the loop. /// CHECK-START-ARM64: int Main.canMergeAfterBCE2() GVN_after_arch (after) - /// CHECK: Arm64IntermediateAddress - /// CHECK-NOT: Arm64IntermediateAddress + /// CHECK: IntermediateAddress + /// CHECK-NOT: IntermediateAddress + + + /// CHECK-START-ARM: int Main.canMergeAfterBCE2() instruction_simplifier_arm (before) + /// CHECK: <<Const1:i\d+>> IntConstant 1 + /// CHECK: <<Array:l\d+>> NewArray + /// CHECK: <<Index:i\d+>> Phi + /// CHECK: If + // -------------- Loop + /// CHECK-DAG: <<Index1:i\d+>> Add [<<Index>>,<<Const1>>] + /// CHECK-DAG: <<ArrayGetI:i\d+>> ArrayGet [<<Array>>,<<Index>>] + /// CHECK-DAG: <<ArrayGetI1:i\d+>> ArrayGet [<<Array>>,<<Index1>>] + /// CHECK: <<Add:i\d+>> Add [<<ArrayGetI>>,<<ArrayGetI1>>] + /// CHECK: ArraySet [<<Array>>,<<Index1>>,<<Add>>] + + /// CHECK-START-ARM: int Main.canMergeAfterBCE2() instruction_simplifier_arm (after) + /// CHECK-DAG: <<Const1:i\d+>> IntConstant 1 + /// CHECK-DAG: <<DataOffset:i\d+>> IntConstant 12 + /// CHECK: <<Array:l\d+>> NewArray + /// CHECK: <<Index:i\d+>> Phi + /// CHECK: If + // -------------- Loop + /// CHECK-DAG: <<Index1:i\d+>> Add [<<Index>>,<<Const1>>] + /// CHECK-DAG: <<Address1:l\d+>> IntermediateAddress [<<Array>>,<<DataOffset>>] + /// CHECK-DAG: <<ArrayGetI:i\d+>> ArrayGet [<<Address1>>,<<Index>>] + /// CHECK-DAG: <<Address2:l\d+>> IntermediateAddress [<<Array>>,<<DataOffset>>] + /// CHECK-DAG: <<ArrayGetI1:i\d+>> ArrayGet [<<Address2>>,<<Index1>>] + /// CHECK: <<Add:i\d+>> Add [<<ArrayGetI>>,<<ArrayGetI1>>] + /// CHECK: <<Address3:l\d+>> IntermediateAddress [<<Array>>,<<DataOffset>>] + /// CHECK: ArraySet [<<Address3>>,<<Index1>>,<<Add>>] + + /// CHECK-START-ARM: int Main.canMergeAfterBCE2() GVN_after_arch (after) + /// CHECK-DAG: <<Const1:i\d+>> IntConstant 1 + /// CHECK-DAG: <<DataOffset:i\d+>> IntConstant 12 + /// CHECK: <<Array:l\d+>> NewArray + /// CHECK: <<Index:i\d+>> Phi + /// CHECK: If + // -------------- Loop + /// CHECK-DAG: <<Index1:i\d+>> Add [<<Index>>,<<Const1>>] + /// CHECK-DAG: <<Address:l\d+>> IntermediateAddress [<<Array>>,<<DataOffset>>] + /// CHECK-DAG: <<ArrayGetI:i\d+>> ArrayGet [<<Address>>,<<Index>>] + /// CHECK-DAG: <<ArrayGetI1:i\d+>> ArrayGet [<<Address>>,<<Index1>>] + /// CHECK: <<Add:i\d+>> Add [<<ArrayGetI>>,<<ArrayGetI1>>] + /// CHECK: ArraySet [<<Address>>,<<Index1>>,<<Add>>] + + /// CHECK-START-ARM: int Main.canMergeAfterBCE2() GVN_after_arch (after) + /// CHECK: IntermediateAddress + /// CHECK-NOT: IntermediateAddress public static int canMergeAfterBCE2() { int[] array = {0, 1, 2, 3}; @@ -315,6 +520,37 @@ public class Main { return array[array.length - 1]; } + /// CHECK-START-ARM: int Main.checkLongFloatDouble() instruction_simplifier_arm (before) + /// CHECK-DAG: <<Array1:l\d+>> NewArray + /// CHECK-DAG: <<Array2:l\d+>> NewArray + /// CHECK-DAG: <<Array3:l\d+>> NewArray + /// CHECK-DAG: <<Index:i\d+>> Phi + /// CHECK-DAG: ArrayGet [<<Array1>>,<<Index>>] + /// CHECK-DAG: ArrayGet [<<Array2>>,<<Index>>] + /// CHECK-DAG: ArrayGet [<<Array3>>,<<Index>>] + + /// CHECK-START-ARM: int Main.checkLongFloatDouble() instruction_simplifier_arm (after) + /// CHECK-DAG: <<Array1:l\d+>> NewArray + /// CHECK-DAG: <<Array2:l\d+>> NewArray + /// CHECK-DAG: <<Array3:l\d+>> NewArray + /// CHECK-DAG: <<Index:i\d+>> Phi + /// CHECK-DAG: ArrayGet [<<Array1>>,<<Index>>] + /// CHECK-DAG: ArrayGet [<<Array2>>,<<Index>>] + /// CHECK-DAG: ArrayGet [<<Array3>>,<<Index>>] + + /// CHECK-START-ARM: int Main.checkLongFloatDouble() instruction_simplifier_arm (after) + /// CHECK-NOT: IntermediateAddress + public static int checkLongFloatDouble() { + long[] array_long = {0, 1, 2, 3}; + float[] array_float = {(float)0.0, (float)1.0, (float)2.0, (float)3.0}; + double[] array_double = {0.0, 1.0, 2.0, 3.0}; + double s = 0.0; + + for (int i = 0; i < 4; i++) { + s += (double)array_long[i] + (double)array_float[i] + array_double[i]; + } + return (int)s; + } public static void main(String[] args) { int[] array = {123, 456, 789}; @@ -337,5 +573,7 @@ public class Main { assertIntEquals(4, canMergeAfterBCE1()); assertIntEquals(6, canMergeAfterBCE2()); + + assertIntEquals(18, checkLongFloatDouble()); } } diff --git a/test/Android.run-test.mk b/test/Android.run-test.mk index dd6b6f3fbc..8f8b667429 100644 --- a/test/Android.run-test.mk +++ b/test/Android.run-test.mk @@ -527,7 +527,7 @@ TEST_ART_BROKEN_INTERPRETER_READ_BARRIER_RUN_TESTS := # Tests that should fail in the read barrier configuration with the Optimizing compiler (AOT). # 484: Baker's fast path based read barrier compiler instrumentation generates code containing # more parallel moves on x86, thus some Checker assertions may fail. -# 527: On ARM64, the read barrier instrumentation does not support the HArm64IntermediateAddress +# 527: On ARM64 and ARM, the read barrier instrumentation does not support the HIntermediateAddress # instruction yet (b/26601270). # 537: Expects an array copy to be intrinsified on x86-64, but calling-on-slowpath intrinsics are # not yet handled in the read barrier configuration. |