diff options
author | 2021-01-23 13:28:56 +0000 | |
---|---|---|
committer | 2021-01-24 17:18:55 +0000 | |
commit | 791df7a161ecfa28eb69862a4bc285282463b960 (patch) | |
tree | a43b022307ea2df82a360164bdf2627cd07eb5b1 | |
parent | 805769bb3800c1e47b0a76ce2c5ebb270a4ac69d (diff) |
Revert "Revert^2 "Partial Load Store Elimination""
This reverts commit fc1ce4e8be0d977e3d41699f5ec746d68f63c024.
Bug: 67037140
Reason for revert: Fails read-barrier-table-lookup tests.
Change-Id: I373867c728789bc14a4370b93a045481167d5f76
29 files changed, 1705 insertions, 8645 deletions
diff --git a/compiler/optimizing/code_generator.cc b/compiler/optimizing/code_generator.cc index 6aeef62df5..68120e2dd7 100644 --- a/compiler/optimizing/code_generator.cc +++ b/compiler/optimizing/code_generator.cc @@ -1702,7 +1702,6 @@ void CodeGenerator::ValidateInvokeRuntimeWithoutRecordingPcInfo(HInstruction* in // PC-related information. DCHECK(kUseBakerReadBarrier); DCHECK(instruction->IsInstanceFieldGet() || - instruction->IsPredicatedInstanceFieldGet() || instruction->IsStaticFieldGet() || instruction->IsArrayGet() || instruction->IsArraySet() || diff --git a/compiler/optimizing/code_generator_arm64.cc b/compiler/optimizing/code_generator_arm64.cc index b945be208f..a9f03b0215 100644 --- a/compiler/optimizing/code_generator_arm64.cc +++ b/compiler/optimizing/code_generator_arm64.cc @@ -16,8 +16,6 @@ #include "code_generator_arm64.h" -#include "aarch64/assembler-aarch64.h" -#include "aarch64/registers-aarch64.h" #include "arch/arm64/asm_support_arm64.h" #include "arch/arm64/instruction_set_features_arm64.h" #include "arch/arm64/jni_frame_arm64.h" @@ -42,7 +40,6 @@ #include "mirror/class-inl.h" #include "mirror/var_handle.h" #include "offsets.h" -#include "optimizing/common_arm64.h" #include "thread.h" #include "utils/arm64/assembler_arm64.h" #include "utils/assembler.h" @@ -648,7 +645,6 @@ class ReadBarrierForHeapReferenceSlowPathARM64 : public SlowPathCodeARM64 { DCHECK(locations->CanCall()); DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(out_.reg())); DCHECK(instruction_->IsInstanceFieldGet() || - instruction_->IsPredicatedInstanceFieldGet() || instruction_->IsStaticFieldGet() || instruction_->IsArrayGet() || instruction_->IsInstanceOf() || @@ -2006,11 +2002,7 @@ void LocationsBuilderARM64::HandleBinaryOp(HBinaryOperation* instr) { void LocationsBuilderARM64::HandleFieldGet(HInstruction* instruction, const FieldInfo& field_info) { - DCHECK(instruction->IsInstanceFieldGet() || - instruction->IsStaticFieldGet() || - instruction->IsPredicatedInstanceFieldGet()); - - bool is_predicated = instruction->IsPredicatedInstanceFieldGet(); + DCHECK(instruction->IsInstanceFieldGet() || instruction->IsStaticFieldGet()); bool object_field_get_with_read_barrier = kEmitCompilerReadBarrier && (instruction->GetType() == DataType::Type::kReference); @@ -2029,45 +2021,29 @@ void LocationsBuilderARM64::HandleFieldGet(HInstruction* instruction, locations->AddTemp(FixedTempLocation()); } } - // Input for object receiver. - locations->SetInAt(is_predicated ? 1 : 0, Location::RequiresRegister()); + locations->SetInAt(0, Location::RequiresRegister()); if (DataType::IsFloatingPointType(instruction->GetType())) { - if (is_predicated) { - locations->SetInAt(0, Location::RequiresFpuRegister()); - locations->SetOut(Location::SameAsFirstInput()); - } else { - locations->SetOut(Location::RequiresFpuRegister()); - } + locations->SetOut(Location::RequiresFpuRegister()); } else { - if (is_predicated) { - locations->SetInAt(0, Location::RequiresRegister()); - locations->SetOut(Location::SameAsFirstInput()); - } else { - // The output overlaps for an object field get when read barriers - // are enabled: we do not want the load to overwrite the object's - // location, as we need it to emit the read barrier. - locations->SetOut(Location::RequiresRegister(), - object_field_get_with_read_barrier ? Location::kOutputOverlap - : Location::kNoOutputOverlap); - } + // The output overlaps for an object field get when read barriers + // are enabled: we do not want the load to overwrite the object's + // location, as we need it to emit the read barrier. + locations->SetOut( + Location::RequiresRegister(), + object_field_get_with_read_barrier ? Location::kOutputOverlap : Location::kNoOutputOverlap); } } void InstructionCodeGeneratorARM64::HandleFieldGet(HInstruction* instruction, const FieldInfo& field_info) { - DCHECK(instruction->IsInstanceFieldGet() || - instruction->IsStaticFieldGet() || - instruction->IsPredicatedInstanceFieldGet()); - bool is_predicated = instruction->IsPredicatedInstanceFieldGet(); + DCHECK(instruction->IsInstanceFieldGet() || instruction->IsStaticFieldGet()); LocationSummary* locations = instruction->GetLocations(); - uint32_t receiver_input = is_predicated ? 1 : 0; - Location base_loc = locations->InAt(receiver_input); + Location base_loc = locations->InAt(0); Location out = locations->Out(); uint32_t offset = field_info.GetFieldOffset().Uint32Value(); DCHECK_EQ(DataType::Size(field_info.GetFieldType()), DataType::Size(instruction->GetType())); DataType::Type load_type = instruction->GetType(); - MemOperand field = - HeapOperand(InputRegisterAt(instruction, receiver_input), field_info.GetFieldOffset()); + MemOperand field = HeapOperand(InputRegisterAt(instruction, 0), field_info.GetFieldOffset()); if (kEmitCompilerReadBarrier && kUseBakerReadBarrier && load_type == DataType::Type::kReference) { @@ -2129,19 +2105,12 @@ void InstructionCodeGeneratorARM64::HandleFieldSet(HInstruction* instruction, const FieldInfo& field_info, bool value_can_be_null) { DCHECK(instruction->IsInstanceFieldSet() || instruction->IsStaticFieldSet()); - bool is_predicated = - instruction->IsInstanceFieldSet() && instruction->AsInstanceFieldSet()->GetIsPredicatedSet(); Register obj = InputRegisterAt(instruction, 0); CPURegister value = InputCPURegisterOrZeroRegAt(instruction, 1); CPURegister source = value; Offset offset = field_info.GetFieldOffset(); DataType::Type field_type = field_info.GetFieldType(); - std::optional<vixl::aarch64::Label> pred_is_null; - if (is_predicated) { - pred_is_null.emplace(); - __ Cbz(obj, &*pred_is_null); - } { // We use a block to end the scratch scope before the write barrier, thus @@ -2170,10 +2139,6 @@ void InstructionCodeGeneratorARM64::HandleFieldSet(HInstruction* instruction, if (CodeGenerator::StoreNeedsWriteBarrier(field_type, instruction->InputAt(1))) { codegen_->MarkGCCard(obj, Register(value), value_can_be_null); } - - if (is_predicated) { - __ Bind(&*pred_is_null); - } } void InstructionCodeGeneratorARM64::HandleBinaryOp(HBinaryOperation* instr) { @@ -3829,23 +3794,10 @@ void CodeGeneratorARM64::GenerateNop() { __ Nop(); } -void LocationsBuilderARM64::VisitPredicatedInstanceFieldGet( - HPredicatedInstanceFieldGet* instruction) { - HandleFieldGet(instruction, instruction->GetFieldInfo()); -} - void LocationsBuilderARM64::VisitInstanceFieldGet(HInstanceFieldGet* instruction) { HandleFieldGet(instruction, instruction->GetFieldInfo()); } -void InstructionCodeGeneratorARM64::VisitPredicatedInstanceFieldGet( - HPredicatedInstanceFieldGet* instruction) { - vixl::aarch64::Label finish; - __ Cbz(InputRegisterAt(instruction, 1), &finish); - HandleFieldGet(instruction, instruction->GetFieldInfo()); - __ Bind(&finish); -} - void InstructionCodeGeneratorARM64::VisitInstanceFieldGet(HInstanceFieldGet* instruction) { HandleFieldGet(instruction, instruction->GetFieldInfo()); } diff --git a/compiler/optimizing/code_generator_arm_vixl.cc b/compiler/optimizing/code_generator_arm_vixl.cc index 18709f8c97..d7d09afc24 100644 --- a/compiler/optimizing/code_generator_arm_vixl.cc +++ b/compiler/optimizing/code_generator_arm_vixl.cc @@ -761,7 +761,6 @@ class ReadBarrierForHeapReferenceSlowPathARMVIXL : public SlowPathCodeARMVIXL { DCHECK(locations->CanCall()); DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(reg_out.GetCode())); DCHECK(instruction_->IsInstanceFieldGet() || - instruction_->IsPredicatedInstanceFieldGet() || instruction_->IsStaticFieldGet() || instruction_->IsArrayGet() || instruction_->IsInstanceOf() || @@ -5734,10 +5733,7 @@ void InstructionCodeGeneratorARMVIXL::HandleFieldSet(HInstruction* instruction, LocationSummary* locations = instruction->GetLocations(); vixl32::Register base = InputRegisterAt(instruction, 0); Location value = locations->InAt(1); - std::optional<vixl::aarch32::Label> pred_is_null; - bool is_predicated = - instruction->IsInstanceFieldSet() && instruction->AsInstanceFieldSet()->GetIsPredicatedSet(); bool is_volatile = field_info.IsVolatile(); bool atomic_ldrd_strd = codegen_->GetInstructionSetFeatures().HasAtomicLdrdAndStrd(); DataType::Type field_type = field_info.GetFieldType(); @@ -5745,11 +5741,6 @@ void InstructionCodeGeneratorARMVIXL::HandleFieldSet(HInstruction* instruction, bool needs_write_barrier = CodeGenerator::StoreNeedsWriteBarrier(field_type, instruction->InputAt(1)); - if (is_predicated) { - pred_is_null.emplace(); - __ CompareAndBranchIfZero(base, &*pred_is_null, /* is_far_target= */ false); - } - if (is_volatile) { codegen_->GenerateMemoryBarrier(MemBarrierKind::kAnyStore); } @@ -5853,21 +5844,14 @@ void InstructionCodeGeneratorARMVIXL::HandleFieldSet(HInstruction* instruction, if (is_volatile) { codegen_->GenerateMemoryBarrier(MemBarrierKind::kAnyAny); } - - if (is_predicated) { - __ Bind(&*pred_is_null); - } } void LocationsBuilderARMVIXL::HandleFieldGet(HInstruction* instruction, const FieldInfo& field_info) { - DCHECK(instruction->IsInstanceFieldGet() || - instruction->IsStaticFieldGet() || - instruction->IsPredicatedInstanceFieldGet()); + DCHECK(instruction->IsInstanceFieldGet() || instruction->IsStaticFieldGet()); bool object_field_get_with_read_barrier = kEmitCompilerReadBarrier && (field_info.GetFieldType() == DataType::Type::kReference); - bool is_predicated = instruction->IsPredicatedInstanceFieldGet(); LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(instruction, object_field_get_with_read_barrier @@ -5876,8 +5860,7 @@ void LocationsBuilderARMVIXL::HandleFieldGet(HInstruction* instruction, if (object_field_get_with_read_barrier && kUseBakerReadBarrier) { locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty()); // No caller-save registers. } - // Input for object receiver. - locations->SetInAt(is_predicated ? 1 : 0, Location::RequiresRegister()); + locations->SetInAt(0, Location::RequiresRegister()); bool volatile_for_double = field_info.IsVolatile() && (field_info.GetFieldType() == DataType::Type::kFloat64) @@ -5892,20 +5875,10 @@ void LocationsBuilderARMVIXL::HandleFieldGet(HInstruction* instruction, object_field_get_with_read_barrier; if (DataType::IsFloatingPointType(instruction->GetType())) { - if (is_predicated) { - locations->SetInAt(0, Location::RequiresFpuRegister()); - locations->SetOut(Location::SameAsFirstInput()); - } else { - locations->SetOut(Location::RequiresFpuRegister()); - } + locations->SetOut(Location::RequiresFpuRegister()); } else { - if (is_predicated) { - locations->SetInAt(0, Location::RequiresRegister()); - locations->SetOut(Location::SameAsFirstInput()); - } else { - locations->SetOut(Location::RequiresRegister(), - (overlap ? Location::kOutputOverlap : Location::kNoOutputOverlap)); - } + locations->SetOut(Location::RequiresRegister(), + (overlap ? Location::kOutputOverlap : Location::kNoOutputOverlap)); } if (volatile_for_double) { // ARM encoding have some additional constraints for ldrexd/strexd: @@ -6006,13 +5979,10 @@ bool LocationsBuilderARMVIXL::CanEncodeConstantAsImmediate(HConstant* input_cst, void InstructionCodeGeneratorARMVIXL::HandleFieldGet(HInstruction* instruction, const FieldInfo& field_info) { - DCHECK(instruction->IsInstanceFieldGet() || - instruction->IsStaticFieldGet() || - instruction->IsPredicatedInstanceFieldGet()); + DCHECK(instruction->IsInstanceFieldGet() || instruction->IsStaticFieldGet()); LocationSummary* locations = instruction->GetLocations(); - uint32_t receiver_input = instruction->IsPredicatedInstanceFieldGet() ? 1 : 0; - vixl32::Register base = InputRegisterAt(instruction, receiver_input); + vixl32::Register base = InputRegisterAt(instruction, 0); Location out = locations->Out(); bool is_volatile = field_info.IsVolatile(); bool atomic_ldrd_strd = codegen_->GetInstructionSetFeatures().HasAtomicLdrdAndStrd(); @@ -6059,8 +6029,7 @@ void InstructionCodeGeneratorARMVIXL::HandleFieldGet(HInstruction* instruction, // If read barriers are enabled, emit read barriers other than // Baker's using a slow path (and also unpoison the loaded // reference, if heap poisoning is enabled). - codegen_->MaybeGenerateReadBarrierSlow( - instruction, out, out, locations->InAt(receiver_input), offset); + codegen_->MaybeGenerateReadBarrierSlow(instruction, out, out, locations->InAt(0), offset); } break; } @@ -6131,19 +6100,6 @@ void LocationsBuilderARMVIXL::VisitInstanceFieldGet(HInstanceFieldGet* instructi HandleFieldGet(instruction, instruction->GetFieldInfo()); } -void LocationsBuilderARMVIXL::VisitPredicatedInstanceFieldGet( - HPredicatedInstanceFieldGet* instruction) { - HandleFieldGet(instruction, instruction->GetFieldInfo()); -} - -void InstructionCodeGeneratorARMVIXL::VisitPredicatedInstanceFieldGet( - HPredicatedInstanceFieldGet* instruction) { - vixl::aarch32::Label finish; - __ CompareAndBranchIfZero(InputRegisterAt(instruction, 1), &finish, false); - HandleFieldGet(instruction, instruction->GetFieldInfo()); - __ Bind(&finish); -} - void InstructionCodeGeneratorARMVIXL::VisitInstanceFieldGet(HInstanceFieldGet* instruction) { HandleFieldGet(instruction, instruction->GetFieldInfo()); } diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc index 4fc29fcb0c..f6c0270804 100644 --- a/compiler/optimizing/code_generator_x86.cc +++ b/compiler/optimizing/code_generator_x86.cc @@ -489,7 +489,6 @@ class ReadBarrierMarkSlowPathX86 : public SlowPathCode { DCHECK(locations->CanCall()); DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(ref_reg)) << ref_reg; DCHECK(instruction_->IsInstanceFieldGet() || - instruction_->IsPredicatedInstanceFieldGet() || instruction_->IsStaticFieldGet() || instruction_->IsArrayGet() || instruction_->IsArraySet() || @@ -750,7 +749,6 @@ class ReadBarrierForHeapReferenceSlowPathX86 : public SlowPathCode { DCHECK(locations->CanCall()); DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(reg_out)); DCHECK(instruction_->IsInstanceFieldGet() || - instruction_->IsPredicatedInstanceFieldGet() || instruction_->IsStaticFieldGet() || instruction_->IsArrayGet() || instruction_->IsInstanceOf() || @@ -5644,13 +5642,10 @@ void CodeGeneratorX86::MarkGCCard(Register temp, } void LocationsBuilderX86::HandleFieldGet(HInstruction* instruction, const FieldInfo& field_info) { - DCHECK(instruction->IsInstanceFieldGet() || - instruction->IsStaticFieldGet() || - instruction->IsPredicatedInstanceFieldGet()); + DCHECK(instruction->IsInstanceFieldGet() || instruction->IsStaticFieldGet()); bool object_field_get_with_read_barrier = kEmitCompilerReadBarrier && (instruction->GetType() == DataType::Type::kReference); - bool is_predicated = instruction->IsPredicatedInstanceFieldGet(); LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(instruction, kEmitCompilerReadBarrier @@ -5659,30 +5654,21 @@ void LocationsBuilderX86::HandleFieldGet(HInstruction* instruction, const FieldI if (object_field_get_with_read_barrier && kUseBakerReadBarrier) { locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty()); // No caller-save registers. } - // receiver_input - locations->SetInAt(is_predicated ? 1 : 0, Location::RequiresRegister()); - if (is_predicated) { - if (DataType::IsFloatingPointType(instruction->GetType())) { - locations->SetInAt(0, Location::RequiresFpuRegister()); - } else { - locations->SetInAt(0, Location::RequiresRegister()); - } - } + locations->SetInAt(0, Location::RequiresRegister()); + if (DataType::IsFloatingPointType(instruction->GetType())) { - locations->SetOut(is_predicated ? Location::SameAsFirstInput() - : Location::RequiresFpuRegister()); + locations->SetOut(Location::RequiresFpuRegister()); } else { // The output overlaps in case of long: we don't want the low move // to overwrite the object's location. Likewise, in the case of // an object field get with read barriers enabled, we do not want // the move to overwrite the object's location, as we need it to emit // the read barrier. - locations->SetOut(is_predicated ? Location::SameAsFirstInput() : Location::RequiresRegister(), - (object_field_get_with_read_barrier || - instruction->GetType() == DataType::Type::kInt64 || - is_predicated) - ? Location::kOutputOverlap - : Location::kNoOutputOverlap); + locations->SetOut( + Location::RequiresRegister(), + (object_field_get_with_read_barrier || instruction->GetType() == DataType::Type::kInt64) ? + Location::kOutputOverlap : + Location::kNoOutputOverlap); } if (field_info.IsVolatile() && (field_info.GetFieldType() == DataType::Type::kInt64)) { @@ -5696,12 +5682,10 @@ void LocationsBuilderX86::HandleFieldGet(HInstruction* instruction, const FieldI void InstructionCodeGeneratorX86::HandleFieldGet(HInstruction* instruction, const FieldInfo& field_info) { - DCHECK(instruction->IsInstanceFieldGet() || - instruction->IsStaticFieldGet() || - instruction->IsPredicatedInstanceFieldGet()); + DCHECK(instruction->IsInstanceFieldGet() || instruction->IsStaticFieldGet()); LocationSummary* locations = instruction->GetLocations(); - Location base_loc = locations->InAt(instruction->IsPredicatedInstanceFieldGet() ? 1 : 0); + Location base_loc = locations->InAt(0); Register base = base_loc.AsRegister<Register>(); Location out = locations->Out(); bool is_volatile = field_info.IsVolatile(); @@ -5995,17 +5979,9 @@ void InstructionCodeGeneratorX86::HandleFieldSet(HInstruction* instruction, bool is_volatile = field_info.IsVolatile(); DataType::Type field_type = field_info.GetFieldType(); uint32_t offset = field_info.GetFieldOffset().Uint32Value(); - bool is_predicated = - instruction->IsInstanceFieldSet() && instruction->AsInstanceFieldSet()->GetIsPredicatedSet(); Address field_addr(base, offset); - NearLabel pred_is_null; - if (is_predicated) { - __ testl(base, base); - __ j(kEqual, &pred_is_null); - } - HandleFieldSet(instruction, /* value_index= */ 1, field_type, @@ -6013,10 +5989,6 @@ void InstructionCodeGeneratorX86::HandleFieldSet(HInstruction* instruction, base, is_volatile, value_can_be_null); - - if (is_predicated) { - __ Bind(&pred_is_null); - } } void LocationsBuilderX86::VisitStaticFieldGet(HStaticFieldGet* instruction) { @@ -6043,25 +6015,10 @@ void InstructionCodeGeneratorX86::VisitInstanceFieldSet(HInstanceFieldSet* instr HandleFieldSet(instruction, instruction->GetFieldInfo(), instruction->GetValueCanBeNull()); } -void LocationsBuilderX86::VisitPredicatedInstanceFieldGet( - HPredicatedInstanceFieldGet* instruction) { - HandleFieldGet(instruction, instruction->GetFieldInfo()); -} - void LocationsBuilderX86::VisitInstanceFieldGet(HInstanceFieldGet* instruction) { HandleFieldGet(instruction, instruction->GetFieldInfo()); } -void InstructionCodeGeneratorX86::VisitPredicatedInstanceFieldGet( - HPredicatedInstanceFieldGet* instruction) { - NearLabel finish; - LocationSummary* locations = instruction->GetLocations(); - Register recv = locations->InAt(1).AsRegister<Register>(); - __ testl(recv, recv); - __ j(kZero, &finish); - HandleFieldGet(instruction, instruction->GetFieldInfo()); - __ Bind(&finish); -} void InstructionCodeGeneratorX86::VisitInstanceFieldGet(HInstanceFieldGet* instruction) { HandleFieldGet(instruction, instruction->GetFieldInfo()); } diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc index d54484c065..d79c2e4911 100644 --- a/compiler/optimizing/code_generator_x86_64.cc +++ b/compiler/optimizing/code_generator_x86_64.cc @@ -39,7 +39,6 @@ #include "utils/assembler.h" #include "utils/stack_checks.h" #include "utils/x86_64/assembler_x86_64.h" -#include "utils/x86_64/constants_x86_64.h" #include "utils/x86_64/managed_register_x86_64.h" namespace art { @@ -501,7 +500,6 @@ class ReadBarrierMarkSlowPathX86_64 : public SlowPathCode { DCHECK(locations->CanCall()); DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(ref_reg)) << ref_reg; DCHECK(instruction_->IsInstanceFieldGet() || - instruction_->IsPredicatedInstanceFieldGet() || instruction_->IsStaticFieldGet() || instruction_->IsArrayGet() || instruction_->IsArraySet() || @@ -763,7 +761,6 @@ class ReadBarrierForHeapReferenceSlowPathX86_64 : public SlowPathCode { DCHECK(locations->CanCall()); DCHECK(!locations->GetLiveRegisters()->ContainsCoreRegister(reg_out.AsRegister())) << out_; DCHECK(instruction_->IsInstanceFieldGet() || - instruction_->IsPredicatedInstanceFieldGet() || instruction_->IsStaticFieldGet() || instruction_->IsArrayGet() || instruction_->IsInstanceOf() || @@ -4859,13 +4856,10 @@ void CodeGeneratorX86_64::GenerateMemoryBarrier(MemBarrierKind kind) { } void LocationsBuilderX86_64::HandleFieldGet(HInstruction* instruction) { - DCHECK(instruction->IsInstanceFieldGet() || - instruction->IsStaticFieldGet() || - instruction->IsPredicatedInstanceFieldGet()); + DCHECK(instruction->IsInstanceFieldGet() || instruction->IsStaticFieldGet()); bool object_field_get_with_read_barrier = kEmitCompilerReadBarrier && (instruction->GetType() == DataType::Type::kReference); - bool is_predicated = instruction->IsPredicatedInstanceFieldGet(); LocationSummary* locations = new (GetGraph()->GetAllocator()) LocationSummary(instruction, object_field_get_with_read_barrier @@ -4874,38 +4868,25 @@ void LocationsBuilderX86_64::HandleFieldGet(HInstruction* instruction) { if (object_field_get_with_read_barrier && kUseBakerReadBarrier) { locations->SetCustomSlowPathCallerSaves(RegisterSet::Empty()); // No caller-save registers. } - // receiver_input - locations->SetInAt(is_predicated ? 1 : 0, Location::RequiresRegister()); - if (is_predicated) { - if (DataType::IsFloatingPointType(instruction->GetType())) { - locations->SetInAt(0, Location::RequiresFpuRegister()); - } else { - locations->SetInAt(0, Location::RequiresRegister()); - } - } + locations->SetInAt(0, Location::RequiresRegister()); if (DataType::IsFloatingPointType(instruction->GetType())) { - locations->SetOut(is_predicated ? Location::SameAsFirstInput() - : Location::RequiresFpuRegister()); + locations->SetOut(Location::RequiresFpuRegister()); } else { - // The output overlaps for an object field get when read barriers are - // enabled: we do not want the move to overwrite the object's location, as - // we need it to emit the read barrier. For predicated instructions we can - // always overlap since the output is SameAsFirst and the default value. - locations->SetOut(is_predicated ? Location::SameAsFirstInput() : Location::RequiresRegister(), - object_field_get_with_read_barrier || is_predicated - ? Location::kOutputOverlap - : Location::kNoOutputOverlap); + // The output overlaps for an object field get when read barriers + // are enabled: we do not want the move to overwrite the object's + // location, as we need it to emit the read barrier. + locations->SetOut( + Location::RequiresRegister(), + object_field_get_with_read_barrier ? Location::kOutputOverlap : Location::kNoOutputOverlap); } } void InstructionCodeGeneratorX86_64::HandleFieldGet(HInstruction* instruction, const FieldInfo& field_info) { - DCHECK(instruction->IsInstanceFieldGet() || - instruction->IsStaticFieldGet() || - instruction->IsPredicatedInstanceFieldGet()); + DCHECK(instruction->IsInstanceFieldGet() || instruction->IsStaticFieldGet()); LocationSummary* locations = instruction->GetLocations(); - Location base_loc = locations->InAt(instruction->IsPredicatedInstanceFieldGet() ? 1 : 0); + Location base_loc = locations->InAt(0); CpuRegister base = base_loc.AsRegister<CpuRegister>(); Location out = locations->Out(); bool is_volatile = field_info.IsVolatile(); @@ -5051,8 +5032,6 @@ void InstructionCodeGeneratorX86_64::HandleFieldSet(HInstruction* instruction, bool is_volatile = field_info.IsVolatile(); DataType::Type field_type = field_info.GetFieldType(); uint32_t offset = field_info.GetFieldOffset().Uint32Value(); - bool is_predicated = - instruction->IsInstanceFieldSet() && instruction->AsInstanceFieldSet()->GetIsPredicatedSet(); if (is_volatile) { codegen_->GenerateMemoryBarrier(MemBarrierKind::kAnyStore); @@ -5060,12 +5039,6 @@ void InstructionCodeGeneratorX86_64::HandleFieldSet(HInstruction* instruction, bool maybe_record_implicit_null_check_done = false; - NearLabel pred_is_null; - if (is_predicated) { - __ testl(base, base); - __ j(kZero, &pred_is_null); - } - switch (field_type) { case DataType::Type::kBool: case DataType::Type::kUint8: @@ -5172,10 +5145,6 @@ void InstructionCodeGeneratorX86_64::HandleFieldSet(HInstruction* instruction, if (is_volatile) { codegen_->GenerateMemoryBarrier(MemBarrierKind::kAnyAny); } - - if (is_predicated) { - __ Bind(&pred_is_null); - } } void LocationsBuilderX86_64::VisitInstanceFieldSet(HInstanceFieldSet* instruction) { @@ -5186,26 +5155,10 @@ void InstructionCodeGeneratorX86_64::VisitInstanceFieldSet(HInstanceFieldSet* in HandleFieldSet(instruction, instruction->GetFieldInfo(), instruction->GetValueCanBeNull()); } -void LocationsBuilderX86_64::VisitPredicatedInstanceFieldGet( - HPredicatedInstanceFieldGet* instruction) { - HandleFieldGet(instruction); -} - void LocationsBuilderX86_64::VisitInstanceFieldGet(HInstanceFieldGet* instruction) { HandleFieldGet(instruction); } -void InstructionCodeGeneratorX86_64::VisitPredicatedInstanceFieldGet( - HPredicatedInstanceFieldGet* instruction) { - NearLabel finish; - LocationSummary* locations = instruction->GetLocations(); - CpuRegister target = locations->InAt(1).AsRegister<CpuRegister>(); - __ testl(target, target); - __ j(kZero, &finish); - HandleFieldGet(instruction, instruction->GetFieldInfo()); - __ Bind(&finish); -} - void InstructionCodeGeneratorX86_64::VisitInstanceFieldGet(HInstanceFieldGet* instruction) { HandleFieldGet(instruction, instruction->GetFieldInfo()); } diff --git a/compiler/optimizing/execution_subgraph.h b/compiler/optimizing/execution_subgraph.h index 7fabbaead1..dac938ed62 100644 --- a/compiler/optimizing/execution_subgraph.h +++ b/compiler/optimizing/execution_subgraph.h @@ -27,7 +27,6 @@ #include "base/bit_vector-inl.h" #include "base/globals.h" #include "base/iteration_range.h" -#include "base/mutex.h" #include "base/scoped_arena_allocator.h" #include "base/scoped_arena_containers.h" #include "base/stl_util.h" @@ -36,18 +35,6 @@ namespace art { -// Helper for transforming blocks to block_ids. -class BlockToBlockIdTransformer { - public: - BlockToBlockIdTransformer(BlockToBlockIdTransformer&&) = default; - BlockToBlockIdTransformer(const BlockToBlockIdTransformer&) = default; - BlockToBlockIdTransformer() {} - - inline uint32_t operator()(const HBasicBlock* b) const { - return b->GetBlockId(); - } -}; - // Helper for transforming block ids to blocks. class BlockIdToBlockTransformer { public: @@ -74,20 +61,6 @@ class BlockIdToBlockTransformer { const HGraph* const graph_; }; -class BlockIdFilterThunk { - public: - explicit BlockIdFilterThunk(const BitVector& i) : inner_(i) {} - BlockIdFilterThunk(BlockIdFilterThunk&& other) noexcept = default; - BlockIdFilterThunk(const BlockIdFilterThunk&) = default; - - bool operator()(const HBasicBlock* b) const { - return inner_.IsBitSet(b->GetBlockId()); - } - - private: - const BitVector& inner_; -}; - // A representation of a particular section of the graph. The graph is split // into an excluded and included area and is used to track escapes. // @@ -107,18 +80,10 @@ class BlockIdFilterThunk { // cohort-exit block to reach any cohort-entry block. This means we can use the // boundary between the cohort and the rest of the graph to insert // materialization blocks for partial LSE. -// -// TODO We really should expand this to take into account where the object -// allocation takes place directly. Currently we always act as though it were -// allocated in the entry block. This is a massively simplifying assumption but -// means we can't partially remove objects that are repeatedly allocated in a -// loop. class ExecutionSubgraph : public ArenaObject<kArenaAllocLSA> { public: using BitVecBlockRange = IterationRange<TransformIterator<BitVector::IndexIterator, BlockIdToBlockTransformer>>; - using FilteredBitVecBlockRange = IterationRange< - FilterIterator<ArenaVector<HBasicBlock*>::const_iterator, BlockIdFilterThunk>>; // A set of connected blocks which are connected and removed from the // ExecutionSubgraph. See above comment for explanation. @@ -145,15 +110,6 @@ class ExecutionSubgraph : public ArenaObject<kArenaAllocLSA> { return BlockIterRange(entry_blocks_); } - FilteredBitVecBlockRange EntryBlocksReversePostOrder() const { - return Filter(MakeIterationRange(graph_->GetReversePostOrder()), - BlockIdFilterThunk(entry_blocks_)); - } - - bool IsEntryBlock(const HBasicBlock* blk) const { - return entry_blocks_.IsBitSet(blk->GetBlockId()); - } - // Blocks that have successors outside of the cohort. The successors of // these blocks will need to have PHI's to restore state. BitVecBlockRange ExitBlocks() const { diff --git a/compiler/optimizing/execution_subgraph_test.cc b/compiler/optimizing/execution_subgraph_test.cc index 98e642f1a7..1fc00d9f6b 100644 --- a/compiler/optimizing/execution_subgraph_test.cc +++ b/compiler/optimizing/execution_subgraph_test.cc @@ -425,150 +425,6 @@ TEST_F(ExecutionSubgraphTest, PropagationLoop3) { ASSERT_TRUE(contents.find(blks.Get("exit")) != contents.end()); } -// ┌───────┐ ┌──────────────┐ -// │ right │ ◀── │ entry │ -// └───────┘ └──────────────┘ -// │ │ -// │ │ -// ▼ ▼ -// ┌────┐ ┌───────┐ ┌──────────────┐ -// │ l2 │ ──▶ │ exit │ ┌─ │ l1 │ ◀┐ -// └────┘ └───────┘ │ └──────────────┘ │ -// ▲ │ │ │ -// └───────────────────┘ │ │ -// ▼ │ -// ┌──────────────┐ │ ┌──────────────┐ -// ┌─ │ l1loop │ │ │ l1loop_right │ ◀┐ -// │ └──────────────┘ │ └──────────────┘ │ -// │ │ │ │ │ -// │ │ │ │ │ -// │ ▼ │ │ │ -// │ ┌−−−−−−−−−−−−−−−−−−┐ │ │ │ -// │ ╎ removed ╎ │ │ │ -// │ ╎ ╎ │ │ │ -// │ ╎ ┌──────────────┐ ╎ │ │ │ -// │ ╎ │ l1loop_left │ ╎ │ │ │ -// │ ╎ └──────────────┘ ╎ │ │ │ -// │ ╎ ╎ │ │ │ -// │ └−−−−−−−−−−−−−−−−−−┘ │ │ │ -// │ │ │ │ │ -// │ │ │ │ │ -// │ ▼ │ │ │ -// │ ┌──────────────┐ │ │ │ -// │ │ l1loop_merge │ ─┘ │ │ -// │ └──────────────┘ │ │ -// │ ▲ │ │ -// │ └──────────────────────┘ │ -// │ │ -// │ │ -// └─────────────────────────────────────────────┘ - -TEST_F(ExecutionSubgraphTest, PropagationLoop4) { - AdjacencyListGraph blks(SetupFromAdjacencyList("entry", - "exit", - {{"entry", "l1"}, - {"l1", "l2"}, - {"l1", "l1loop"}, - {"l1loop", "l1loop_left"}, - {"l1loop", "l1loop_right"}, - {"l1loop_left", "l1loop_merge"}, - {"l1loop_right", "l1loop_merge"}, - {"l1loop_merge", "l1"}, - {"l2", "exit"}, - {"entry", "right"}, - {"right", "exit"}})); - ASSERT_TRUE(ExecutionSubgraph::CanAnalyse(graph_)); - ExecutionSubgraph esg(graph_, /*analysis_possible=*/true, GetScopedAllocator()); - esg.RemoveBlock(blks.Get("l1loop_left")); - esg.Finalize(); - ASSERT_TRUE(esg.IsValid()); - ASSERT_TRUE(IsValidSubgraph(esg)); - std::unordered_set<const HBasicBlock*> contents(esg.ReachableBlocks().begin(), - esg.ReachableBlocks().end()); - - ASSERT_EQ(contents.size(), 3u); - - // Not present, no path through. If we got to l1 loop then we must merge back - // with l1 and l2 so they're bad too. - ASSERT_TRUE(contents.find(blks.Get("l1loop")) == contents.end()); - ASSERT_TRUE(contents.find(blks.Get("l1")) == contents.end()); - ASSERT_TRUE(contents.find(blks.Get("l1loop_left")) == contents.end()); - ASSERT_TRUE(contents.find(blks.Get("l1loop_right")) == contents.end()); - ASSERT_TRUE(contents.find(blks.Get("l1loop_merge")) == contents.end()); - ASSERT_TRUE(contents.find(blks.Get("l2")) == contents.end()); - - // present, path through. - ASSERT_TRUE(contents.find(blks.Get("right")) != contents.end()); - ASSERT_TRUE(contents.find(blks.Get("entry")) != contents.end()); - ASSERT_TRUE(contents.find(blks.Get("exit")) != contents.end()); -} - -// +------------------------------------------------------+ -// | | -// | +--------------+ +-------------+ | -// | | right | <-- | entry | | -// | +--------------+ +-------------+ | -// | | | | -// | | | | -// | v v | -// | +--------------+ +--------------------+ +----+ -// +> | exit | +> | l1 | --> | l2 | -// +--------------+ | +--------------------+ +----+ -// | | ^ -// +---------------+ | | -// | v | -// +--------------+ +-------------+ | -// | l1loop_right | <-- | l1loop | | -// +--------------+ +-------------+ | -// | | -// | | -// v | -// + - - - - - - - - + | -// ' removed ' | -// ' ' | -// ' +-------------+ ' | -// ' | l1loop_left | ' -+ -// ' +-------------+ ' -// ' ' -// + - - - - - - - - + -TEST_F(ExecutionSubgraphTest, PropagationLoop5) { - AdjacencyListGraph blks(SetupFromAdjacencyList("entry", - "exit", - {{"entry", "l1"}, - {"l1", "l2"}, - {"l1", "l1loop"}, - {"l1loop", "l1loop_left"}, - {"l1loop", "l1loop_right"}, - {"l1loop_left", "l1"}, - {"l1loop_right", "l1"}, - {"l2", "exit"}, - {"entry", "right"}, - {"right", "exit"}})); - ASSERT_TRUE(ExecutionSubgraph::CanAnalyse(graph_)); - ExecutionSubgraph esg(graph_, /*analysis_possible=*/true, GetScopedAllocator()); - esg.RemoveBlock(blks.Get("l1loop_left")); - esg.Finalize(); - ASSERT_TRUE(esg.IsValid()); - ASSERT_TRUE(IsValidSubgraph(esg)); - std::unordered_set<const HBasicBlock*> contents(esg.ReachableBlocks().begin(), - esg.ReachableBlocks().end()); - - ASSERT_EQ(contents.size(), 3u); - - // Not present, no path through. If we got to l1 loop then we must merge back - // with l1 and l2 so they're bad too. - ASSERT_TRUE(contents.find(blks.Get("l1loop")) == contents.end()); - ASSERT_TRUE(contents.find(blks.Get("l1")) == contents.end()); - ASSERT_TRUE(contents.find(blks.Get("l1loop_left")) == contents.end()); - ASSERT_TRUE(contents.find(blks.Get("l1loop_right")) == contents.end()); - ASSERT_TRUE(contents.find(blks.Get("l2")) == contents.end()); - - // present, path through. - ASSERT_TRUE(contents.find(blks.Get("right")) != contents.end()); - ASSERT_TRUE(contents.find(blks.Get("entry")) != contents.end()); - ASSERT_TRUE(contents.find(blks.Get("exit")) != contents.end()); -} - TEST_F(ExecutionSubgraphTest, Invalid) { AdjacencyListGraph blks(SetupFromAdjacencyList( "entry", diff --git a/compiler/optimizing/graph_visualizer.cc b/compiler/optimizing/graph_visualizer.cc index 5a264b7a70..da34af28a5 100644 --- a/compiler/optimizing/graph_visualizer.cc +++ b/compiler/optimizing/graph_visualizer.cc @@ -19,7 +19,6 @@ #include <dlfcn.h> #include <cctype> -#include <ios> #include <sstream> #include "android-base/stringprintf.h" @@ -530,13 +529,6 @@ class HGraphVisualizerPrinter : public HGraphDelegateVisitor { StartAttributeStream("invoke_type") << "InvokePolymorphic"; } - void VisitPredicatedInstanceFieldGet(HPredicatedInstanceFieldGet* iget) override { - StartAttributeStream("field_name") << - iget->GetFieldInfo().GetDexFile().PrettyField(iget->GetFieldInfo().GetFieldIndex(), - /* with type */ false); - StartAttributeStream("field_type") << iget->GetFieldType(); - } - void VisitInstanceFieldGet(HInstanceFieldGet* iget) override { StartAttributeStream("field_name") << iget->GetFieldInfo().GetDexFile().PrettyField(iget->GetFieldInfo().GetFieldIndex(), @@ -549,7 +541,6 @@ class HGraphVisualizerPrinter : public HGraphDelegateVisitor { iset->GetFieldInfo().GetDexFile().PrettyField(iset->GetFieldInfo().GetFieldIndex(), /* with type */ false); StartAttributeStream("field_type") << iset->GetFieldType(); - StartAttributeStream("predicated") << std::boolalpha << iset->GetIsPredicatedSet(); } void VisitStaticFieldGet(HStaticFieldGet* sget) override { diff --git a/compiler/optimizing/instruction_simplifier.cc b/compiler/optimizing/instruction_simplifier.cc index 71376178b1..8886f14726 100644 --- a/compiler/optimizing/instruction_simplifier.cc +++ b/compiler/optimizing/instruction_simplifier.cc @@ -23,7 +23,6 @@ #include "escape.h" #include "intrinsics.h" #include "mirror/class-inl.h" -#include "optimizing/nodes.h" #include "scoped_thread_state_change-inl.h" #include "sharpening.h" #include "string_builder_append.h" @@ -110,7 +109,6 @@ class InstructionSimplifierVisitor : public HGraphDelegateVisitor { void VisitInvoke(HInvoke* invoke) override; void VisitDeoptimize(HDeoptimize* deoptimize) override; void VisitVecMul(HVecMul* instruction) override; - void VisitPredicatedInstanceFieldGet(HPredicatedInstanceFieldGet* instruction) override; bool CanEnsureNotNullAt(HInstruction* instr, HInstruction* at) const; @@ -917,42 +915,6 @@ static HInstruction* AllowInMinMax(IfCondition cmp, return nullptr; } -// TODO This should really be done by LSE itself since there is significantly -// more information available there. -void InstructionSimplifierVisitor::VisitPredicatedInstanceFieldGet( - HPredicatedInstanceFieldGet* pred_get) { - HInstruction* target = pred_get->GetTarget(); - HInstruction* default_val = pred_get->GetDefaultValue(); - // TODO Technically we could end up with a case where the target isn't a phi - // (allowing us to eliminate the instruction and replace with either a - // InstanceFieldGet or the default) but due to the ordering of compilation - // passes this can't happen in ART. - if (!target->IsPhi() || !default_val->IsPhi() || default_val->GetBlock() != target->GetBlock()) { - // Already reduced the target or the phi selection will differ between the - // target and default. - return; - } - DCHECK_EQ(default_val->InputCount(), target->InputCount()); - // In the same block both phis only one non-null we can remove the phi from default_val. - HInstruction* single_value = nullptr; - auto inputs = target->GetInputs(); - for (auto [input, idx] : ZipCount(MakeIterationRange(inputs))) { - if (input->CanBeNull()) { - if (single_value == nullptr) { - single_value = default_val->InputAt(idx); - } else if (single_value != default_val->InputAt(idx) && - !single_value->Equals(default_val->InputAt(idx))) { - // Multiple values, can't combine. - return; - } - } - } - if (single_value->StrictlyDominates(pred_get)) { - // Combine all the maybe null values into one. - pred_get->ReplaceInput(single_value, 0); - } -} - void InstructionSimplifierVisitor::VisitSelect(HSelect* select) { HInstruction* replace_with = nullptr; HInstruction* condition = select->GetCondition(); @@ -1136,9 +1098,6 @@ static inline bool TryReplaceFieldOrArrayGetType(HInstruction* maybe_get, DataTy if (maybe_get->IsInstanceFieldGet()) { maybe_get->AsInstanceFieldGet()->SetType(new_type); return true; - } else if (maybe_get->IsPredicatedInstanceFieldGet()) { - maybe_get->AsPredicatedInstanceFieldGet()->SetType(new_type); - return true; } else if (maybe_get->IsStaticFieldGet()) { maybe_get->AsStaticFieldGet()->SetType(new_type); return true; diff --git a/compiler/optimizing/load_store_analysis.cc b/compiler/optimizing/load_store_analysis.cc index 38ed98adaf..3daa6472de 100644 --- a/compiler/optimizing/load_store_analysis.cc +++ b/compiler/optimizing/load_store_analysis.cc @@ -16,9 +16,6 @@ #include "load_store_analysis.h" -#include "base/scoped_arena_allocator.h" -#include "optimizing/escape.h" - namespace art { // A cap for the number of heap locations to prevent pathological time/space consumption. @@ -103,11 +100,14 @@ void ReferenceInfo::PrunePartialEscapeWrites() { allocator_, graph->GetBlocks().size(), false, kArenaAllocLSA); for (const HUseListNode<HInstruction*>& use : reference_->GetUses()) { const HInstruction* user = use.GetUser(); - if (!additional_exclusions.IsBitSet(user->GetBlock()->GetBlockId()) && - subgraph_.ContainsBlock(user->GetBlock()) && + const bool possible_exclusion = + !additional_exclusions.IsBitSet(user->GetBlock()->GetBlockId()) && + subgraph_.ContainsBlock(user->GetBlock()); + const bool is_written_to = (user->IsUnresolvedInstanceFieldSet() || user->IsUnresolvedStaticFieldSet() || user->IsInstanceFieldSet() || user->IsStaticFieldSet() || user->IsArraySet()) && - (reference_ == user->InputAt(0)) && + (reference_ == user->InputAt(0)); + if (possible_exclusion && is_written_to && std::any_of(subgraph_.UnreachableBlocks().begin(), subgraph_.UnreachableBlocks().end(), [&](const HBasicBlock* excluded) -> bool { @@ -148,37 +148,6 @@ bool HeapLocationCollector::InstructionEligibleForLSERemoval(HInstruction* inst) } } -void ReferenceInfo::CollectPartialEscapes(HGraph* graph) { - ScopedArenaAllocator saa(graph->GetArenaStack()); - ArenaBitVector seen_instructions(&saa, graph->GetCurrentInstructionId(), false, kArenaAllocLSA); - // Get regular escapes. - ScopedArenaVector<HInstruction*> additional_escape_vectors(saa.Adapter(kArenaAllocLSA)); - LambdaEscapeVisitor scan_instructions([&](HInstruction* escape) -> bool { - HandleEscape(escape); - // LSE can't track heap-locations through Phi and Select instructions so we - // need to assume all escapes from these are escapes for the base reference. - if ((escape->IsPhi() || escape->IsSelect()) && !seen_instructions.IsBitSet(escape->GetId())) { - seen_instructions.SetBit(escape->GetId()); - additional_escape_vectors.push_back(escape); - } - return true; - }); - additional_escape_vectors.push_back(reference_); - while (!additional_escape_vectors.empty()) { - HInstruction* ref = additional_escape_vectors.back(); - additional_escape_vectors.pop_back(); - DCHECK(ref == reference_ || ref->IsPhi() || ref->IsSelect()) << *ref; - VisitEscapes(ref, scan_instructions); - } - - // Mark irreducible loop headers as escaping since they cannot be tracked through. - for (HBasicBlock* blk : graph->GetActiveBlocks()) { - if (blk->IsLoopHeader() && blk->GetLoopInformation()->IsIrreducible()) { - HandleEscape(blk); - } - } -} - void HeapLocationCollector::DumpReferenceStats(OptimizingCompilerStats* stats) { if (stats == nullptr) { return; diff --git a/compiler/optimizing/load_store_analysis.h b/compiler/optimizing/load_store_analysis.h index e81572743e..5d2d841b37 100644 --- a/compiler/optimizing/load_store_analysis.h +++ b/compiler/optimizing/load_store_analysis.h @@ -30,12 +30,6 @@ namespace art { -enum class LoadStoreAnalysisType { - kBasic, - kNoPredicatedInstructions, - kFull, -}; - // A ReferenceInfo contains additional info about a reference such as // whether it's a singleton, returned, etc. class ReferenceInfo : public DeletableArenaObject<kArenaAllocLSA> { @@ -43,23 +37,22 @@ class ReferenceInfo : public DeletableArenaObject<kArenaAllocLSA> { ReferenceInfo(HInstruction* reference, ScopedArenaAllocator* allocator, size_t pos, - LoadStoreAnalysisType elimination_type) + bool for_partial_elimination) : reference_(reference), position_(pos), is_singleton_(true), is_singleton_and_not_returned_(true), is_singleton_and_not_deopt_visible_(true), allocator_(allocator), - subgraph_(reference->GetBlock()->GetGraph(), - elimination_type != LoadStoreAnalysisType::kBasic, - allocator_) { + subgraph_(reference->GetBlock()->GetGraph(), for_partial_elimination, allocator_) { // TODO We can do this in one pass. // TODO NewArray is possible but will need to get a handle on how to deal with the dynamic loads // for now just ignore it. - bool can_be_partial = elimination_type != LoadStoreAnalysisType::kBasic && - (/* reference_->IsNewArray() || */ reference_->IsNewInstance()); + bool can_be_partial = + for_partial_elimination && (/* reference_->IsNewArray() || */ reference_->IsNewInstance()); + LambdaEscapeVisitor func([&](HInstruction* inst) { return HandleEscape(inst); }); if (can_be_partial) { - CollectPartialEscapes(reference_->GetBlock()->GetGraph()); + VisitEscapes(reference_, func); } CalculateEscape(reference_, nullptr, @@ -67,12 +60,10 @@ class ReferenceInfo : public DeletableArenaObject<kArenaAllocLSA> { &is_singleton_and_not_returned_, &is_singleton_and_not_deopt_visible_); if (can_be_partial) { - if (elimination_type == LoadStoreAnalysisType::kNoPredicatedInstructions) { - // This is to mark writes to partially escaped values as also part of the escaped subset. - // TODO We can avoid this if we have a 'ConditionalWrite' instruction. Will require testing - // to see if the additional branches are worth it. - PrunePartialEscapeWrites(); - } + // This is to mark writes to partially escaped values as also part of the escaped subset. + // TODO We can avoid this if we have a 'ConditionalWrite' instruction. Will require testing + // to see if the additional branches are worth it. + PrunePartialEscapeWrites(); subgraph_.Finalize(); } else { subgraph_.Invalidate(); @@ -121,12 +112,9 @@ class ReferenceInfo : public DeletableArenaObject<kArenaAllocLSA> { } private: - void CollectPartialEscapes(HGraph* graph); - void HandleEscape(HBasicBlock* escape) { - subgraph_.RemoveBlock(escape); - } - void HandleEscape(HInstruction* escape) { - HandleEscape(escape->GetBlock()); + bool HandleEscape(HInstruction* escape) { + subgraph_.RemoveBlock(escape->GetBlock()); + return true; } // Make sure we mark any writes/potential writes to heap-locations within partially @@ -241,7 +229,7 @@ class HeapLocationCollector : public HGraphVisitor { HeapLocationCollector(HGraph* graph, ScopedArenaAllocator* allocator, - LoadStoreAnalysisType lse_type) + bool for_partial_elimination) : HGraphVisitor(graph), allocator_(allocator), ref_info_array_(allocator->Adapter(kArenaAllocLSA)), @@ -250,7 +238,7 @@ class HeapLocationCollector : public HGraphVisitor { has_heap_stores_(false), has_volatile_(false), has_monitor_operations_(false), - lse_type_(lse_type) { + for_partial_elimination_(for_partial_elimination) { aliasing_matrix_.ClearAllBits(); } @@ -264,10 +252,6 @@ class HeapLocationCollector : public HGraphVisitor { ref_info_array_.clear(); } - size_t GetNumberOfReferenceInfos() const { - return ref_info_array_.size(); - } - size_t GetNumberOfHeapLocations() const { return heap_locations_.size(); } @@ -276,11 +260,6 @@ class HeapLocationCollector : public HGraphVisitor { return heap_locations_[index]; } - size_t GetHeapLocationIndex(const HeapLocation* hl) const { - auto res = std::find(heap_locations_.cbegin(), heap_locations_.cend(), hl); - return std::distance(heap_locations_.cbegin(), res); - } - HInstruction* HuntForOriginalReference(HInstruction* ref) const { // An original reference can be transformed by instructions like: // i0 NewArray @@ -501,7 +480,8 @@ class HeapLocationCollector : public HGraphVisitor { ReferenceInfo* ref_info = FindReferenceInfoOf(instruction); if (ref_info == nullptr) { size_t pos = ref_info_array_.size(); - ref_info = new (allocator_) ReferenceInfo(instruction, allocator_, pos, lse_type_); + ref_info = + new (allocator_) ReferenceInfo(instruction, allocator_, pos, for_partial_elimination_); ref_info_array_.push_back(ref_info); } return ref_info; @@ -559,10 +539,6 @@ class HeapLocationCollector : public HGraphVisitor { HeapLocation::kDeclaringClassDefIndexForArrays); } - void VisitPredicatedInstanceFieldGet(HPredicatedInstanceFieldGet* instruction) override { - VisitFieldAccess(instruction->InputAt(0), instruction->GetFieldInfo()); - CreateReferenceInfoForReferenceType(instruction); - } void VisitInstanceFieldGet(HInstanceFieldGet* instruction) override { VisitFieldAccess(instruction->InputAt(0), instruction->GetFieldInfo()); CreateReferenceInfoForReferenceType(instruction); @@ -642,7 +618,7 @@ class HeapLocationCollector : public HGraphVisitor { // alias analysis and won't be as effective. bool has_volatile_; // If there are volatile field accesses. bool has_monitor_operations_; // If there are monitor operations. - LoadStoreAnalysisType lse_type_; + bool for_partial_elimination_; DISALLOW_COPY_AND_ASSIGN(HeapLocationCollector); }; @@ -654,13 +630,13 @@ class LoadStoreAnalysis { explicit LoadStoreAnalysis(HGraph* graph, OptimizingCompilerStats* stats, ScopedArenaAllocator* local_allocator, - LoadStoreAnalysisType lse_type) + bool for_elimination = true) : graph_(graph), stats_(stats), heap_location_collector_( graph, local_allocator, - ExecutionSubgraph::CanAnalyse(graph_) ? lse_type : LoadStoreAnalysisType::kBasic) {} + /*for_partial_elimination=*/for_elimination && ExecutionSubgraph::CanAnalyse(graph_)) {} const HeapLocationCollector& GetHeapLocationCollector() const { return heap_location_collector_; diff --git a/compiler/optimizing/load_store_analysis_test.cc b/compiler/optimizing/load_store_analysis_test.cc index fd15802fd3..a5b628cf05 100644 --- a/compiler/optimizing/load_store_analysis_test.cc +++ b/compiler/optimizing/load_store_analysis_test.cc @@ -100,7 +100,8 @@ TEST_F(LoadStoreAnalysisTest, ArrayHeapLocations) { // Test HeapLocationCollector initialization. // Should be no heap locations, no operations on the heap. ScopedArenaAllocator allocator(graph_->GetArenaStack()); - HeapLocationCollector heap_location_collector(graph_, &allocator, LoadStoreAnalysisType::kFull); + HeapLocationCollector heap_location_collector( + graph_, &allocator, /*for_partial_elimination=*/true); ASSERT_EQ(heap_location_collector.GetNumberOfHeapLocations(), 0U); ASSERT_FALSE(heap_location_collector.HasHeapStores()); @@ -197,7 +198,8 @@ TEST_F(LoadStoreAnalysisTest, FieldHeapLocations) { // Test HeapLocationCollector initialization. // Should be no heap locations, no operations on the heap. ScopedArenaAllocator allocator(graph_->GetArenaStack()); - HeapLocationCollector heap_location_collector(graph_, &allocator, LoadStoreAnalysisType::kFull); + HeapLocationCollector heap_location_collector( + graph_, &allocator, /*for_partial_elimination=*/true); ASSERT_EQ(heap_location_collector.GetNumberOfHeapLocations(), 0U); ASSERT_FALSE(heap_location_collector.HasHeapStores()); @@ -277,7 +279,7 @@ TEST_F(LoadStoreAnalysisTest, ArrayIndexAliasingTest) { entry->AddInstruction(arr_set8); // array[i-(-1)] = c0 ScopedArenaAllocator allocator(graph_->GetArenaStack()); - LoadStoreAnalysis lsa(graph_, nullptr, &allocator, LoadStoreAnalysisType::kBasic); + LoadStoreAnalysis lsa(graph_, nullptr, &allocator, /*for_elimination=*/false); lsa.Run(); const HeapLocationCollector& heap_location_collector = lsa.GetHeapLocationCollector(); @@ -444,7 +446,7 @@ TEST_F(LoadStoreAnalysisTest, ArrayAliasingTest) { entry->AddInstruction(vstore_i_add6_vlen2); ScopedArenaAllocator allocator(graph_->GetArenaStack()); - LoadStoreAnalysis lsa(graph_, nullptr, &allocator, LoadStoreAnalysisType::kBasic); + LoadStoreAnalysis lsa(graph_, nullptr, &allocator, /*for_elimination=*/false); lsa.Run(); const HeapLocationCollector& heap_location_collector = lsa.GetHeapLocationCollector(); @@ -603,7 +605,7 @@ TEST_F(LoadStoreAnalysisTest, ArrayIndexCalculationOverflowTest) { entry->AddInstruction(arr_set_8); ScopedArenaAllocator allocator(graph_->GetArenaStack()); - LoadStoreAnalysis lsa(graph_, nullptr, &allocator, LoadStoreAnalysisType::kBasic); + LoadStoreAnalysis lsa(graph_, nullptr, &allocator, /*for_elimination=*/false); lsa.Run(); const HeapLocationCollector& heap_location_collector = lsa.GetHeapLocationCollector(); @@ -693,7 +695,8 @@ TEST_F(LoadStoreAnalysisTest, TestHuntOriginalRef) { entry->AddInstruction(array_get4); ScopedArenaAllocator allocator(graph_->GetArenaStack()); - HeapLocationCollector heap_location_collector(graph_, &allocator, LoadStoreAnalysisType::kFull); + HeapLocationCollector heap_location_collector( + graph_, &allocator, /*for_partial_elimination=*/true); heap_location_collector.VisitBasicBlock(entry); // Test that the HeapLocationCollector should be able to tell @@ -913,7 +916,7 @@ TEST_F(LoadStoreAnalysisTest, PartialEscape) { exit->AddInstruction(read_final); ScopedArenaAllocator allocator(graph_->GetArenaStack()); - LoadStoreAnalysis lsa(graph_, nullptr, &allocator, LoadStoreAnalysisType::kFull); + LoadStoreAnalysis lsa(graph_, nullptr, &allocator, /*for_elimination=*/true); lsa.Run(); const HeapLocationCollector& heap_location_collector = lsa.GetHeapLocationCollector(); @@ -1020,7 +1023,7 @@ TEST_F(LoadStoreAnalysisTest, PartialEscape2) { exit->AddInstruction(read_final); ScopedArenaAllocator allocator(graph_->GetArenaStack()); - LoadStoreAnalysis lsa(graph_, nullptr, &allocator, LoadStoreAnalysisType::kFull); + LoadStoreAnalysis lsa(graph_, nullptr, &allocator, /*for_elimination=*/true); lsa.Run(); const HeapLocationCollector& heap_location_collector = lsa.GetHeapLocationCollector(); @@ -1141,7 +1144,7 @@ TEST_F(LoadStoreAnalysisTest, PartialEscape3) { exit->AddInstruction(read_final); ScopedArenaAllocator allocator(graph_->GetArenaStack()); - LoadStoreAnalysis lsa(graph_, nullptr, &allocator, LoadStoreAnalysisType::kFull); + LoadStoreAnalysis lsa(graph_, nullptr, &allocator, /*for_elimination=*/true); lsa.Run(); const HeapLocationCollector& heap_location_collector = lsa.GetHeapLocationCollector(); @@ -1162,119 +1165,6 @@ TEST_F(LoadStoreAnalysisTest, PartialEscape3) { ASSERT_TRUE(contents.find(blks.Get("exit")) != contents.end()); } -// before we had predicated-set we needed to be able to remove the store as -// well. This test makes sure that still works. -// // ENTRY -// obj = new Obj(); -// if (parameter_value) { -// // LEFT -// call_func(obj); -// } else { -// // RIGHT -// obj.f1 = 0; -// } -// // EXIT -// // call_func prevents the elimination of this store. -// obj.f2 = 0; -TEST_F(LoadStoreAnalysisTest, TotalEscapeAdjacentNoPredicated) { - AdjacencyListGraph blks(SetupFromAdjacencyList( - "entry", - "exit", - {{"entry", "left"}, {"entry", "right"}, {"left", "exit"}, {"right", "exit"}})); - HBasicBlock* entry = blks.Get("entry"); - HBasicBlock* left = blks.Get("left"); - HBasicBlock* right = blks.Get("right"); - HBasicBlock* exit = blks.Get("exit"); - - HInstruction* bool_value = new (GetAllocator()) - HParameterValue(graph_->GetDexFile(), dex::TypeIndex(1), 1, DataType::Type::kBool); - HInstruction* c0 = graph_->GetIntConstant(0); - HInstruction* cls = new (GetAllocator()) HLoadClass(graph_->GetCurrentMethod(), - dex::TypeIndex(10), - graph_->GetDexFile(), - ScopedNullHandle<mirror::Class>(), - false, - 0, - false); - HInstruction* new_inst = - new (GetAllocator()) HNewInstance(cls, - 0, - dex::TypeIndex(10), - graph_->GetDexFile(), - false, - QuickEntrypointEnum::kQuickAllocObjectInitialized); - HInstruction* if_inst = new (GetAllocator()) HIf(bool_value); - entry->AddInstruction(bool_value); - entry->AddInstruction(cls); - entry->AddInstruction(new_inst); - entry->AddInstruction(if_inst); - - HInstruction* call_left = new (GetAllocator()) - HInvokeStaticOrDirect(GetAllocator(), - 1, - DataType::Type::kVoid, - 0, - {nullptr, 0}, - nullptr, - {}, - InvokeType::kStatic, - {nullptr, 0}, - HInvokeStaticOrDirect::ClinitCheckRequirement::kNone); - HInstruction* goto_left = new (GetAllocator()) HGoto(); - call_left->AsInvoke()->SetRawInputAt(0, new_inst); - left->AddInstruction(call_left); - left->AddInstruction(goto_left); - - HInstruction* write_right = new (GetAllocator()) HInstanceFieldSet(new_inst, - c0, - nullptr, - DataType::Type::kInt32, - MemberOffset(32), - false, - 0, - 0, - graph_->GetDexFile(), - 0); - HInstruction* goto_right = new (GetAllocator()) HGoto(); - right->AddInstruction(write_right); - right->AddInstruction(goto_right); - - HInstruction* write_final = new (GetAllocator()) HInstanceFieldSet(new_inst, - c0, - nullptr, - DataType::Type::kInt32, - MemberOffset(16), - false, - 0, - 0, - graph_->GetDexFile(), - 0); - exit->AddInstruction(write_final); - - ScopedArenaAllocator allocator(graph_->GetArenaStack()); - graph_->ClearDominanceInformation(); - graph_->BuildDominatorTree(); - LoadStoreAnalysis lsa( - graph_, nullptr, &allocator, LoadStoreAnalysisType::kNoPredicatedInstructions); - lsa.Run(); - - const HeapLocationCollector& heap_location_collector = lsa.GetHeapLocationCollector(); - ReferenceInfo* info = heap_location_collector.FindReferenceInfoOf(new_inst); - const ExecutionSubgraph* esg = info->GetNoEscapeSubgraph(); - - EXPECT_FALSE(esg->IsValid()) << esg->GetExcludedCohorts(); - EXPECT_FALSE(IsValidSubgraph(esg)); - std::unordered_set<const HBasicBlock*> contents(esg->ReachableBlocks().begin(), - esg->ReachableBlocks().end()); - - EXPECT_EQ(contents.size(), 0u); - EXPECT_TRUE(contents.find(blks.Get("left")) == contents.end()); - EXPECT_TRUE(contents.find(blks.Get("right")) == contents.end()); - EXPECT_TRUE(contents.find(blks.Get("entry")) == contents.end()); - EXPECT_TRUE(contents.find(blks.Get("exit")) == contents.end()); -} - -// With predicated-set we can (partially) remove the store as well. // // ENTRY // obj = new Obj(); // if (parameter_value) { @@ -1363,25 +1253,23 @@ TEST_F(LoadStoreAnalysisTest, TotalEscapeAdjacent) { exit->AddInstruction(write_final); ScopedArenaAllocator allocator(graph_->GetArenaStack()); - graph_->ClearDominanceInformation(); - graph_->BuildDominatorTree(); - LoadStoreAnalysis lsa(graph_, nullptr, &allocator, LoadStoreAnalysisType::kFull); + LoadStoreAnalysis lsa(graph_, nullptr, &allocator, /*for_elimination=*/true); lsa.Run(); const HeapLocationCollector& heap_location_collector = lsa.GetHeapLocationCollector(); ReferenceInfo* info = heap_location_collector.FindReferenceInfoOf(new_inst); const ExecutionSubgraph* esg = info->GetNoEscapeSubgraph(); - EXPECT_TRUE(esg->IsValid()) << esg->GetExcludedCohorts(); - EXPECT_TRUE(IsValidSubgraph(esg)); + ASSERT_FALSE(esg->IsValid()) << esg->GetExcludedCohorts(); + ASSERT_FALSE(IsValidSubgraph(esg)); std::unordered_set<const HBasicBlock*> contents(esg->ReachableBlocks().begin(), esg->ReachableBlocks().end()); - EXPECT_EQ(contents.size(), 3u); - EXPECT_TRUE(contents.find(blks.Get("left")) == contents.end()); - EXPECT_FALSE(contents.find(blks.Get("right")) == contents.end()); - EXPECT_FALSE(contents.find(blks.Get("entry")) == contents.end()); - EXPECT_FALSE(contents.find(blks.Get("exit")) == contents.end()); + ASSERT_EQ(contents.size(), 0u); + ASSERT_TRUE(contents.find(blks.Get("left")) == contents.end()); + ASSERT_TRUE(contents.find(blks.Get("right")) == contents.end()); + ASSERT_TRUE(contents.find(blks.Get("entry")) == contents.end()); + ASSERT_TRUE(contents.find(blks.Get("exit")) == contents.end()); } // // ENTRY @@ -1484,7 +1372,7 @@ TEST_F(LoadStoreAnalysisTest, TotalEscape) { exit->AddInstruction(read_final); ScopedArenaAllocator allocator(graph_->GetArenaStack()); - LoadStoreAnalysis lsa(graph_, nullptr, &allocator, LoadStoreAnalysisType::kFull); + LoadStoreAnalysis lsa(graph_, nullptr, &allocator, /*for_elimination=*/true); lsa.Run(); const HeapLocationCollector& heap_location_collector = lsa.GetHeapLocationCollector(); @@ -1549,7 +1437,7 @@ TEST_F(LoadStoreAnalysisTest, TotalEscape2) { exit->AddInstruction(return_final); ScopedArenaAllocator allocator(graph_->GetArenaStack()); - LoadStoreAnalysis lsa(graph_, nullptr, &allocator, LoadStoreAnalysisType::kFull); + LoadStoreAnalysis lsa(graph_, nullptr, &allocator, /*for_elimination=*/true); lsa.Run(); const HeapLocationCollector& heap_location_collector = lsa.GetHeapLocationCollector(); @@ -1730,7 +1618,7 @@ TEST_F(LoadStoreAnalysisTest, DoubleDiamondEscape) { exit->AddInstruction(read_final); ScopedArenaAllocator allocator(graph_->GetArenaStack()); - LoadStoreAnalysis lsa(graph_, nullptr, &allocator, LoadStoreAnalysisType::kFull); + LoadStoreAnalysis lsa(graph_, nullptr, &allocator, /*for_elimination=*/true); lsa.Run(); const HeapLocationCollector& heap_location_collector = lsa.GetHeapLocationCollector(); @@ -1745,172 +1633,4 @@ TEST_F(LoadStoreAnalysisTest, DoubleDiamondEscape) { ASSERT_EQ(contents.size(), 0u); } -// // ENTRY -// Obj new_inst = new Obj(); -// new_inst.foo = 12; -// Obj obj; -// Obj out; -// if (param1) { -// // LEFT_START -// if (param2) { -// // LEFT_LEFT -// obj = new_inst; -// } else { -// // LEFT_RIGHT -// obj = obj_param; -// } -// // LEFT_MERGE -// // technically the phi is enough to cause an escape but might as well be -// // thorough. -// // obj = phi[new_inst, param] -// escape(obj); -// out = obj; -// } else { -// // RIGHT -// out = obj_param; -// } -// // EXIT -// // Can't do anything with this since we don't have good tracking for the heap-locations -// // out = phi[param, phi[new_inst, param]] -// return out.foo -TEST_F(LoadStoreAnalysisTest, PartialPhiPropagation1) { - CreateGraph(); - AdjacencyListGraph blks(SetupFromAdjacencyList("entry", - "exit", - {{"entry", "left"}, - {"entry", "right"}, - {"left", "left_left"}, - {"left", "left_right"}, - {"left_left", "left_merge"}, - {"left_right", "left_merge"}, - {"left_merge", "breturn"}, - {"right", "breturn"}, - {"breturn", "exit"}})); -#define GET_BLOCK(name) HBasicBlock* name = blks.Get(#name) - GET_BLOCK(entry); - GET_BLOCK(exit); - GET_BLOCK(breturn); - GET_BLOCK(left); - GET_BLOCK(right); - GET_BLOCK(left_left); - GET_BLOCK(left_right); - GET_BLOCK(left_merge); -#undef GET_BLOCK - EnsurePredecessorOrder(breturn, {left_merge, right}); - EnsurePredecessorOrder(left_merge, {left_left, left_right}); - HInstruction* param1 = new (GetAllocator()) - HParameterValue(graph_->GetDexFile(), dex::TypeIndex(1), 1, DataType::Type::kBool); - HInstruction* param2 = new (GetAllocator()) - HParameterValue(graph_->GetDexFile(), dex::TypeIndex(1), 2, DataType::Type::kBool); - HInstruction* obj_param = new (GetAllocator()) - HParameterValue(graph_->GetDexFile(), dex::TypeIndex(10), 3, DataType::Type::kReference); - HInstruction* c12 = graph_->GetIntConstant(12); - HInstruction* cls = new (GetAllocator()) HLoadClass(graph_->GetCurrentMethod(), - dex::TypeIndex(10), - graph_->GetDexFile(), - ScopedNullHandle<mirror::Class>(), - false, - 0, - false); - HInstruction* new_inst = - new (GetAllocator()) HNewInstance(cls, - 0, - dex::TypeIndex(10), - graph_->GetDexFile(), - false, - QuickEntrypointEnum::kQuickAllocObjectInitialized); - HInstruction* store = new (GetAllocator()) HInstanceFieldSet(new_inst, - c12, - nullptr, - DataType::Type::kInt32, - MemberOffset(32), - false, - 0, - 0, - graph_->GetDexFile(), - 0); - HInstruction* if_param1 = new (GetAllocator()) HIf(param1); - entry->AddInstruction(param1); - entry->AddInstruction(param2); - entry->AddInstruction(obj_param); - entry->AddInstruction(cls); - entry->AddInstruction(new_inst); - entry->AddInstruction(store); - entry->AddInstruction(if_param1); - ArenaVector<HInstruction*> current_locals({}, GetAllocator()->Adapter(kArenaAllocInstruction)); - ManuallyBuildEnvFor(cls, ¤t_locals); - new_inst->CopyEnvironmentFrom(cls->GetEnvironment()); - - HInstruction* if_left = new (GetAllocator()) HIf(param2); - left->AddInstruction(if_left); - - HInstruction* goto_left_left = new (GetAllocator()) HGoto(); - left_left->AddInstruction(goto_left_left); - - HInstruction* goto_left_right = new (GetAllocator()) HGoto(); - left_right->AddInstruction(goto_left_right); - - HPhi* left_phi = - new (GetAllocator()) HPhi(GetAllocator(), kNoRegNumber, 2, DataType::Type::kReference); - HInstruction* call_left = new (GetAllocator()) - HInvokeStaticOrDirect(GetAllocator(), - 1, - DataType::Type::kVoid, - 0, - {nullptr, 0}, - nullptr, - {}, - InvokeType::kStatic, - {nullptr, 0}, - HInvokeStaticOrDirect::ClinitCheckRequirement::kNone); - HInstruction* goto_left_merge = new (GetAllocator()) HGoto(); - left_phi->SetRawInputAt(0, obj_param); - left_phi->SetRawInputAt(1, new_inst); - call_left->AsInvoke()->SetRawInputAt(0, left_phi); - left_merge->AddPhi(left_phi); - left_merge->AddInstruction(call_left); - left_merge->AddInstruction(goto_left_merge); - left_phi->SetCanBeNull(true); - call_left->CopyEnvironmentFrom(cls->GetEnvironment()); - - HInstruction* goto_right = new (GetAllocator()) HGoto(); - right->AddInstruction(goto_right); - - HPhi* return_phi = - new (GetAllocator()) HPhi(GetAllocator(), kNoRegNumber, 2, DataType::Type::kReference); - HInstruction* read_exit = new (GetAllocator()) HInstanceFieldGet(return_phi, - nullptr, - DataType::Type::kReference, - MemberOffset(32), - false, - 0, - 0, - graph_->GetDexFile(), - 0); - HInstruction* return_exit = new (GetAllocator()) HReturn(read_exit); - return_phi->SetRawInputAt(0, left_phi); - return_phi->SetRawInputAt(1, obj_param); - breturn->AddPhi(return_phi); - breturn->AddInstruction(read_exit); - breturn->AddInstruction(return_exit); - - HInstruction* exit_instruction = new (GetAllocator()) HExit(); - exit->AddInstruction(exit_instruction); - - graph_->ClearDominanceInformation(); - graph_->BuildDominatorTree(); - - ScopedArenaAllocator allocator(graph_->GetArenaStack()); - LoadStoreAnalysis lsa(graph_, nullptr, &allocator, LoadStoreAnalysisType::kFull); - lsa.Run(); - - const HeapLocationCollector& heap_location_collector = lsa.GetHeapLocationCollector(); - ReferenceInfo* info = heap_location_collector.FindReferenceInfoOf(new_inst); - const ExecutionSubgraph* esg = info->GetNoEscapeSubgraph(); - std::unordered_set<const HBasicBlock*> contents(esg->ReachableBlocks().begin(), - esg->ReachableBlocks().end()); - - ASSERT_EQ(contents.size(), 0u); - ASSERT_FALSE(esg->IsValid()); -} } // namespace art diff --git a/compiler/optimizing/load_store_elimination.cc b/compiler/optimizing/load_store_elimination.cc index 17ce694750..2e0f2b12d5 100644 --- a/compiler/optimizing/load_store_elimination.cc +++ b/compiler/optimizing/load_store_elimination.cc @@ -27,23 +27,16 @@ #include "base/bit_vector-inl.h" #include "base/bit_vector.h" #include "base/globals.h" -#include "base/indenter.h" #include "base/iteration_range.h" #include "base/scoped_arena_allocator.h" #include "base/scoped_arena_containers.h" -#include "base/transform_iterator.h" #include "escape.h" #include "execution_subgraph.h" -#include "handle.h" #include "load_store_analysis.h" -#include "mirror/class_loader.h" -#include "mirror/dex_cache.h" #include "nodes.h" -#include "optimizing/execution_subgraph.h" #include "optimizing_compiler_stats.h" #include "reference_type_propagation.h" #include "side_effects_analysis.h" -#include "stack_map.h" /** * The general algorithm of load-store elimination (LSE). @@ -64,9 +57,6 @@ * - In phase 4, we commit the changes, replacing loads marked for elimination * in previous processing and removing stores not marked for keeping. We also * remove allocations that are no longer needed. - * - In phase 5, we move allocations which only escape along some executions - * closer to their escape points and fixup non-escaping paths with their actual - * values, creating PHIs when needed. * * 1. Walk over blocks and their instructions. * @@ -92,9 +82,7 @@ * to maintain the validity of all heap locations during the optimization * phase, we only record substitutes at this phase and the real elimination * is delayed till the end of LSE. Loads that require a loop Phi placeholder - * replacement are recorded for processing later. We also keep track of the - * heap-value at the start load so that later partial-LSE can predicate the - * load. + * replacement are recorded for processing later. * - If the instruction is a store, it updates the heap value for the heap * location with the stored value and records the store itself so that we can * mark it for keeping if the value becomes observable. Heap values are @@ -240,80 +228,7 @@ * The time complexity of this phase is * O(instructions + instruction_uses) . * - * 5. Partial LSE - * - * Move allocations closer to their escapes and remove/predicate loads and - * stores as required. - * - * Partial singletons are objects which only escape from the function or have - * multiple names along certain execution paths. In cases where we recognize - * these partial singletons we can move the allocation and initialization - * closer to the actual escape(s). We can then perform a simplified version of - * LSE step 2 to determine the unescaped value of any reads performed after the - * object may have escaped. These are used to replace these reads with - * 'predicated-read' instructions where the value is only read if the object - * has actually escaped. We use the existence of the object itself as the - * marker of whether escape has occurred. - * - * There are several steps in this sub-pass - * - * 5.1 Group references - * - * Since all heap-locations for a single reference escape at the same time, we - * need to group the heap-locations by reference and process them at the same - * time. - * - * O(heap_locations). - * - * FIXME: The time complexity above assumes we can bucket the heap-locations in - * O(1) which is not true since we just perform a linear-scan of the heap-ref - * list. Since there are generally only a small number of heap-references which - * are partial-singletons this is fine and lower real overhead than a hash map. - * - * 5.2 Generate materializations - * - * Once we have the references we add new 'materialization blocks' on the edges - * where escape becomes inevitable. This information is calculated by the - * execution-subgraphs created during load-store-analysis. We create new - * 'materialization's in these blocks and initialize them with the value of - * each heap-location ignoring side effects (since the object hasn't escaped - * yet). Worst case this is the same time-complexity as step 3 since we may - * need to materialize phis. - * - * O(heap_locations^2 * materialization_edges) - * - * 5.3 Propagate materializations - * - * Since we use the materialization as the marker for escape we need to - * propagate it throughout the graph. Since the subgraph analysis considers any - * lifetime that escapes a loop (and hence would require a loop-phi) to be - * escaping at the loop-header we do not need to create any loop-phis to do - * this. - * - * O(edges) - * - * NB: Currently the subgraph analysis considers all objects to have their - * lifetimes start at the entry block. This simplifies that analysis enormously - * but means that we cannot distinguish between an escape in a loop where the - * lifetime does not escape the loop (in which case this pass could optimize) - * and one where it does escape the loop (in which case the whole loop is - * escaping). This is a shortcoming that would be good to fix at some point. - * - * 5.4 Propagate partial values - * - * We need to replace loads and stores to the partial reference with predicated - * ones that have default non-escaping values. Again this is the same as step 3. - * - * O(heap_locations^2 * edges) - * - * 5.5 Final fixup - * - * Now all we need to do is replace and remove uses of the old reference with the - * appropriate materialization. - * - * O(instructions + uses) - * - * FIXME: The time complexities described above assumes that the + * FIXME: The time complexity described above assumes that the * HeapLocationCollector finds a heap location for an instruction in O(1) * time but it is currently O(heap_locations); this can be fixed by adding * a hash map to the HeapLocationCollector. @@ -321,18 +236,11 @@ namespace art { -#define LSE_VLOG \ - if (::art::LoadStoreElimination::kVerboseLoggingMode && VLOG_IS_ON(compiler)) LOG(INFO) - -class PartialLoadStoreEliminationHelper; -class HeapRefHolder; - // Use HGraphDelegateVisitor for which all VisitInvokeXXX() delegate to VisitInvoke(). class LSEVisitor final : private HGraphDelegateVisitor { public: LSEVisitor(HGraph* graph, const HeapLocationCollector& heap_location_collector, - bool perform_partial_lse, OptimizingCompilerStats* stats); void Run(); @@ -370,45 +278,6 @@ class LSEVisitor final : private HGraphDelegateVisitor { uint32_t heap_location_; }; - struct Marker {}; - - class Value; - - class PriorValueHolder { - public: - constexpr explicit PriorValueHolder(Value prior); - - constexpr bool IsInstruction() const { - return std::holds_alternative<HInstruction*>(value_); - } - constexpr bool IsPhi() const { - return std::holds_alternative<PhiPlaceholder>(value_); - } - constexpr bool IsDefault() const { - return std::holds_alternative<Marker>(value_); - } - constexpr PhiPlaceholder GetPhiPlaceholder() const { - DCHECK(IsPhi()); - return std::get<PhiPlaceholder>(value_); - } - constexpr HInstruction* GetInstruction() const { - DCHECK(IsInstruction()); - return std::get<HInstruction*>(value_); - } - - Value ToValue() const; - void Dump(std::ostream& oss) const; - - constexpr bool Equals(PriorValueHolder other) const { - return value_ == other.value_; - } - - private: - std::variant<Marker, HInstruction*, PhiPlaceholder> value_; - }; - - friend constexpr bool operator==(const Marker&, const Marker&); - friend constexpr bool operator==(const PriorValueHolder& p1, const PriorValueHolder& p2); friend constexpr bool operator==(const PhiPlaceholder& p1, const PhiPlaceholder& p2); friend std::ostream& operator<<(std::ostream& oss, const PhiPlaceholder& p2); @@ -441,14 +310,6 @@ class LSEVisitor final : private HGraphDelegateVisitor { return Value(ValuelessType::kPureUnknown); } - static constexpr Value PartialUnknown(Value old_value) { - if (old_value.IsInvalid() || old_value.IsPureUnknown()) { - return PureUnknown(); - } else { - return Value(PriorValueHolder(old_value)); - } - } - static constexpr Value MergedUnknown(PhiPlaceholder phi_placeholder) { return Value(MergedUnknownMarker{phi_placeholder}); } @@ -485,10 +346,6 @@ class LSEVisitor final : private HGraphDelegateVisitor { GetValuelessType() == ValuelessType::kInvalid; } - bool IsPartialUnknown() const { - return std::holds_alternative<PriorValueHolder>(value_); - } - bool IsMergedUnknown() const { return std::holds_alternative<MergedUnknownMarker>(value_); } @@ -499,7 +356,7 @@ class LSEVisitor final : private HGraphDelegateVisitor { } bool IsUnknown() const { - return IsPureUnknown() || IsMergedUnknown() || IsPartialUnknown(); + return IsPureUnknown() || IsMergedUnknown(); } bool IsDefault() const { @@ -524,15 +381,10 @@ class LSEVisitor final : private HGraphDelegateVisitor { } HInstruction* GetInstruction() const { - DCHECK(IsInstruction()) << *this; + DCHECK(IsInstruction()); return std::get<HInstruction*>(value_); } - PriorValueHolder GetPriorValue() const { - DCHECK(IsPartialUnknown()); - return std::get<PriorValueHolder>(value_); - } - PhiPlaceholder GetPhiPlaceholder() const { DCHECK(NeedsPhi() || IsMergedUnknown()); if (NeedsNonLoopPhi()) { @@ -550,7 +402,7 @@ class LSEVisitor final : private HGraphDelegateVisitor { } HBasicBlock* GetMergeBlock(const HGraph* graph) const { - DCHECK(IsMergedUnknown()) << *this; + DCHECK(IsMergedUnknown()) << this; return graph->GetBlocks()[GetMergeBlockId()]; } @@ -559,8 +411,6 @@ class LSEVisitor final : private HGraphDelegateVisitor { return GetPhiPlaceholder().GetHeapLocation(); } - constexpr bool ExactEquals(Value other) const; - constexpr bool Equals(Value other) const; constexpr bool Equals(HInstruction* instruction) const { @@ -577,8 +427,7 @@ class LSEVisitor final : private HGraphDelegateVisitor { HInstruction*, MergedUnknownMarker, NeedsNonLoopPhiMarker, - NeedsLoopPhiMarker, - PriorValueHolder>; + NeedsLoopPhiMarker>; constexpr ValuelessType GetValuelessType() const { return std::get<ValuelessType>(value_); } @@ -644,9 +493,7 @@ class LSEVisitor final : private HGraphDelegateVisitor { } Value Replacement(Value value) const { - DCHECK(value.NeedsPhi() || - (current_phase_ == Phase::kPartialElimination && value.IsMergedUnknown())) - << value << " phase: " << current_phase_; + DCHECK(value.NeedsPhi()); Value replacement = phi_placeholder_replacements_[PhiPlaceholderIndex(value)]; DCHECK(replacement.IsUnknown() || replacement.IsInstruction()); DCHECK(replacement.IsUnknown() || @@ -655,16 +502,6 @@ class LSEVisitor final : private HGraphDelegateVisitor { } Value ReplacementOrValue(Value value) const { - if (current_phase_ == Phase::kPartialElimination) { - if (value.IsPartialUnknown()) { - value = value.GetPriorValue().ToValue(); - } - if (value.IsMergedUnknown()) { - return phi_placeholder_replacements_[PhiPlaceholderIndex(value)].IsValid() - ? Replacement(value) - : Value::ForLoopPhiPlaceholder(value.GetPhiPlaceholder()); - } - } if (value.NeedsPhi() && phi_placeholder_replacements_[PhiPlaceholderIndex(value)].IsValid()) { return Replacement(value); } else { @@ -761,7 +598,6 @@ class LSEVisitor final : private HGraphDelegateVisitor { static bool IsLoad(HInstruction* instruction) { // Unresolved load is not treated as a load. return instruction->IsInstanceFieldGet() || - instruction->IsPredicatedInstanceFieldGet() || instruction->IsStaticFieldGet() || instruction->IsVecLoad() || instruction->IsArrayGet(); @@ -787,7 +623,7 @@ class LSEVisitor final : private HGraphDelegateVisitor { // Keep the store referenced by the instruction, or all stores that feed a Phi placeholder. // This is necessary if the stored heap value can be observed. void KeepStores(Value value) { - if (value.IsPureUnknown() || value.IsPartialUnknown()) { + if (value.IsPureUnknown()) { return; } if (value.IsMergedUnknown()) { @@ -907,16 +743,12 @@ class LSEVisitor final : private HGraphDelegateVisitor { void VisitGetLocation(HInstruction* instruction, size_t idx); void VisitSetLocation(HInstruction* instruction, size_t idx, HInstruction* value); - void RecordFieldInfo(const FieldInfo* info, size_t heap_loc) { - field_infos_[heap_loc] = info; - } void VisitBasicBlock(HBasicBlock* block) override; enum class Phase { kLoadElimination, - kStoreElimination, - kPartialElimination, + kStoreElimination }; bool TryReplacingLoopPhiPlaceholderWithDefault( @@ -933,10 +765,8 @@ class LSEVisitor final : private HGraphDelegateVisitor { bool can_use_default_or_phi); bool MaterializeLoopPhis(const ScopedArenaVector<size_t>& phi_placeholder_indexes, DataType::Type type); - bool MaterializeLoopPhis(ArrayRef<const size_t> phi_placeholder_indexes, DataType::Type type); bool MaterializeLoopPhis(const ArenaBitVector& phi_placeholders_to_materialize, DataType::Type type); - bool FullyMaterializePhi(PhiPlaceholder phi_placeholder, DataType::Type type); std::optional<PhiPlaceholder> TryToMaterializeLoopPhis(PhiPlaceholder phi_placeholder, HInstruction* load); void ProcessLoopPhiWithUnknownInput(PhiPlaceholder loop_phi_with_unknown_input); @@ -946,22 +776,6 @@ class LSEVisitor final : private HGraphDelegateVisitor { void UpdateValueRecordForStoreElimination(/*inout*/ValueRecord* value_record); void FindOldValueForPhiPlaceholder(PhiPlaceholder phi_placeholder, DataType::Type type); void FindStoresWritingOldValues(); - void FinishFullLSE(); - void PrepareForPartialPhiComputation(); - // Create materialization block and materialization object for the given predecessor of entry. - HInstruction* SetupPartialMaterialization(PartialLoadStoreEliminationHelper& helper, - HeapRefHolder&& holder, - size_t pred_idx, - HBasicBlock* blk); - // Returns the value that would be read by the 'read' instruction on - // 'orig_new_inst' if 'orig_new_inst' has not escaped. - HInstruction* GetPartialValueAt(HNewInstance* orig_new_inst, HInstruction* read); - void MovePartialEscapes(); - - void VisitPredicatedInstanceFieldGet(HPredicatedInstanceFieldGet* instruction) override { - LOG(FATAL) << "Visited instruction " << instruction->DumpWithoutArgs() - << " but LSE should be the only source of predicated-ifield-gets!"; - } void VisitInstanceFieldGet(HInstanceFieldGet* instruction) override { HInstruction* object = instruction->InputAt(0); @@ -1100,7 +914,7 @@ class LSEVisitor final : private HGraphDelegateVisitor { } if (side_effects.DoesAnyWrite()) { // The value may be clobbered. - heap_values[i].value = Value::PartialUnknown(heap_values[i].value); + heap_values[i].value = Value::PureUnknown(); } } } @@ -1196,12 +1010,6 @@ class LSEVisitor final : private HGraphDelegateVisitor { } } - bool ShouldPerformPartialLSE() const { - return perform_partial_lse_ && !GetGraph()->IsCompilingOsr(); - } - - bool perform_partial_lse_; - const HeapLocationCollector& heap_location_collector_; // Use local allocator for allocating memory. @@ -1227,12 +1035,6 @@ class LSEVisitor final : private HGraphDelegateVisitor { // in the end. These are indexed by the load's id. ScopedArenaVector<HInstruction*> substitute_instructions_for_loads_; - // Value at the start of the given instruction for instructions which directly - // read from a heap-location (i.e. FieldGet). The mapping to heap-location is - // implicit through the fact that each instruction can only directly refer to - // a single heap-location. - ScopedArenaHashMap<HInstruction*, Value> intermediate_values_; - // Record stores to keep in a bit vector indexed by instruction ID. ArenaBitVector kept_stores_; // When we need to keep all stores that feed a Phi placeholder, we just record the @@ -1261,70 +1063,23 @@ class LSEVisitor final : private HGraphDelegateVisitor { ScopedArenaVector<HInstruction*> singleton_new_instances_; - // The field infos for each heap location (if relevant). - ScopedArenaVector<const FieldInfo*> field_infos_; - Phase current_phase_; - friend class PartialLoadStoreEliminationHelper; - friend struct ScopedRestoreHeapValues; - friend std::ostream& operator<<(std::ostream& os, const Value& v); - friend std::ostream& operator<<(std::ostream& os, const PriorValueHolder& v); - friend std::ostream& operator<<(std::ostream& oss, const LSEVisitor::Phase& phase); + friend std::ostream& operator<<(std::ostream& os, const Phase& phase); DISALLOW_COPY_AND_ASSIGN(LSEVisitor); }; -std::ostream& operator<<(std::ostream& oss, const LSEVisitor::PriorValueHolder& p) { - p.Dump(oss); - return oss; -} - std::ostream& operator<<(std::ostream& oss, const LSEVisitor::Phase& phase) { switch (phase) { case LSEVisitor::Phase::kLoadElimination: return oss << "kLoadElimination"; case LSEVisitor::Phase::kStoreElimination: return oss << "kStoreElimination"; - case LSEVisitor::Phase::kPartialElimination: - return oss << "kPartialElimination"; } } -void LSEVisitor::PriorValueHolder::Dump(std::ostream& oss) const { - if (IsDefault()) { - oss << "Default"; - } else if (IsPhi()) { - oss << "Phi: " << GetPhiPlaceholder(); - } else { - oss << "Instruction: " << *GetInstruction(); - } -} - -constexpr LSEVisitor::PriorValueHolder::PriorValueHolder(Value val) - : value_(Marker{}) { - DCHECK(!val.IsInvalid() && !val.IsPureUnknown()); - if (val.IsPartialUnknown()) { - value_ = val.GetPriorValue().value_; - } else if (val.IsMergedUnknown() || val.NeedsPhi()) { - value_ = val.GetPhiPlaceholder(); - } else if (val.IsInstruction()) { - value_ = val.GetInstruction(); - } else { - DCHECK(val.IsDefault()); - } -} - -constexpr bool operator==(const LSEVisitor::Marker&, const LSEVisitor::Marker&) { - return true; -} - -constexpr bool operator==(const LSEVisitor::PriorValueHolder& p1, - const LSEVisitor::PriorValueHolder& p2) { - return p1.Equals(p2); -} - constexpr bool operator==(const LSEVisitor::PhiPlaceholder& p1, const LSEVisitor::PhiPlaceholder& p2) { return p1.Equals(p2); @@ -1350,20 +1105,6 @@ std::ostream& operator<<(std::ostream& oss, const LSEVisitor::PhiPlaceholder& p) return oss; } -LSEVisitor::Value LSEVisitor::PriorValueHolder::ToValue() const { - if (IsDefault()) { - return Value::Default(); - } else if (IsPhi()) { - return Value::ForLoopPhiPlaceholder(GetPhiPlaceholder()); - } else { - return Value::ForInstruction(GetInstruction()); - } -} - -constexpr bool LSEVisitor::Value::ExactEquals(LSEVisitor::Value other) const { - return value_ == other.value_; -} - constexpr bool LSEVisitor::Value::Equals(LSEVisitor::Value other) const { // Only valid values can be compared. DCHECK(IsValid()); @@ -1388,8 +1129,6 @@ std::ostream& LSEVisitor::Value::Dump(std::ostream& os) const { case ValuelessType::kInvalid: return os << "Invalid"; } - } else if (IsPartialUnknown()) { - return os << "PartialUnknown[" << GetPriorValue() << "]"; } else if (IsInstruction()) { return os << "Instruction[id: " << GetInstruction()->GetId() << ", block: " << GetInstruction()->GetBlock()->GetBlockId() << "]"; @@ -1412,10 +1151,8 @@ std::ostream& operator<<(std::ostream& os, const LSEVisitor::Value& v) { LSEVisitor::LSEVisitor(HGraph* graph, const HeapLocationCollector& heap_location_collector, - bool perform_partial_lse, OptimizingCompilerStats* stats) : HGraphDelegateVisitor(graph, stats), - perform_partial_lse_(perform_partial_lse), heap_location_collector_(heap_location_collector), allocator_(graph->GetArenaStack()), num_phi_placeholders_(GetGraph()->GetBlocks().size() * @@ -1429,14 +1166,13 @@ LSEVisitor::LSEVisitor(HGraph* graph, substitute_instructions_for_loads_(graph->GetCurrentInstructionId(), nullptr, allocator_.Adapter(kArenaAllocLSE)), - intermediate_values_(allocator_.Adapter(kArenaAllocLSE)), kept_stores_(&allocator_, - /*start_bits=*/graph->GetCurrentInstructionId(), - /*expandable=*/false, + /*start_bits=*/ graph->GetCurrentInstructionId(), + /*expandable=*/ false, kArenaAllocLSE), phi_placeholders_to_search_for_kept_stores_(&allocator_, num_phi_placeholders_, - /*expandable=*/false, + /*expandable=*/ false, kArenaAllocLSE), loads_requiring_loop_phi_(allocator_.Adapter(kArenaAllocLSE)), store_records_(allocator_.Adapter(kArenaAllocLSE)), @@ -1444,12 +1180,10 @@ LSEVisitor::LSEVisitor(HGraph* graph, Value::Invalid(), allocator_.Adapter(kArenaAllocLSE)), kept_merged_unknowns_(&allocator_, - /*start_bits=*/num_phi_placeholders_, - /*expandable=*/false, + /*start_bits=*/ num_phi_placeholders_, + /*expandable=*/ false, kArenaAllocLSE), singleton_new_instances_(allocator_.Adapter(kArenaAllocLSE)), - field_infos_(heap_location_collector_.GetNumberOfHeapLocations(), - allocator_.Adapter(kArenaAllocLSE)), current_phase_(Phase::kLoadElimination) { // Clear bit vectors. phi_placeholders_to_search_for_kept_stores_.ClearAllBits(); @@ -1515,13 +1249,9 @@ void LSEVisitor::PrepareLoopRecords(HBasicBlock* block) { // Don't eliminate loads in irreducible loops. if (block->GetLoopInformation()->IsIrreducible()) { heap_values.resize(num_heap_locations, - {/*value=*/Value::Invalid(), /*stored_by=*/Value::PureUnknown()}); + {/*value=*/Value::PureUnknown(), /*stored_by=*/Value::PureUnknown()}); // Also keep the stores before the loop header, including in blocks that were not visited yet. - bool is_osr = GetGraph()->IsCompilingOsr(); for (size_t idx = 0u; idx != num_heap_locations; ++idx) { - heap_values[idx].value = - is_osr ? Value::PureUnknown() - : Value::MergedUnknown(GetPhiPlaceholder(block->GetBlockId(), idx)); KeepStores(Value::ForLoopPhiPlaceholder(GetPhiPlaceholder(block->GetBlockId(), idx))); } return; @@ -1680,10 +1410,9 @@ void LSEVisitor::MaterializeNonLoopPhis(PhiPlaceholder phi_placeholder, DataType phi_inputs.clear(); for (HBasicBlock* predecessor : current_block->GetPredecessors()) { Value pred_value = ReplacementOrValue(heap_values_for_[predecessor->GetBlockId()][idx].value); - DCHECK(!pred_value.IsPureUnknown()) << pred_value << " block " << current_block->GetBlockId() - << " pred: " << predecessor->GetBlockId(); - if (pred_value.NeedsNonLoopPhi() || - (current_phase_ == Phase::kPartialElimination && pred_value.IsMergedUnknown())) { + DCHECK(!pred_value.IsUnknown()) + << "block " << current_block->GetBlockId() << " pred: " << predecessor->GetBlockId(); + if (pred_value.NeedsNonLoopPhi()) { // We need to process the Phi placeholder first. work_queue.push_back(pred_value.GetPhiPlaceholder()); } else if (pred_value.IsDefault()) { @@ -1710,17 +1439,7 @@ void LSEVisitor::VisitGetLocation(HInstruction* instruction, size_t idx) { uint32_t block_id = instruction->GetBlock()->GetBlockId(); ScopedArenaVector<ValueRecord>& heap_values = heap_values_for_[block_id]; ValueRecord& record = heap_values[idx]; - if (instruction->IsFieldAccess()) { - RecordFieldInfo(&instruction->GetFieldInfo(), idx); - } DCHECK(record.value.IsUnknown() || record.value.Equals(ReplacementOrValue(record.value))); - // If we are unknown, we either come from somewhere untracked or we can reconstruct the partial - // value. - DCHECK(!record.value.IsPureUnknown() || - heap_location_collector_.GetHeapLocation(idx)->GetReferenceInfo() == nullptr || - !heap_location_collector_.GetHeapLocation(idx)->GetReferenceInfo()->IsPartialSingleton()) - << "In " << GetGraph()->PrettyMethod() << ": " << record.value << " for " << *instruction; - intermediate_values_.insert({instruction, record.value}); loads_and_stores_.push_back({ instruction, idx }); if ((record.value.IsDefault() || record.value.NeedsNonLoopPhi()) && !IsDefaultOrPhiAllowedForLoad(instruction)) { @@ -1756,9 +1475,6 @@ void LSEVisitor::VisitGetLocation(HInstruction* instruction, size_t idx) { void LSEVisitor::VisitSetLocation(HInstruction* instruction, size_t idx, HInstruction* value) { DCHECK_NE(idx, HeapLocationCollector::kHeapLocationNotFound); DCHECK(!IsStore(value)) << value->DebugName(); - if (instruction->IsFieldAccess()) { - RecordFieldInfo(&instruction->GetFieldInfo(), idx); - } // value may already have a substitute. value = FindSubstitute(value); HBasicBlock* block = instruction->GetBlock(); @@ -1817,7 +1533,7 @@ void LSEVisitor::VisitSetLocation(HInstruction* instruction, size_t idx, HInstru // Kill heap locations that may alias and keep previous stores to these locations. KeepStores(heap_values[i].stored_by); heap_values[i].stored_by = Value::PureUnknown(); - heap_values[i].value = Value::PartialUnknown(heap_values[i].value); + heap_values[i].value = Value::PureUnknown(); } } @@ -2037,8 +1753,6 @@ std::optional<LSEVisitor::PhiPlaceholder> LSEVisitor::FindLoopPhisToMaterialize( if (!phi_placeholders_to_materialize->IsBitSet(PhiPlaceholderIndex(value))) { phi_placeholders_to_materialize->SetBit(PhiPlaceholderIndex(value)); work_queue.push_back(value.GetPhiPlaceholder()); - LSE_VLOG << "For materialization of " << phi_placeholder - << " we need to materialize " << value; } } } @@ -2050,11 +1764,6 @@ std::optional<LSEVisitor::PhiPlaceholder> LSEVisitor::FindLoopPhisToMaterialize( bool LSEVisitor::MaterializeLoopPhis(const ScopedArenaVector<size_t>& phi_placeholder_indexes, DataType::Type type) { - return MaterializeLoopPhis(ArrayRef<const size_t>(phi_placeholder_indexes), type); -} - -bool LSEVisitor::MaterializeLoopPhis(ArrayRef<const size_t> phi_placeholder_indexes, - DataType::Type type) { // Materialize all predecessors that do not need a loop Phi and determine if all inputs // other than loop Phis are the same. const ArenaVector<HBasicBlock*>& blocks = GetGraph()->GetBlocks(); @@ -2066,11 +1775,8 @@ bool LSEVisitor::MaterializeLoopPhis(ArrayRef<const size_t> phi_placeholder_inde size_t idx = phi_placeholder.GetHeapLocation(); for (HBasicBlock* predecessor : block->GetPredecessors()) { Value value = ReplacementOrValue(heap_values_for_[predecessor->GetBlockId()][idx].value); - if (value.NeedsNonLoopPhi() || - (current_phase_ == Phase::kPartialElimination && value.IsMergedUnknown())) { - DCHECK(current_phase_ == Phase::kLoadElimination || - current_phase_ == Phase::kPartialElimination) - << current_phase_; + if (value.NeedsNonLoopPhi()) { + DCHECK(current_phase_ == Phase::kLoadElimination); MaterializeNonLoopPhis(value.GetPhiPlaceholder(), type); value = Replacement(value); } @@ -2295,15 +2001,6 @@ bool LSEVisitor::MaterializeLoopPhis(const ArenaBitVector& phi_placeholders_to_m return true; } -bool LSEVisitor::FullyMaterializePhi(PhiPlaceholder phi_placeholder, DataType::Type type) { - ScopedArenaAllocator saa(GetGraph()->GetArenaStack()); - ArenaBitVector abv(&saa, num_phi_placeholders_, false, ArenaAllocKind::kArenaAllocLSE); - auto res = - FindLoopPhisToMaterialize(phi_placeholder, &abv, type, /* can_use_default_or_phi=*/true); - CHECK(!res.has_value()) << *res; - return MaterializeLoopPhis(abv, type); -} - std::optional<LSEVisitor::PhiPlaceholder> LSEVisitor::TryToMaterializeLoopPhis( PhiPlaceholder phi_placeholder, HInstruction* load) { DCHECK(phi_placeholder_replacements_[PhiPlaceholderIndex(phi_placeholder)].IsInvalid()); @@ -2447,7 +2144,7 @@ void LSEVisitor::ProcessLoopPhiWithUnknownInput(PhiPlaceholder loop_phi_with_unk // propagated as a value to this load) and store the load as the new heap value. found_unreplaceable_load = true; KeepStores(record.value); - record.value = Value::MergedUnknown(record.value.GetPhiPlaceholder()); + record.value = Value::PureUnknown(); local_heap_values[idx] = Value::ForInstruction(load_or_store); } else if (local_heap_values[idx].NeedsLoopPhi()) { // The load may still be replaced with a Phi later. @@ -2689,57 +2386,7 @@ void LSEVisitor::FindOldValueForPhiPlaceholder(PhiPlaceholder phi_placeholder, !success); } -struct ScopedRestoreHeapValues { - public: - ScopedRestoreHeapValues(ArenaStack* alloc, - size_t num_heap_locs, - ScopedArenaVector<ScopedArenaVector<LSEVisitor::ValueRecord>>& to_restore) - : alloc_(alloc), - updated_values_(alloc_.Adapter(kArenaAllocLSE)), - to_restore_(to_restore) { - updated_values_.reserve(num_heap_locs * to_restore_.size()); - } - - ~ScopedRestoreHeapValues() { - for (const auto& rec : updated_values_) { - to_restore_[rec.blk_id][rec.heap_loc].value = rec.val_; - } - } - - template<typename Func> - void ForEachRecord(Func func) { - for (size_t blk_id : Range(to_restore_.size())) { - for (size_t heap_loc : Range(to_restore_[blk_id].size())) { - LSEVisitor::ValueRecord* vr = &to_restore_[blk_id][heap_loc]; - LSEVisitor::Value initial = vr->value; - func(vr); - if (!vr->value.ExactEquals(initial)) { - updated_values_.push_back({blk_id, heap_loc, initial}); - } - } - } - } - - private: - struct UpdateRecord { - size_t blk_id; - size_t heap_loc; - LSEVisitor::Value val_; - }; - ScopedArenaAllocator alloc_; - ScopedArenaVector<UpdateRecord> updated_values_; - ScopedArenaVector<ScopedArenaVector<LSEVisitor::ValueRecord>>& to_restore_; - - DISALLOW_COPY_AND_ASSIGN(ScopedRestoreHeapValues); -}; - void LSEVisitor::FindStoresWritingOldValues() { - // Partial LSE relies on knowing the real heap-values not the - // store-replacement versions so we need to restore the map after removing - // stores. - ScopedRestoreHeapValues heap_vals(allocator_.GetArenaStack(), - heap_location_collector_.GetNumberOfHeapLocations(), - heap_values_for_); // The Phi placeholder replacements have so far been used for eliminating loads, // tracking values that would be stored if all stores were kept. As we want to // compare actual old values after removing unmarked stores, prune the Phi @@ -2754,14 +2401,10 @@ void LSEVisitor::FindStoresWritingOldValues() { } // Update heap values at end of blocks. - heap_vals.ForEachRecord([&](ValueRecord* rec) { - UpdateValueRecordForStoreElimination(rec); - }); - - if (kIsDebugBuild) { - heap_vals.ForEachRecord([](ValueRecord* rec) { - DCHECK(!rec->value.NeedsNonLoopPhi()) << rec->value; - }); + for (HBasicBlock* block : GetGraph()->GetReversePostOrder()) { + for (ValueRecord& value_record : heap_values_for_[block->GetBlockId()]) { + UpdateValueRecordForStoreElimination(&value_record); + } } // Use local allocator to reduce peak memory usage. @@ -2815,903 +2458,7 @@ void LSEVisitor::Run() { FindStoresWritingOldValues(); // 4. Replace loads and remove unnecessary stores and singleton allocations. - FinishFullLSE(); - - // 5. Move partial escapes down and fixup with PHIs. - current_phase_ = Phase::kPartialElimination; - MovePartialEscapes(); -} - -// Clear unknown loop-phi results. Here we'll be able to use partial-unknowns so we need to -// retry all of them with more information about where they come from. -void LSEVisitor::PrepareForPartialPhiComputation() { - std::replace_if( - phi_placeholder_replacements_.begin(), - phi_placeholder_replacements_.end(), - [](const Value& val) { return val.IsPureUnknown(); }, - Value::Invalid()); -} - -class PartialLoadStoreEliminationHelper { - public: - PartialLoadStoreEliminationHelper(LSEVisitor* lse, ScopedArenaAllocator* alloc) - : lse_(lse), - alloc_(alloc), - new_ref_phis_(alloc_->Adapter(kArenaAllocLSE)), - heap_refs_(alloc_->Adapter(kArenaAllocLSE)), - max_preds_per_block_((*std::max_element(GetGraph()->GetActiveBlocks().begin(), - GetGraph()->GetActiveBlocks().end(), - [](HBasicBlock* a, HBasicBlock* b) { - return a->GetNumberOfPredecessors() < - b->GetNumberOfPredecessors(); - })) - ->GetNumberOfPredecessors()), - materialization_blocks_(GetGraph()->GetBlocks().size() * max_preds_per_block_, - nullptr, - alloc_->Adapter(kArenaAllocLSE)), - first_materialization_block_id_(GetGraph()->GetBlocks().size()) { - heap_refs_.reserve(lse_->heap_location_collector_.GetNumberOfReferenceInfos()); - new_ref_phis_.reserve(lse_->heap_location_collector_.GetNumberOfReferenceInfos() * - GetGraph()->GetBlocks().size()); - CollectInterestingHeapRefs(); - } - - ~PartialLoadStoreEliminationHelper() { - if (heap_refs_.empty()) { - return; - } - ReferenceTypePropagation rtp_fixup(GetGraph(), - Handle<mirror::ClassLoader>(), - Handle<mirror::DexCache>(), - /* is_first_run= */ false); - rtp_fixup.Visit(ArrayRef<HInstruction* const>(new_ref_phis_)); - GetGraph()->ClearLoopInformation(); - GetGraph()->ClearDominanceInformation(); - GetGraph()->ClearReachabilityInformation(); - GetGraph()->BuildDominatorTree(); - GetGraph()->ComputeReachabilityInformation(); - } - class IdxToHeapLoc { - public: - explicit IdxToHeapLoc(const HeapLocationCollector* hlc) : collector_(hlc) {} - HeapLocation* operator()(size_t idx) const { - return collector_->GetHeapLocation(idx); - } - - private: - const HeapLocationCollector* collector_; - }; - - - class HeapReferenceData { - public: - using LocIterator = IterationRange<TransformIterator<BitVector::IndexIterator, IdxToHeapLoc>>; - HeapReferenceData(PartialLoadStoreEliminationHelper* helper, - HNewInstance* new_inst, - const ExecutionSubgraph* subgraph, - ScopedArenaAllocator* alloc) - : new_instance_(new_inst), - helper_(helper), - heap_locs_(alloc, - helper->lse_->heap_location_collector_.GetNumberOfHeapLocations(), - /* expandable= */ false, - kArenaAllocLSE), - materializations_( - // We generally won't need to create too many materialization blocks and we can expand - // this as needed so just start off with 2x. - 2 * helper->lse_->GetGraph()->GetBlocks().size(), - nullptr, - alloc->Adapter(kArenaAllocLSE)), - collector_(helper->lse_->heap_location_collector_), - subgraph_(subgraph) {} - - LocIterator IterateLocations() { - auto idxs = heap_locs_.Indexes(); - return MakeTransformRange(idxs, IdxToHeapLoc(&collector_)); - } - - void AddHeapLocation(size_t idx) { - heap_locs_.SetBit(idx); - } - - const ExecutionSubgraph* GetNoEscapeSubgraph() const { - return subgraph_; - } - - bool IsPostEscape(HBasicBlock* blk) { - return std::any_of( - subgraph_->GetExcludedCohorts().cbegin(), - subgraph_->GetExcludedCohorts().cend(), - [&](const ExecutionSubgraph::ExcludedCohort& ec) { return ec.PrecedesBlock(blk); }); - } - - bool InEscapeCohort(HBasicBlock* blk) { - return std::any_of( - subgraph_->GetExcludedCohorts().cbegin(), - subgraph_->GetExcludedCohorts().cend(), - [&](const ExecutionSubgraph::ExcludedCohort& ec) { return ec.ContainsBlock(blk); }); - } - - bool BeforeAllEscapes(HBasicBlock* b) { - return std::none_of(subgraph_->GetExcludedCohorts().cbegin(), - subgraph_->GetExcludedCohorts().cend(), - [&](const ExecutionSubgraph::ExcludedCohort& ec) { - return ec.PrecedesBlock(b) || ec.ContainsBlock(b); - }); - } - - HNewInstance* OriginalNewInstance() const { - return new_instance_; - } - - // Collect and replace all uses. We need to perform this twice since we will - // generate PHIs and additional uses as we create the default-values for - // pred-gets. These values might be other references that are also being - // partially eliminated. By running just the replacement part again we are - // able to avoid having to keep another whole in-progress partial map - // around. Since we will have already handled all the other uses in the - // first pass the second one will be quite fast. - void FixupUses(bool first_pass) { - ScopedArenaAllocator saa(GetGraph()->GetArenaStack()); - // Replace uses with materialized values. - ScopedArenaVector<InstructionUse<HInstruction>> to_replace(saa.Adapter(kArenaAllocLSE)); - ScopedArenaVector<HInstruction*> to_remove(saa.Adapter(kArenaAllocLSE)); - // Do we need to add a constructor-fence. - ScopedArenaVector<InstructionUse<HConstructorFence>> constructor_fences( - saa.Adapter(kArenaAllocLSE)); - ScopedArenaVector<InstructionUse<HInstruction>> to_predicate(saa.Adapter(kArenaAllocLSE)); - - CollectReplacements(to_replace, to_remove, constructor_fences, to_predicate); - - if (!first_pass) { - // If another partial creates new references they can only be in Phis or pred-get defaults - // so they must be in the to_replace group. - DCHECK(to_predicate.empty()); - DCHECK(constructor_fences.empty()); - DCHECK(to_remove.empty()); - } - - ReplaceInput(to_replace); - RemoveInput(to_remove); - CreateConstructorFences(constructor_fences); - PredicateInstructions(to_predicate); - - CHECK(OriginalNewInstance()->GetUses().empty()) - << OriginalNewInstance()->GetUses() << ", " << OriginalNewInstance()->GetEnvUses(); - } - - void AddMaterialization(HBasicBlock* blk, HInstruction* ins) { - if (blk->GetBlockId() >= materializations_.size()) { - // Make sure the materialization array is large enough, try to avoid - // re-sizing too many times by giving extra space. - materializations_.resize(blk->GetBlockId() * 2, nullptr); - } - DCHECK(materializations_[blk->GetBlockId()] == nullptr) - << "Already have a materialization in block " << blk->GetBlockId() << ": " - << *materializations_[blk->GetBlockId()] << " when trying to set materialization to " - << *ins; - materializations_[blk->GetBlockId()] = ins; - LSE_VLOG << "In block " << blk->GetBlockId() << " materialization is " << *ins; - helper_->NotifyNewMaterialization(ins); - } - - bool HasMaterialization(HBasicBlock* blk) const { - return blk->GetBlockId() < materializations_.size() && - materializations_[blk->GetBlockId()] != nullptr; - } - - HInstruction* GetMaterialization(HBasicBlock* blk) const { - if (materializations_.size() <= blk->GetBlockId() || - materializations_[blk->GetBlockId()] == nullptr) { - // This must be a materialization block added after the partial LSE of - // the current reference finished. Since every edge can only have at - // most one materialization block added to it we can just check the - // blocks predecessor. - DCHECK(helper_->IsMaterializationBlock(blk)); - blk = helper_->FindDominatingNonMaterializationBlock(blk); - DCHECK(!helper_->IsMaterializationBlock(blk)); - } - DCHECK_GT(materializations_.size(), blk->GetBlockId()); - DCHECK(materializations_[blk->GetBlockId()] != nullptr); - return materializations_[blk->GetBlockId()]; - } - - void GenerateMaterializationValueFromPredecessors(HBasicBlock* blk) { - DCHECK(std::none_of(GetNoEscapeSubgraph()->GetExcludedCohorts().begin(), - GetNoEscapeSubgraph()->GetExcludedCohorts().end(), - [&](const ExecutionSubgraph::ExcludedCohort& cohort) { - return cohort.IsEntryBlock(blk); - })); - DCHECK(!HasMaterialization(blk)); - if (blk->IsExitBlock()) { - return; - } else if (blk->IsLoopHeader()) { - // See comment in execution_subgraph.h. Currently we act as though every - // allocation for partial elimination takes place in the entry block. - // This simplifies the analysis by making it so any escape cohort - // expands to contain any loops it is a part of. This is something that - // we should rectify at some point. In either case however we can still - // special case the loop-header since (1) currently the loop can't have - // any merges between different cohort entries since the pre-header will - // be the earliest place entry can happen and (2) even if the analysis - // is improved to consider lifetime of the object WRT loops any values - // which would require loop-phis would have to make the whole loop - // escape anyway. - // This all means we can always use value from the pre-header when the - // block is the loop-header and we didn't already create a - // materialization block. (NB when we do improve the analysis we will - // need to modify the materialization creation code to deal with this - // correctly.) - HInstruction* pre_header_val = - GetMaterialization(blk->GetLoopInformation()->GetPreHeader()); - AddMaterialization(blk, pre_header_val); - return; - } - ScopedArenaAllocator saa(GetGraph()->GetArenaStack()); - ScopedArenaVector<HInstruction*> pred_vals(saa.Adapter(kArenaAllocLSE)); - pred_vals.reserve(blk->GetNumberOfPredecessors()); - for (HBasicBlock* pred : blk->GetPredecessors()) { - DCHECK(HasMaterialization(pred)); - pred_vals.push_back(GetMaterialization(pred)); - } - GenerateMaterializationValueFromPredecessorsDirect(blk, pred_vals); - } - - void GenerateMaterializationValueFromPredecessorsForEntry( - HBasicBlock* entry, const ScopedArenaVector<HInstruction*>& pred_vals) { - DCHECK(std::any_of(GetNoEscapeSubgraph()->GetExcludedCohorts().begin(), - GetNoEscapeSubgraph()->GetExcludedCohorts().end(), - [&](const ExecutionSubgraph::ExcludedCohort& cohort) { - return cohort.IsEntryBlock(entry); - })); - GenerateMaterializationValueFromPredecessorsDirect(entry, pred_vals); - } - - private: - template <typename InstructionType> - struct InstructionUse { - InstructionType* instruction_; - size_t index_; - }; - - void ReplaceInput(const ScopedArenaVector<InstructionUse<HInstruction>>& to_replace) { - for (auto& [ins, idx] : to_replace) { - HInstruction* merged_inst = GetMaterialization(ins->GetBlock()); - if (ins->IsPhi() && merged_inst->IsPhi() && ins->GetBlock() == merged_inst->GetBlock()) { - // Phis we just pass through the appropriate inputs. - ins->ReplaceInput(merged_inst->InputAt(idx), idx); - } else { - ins->ReplaceInput(merged_inst, idx); - } - } - } - - void RemoveInput(const ScopedArenaVector<HInstruction*>& to_remove) { - for (HInstruction* ins : to_remove) { - if (ins->GetBlock() == nullptr) { - // Already dealt with. - continue; - } - DCHECK(BeforeAllEscapes(ins->GetBlock())) << *ins; - if (ins->IsInstanceFieldGet() || ins->IsInstanceFieldSet()) { - ins->GetBlock()->RemoveInstruction(ins); - } else { - // Can only be obj == other, obj != other, obj == obj (!?) or, obj != obj (!?) - // Since PHIs are escapes as far as LSE is concerned and we are before - // any escapes these are the only 4 options. - DCHECK(ins->IsEqual() || ins->IsNotEqual()) << *ins; - HInstruction* replacement; - if (UNLIKELY(ins->InputAt(0) == ins->InputAt(1))) { - replacement = ins->IsEqual() ? GetGraph()->GetIntConstant(1) - : GetGraph()->GetIntConstant(0); - } else { - replacement = ins->IsEqual() ? GetGraph()->GetIntConstant(0) - : GetGraph()->GetIntConstant(1); - } - ins->ReplaceWith(replacement); - ins->GetBlock()->RemoveInstruction(ins); - } - } - } - - void CreateConstructorFences( - const ScopedArenaVector<InstructionUse<HConstructorFence>>& constructor_fences) { - if (!constructor_fences.empty()) { - uint32_t pc = constructor_fences.front().instruction_->GetDexPc(); - for (auto& [cf, idx] : constructor_fences) { - if (cf->GetInputs().size() == 1) { - cf->GetBlock()->RemoveInstruction(cf); - } else { - cf->RemoveInputAt(idx); - } - } - for (const ExecutionSubgraph::ExcludedCohort& ec : - GetNoEscapeSubgraph()->GetExcludedCohorts()) { - for (HBasicBlock* blk : ec.EntryBlocks()) { - for (HBasicBlock* materializer : - Filter(MakeIterationRange(blk->GetPredecessors()), - [&](HBasicBlock* blk) { return helper_->IsMaterializationBlock(blk); })) { - HInstruction* new_cf = new (GetGraph()->GetAllocator()) HConstructorFence( - GetMaterialization(materializer), pc, GetGraph()->GetAllocator()); - materializer->InsertInstructionBefore(new_cf, materializer->GetLastInstruction()); - } - } - } - } - } - - void PredicateInstructions( - const ScopedArenaVector<InstructionUse<HInstruction>>& to_predicate) { - for (auto& [ins, idx] : to_predicate) { - if (UNLIKELY(ins->GetBlock() == nullptr)) { - // Already handled due to obj == obj; - continue; - } else if (ins->IsInstanceFieldGet()) { - // IFieldGet[obj] => PredicatedIFieldGet[PartialValue, obj] - HInstruction* new_fget = new (GetGraph()->GetAllocator()) HPredicatedInstanceFieldGet( - ins->AsInstanceFieldGet(), - GetMaterialization(ins->GetBlock()), - helper_->lse_->GetPartialValueAt(OriginalNewInstance(), ins)); - MaybeRecordStat(helper_->lse_->stats_, MethodCompilationStat::kPredicatedLoadAdded); - ins->GetBlock()->InsertInstructionBefore(new_fget, ins); - if (ins->GetType() == DataType::Type::kReference) { - // Reference info is the same - new_fget->SetReferenceTypeInfo(ins->GetReferenceTypeInfo()); - } - ins->ReplaceWith(new_fget); - ins->ReplaceEnvUsesDominatedBy(ins, new_fget); - CHECK(ins->GetEnvUses().empty() && ins->GetUses().empty()) - << "Instruction: " << *ins << " uses: " << ins->GetUses() - << ", env: " << ins->GetEnvUses(); - ins->GetBlock()->RemoveInstruction(ins); - } else if (ins->IsInstanceFieldSet()) { - // Any predicated sets shouldn't require movement. - ins->AsInstanceFieldSet()->SetIsPredicatedSet(); - MaybeRecordStat(helper_->lse_->stats_, MethodCompilationStat::kPredicatedStoreAdded); - HInstruction* merged_inst = GetMaterialization(ins->GetBlock()); - ins->ReplaceInput(merged_inst, idx); - } else { - // comparisons need to be split into 2. - DCHECK(ins->IsEqual() || ins->IsNotEqual()) << "bad instruction " << *ins; - bool this_is_first = idx == 0; - if (ins->InputAt(0) == ins->InputAt(1)) { - // This is a obj == obj or obj != obj. - // No idea why anyone would do this but whatever. - ins->ReplaceWith(GetGraph()->GetIntConstant(ins->IsEqual() ? 1 : 0)); - ins->GetBlock()->RemoveInstruction(ins); - continue; - } else { - HInstruction* is_escaped = new (GetGraph()->GetAllocator()) - HNotEqual(GetMaterialization(ins->GetBlock()), GetGraph()->GetNullConstant()); - HInstruction* combine_inst = - ins->IsEqual() ? static_cast<HInstruction*>(new (GetGraph()->GetAllocator()) HAnd( - DataType::Type::kBool, is_escaped, ins)) - : static_cast<HInstruction*>(new (GetGraph()->GetAllocator()) HOr( - DataType::Type::kBool, is_escaped, ins)); - ins->ReplaceInput(GetMaterialization(ins->GetBlock()), this_is_first ? 0 : 1); - ins->GetBlock()->InsertInstructionBefore(is_escaped, ins); - ins->GetBlock()->InsertInstructionAfter(combine_inst, ins); - ins->ReplaceWith(combine_inst); - combine_inst->ReplaceInput(ins, 1); - } - } - } - } - - // Figure out all the instructions we need to - // fixup/replace/remove/duplicate. Since this requires an iteration of an - // intrusive linked list we want to do it only once and collect all the data - // here. - void CollectReplacements( - ScopedArenaVector<InstructionUse<HInstruction>>& to_replace, - ScopedArenaVector<HInstruction*>& to_remove, - ScopedArenaVector<InstructionUse<HConstructorFence>>& constructor_fences, - ScopedArenaVector<InstructionUse<HInstruction>>& to_predicate) { - size_t size = new_instance_->GetUses().SizeSlow(); - to_replace.reserve(size); - to_remove.reserve(size); - constructor_fences.reserve(size); - to_predicate.reserve(size); - for (auto& use : new_instance_->GetUses()) { - HBasicBlock* blk = - helper_->FindDominatingNonMaterializationBlock(use.GetUser()->GetBlock()); - if (InEscapeCohort(blk)) { - LSE_VLOG << "Replacing " << *new_instance_ << " use in " << *use.GetUser() << " with " - << *GetMaterialization(blk); - to_replace.push_back({use.GetUser(), use.GetIndex()}); - } else if (IsPostEscape(blk)) { - LSE_VLOG << "User " << *use.GetUser() << " after escapes!"; - // The fields + cmp are normal uses. Phi can only be here if it was - // generated by full LSE so whatever store+load that created the phi - // is the escape. - if (use.GetUser()->IsPhi()) { - to_replace.push_back({use.GetUser(), use.GetIndex()}); - } else { - DCHECK(use.GetUser()->IsFieldAccess() || - use.GetUser()->IsEqual() || - use.GetUser()->IsNotEqual()) - << *use.GetUser() << "@" << use.GetIndex(); - to_predicate.push_back({use.GetUser(), use.GetIndex()}); - } - } else if (use.GetUser()->IsConstructorFence()) { - LSE_VLOG << "User " << *use.GetUser() << " being moved to materialization!"; - constructor_fences.push_back({use.GetUser()->AsConstructorFence(), use.GetIndex()}); - } else { - LSE_VLOG << "User " << *use.GetUser() << " not contained in cohort!"; - to_remove.push_back(use.GetUser()); - } - } - DCHECK_EQ( - to_replace.size() + to_remove.size() + constructor_fences.size() + to_predicate.size(), - size); - } - - void GenerateMaterializationValueFromPredecessorsDirect( - HBasicBlock* blk, const ScopedArenaVector<HInstruction*>& pred_vals) { - DCHECK(!pred_vals.empty()); - bool all_equal = std::all_of(pred_vals.begin() + 1, pred_vals.end(), [&](HInstruction* val) { - return val == pred_vals.front(); - }); - if (LIKELY(all_equal)) { - AddMaterialization(blk, pred_vals.front()); - } else { - // Make a PHI for the predecessors. - HPhi* phi = new (GetGraph()->GetAllocator()) HPhi( - GetGraph()->GetAllocator(), kNoRegNumber, pred_vals.size(), DataType::Type::kReference); - for (const auto& [ins, off] : ZipCount(MakeIterationRange(pred_vals))) { - phi->SetRawInputAt(off, ins); - } - blk->AddPhi(phi); - AddMaterialization(blk, phi); - } - } - - HGraph* GetGraph() const { - return helper_->GetGraph(); - } - - HNewInstance* new_instance_; - PartialLoadStoreEliminationHelper* helper_; - ArenaBitVector heap_locs_; - ScopedArenaVector<HInstruction*> materializations_; - const HeapLocationCollector& collector_; - const ExecutionSubgraph* subgraph_; - }; - - ArrayRef<HeapReferenceData> GetHeapRefs() { - return ArrayRef<HeapReferenceData>(heap_refs_); - } - - bool IsMaterializationBlock(HBasicBlock* blk) const { - return blk->GetBlockId() >= first_materialization_block_id_; - } - - HBasicBlock* GetOrCreateMaterializationBlock(HBasicBlock* entry, size_t pred_num) { - size_t idx = GetMaterializationBlockIndex(entry, pred_num); - HBasicBlock* blk = materialization_blocks_[idx]; - if (blk == nullptr) { - blk = new (GetGraph()->GetAllocator()) HBasicBlock(GetGraph()); - GetGraph()->AddBlock(blk); - LSE_VLOG << "creating materialization block " << blk->GetBlockId() << " on edge " - << entry->GetPredecessors()[pred_num]->GetBlockId() << "->" << entry->GetBlockId(); - blk->AddInstruction(new (GetGraph()->GetAllocator()) HGoto()); - materialization_blocks_[idx] = blk; - } - return blk; - } - - HBasicBlock* GetMaterializationBlock(HBasicBlock* entry, size_t pred_num) { - HBasicBlock* out = materialization_blocks_[GetMaterializationBlockIndex(entry, pred_num)]; - DCHECK(out != nullptr) << "No materialization block for edge " << entry->GetBlockId() << "->" - << entry->GetPredecessors()[pred_num]->GetBlockId(); - return out; - } - - IterationRange<ArenaVector<HBasicBlock*>::const_iterator> IterateMaterializationBlocks() { - return MakeIterationRange(GetGraph()->GetBlocks().begin() + first_materialization_block_id_, - GetGraph()->GetBlocks().end()); - } - - void FixupPartialObjectUsers() { - for (PartialLoadStoreEliminationHelper::HeapReferenceData& ref_data : GetHeapRefs()) { - // Use the materialized instances to replace original instance - ref_data.FixupUses(/*first_pass=*/true); - CHECK(ref_data.OriginalNewInstance()->GetUses().empty()) - << ref_data.OriginalNewInstance()->GetUses() << ", " - << ref_data.OriginalNewInstance()->GetEnvUses(); - } - // This can cause new uses to be created due to the creation of phis/pred-get defaults - for (PartialLoadStoreEliminationHelper::HeapReferenceData& ref_data : GetHeapRefs()) { - // Only need to handle new phis/pred-get defaults. DCHECK that's all we find. - ref_data.FixupUses(/*first_pass=*/false); - CHECK(ref_data.OriginalNewInstance()->GetUses().empty()) - << ref_data.OriginalNewInstance()->GetUses() << ", " - << ref_data.OriginalNewInstance()->GetEnvUses(); - } - } - - // Finds the first block which either is or dominates the given block which is - // not a materialization block - HBasicBlock* FindDominatingNonMaterializationBlock(HBasicBlock* blk) { - if (LIKELY(!IsMaterializationBlock(blk))) { - // Not a materialization block so itself. - return blk; - } else if (blk->GetNumberOfPredecessors() != 0) { - // We're far enough along that the materialization blocks have been - // inserted into the graph so no need to go searching. - return blk->GetSinglePredecessor(); - } - // Search through the materialization blocks to find where it will be - // inserted. - for (auto [mat, idx] : ZipCount(MakeIterationRange(materialization_blocks_))) { - if (mat == blk) { - size_t cur_pred_idx = idx % max_preds_per_block_; - HBasicBlock* entry = GetGraph()->GetBlocks()[idx / max_preds_per_block_]; - return entry->GetPredecessors()[cur_pred_idx]; - } - } - LOG(FATAL) << "Unable to find materialization block position for " << blk->GetBlockId() << "!"; - return nullptr; - } - - void InsertMaterializationBlocks() { - for (auto [mat, idx] : ZipCount(MakeIterationRange(materialization_blocks_))) { - if (mat == nullptr) { - continue; - } - size_t cur_pred_idx = idx % max_preds_per_block_; - HBasicBlock* entry = GetGraph()->GetBlocks()[idx / max_preds_per_block_]; - HBasicBlock* pred = entry->GetPredecessors()[cur_pred_idx]; - mat->InsertBetween(pred, entry); - LSE_VLOG << "Adding materialization block " << mat->GetBlockId() << " on edge " - << pred->GetBlockId() << "->" << entry->GetBlockId(); - } - } - - // Replace any env-uses remaining of the partial singletons with the - // appropriate phis and remove the instructions. - void RemoveReplacedInstructions() { - for (HeapReferenceData& ref_data : GetHeapRefs()) { - CHECK(ref_data.OriginalNewInstance()->GetUses().empty()) - << ref_data.OriginalNewInstance()->GetUses() << ", " - << ref_data.OriginalNewInstance()->GetEnvUses() - << " inst is: " << ref_data.OriginalNewInstance(); - const auto& env_uses = ref_data.OriginalNewInstance()->GetEnvUses(); - while (!env_uses.empty()) { - const HUseListNode<HEnvironment*>& use = env_uses.front(); - HInstruction* merged_inst = - ref_data.GetMaterialization(use.GetUser()->GetHolder()->GetBlock()); - LSE_VLOG << "Replacing env use of " << *use.GetUser()->GetHolder() << "@" << use.GetIndex() - << " with " << *merged_inst; - use.GetUser()->ReplaceInput(merged_inst, use.GetIndex()); - } - ref_data.OriginalNewInstance()->GetBlock()->RemoveInstruction(ref_data.OriginalNewInstance()); - } - } - - // We need to make sure any allocations dominate their environment uses. - // Technically we could probably remove the env-uses and be fine but this is easy. - void ReorderMaterializationsForEnvDominance() { - for (HBasicBlock* blk : IterateMaterializationBlocks()) { - ScopedArenaAllocator alloc(alloc_->GetArenaStack()); - ArenaBitVector still_unsorted( - &alloc, GetGraph()->GetCurrentInstructionId(), false, kArenaAllocLSE); - // This is guaranteed to be very short (since we will abandon LSE if there - // are >= kMaxNumberOfHeapLocations (32) heap locations so that is the - // absolute maximum size this list can be) so doing a selection sort is - // fine. This avoids the need to do a complicated recursive check to - // ensure transitivity for std::sort. - ScopedArenaVector<HNewInstance*> materializations(alloc.Adapter(kArenaAllocLSE)); - materializations.reserve(GetHeapRefs().size()); - for (HInstruction* ins : - MakeSTLInstructionIteratorRange(HInstructionIterator(blk->GetInstructions()))) { - if (ins->IsNewInstance()) { - materializations.push_back(ins->AsNewInstance()); - still_unsorted.SetBit(ins->GetId()); - } - } - using Iter = ScopedArenaVector<HNewInstance*>::iterator; - Iter unsorted_start = materializations.begin(); - Iter unsorted_end = materializations.end(); - // selection sort. Required since the only check we can easily perform a - // is-before-all-unsorted check. - while (unsorted_start != unsorted_end) { - bool found_instruction = false; - for (Iter candidate = unsorted_start; candidate != unsorted_end; ++candidate) { - HNewInstance* ni = *candidate; - if (std::none_of(ni->GetAllEnvironments().cbegin(), - ni->GetAllEnvironments().cend(), - [&](const HEnvironment* env) { - return std::any_of( - env->GetEnvInputs().cbegin(), - env->GetEnvInputs().cend(), - [&](const HInstruction* env_element) { - return env_element != nullptr && - still_unsorted.IsBitSet(env_element->GetId()); - }); - })) { - still_unsorted.ClearBit(ni->GetId()); - std::swap(*unsorted_start, *candidate); - ++unsorted_start; - found_instruction = true; - break; - } - } - CHECK(found_instruction) << "Unable to select next materialization instruction." - << " Environments have a dependency loop!"; - } - // Reverse so we as we prepend them we end up with the correct order. - auto reverse_iter = MakeIterationRange(materializations.rbegin(), materializations.rend()); - for (HNewInstance* ins : reverse_iter) { - if (blk->GetFirstInstruction() != ins) { - // Don't do checks since that makes sure the move is safe WRT - // ins->CanBeMoved which for NewInstance is false. - ins->MoveBefore(blk->GetFirstInstruction(), /*do_checks=*/false); - } - } - } - } - - private: - void CollectInterestingHeapRefs() { - // Get all the partials we need to move around. - for (size_t i = 0; i < lse_->heap_location_collector_.GetNumberOfHeapLocations(); ++i) { - ReferenceInfo* ri = lse_->heap_location_collector_.GetHeapLocation(i)->GetReferenceInfo(); - if (ri->IsPartialSingleton() && - ri->GetReference()->GetBlock() != nullptr && - ri->GetNoEscapeSubgraph()->ContainsBlock(ri->GetReference()->GetBlock())) { - RecordHeapRefField(ri->GetReference()->AsNewInstance(), i); - } - } - } - - void RecordHeapRefField(HNewInstance* ni, size_t loc) { - DCHECK(ni != nullptr); - // This is likely to be very short so just do a linear search. - auto it = std::find_if(heap_refs_.begin(), heap_refs_.end(), [&](HeapReferenceData& data) { - return data.OriginalNewInstance() == ni; - }); - HeapReferenceData& cur_ref = - (it == heap_refs_.end()) - ? heap_refs_.emplace_back(this, - ni, - lse_->heap_location_collector_.GetHeapLocation(loc) - ->GetReferenceInfo() - ->GetNoEscapeSubgraph(), - alloc_) - : *it; - cur_ref.AddHeapLocation(loc); - } - - - void NotifyNewMaterialization(HInstruction* ins) { - if (ins->IsPhi()) { - new_ref_phis_.push_back(ins->AsPhi()); - } - } - - size_t GetMaterializationBlockIndex(HBasicBlock* blk, size_t pred_num) const { - DCHECK_LT(blk->GetBlockId(), first_materialization_block_id_) - << "block is a materialization block!"; - DCHECK_LT(pred_num, max_preds_per_block_); - return blk->GetBlockId() * max_preds_per_block_ + pred_num; - } - - HGraph* GetGraph() const { - return lse_->GetGraph(); - } - - LSEVisitor* lse_; - ScopedArenaAllocator* alloc_; - ScopedArenaVector<HInstruction*> new_ref_phis_; - ScopedArenaVector<HeapReferenceData> heap_refs_; - size_t max_preds_per_block_; - // An array of (# of non-materialization blocks) * max_preds_per_block - // arranged in block-id major order. Since we can only have at most one - // materialization block on each edge this is the maximum possible number of - // materialization blocks. - ScopedArenaVector<HBasicBlock*> materialization_blocks_; - size_t first_materialization_block_id_; - - friend void LSEVisitor::MovePartialEscapes(); - friend class HeapReferenceData; -}; - -// Work around c++ type checking annoyances with not being able to forward-declare inner types. -class HeapRefHolder - : public std::reference_wrapper<PartialLoadStoreEliminationHelper::HeapReferenceData> {}; - -HInstruction* LSEVisitor::SetupPartialMaterialization(PartialLoadStoreEliminationHelper& helper, - HeapRefHolder&& holder, - size_t pred_idx, - HBasicBlock* entry) { - PartialLoadStoreEliminationHelper::HeapReferenceData& ref_data = holder.get(); - HBasicBlock* old_pred = entry->GetPredecessors()[pred_idx]; - HInstruction* new_inst = ref_data.OriginalNewInstance(); - if (UNLIKELY(!new_inst->GetBlock()->Dominates(entry))) { - LSE_VLOG << "Initial materialization in non-dominating block " << entry->GetBlockId() - << " is null!"; - return GetGraph()->GetNullConstant(); - } - HBasicBlock* bb = helper.GetOrCreateMaterializationBlock(entry, pred_idx); - CHECK(bb != nullptr) << "entry " << entry->GetBlockId() << " -> " << old_pred->GetBlockId(); - HNewInstance* repl_create = new_inst->Clone(GetGraph()->GetAllocator())->AsNewInstance(); - repl_create->SetPartialMaterialization(); - bb->InsertInstructionBefore(repl_create, bb->GetLastInstruction()); - repl_create->CopyEnvironmentFrom(new_inst->GetEnvironment()); - MaybeRecordStat(stats_, MethodCompilationStat::kPartialAllocationMoved); - LSE_VLOG << "In blk " << bb->GetBlockId() << " initial materialization is " << *repl_create; - ref_data.AddMaterialization(bb, repl_create); - const FieldInfo* info = nullptr; - for (const HeapLocation* loc : ref_data.IterateLocations()) { - size_t loc_off = heap_location_collector_.GetHeapLocationIndex(loc); - info = field_infos_[loc_off]; - DCHECK(loc->GetIndex() == nullptr); - Value value = ReplacementOrValue(heap_values_for_[old_pred->GetBlockId()][loc_off].value); - if (value.NeedsLoopPhi()) { - Value repl = phi_placeholder_replacements_[PhiPlaceholderIndex(value.GetPhiPlaceholder())]; - DCHECK(!repl.IsUnknown()); - DCHECK(repl.IsDefault() || repl.IsInvalid() || repl.IsInstruction()) - << repl << " from " << value << " pred is " << old_pred->GetBlockId(); - if (!repl.IsInvalid()) { - value = repl; - } else { - FullyMaterializePhi(value.GetPhiPlaceholder(), info->GetFieldType()); - value = phi_placeholder_replacements_[PhiPlaceholderIndex(value.GetPhiPlaceholder())]; - } - } else if (value.NeedsNonLoopPhi()) { - Value repl = phi_placeholder_replacements_[PhiPlaceholderIndex(value.GetPhiPlaceholder())]; - DCHECK(repl.IsDefault() || repl.IsInvalid() || repl.IsInstruction()) - << repl << " from " << value << " pred is " << old_pred->GetBlockId(); - if (!repl.IsInvalid()) { - value = repl; - } else { - MaterializeNonLoopPhis(value.GetPhiPlaceholder(), info->GetFieldType()); - value = phi_placeholder_replacements_[PhiPlaceholderIndex(value.GetPhiPlaceholder())]; - } - } - DCHECK(value.IsDefault() || value.IsInstruction()) - << GetGraph()->PrettyMethod() << ": " << value; - - if (!value.IsDefault() && - // shadow$_klass_ doesn't need to be manually initialized. - MemberOffset(loc->GetOffset()) != mirror::Object::ClassOffset()) { - CHECK(info != nullptr); - HInstruction* set_value = - new (GetGraph()->GetAllocator()) HInstanceFieldSet(repl_create, - value.GetInstruction(), - field_infos_[loc_off]->GetField(), - loc->GetType(), - MemberOffset(loc->GetOffset()), - false, - field_infos_[loc_off]->GetFieldIndex(), - loc->GetDeclaringClassDefIndex(), - field_infos_[loc_off]->GetDexFile(), - 0u); - bb->InsertInstructionAfter(set_value, repl_create); - LSE_VLOG << "Adding " << *set_value << " for materialization setup!"; - } - } - return repl_create; -} - -HInstruction* LSEVisitor::GetPartialValueAt(HNewInstance* orig_new_inst, HInstruction* read) { - size_t loc = heap_location_collector_.GetFieldHeapLocation(orig_new_inst, &read->GetFieldInfo()); - Value pred = ReplacementOrValue(intermediate_values_.find(read)->second); - LSE_VLOG << "using " << pred << " as default value for " << *read; - if (pred.IsInstruction()) { - return pred.GetInstruction(); - } else if (pred.IsMergedUnknown() || pred.NeedsPhi()) { - FullyMaterializePhi(pred.GetPhiPlaceholder(), - heap_location_collector_.GetHeapLocation(loc)->GetType()); - HInstruction* res = Replacement(pred).GetInstruction(); - LSE_VLOG << pred << " materialized to " << res->DumpWithArgs(); - return res; - } - LOG(FATAL) << "Unable to find unescaped value at " << read->DumpWithArgs() - << "! This should be impossible!"; - UNREACHABLE(); -} - -void LSEVisitor::MovePartialEscapes() { - if (!ShouldPerformPartialLSE()) { - return; - } - - ScopedArenaAllocator saa(allocator_.GetArenaStack()); - PartialLoadStoreEliminationHelper helper(this, &saa); - - // Since for PHIs we now will have more information (since we know the object - // hasn't escaped) we need to clear the old phi-replacements where we weren't - // able to find the value. - PrepareForPartialPhiComputation(); - - for (PartialLoadStoreEliminationHelper::HeapReferenceData& ref_data : helper.GetHeapRefs()) { - LSE_VLOG << "Creating materializations for " << *ref_data.OriginalNewInstance(); - // Setup entry and exit blocks. - for (const auto& excluded_cohort : ref_data.GetNoEscapeSubgraph()->GetExcludedCohorts()) { - // Setup materialization blocks. - for (HBasicBlock* entry : excluded_cohort.EntryBlocksReversePostOrder()) { - // Setup entries. - // TODO Assuming we correctly break critical edges every entry block - // must have only a single predecessor so we could just put all this - // stuff in there. OTOH simplifier can do it for us and this is simpler - // to implement - giving clean separation between the original graph and - // materialization blocks - so for now we might as well have these new - // blocks. - ScopedArenaAllocator pred_alloc(saa.GetArenaStack()); - ScopedArenaVector<HInstruction*> pred_vals(pred_alloc.Adapter(kArenaAllocLSE)); - pred_vals.reserve(entry->GetNumberOfPredecessors()); - for (const auto& [pred, pred_idx] : - ZipCount(MakeIterationRange(entry->GetPredecessors()))) { - DCHECK(!helper.IsMaterializationBlock(pred)); - if (excluded_cohort.IsEntryBlock(pred)) { - pred_vals.push_back(ref_data.GetMaterialization(pred)); - continue; - } else { - pred_vals.push_back(SetupPartialMaterialization(helper, {ref_data}, pred_idx, entry)); - } - } - ref_data.GenerateMaterializationValueFromPredecessorsForEntry(entry, pred_vals); - } - - // Setup exit block heap-values for later phi-generation. - for (HBasicBlock* exit : excluded_cohort.ExitBlocks()) { - // mark every exit of cohorts as having a value so we can easily - // materialize the PHIs. - // TODO By setting this we can easily use the normal MaterializeLoopPhis - // (via FullyMaterializePhis) in order to generate the default-values - // for predicated-gets. This has the unfortunate side effect of creating - // somewhat more phis than are really needed (in some cases). We really - // should try to eventually know that we can lower these PHIs to only - // the non-escaping value in cases where it is possible. Currently this - // is done to some extent in instruction_simplifier but we have more - // information here to do the right thing. - for (const HeapLocation* loc : ref_data.IterateLocations()) { - size_t loc_off = heap_location_collector_.GetHeapLocationIndex(loc); - // This Value::Default() is only used to fill in PHIs used as the - // default value for PredicatedInstanceFieldGets. The actual value - // stored there is meaningless since the Predicated-iget will use the - // actual field value instead on these paths. - heap_values_for_[exit->GetBlockId()][loc_off].value = Value::Default(); - } - } - } - - // string materialization through the graph. - // // Visit RPO to PHI the materialized object through the cohort. - for (HBasicBlock* blk : GetGraph()->GetReversePostOrder()) { - // NB This doesn't include materialization blocks. - DCHECK(!helper.IsMaterializationBlock(blk)) - << "Materialization blocks should not be in RPO yet."; - if (ref_data.HasMaterialization(blk)) { - continue; - } else if (ref_data.BeforeAllEscapes(blk)) { - ref_data.AddMaterialization(blk, GetGraph()->GetNullConstant()); - continue; - } else { - ref_data.GenerateMaterializationValueFromPredecessors(blk); - } - } - } - - // Once we've generated all the materializations we can update the users. - helper.FixupPartialObjectUsers(); - - // Actually put materialization blocks into the graph - helper.InsertMaterializationBlocks(); - - // Get rid of the original instructions. - helper.RemoveReplacedInstructions(); - - // Ensure everything is ordered correctly in the materialization blocks. This - // involves moving every NewInstance to the top and ordering them so that any - // required env-uses are correctly ordered. - helper.ReorderMaterializationsForEnvDominance(); -} - -void LSEVisitor::FinishFullLSE() { // Remove recorded load instructions that should be eliminated. for (const LoadStoreRecord& record : loads_and_stores_) { size_t id = dchecked_integral_cast<size_t>(record.load_or_store->GetId()); @@ -3758,7 +2505,7 @@ void LSEVisitor::FinishFullLSE() { } } -bool LoadStoreElimination::Run(bool enable_partial_lse) { +bool LoadStoreElimination::Run() { if (graph_->IsDebuggable() || graph_->HasTryCatch()) { // Debugger may set heap values or trigger deoptimization of callers. // Try/catch support not implemented yet. @@ -3772,11 +2519,7 @@ bool LoadStoreElimination::Run(bool enable_partial_lse) { // O(1) though. graph_->ComputeReachabilityInformation(); ScopedArenaAllocator allocator(graph_->GetArenaStack()); - LoadStoreAnalysis lsa(graph_, - stats_, - &allocator, - enable_partial_lse ? LoadStoreAnalysisType::kFull - : LoadStoreAnalysisType::kNoPredicatedInstructions); + LoadStoreAnalysis lsa(graph_, stats_, &allocator, /*for_elimination=*/true); lsa.Run(); const HeapLocationCollector& heap_location_collector = lsa.GetHeapLocationCollector(); if (heap_location_collector.GetNumberOfHeapLocations() == 0) { @@ -3784,11 +2527,9 @@ bool LoadStoreElimination::Run(bool enable_partial_lse) { return false; } - LSEVisitor lse_visitor(graph_, heap_location_collector, enable_partial_lse, stats_); + LSEVisitor lse_visitor(graph_, heap_location_collector, stats_); lse_visitor.Run(); return true; } -#undef LSE_VLOG - } // namespace art diff --git a/compiler/optimizing/load_store_elimination.h b/compiler/optimizing/load_store_elimination.h index e73ef5ef34..60c547cb8b 100644 --- a/compiler/optimizing/load_store_elimination.h +++ b/compiler/optimizing/load_store_elimination.h @@ -25,24 +25,12 @@ class SideEffectsAnalysis; class LoadStoreElimination : public HOptimization { public: - // Whether or not we should attempt partial Load-store-elimination which - // requires additional blocks and predicated instructions. - static constexpr bool kEnablePartialLSE = true; - - // Controls whether to enable VLOG(compiler) logs explaining the transforms taking place. - static constexpr bool kVerboseLoggingMode = false; - LoadStoreElimination(HGraph* graph, OptimizingCompilerStats* stats, const char* name = kLoadStoreEliminationPassName) : HOptimization(graph, name, stats) {} - bool Run() override { - return Run(kEnablePartialLSE); - } - - // Exposed for testing. - bool Run(bool enable_partial_lse); + bool Run() override; static constexpr const char* kLoadStoreEliminationPassName = "load_store_elimination"; diff --git a/compiler/optimizing/load_store_elimination_test.cc b/compiler/optimizing/load_store_elimination_test.cc index 9b000a1b1a..9904192501 100644 --- a/compiler/optimizing/load_store_elimination_test.cc +++ b/compiler/optimizing/load_store_elimination_test.cc @@ -14,99 +14,38 @@ * limitations under the License. */ -#include "load_store_elimination.h" - -#include <initializer_list> -#include <memory> #include <tuple> -#include <variant> -#include "base/iteration_range.h" #include "compilation_kind.h" -#include "dex/dex_file_types.h" -#include "entrypoints/quick/quick_entrypoints.h" #include "entrypoints/quick/quick_entrypoints_enum.h" #include "gtest/gtest.h" #include "handle_scope.h" #include "load_store_analysis.h" +#include "load_store_elimination.h" #include "nodes.h" -#include "optimizing/data_type.h" -#include "optimizing/instruction_simplifier.h" -#include "optimizing/optimizing_compiler_stats.h" #include "optimizing_unit_test.h" -#include "scoped_thread_state_change.h" - -namespace art { - -struct InstructionDumper { - public: - HInstruction* ins_; -}; -bool operator==(const InstructionDumper& a, const InstructionDumper& b) { - return a.ins_ == b.ins_; -} -bool operator!=(const InstructionDumper& a, const InstructionDumper& b) { - return !(a == b); -} +#include "gtest/gtest.h" -std::ostream& operator<<(std::ostream& os, const InstructionDumper& id) { - if (id.ins_ == nullptr) { - return os << "NULL"; - } else { - return os << "(" << id.ins_ << "): " << id.ins_->DumpWithArgs(); - } -} +namespace art { -#define CHECK_SUBROUTINE_FAILURE() \ - do { \ - if (HasFatalFailure()) { \ - return; \ - } \ - } while (false) - -#define EXPECT_INS_EQ(a, b) EXPECT_EQ(InstructionDumper{a}, InstructionDumper{b}) -#define EXPECT_INS_REMOVED(a) EXPECT_TRUE(IsRemoved(a)) << "Not removed: " << (InstructionDumper{a}) -#define EXPECT_INS_RETAINED(a) EXPECT_FALSE(IsRemoved(a)) << "Removed: " << (InstructionDumper{a}) -#define ASSERT_INS_EQ(a, b) ASSERT_EQ(InstructionDumper{a}, InstructionDumper{b}) -#define ASSERT_INS_REMOVED(a) ASSERT_TRUE(IsRemoved(a)) << "Not removed: " << (InstructionDumper{a}) -#define ASSERT_INS_RETAINED(a) ASSERT_FALSE(IsRemoved(a)) << "Removed: " << (InstructionDumper{a}) - -template <typename SuperTest> -class LoadStoreEliminationTestBase : public SuperTest, public OptimizingUnitTestHelper { +class LoadStoreEliminationTest : public OptimizingUnitTest { public: - void SetUp() override { - SuperTest::SetUp(); - gLogVerbosity.compiler = true; - } - - void TearDown() override { - SuperTest::TearDown(); - gLogVerbosity.compiler = false; - } - - AdjacencyListGraph SetupFromAdjacencyList(const std::string_view entry_name, - const std::string_view exit_name, - const std::vector<AdjacencyListGraph::Edge>& adj) { + AdjacencyListGraph SetupFromAdjacencyList( + const std::string_view entry_name, + const std::string_view exit_name, + const std::vector<AdjacencyListGraph::Edge>& adj) { return AdjacencyListGraph(graph_, GetAllocator(), entry_name, exit_name, adj); } - void PerformLSE(bool with_partial = true) { + void PerformLSE() { graph_->BuildDominatorTree(); - LoadStoreElimination lse(graph_, /*stats=*/nullptr); - lse.Run(with_partial); + LoadStoreElimination lse(graph_, /*stats=*/ nullptr); + lse.Run(); std::ostringstream oss; EXPECT_TRUE(CheckGraphSkipRefTypeInfoChecks(oss)) << oss.str(); } - void PerformLSEWithPartial() { - PerformLSE(true); - } - - void PerformLSENoPartial() { - PerformLSE(false); - } - // Create instructions shared among tests. void CreateEntryBlockInstructions() { HInstruction* c1 = graph_->GetIntConstant(1); @@ -169,7 +108,9 @@ class LoadStoreEliminationTestBase : public SuperTest, public OptimizingUnitTest } void CreateEnvForSuspendCheck() { - ManuallyBuildEnvFor(suspend_check_, {array_, i_, j_}); + ArenaVector<HInstruction*> current_locals({array_, i_, j_}, + GetAllocator()->Adapter(kArenaAllocInstruction)); + ManuallyBuildEnvFor(suspend_check_, ¤t_locals); } // Create the diamond-shaped CFG: @@ -212,15 +153,15 @@ class LoadStoreEliminationTestBase : public SuperTest, public OptimizingUnitTest DCHECK(block != nullptr); DCHECK(array != nullptr); DCHECK(index != nullptr); - HInstruction* vload = - new (GetAllocator()) HVecLoad(GetAllocator(), - array, - index, - DataType::Type::kInt32, - SideEffects::ArrayReadOfType(DataType::Type::kInt32), - 4, - /*is_string_char_at*/ false, - kNoDexPc); + HInstruction* vload = new (GetAllocator()) HVecLoad( + GetAllocator(), + array, + index, + DataType::Type::kInt32, + SideEffects::ArrayReadOfType(DataType::Type::kInt32), + 4, + /*is_string_char_at*/ false, + kNoDexPc); block->InsertInstructionBefore(vload, block->GetLastInstruction()); return vload; } @@ -238,19 +179,22 @@ class LoadStoreEliminationTestBase : public SuperTest, public OptimizingUnitTest DCHECK(index != nullptr); if (vdata == nullptr) { HInstruction* c1 = graph_->GetIntConstant(1); - vdata = new (GetAllocator()) - HVecReplicateScalar(GetAllocator(), c1, DataType::Type::kInt32, 4, kNoDexPc); + vdata = new (GetAllocator()) HVecReplicateScalar(GetAllocator(), + c1, + DataType::Type::kInt32, + 4, + kNoDexPc); block->InsertInstructionBefore(vdata, block->GetLastInstruction()); } - HInstruction* vstore = - new (GetAllocator()) HVecStore(GetAllocator(), - array, - index, - vdata, - DataType::Type::kInt32, - SideEffects::ArrayWriteOfType(DataType::Type::kInt32), - 4, - kNoDexPc); + HInstruction* vstore = new (GetAllocator()) HVecStore( + GetAllocator(), + array, + index, + vdata, + DataType::Type::kInt32, + SideEffects::ArrayWriteOfType(DataType::Type::kInt32), + 4, + kNoDexPc); block->InsertInstructionBefore(vstore, block->GetLastInstruction()); return vstore; } @@ -281,153 +225,34 @@ class LoadStoreEliminationTestBase : public SuperTest, public OptimizingUnitTest if (data == nullptr) { data = graph_->GetIntConstant(1); } - HInstruction* store = - new (GetAllocator()) HArraySet(array, index, data, DataType::Type::kInt32, 0); + HInstruction* store = new (GetAllocator()) HArraySet(array, + index, + data, + DataType::Type::kInt32, + 0); block->InsertInstructionBefore(store, block->GetLastInstruction()); return store; } void InitGraphAndParameters() { InitGraph(); - AddParameter(new (GetAllocator()) HParameterValue( - graph_->GetDexFile(), dex::TypeIndex(0), 0, DataType::Type::kInt32)); + AddParameter(new (GetAllocator()) HParameterValue(graph_->GetDexFile(), + dex::TypeIndex(0), + 0, + DataType::Type::kInt32)); array_ = parameters_.back(); - AddParameter(new (GetAllocator()) HParameterValue( - graph_->GetDexFile(), dex::TypeIndex(1), 1, DataType::Type::kInt32)); + AddParameter(new (GetAllocator()) HParameterValue(graph_->GetDexFile(), + dex::TypeIndex(1), + 1, + DataType::Type::kInt32)); i_ = parameters_.back(); - AddParameter(new (GetAllocator()) HParameterValue( - graph_->GetDexFile(), dex::TypeIndex(1), 2, DataType::Type::kInt32)); + AddParameter(new (GetAllocator()) HParameterValue(graph_->GetDexFile(), + dex::TypeIndex(1), + 2, + DataType::Type::kInt32)); j_ = parameters_.back(); } - void ManuallyBuildEnvFor(HInstruction* ins, const std::initializer_list<HInstruction*>& env) { - ArenaVector<HInstruction*> current_locals(env, GetAllocator()->Adapter(kArenaAllocInstruction)); - OptimizingUnitTestHelper::ManuallyBuildEnvFor(ins, ¤t_locals); - } - - HLoadClass* MakeClassLoad(std::optional<dex::TypeIndex> ti = std::nullopt) { - return new (GetAllocator()) HLoadClass(graph_->GetCurrentMethod(), - ti ? *ti : dex::TypeIndex(class_idx_++), - graph_->GetDexFile(), - /* klass= */ null_klass_, - /* is_referrers_class= */ false, - /* dex_pc= */ 0, - /* needs_access_check= */ false); - } - - HNewInstance* MakeNewInstance(HInstruction* cls, uint32_t dex_pc = 0u) { - EXPECT_TRUE(cls->IsLoadClass() || cls->IsClinitCheck()) << *cls; - HLoadClass* load = - cls->IsLoadClass() ? cls->AsLoadClass() : cls->AsClinitCheck()->GetLoadClass(); - return new (GetAllocator()) HNewInstance(cls, - dex_pc, - load->GetTypeIndex(), - graph_->GetDexFile(), - /* finalizable= */ false, - QuickEntrypointEnum::kQuickAllocObjectInitialized); - } - - HInstanceFieldSet* MakeIFieldSet(HInstruction* inst, - HInstruction* data, - MemberOffset off, - uint32_t dex_pc = 0u) { - return new (GetAllocator()) HInstanceFieldSet(inst, - data, - /* field= */ nullptr, - /* field_type= */ data->GetType(), - /* field_offset= */ off, - /* is_volatile= */ false, - /* field_idx= */ 0, - /* declaring_class_def_index= */ 0, - graph_->GetDexFile(), - dex_pc); - } - - HInstanceFieldGet* MakeIFieldGet(HInstruction* inst, - DataType::Type type, - MemberOffset off, - uint32_t dex_pc = 0u) { - return new (GetAllocator()) HInstanceFieldGet(inst, - /* field= */ nullptr, - /* field_type= */ type, - /* field_offset= */ off, - /* is_volatile= */ false, - /* field_idx= */ 0, - /* declaring_class_def_index= */ 0, - graph_->GetDexFile(), - dex_pc); - } - - HInvokeStaticOrDirect* MakeInvoke(DataType::Type return_type, - const std::vector<HInstruction*>& args) { - MethodReference method_reference{/* file= */ &graph_->GetDexFile(), /* index= */ method_idx_++}; - HInvokeStaticOrDirect* res = new (GetAllocator()) - HInvokeStaticOrDirect(GetAllocator(), - args.size(), - return_type, - /* dex_pc= */ 0, - method_reference, - /* resolved_method= */ nullptr, - HInvokeStaticOrDirect::DispatchInfo{}, - InvokeType::kStatic, - /* resolved_method_reference= */ method_reference, - HInvokeStaticOrDirect::ClinitCheckRequirement::kNone); - for (auto [ins, idx] : ZipCount(MakeIterationRange(args))) { - res->SetRawInputAt(idx, ins); - } - return res; - } - - HPhi* MakePhi(const std::vector<HInstruction*>& ins) { - EXPECT_GE(ins.size(), 2u) << "Phi requires at least 2 inputs"; - HPhi* phi = - new (GetAllocator()) HPhi(GetAllocator(), kNoRegNumber, ins.size(), ins[0]->GetType()); - for (auto [i, idx] : ZipCount(MakeIterationRange(ins))) { - phi->SetRawInputAt(idx, i); - } - return phi; - } - - void SetupExit(HBasicBlock* exit) { - exit->AddInstruction(new (GetAllocator()) HExit()); - } - - dex::TypeIndex DefaultTypeIndexForType(DataType::Type type) { - switch (type) { - case DataType::Type::kBool: - return dex::TypeIndex(1); - case DataType::Type::kUint8: - case DataType::Type::kInt8: - return dex::TypeIndex(2); - case DataType::Type::kUint16: - case DataType::Type::kInt16: - return dex::TypeIndex(3); - case DataType::Type::kUint32: - case DataType::Type::kInt32: - return dex::TypeIndex(4); - case DataType::Type::kUint64: - case DataType::Type::kInt64: - return dex::TypeIndex(5); - case DataType::Type::kReference: - return dex::TypeIndex(6); - case DataType::Type::kFloat32: - return dex::TypeIndex(7); - case DataType::Type::kFloat64: - return dex::TypeIndex(8); - case DataType::Type::kVoid: - EXPECT_TRUE(false) << "No type for void!"; - return dex::TypeIndex(1000); - } - } - - // Creates a parameter. The instruction is automatically added to the entry-block - HParameterValue* MakeParam(DataType::Type type, std::optional<dex::TypeIndex> ti = std::nullopt) { - HParameterValue* val = new (GetAllocator()) HParameterValue( - graph_->GetDexFile(), ti ? *ti : DefaultTypeIndexForType(type), param_count_++, type); - graph_->GetEntryBlock()->AddInstruction(val); - return val; - } - HBasicBlock* pre_header_; HBasicBlock* loop_; @@ -439,208 +264,6 @@ class LoadStoreEliminationTestBase : public SuperTest, public OptimizingUnitTest HInstruction* suspend_check_; HPhi* phi_; - - size_t param_count_ = 0; - size_t class_idx_ = 42; - uint32_t method_idx_ = 100; - - ScopedNullHandle<mirror::Class> null_klass_; -}; - -class LoadStoreEliminationTest : public LoadStoreEliminationTestBase<CommonCompilerTest> {}; - -enum class TestOrder { kSameAsAlloc, kReverseOfAlloc }; -std::ostream& operator<<(std::ostream& os, const TestOrder& ord) { - switch (ord) { - case TestOrder::kSameAsAlloc: - return os << "SameAsAlloc"; - case TestOrder::kReverseOfAlloc: - return os << "ReverseOfAlloc"; - } -} - -class OrderDependentTestGroup - : public LoadStoreEliminationTestBase<CommonCompilerTestWithParam<TestOrder>> {}; - -// Various configs we can use for testing. Currently used in PartialComparison tests. -struct PartialComparisonKind { - public: - enum class Type : uint8_t { kEquals, kNotEquals }; - enum class Target : uint8_t { kNull, kValue, kSelf }; - enum class Position : uint8_t { kLeft, kRight }; - - const Type type_; - const Target target_; - const Position position_; - - bool IsDefinitelyFalse() const { - return !IsPossiblyTrue(); - } - bool IsPossiblyFalse() const { - return !IsDefinitelyTrue(); - } - bool IsDefinitelyTrue() const { - if (target_ == Target::kSelf) { - return type_ == Type::kEquals; - } else if (target_ == Target::kNull) { - return type_ == Type::kNotEquals; - } else { - return false; - } - } - bool IsPossiblyTrue() const { - if (target_ == Target::kSelf) { - return type_ == Type::kEquals; - } else if (target_ == Target::kNull) { - return type_ == Type::kNotEquals; - } else { - return true; - } - } - std::ostream& Dump(std::ostream& os) const { - os << "PartialComparisonKind{" << (type_ == Type::kEquals ? "kEquals" : "kNotEquals") << ", " - << (target_ == Target::kNull ? "kNull" : (target_ == Target::kSelf ? "kSelf" : "kValue")) - << ", " << (position_ == Position::kLeft ? "kLeft" : "kRight") << "}"; - return os; - } -}; - -std::ostream& operator<<(std::ostream& os, const PartialComparisonKind& comp) { - return comp.Dump(os); -} - -class PartialComparisonTestGroup - : public LoadStoreEliminationTestBase<CommonCompilerTestWithParam<PartialComparisonKind>> { - public: - enum class ComparisonPlacement { - kBeforeEscape, - kInEscape, - kAfterEscape, - }; - void CheckFinalInstruction(HInstruction* ins, ComparisonPlacement placement) { - using Target = PartialComparisonKind::Target; - using Type = PartialComparisonKind::Type; - using Position = PartialComparisonKind::Position; - PartialComparisonKind kind = GetParam(); - if (ins->IsIntConstant()) { - if (kind.IsDefinitelyTrue()) { - EXPECT_TRUE(ins->AsIntConstant()->IsTrue()) << kind << " " << *ins; - } else if (kind.IsDefinitelyFalse()) { - EXPECT_TRUE(ins->AsIntConstant()->IsFalse()) << kind << " " << *ins; - } else { - EXPECT_EQ(placement, ComparisonPlacement::kBeforeEscape); - EXPECT_EQ(kind.target_, Target::kValue); - // We are before escape so value is not the object - if (kind.type_ == Type::kEquals) { - EXPECT_TRUE(ins->AsIntConstant()->IsFalse()) << kind << " " << *ins; - } else { - EXPECT_TRUE(ins->AsIntConstant()->IsTrue()) << kind << " " << *ins; - } - } - return; - } - EXPECT_NE(placement, ComparisonPlacement::kBeforeEscape) - << "For comparisons before escape we should always be able to transform into a constant." - << " Instead we got:" << std::endl << ins->DumpWithArgs(); - if (placement == ComparisonPlacement::kInEscape) { - // Should be the same type. - ASSERT_TRUE(ins->IsEqual() || ins->IsNotEqual()) << *ins; - HInstruction* other = kind.position_ == Position::kLeft ? ins->AsBinaryOperation()->GetRight() - : ins->AsBinaryOperation()->GetLeft(); - if (kind.target_ == Target::kSelf) { - EXPECT_INS_EQ(ins->AsBinaryOperation()->GetLeft(), ins->AsBinaryOperation()->GetRight()) - << " ins is: " << *ins; - } else if (kind.target_ == Target::kNull) { - EXPECT_INS_EQ(other, graph_->GetNullConstant()) << " ins is: " << *ins; - } else { - EXPECT_TRUE(other->IsStaticFieldGet()) << " ins is: " << *ins; - } - if (kind.type_ == Type::kEquals) { - EXPECT_TRUE(ins->IsEqual()) << *ins; - } else { - EXPECT_TRUE(ins->IsNotEqual()) << *ins; - } - } else { - ASSERT_EQ(placement, ComparisonPlacement::kAfterEscape); - if (kind.type_ == Type::kEquals) { - // obj == <anything> can only be true if (1) it's obj == obj or (2) obj has escaped. - ASSERT_TRUE(ins->IsAnd()) << ins->DumpWithArgs(); - EXPECT_TRUE(ins->InputAt(1)->IsEqual()) << ins->DumpWithArgs(); - } else { - // obj != <anything> is true if (2) obj has escaped. - ASSERT_TRUE(ins->IsOr()) << ins->DumpWithArgs(); - EXPECT_TRUE(ins->InputAt(1)->IsNotEqual()) << ins->DumpWithArgs(); - } - // Check the first part of AND is the obj-has-escaped - ASSERT_TRUE(ins->InputAt(0)->IsNotEqual()) << ins->DumpWithArgs(); - EXPECT_TRUE(ins->InputAt(0)->InputAt(0)->IsPhi()) << ins->DumpWithArgs(); - EXPECT_TRUE(ins->InputAt(0)->InputAt(1)->IsNullConstant()) << ins->DumpWithArgs(); - // Check the second part of AND is the eq other - EXPECT_INS_EQ(ins->InputAt(1)->InputAt(kind.position_ == Position::kLeft ? 0 : 1), - ins->InputAt(0)->InputAt(0)) - << ins->DumpWithArgs(); - } - } - - struct ComparisonInstructions { - void AddSetup(HBasicBlock* blk) const { - for (HInstruction* i : setup_instructions_) { - blk->AddInstruction(i); - } - } - - void AddEnvironment(HEnvironment* env) const { - for (HInstruction* i : setup_instructions_) { - if (i->NeedsEnvironment()) { - i->CopyEnvironmentFrom(env); - } - } - } - - const std::vector<HInstruction*> setup_instructions_; - HInstruction* const cmp_; - }; - - ComparisonInstructions GetComparisonInstructions(HInstruction* partial) { - PartialComparisonKind kind = GetParam(); - std::vector<HInstruction*> setup; - HInstruction* target_other; - switch (kind.target_) { - case PartialComparisonKind::Target::kSelf: - target_other = partial; - break; - case PartialComparisonKind::Target::kNull: - target_other = graph_->GetNullConstant(); - break; - case PartialComparisonKind::Target::kValue: { - HInstruction* cls = MakeClassLoad(); - HInstruction* static_read = - new (GetAllocator()) HStaticFieldGet(cls, - /* field= */ nullptr, - DataType::Type::kReference, - /* field_offset= */ MemberOffset(40), - /* is_volatile= */ false, - /* field_idx= */ 0, - /* declaring_class_def_index= */ 0, - graph_->GetDexFile(), - /* dex_pc= */ 0); - setup.push_back(cls); - setup.push_back(static_read); - target_other = static_read; - break; - } - } - HInstruction* target_left; - HInstruction* target_right; - std::tie(target_left, target_right) = kind.position_ == PartialComparisonKind::Position::kLeft - ? std::pair{partial, target_other} - : std::pair{target_other, partial}; - HInstruction* cmp = - kind.type_ == PartialComparisonKind::Type::kEquals - ? static_cast<HInstruction*>(new (GetAllocator()) HEqual(target_left, target_right)) - : static_cast<HInstruction*>(new (GetAllocator()) HNotEqual(target_left, target_right)); - return {setup, cmp}; - } }; TEST_F(LoadStoreEliminationTest, ArrayGetSetElimination) { @@ -1046,8 +669,10 @@ TEST_F(LoadStoreEliminationTest, StoreAfterLoopWithSideEffects2) { // Add another array parameter that may alias with `array_`. // Note: We're not adding it to the suspend check environment. - AddParameter(new (GetAllocator()) HParameterValue( - graph_->GetDexFile(), dex::TypeIndex(0), 3, DataType::Type::kInt32)); + AddParameter(new (GetAllocator()) HParameterValue(graph_->GetDexFile(), + dex::TypeIndex(0), + 3, + DataType::Type::kInt32)); HInstruction* array2 = parameters_.back(); HInstruction* c0 = graph_->GetIntConstant(0); @@ -1306,14 +931,43 @@ TEST_F(LoadStoreEliminationTest, DefaultShadowClass) { HInstruction* suspend_check = new (GetAllocator()) HSuspendCheck(); entry->AddInstruction(suspend_check); entry->AddInstruction(new (GetAllocator()) HGoto()); - ManuallyBuildEnvFor(suspend_check, {}); - - HInstruction* cls = MakeClassLoad(); - HInstruction* new_inst = MakeNewInstance(cls); + ArenaVector<HInstruction*> current_locals({}, GetAllocator()->Adapter(kArenaAllocInstruction)); + ManuallyBuildEnvFor(suspend_check, ¤t_locals); + + HInstruction* cls = new (GetAllocator()) HLoadClass(graph_->GetCurrentMethod(), + dex::TypeIndex(10), + graph_->GetDexFile(), + ScopedNullHandle<mirror::Class>(), + false, + 0, + false); + HInstruction* new_inst = + new (GetAllocator()) HNewInstance(cls, + 0, + dex::TypeIndex(10), + graph_->GetDexFile(), + false, + QuickEntrypointEnum::kQuickAllocObjectInitialized); HInstruction* const_fence = new (GetAllocator()) HConstructorFence(new_inst, 0, GetAllocator()); - HInstruction* set_field = MakeIFieldSet(new_inst, graph_->GetIntConstant(33), MemberOffset(32)); - HInstruction* get_field = - MakeIFieldGet(new_inst, DataType::Type::kReference, mirror::Object::ClassOffset()); + HInstruction* set_field = new (GetAllocator()) HInstanceFieldSet(new_inst, + graph_->GetIntConstant(33), + nullptr, + DataType::Type::kReference, + MemberOffset(32), + false, + 0, + 0, + graph_->GetDexFile(), + 0); + HInstruction* get_field = new (GetAllocator()) HInstanceFieldGet(new_inst, + nullptr, + DataType::Type::kReference, + mirror::Object::ClassOffset(), + false, + 0, + 0, + graph_->GetDexFile(), + 0); HInstruction* return_val = new (GetAllocator()) HReturn(get_field); main->AddInstruction(cls); main->AddInstruction(new_inst); @@ -1324,17 +978,17 @@ TEST_F(LoadStoreEliminationTest, DefaultShadowClass) { cls->CopyEnvironmentFrom(suspend_check->GetEnvironment()); new_inst->CopyEnvironmentFrom(suspend_check->GetEnvironment()); - SetupExit(exit); + exit->AddInstruction(new (GetAllocator()) HExit()); graph_->ClearDominanceInformation(); PerformLSE(); - EXPECT_INS_REMOVED(new_inst); - EXPECT_INS_REMOVED(const_fence); - EXPECT_INS_REMOVED(get_field); - EXPECT_INS_REMOVED(set_field); - EXPECT_INS_RETAINED(cls); - EXPECT_INS_EQ(cls, return_val->InputAt(0)); + EXPECT_TRUE(IsRemoved(new_inst)); + EXPECT_TRUE(IsRemoved(const_fence)); + EXPECT_TRUE(IsRemoved(get_field)); + EXPECT_TRUE(IsRemoved(set_field)); + EXPECT_FALSE(IsRemoved(cls)); + EXPECT_EQ(cls, return_val->InputAt(0)); } // Object o = new Obj(); @@ -1357,14 +1011,43 @@ TEST_F(LoadStoreEliminationTest, DefaultShadowMonitor) { HInstruction* suspend_check = new (GetAllocator()) HSuspendCheck(); entry->AddInstruction(suspend_check); entry->AddInstruction(new (GetAllocator()) HGoto()); - ManuallyBuildEnvFor(suspend_check, {}); - - HInstruction* cls = MakeClassLoad(); - HInstruction* new_inst = MakeNewInstance(cls); + ArenaVector<HInstruction*> current_locals({}, GetAllocator()->Adapter(kArenaAllocInstruction)); + ManuallyBuildEnvFor(suspend_check, ¤t_locals); + + HInstruction* cls = new (GetAllocator()) HLoadClass(graph_->GetCurrentMethod(), + dex::TypeIndex(10), + graph_->GetDexFile(), + ScopedNullHandle<mirror::Class>(), + false, + 0, + false); + HInstruction* new_inst = + new (GetAllocator()) HNewInstance(cls, + 0, + dex::TypeIndex(10), + graph_->GetDexFile(), + false, + QuickEntrypointEnum::kQuickAllocObjectInitialized); HInstruction* const_fence = new (GetAllocator()) HConstructorFence(new_inst, 0, GetAllocator()); - HInstruction* set_field = MakeIFieldSet(new_inst, graph_->GetIntConstant(33), MemberOffset(32)); - HInstruction* get_field = - MakeIFieldGet(new_inst, DataType::Type::kInt32, mirror::Object::MonitorOffset()); + HInstruction* set_field = new (GetAllocator()) HInstanceFieldSet(new_inst, + graph_->GetIntConstant(33), + nullptr, + DataType::Type::kReference, + MemberOffset(32), + false, + 0, + 0, + graph_->GetDexFile(), + 0); + HInstruction* get_field = new (GetAllocator()) HInstanceFieldGet(new_inst, + nullptr, + DataType::Type::kInt32, + mirror::Object::MonitorOffset(), + false, + 0, + 0, + graph_->GetDexFile(), + 0); HInstruction* return_val = new (GetAllocator()) HReturn(get_field); main->AddInstruction(cls); main->AddInstruction(new_inst); @@ -1375,17 +1058,17 @@ TEST_F(LoadStoreEliminationTest, DefaultShadowMonitor) { cls->CopyEnvironmentFrom(suspend_check->GetEnvironment()); new_inst->CopyEnvironmentFrom(suspend_check->GetEnvironment()); - SetupExit(exit); + exit->AddInstruction(new (GetAllocator()) HExit()); graph_->ClearDominanceInformation(); PerformLSE(); - EXPECT_INS_REMOVED(new_inst); - EXPECT_INS_REMOVED(const_fence); - EXPECT_INS_REMOVED(get_field); - EXPECT_INS_REMOVED(set_field); - EXPECT_INS_RETAINED(cls); - EXPECT_INS_EQ(graph_->GetIntConstant(0), return_val->InputAt(0)); + EXPECT_TRUE(IsRemoved(new_inst)); + EXPECT_TRUE(IsRemoved(const_fence)); + EXPECT_TRUE(IsRemoved(get_field)); + EXPECT_TRUE(IsRemoved(set_field)); + EXPECT_FALSE(IsRemoved(cls)); + EXPECT_EQ(graph_->GetIntConstant(0), return_val->InputAt(0)); } // void DO_CAL() { @@ -1400,8 +1083,7 @@ TEST_F(LoadStoreEliminationTest, DefaultShadowMonitor) { // return t; // } TEST_F(LoadStoreEliminationTest, ArrayLoopOverlap) { - VariableSizedHandleScope vshs(Thread::Current()); - CreateGraph(&vshs); + CreateGraph(); AdjacencyListGraph blocks(graph_, GetAllocator(), "entry", @@ -1432,7 +1114,8 @@ TEST_F(LoadStoreEliminationTest, ArrayLoopOverlap) { loop_pre_header->AddInstruction(alloc_w); loop_pre_header->AddInstruction(pre_header_goto); // environment - ManuallyBuildEnvFor(alloc_w, {}); + ArenaVector<HInstruction*> alloc_locals({}, GetAllocator()->Adapter(kArenaAllocInstruction)); + ManuallyBuildEnvFor(alloc_w, &alloc_locals); // loop-start HPhi* i_phi = new (GetAllocator()) HPhi(GetAllocator(), 0, 0, DataType::Type::kInt32); @@ -1457,18 +1140,44 @@ TEST_F(LoadStoreEliminationTest, ArrayLoopOverlap) { t_phi->AddInput(zero_const); // environment - ManuallyBuildEnvFor(suspend, { alloc_w, i_phi, t_phi }); + ArenaVector<HInstruction*> suspend_locals({ alloc_w, i_phi, t_phi }, + GetAllocator()->Adapter(kArenaAllocInstruction)); + ManuallyBuildEnvFor(suspend, &suspend_locals); // BODY HInstruction* last_i = new (GetAllocator()) HSub(DataType::Type::kInt32, i_phi, one_const); HInstruction* last_get = new (GetAllocator()) HArrayGet(alloc_w, last_i, DataType::Type::kInt32, 0); - HInvoke* body_value = MakeInvoke(DataType::Type::kInt32, { last_get, one_const }); + HInvoke* body_value = new (GetAllocator()) + HInvokeStaticOrDirect(GetAllocator(), + 2, + DataType::Type::kInt32, + 0, + { nullptr, 0 }, + nullptr, + {}, + InvokeType::kStatic, + { nullptr, 0 }, + HInvokeStaticOrDirect::ClinitCheckRequirement::kNone); + body_value->SetRawInputAt(0, last_get); + body_value->SetRawInputAt(1, one_const); HInstruction* body_set = new (GetAllocator()) HArraySet(alloc_w, i_phi, body_value, DataType::Type::kInt32, 0); HInstruction* body_get = new (GetAllocator()) HArrayGet(alloc_w, i_phi, DataType::Type::kInt32, 0); - HInvoke* t_next = MakeInvoke(DataType::Type::kInt32, { body_get, t_phi }); + HInvoke* t_next = new (GetAllocator()) + HInvokeStaticOrDirect(GetAllocator(), + 2, + DataType::Type::kInt32, + 0, + { nullptr, 0 }, + nullptr, + {}, + InvokeType::kStatic, + { nullptr, 0 }, + HInvokeStaticOrDirect::ClinitCheckRequirement::kNone); + t_next->SetRawInputAt(0, body_get); + t_next->SetRawInputAt(1, t_phi); HInstruction* i_next = new (GetAllocator()) HAdd(DataType::Type::kInt32, i_phi, one_const); HInstruction* body_goto = new (GetAllocator()) HGoto(); loop_body->AddInstruction(last_i); @@ -1490,7 +1199,8 @@ TEST_F(LoadStoreEliminationTest, ArrayLoopOverlap) { loop_post->AddInstruction(return_inst); // exit - SetupExit(exit); + HInstruction* exit_inst = new (GetAllocator()) HExit(); + exit->AddInstruction(exit_inst); graph_->ClearDominanceInformation(); graph_->ClearLoopInformation(); @@ -1501,17 +1211,18 @@ TEST_F(LoadStoreEliminationTest, ArrayLoopOverlap) { // back into the array. if (IsRemoved(last_get)) { // If we were able to remove the previous read the entire array should be removable. - EXPECT_INS_REMOVED(body_set); - EXPECT_INS_REMOVED(alloc_w); + EXPECT_TRUE(IsRemoved(body_set)); + EXPECT_TRUE(IsRemoved(alloc_w)); } else { // This is the branch we actually take for now. If we rely on being able to // read the array we'd better remember to write to it as well. - EXPECT_INS_RETAINED(body_set); + EXPECT_FALSE(IsRemoved(body_set)); } // The last 'get' should always be removable. - EXPECT_INS_REMOVED(body_get); + EXPECT_TRUE(IsRemoved(body_get)); } + // void DO_CAL2() { // int i = 1; // int[] w = new int[80]; @@ -1528,8 +1239,7 @@ TEST_F(LoadStoreEliminationTest, ArrayLoopOverlap) { // return t; // } TEST_F(LoadStoreEliminationTest, ArrayLoopOverlap2) { - VariableSizedHandleScope vshs(Thread::Current()); - CreateGraph(&vshs); + CreateGraph(); AdjacencyListGraph blocks(graph_, GetAllocator(), "entry", @@ -1560,7 +1270,8 @@ TEST_F(LoadStoreEliminationTest, ArrayLoopOverlap2) { loop_pre_header->AddInstruction(alloc_w); loop_pre_header->AddInstruction(pre_header_goto); // environment - ManuallyBuildEnvFor(alloc_w, {}); + ArenaVector<HInstruction*> alloc_locals({}, GetAllocator()->Adapter(kArenaAllocInstruction)); + ManuallyBuildEnvFor(alloc_w, &alloc_locals); // loop-start HPhi* i_phi = new (GetAllocator()) HPhi(GetAllocator(), 0, 0, DataType::Type::kInt32); @@ -1585,24 +1296,50 @@ TEST_F(LoadStoreEliminationTest, ArrayLoopOverlap2) { t_phi->AddInput(zero_const); // environment - ManuallyBuildEnvFor(suspend, { alloc_w, i_phi, t_phi }); + ArenaVector<HInstruction*> suspend_locals({ alloc_w, i_phi, t_phi }, + GetAllocator()->Adapter(kArenaAllocInstruction)); + ManuallyBuildEnvFor(suspend, &suspend_locals); // BODY HInstruction* last_i = new (GetAllocator()) HSub(DataType::Type::kInt32, i_phi, one_const); - HInstruction *last_get_1, *last_get_2, *last_get_3; - HInstruction *body_value_1, *body_value_2, *body_value_3; - HInstruction *body_set_1, *body_set_2, *body_set_3; - HInstruction *body_get_1, *body_get_2, *body_get_3; - HInstruction *t_next_1, *t_next_2, *t_next_3; + HInstruction* last_get_1, *last_get_2, *last_get_3; + HInstruction* body_value_1, *body_value_2, *body_value_3; + HInstruction* body_set_1, *body_set_2, *body_set_3; + HInstruction* body_get_1, *body_get_2, *body_get_3; + HInstruction* t_next_1, *t_next_2, *t_next_3; auto make_instructions = [&](HInstruction* last_t_value) { HInstruction* last_get = new (GetAllocator()) HArrayGet(alloc_w, last_i, DataType::Type::kInt32, 0); - HInvoke* body_value = MakeInvoke(DataType::Type::kInt32, { last_get, one_const }); + HInvoke* body_value = new (GetAllocator()) + HInvokeStaticOrDirect(GetAllocator(), + 2, + DataType::Type::kInt32, + 0, + { nullptr, 0 }, + nullptr, + {}, + InvokeType::kStatic, + { nullptr, 0 }, + HInvokeStaticOrDirect::ClinitCheckRequirement::kNone); + body_value->SetRawInputAt(0, last_get); + body_value->SetRawInputAt(1, one_const); HInstruction* body_set = new (GetAllocator()) HArraySet(alloc_w, i_phi, body_value, DataType::Type::kInt32, 0); HInstruction* body_get = new (GetAllocator()) HArrayGet(alloc_w, i_phi, DataType::Type::kInt32, 0); - HInvoke* t_next = MakeInvoke(DataType::Type::kInt32, { body_get, last_t_value }); + HInvoke* t_next = new (GetAllocator()) + HInvokeStaticOrDirect(GetAllocator(), + 2, + DataType::Type::kInt32, + 0, + { nullptr, 0 }, + nullptr, + {}, + InvokeType::kStatic, + { nullptr, 0 }, + HInvokeStaticOrDirect::ClinitCheckRequirement::kNone); + t_next->SetRawInputAt(0, body_get); + t_next->SetRawInputAt(1, last_t_value); loop_body->AddInstruction(last_get); loop_body->AddInstruction(body_value); loop_body->AddInstruction(body_set); @@ -1635,7 +1372,8 @@ TEST_F(LoadStoreEliminationTest, ArrayLoopOverlap2) { loop_post->AddInstruction(return_inst); // exit - SetupExit(exit); + HInstruction* exit_inst = new (GetAllocator()) HExit(); + exit->AddInstruction(exit_inst); graph_->ClearDominanceInformation(); graph_->ClearLoopInformation(); @@ -1646,29 +1384,28 @@ TEST_F(LoadStoreEliminationTest, ArrayLoopOverlap2) { // back into the array. if (IsRemoved(last_get_1)) { // If we were able to remove the previous read the entire array should be removable. - EXPECT_INS_REMOVED(body_set_1); - EXPECT_INS_REMOVED(body_set_2); - EXPECT_INS_REMOVED(body_set_3); - EXPECT_INS_REMOVED(last_get_1); - EXPECT_INS_REMOVED(last_get_2); - EXPECT_INS_REMOVED(alloc_w); + EXPECT_TRUE(IsRemoved(body_set_1)); + EXPECT_TRUE(IsRemoved(body_set_2)); + EXPECT_TRUE(IsRemoved(body_set_3)); + EXPECT_TRUE(IsRemoved(last_get_1)); + EXPECT_TRUE(IsRemoved(last_get_2)); + EXPECT_TRUE(IsRemoved(alloc_w)); } else { // This is the branch we actually take for now. If we rely on being able to // read the array we'd better remember to write to it as well. - EXPECT_INS_RETAINED(body_set_3); + EXPECT_FALSE(IsRemoved(body_set_3)); } // The last 'get' should always be removable. - EXPECT_INS_REMOVED(body_get_1); - EXPECT_INS_REMOVED(body_get_2); - EXPECT_INS_REMOVED(body_get_3); + EXPECT_TRUE(IsRemoved(body_get_1)); + EXPECT_TRUE(IsRemoved(body_get_2)); + EXPECT_TRUE(IsRemoved(body_get_3)); // shadowed writes should always be removed - EXPECT_INS_REMOVED(body_set_1); - EXPECT_INS_REMOVED(body_set_2); + EXPECT_TRUE(IsRemoved(body_set_1)); + EXPECT_TRUE(IsRemoved(body_set_2)); } TEST_F(LoadStoreEliminationTest, ArrayNonLoopPhi) { - VariableSizedHandleScope vshs(Thread::Current()); - CreateGraph(&vshs); + CreateGraph(); AdjacencyListGraph blocks(graph_, GetAllocator(), "entry", @@ -1691,9 +1428,10 @@ TEST_F(LoadStoreEliminationTest, ArrayNonLoopPhi) { HInstruction* zero_const = graph_->GetConstant(DataType::Type::kInt32, 0); HInstruction* one_const = graph_->GetConstant(DataType::Type::kInt32, 1); HInstruction* two_const = graph_->GetConstant(DataType::Type::kInt32, 2); - HInstruction* param = MakeParam(DataType::Type::kBool); - + HInstruction* param = new (GetAllocator()) + HParameterValue(graph_->GetDexFile(), dex::TypeIndex(1), 0, DataType::Type::kBool); HInstruction* entry_goto = new (GetAllocator()) HGoto(); + entry->AddInstruction(param); entry->AddInstruction(entry_goto); HInstruction* alloc_w = new (GetAllocator()) HNewArray(zero_const, two_const, 0, 0); @@ -1701,10 +1439,22 @@ TEST_F(LoadStoreEliminationTest, ArrayNonLoopPhi) { start->AddInstruction(alloc_w); start->AddInstruction(branch); // environment - ManuallyBuildEnvFor(alloc_w, {}); + ArenaVector<HInstruction*> alloc_locals({}, GetAllocator()->Adapter(kArenaAllocInstruction)); + ManuallyBuildEnvFor(alloc_w, &alloc_locals); // left - HInvoke* left_value = MakeInvoke(DataType::Type::kInt32, { zero_const }); + HInvoke* left_value = new (GetAllocator()) + HInvokeStaticOrDirect(GetAllocator(), + 1, + DataType::Type::kInt32, + 0, + { nullptr, 0 }, + nullptr, + {}, + InvokeType::kStatic, + { nullptr, 0 }, + HInvokeStaticOrDirect::ClinitCheckRequirement::kNone); + left_value->SetRawInputAt(0, zero_const); HInstruction* left_set_1 = new (GetAllocator()) HArraySet(alloc_w, zero_const, left_value, DataType::Type::kInt32, 0); HInstruction* left_set_2 = @@ -1714,10 +1464,23 @@ TEST_F(LoadStoreEliminationTest, ArrayNonLoopPhi) { left->AddInstruction(left_set_1); left->AddInstruction(left_set_2); left->AddInstruction(left_goto); - ManuallyBuildEnvFor(left_value, { alloc_w }); + ArenaVector<HInstruction*> left_locals({ alloc_w }, + GetAllocator()->Adapter(kArenaAllocInstruction)); + ManuallyBuildEnvFor(left_value, &alloc_locals); // right - HInvoke* right_value = MakeInvoke(DataType::Type::kInt32, { one_const }); + HInvoke* right_value = new (GetAllocator()) + HInvokeStaticOrDirect(GetAllocator(), + 1, + DataType::Type::kInt32, + 0, + { nullptr, 0 }, + nullptr, + {}, + InvokeType::kStatic, + { nullptr, 0 }, + HInvokeStaticOrDirect::ClinitCheckRequirement::kNone); + right_value->SetRawInputAt(0, one_const); HInstruction* right_set_1 = new (GetAllocator()) HArraySet(alloc_w, zero_const, right_value, DataType::Type::kInt32, 0); HInstruction* right_set_2 = @@ -1727,7 +1490,9 @@ TEST_F(LoadStoreEliminationTest, ArrayNonLoopPhi) { right->AddInstruction(right_set_1); right->AddInstruction(right_set_2); right->AddInstruction(right_goto); - ManuallyBuildEnvFor(right_value, { alloc_w }); + ArenaVector<HInstruction*> right_locals({ alloc_w }, + GetAllocator()->Adapter(kArenaAllocInstruction)); + ManuallyBuildEnvFor(right_value, &alloc_locals); // ret HInstruction* read_1 = @@ -1742,27 +1507,27 @@ TEST_F(LoadStoreEliminationTest, ArrayNonLoopPhi) { ret->AddInstruction(return_inst); // exit - SetupExit(exit); + HInstruction* exit_inst = new (GetAllocator()) HExit(); + exit->AddInstruction(exit_inst); graph_->ClearDominanceInformation(); graph_->ClearLoopInformation(); PerformLSE(); - EXPECT_INS_REMOVED(read_1); - EXPECT_INS_REMOVED(read_2); - EXPECT_INS_REMOVED(left_set_1); - EXPECT_INS_REMOVED(left_set_2); - EXPECT_INS_REMOVED(right_set_1); - EXPECT_INS_REMOVED(right_set_2); - EXPECT_INS_REMOVED(alloc_w); + EXPECT_TRUE(IsRemoved(read_1)); + EXPECT_TRUE(IsRemoved(read_2)); + EXPECT_TRUE(IsRemoved(left_set_1)); + EXPECT_TRUE(IsRemoved(left_set_2)); + EXPECT_TRUE(IsRemoved(right_set_1)); + EXPECT_TRUE(IsRemoved(right_set_2)); + EXPECT_TRUE(IsRemoved(alloc_w)); - EXPECT_INS_RETAINED(left_value); - EXPECT_INS_RETAINED(right_value); + EXPECT_FALSE(IsRemoved(left_value)); + EXPECT_FALSE(IsRemoved(right_value)); } TEST_F(LoadStoreEliminationTest, ArrayMergeDefault) { - VariableSizedHandleScope vshs(Thread::Current()); - CreateGraph(&vshs); + CreateGraph(); AdjacencyListGraph blocks(graph_, GetAllocator(), "entry", @@ -1785,9 +1550,10 @@ TEST_F(LoadStoreEliminationTest, ArrayMergeDefault) { HInstruction* zero_const = graph_->GetConstant(DataType::Type::kInt32, 0); HInstruction* one_const = graph_->GetConstant(DataType::Type::kInt32, 1); HInstruction* two_const = graph_->GetConstant(DataType::Type::kInt32, 2); - HInstruction* param = MakeParam(DataType::Type::kBool); + HInstruction* param = new (GetAllocator()) + HParameterValue(graph_->GetDexFile(), dex::TypeIndex(1), 0, DataType::Type::kBool); HInstruction* entry_goto = new (GetAllocator()) HGoto(); - + entry->AddInstruction(param); entry->AddInstruction(entry_goto); HInstruction* alloc_w = new (GetAllocator()) HNewArray(zero_const, two_const, 0, 0); @@ -1796,7 +1562,7 @@ TEST_F(LoadStoreEliminationTest, ArrayMergeDefault) { start->AddInstruction(branch); // environment ArenaVector<HInstruction*> alloc_locals({}, GetAllocator()->Adapter(kArenaAllocInstruction)); - ManuallyBuildEnvFor(alloc_w, {}); + ManuallyBuildEnvFor(alloc_w, &alloc_locals); // left HInstruction* left_set_1 = @@ -1831,19 +1597,20 @@ TEST_F(LoadStoreEliminationTest, ArrayMergeDefault) { ret->AddInstruction(return_inst); // exit - SetupExit(exit); + HInstruction* exit_inst = new (GetAllocator()) HExit(); + exit->AddInstruction(exit_inst); graph_->ClearDominanceInformation(); graph_->ClearLoopInformation(); PerformLSE(); - EXPECT_INS_REMOVED(read_1); - EXPECT_INS_REMOVED(read_2); - EXPECT_INS_REMOVED(left_set_1); - EXPECT_INS_REMOVED(left_set_2); - EXPECT_INS_REMOVED(right_set_1); - EXPECT_INS_REMOVED(right_set_2); - EXPECT_INS_REMOVED(alloc_w); + EXPECT_TRUE(IsRemoved(read_1)); + EXPECT_TRUE(IsRemoved(read_2)); + EXPECT_TRUE(IsRemoved(left_set_1)); + EXPECT_TRUE(IsRemoved(left_set_2)); + EXPECT_TRUE(IsRemoved(right_set_1)); + EXPECT_TRUE(IsRemoved(right_set_2)); + EXPECT_TRUE(IsRemoved(alloc_w)); } // // ENTRY @@ -1884,22 +1651,22 @@ TEST_F(LoadStoreEliminationTest, PartialUnknownMerge) { CreateGraph(); AdjacencyListGraph blks(SetupFromAdjacencyList("entry", "exit", - {{"entry", "bswitch"}, - {"bswitch", "case1"}, - {"bswitch", "case2"}, - {"bswitch", "case3"}, - {"case1", "breturn"}, - {"case2", "breturn"}, - {"case3", "loop_pre_header"}, - {"loop_pre_header", "loop_header"}, - {"loop_header", "loop_body"}, - {"loop_body", "loop_if_left"}, - {"loop_body", "loop_if_right"}, - {"loop_if_left", "loop_end"}, - {"loop_if_right", "loop_end"}, - {"loop_end", "loop_header"}, - {"loop_header", "breturn"}, - {"breturn", "exit"}})); + { { "entry", "bswitch" }, + { "bswitch", "case1" }, + { "bswitch", "case2" }, + { "bswitch", "case3" }, + { "case1", "breturn" }, + { "case2", "breturn" }, + { "case3", "loop_pre_header" }, + { "loop_pre_header", "loop_header" }, + { "loop_header", "loop_body" }, + { "loop_body", "loop_if_left" }, + { "loop_body", "loop_if_right" }, + { "loop_if_left", "loop_end" }, + { "loop_if_right", "loop_end" }, + { "loop_end", "loop_header" }, + { "loop_header", "breturn" }, + { "breturn", "exit" } })); #define GET_BLOCK(name) HBasicBlock* name = blks.Get(#name) GET_BLOCK(entry); GET_BLOCK(bswitch); @@ -1916,41 +1683,104 @@ TEST_F(LoadStoreEliminationTest, PartialUnknownMerge) { GET_BLOCK(loop_if_right); GET_BLOCK(loop_end); #undef GET_BLOCK - HInstruction* switch_val = MakeParam(DataType::Type::kInt32); + HInstruction* switch_val = new (GetAllocator()) + HParameterValue(graph_->GetDexFile(), dex::TypeIndex(1), 1, DataType::Type::kInt32); HInstruction* c1 = graph_->GetIntConstant(1); HInstruction* c2 = graph_->GetIntConstant(2); HInstruction* c3 = graph_->GetIntConstant(3); HInstruction* c5 = graph_->GetIntConstant(5); - - HInstruction* cls = MakeClassLoad(); - HInstruction* new_inst = MakeNewInstance(cls); + HInstruction* cls = new (GetAllocator()) HLoadClass(graph_->GetCurrentMethod(), + dex::TypeIndex(10), + graph_->GetDexFile(), + ScopedNullHandle<mirror::Class>(), + false, + 0, + false); + HInstruction* new_inst = + new (GetAllocator()) HNewInstance(cls, + 0, + dex::TypeIndex(10), + graph_->GetDexFile(), + false, + QuickEntrypointEnum::kQuickAllocObjectInitialized); HInstruction* entry_goto = new (GetAllocator()) HGoto(); + entry->AddInstruction(switch_val); entry->AddInstruction(cls); entry->AddInstruction(new_inst); entry->AddInstruction(entry_goto); - ManuallyBuildEnvFor(cls, {}); + ArenaVector<HInstruction*> current_locals({}, GetAllocator()->Adapter(kArenaAllocInstruction)); + ManuallyBuildEnvFor(cls, ¤t_locals); new_inst->CopyEnvironmentFrom(cls->GetEnvironment()); HInstruction* switch_inst = new (GetAllocator()) HPackedSwitch(0, 2, switch_val); bswitch->AddInstruction(switch_inst); - HInstruction* write_c1 = MakeIFieldSet(new_inst, c1, MemberOffset(32)); - HInstruction* call_c1 = MakeInvoke(DataType::Type::kVoid, { new_inst }); + HInstruction* write_c1 = new (GetAllocator()) HInstanceFieldSet(new_inst, + c1, + nullptr, + DataType::Type::kInt32, + MemberOffset(32), + false, + 0, + 0, + graph_->GetDexFile(), + 0); + HInstruction* call_c1 = new (GetAllocator()) + HInvokeStaticOrDirect(GetAllocator(), + 1, + DataType::Type::kVoid, + 0, + { nullptr, 0 }, + nullptr, + {}, + InvokeType::kStatic, + { nullptr, 0 }, + HInvokeStaticOrDirect::ClinitCheckRequirement::kNone); HInstruction* goto_c1 = new (GetAllocator()) HGoto(); + call_c1->AsInvoke()->SetRawInputAt(0, new_inst); case1->AddInstruction(write_c1); case1->AddInstruction(call_c1); case1->AddInstruction(goto_c1); call_c1->CopyEnvironmentFrom(cls->GetEnvironment()); - HInstruction* write_c2 = MakeIFieldSet(new_inst, c2, MemberOffset(32)); - HInstruction* call_c2 = MakeInvoke(DataType::Type::kVoid, { new_inst }); + HInstruction* write_c2 = new (GetAllocator()) HInstanceFieldSet(new_inst, + c2, + nullptr, + DataType::Type::kInt32, + MemberOffset(32), + false, + 0, + 0, + graph_->GetDexFile(), + 0); + HInstruction* call_c2 = new (GetAllocator()) + HInvokeStaticOrDirect(GetAllocator(), + 1, + DataType::Type::kVoid, + 0, + { nullptr, 0 }, + nullptr, + {}, + InvokeType::kStatic, + { nullptr, 0 }, + HInvokeStaticOrDirect::ClinitCheckRequirement::kNone); HInstruction* goto_c2 = new (GetAllocator()) HGoto(); + call_c2->AsInvoke()->SetRawInputAt(0, new_inst); case2->AddInstruction(write_c2); case2->AddInstruction(call_c2); case2->AddInstruction(goto_c2); call_c2->CopyEnvironmentFrom(cls->GetEnvironment()); - HInstruction* write_c3 = MakeIFieldSet(new_inst, c3, MemberOffset(32)); + HInstruction* write_c3 = new (GetAllocator()) HInstanceFieldSet(new_inst, + c3, + nullptr, + DataType::Type::kInt32, + MemberOffset(32), + false, + 0, + 0, + graph_->GetDexFile(), + 0); HInstruction* goto_c3 = new (GetAllocator()) HGoto(); case3->AddInstruction(write_c3); case3->AddInstruction(goto_c3); @@ -1959,7 +1789,17 @@ TEST_F(LoadStoreEliminationTest, PartialUnknownMerge) { loop_pre_header->AddInstruction(goto_preheader); HInstruction* suspend_check_header = new (GetAllocator()) HSuspendCheck(); - HInstruction* call_loop_header = MakeInvoke(DataType::Type::kBool, {}); + HInstruction* call_loop_header = new (GetAllocator()) + HInvokeStaticOrDirect(GetAllocator(), + 0, + DataType::Type::kBool, + 0, + { nullptr, 0 }, + nullptr, + {}, + InvokeType::kStatic, + { nullptr, 0 }, + HInvokeStaticOrDirect::ClinitCheckRequirement::kNone); HInstruction* if_loop_header = new (GetAllocator()) HIf(call_loop_header); loop_header->AddInstruction(suspend_check_header); loop_header->AddInstruction(call_loop_header); @@ -1967,7 +1807,17 @@ TEST_F(LoadStoreEliminationTest, PartialUnknownMerge) { call_loop_header->CopyEnvironmentFrom(cls->GetEnvironment()); suspend_check_header->CopyEnvironmentFrom(cls->GetEnvironment()); - HInstruction* call_loop_body = MakeInvoke(DataType::Type::kBool, {}); + HInstruction* call_loop_body = new (GetAllocator()) + HInvokeStaticOrDirect(GetAllocator(), + 0, + DataType::Type::kBool, + 0, + { nullptr, 0 }, + nullptr, + {}, + InvokeType::kStatic, + { nullptr, 0 }, + HInvokeStaticOrDirect::ClinitCheckRequirement::kNone); HInstruction* if_loop_body = new (GetAllocator()) HIf(call_loop_body); loop_body->AddInstruction(call_loop_body); loop_body->AddInstruction(if_loop_body); @@ -1976,7 +1826,16 @@ TEST_F(LoadStoreEliminationTest, PartialUnknownMerge) { HInstruction* goto_loop_left = new (GetAllocator()) HGoto(); loop_if_left->AddInstruction(goto_loop_left); - HInstruction* write_loop_right = MakeIFieldSet(new_inst, c5, MemberOffset(32)); + HInstruction* write_loop_right = new (GetAllocator()) HInstanceFieldSet(new_inst, + c5, + nullptr, + DataType::Type::kInt32, + MemberOffset(32), + false, + 0, + 0, + graph_->GetDexFile(), + 0); HInstruction* goto_loop_right = new (GetAllocator()) HGoto(); loop_if_right->AddInstruction(write_loop_right); loop_if_right->AddInstruction(goto_loop_right); @@ -1984,23 +1843,31 @@ TEST_F(LoadStoreEliminationTest, PartialUnknownMerge) { HInstruction* goto_loop_end = new (GetAllocator()) HGoto(); loop_end->AddInstruction(goto_loop_end); - HInstruction* read_bottom = MakeIFieldGet(new_inst, DataType::Type::kInt32, MemberOffset(32)); + HInstruction* read_bottom = new (GetAllocator()) HInstanceFieldGet(new_inst, + nullptr, + DataType::Type::kInt32, + MemberOffset(32), + false, + 0, + 0, + graph_->GetDexFile(), + 0); HInstruction* return_exit = new (GetAllocator()) HReturn(read_bottom); breturn->AddInstruction(read_bottom); breturn->AddInstruction(return_exit); - SetupExit(exit); - + HInstruction* exit_ins = new (GetAllocator()) HExit(); + exit->AddInstruction(exit_ins); // PerformLSE expects this to be empty. graph_->ClearDominanceInformation(); - LOG(INFO) << "Pre LSE " << blks; - PerformLSENoPartial(); - - EXPECT_INS_RETAINED(read_bottom); - EXPECT_INS_RETAINED(write_c1); - EXPECT_INS_RETAINED(write_c2); - EXPECT_INS_RETAINED(write_c3); - EXPECT_INS_RETAINED(write_loop_right); + PerformLSE(); + + EXPECT_FALSE(IsRemoved(read_bottom)); + EXPECT_FALSE(IsRemoved(write_c1)); + EXPECT_FALSE(IsRemoved(write_c2)); + EXPECT_FALSE(IsRemoved(write_c3)); + // EXPECT_FALSE(IsRemoved(write_loop_left)); + EXPECT_FALSE(IsRemoved(write_loop_right)); } // // ENTRY @@ -2020,8 +1887,7 @@ TEST_F(LoadStoreEliminationTest, PartialUnknownMerge) { // EXIT // return PHI(foo_l, foo_r) TEST_F(LoadStoreEliminationTest, PartialLoadElimination) { - VariableSizedHandleScope vshs(Thread::Current()); - CreateGraph(&vshs); + InitGraph(); AdjacencyListGraph blks(SetupFromAdjacencyList("entry", "exit_REAL", { { "entry", "left" }, @@ -2033,37 +1899,99 @@ TEST_F(LoadStoreEliminationTest, PartialLoadElimination) { HBasicBlock* left = blks.Get("left"); HBasicBlock* right = blks.Get("right"); HBasicBlock* exit = blks.Get("exit"); - HInstruction* bool_value = MakeParam(DataType::Type::kBool); + HInstruction* bool_value = new (GetAllocator()) + HParameterValue(graph_->GetDexFile(), dex::TypeIndex(1), 1, DataType::Type::kBool); HInstruction* c1 = graph_->GetIntConstant(1); HInstruction* c2 = graph_->GetIntConstant(2); - - HInstruction* cls = MakeClassLoad(); - HInstruction* new_inst = MakeNewInstance(cls); + HInstruction* cls = new (GetAllocator()) HLoadClass(graph_->GetCurrentMethod(), + dex::TypeIndex(10), + graph_->GetDexFile(), + ScopedNullHandle<mirror::Class>(), + false, + 0, + false); + HInstruction* new_inst = + new (GetAllocator()) HNewInstance(cls, + 0, + dex::TypeIndex(10), + graph_->GetDexFile(), + false, + QuickEntrypointEnum::kQuickAllocObjectInitialized); HInstruction* if_inst = new (GetAllocator()) HIf(bool_value); + entry->AddInstruction(bool_value); entry->AddInstruction(cls); entry->AddInstruction(new_inst); entry->AddInstruction(if_inst); - ManuallyBuildEnvFor(cls, {}); + ArenaVector<HInstruction*> current_locals({}, GetAllocator()->Adapter(kArenaAllocInstruction)); + ManuallyBuildEnvFor(cls, ¤t_locals); new_inst->CopyEnvironmentFrom(cls->GetEnvironment()); - HInstruction* write_left = MakeIFieldSet(new_inst, c1, MemberOffset(32)); - HInstruction* call_left = MakeInvoke(DataType::Type::kVoid, { new_inst }); - HInstruction* read_left = MakeIFieldGet(new_inst, DataType::Type::kInt32, MemberOffset(16)); + HInstruction* write_left = new (GetAllocator()) HInstanceFieldSet(new_inst, + c1, + nullptr, + DataType::Type::kInt32, + MemberOffset(32), + false, + 0, + 0, + graph_->GetDexFile(), + 0); + HInstruction* call_left = new (GetAllocator()) + HInvokeStaticOrDirect(GetAllocator(), + 1, + DataType::Type::kVoid, + 0, + { nullptr, 0 }, + nullptr, + {}, + InvokeType::kStatic, + { nullptr, 0 }, + HInvokeStaticOrDirect::ClinitCheckRequirement::kNone); + HInstruction* read_left = new (GetAllocator()) HInstanceFieldGet(new_inst, + nullptr, + DataType::Type::kInt32, + MemberOffset(16), + false, + 0, + 0, + graph_->GetDexFile(), + 0); HInstruction* goto_left = new (GetAllocator()) HGoto(); + call_left->AsInvoke()->SetRawInputAt(0, new_inst); left->AddInstruction(write_left); left->AddInstruction(call_left); left->AddInstruction(read_left); left->AddInstruction(goto_left); call_left->CopyEnvironmentFrom(cls->GetEnvironment()); - HInstruction* write_right = MakeIFieldSet(new_inst, c2, MemberOffset(16)); - HInstruction* read_right = MakeIFieldGet(new_inst, DataType::Type::kInt32, MemberOffset(16)); + HInstruction* write_right = new (GetAllocator()) HInstanceFieldSet(new_inst, + c2, + nullptr, + DataType::Type::kInt32, + MemberOffset(16), + false, + 0, + 0, + graph_->GetDexFile(), + 0); + HInstruction* read_right = new (GetAllocator()) HInstanceFieldGet(new_inst, + nullptr, + DataType::Type::kInt32, + MemberOffset(16), + false, + 0, + 0, + graph_->GetDexFile(), + 0); HInstruction* goto_right = new (GetAllocator()) HGoto(); right->AddInstruction(write_right); right->AddInstruction(read_right); right->AddInstruction(goto_right); - HInstruction* phi_final = MakePhi({read_left, read_right}); + HInstruction* phi_final = + new (GetAllocator()) HPhi(GetAllocator(), 12, 2, DataType::Type::kInt32); + phi_final->SetRawInputAt(0, read_left); + phi_final->SetRawInputAt(1, read_right); HInstruction* return_exit = new (GetAllocator()) HReturn(phi_final); exit->AddPhi(phi_final->AsPhi()); exit->AddInstruction(return_exit); @@ -2094,10 +2022,10 @@ TEST_F(LoadStoreEliminationTest, PartialLoadElimination) { // } // EXIT // return obj.field -// This test runs with partial LSE disabled. +// TODO We eventually want to be able to eliminate the right write along with the final read but +// will need either new blocks or new instructions. TEST_F(LoadStoreEliminationTest, PartialLoadPreserved) { - VariableSizedHandleScope vshs(Thread::Current()); - CreateGraph(&vshs); + InitGraph(); AdjacencyListGraph blks(SetupFromAdjacencyList("entry", "exit_REAL", { { "entry", "left" }, @@ -2109,42 +2037,93 @@ TEST_F(LoadStoreEliminationTest, PartialLoadPreserved) { HBasicBlock* left = blks.Get("left"); HBasicBlock* right = blks.Get("right"); HBasicBlock* exit = blks.Get("exit"); - HInstruction* bool_value = MakeParam(DataType::Type::kBool); + HInstruction* bool_value = new (GetAllocator()) + HParameterValue(graph_->GetDexFile(), dex::TypeIndex(1), 1, DataType::Type::kBool); HInstruction* c1 = graph_->GetIntConstant(1); HInstruction* c2 = graph_->GetIntConstant(2); - - HInstruction* cls = MakeClassLoad(); - HInstruction* new_inst = MakeNewInstance(cls); + HInstruction* cls = new (GetAllocator()) HLoadClass(graph_->GetCurrentMethod(), + dex::TypeIndex(10), + graph_->GetDexFile(), + ScopedNullHandle<mirror::Class>(), + false, + 0, + false); + HInstruction* new_inst = + new (GetAllocator()) HNewInstance(cls, + 0, + dex::TypeIndex(10), + graph_->GetDexFile(), + false, + QuickEntrypointEnum::kQuickAllocObjectInitialized); HInstruction* if_inst = new (GetAllocator()) HIf(bool_value); + entry->AddInstruction(bool_value); entry->AddInstruction(cls); entry->AddInstruction(new_inst); entry->AddInstruction(if_inst); - ManuallyBuildEnvFor(cls, {}); + ArenaVector<HInstruction*> current_locals({}, GetAllocator()->Adapter(kArenaAllocInstruction)); + ManuallyBuildEnvFor(cls, ¤t_locals); new_inst->CopyEnvironmentFrom(cls->GetEnvironment()); - HInstruction* write_left = MakeIFieldSet(new_inst, c1, MemberOffset(32)); - HInstruction* call_left = MakeInvoke(DataType::Type::kVoid, { new_inst }); + HInstruction* write_left = new (GetAllocator()) HInstanceFieldSet(new_inst, + c1, + nullptr, + DataType::Type::kInt32, + MemberOffset(32), + false, + 0, + 0, + graph_->GetDexFile(), + 0); + HInstruction* call_left = new (GetAllocator()) + HInvokeStaticOrDirect(GetAllocator(), + 1, + DataType::Type::kVoid, + 0, + { nullptr, 0 }, + nullptr, + {}, + InvokeType::kStatic, + { nullptr, 0 }, + HInvokeStaticOrDirect::ClinitCheckRequirement::kNone); HInstruction* goto_left = new (GetAllocator()) HGoto(); + call_left->AsInvoke()->SetRawInputAt(0, new_inst); left->AddInstruction(write_left); left->AddInstruction(call_left); left->AddInstruction(goto_left); call_left->CopyEnvironmentFrom(cls->GetEnvironment()); - HInstruction* write_right = MakeIFieldSet(new_inst, c2, MemberOffset(32)); + HInstruction* write_right = new (GetAllocator()) HInstanceFieldSet(new_inst, + c2, + nullptr, + DataType::Type::kInt32, + MemberOffset(32), + false, + 0, + 0, + graph_->GetDexFile(), + 0); HInstruction* goto_right = new (GetAllocator()) HGoto(); right->AddInstruction(write_right); right->AddInstruction(goto_right); - HInstruction* read_bottom = MakeIFieldGet(new_inst, DataType::Type::kInt32, MemberOffset(32)); + HInstruction* read_bottom = new (GetAllocator()) HInstanceFieldGet(new_inst, + nullptr, + DataType::Type::kInt32, + MemberOffset(32), + false, + 0, + 0, + graph_->GetDexFile(), + 0); HInstruction* return_exit = new (GetAllocator()) HReturn(read_bottom); exit->AddInstruction(read_bottom); exit->AddInstruction(return_exit); // PerformLSE expects this to be empty. graph_->ClearDominanceInformation(); - PerformLSENoPartial(); + PerformLSE(); - EXPECT_INS_RETAINED(read_bottom) << *read_bottom; - EXPECT_INS_RETAINED(write_right) << *write_right; + ASSERT_FALSE(IsRemoved(read_bottom)); + ASSERT_FALSE(IsRemoved(write_right)); } // // ENTRY @@ -2165,10 +2144,10 @@ TEST_F(LoadStoreEliminationTest, PartialLoadPreserved) { // } // EXIT // return obj.field -// NB This test is for non-partial LSE flow. Normally the obj.field writes will be removed +// TODO We eventually want to be able to eliminate the right write along with the final read but +// will need either new blocks or new instructions. TEST_F(LoadStoreEliminationTest, PartialLoadPreserved2) { - VariableSizedHandleScope vshs(Thread::Current()); - CreateGraph(&vshs); + InitGraph(); AdjacencyListGraph blks(SetupFromAdjacencyList("entry", "exit_REAL", { { "entry", "left" }, @@ -2187,24 +2166,60 @@ TEST_F(LoadStoreEliminationTest, PartialLoadPreserved2) { HBasicBlock* right_second = blks.Get("right_second"); HBasicBlock* right_end = blks.Get("right_end"); HBasicBlock* exit = blks.Get("exit"); - HInstruction* bool_value = MakeParam(DataType::Type::kBool); - HInstruction* bool_value_2 = MakeParam(DataType::Type::kBool); + HInstruction* bool_value = new (GetAllocator()) + HParameterValue(graph_->GetDexFile(), dex::TypeIndex(1), 1, DataType::Type::kBool); + HInstruction* bool_value_2 = new (GetAllocator()) + HParameterValue(graph_->GetDexFile(), dex::TypeIndex(1), 2, DataType::Type::kBool); HInstruction* c1 = graph_->GetIntConstant(1); HInstruction* c2 = graph_->GetIntConstant(2); HInstruction* c3 = graph_->GetIntConstant(3); - - HInstruction* cls = MakeClassLoad(); - HInstruction* new_inst = MakeNewInstance(cls); + HInstruction* cls = new (GetAllocator()) HLoadClass(graph_->GetCurrentMethod(), + dex::TypeIndex(10), + graph_->GetDexFile(), + ScopedNullHandle<mirror::Class>(), + false, + 0, + false); + HInstruction* new_inst = + new (GetAllocator()) HNewInstance(cls, + 0, + dex::TypeIndex(10), + graph_->GetDexFile(), + false, + QuickEntrypointEnum::kQuickAllocObjectInitialized); HInstruction* if_inst = new (GetAllocator()) HIf(bool_value); + entry->AddInstruction(bool_value); + entry->AddInstruction(bool_value_2); entry->AddInstruction(cls); entry->AddInstruction(new_inst); entry->AddInstruction(if_inst); - ManuallyBuildEnvFor(cls, {}); + ArenaVector<HInstruction*> current_locals({}, GetAllocator()->Adapter(kArenaAllocInstruction)); + ManuallyBuildEnvFor(cls, ¤t_locals); new_inst->CopyEnvironmentFrom(cls->GetEnvironment()); - HInstruction* write_left = MakeIFieldSet(new_inst, c1, MemberOffset(32)); - HInstruction* call_left = MakeInvoke(DataType::Type::kVoid, { new_inst }); + HInstruction* write_left = new (GetAllocator()) HInstanceFieldSet(new_inst, + c1, + nullptr, + DataType::Type::kInt32, + MemberOffset(32), + false, + 0, + 0, + graph_->GetDexFile(), + 0); + HInstruction* call_left = new (GetAllocator()) + HInvokeStaticOrDirect(GetAllocator(), + 1, + DataType::Type::kVoid, + 0, + { nullptr, 0 }, + nullptr, + {}, + InvokeType::kStatic, + { nullptr, 0 }, + HInvokeStaticOrDirect::ClinitCheckRequirement::kNone); HInstruction* goto_left = new (GetAllocator()) HGoto(); + call_left->AsInvoke()->SetRawInputAt(0, new_inst); left->AddInstruction(write_left); left->AddInstruction(call_left); left->AddInstruction(goto_left); @@ -2213,12 +2228,30 @@ TEST_F(LoadStoreEliminationTest, PartialLoadPreserved2) { HInstruction* right_if = new (GetAllocator()) HIf(bool_value_2); right_start->AddInstruction(right_if); - HInstruction* write_right_first = MakeIFieldSet(new_inst, c2, MemberOffset(32)); + HInstruction* write_right_first = new (GetAllocator()) HInstanceFieldSet(new_inst, + c2, + nullptr, + DataType::Type::kInt32, + MemberOffset(32), + false, + 0, + 0, + graph_->GetDexFile(), + 0); HInstruction* goto_right_first = new (GetAllocator()) HGoto(); right_first->AddInstruction(write_right_first); right_first->AddInstruction(goto_right_first); - HInstruction* write_right_second = MakeIFieldSet(new_inst, c3, MemberOffset(32)); + HInstruction* write_right_second = new (GetAllocator()) HInstanceFieldSet(new_inst, + c3, + nullptr, + DataType::Type::kInt32, + MemberOffset(32), + false, + 0, + 0, + graph_->GetDexFile(), + 0); HInstruction* goto_right_second = new (GetAllocator()) HGoto(); right_second->AddInstruction(write_right_second); right_second->AddInstruction(goto_right_second); @@ -2226,17 +2259,25 @@ TEST_F(LoadStoreEliminationTest, PartialLoadPreserved2) { HInstruction* goto_right_end = new (GetAllocator()) HGoto(); right_end->AddInstruction(goto_right_end); - HInstruction* read_bottom = MakeIFieldGet(new_inst, DataType::Type::kInt32, MemberOffset(32)); + HInstruction* read_bottom = new (GetAllocator()) HInstanceFieldGet(new_inst, + nullptr, + DataType::Type::kInt32, + MemberOffset(32), + false, + 0, + 0, + graph_->GetDexFile(), + 0); HInstruction* return_exit = new (GetAllocator()) HReturn(read_bottom); exit->AddInstruction(read_bottom); exit->AddInstruction(return_exit); // PerformLSE expects this to be empty. graph_->ClearDominanceInformation(); - PerformLSENoPartial(); + PerformLSE(); - EXPECT_INS_RETAINED(read_bottom); - EXPECT_INS_RETAINED(write_right_first); - EXPECT_INS_RETAINED(write_right_second); + ASSERT_FALSE(IsRemoved(read_bottom)); + EXPECT_FALSE(IsRemoved(write_right_first)); + EXPECT_FALSE(IsRemoved(write_right_second)); } // // ENTRY @@ -2255,15 +2296,14 @@ TEST_F(LoadStoreEliminationTest, PartialLoadPreserved2) { // ELIMINATE // return obj.field TEST_F(LoadStoreEliminationTest, PartialLoadElimination2) { - VariableSizedHandleScope vshs(Thread::Current()); - CreateGraph(&vshs); + InitGraph(); AdjacencyListGraph blks(SetupFromAdjacencyList("entry", "exit", - {{"entry", "left"}, - {"entry", "right"}, - {"left", "breturn"}, - {"right", "breturn"}, - {"breturn", "exit"}})); + { { "entry", "left" }, + { "entry", "right" }, + { "left", "breturn"}, + { "right", "breturn" }, + { "breturn", "exit" } })); #define GET_BLOCK(name) HBasicBlock* name = blks.Get(#name) GET_BLOCK(entry); GET_BLOCK(exit); @@ -2271,1888 +2311,98 @@ TEST_F(LoadStoreEliminationTest, PartialLoadElimination2) { GET_BLOCK(left); GET_BLOCK(right); #undef GET_BLOCK - HInstruction* bool_value = MakeParam(DataType::Type::kBool); + HInstruction* bool_value = new (GetAllocator()) + HParameterValue(graph_->GetDexFile(), dex::TypeIndex(1), 1, DataType::Type::kBool); HInstruction* c1 = graph_->GetIntConstant(1); HInstruction* c2 = graph_->GetIntConstant(2); - - HInstruction* cls = MakeClassLoad(); - HInstruction* new_inst = MakeNewInstance(cls); + HInstruction* cls = new (GetAllocator()) HLoadClass(graph_->GetCurrentMethod(), + dex::TypeIndex(10), + graph_->GetDexFile(), + ScopedNullHandle<mirror::Class>(), + false, + 0, + false); + HInstruction* new_inst = + new (GetAllocator()) HNewInstance(cls, + 0, + dex::TypeIndex(10), + graph_->GetDexFile(), + false, + QuickEntrypointEnum::kQuickAllocObjectInitialized); HInstruction* if_inst = new (GetAllocator()) HIf(bool_value); + entry->AddInstruction(bool_value); entry->AddInstruction(cls); entry->AddInstruction(new_inst); entry->AddInstruction(if_inst); - ManuallyBuildEnvFor(cls, {}); + ArenaVector<HInstruction*> current_locals({}, GetAllocator()->Adapter(kArenaAllocInstruction)); + ManuallyBuildEnvFor(cls, ¤t_locals); new_inst->CopyEnvironmentFrom(cls->GetEnvironment()); - HInstruction* call_left = MakeInvoke(DataType::Type::kVoid, { new_inst }); - HInstruction* write_left = MakeIFieldSet(new_inst, c1, MemberOffset(32)); + HInstruction* call_left = new (GetAllocator()) + HInvokeStaticOrDirect(GetAllocator(), + 1, + DataType::Type::kVoid, + 0, + { nullptr, 0 }, + nullptr, + {}, + InvokeType::kStatic, + { nullptr, 0 }, + HInvokeStaticOrDirect::ClinitCheckRequirement::kNone); + HInstruction* write_left = new (GetAllocator()) HInstanceFieldSet(new_inst, + c1, + nullptr, + DataType::Type::kInt32, + MemberOffset(32), + false, + 0, + 0, + graph_->GetDexFile(), + 0); HInstruction* goto_left = new (GetAllocator()) HGoto(); + call_left->AsInvoke()->SetRawInputAt(0, new_inst); left->AddInstruction(call_left); left->AddInstruction(write_left); left->AddInstruction(goto_left); call_left->CopyEnvironmentFrom(cls->GetEnvironment()); - HInstruction* write_right = MakeIFieldSet(new_inst, c2, MemberOffset(32)); + HInstruction* write_right = new (GetAllocator()) HInstanceFieldSet(new_inst, + c2, + nullptr, + DataType::Type::kInt32, + MemberOffset(32), + false, + 0, + 0, + graph_->GetDexFile(), + 0); HInstruction* goto_right = new (GetAllocator()) HGoto(); right->AddInstruction(write_right); right->AddInstruction(goto_right); - HInstruction* read_bottom = MakeIFieldGet(new_inst, DataType::Type::kInt32, MemberOffset(32)); + HInstruction* read_bottom = new (GetAllocator()) HInstanceFieldGet(new_inst, + nullptr, + DataType::Type::kInt32, + MemberOffset(32), + false, + 0, + 0, + graph_->GetDexFile(), + 0); HInstruction* return_exit = new (GetAllocator()) HReturn(read_bottom); breturn->AddInstruction(read_bottom); breturn->AddInstruction(return_exit); - SetupExit(exit); - + HInstruction* exit_instruction = new (GetAllocator()) HExit(); + exit->AddInstruction(exit_instruction); // PerformLSE expects this to be empty. graph_->ClearDominanceInformation(); PerformLSE(); - EXPECT_INS_REMOVED(read_bottom); - EXPECT_INS_REMOVED(write_right); - EXPECT_INS_RETAINED(write_left); - EXPECT_INS_RETAINED(call_left); -} - -class PatternMatchGraphVisitor : public HGraphVisitor { - private: - struct HandlerWrapper { - public: - virtual ~HandlerWrapper() {} - virtual void operator()(HInstruction* h) = 0; - }; - - template <HInstruction::InstructionKind kKind, typename F> - struct KindWrapper; - -#define GEN_HANDLER(nm, unused) \ - template <typename F> \ - struct KindWrapper<HInstruction::InstructionKind::k##nm, F> : public HandlerWrapper { \ - public: \ - explicit KindWrapper(F f) : f_(f) {} \ - void operator()(HInstruction* h) override { \ - if constexpr (std::is_invocable_v<F, H##nm*>) { \ - f_(h->As##nm()); \ - } else { \ - LOG(FATAL) << "Incorrect call with " << #nm; \ - } \ - } \ - \ - private: \ - F f_; \ - }; - - FOR_EACH_CONCRETE_INSTRUCTION(GEN_HANDLER) -#undef GEN_HANDLER - - template <typename F> - std::unique_ptr<HandlerWrapper> GetWrapper(HInstruction::InstructionKind kind, F f) { - switch (kind) { -#define GEN_GETTER(nm, unused) \ - case HInstruction::InstructionKind::k##nm: \ - return std::unique_ptr<HandlerWrapper>( \ - new KindWrapper<HInstruction::InstructionKind::k##nm, F>(f)); - FOR_EACH_CONCRETE_INSTRUCTION(GEN_GETTER) -#undef GEN_GETTER - default: - LOG(FATAL) << "Unable to handle kind " << kind; - return nullptr; - } - } - - public: - template <typename... Inst> - explicit PatternMatchGraphVisitor(HGraph* graph, Inst... handlers) : HGraphVisitor(graph) { - FillHandlers(handlers...); - } - - void VisitInstruction(HInstruction* instruction) override { - auto& h = handlers_[instruction->GetKind()]; - if (h.get() != nullptr) { - (*h)(instruction); - } - } - - private: - template <typename Func> - constexpr HInstruction::InstructionKind GetKind() { -#define CHECK_INST(nm, unused) \ - if constexpr (std::is_invocable_v<Func, H##nm*>) { \ - return HInstruction::InstructionKind::k##nm; \ - } - FOR_EACH_CONCRETE_INSTRUCTION(CHECK_INST); -#undef CHECK_INST - static_assert(!std::is_invocable_v<Func, HInstruction*>, - "Use on generic HInstruction not allowed"); -#define STATIC_ASSERT_ABSTRACT(nm, unused) && !std::is_invocable_v<Func, H##nm*> - static_assert(true FOR_EACH_ABSTRACT_INSTRUCTION(STATIC_ASSERT_ABSTRACT), - "Must not be abstract instruction"); -#undef STATIC_ASSERT_ABSTRACT -#define STATIC_ASSERT_CONCRETE(nm, unused) || std::is_invocable_v<Func, H##nm*> - static_assert(false FOR_EACH_CONCRETE_INSTRUCTION(STATIC_ASSERT_CONCRETE), - "Must be a concrete instruction"); -#undef STATIC_ASSERT_CONCRETE - return HInstruction::InstructionKind::kLastInstructionKind; - } - template <typename First> - void FillHandlers(First h1) { - HInstruction::InstructionKind type = GetKind<First>(); - CHECK_NE(type, HInstruction::kLastInstructionKind) - << "Unknown instruction kind. Only concrete ones please."; - handlers_[type] = GetWrapper(type, h1); - } - - template <typename First, typename... Inst> - void FillHandlers(First h1, Inst... handlers) { - FillHandlers(h1); - FillHandlers<Inst...>(handlers...); - } - - std::array<std::unique_ptr<HandlerWrapper>, HInstruction::InstructionKind::kLastInstructionKind> - handlers_; -}; - -template <typename... Target> -std::tuple<std::vector<Target*>...> FindAllInstructions( - HGraph* graph, - std::variant<std::nullopt_t, HBasicBlock*, std::initializer_list<HBasicBlock*>> blks = - std::nullopt) { - std::tuple<std::vector<Target*>...> res; - PatternMatchGraphVisitor vis( - graph, [&](Target* t) { std::get<std::vector<Target*>>(res).push_back(t); }...); - - if (std::holds_alternative<std::initializer_list<HBasicBlock*>>(blks)) { - for (HBasicBlock* blk : std::get<std::initializer_list<HBasicBlock*>>(blks)) { - vis.VisitBasicBlock(blk); - } - } else if (std::holds_alternative<std::nullopt_t>(blks)) { - vis.VisitInsertionOrder(); - } else { - vis.VisitBasicBlock(std::get<HBasicBlock*>(blks)); - } - return res; -} - -template <typename... Target> -std::tuple<Target*...> FindSingleInstructions( - HGraph* graph, - std::variant<std::nullopt_t, HBasicBlock*, std::initializer_list<HBasicBlock*>> blks = - std::nullopt) { - std::tuple<Target*...> res; - PatternMatchGraphVisitor vis(graph, [&](Target* t) { - EXPECT_EQ(std::get<Target*>(res), nullptr) - << *std::get<Target*>(res) << " already found but found " << *t << "!"; - std::get<Target*>(res) = t; - }...); - if (std::holds_alternative<std::initializer_list<HBasicBlock*>>(blks)) { - for (HBasicBlock* blk : std::get<std::initializer_list<HBasicBlock*>>(blks)) { - vis.VisitBasicBlock(blk); - } - } else if (std::holds_alternative<std::nullopt_t>(blks)) { - vis.VisitInsertionOrder(); - } else { - vis.VisitBasicBlock(std::get<HBasicBlock*>(blks)); - } - return res; -} - -template <typename Target> -Target* FindSingleInstruction( - HGraph* graph, - std::variant<std::nullopt_t, HBasicBlock*, std::initializer_list<HBasicBlock*>> blks = - std::nullopt) { - return std::get<Target*>(FindSingleInstructions<Target>(graph, blks)); -} - -template<typename Iter, typename Func> -typename Iter::value_type FindOrNull(Iter begin, Iter end, Func func) { - static_assert(std::is_pointer_v<typename Iter::value_type>); - auto it = std::find_if(begin, end, func); - if (it == end) { - return nullptr; - } else { - return *it; - } -} - -// // ENTRY -// Obj new_inst = new Obj(); -// new_inst.foo = 12; -// Obj obj; -// Obj out; -// int first; -// if (param0) { -// // ESCAPE_ROUTE -// if (param1) { -// // LEFT_START -// if (param2) { -// // LEFT_LEFT -// obj = new_inst; -// } else { -// // LEFT_RIGHT -// obj = obj_param; -// } -// // LEFT_MERGE -// // technically the phi is enough to cause an escape but might as well be -// // thorough. -// // obj = phi[new_inst, param] -// escape(obj); -// out = obj; -// } else { -// // RIGHT -// out = obj_param; -// } -// // EXIT -// // Can't do anything with this since we don't have good tracking for the heap-locations -// // out = phi[param, phi[new_inst, param]] -// first = out.foo -// } else { -// new_inst.foo = 15; -// first = 13; -// } -// // first = phi[out.foo, 13] -// return first + new_inst.foo; -TEST_F(LoadStoreEliminationTest, PartialPhiPropagation) { - VariableSizedHandleScope vshs(Thread::Current()); - CreateGraph(&vshs); - AdjacencyListGraph blks(SetupFromAdjacencyList("entry", - "exit", - {{"entry", "escape_route"}, - {"entry", "noescape_route"}, - {"escape_route", "left"}, - {"escape_route", "right"}, - {"left", "left_left"}, - {"left", "left_right"}, - {"left_left", "left_merge"}, - {"left_right", "left_merge"}, - {"left_merge", "escape_end"}, - {"right", "escape_end"}, - {"escape_end", "breturn"}, - {"noescape_route", "breturn"}, - {"breturn", "exit"}})); -#define GET_BLOCK(name) HBasicBlock* name = blks.Get(#name) - GET_BLOCK(entry); - GET_BLOCK(exit); - GET_BLOCK(breturn); - GET_BLOCK(left); - GET_BLOCK(right); - GET_BLOCK(left_left); - GET_BLOCK(left_right); - GET_BLOCK(left_merge); - GET_BLOCK(escape_end); - GET_BLOCK(escape_route); - GET_BLOCK(noescape_route); -#undef GET_BLOCK - EnsurePredecessorOrder(escape_end, {left_merge, right}); - EnsurePredecessorOrder(left_merge, {left_left, left_right}); - EnsurePredecessorOrder(breturn, {escape_end, noescape_route}); - HInstruction* param0 = MakeParam(DataType::Type::kBool); - HInstruction* param1 = MakeParam(DataType::Type::kBool); - HInstruction* param2 = MakeParam(DataType::Type::kBool); - HInstruction* obj_param = MakeParam(DataType::Type::kReference); - HInstruction* c12 = graph_->GetIntConstant(12); - HInstruction* c13 = graph_->GetIntConstant(13); - HInstruction* c15 = graph_->GetIntConstant(15); - - HInstruction* cls = MakeClassLoad(); - HInstruction* new_inst = MakeNewInstance(cls); - HInstruction* store = MakeIFieldSet(new_inst, c12, MemberOffset(32)); - HInstruction* if_param0 = new (GetAllocator()) HIf(param0); - entry->AddInstruction(cls); - entry->AddInstruction(new_inst); - entry->AddInstruction(store); - entry->AddInstruction(if_param0); - ManuallyBuildEnvFor(cls, {}); - new_inst->CopyEnvironmentFrom(cls->GetEnvironment()); - - HInstruction* store_noescape = MakeIFieldSet(new_inst, c15, MemberOffset(32)); - noescape_route->AddInstruction(store_noescape); - noescape_route->AddInstruction(new (GetAllocator()) HGoto()); - - escape_route->AddInstruction(new (GetAllocator()) HIf(param1)); - - HInstruction* if_left = new (GetAllocator()) HIf(param2); - left->AddInstruction(if_left); - - HInstruction* goto_left_left = new (GetAllocator()) HGoto(); - left_left->AddInstruction(goto_left_left); - - HInstruction* goto_left_right = new (GetAllocator()) HGoto(); - left_right->AddInstruction(goto_left_right); - - HPhi* left_phi = MakePhi({obj_param, new_inst}); - HInstruction* call_left = MakeInvoke(DataType::Type::kVoid, { left_phi }); - HInstruction* goto_left_merge = new (GetAllocator()) HGoto(); - left_merge->AddPhi(left_phi); - left_merge->AddInstruction(call_left); - left_merge->AddInstruction(goto_left_merge); - left_phi->SetCanBeNull(true); - call_left->CopyEnvironmentFrom(cls->GetEnvironment()); - - HInstruction* goto_right = new (GetAllocator()) HGoto(); - right->AddInstruction(goto_right); - - HPhi* escape_end_phi = MakePhi({left_phi, obj_param}); - HInstruction* read_escape_end = - MakeIFieldGet(escape_end_phi, DataType::Type::kInt32, MemberOffset(32)); - HInstruction* goto_escape_end = new (GetAllocator()) HGoto(); - escape_end->AddPhi(escape_end_phi); - escape_end->AddInstruction(read_escape_end); - escape_end->AddInstruction(goto_escape_end); - - HPhi* return_phi = MakePhi({read_escape_end, c13}); - HInstruction* read_exit = MakeIFieldGet(new_inst, DataType::Type::kInt32, MemberOffset(32)); - HInstruction* add_exit = new (GetAllocator()) HAdd(DataType::Type::kInt32, return_phi, read_exit); - HInstruction* return_exit = new (GetAllocator()) HReturn(add_exit); - breturn->AddPhi(return_phi); - breturn->AddInstruction(read_exit); - breturn->AddInstruction(add_exit); - breturn->AddInstruction(return_exit); - - SetupExit(exit); - - // PerformLSE expects this to be empty. - graph_->ClearDominanceInformation(); - LOG(INFO) << "Pre LSE " << blks; - PerformLSEWithPartial(); - LOG(INFO) << "Post LSE " << blks; - - HPredicatedInstanceFieldGet* pred_get = - FindSingleInstruction<HPredicatedInstanceFieldGet>(graph_); - std::vector<HPhi*> all_return_phis; - std::tie(all_return_phis) = FindAllInstructions<HPhi>(graph_, breturn); - EXPECT_EQ(all_return_phis.size(), 3u); - EXPECT_INS_RETAINED(return_phi); - EXPECT_TRUE(std::find(all_return_phis.begin(), all_return_phis.end(), return_phi) != - all_return_phis.end()); - HPhi* instance_phi = - FindOrNull(all_return_phis.begin(), all_return_phis.end(), [&](HPhi* phi) { - return phi != return_phi && phi->GetType() == DataType::Type::kReference; - }); - ASSERT_NE(instance_phi, nullptr); - HPhi* value_phi = FindOrNull(all_return_phis.begin(), all_return_phis.end(), [&](HPhi* phi) { - return phi != return_phi && phi->GetType() == DataType::Type::kInt32; - }); - ASSERT_NE(value_phi, nullptr); - EXPECT_INS_EQ( - instance_phi->InputAt(0), - FindSingleInstruction<HNewInstance>(graph_, escape_route->GetSinglePredecessor())); - // Check materialize block - EXPECT_INS_EQ(FindSingleInstruction<HInstanceFieldSet>( - graph_, escape_route->GetSinglePredecessor()) - ->InputAt(1), - c12); - - EXPECT_INS_EQ(instance_phi->InputAt(1), graph_->GetNullConstant()); - EXPECT_INS_EQ(value_phi->InputAt(0), graph_->GetIntConstant(0)); - EXPECT_INS_EQ(value_phi->InputAt(1), c15); - EXPECT_INS_REMOVED(store_noescape); - EXPECT_INS_EQ(pred_get->GetTarget(), instance_phi); - EXPECT_INS_EQ(pred_get->GetDefaultValue(), value_phi); -} - -// // ENTRY -// // To be moved -// // NB Order important. By having alloc and store of obj1 before obj2 that -// // ensure we'll build the materialization for obj1 first (just due to how -// // we iterate.) -// obj1 = new Obj(); -// obj2 = new Obj(); // has env[obj1] -// // Swap the order of these -// obj1.foo = param_obj1; -// obj2.foo = param_obj2; -// if (param1) { -// // LEFT -// obj2.foo = obj1; -// if (param2) { -// // LEFT_LEFT -// escape(obj2); -// } else {} -// } else {} -// return select(param3, obj1.foo, obj2.foo); -// EXIT -TEST_P(OrderDependentTestGroup, PredicatedUse) { - VariableSizedHandleScope vshs(Thread::Current()); - CreateGraph(&vshs); - AdjacencyListGraph blks(SetupFromAdjacencyList("entry", - "exit", - {{"entry", "left"}, - {"entry", "right"}, - {"left", "left_left"}, - {"left", "left_right"}, - {"left_left", "left_end"}, - {"left_right", "left_end"}, - {"left_end", "breturn"}, - {"right", "breturn"}, - {"breturn", "exit"}})); -#define GET_BLOCK(name) HBasicBlock* name = blks.Get(#name) - GET_BLOCK(entry); - GET_BLOCK(exit); - GET_BLOCK(breturn); - GET_BLOCK(right); - GET_BLOCK(left); - GET_BLOCK(left_left); - GET_BLOCK(left_right); - GET_BLOCK(left_end); -#undef GET_BLOCK - TestOrder order = GetParam(); - EnsurePredecessorOrder(breturn, {left_end, right}); - EnsurePredecessorOrder(left_end, {left_left, left_right}); - HInstruction* param1 = MakeParam(DataType::Type::kBool); - HInstruction* param2 = MakeParam(DataType::Type::kBool); - HInstruction* param3 = MakeParam(DataType::Type::kBool); - HInstruction* param_obj1 = MakeParam(DataType::Type::kReference); - HInstruction* param_obj2 = MakeParam(DataType::Type::kReference); - - HInstruction* cls1 = MakeClassLoad(); - HInstruction* cls2 = MakeClassLoad(); - HInstruction* new_inst1 = MakeNewInstance(cls1); - HInstruction* new_inst2 = MakeNewInstance(cls2); - HInstruction* store1 = MakeIFieldSet(new_inst1, param_obj1, MemberOffset(32)); - HInstruction* store2 = MakeIFieldSet(new_inst2, param_obj2, MemberOffset(32)); - HInstruction* null_const = graph_->GetNullConstant(); - HInstruction* if_inst = new (GetAllocator()) HIf(param1); - entry->AddInstruction(cls1); - entry->AddInstruction(cls2); - entry->AddInstruction(new_inst1); - entry->AddInstruction(new_inst2); - if (order == TestOrder::kSameAsAlloc) { - entry->AddInstruction(store1); - entry->AddInstruction(store2); - } else { - entry->AddInstruction(store2); - entry->AddInstruction(store1); - } - entry->AddInstruction(if_inst); - ManuallyBuildEnvFor(cls1, {}); - cls2->CopyEnvironmentFrom(cls1->GetEnvironment()); - new_inst1->CopyEnvironmentFrom(cls1->GetEnvironment()); - new_inst2->CopyEnvironmentFrom(cls1->GetEnvironment()); - - // This is the escape of new_inst1 - HInstruction* store_left = MakeIFieldSet(new_inst2, new_inst1, MemberOffset(32)); - HInstruction* if_left = new (GetAllocator()) HIf(param2); - left->AddInstruction(store_left); - left->AddInstruction(if_left); - - HInstruction* call_left_left = MakeInvoke(DataType::Type::kVoid, { new_inst2 }); - HInstruction* goto_left_left = new (GetAllocator()) HGoto(); - left_left->AddInstruction(call_left_left); - left_left->AddInstruction(goto_left_left); - call_left_left->CopyEnvironmentFrom(new_inst2->GetEnvironment()); - - left_right->AddInstruction(new (GetAllocator()) HGoto()); - left_end->AddInstruction(new (GetAllocator()) HGoto()); - - right->AddInstruction(new (GetAllocator()) HGoto()); - - // Used to distinguish the pred-gets without having to dig through the - // multiple phi layers. - constexpr uint32_t kRead1DexPc = 10; - constexpr uint32_t kRead2DexPc = 20; - HInstruction* read1 = - MakeIFieldGet(new_inst1, DataType::Type::kReference, MemberOffset(32), kRead1DexPc); - read1->SetReferenceTypeInfo( - ReferenceTypeInfo::CreateUnchecked(graph_->GetHandleCache()->GetObjectClassHandle(), false)); - HInstruction* read2 = - MakeIFieldGet(new_inst2, DataType::Type::kReference, MemberOffset(32), kRead2DexPc); - read2->SetReferenceTypeInfo( - ReferenceTypeInfo::CreateUnchecked(graph_->GetHandleCache()->GetObjectClassHandle(), false)); - HInstruction* sel_return = new (GetAllocator()) HSelect(param3, read1, read2, 0); - HInstruction* return_exit = new (GetAllocator()) HReturn(sel_return); - breturn->AddInstruction(read1); - breturn->AddInstruction(read2); - breturn->AddInstruction(sel_return); - breturn->AddInstruction(return_exit); - - SetupExit(exit); - - // PerformLSE expects this to be empty. - graph_->ClearDominanceInformation(); - LOG(INFO) << "Pre LSE " << blks; - PerformLSEWithPartial(); - LOG(INFO) << "Post LSE " << blks; - - EXPECT_INS_RETAINED(call_left_left); - EXPECT_INS_REMOVED(read1); - EXPECT_INS_REMOVED(read2); - EXPECT_INS_REMOVED(new_inst1); - EXPECT_INS_REMOVED(new_inst2); - EXPECT_TRUE(new_inst1->GetUses().empty()) << *new_inst1 << " " << new_inst1->GetUses(); - EXPECT_TRUE(new_inst2->GetUses().empty()) << *new_inst2 << " " << new_inst2->GetUses(); - EXPECT_INS_RETAINED(sel_return); - // Make sure the selector is the same - EXPECT_INS_EQ(sel_return->InputAt(2), param3); - std::vector<HPredicatedInstanceFieldGet*> pred_gets; - std::tie(pred_gets) = FindAllInstructions<HPredicatedInstanceFieldGet>(graph_, breturn); - HPredicatedInstanceFieldGet* pred1 = FindOrNull(pred_gets.begin(), pred_gets.end(), [&](auto i) { - return i->GetDexPc() == kRead1DexPc; - }); - HPredicatedInstanceFieldGet* pred2 = FindOrNull(pred_gets.begin(), pred_gets.end(), [&](auto i) { - return i->GetDexPc() == kRead2DexPc; - }); - ASSERT_NE(pred1, nullptr); - ASSERT_NE(pred2, nullptr); - EXPECT_INS_EQ(sel_return->InputAt(0), pred2); - EXPECT_INS_EQ(sel_return->InputAt(1), pred1); - // Check targets - EXPECT_TRUE(pred1->GetTarget()->IsPhi()) << pred1->DumpWithArgs(); - EXPECT_TRUE(pred2->GetTarget()->IsPhi()) << pred2->DumpWithArgs(); - HInstruction* mat1 = FindSingleInstruction<HNewInstance>(graph_, left->GetSinglePredecessor()); - HInstruction* mat2 = - FindSingleInstruction<HNewInstance>(graph_, left_left->GetSinglePredecessor()); - EXPECT_INS_EQ(pred1->GetTarget()->InputAt(0), mat1); - EXPECT_INS_EQ(pred1->GetTarget()->InputAt(1), null_const); - EXPECT_TRUE(pred2->GetTarget()->InputAt(0)->IsPhi()) << pred2->DumpWithArgs(); - EXPECT_INS_EQ(pred2->GetTarget()->InputAt(0)->InputAt(0), mat2); - EXPECT_INS_EQ(pred2->GetTarget()->InputAt(0)->InputAt(1), null_const); - EXPECT_INS_EQ(pred2->GetTarget()->InputAt(1), null_const); - // Check default values. - EXPECT_TRUE(pred1->GetDefaultValue()->IsPhi()) << pred1->DumpWithArgs(); - EXPECT_TRUE(pred2->GetDefaultValue()->IsPhi()) << pred2->DumpWithArgs(); - EXPECT_INS_EQ(pred1->GetDefaultValue()->InputAt(0), null_const); - EXPECT_INS_EQ(pred1->GetDefaultValue()->InputAt(1), param_obj1); - EXPECT_TRUE(pred2->GetDefaultValue()->InputAt(0)->IsPhi()) << pred2->DumpWithArgs(); - EXPECT_INS_EQ(pred2->GetDefaultValue()->InputAt(0)->InputAt(0), null_const); - EXPECT_INS_EQ(pred2->GetDefaultValue()->InputAt(0)->InputAt(1), mat1); - EXPECT_INS_EQ(pred2->GetDefaultValue()->InputAt(1), param_obj2); -} - -// // ENTRY -// // To be moved -// // NB Order important. By having alloc and store of obj1 before obj2 that -// // ensure we'll build the materialization for obj1 first (just due to how -// // we iterate.) -// obj1 = new Obj(); -// obj.foo = 12; -// obj2 = new Obj(); // has env[obj1] -// obj2.foo = 15; -// if (param1) { -// // LEFT -// // Need to update env to nullptr -// escape(obj1/2); -// if (param2) { -// // LEFT_LEFT -// escape(obj2/1); -// } else {} -// } else {} -// return obj1.foo + obj2.foo; -// EXIT -TEST_P(OrderDependentTestGroup, PredicatedEnvUse) { - VariableSizedHandleScope vshs(Thread::Current()); - CreateGraph(&vshs); - AdjacencyListGraph blks(SetupFromAdjacencyList("entry", - "exit", - {{"entry", "left"}, - {"entry", "right"}, - {"left", "left_left"}, - {"left", "left_right"}, - {"left_left", "left_end"}, - {"left_right", "left_end"}, - {"left_end", "breturn"}, - {"right", "breturn"}, - {"breturn", "exit"}})); -#define GET_BLOCK(name) HBasicBlock* name = blks.Get(#name) - GET_BLOCK(entry); - GET_BLOCK(exit); - GET_BLOCK(breturn); - GET_BLOCK(right); - GET_BLOCK(left); - GET_BLOCK(left_left); - GET_BLOCK(left_right); - GET_BLOCK(left_end); -#undef GET_BLOCK - TestOrder order = GetParam(); - EnsurePredecessorOrder(breturn, {left_end, right}); - EnsurePredecessorOrder(left_end, {left_left, left_right}); - HInstruction* param1 = MakeParam(DataType::Type::kBool); - HInstruction* param2 = MakeParam(DataType::Type::kBool); - HInstruction* c12 = graph_->GetIntConstant(12); - HInstruction* c15 = graph_->GetIntConstant(15); - - HInstruction* cls1 = MakeClassLoad(); - HInstruction* cls2 = MakeClassLoad(); - HInstruction* new_inst1 = MakeNewInstance(cls1); - HInstruction* store1 = MakeIFieldSet(new_inst1, c12, MemberOffset(32)); - HInstruction* new_inst2 = MakeNewInstance(cls2); - HInstruction* store2 = MakeIFieldSet(new_inst2, c15, MemberOffset(32)); - HInstruction* if_inst = new (GetAllocator()) HIf(param1); - entry->AddInstruction(cls1); - entry->AddInstruction(cls2); - entry->AddInstruction(new_inst1); - entry->AddInstruction(store1); - entry->AddInstruction(new_inst2); - entry->AddInstruction(store2); - entry->AddInstruction(if_inst); - ManuallyBuildEnvFor(cls1, {}); - cls2->CopyEnvironmentFrom(cls1->GetEnvironment()); - new_inst1->CopyEnvironmentFrom(cls1->GetEnvironment()); - ManuallyBuildEnvFor(new_inst2, {new_inst1}); - - HInstruction* first_inst = new_inst1; - HInstruction* second_inst = new_inst2; - - if (order == TestOrder::kReverseOfAlloc) { - std::swap(first_inst, second_inst); - } - - HInstruction* call_left = MakeInvoke(DataType::Type::kVoid, { first_inst }); - HInstruction* if_left = new (GetAllocator()) HIf(param2); - left->AddInstruction(call_left); - left->AddInstruction(if_left); - call_left->CopyEnvironmentFrom(new_inst2->GetEnvironment()); - - HInstruction* call_left_left = MakeInvoke(DataType::Type::kVoid, { second_inst }); - HInstruction* goto_left_left = new (GetAllocator()) HGoto(); - left_left->AddInstruction(call_left_left); - left_left->AddInstruction(goto_left_left); - call_left_left->CopyEnvironmentFrom(new_inst2->GetEnvironment()); - - left_right->AddInstruction(new (GetAllocator()) HGoto()); - left_end->AddInstruction(new (GetAllocator()) HGoto()); - - right->AddInstruction(new (GetAllocator()) HGoto()); - - HInstruction* read1 = MakeIFieldGet(new_inst1, DataType::Type::kInt32, MemberOffset(32)); - HInstruction* read2 = MakeIFieldGet(new_inst2, DataType::Type::kInt32, MemberOffset(32)); - HInstruction* add_return = new (GetAllocator()) HAdd(DataType::Type::kInt32, read1, read2); - HInstruction* return_exit = new (GetAllocator()) HReturn(add_return); - breturn->AddInstruction(read1); - breturn->AddInstruction(read2); - breturn->AddInstruction(add_return); - breturn->AddInstruction(return_exit); - - SetupExit(exit); - - // PerformLSE expects this to be empty. - graph_->ClearDominanceInformation(); - LOG(INFO) << "Pre LSE " << blks; - PerformLSEWithPartial(); - LOG(INFO) << "Post LSE " << blks; - - HNewInstance* moved_new_inst1; - HInstanceFieldSet* moved_set1; - HNewInstance* moved_new_inst2; - HInstanceFieldSet* moved_set2; - HBasicBlock* first_mat_block = left->GetSinglePredecessor(); - HBasicBlock* second_mat_block = left_left->GetSinglePredecessor(); - if (order == TestOrder::kReverseOfAlloc) { - std::swap(first_mat_block, second_mat_block); - } - std::tie(moved_new_inst1, moved_set1) = - FindSingleInstructions<HNewInstance, HInstanceFieldSet>(graph_, first_mat_block); - std::tie(moved_new_inst2, moved_set2) = - FindSingleInstructions<HNewInstance, HInstanceFieldSet>(graph_, second_mat_block); - std::vector<HPredicatedInstanceFieldGet*> pred_gets; - std::vector<HPhi*> phis; - std::tie(pred_gets, phis) = FindAllInstructions<HPredicatedInstanceFieldGet, HPhi>(graph_); - EXPECT_NE(moved_new_inst1, nullptr); - EXPECT_NE(moved_new_inst2, nullptr); - EXPECT_NE(moved_set1, nullptr); - EXPECT_NE(moved_set2, nullptr); - EXPECT_INS_EQ(moved_set1->InputAt(1), c12); - EXPECT_INS_EQ(moved_set2->InputAt(1), c15); - EXPECT_INS_RETAINED(call_left); - EXPECT_INS_RETAINED(call_left_left); - EXPECT_INS_REMOVED(store1); - EXPECT_INS_REMOVED(store2); - EXPECT_INS_REMOVED(read1); - EXPECT_INS_REMOVED(read2); - EXPECT_INS_EQ(moved_new_inst2->GetEnvironment()->GetInstructionAt(0), - order == TestOrder::kSameAsAlloc - ? moved_new_inst1 - : static_cast<HInstruction*>(graph_->GetNullConstant())); -} - -// // ENTRY -// obj1 = new Obj1(); -// obj2 = new Obj2(); -// val1 = 3; -// val2 = 13; -// // The exact order the stores are written affects what the order we perform -// // partial LSE on the values -// obj1/2.field = val1/2; -// obj2/1.field = val2/1; -// if (parameter_value) { -// // LEFT -// escape(obj1); -// escape(obj2); -// } else { -// // RIGHT -// // ELIMINATE -// obj1.field = 2; -// obj2.field = 12; -// } -// EXIT -// predicated-ELIMINATE -// return obj1.field + obj2.field -TEST_P(OrderDependentTestGroup, FieldSetOrderEnv) { - VariableSizedHandleScope vshs(Thread::Current()); - CreateGraph(/*handles=*/&vshs); - AdjacencyListGraph blks(SetupFromAdjacencyList("entry", - "exit", - {{"entry", "left"}, - {"entry", "right"}, - {"left", "breturn"}, - {"right", "breturn"}, - {"breturn", "exit"}})); -#define GET_BLOCK(name) HBasicBlock* name = blks.Get(#name) - GET_BLOCK(entry); - GET_BLOCK(exit); - GET_BLOCK(breturn); - GET_BLOCK(left); - GET_BLOCK(right); -#undef GET_BLOCK - TestOrder order = GetParam(); - EnsurePredecessorOrder(breturn, {left, right}); - HInstruction* bool_value = MakeParam(DataType::Type::kBool); - HInstruction* c2 = graph_->GetIntConstant(2); - HInstruction* c3 = graph_->GetIntConstant(3); - HInstruction* c12 = graph_->GetIntConstant(12); - HInstruction* c13 = graph_->GetIntConstant(13); - - HInstruction* cls1 = MakeClassLoad(); - HInstruction* cls2 = MakeClassLoad(); - HInstruction* new_inst1 = MakeNewInstance(cls1); - HInstruction* new_inst2 = MakeNewInstance(cls2); - HInstruction* write_entry1 = MakeIFieldSet(new_inst1, c3, MemberOffset(32)); - HInstruction* write_entry2 = MakeIFieldSet(new_inst2, c13, MemberOffset(32)); - HInstruction* if_inst = new (GetAllocator()) HIf(bool_value); - entry->AddInstruction(cls1); - entry->AddInstruction(cls2); - entry->AddInstruction(new_inst1); - entry->AddInstruction(new_inst2); - if (order == TestOrder::kSameAsAlloc) { - entry->AddInstruction(write_entry1); - entry->AddInstruction(write_entry2); - } else { - entry->AddInstruction(write_entry2); - entry->AddInstruction(write_entry1); - } - entry->AddInstruction(if_inst); - ManuallyBuildEnvFor(cls1, {}); - cls2->CopyEnvironmentFrom(cls1->GetEnvironment()); - new_inst1->CopyEnvironmentFrom(cls1->GetEnvironment()); - ManuallyBuildEnvFor(new_inst2, {new_inst1}); - - HInstruction* call_left1 = MakeInvoke(DataType::Type::kVoid, { new_inst1 }); - HInstruction* call_left2 = MakeInvoke(DataType::Type::kVoid, { new_inst2 }); - HInstruction* goto_left = new (GetAllocator()) HGoto(); - left->AddInstruction(call_left1); - left->AddInstruction(call_left2); - left->AddInstruction(goto_left); - call_left1->CopyEnvironmentFrom(cls1->GetEnvironment()); - call_left2->CopyEnvironmentFrom(cls1->GetEnvironment()); - - HInstruction* write_right1 = MakeIFieldSet(new_inst1, c2, MemberOffset(32)); - HInstruction* write_right2 = MakeIFieldSet(new_inst2, c12, MemberOffset(32)); - HInstruction* goto_right = new (GetAllocator()) HGoto(); - right->AddInstruction(write_right1); - right->AddInstruction(write_right2); - right->AddInstruction(goto_right); - - HInstruction* read_bottom1 = MakeIFieldGet(new_inst1, DataType::Type::kInt32, MemberOffset(32)); - HInstruction* read_bottom2 = MakeIFieldGet(new_inst2, DataType::Type::kInt32, MemberOffset(32)); - HInstruction* combine = - new (GetAllocator()) HAdd(DataType::Type::kInt32, read_bottom1, read_bottom2); - HInstruction* return_exit = new (GetAllocator()) HReturn(combine); - breturn->AddInstruction(read_bottom1); - breturn->AddInstruction(read_bottom2); - breturn->AddInstruction(combine); - breturn->AddInstruction(return_exit); - - SetupExit(exit); - - // PerformLSE expects this to be empty. - graph_->ClearDominanceInformation(); - LOG(INFO) << "Pre LSE " << blks; - PerformLSEWithPartial(); - LOG(INFO) << "Post LSE " << blks; - - EXPECT_INS_REMOVED(write_entry1); - EXPECT_INS_REMOVED(write_entry2); - EXPECT_INS_REMOVED(read_bottom1); - EXPECT_INS_REMOVED(read_bottom2); - EXPECT_INS_REMOVED(write_right1); - EXPECT_INS_REMOVED(write_right2); - EXPECT_INS_RETAINED(call_left1); - EXPECT_INS_RETAINED(call_left2); - std::vector<HPhi*> merges; - std::vector<HPredicatedInstanceFieldGet*> pred_gets; - std::vector<HNewInstance*> materializations; - std::tie(merges, pred_gets) = - FindAllInstructions<HPhi, HPredicatedInstanceFieldGet>(graph_, breturn); - std::tie(materializations) = FindAllInstructions<HNewInstance>(graph_); - ASSERT_EQ(merges.size(), 4u); - ASSERT_EQ(pred_gets.size(), 2u); - ASSERT_EQ(materializations.size(), 2u); - HPhi* merge_value_return1 = FindOrNull(merges.begin(), merges.end(), [&](HPhi* p) { - return p->GetType() == DataType::Type::kInt32 && p->InputAt(1) == c2; - }); - HPhi* merge_value_return2 = FindOrNull(merges.begin(), merges.end(), [&](HPhi* p) { - return p->GetType() == DataType::Type::kInt32 && p->InputAt(1) == c12; - }); - HNewInstance* mat_alloc1 = FindOrNull(materializations.begin(), - materializations.end(), - [&](HNewInstance* n) { return n->InputAt(0) == cls1; }); - HNewInstance* mat_alloc2 = FindOrNull(materializations.begin(), - materializations.end(), - [&](HNewInstance* n) { return n->InputAt(0) == cls2; }); - ASSERT_NE(mat_alloc1, nullptr); - ASSERT_NE(mat_alloc2, nullptr); - HPhi* merge_alloc1 = FindOrNull(merges.begin(), merges.end(), [&](HPhi* p) { - return p->GetType() == DataType::Type::kReference && p->InputAt(0) == mat_alloc1; - }); - HPhi* merge_alloc2 = FindOrNull(merges.begin(), merges.end(), [&](HPhi* p) { - return p->GetType() == DataType::Type::kReference && p->InputAt(0) == mat_alloc2; - }); - ASSERT_NE(merge_alloc1, nullptr); - HPredicatedInstanceFieldGet* pred_get1 = - FindOrNull(pred_gets.begin(), pred_gets.end(), [&](HPredicatedInstanceFieldGet* pg) { - return pg->GetTarget() == merge_alloc1; - }); - ASSERT_NE(merge_alloc2, nullptr); - HPredicatedInstanceFieldGet* pred_get2 = - FindOrNull(pred_gets.begin(), pred_gets.end(), [&](HPredicatedInstanceFieldGet* pg) { - return pg->GetTarget() == merge_alloc2; - }); - ASSERT_NE(merge_value_return1, nullptr); - ASSERT_NE(merge_value_return2, nullptr); - EXPECT_INS_EQ(merge_alloc1->InputAt(1), graph_->GetNullConstant()); - EXPECT_INS_EQ(merge_alloc2->InputAt(1), graph_->GetNullConstant()); - ASSERT_NE(pred_get1, nullptr); - EXPECT_INS_EQ(pred_get1->GetTarget(), merge_alloc1); - EXPECT_INS_EQ(pred_get1->GetDefaultValue(), merge_value_return1) - << " pred-get is: " << *pred_get1; - EXPECT_INS_EQ(merge_value_return1->InputAt(0), graph_->GetIntConstant(0)) - << " merge val is: " << *merge_value_return1; - EXPECT_INS_EQ(merge_value_return1->InputAt(1), c2) << " merge val is: " << *merge_value_return1; - ASSERT_NE(pred_get2, nullptr); - EXPECT_INS_EQ(pred_get2->GetTarget(), merge_alloc2); - EXPECT_INS_EQ(pred_get2->GetDefaultValue(), merge_value_return2) - << " pred-get is: " << *pred_get2; - EXPECT_INS_EQ(merge_value_return2->InputAt(0), graph_->GetIntConstant(0)) - << " merge val is: " << *merge_value_return1; - EXPECT_INS_EQ(merge_value_return2->InputAt(1), c12) << " merge val is: " << *merge_value_return1; - EXPECT_INS_EQ(mat_alloc2->GetEnvironment()->GetInstructionAt(0), mat_alloc1); -} - -// // TODO We can compile this better if we are better able to understand lifetimes. -// // ENTRY -// obj1 = new Obj1(); -// obj2 = new Obj2(); -// // The exact order the stores are written affects what the order we perform -// // partial LSE on the values -// obj{1,2}.var = param_obj; -// obj{2,1}.var = param_obj; -// if (param_1) { -// // EARLY_RETURN -// return; -// } -// // escape of obj1 -// obj2.var = obj1; -// if (param_2) { -// // escape of obj2 with a materialization that uses obj1 -// escape(obj2); -// } -// // EXIT -// return; -TEST_P(OrderDependentTestGroup, MaterializationMovedUse) { - VariableSizedHandleScope vshs(Thread::Current()); - CreateGraph(/*handles=*/&vshs); - AdjacencyListGraph blks(SetupFromAdjacencyList("entry", - "exit", - {{"entry", "early_return"}, - {"early_return", "exit"}, - {"entry", "escape_1"}, - {"escape_1", "escape_2"}, - {"escape_1", "escape_1_crit_break"}, - {"escape_1_crit_break", "exit"}, - {"escape_2", "exit"}})); -#define GET_BLOCK(name) HBasicBlock* name = blks.Get(#name) - GET_BLOCK(entry); - GET_BLOCK(exit); - GET_BLOCK(early_return); - GET_BLOCK(escape_1); - GET_BLOCK(escape_1_crit_break); - GET_BLOCK(escape_2); -#undef GET_BLOCK - TestOrder order = GetParam(); - HInstruction* param_1 = MakeParam(DataType::Type::kBool); - HInstruction* param_2 = MakeParam(DataType::Type::kBool); - HInstruction* param_obj = MakeParam(DataType::Type::kReference); - - HInstruction* cls1 = MakeClassLoad(); - HInstruction* cls2 = MakeClassLoad(); - HInstruction* new_inst1 = MakeNewInstance(cls1); - HInstruction* new_inst2 = MakeNewInstance(cls2); - HInstruction* write_entry1 = MakeIFieldSet(new_inst1, param_obj, MemberOffset(32)); - HInstruction* write_entry2 = MakeIFieldSet(new_inst2, param_obj, MemberOffset(32)); - HInstruction* if_inst = new (GetAllocator()) HIf(param_1); - entry->AddInstruction(cls1); - entry->AddInstruction(cls2); - entry->AddInstruction(new_inst1); - entry->AddInstruction(new_inst2); - if (order == TestOrder::kSameAsAlloc) { - entry->AddInstruction(write_entry1); - entry->AddInstruction(write_entry2); - } else { - entry->AddInstruction(write_entry2); - entry->AddInstruction(write_entry1); - } - entry->AddInstruction(if_inst); - ManuallyBuildEnvFor(cls1, {}); - cls2->CopyEnvironmentFrom(cls1->GetEnvironment()); - new_inst1->CopyEnvironmentFrom(cls1->GetEnvironment()); - new_inst2->CopyEnvironmentFrom(cls1->GetEnvironment()); - - early_return->AddInstruction(new (GetAllocator()) HReturnVoid()); - - HInstruction* escape_1_set = MakeIFieldSet(new_inst2, new_inst1, MemberOffset(32)); - HInstruction* escape_1_if = new (GetAllocator()) HIf(param_2); - escape_1->AddInstruction(escape_1_set); - escape_1->AddInstruction(escape_1_if); - - escape_1_crit_break->AddInstruction(new (GetAllocator()) HReturnVoid()); - - HInstruction* escape_2_call = MakeInvoke(DataType::Type::kVoid, {new_inst2}); - HInstruction* escape_2_return = new (GetAllocator()) HReturnVoid(); - escape_2->AddInstruction(escape_2_call); - escape_2->AddInstruction(escape_2_return); - escape_2_call->CopyEnvironmentFrom(cls1->GetEnvironment()); - - SetupExit(exit); - - // PerformLSE expects this to be empty. - graph_->ClearDominanceInformation(); - LOG(INFO) << "Pre LSE " << blks; - PerformLSEWithPartial(); - LOG(INFO) << "Post LSE " << blks; - - EXPECT_INS_REMOVED(new_inst1); - EXPECT_INS_REMOVED(new_inst2); - EXPECT_INS_REMOVED(write_entry1); - EXPECT_INS_REMOVED(write_entry2); - EXPECT_INS_REMOVED(escape_1_set); - EXPECT_INS_RETAINED(escape_2_call); - - HInstruction* obj1_mat = - FindSingleInstruction<HNewInstance>(graph_, escape_1->GetSinglePredecessor()); - HInstruction* obj1_set = - FindSingleInstruction<HInstanceFieldSet>(graph_, escape_1->GetSinglePredecessor()); - HInstruction* obj2_mat = - FindSingleInstruction<HNewInstance>(graph_, escape_2->GetSinglePredecessor()); - HInstruction* obj2_set = - FindSingleInstruction<HInstanceFieldSet>(graph_, escape_2->GetSinglePredecessor()); - ASSERT_TRUE(obj1_mat != nullptr); - ASSERT_TRUE(obj2_mat != nullptr); - ASSERT_TRUE(obj1_set != nullptr); - ASSERT_TRUE(obj2_set != nullptr); - EXPECT_INS_EQ(obj1_set->InputAt(0), obj1_mat); - EXPECT_INS_EQ(obj1_set->InputAt(1), param_obj); - EXPECT_INS_EQ(obj2_set->InputAt(0), obj2_mat); - EXPECT_INS_EQ(obj2_set->InputAt(1), obj1_mat); -} - -INSTANTIATE_TEST_SUITE_P(LoadStoreEliminationTest, - OrderDependentTestGroup, - testing::Values(TestOrder::kSameAsAlloc, TestOrder::kReverseOfAlloc)); - -// // ENTRY -// // To be moved -// obj = new Obj(); -// obj.foo = 12; -// if (parameter_value) { -// // LEFT -// escape(obj); -// } else {} -// EXIT -TEST_F(LoadStoreEliminationTest, MovePredicatedAlloc) { - VariableSizedHandleScope vshs(Thread::Current()); - CreateGraph(&vshs); - AdjacencyListGraph blks(SetupFromAdjacencyList("entry", - "exit", - {{"entry", "left"}, - {"entry", "right"}, - {"right", "breturn"}, - {"left", "breturn"}, - {"breturn", "exit"}})); -#define GET_BLOCK(name) HBasicBlock* name = blks.Get(#name) - GET_BLOCK(entry); - GET_BLOCK(exit); - GET_BLOCK(breturn); - GET_BLOCK(left); - GET_BLOCK(right); -#undef GET_BLOCK - EnsurePredecessorOrder(breturn, {left, right}); - HInstruction* bool_value = MakeParam(DataType::Type::kBool); - HInstruction* c12 = graph_->GetIntConstant(12); - - HInstruction* cls = MakeClassLoad(); - HInstruction* new_inst = MakeNewInstance(cls); - HInstruction* store = MakeIFieldSet(new_inst, c12, MemberOffset(32)); - HInstruction* if_inst = new (GetAllocator()) HIf(bool_value); - entry->AddInstruction(cls); - entry->AddInstruction(new_inst); - entry->AddInstruction(store); - entry->AddInstruction(if_inst); - ManuallyBuildEnvFor(cls, {}); - new_inst->CopyEnvironmentFrom(cls->GetEnvironment()); - - HInstruction* call_left = MakeInvoke(DataType::Type::kVoid, { new_inst }); - HInstruction* goto_left = new (GetAllocator()) HGoto(); - left->AddInstruction(call_left); - left->AddInstruction(goto_left); - call_left->CopyEnvironmentFrom(cls->GetEnvironment()); - - right->AddInstruction(new (GetAllocator()) HGoto()); - - HInstruction* return_exit = new (GetAllocator()) HReturnVoid(); - breturn->AddInstruction(return_exit); - - SetupExit(exit); - - // PerformLSE expects this to be empty. - graph_->ClearDominanceInformation(); - LOG(INFO) << "Pre LSE " << blks; - PerformLSEWithPartial(); - LOG(INFO) << "Post LSE " << blks; - - HNewInstance* moved_new_inst = nullptr; - HInstanceFieldSet* moved_set = nullptr; - std::tie(moved_new_inst, moved_set) = - FindSingleInstructions<HNewInstance, HInstanceFieldSet>(graph_); - EXPECT_NE(moved_new_inst, nullptr); - EXPECT_NE(moved_set, nullptr); - EXPECT_INS_RETAINED(call_left); - // store removed or moved. - EXPECT_NE(store->GetBlock(), entry); - // New-inst removed or moved. - EXPECT_NE(new_inst->GetBlock(), entry); - EXPECT_INS_EQ(moved_set->InputAt(0), moved_new_inst); - EXPECT_INS_EQ(moved_set->InputAt(1), c12); -} - -// // ENTRY -// // To be moved -// obj = new Obj(); -// obj.foo = 12; -// if (parameter_value) { -// // LEFT -// escape(obj); -// } -// EXIT -// int a = obj.foo; -// obj.foo = 13; -// noescape(); -// int b = obj.foo; -// obj.foo = 14; -// noescape(); -// int c = obj.foo; -// obj.foo = 15; -// noescape(); -// return a + b + c -TEST_F(LoadStoreEliminationTest, MutiPartialLoadStore) { - VariableSizedHandleScope vshs(Thread::Current()); - CreateGraph(&vshs); - AdjacencyListGraph blks(SetupFromAdjacencyList("entry", - "exit", - {{"entry", "left"}, - {"entry", "right"}, - {"right", "breturn"}, - {"left", "breturn"}, - {"breturn", "exit"}})); -#define GET_BLOCK(name) HBasicBlock* name = blks.Get(#name) - GET_BLOCK(entry); - GET_BLOCK(exit); - GET_BLOCK(breturn); - GET_BLOCK(left); - GET_BLOCK(right); -#undef GET_BLOCK - EnsurePredecessorOrder(breturn, {left, right}); - HInstruction* bool_value = MakeParam(DataType::Type::kBool); - HInstruction* c12 = graph_->GetIntConstant(12); - HInstruction* c13 = graph_->GetIntConstant(13); - HInstruction* c14 = graph_->GetIntConstant(14); - HInstruction* c15 = graph_->GetIntConstant(15); - - HInstruction* cls = MakeClassLoad(); - HInstruction* new_inst = MakeNewInstance(cls); - HInstruction* store = MakeIFieldSet(new_inst, c12, MemberOffset(32)); - HInstruction* if_inst = new (GetAllocator()) HIf(bool_value); - entry->AddInstruction(cls); - entry->AddInstruction(new_inst); - entry->AddInstruction(store); - entry->AddInstruction(if_inst); - ManuallyBuildEnvFor(cls, {}); - new_inst->CopyEnvironmentFrom(cls->GetEnvironment()); - - HInstruction* call_left = MakeInvoke(DataType::Type::kVoid, { new_inst }); - HInstruction* goto_left = new (GetAllocator()) HGoto(); - left->AddInstruction(call_left); - left->AddInstruction(goto_left); - call_left->CopyEnvironmentFrom(cls->GetEnvironment()); - - HInstruction* goto_right = new (GetAllocator()) HGoto(); - right->AddInstruction(goto_right); - - HInstruction* a_val = MakeIFieldGet(new_inst, DataType::Type::kInt32, MemberOffset(32)); - HInstruction* a_reset = MakeIFieldSet(new_inst, c13, MemberOffset(32)); - HInstruction* a_noescape = MakeInvoke(DataType::Type::kVoid, {}); - HInstruction* b_val = MakeIFieldGet(new_inst, DataType::Type::kInt32, MemberOffset(32)); - HInstruction* b_reset = MakeIFieldSet(new_inst, c14, MemberOffset(32)); - HInstruction* b_noescape = MakeInvoke(DataType::Type::kVoid, {}); - HInstruction* c_val = MakeIFieldGet(new_inst, DataType::Type::kInt32, MemberOffset(32)); - HInstruction* c_reset = MakeIFieldSet(new_inst, c15, MemberOffset(32)); - HInstruction* c_noescape = MakeInvoke(DataType::Type::kVoid, {}); - HInstruction* add_1_exit = new (GetAllocator()) HAdd(DataType::Type::kInt32, a_val, b_val); - HInstruction* add_2_exit = new (GetAllocator()) HAdd(DataType::Type::kInt32, c_val, add_1_exit); - HInstruction* return_exit = new (GetAllocator()) HReturn(add_2_exit); - breturn->AddInstruction(a_val); - breturn->AddInstruction(a_reset); - breturn->AddInstruction(a_noescape); - breturn->AddInstruction(b_val); - breturn->AddInstruction(b_reset); - breturn->AddInstruction(b_noescape); - breturn->AddInstruction(c_val); - breturn->AddInstruction(c_reset); - breturn->AddInstruction(c_noescape); - breturn->AddInstruction(add_1_exit); - breturn->AddInstruction(add_2_exit); - breturn->AddInstruction(return_exit); - ManuallyBuildEnvFor(a_noescape, {new_inst, a_val}); - ManuallyBuildEnvFor(b_noescape, {new_inst, a_val, b_val}); - ManuallyBuildEnvFor(c_noescape, {new_inst, a_val, b_val, c_val}); - - SetupExit(exit); - - // PerformLSE expects this to be empty. - graph_->ClearDominanceInformation(); - LOG(INFO) << "Pre LSE " << blks; - PerformLSEWithPartial(); - LOG(INFO) << "Post LSE " << blks; - - HNewInstance* moved_new_inst = nullptr; - HInstanceFieldSet* moved_set = nullptr; - std::tie(moved_new_inst, moved_set) = - FindSingleInstructions<HNewInstance, HInstanceFieldSet>(graph_, left->GetSinglePredecessor()); - std::vector<HPredicatedInstanceFieldGet*> pred_gets; - std::vector<HInstanceFieldSet*> pred_sets; - std::vector<HPhi*> return_phis; - std::tie(return_phis, pred_gets, pred_sets) = - FindAllInstructions<HPhi, HPredicatedInstanceFieldGet, HInstanceFieldSet>(graph_, breturn); - ASSERT_EQ(return_phis.size(), 2u); - HPhi* inst_phi = return_phis[0]; - HPhi* val_phi = return_phis[1]; - if (inst_phi->GetType() != DataType::Type::kReference) { - std::swap(inst_phi, val_phi); - } - ASSERT_NE(moved_new_inst, nullptr); - EXPECT_INS_EQ(inst_phi->InputAt(0), moved_new_inst); - EXPECT_INS_EQ(inst_phi->InputAt(1), graph_->GetNullConstant()); - EXPECT_INS_EQ(val_phi->InputAt(0), graph_->GetIntConstant(0)); - EXPECT_EQ(val_phi->InputAt(1), c12); - ASSERT_EQ(pred_gets.size(), 3u); - ASSERT_EQ(pred_gets.size(), pred_sets.size()); - std::vector<HInstruction*> set_values{c13, c14, c15}; - std::vector<HInstruction*> get_values{val_phi, c13, c14}; - ASSERT_NE(moved_set, nullptr); - EXPECT_INS_EQ(moved_set->InputAt(0), moved_new_inst); - EXPECT_INS_EQ(moved_set->InputAt(1), c12); - EXPECT_INS_RETAINED(call_left); - // store removed or moved. - EXPECT_NE(store->GetBlock(), entry); - // New-inst removed or moved. - EXPECT_NE(new_inst->GetBlock(), entry); - for (auto [get, val] : ZipLeft(MakeIterationRange(pred_gets), MakeIterationRange(get_values))) { - EXPECT_INS_EQ(get->GetDefaultValue(), val); - } - for (auto [set, val] : ZipLeft(MakeIterationRange(pred_sets), MakeIterationRange(set_values))) { - EXPECT_INS_EQ(set->InputAt(1), val); - EXPECT_TRUE(set->GetIsPredicatedSet()) << *set; - } - EXPECT_INS_RETAINED(a_noescape); - EXPECT_INS_RETAINED(b_noescape); - EXPECT_INS_RETAINED(c_noescape); - EXPECT_INS_EQ(add_1_exit->InputAt(0), pred_gets[0]); - EXPECT_INS_EQ(add_1_exit->InputAt(1), pred_gets[1]); - EXPECT_INS_EQ(add_2_exit->InputAt(0), pred_gets[2]); - - EXPECT_EQ(a_noescape->GetEnvironment()->Size(), 2u); - EXPECT_INS_EQ(a_noescape->GetEnvironment()->GetInstructionAt(0), inst_phi); - EXPECT_INS_EQ(a_noescape->GetEnvironment()->GetInstructionAt(1), pred_gets[0]); - EXPECT_EQ(b_noescape->GetEnvironment()->Size(), 3u); - EXPECT_INS_EQ(b_noescape->GetEnvironment()->GetInstructionAt(0), inst_phi); - EXPECT_INS_EQ(b_noescape->GetEnvironment()->GetInstructionAt(1), pred_gets[0]); - EXPECT_INS_EQ(b_noescape->GetEnvironment()->GetInstructionAt(2), pred_gets[1]); - EXPECT_EQ(c_noescape->GetEnvironment()->Size(), 4u); - EXPECT_INS_EQ(c_noescape->GetEnvironment()->GetInstructionAt(0), inst_phi); - EXPECT_INS_EQ(c_noescape->GetEnvironment()->GetInstructionAt(1), pred_gets[0]); - EXPECT_INS_EQ(c_noescape->GetEnvironment()->GetInstructionAt(2), pred_gets[1]); - EXPECT_INS_EQ(c_noescape->GetEnvironment()->GetInstructionAt(3), pred_gets[2]); -} - -// // ENTRY -// // To be moved -// obj = new Obj(); -// obj.foo = 12; -// int a = obj.foo; -// obj.foo = 13; -// noescape(); -// int b = obj.foo; -// obj.foo = 14; -// noescape(); -// int c = obj.foo; -// obj.foo = 15; -// noescape(); -// if (parameter_value) { -// // LEFT -// escape(obj); -// } -// EXIT -// return a + b + c + obj.foo -TEST_F(LoadStoreEliminationTest, MutiPartialLoadStore2) { - VariableSizedHandleScope vshs(Thread::Current()); - CreateGraph(&vshs); - // Need to have an actual entry block since we check env-layout and the way we - // add constants would screw this up otherwise. - AdjacencyListGraph blks(SetupFromAdjacencyList("start", - "exit", - {{"start", "entry"}, - {"entry", "left"}, - {"entry", "right"}, - {"right", "breturn"}, - {"left", "breturn"}, - {"breturn", "exit"}})); -#define GET_BLOCK(name) HBasicBlock* name = blks.Get(#name) - GET_BLOCK(start); - GET_BLOCK(entry); - GET_BLOCK(exit); - GET_BLOCK(breturn); - GET_BLOCK(left); - GET_BLOCK(right); -#undef GET_BLOCK - EnsurePredecessorOrder(breturn, {left, right}); - HInstruction* bool_value = MakeParam(DataType::Type::kBool); - HInstruction* c12 = graph_->GetIntConstant(12); - HInstruction* c13 = graph_->GetIntConstant(13); - HInstruction* c14 = graph_->GetIntConstant(14); - HInstruction* c15 = graph_->GetIntConstant(15); - - HInstruction* start_suspend = new (GetAllocator()) HSuspendCheck(); - HInstruction* start_goto = new (GetAllocator()) HGoto(); - - start->AddInstruction(start_suspend); - start->AddInstruction(start_goto); - ManuallyBuildEnvFor(start_suspend, {}); - - HInstruction* cls = MakeClassLoad(); - HInstruction* new_inst = MakeNewInstance(cls); - HInstruction* store = MakeIFieldSet(new_inst, c12, MemberOffset(32)); - - HInstruction* a_val = MakeIFieldGet(new_inst, DataType::Type::kInt32, MemberOffset(32)); - HInstruction* a_reset = MakeIFieldSet(new_inst, c13, MemberOffset(32)); - HInstruction* a_noescape = MakeInvoke(DataType::Type::kVoid, {}); - HInstruction* b_val = MakeIFieldGet(new_inst, DataType::Type::kInt32, MemberOffset(32)); - HInstruction* b_reset = MakeIFieldSet(new_inst, c14, MemberOffset(32)); - HInstruction* b_noescape = MakeInvoke(DataType::Type::kVoid, {}); - HInstruction* c_val = MakeIFieldGet(new_inst, DataType::Type::kInt32, MemberOffset(32)); - HInstruction* c_reset = MakeIFieldSet(new_inst, c15, MemberOffset(32)); - HInstruction* c_noescape = MakeInvoke(DataType::Type::kVoid, {}); - HInstruction* if_inst = new (GetAllocator()) HIf(bool_value); - entry->AddInstruction(cls); - entry->AddInstruction(new_inst); - entry->AddInstruction(store); - entry->AddInstruction(a_val); - entry->AddInstruction(a_reset); - entry->AddInstruction(a_noescape); - entry->AddInstruction(b_val); - entry->AddInstruction(b_reset); - entry->AddInstruction(b_noescape); - entry->AddInstruction(c_val); - entry->AddInstruction(c_reset); - entry->AddInstruction(c_noescape); - entry->AddInstruction(if_inst); - ManuallyBuildEnvFor(cls, {}); - new_inst->CopyEnvironmentFrom(cls->GetEnvironment()); - ManuallyBuildEnvFor(a_noescape, {new_inst, a_val}); - ManuallyBuildEnvFor(b_noescape, {new_inst, a_val, b_val}); - ManuallyBuildEnvFor(c_noescape, {new_inst, a_val, b_val, c_val}); - - HInstruction* call_left = MakeInvoke(DataType::Type::kVoid, { new_inst }); - HInstruction* goto_left = new (GetAllocator()) HGoto(); - left->AddInstruction(call_left); - left->AddInstruction(goto_left); - call_left->CopyEnvironmentFrom(c_noescape->GetEnvironment()); - - HInstruction* goto_right = new (GetAllocator()) HGoto(); - right->AddInstruction(goto_right); - - HInstruction* val_exit = MakeIFieldGet(new_inst, DataType::Type::kInt32, MemberOffset(32)); - HInstruction* add_1_exit = new (GetAllocator()) HAdd(DataType::Type::kInt32, a_val, b_val); - HInstruction* add_2_exit = new (GetAllocator()) HAdd(DataType::Type::kInt32, c_val, add_1_exit); - HInstruction* add_3_exit = - new (GetAllocator()) HAdd(DataType::Type::kInt32, val_exit, add_2_exit); - HInstruction* return_exit = new (GetAllocator()) HReturn(add_3_exit); - breturn->AddInstruction(val_exit); - breturn->AddInstruction(add_1_exit); - breturn->AddInstruction(add_2_exit); - breturn->AddInstruction(add_3_exit); - breturn->AddInstruction(return_exit); - - SetupExit(exit); - - // PerformLSE expects this to be empty. - graph_->ClearDominanceInformation(); - LOG(INFO) << "Pre LSE " << blks; - PerformLSEWithPartial(); - LOG(INFO) << "Post LSE " << blks; - - HNewInstance* moved_new_inst = nullptr; - HInstanceFieldSet* moved_set = nullptr; - std::tie(moved_new_inst, moved_set) = - FindSingleInstructions<HNewInstance, HInstanceFieldSet>(graph_, left->GetSinglePredecessor()); - std::vector<HPredicatedInstanceFieldGet*> pred_gets; - std::vector<HInstanceFieldSet*> pred_sets; - std::vector<HPhi*> return_phis; - std::tie(return_phis, pred_gets, pred_sets) = - FindAllInstructions<HPhi, HPredicatedInstanceFieldGet, HInstanceFieldSet>(graph_, breturn); - ASSERT_EQ(return_phis.size(), 2u); - HPhi* inst_phi = return_phis[0]; - HPhi* val_phi = return_phis[1]; - if (inst_phi->GetType() != DataType::Type::kReference) { - std::swap(inst_phi, val_phi); - } - ASSERT_NE(moved_new_inst, nullptr); - EXPECT_INS_EQ(inst_phi->InputAt(0), moved_new_inst); - EXPECT_INS_EQ(inst_phi->InputAt(1), graph_->GetNullConstant()); - EXPECT_INS_EQ(val_phi->InputAt(0), graph_->GetIntConstant(0)); - EXPECT_INS_EQ(val_phi->InputAt(1), c15); - ASSERT_EQ(pred_gets.size(), 1u); - ASSERT_EQ(pred_sets.size(), 0u); - ASSERT_NE(moved_set, nullptr); - EXPECT_INS_EQ(moved_set->InputAt(0), moved_new_inst); - EXPECT_INS_EQ(moved_set->InputAt(1), c15); - EXPECT_INS_RETAINED(call_left); - // store removed or moved. - EXPECT_NE(store->GetBlock(), entry); - // New-inst removed or moved. - EXPECT_NE(new_inst->GetBlock(), entry); - EXPECT_INS_REMOVED(a_val); - EXPECT_INS_REMOVED(b_val); - EXPECT_INS_REMOVED(c_val); - EXPECT_INS_RETAINED(a_noescape); - EXPECT_INS_RETAINED(b_noescape); - EXPECT_INS_RETAINED(c_noescape); - EXPECT_INS_EQ(add_1_exit->InputAt(0), c12); - EXPECT_INS_EQ(add_1_exit->InputAt(1), c13); - EXPECT_INS_EQ(add_2_exit->InputAt(0), c14); - EXPECT_INS_EQ(add_2_exit->InputAt(1), add_1_exit); - EXPECT_INS_EQ(add_3_exit->InputAt(0), pred_gets[0]); - EXPECT_INS_EQ(pred_gets[0]->GetDefaultValue(), val_phi); - EXPECT_INS_EQ(add_3_exit->InputAt(1), add_2_exit); - EXPECT_EQ(a_noescape->GetEnvironment()->Size(), 2u); - EXPECT_INS_EQ(a_noescape->GetEnvironment()->GetInstructionAt(0), graph_->GetNullConstant()); - EXPECT_INS_EQ(a_noescape->GetEnvironment()->GetInstructionAt(1), c12); - EXPECT_EQ(b_noescape->GetEnvironment()->Size(), 3u); - EXPECT_INS_EQ(b_noescape->GetEnvironment()->GetInstructionAt(0), graph_->GetNullConstant()); - EXPECT_INS_EQ(b_noescape->GetEnvironment()->GetInstructionAt(1), c12); - EXPECT_INS_EQ(b_noescape->GetEnvironment()->GetInstructionAt(2), c13); - EXPECT_EQ(c_noescape->GetEnvironment()->Size(), 4u); - EXPECT_INS_EQ(c_noescape->GetEnvironment()->GetInstructionAt(0), graph_->GetNullConstant()); - EXPECT_INS_EQ(c_noescape->GetEnvironment()->GetInstructionAt(1), c12); - EXPECT_INS_EQ(c_noescape->GetEnvironment()->GetInstructionAt(2), c13); - EXPECT_INS_EQ(c_noescape->GetEnvironment()->GetInstructionAt(3), c14); -} - -// // ENTRY -// // To be moved -// obj = new Obj(); -// // Transforms required for creation non-trivial and unimportant -// if (parameter_value) { -// obj.foo = 10 -// } else { -// obj.foo = 12; -// } -// if (parameter_value_2) { -// escape(obj); -// } -// EXIT -TEST_F(LoadStoreEliminationTest, MovePredicatedAlloc2) { - VariableSizedHandleScope vshs(Thread::Current()); - CreateGraph(&vshs); - AdjacencyListGraph blks(SetupFromAdjacencyList("entry", - "exit", - {{"entry", "left_set"}, - {"entry", "right_set"}, - {"left_set", "merge_crit_break"}, - {"right_set", "merge_crit_break"}, - {"merge_crit_break", "merge"}, - {"merge", "escape"}, - {"escape", "breturn"}, - {"merge", "breturn"}, - {"breturn", "exit"}})); -#define GET_BLOCK(name) HBasicBlock* name = blks.Get(#name) - GET_BLOCK(entry); - GET_BLOCK(exit); - GET_BLOCK(breturn); - GET_BLOCK(left_set); - GET_BLOCK(right_set); - GET_BLOCK(merge); - GET_BLOCK(merge_crit_break); - GET_BLOCK(escape); -#undef GET_BLOCK - EnsurePredecessorOrder(breturn, {merge, escape}); - EnsurePredecessorOrder(merge_crit_break, {left_set, right_set}); - HInstruction* bool_value = MakeParam(DataType::Type::kBool); - HInstruction* bool_value_2 = MakeParam(DataType::Type::kBool); - HInstruction* c10 = graph_->GetIntConstant(10); - HInstruction* c12 = graph_->GetIntConstant(12); - - HInstruction* cls = MakeClassLoad(); - HInstruction* new_inst = MakeNewInstance(cls); - HInstruction* if_inst = new (GetAllocator()) HIf(bool_value); - entry->AddInstruction(cls); - entry->AddInstruction(new_inst); - entry->AddInstruction(if_inst); - ManuallyBuildEnvFor(cls, {}); - new_inst->CopyEnvironmentFrom(cls->GetEnvironment()); - - HInstruction* store_left = MakeIFieldSet(new_inst, c10, MemberOffset(32)); - HInstruction* goto_left = new (GetAllocator()) HGoto(); - left_set->AddInstruction(store_left); - left_set->AddInstruction(goto_left); - - HInstruction* store_right = MakeIFieldSet(new_inst, c12, MemberOffset(32)); - HInstruction* goto_right = new (GetAllocator()) HGoto(); - right_set->AddInstruction(store_right); - right_set->AddInstruction(goto_right); - - merge_crit_break->AddInstruction(new (GetAllocator()) HGoto()); - HInstruction* if_merge = new (GetAllocator()) HIf(bool_value_2); - merge->AddInstruction(if_merge); - - HInstruction* escape_instruction = MakeInvoke(DataType::Type::kVoid, { new_inst }); - HInstruction* escape_goto = new (GetAllocator()) HGoto(); - escape->AddInstruction(escape_instruction); - escape->AddInstruction(escape_goto); - escape_instruction->CopyEnvironmentFrom(cls->GetEnvironment()); - - HInstruction* return_exit = new (GetAllocator()) HReturnVoid(); - breturn->AddInstruction(return_exit); - - SetupExit(exit); - - // PerformLSE expects this to be empty. - graph_->ClearDominanceInformation(); - LOG(INFO) << "Pre LSE " << blks; - PerformLSEWithPartial(); - LOG(INFO) << "Post LSE " << blks; - - HNewInstance* moved_new_inst; - HInstanceFieldSet* moved_set; - std::tie(moved_new_inst, moved_set) = - FindSingleInstructions<HNewInstance, HInstanceFieldSet>(graph_); - HPhi* merge_phi = FindSingleInstruction<HPhi>(graph_, merge_crit_break); - HPhi* alloc_phi = FindSingleInstruction<HPhi>(graph_, breturn); - EXPECT_INS_EQ(moved_new_inst, moved_set->InputAt(0)); - ASSERT_NE(alloc_phi, nullptr); - EXPECT_EQ(alloc_phi->InputAt(0), graph_->GetNullConstant()) - << alloc_phi->GetBlock()->GetPredecessors()[0]->GetBlockId() << " " << *alloc_phi; - EXPECT_TRUE(alloc_phi->InputAt(1)->IsNewInstance()) << *alloc_phi; - ASSERT_NE(merge_phi, nullptr); - EXPECT_EQ(merge_phi->InputCount(), 2u); - EXPECT_INS_EQ(merge_phi->InputAt(0), c10); - EXPECT_INS_EQ(merge_phi->InputAt(1), c12); - EXPECT_TRUE(merge_phi->GetUses().HasExactlyOneElement()); - EXPECT_INS_EQ(merge_phi->GetUses().front().GetUser(), moved_set); - EXPECT_INS_RETAINED(escape_instruction); - EXPECT_INS_EQ(escape_instruction->InputAt(0), moved_new_inst); - // store removed or moved. - EXPECT_NE(store_left->GetBlock(), left_set); - EXPECT_NE(store_right->GetBlock(), left_set); - // New-inst removed or moved. - EXPECT_NE(new_inst->GetBlock(), entry); -} - -// // ENTRY -// // To be moved -// obj = new Obj(); -// switch(args) { -// default: -// return obj.a; -// case b: -// obj.a = 5; break; -// case c: -// obj.b = 4; break; -// } -// escape(obj); -// return obj.a; -// EXIT -TEST_F(LoadStoreEliminationTest, MovePredicatedAlloc3) { - VariableSizedHandleScope vshs(Thread::Current()); - CreateGraph(&vshs); - AdjacencyListGraph blks(SetupFromAdjacencyList("entry", - "exit", - {{"entry", "early_return"}, - {"entry", "set_one"}, - {"entry", "set_two"}, - {"early_return", "exit"}, - {"set_one", "escape"}, - {"set_two", "escape"}, - {"escape", "exit"}})); -#define GET_BLOCK(name) HBasicBlock* name = blks.Get(#name) - GET_BLOCK(entry); - GET_BLOCK(exit); - GET_BLOCK(escape); - GET_BLOCK(early_return); - GET_BLOCK(set_one); - GET_BLOCK(set_two); -#undef GET_BLOCK - EnsurePredecessorOrder(escape, {set_one, set_two}); - HInstruction* int_val = MakeParam(DataType::Type::kInt32); - HInstruction* c0 = graph_->GetIntConstant(0); - HInstruction* c4 = graph_->GetIntConstant(4); - HInstruction* c5 = graph_->GetIntConstant(5); - - HInstruction* cls = MakeClassLoad(); - HInstruction* new_inst = MakeNewInstance(cls); - HInstruction* switch_inst = new (GetAllocator()) HPackedSwitch(0, 2, int_val); - entry->AddInstruction(cls); - entry->AddInstruction(new_inst); - entry->AddInstruction(switch_inst); - ManuallyBuildEnvFor(cls, {}); - new_inst->CopyEnvironmentFrom(cls->GetEnvironment()); - - HInstruction* store_one = MakeIFieldSet(new_inst, c4, MemberOffset(32)); - HInstruction* goto_one = new (GetAllocator()) HGoto(); - set_one->AddInstruction(store_one); - set_one->AddInstruction(goto_one); - - HInstruction* store_two = MakeIFieldSet(new_inst, c5, MemberOffset(32)); - HInstruction* goto_two = new (GetAllocator()) HGoto(); - set_two->AddInstruction(store_two); - set_two->AddInstruction(goto_two); - - HInstruction* read_early = MakeIFieldGet(new_inst, DataType::Type::kInt32, MemberOffset(32)); - HInstruction* return_early = new (GetAllocator()) HReturn(read_early); - early_return->AddInstruction(read_early); - early_return->AddInstruction(return_early); - - HInstruction* escape_instruction = MakeInvoke(DataType::Type::kVoid, { new_inst }); - HInstruction* read_escape = MakeIFieldGet(new_inst, DataType::Type::kInt32, MemberOffset(32)); - HInstruction* return_escape = new (GetAllocator()) HReturn(read_escape); - escape->AddInstruction(escape_instruction); - escape->AddInstruction(read_escape); - escape->AddInstruction(return_escape); - escape_instruction->CopyEnvironmentFrom(cls->GetEnvironment()); - - SetupExit(exit); - - // PerformLSE expects this to be empty. - graph_->ClearDominanceInformation(); - LOG(INFO) << "Pre LSE " << blks; - PerformLSEWithPartial(); - LOG(INFO) << "Post LSE " << blks; - - // Each escaping switch path gets its own materialization block. - // Blocks: - // early_return(5) -> [exit(4)] - // entry(3) -> [early_return(5), <Unnamed>(9), <Unnamed>(10)] - // escape(8) -> [exit(4)] - // exit(4) -> [] - // set_one(6) -> [escape(8)] - // set_two(7) -> [escape(8)] - // <Unnamed>(10) -> [set_two(7)] - // <Unnamed>(9) -> [set_one(6)] - HBasicBlock* materialize_one = set_one->GetSinglePredecessor(); - HBasicBlock* materialize_two = set_two->GetSinglePredecessor(); - HNewInstance* materialization_ins_one = - FindSingleInstruction<HNewInstance>(graph_, materialize_one); - HNewInstance* materialization_ins_two = - FindSingleInstruction<HNewInstance>(graph_, materialize_two); - HPhi* new_phi = FindSingleInstruction<HPhi>(graph_, escape); - EXPECT_NE(materialization_ins_one, nullptr); - EXPECT_NE(materialization_ins_two, nullptr); - EXPECT_EQ(materialization_ins_one, new_phi->InputAt(0)) - << *materialization_ins_one << " vs " << *new_phi; - EXPECT_EQ(materialization_ins_two, new_phi->InputAt(1)) - << *materialization_ins_two << " vs " << *new_phi; - - EXPECT_INS_RETAINED(escape_instruction); - EXPECT_INS_RETAINED(read_escape); - EXPECT_EQ(read_escape->InputAt(0), new_phi) << *new_phi << " vs " << *read_escape->InputAt(0); - EXPECT_EQ(store_one->InputAt(0), materialization_ins_one); - EXPECT_EQ(store_two->InputAt(0), materialization_ins_two); - EXPECT_EQ(escape_instruction->InputAt(0), new_phi); - EXPECT_INS_REMOVED(read_early); - EXPECT_EQ(return_early->InputAt(0), c0); -} - -// // ENTRY -// // To be moved -// obj = new Obj(); -// switch(args) { -// case a: -// // set_one_and_escape -// obj.a = 5; -// escape(obj); -// // FALLTHROUGH -// case c: -// // set_two -// obj.a = 4; break; -// default: -// return obj.a; -// } -// escape(obj); -// return obj.a; -// EXIT -TEST_F(LoadStoreEliminationTest, MovePredicatedAlloc4) { - VariableSizedHandleScope vshs(Thread::Current()); - CreateGraph(&vshs); - // Break the critical edge between entry and set_two with the - // set_two_critical_break node. Graph simplification would do this for us if - // we didn't do it manually. This way we have a nice-name for debugging and - // testing. - AdjacencyListGraph blks(SetupFromAdjacencyList("entry", - "exit", - {{"entry", "early_return"}, - {"entry", "set_one_and_escape"}, - {"entry", "set_two_critical_break"}, - {"set_two_critical_break", "set_two"}, - {"early_return", "exit"}, - {"set_one_and_escape", "set_two"}, - {"set_two", "escape"}, - {"escape", "exit"}})); -#define GET_BLOCK(name) HBasicBlock* name = blks.Get(#name) - GET_BLOCK(entry); - GET_BLOCK(exit); - GET_BLOCK(escape); - GET_BLOCK(early_return); - GET_BLOCK(set_one_and_escape); - GET_BLOCK(set_two); - GET_BLOCK(set_two_critical_break); -#undef GET_BLOCK - EnsurePredecessorOrder(set_two, {set_one_and_escape, set_two_critical_break}); - HInstruction* int_val = MakeParam(DataType::Type::kInt32); - HInstruction* c0 = graph_->GetIntConstant(0); - HInstruction* c4 = graph_->GetIntConstant(4); - HInstruction* c5 = graph_->GetIntConstant(5); - - HInstruction* cls = MakeClassLoad(); - HInstruction* new_inst = MakeNewInstance(cls); - HInstruction* switch_inst = new (GetAllocator()) HPackedSwitch(0, 2, int_val); - entry->AddInstruction(cls); - entry->AddInstruction(new_inst); - entry->AddInstruction(switch_inst); - ManuallyBuildEnvFor(cls, {}); - new_inst->CopyEnvironmentFrom(cls->GetEnvironment()); - - HInstruction* store_one = MakeIFieldSet(new_inst, c4, MemberOffset(32)); - HInstruction* escape_one = MakeInvoke(DataType::Type::kVoid, { new_inst }); - HInstruction* goto_one = new (GetAllocator()) HGoto(); - set_one_and_escape->AddInstruction(store_one); - set_one_and_escape->AddInstruction(escape_one); - set_one_and_escape->AddInstruction(goto_one); - escape_one->CopyEnvironmentFrom(cls->GetEnvironment()); - - HInstruction* goto_crit_break = new (GetAllocator()) HGoto(); - set_two_critical_break->AddInstruction(goto_crit_break); - - HInstruction* store_two = MakeIFieldSet(new_inst, c5, MemberOffset(32)); - HInstruction* goto_two = new (GetAllocator()) HGoto(); - set_two->AddInstruction(store_two); - set_two->AddInstruction(goto_two); - - HInstruction* read_early = MakeIFieldGet(new_inst, DataType::Type::kInt32, MemberOffset(32)); - HInstruction* return_early = new (GetAllocator()) HReturn(read_early); - early_return->AddInstruction(read_early); - early_return->AddInstruction(return_early); - - HInstruction* escape_instruction = MakeInvoke(DataType::Type::kVoid, { new_inst }); - HInstruction* read_escape = MakeIFieldGet(new_inst, DataType::Type::kInt32, MemberOffset(32)); - HInstruction* return_escape = new (GetAllocator()) HReturn(read_escape); - escape->AddInstruction(escape_instruction); - escape->AddInstruction(read_escape); - escape->AddInstruction(return_escape); - escape_instruction->CopyEnvironmentFrom(cls->GetEnvironment()); - - SetupExit(exit); - - // PerformLSE expects this to be empty. - graph_->ClearDominanceInformation(); - LOG(INFO) << "Pre LSE " << blks; - PerformLSEWithPartial(); - LOG(INFO) << "Post LSE " << blks; - - EXPECT_INS_REMOVED(read_early); - EXPECT_EQ(return_early->InputAt(0), c0); - // Each escaping switch path gets its own materialization block. - // Blocks: - // early_return(5) -> [exit(4)] - // entry(3) -> [early_return(5), <Unnamed>(10), <Unnamed>(11)] - // escape(9) -> [exit(4)] - // exit(4) -> [] - // set_one_and_escape(6) -> [set_two(8)] - // set_two(8) -> [escape(9)] - // set_two_critical_break(7) -> [set_two(8)] - // <Unnamed>(11) -> [set_two_critical_break(7)] - // <Unnamed>(10) -> [set_one_and_escape(6)] - HBasicBlock* materialize_one = set_one_and_escape->GetSinglePredecessor(); - HBasicBlock* materialize_two = set_two_critical_break->GetSinglePredecessor(); - HNewInstance* materialization_ins_one = - FindSingleInstruction<HNewInstance>(graph_, materialize_one); - HNewInstance* materialization_ins_two = - FindSingleInstruction<HNewInstance>(graph_, materialize_two); - HPhi* new_phi = FindSingleInstruction<HPhi>(graph_, set_two); - ASSERT_NE(new_phi, nullptr); - ASSERT_NE(materialization_ins_one, nullptr); - ASSERT_NE(materialization_ins_two, nullptr); - EXPECT_INS_EQ(materialization_ins_one, new_phi->InputAt(0)); - EXPECT_INS_EQ(materialization_ins_two, new_phi->InputAt(1)); - - EXPECT_INS_EQ(store_one->InputAt(0), materialization_ins_one); - EXPECT_INS_EQ(store_two->InputAt(0), new_phi) << *store_two << " vs " << *new_phi; - EXPECT_INS_EQ(escape_instruction->InputAt(0), new_phi); - EXPECT_INS_RETAINED(escape_one); - EXPECT_INS_EQ(escape_one->InputAt(0), materialization_ins_one); - EXPECT_INS_RETAINED(escape_instruction); - EXPECT_INS_RETAINED(read_escape); - EXPECT_EQ(read_escape->InputAt(0), new_phi) << *new_phi << " vs " << *read_escape->InputAt(0); -} - -// // ENTRY -// // To be moved -// obj = new Obj(); -// switch(args) { -// case a: -// // set_one -// obj.a = 5; -// // nb passthrough -// case c: -// // set_two_and_escape -// obj.a += 4; -// escape(obj); -// break; -// default: -// obj.a = 10; -// } -// return obj.a; -// EXIT -TEST_F(LoadStoreEliminationTest, MovePredicatedAlloc5) { - VariableSizedHandleScope vshs(Thread::Current()); - CreateGraph(&vshs); - // Break the critical edge between entry and set_two with the - // set_two_critical_break node. Graph simplification would do this for us if - // we didn't do it manually. This way we have a nice-name for debugging and - // testing. - AdjacencyListGraph blks(SetupFromAdjacencyList("entry", - "exit", - {{"entry", "set_noescape"}, - {"entry", "set_one"}, - {"entry", "set_two_critical_break"}, - {"set_two_critical_break", "set_two_and_escape"}, - {"set_noescape", "breturn"}, - {"set_one", "set_two_and_escape"}, - {"set_two_and_escape", "breturn"}, - {"breturn", "exit"}})); -#define GET_BLOCK(name) HBasicBlock* name = blks.Get(#name) - GET_BLOCK(entry); - GET_BLOCK(exit); - GET_BLOCK(breturn); - GET_BLOCK(set_noescape); - GET_BLOCK(set_one); - GET_BLOCK(set_two_and_escape); - GET_BLOCK(set_two_critical_break); -#undef GET_BLOCK - EnsurePredecessorOrder(set_two_and_escape, {set_one, set_two_critical_break}); - EnsurePredecessorOrder(breturn, {set_two_and_escape, set_noescape}); - HInstruction* int_val = MakeParam(DataType::Type::kInt32); - HInstruction* c0 = graph_->GetIntConstant(0); - HInstruction* c4 = graph_->GetIntConstant(4); - HInstruction* c5 = graph_->GetIntConstant(5); - HInstruction* c10 = graph_->GetIntConstant(10); - - HInstruction* cls = MakeClassLoad(); - HInstruction* new_inst = MakeNewInstance(cls); - HInstruction* switch_inst = new (GetAllocator()) HPackedSwitch(0, 2, int_val); - entry->AddInstruction(cls); - entry->AddInstruction(new_inst); - entry->AddInstruction(switch_inst); - ManuallyBuildEnvFor(cls, {}); - new_inst->CopyEnvironmentFrom(cls->GetEnvironment()); - - HInstruction* store_one = MakeIFieldSet(new_inst, c5, MemberOffset(32)); - HInstruction* goto_one = new (GetAllocator()) HGoto(); - set_one->AddInstruction(store_one); - set_one->AddInstruction(goto_one); - - HInstruction* goto_crit_break = new (GetAllocator()) HGoto(); - set_two_critical_break->AddInstruction(goto_crit_break); - - HInstruction* get_two = MakeIFieldGet(new_inst, DataType::Type::kInt32, MemberOffset(32)); - HInstruction* add_two = new (GetAllocator()) HAdd(DataType::Type::kInt32, get_two, c4); - HInstruction* store_two = MakeIFieldSet(new_inst, add_two, MemberOffset(32)); - HInstruction* escape_two = MakeInvoke(DataType::Type::kVoid, {new_inst}); - HInstruction* goto_two = new (GetAllocator()) HGoto(); - set_two_and_escape->AddInstruction(get_two); - set_two_and_escape->AddInstruction(add_two); - set_two_and_escape->AddInstruction(store_two); - set_two_and_escape->AddInstruction(escape_two); - set_two_and_escape->AddInstruction(goto_two); - escape_two->CopyEnvironmentFrom(cls->GetEnvironment()); - - HInstruction* store_noescape = MakeIFieldSet(new_inst, c10, MemberOffset(32)); - HInstruction* goto_noescape = new (GetAllocator()) HGoto(); - set_noescape->AddInstruction(store_noescape); - set_noescape->AddInstruction(goto_noescape); - - HInstruction* read_breturn = MakeIFieldGet(new_inst, DataType::Type::kInt32, MemberOffset(32)); - HInstruction* return_breturn = new (GetAllocator()) HReturn(read_breturn); - breturn->AddInstruction(read_breturn); - breturn->AddInstruction(return_breturn); - - SetupExit(exit); - - // PerformLSE expects this to be empty. - graph_->ClearDominanceInformation(); - LOG(INFO) << "Pre LSE " << blks; - PerformLSEWithPartial(); - LOG(INFO) << "Post LSE " << blks; - - // Normal LSE can get rid of these two. - EXPECT_INS_REMOVED(store_one); - EXPECT_INS_REMOVED(get_two); - EXPECT_INS_RETAINED(add_two); - EXPECT_TRUE(add_two->InputAt(0)->IsPhi()); - EXPECT_INS_EQ(add_two->InputAt(0)->InputAt(0), c5); - EXPECT_INS_EQ(add_two->InputAt(0)->InputAt(1), c0); - EXPECT_INS_EQ(add_two->InputAt(1), c4); - - HBasicBlock* materialize_one = set_one->GetSinglePredecessor(); - HBasicBlock* materialize_two = set_two_critical_break->GetSinglePredecessor(); - HNewInstance* materialization_ins_one = - FindSingleInstruction<HNewInstance>(graph_, materialize_one); - HNewInstance* materialization_ins_two = - FindSingleInstruction<HNewInstance>(graph_, materialize_two); - std::vector<HPhi*> phis; - std::tie(phis) = FindAllInstructions<HPhi>(graph_, set_two_and_escape); - HPhi* new_phi = FindOrNull( - phis.begin(), phis.end(), [&](auto p) { return p->GetType() == DataType::Type::kReference; }); - ASSERT_NE(new_phi, nullptr); - ASSERT_NE(materialization_ins_one, nullptr); - ASSERT_NE(materialization_ins_two, nullptr); - EXPECT_INS_EQ(materialization_ins_one, new_phi->InputAt(0)); - EXPECT_INS_EQ(materialization_ins_two, new_phi->InputAt(1)); - - HPredicatedInstanceFieldGet* pred_get = - FindSingleInstruction<HPredicatedInstanceFieldGet>(graph_, breturn); - EXPECT_TRUE(pred_get->GetTarget()->IsPhi()); - EXPECT_INS_EQ(pred_get->GetTarget()->InputAt(0), new_phi); - EXPECT_INS_EQ(pred_get->GetTarget()->InputAt(1), graph_->GetNullConstant()); - - EXPECT_INS_EQ(pred_get->GetDefaultValue()->InputAt(0), c0); - EXPECT_INS_EQ(pred_get->GetDefaultValue()->InputAt(1), c10); + EXPECT_TRUE(IsRemoved(read_bottom)); + EXPECT_TRUE(IsRemoved(write_right)); + EXPECT_FALSE(IsRemoved(write_left)); + EXPECT_FALSE(IsRemoved(call_left)); } // // ENTRY @@ -4171,59 +2421,117 @@ TEST_F(LoadStoreEliminationTest, MovePredicatedAlloc5) { // } // EXIT TEST_F(LoadStoreEliminationTest, PartialLoadElimination3) { - VariableSizedHandleScope vshs(Thread::Current()); - CreateGraph(&vshs); + InitGraph(); AdjacencyListGraph blks(SetupFromAdjacencyList( "entry", "exit", - {{"entry", "left"}, {"entry", "right"}, {"left", "exit"}, {"right", "exit"}})); + { { "entry", "left" }, { "entry", "right" }, { "left", "exit" }, { "right", "exit" } })); #define GET_BLOCK(name) HBasicBlock* name = blks.Get(#name) GET_BLOCK(entry); GET_BLOCK(exit); GET_BLOCK(left); GET_BLOCK(right); #undef GET_BLOCK - HInstruction* bool_value = MakeParam(DataType::Type::kBool); + HInstruction* bool_value = new (GetAllocator()) + HParameterValue(graph_->GetDexFile(), dex::TypeIndex(1), 1, DataType::Type::kBool); HInstruction* c1 = graph_->GetIntConstant(1); HInstruction* c2 = graph_->GetIntConstant(2); - - HInstruction* cls = MakeClassLoad(); - HInstruction* new_inst = MakeNewInstance(cls); + HInstruction* cls = new (GetAllocator()) HLoadClass(graph_->GetCurrentMethod(), + dex::TypeIndex(10), + graph_->GetDexFile(), + ScopedNullHandle<mirror::Class>(), + false, + 0, + false); + HInstruction* new_inst = + new (GetAllocator()) HNewInstance(cls, + 0, + dex::TypeIndex(10), + graph_->GetDexFile(), + false, + QuickEntrypointEnum::kQuickAllocObjectInitialized); HInstruction* if_inst = new (GetAllocator()) HIf(bool_value); + entry->AddInstruction(bool_value); entry->AddInstruction(cls); entry->AddInstruction(new_inst); entry->AddInstruction(if_inst); - ManuallyBuildEnvFor(cls, {}); + ArenaVector<HInstruction*> current_locals({}, GetAllocator()->Adapter(kArenaAllocInstruction)); + ManuallyBuildEnvFor(cls, ¤t_locals); new_inst->CopyEnvironmentFrom(cls->GetEnvironment()); - HInstruction* write_left = MakeIFieldSet(new_inst, c1, MemberOffset(32)); - HInstruction* call_left = MakeInvoke(DataType::Type::kVoid, { new_inst }); - HInstruction* read_left = MakeIFieldGet(new_inst, DataType::Type::kInt32, MemberOffset(32)); + HInstruction* write_left = new (GetAllocator()) HInstanceFieldSet(new_inst, + c1, + nullptr, + DataType::Type::kInt32, + MemberOffset(32), + false, + 0, + 0, + graph_->GetDexFile(), + 0); + HInstruction* call_left = new (GetAllocator()) + HInvokeStaticOrDirect(GetAllocator(), + 1, + DataType::Type::kVoid, + 0, + { nullptr, 0 }, + nullptr, + {}, + InvokeType::kStatic, + { nullptr, 0 }, + HInvokeStaticOrDirect::ClinitCheckRequirement::kNone); + HInstruction* read_left = new (GetAllocator()) HInstanceFieldGet(new_inst, + nullptr, + DataType::Type::kInt32, + MemberOffset(32), + false, + 0, + 0, + graph_->GetDexFile(), + 0); HInstruction* return_left = new (GetAllocator()) HReturn(read_left); + call_left->AsInvoke()->SetRawInputAt(0, new_inst); left->AddInstruction(write_left); left->AddInstruction(call_left); left->AddInstruction(read_left); left->AddInstruction(return_left); call_left->CopyEnvironmentFrom(cls->GetEnvironment()); - HInstruction* write_right = MakeIFieldSet(new_inst, c2, MemberOffset(32)); - HInstruction* read_right = MakeIFieldGet(new_inst, DataType::Type::kInt32, MemberOffset(32)); + HInstruction* write_right = new (GetAllocator()) HInstanceFieldSet(new_inst, + c2, + nullptr, + DataType::Type::kInt32, + MemberOffset(32), + false, + 0, + 0, + graph_->GetDexFile(), + 0); + HInstruction* read_right = new (GetAllocator()) HInstanceFieldGet(new_inst, + nullptr, + DataType::Type::kInt32, + MemberOffset(32), + false, + 0, + 0, + graph_->GetDexFile(), + 0); HInstruction* return_right = new (GetAllocator()) HReturn(read_right); right->AddInstruction(write_right); right->AddInstruction(read_right); right->AddInstruction(return_right); - SetupExit(exit); - + HInstruction* exit_instruction = new (GetAllocator()) HExit(); + exit->AddInstruction(exit_instruction); // PerformLSE expects this to be empty. graph_->ClearDominanceInformation(); PerformLSE(); - EXPECT_INS_REMOVED(read_right); - EXPECT_INS_REMOVED(write_right); - EXPECT_INS_RETAINED(write_left); - EXPECT_INS_RETAINED(call_left); - EXPECT_INS_RETAINED(read_left); + EXPECT_TRUE(IsRemoved(read_right)); + EXPECT_TRUE(IsRemoved(write_right)); + EXPECT_FALSE(IsRemoved(write_left)); + EXPECT_FALSE(IsRemoved(call_left)); + EXPECT_FALSE(IsRemoved(read_left)); } // // ENTRY @@ -4248,18 +2556,17 @@ TEST_F(LoadStoreEliminationTest, PartialLoadElimination3) { // } // EXIT TEST_F(LoadStoreEliminationTest, PartialLoadElimination4) { - VariableSizedHandleScope vshs(Thread::Current()); - CreateGraph(&vshs); + InitGraph(); AdjacencyListGraph blks(SetupFromAdjacencyList("entry", "exit", - {{"entry", "entry_post"}, - {"entry_post", "right"}, - {"right", "exit"}, - {"entry_post", "left_pre"}, - {"left_pre", "left_loop"}, - {"left_loop", "left_loop"}, - {"left_loop", "left_finish"}, - {"left_finish", "exit"}})); + { { "entry", "entry_post" }, + { "entry_post", "right" }, + { "right", "exit" }, + { "entry_post", "left_pre" }, + { "left_pre", "left_loop" }, + { "left_loop", "left_loop" }, + { "left_loop", "left_finish" }, + { "left_finish", "exit" } })); #define GET_BLOCK(name) HBasicBlock* name = blks.Get(#name) GET_BLOCK(entry); GET_BLOCK(entry_post); @@ -4273,32 +2580,75 @@ TEST_F(LoadStoreEliminationTest, PartialLoadElimination4) { if (left_loop->GetSuccessors()[0] != left_finish) { left_loop->SwapSuccessors(); } - HInstruction* bool_value = MakeParam(DataType::Type::kBool); + HInstruction* bool_value = new (GetAllocator()) + HParameterValue(graph_->GetDexFile(), dex::TypeIndex(1), 1, DataType::Type::kBool); HInstruction* c1 = graph_->GetIntConstant(1); HInstruction* c2 = graph_->GetIntConstant(2); HInstruction* c3 = graph_->GetIntConstant(3); - - HInstruction* cls = MakeClassLoad(); - HInstruction* new_inst = MakeNewInstance(cls); + HInstruction* cls = new (GetAllocator()) HLoadClass(graph_->GetCurrentMethod(), + dex::TypeIndex(10), + graph_->GetDexFile(), + ScopedNullHandle<mirror::Class>(), + false, + 0, + false); + HInstruction* new_inst = + new (GetAllocator()) HNewInstance(cls, + 0, + dex::TypeIndex(10), + graph_->GetDexFile(), + false, + QuickEntrypointEnum::kQuickAllocObjectInitialized); HInstruction* goto_entry = new (GetAllocator()) HGoto(); + entry->AddInstruction(bool_value); entry->AddInstruction(cls); entry->AddInstruction(new_inst); entry->AddInstruction(goto_entry); - ManuallyBuildEnvFor(cls, {}); + ArenaVector<HInstruction*> current_locals({}, GetAllocator()->Adapter(kArenaAllocInstruction)); + ManuallyBuildEnvFor(cls, ¤t_locals); new_inst->CopyEnvironmentFrom(cls->GetEnvironment()); HInstruction* if_inst = new (GetAllocator()) HIf(bool_value); entry_post->AddInstruction(if_inst); - HInstruction* write_left_pre = MakeIFieldSet(new_inst, c1, MemberOffset(32)); + HInstruction* write_left_pre = new (GetAllocator()) HInstanceFieldSet(new_inst, + c1, + nullptr, + DataType::Type::kInt32, + MemberOffset(32), + false, + 0, + 0, + graph_->GetDexFile(), + 0); HInstruction* goto_left_pre = new (GetAllocator()) HGoto(); left_pre->AddInstruction(write_left_pre); left_pre->AddInstruction(goto_left_pre); HInstruction* suspend_left_loop = new (GetAllocator()) HSuspendCheck(); - HInstruction* call_left_loop = MakeInvoke(DataType::Type::kBool, { new_inst }); - HInstruction* write_left_loop = MakeIFieldSet(new_inst, c3, MemberOffset(32)); + HInstruction* call_left_loop = new (GetAllocator()) + HInvokeStaticOrDirect(GetAllocator(), + 1, + DataType::Type::kBool, + 0, + { nullptr, 0 }, + nullptr, + {}, + InvokeType::kStatic, + { nullptr, 0 }, + HInvokeStaticOrDirect::ClinitCheckRequirement::kNone); + HInstruction* write_left_loop = new (GetAllocator()) HInstanceFieldSet(new_inst, + c3, + nullptr, + DataType::Type::kInt32, + MemberOffset(32), + false, + 0, + 0, + graph_->GetDexFile(), + 0); HInstruction* if_left_loop = new (GetAllocator()) HIf(call_left_loop); + call_left_loop->AsInvoke()->SetRawInputAt(0, new_inst); left_loop->AddInstruction(suspend_left_loop); left_loop->AddInstruction(call_left_loop); left_loop->AddInstruction(write_left_loop); @@ -4306,30 +2656,55 @@ TEST_F(LoadStoreEliminationTest, PartialLoadElimination4) { suspend_left_loop->CopyEnvironmentFrom(cls->GetEnvironment()); call_left_loop->CopyEnvironmentFrom(cls->GetEnvironment()); - HInstruction* read_left_end = MakeIFieldGet(new_inst, DataType::Type::kInt32, MemberOffset(32)); + HInstruction* read_left_end = new (GetAllocator()) HInstanceFieldGet(new_inst, + nullptr, + DataType::Type::kInt32, + MemberOffset(32), + false, + 0, + 0, + graph_->GetDexFile(), + 0); HInstruction* return_left_end = new (GetAllocator()) HReturn(read_left_end); left_finish->AddInstruction(read_left_end); left_finish->AddInstruction(return_left_end); - HInstruction* write_right = MakeIFieldSet(new_inst, c2, MemberOffset(32)); - HInstruction* read_right = MakeIFieldGet(new_inst, DataType::Type::kInt32, MemberOffset(32)); + HInstruction* write_right = new (GetAllocator()) HInstanceFieldSet(new_inst, + c2, + nullptr, + DataType::Type::kInt32, + MemberOffset(32), + false, + 0, + 0, + graph_->GetDexFile(), + 0); + HInstruction* read_right = new (GetAllocator()) HInstanceFieldGet(new_inst, + nullptr, + DataType::Type::kInt32, + MemberOffset(32), + false, + 0, + 0, + graph_->GetDexFile(), + 0); HInstruction* return_right = new (GetAllocator()) HReturn(read_right); right->AddInstruction(write_right); right->AddInstruction(read_right); right->AddInstruction(return_right); - SetupExit(exit); - + HInstruction* exit_instruction = new (GetAllocator()) HExit(); + exit->AddInstruction(exit_instruction); // PerformLSE expects this to be empty. graph_->ClearDominanceInformation(); PerformLSE(); - EXPECT_INS_RETAINED(write_left_pre); - EXPECT_INS_REMOVED(read_right); - EXPECT_INS_REMOVED(write_right); - EXPECT_INS_RETAINED(write_left_loop); - EXPECT_INS_RETAINED(call_left_loop); - EXPECT_INS_REMOVED(read_left_end); + EXPECT_FALSE(IsRemoved(write_left_pre)); + EXPECT_TRUE(IsRemoved(read_right)); + EXPECT_TRUE(IsRemoved(write_right)); + EXPECT_FALSE(IsRemoved(write_left_loop)); + EXPECT_FALSE(IsRemoved(call_left_loop)); + EXPECT_TRUE(IsRemoved(read_left_end)); } // // ENTRY @@ -4350,15 +2725,14 @@ TEST_F(LoadStoreEliminationTest, PartialLoadElimination4) { // ELIMINATE // return obj.field TEST_F(LoadStoreEliminationTest, PartialLoadElimination5) { - VariableSizedHandleScope vshs(Thread::Current()); - CreateGraph(&vshs); + InitGraph(); AdjacencyListGraph blks(SetupFromAdjacencyList("entry", "exit", - {{"entry", "left"}, - {"entry", "right"}, - {"left", "breturn"}, - {"right", "breturn"}, - {"breturn", "exit"}})); + { { "entry", "left" }, + { "entry", "right" }, + { "left", "breturn" }, + { "right", "breturn" }, + { "breturn", "exit" } })); #define GET_BLOCK(name) HBasicBlock* name = blks.Get(#name) GET_BLOCK(entry); GET_BLOCK(exit); @@ -4366,51 +2740,112 @@ TEST_F(LoadStoreEliminationTest, PartialLoadElimination5) { GET_BLOCK(left); GET_BLOCK(right); #undef GET_BLOCK - HInstruction* bool_value = MakeParam(DataType::Type::kBool); + HInstruction* bool_value = new (GetAllocator()) + HParameterValue(graph_->GetDexFile(), dex::TypeIndex(1), 1, DataType::Type::kBool); HInstruction* c1 = graph_->GetIntConstant(1); HInstruction* c2 = graph_->GetIntConstant(2); - - HInstruction* cls = MakeClassLoad(); - HInstruction* new_inst = MakeNewInstance(cls); + HInstruction* cls = new (GetAllocator()) HLoadClass(graph_->GetCurrentMethod(), + dex::TypeIndex(10), + graph_->GetDexFile(), + ScopedNullHandle<mirror::Class>(), + false, + 0, + false); + HInstruction* new_inst = + new (GetAllocator()) HNewInstance(cls, + 0, + dex::TypeIndex(10), + graph_->GetDexFile(), + false, + QuickEntrypointEnum::kQuickAllocObjectInitialized); HInstruction* if_inst = new (GetAllocator()) HIf(bool_value); + entry->AddInstruction(bool_value); entry->AddInstruction(cls); entry->AddInstruction(new_inst); entry->AddInstruction(if_inst); - ManuallyBuildEnvFor(cls, {}); + ArenaVector<HInstruction*> current_locals({}, GetAllocator()->Adapter(kArenaAllocInstruction)); + ManuallyBuildEnvFor(cls, ¤t_locals); new_inst->CopyEnvironmentFrom(cls->GetEnvironment()); - HInstruction* call_left = MakeInvoke(DataType::Type::kVoid, { new_inst }); - HInstruction* write_left = MakeIFieldSet(new_inst, c1, MemberOffset(32)); + HInstruction* call_left = new (GetAllocator()) + HInvokeStaticOrDirect(GetAllocator(), + 1, + DataType::Type::kVoid, + 0, + { nullptr, 0 }, + nullptr, + {}, + InvokeType::kStatic, + { nullptr, 0 }, + HInvokeStaticOrDirect::ClinitCheckRequirement::kNone); + HInstruction* write_left = new (GetAllocator()) HInstanceFieldSet(new_inst, + c1, + nullptr, + DataType::Type::kInt32, + MemberOffset(32), + false, + 0, + 0, + graph_->GetDexFile(), + 0); HInstruction* goto_left = new (GetAllocator()) HGoto(); + call_left->AsInvoke()->SetRawInputAt(0, new_inst); left->AddInstruction(call_left); left->AddInstruction(write_left); left->AddInstruction(goto_left); call_left->CopyEnvironmentFrom(cls->GetEnvironment()); - HInstruction* write_right = MakeIFieldSet(new_inst, c2, MemberOffset(32)); - HInstruction* call_right = MakeInvoke(DataType::Type::kVoid, {}); + HInstruction* write_right = new (GetAllocator()) HInstanceFieldSet(new_inst, + c2, + nullptr, + DataType::Type::kInt32, + MemberOffset(32), + false, + 0, + 0, + graph_->GetDexFile(), + 0); + HInstruction* call_right = new (GetAllocator()) + HInvokeStaticOrDirect(GetAllocator(), + 0, + DataType::Type::kVoid, + 0, + { nullptr, 0 }, + nullptr, + {}, + InvokeType::kStatic, + { nullptr, 0 }, + HInvokeStaticOrDirect::ClinitCheckRequirement::kNone); HInstruction* goto_right = new (GetAllocator()) HGoto(); right->AddInstruction(write_right); right->AddInstruction(call_right); right->AddInstruction(goto_right); call_right->CopyEnvironmentFrom(cls->GetEnvironment()); - HInstruction* read_bottom = MakeIFieldGet(new_inst, DataType::Type::kInt32, MemberOffset(32)); + HInstruction* read_bottom = new (GetAllocator()) HInstanceFieldGet(new_inst, + nullptr, + DataType::Type::kInt32, + MemberOffset(32), + false, + 0, + 0, + graph_->GetDexFile(), + 0); HInstruction* return_exit = new (GetAllocator()) HReturn(read_bottom); breturn->AddInstruction(read_bottom); breturn->AddInstruction(return_exit); - SetupExit(exit); - + HInstruction* exit_instruction = new (GetAllocator()) HExit(); + exit->AddInstruction(exit_instruction); // PerformLSE expects this to be empty. graph_->ClearDominanceInformation(); PerformLSE(); - EXPECT_INS_REMOVED(read_bottom); - EXPECT_INS_REMOVED(write_right); - EXPECT_INS_RETAINED(write_left); - EXPECT_INS_RETAINED(call_left); - EXPECT_INS_RETAINED(call_right); + EXPECT_TRUE(IsRemoved(read_bottom)); + EXPECT_TRUE(IsRemoved(write_right)); + EXPECT_FALSE(IsRemoved(write_left)); + EXPECT_FALSE(IsRemoved(call_left)); + EXPECT_FALSE(IsRemoved(call_right)); } // // ENTRY @@ -4431,17 +2866,16 @@ TEST_F(LoadStoreEliminationTest, PartialLoadElimination5) { // } // EXIT // ELIMINATE -// return obj.fid +// return obj.field TEST_F(LoadStoreEliminationTest, PartialLoadElimination6) { - VariableSizedHandleScope vshs(Thread::Current()); - CreateGraph(&vshs); + InitGraph(); AdjacencyListGraph blks(SetupFromAdjacencyList("entry", "exit", - {{"entry", "left"}, - {"entry", "right"}, - {"left", "breturn"}, - {"right", "breturn"}, - {"breturn", "exit"}})); + { { "entry", "left" }, + { "entry", "right" }, + { "left", "breturn" }, + { "right", "breturn" }, + { "breturn", "exit" } })); #define GET_BLOCK(name) HBasicBlock* name = blks.Get(#name) GET_BLOCK(entry); GET_BLOCK(exit); @@ -4449,59 +2883,138 @@ TEST_F(LoadStoreEliminationTest, PartialLoadElimination6) { GET_BLOCK(left); GET_BLOCK(right); #undef GET_BLOCK - HInstruction* bool_value = MakeParam(DataType::Type::kBool); + HInstruction* bool_value = new (GetAllocator()) + HParameterValue(graph_->GetDexFile(), dex::TypeIndex(1), 1, DataType::Type::kBool); HInstruction* c1 = graph_->GetIntConstant(1); HInstruction* c2 = graph_->GetIntConstant(2); HInstruction* c3 = graph_->GetIntConstant(3); HInstruction* c5 = graph_->GetIntConstant(5); - - HInstruction* cls = MakeClassLoad(); - HInstruction* new_inst = MakeNewInstance(cls); - HInstruction* write_entry = MakeIFieldSet(new_inst, c3, MemberOffset(32)); - HInstruction* call_entry = MakeInvoke(DataType::Type::kVoid, {}); + HInstruction* cls = new (GetAllocator()) HLoadClass(graph_->GetCurrentMethod(), + dex::TypeIndex(10), + graph_->GetDexFile(), + ScopedNullHandle<mirror::Class>(), + false, + 0, + false); + HInstruction* new_inst = + new (GetAllocator()) HNewInstance(cls, + 0, + dex::TypeIndex(10), + graph_->GetDexFile(), + false, + QuickEntrypointEnum::kQuickAllocObjectInitialized); + HInstruction* write_entry = new (GetAllocator()) HInstanceFieldSet(new_inst, + c3, + nullptr, + DataType::Type::kInt32, + MemberOffset(32), + false, + 0, + 0, + graph_->GetDexFile(), + 0); + HInstruction* call_entry = new (GetAllocator()) + HInvokeStaticOrDirect(GetAllocator(), + 0, + DataType::Type::kVoid, + 0, + { nullptr, 0 }, + nullptr, + {}, + InvokeType::kStatic, + { nullptr, 0 }, + HInvokeStaticOrDirect::ClinitCheckRequirement::kNone); HInstruction* if_inst = new (GetAllocator()) HIf(bool_value); + entry->AddInstruction(bool_value); entry->AddInstruction(cls); entry->AddInstruction(new_inst); entry->AddInstruction(write_entry); entry->AddInstruction(call_entry); entry->AddInstruction(if_inst); - ManuallyBuildEnvFor(cls, {}); + ArenaVector<HInstruction*> current_locals({}, GetAllocator()->Adapter(kArenaAllocInstruction)); + ManuallyBuildEnvFor(cls, ¤t_locals); new_inst->CopyEnvironmentFrom(cls->GetEnvironment()); call_entry->CopyEnvironmentFrom(cls->GetEnvironment()); - HInstruction* write_left_start = MakeIFieldSet(new_inst, c5, MemberOffset(32)); - HInstruction* call_left = MakeInvoke(DataType::Type::kVoid, { new_inst }); - HInstruction* write_left = MakeIFieldSet(new_inst, c1, MemberOffset(32)); + HInstruction* write_left_start = new (GetAllocator()) HInstanceFieldSet(new_inst, + c5, + nullptr, + DataType::Type::kInt32, + MemberOffset(32), + false, + 0, + 0, + graph_->GetDexFile(), + 0); + HInstruction* call_left = new (GetAllocator()) + HInvokeStaticOrDirect(GetAllocator(), + 1, + DataType::Type::kVoid, + 0, + { nullptr, 0 }, + nullptr, + {}, + InvokeType::kStatic, + { nullptr, 0 }, + HInvokeStaticOrDirect::ClinitCheckRequirement::kNone); + HInstruction* write_left = new (GetAllocator()) HInstanceFieldSet(new_inst, + c1, + nullptr, + DataType::Type::kInt32, + MemberOffset(32), + false, + 0, + 0, + graph_->GetDexFile(), + 0); HInstruction* goto_left = new (GetAllocator()) HGoto(); + call_left->AsInvoke()->SetRawInputAt(0, new_inst); left->AddInstruction(write_left_start); left->AddInstruction(call_left); left->AddInstruction(write_left); left->AddInstruction(goto_left); call_left->CopyEnvironmentFrom(cls->GetEnvironment()); - HInstruction* write_right = MakeIFieldSet(new_inst, c2, MemberOffset(32)); + HInstruction* write_right = new (GetAllocator()) HInstanceFieldSet(new_inst, + c2, + nullptr, + DataType::Type::kInt32, + MemberOffset(32), + false, + 0, + 0, + graph_->GetDexFile(), + 0); HInstruction* goto_right = new (GetAllocator()) HGoto(); right->AddInstruction(write_right); right->AddInstruction(goto_right); - HInstruction* read_bottom = MakeIFieldGet(new_inst, DataType::Type::kInt32, MemberOffset(32)); + HInstruction* read_bottom = new (GetAllocator()) HInstanceFieldGet(new_inst, + nullptr, + DataType::Type::kInt32, + MemberOffset(32), + false, + 0, + 0, + graph_->GetDexFile(), + 0); HInstruction* return_exit = new (GetAllocator()) HReturn(read_bottom); breturn->AddInstruction(read_bottom); breturn->AddInstruction(return_exit); - SetupExit(exit); - + HInstruction* exit_instruction = new (GetAllocator()) HExit(); + exit->AddInstruction(exit_instruction); // PerformLSE expects this to be empty. graph_->ClearDominanceInformation(); PerformLSE(); - EXPECT_INS_REMOVED(read_bottom); - EXPECT_INS_REMOVED(write_right); - EXPECT_INS_REMOVED(write_entry); - EXPECT_INS_RETAINED(write_left_start); - EXPECT_INS_RETAINED(write_left); - EXPECT_INS_RETAINED(call_left); - EXPECT_INS_RETAINED(call_entry); + EXPECT_TRUE(IsRemoved(read_bottom)); + EXPECT_TRUE(IsRemoved(write_right)); + EXPECT_TRUE(IsRemoved(write_entry)); + EXPECT_FALSE(IsRemoved(write_left_start)); + EXPECT_FALSE(IsRemoved(write_left)); + EXPECT_FALSE(IsRemoved(call_left)); + EXPECT_FALSE(IsRemoved(call_entry)); } // // ENTRY @@ -4525,19 +3038,18 @@ TEST_F(LoadStoreEliminationTest, PartialLoadElimination6) { // return obj.field; // EXIT TEST_F(LoadStoreEliminationTest, PartialLoadPreserved3) { - VariableSizedHandleScope vshs(Thread::Current()); - CreateGraph(&vshs); + InitGraph(); AdjacencyListGraph blks(SetupFromAdjacencyList("entry", "exit", - {{"entry", "entry_post"}, - {"entry_post", "right"}, - {"right", "return_block"}, - {"entry_post", "left_pre"}, - {"left_pre", "left_loop"}, - {"left_loop", "left_loop_post"}, - {"left_loop_post", "left_loop"}, - {"left_loop", "return_block"}, - {"return_block", "exit"}})); + { { "entry", "entry_post" }, + { "entry_post", "right" }, + { "right", "return_block" }, + { "entry_post", "left_pre" }, + { "left_pre", "left_loop" }, + { "left_loop", "left_loop_post" }, + { "left_loop_post", "left_loop" }, + { "left_loop", "return_block" }, + { "return_block", "exit" } })); #define GET_BLOCK(name) HBasicBlock* name = blks.Get(#name) GET_BLOCK(entry); GET_BLOCK(entry_post); @@ -4552,63 +3064,123 @@ TEST_F(LoadStoreEliminationTest, PartialLoadPreserved3) { if (left_loop->GetSuccessors()[0] != return_block) { left_loop->SwapSuccessors(); } - HInstruction* bool_value = MakeParam(DataType::Type::kBool); + HInstruction* bool_value = new (GetAllocator()) + HParameterValue(graph_->GetDexFile(), dex::TypeIndex(1), 1, DataType::Type::kBool); HInstruction* c1 = graph_->GetIntConstant(1); HInstruction* c2 = graph_->GetIntConstant(2); HInstruction* c3 = graph_->GetIntConstant(3); - - HInstruction* cls = MakeClassLoad(); - HInstruction* new_inst = MakeNewInstance(cls); + HInstruction* cls = new (GetAllocator()) HLoadClass(graph_->GetCurrentMethod(), + dex::TypeIndex(10), + graph_->GetDexFile(), + ScopedNullHandle<mirror::Class>(), + false, + 0, + false); + HInstruction* new_inst = + new (GetAllocator()) HNewInstance(cls, + 0, + dex::TypeIndex(10), + graph_->GetDexFile(), + false, + QuickEntrypointEnum::kQuickAllocObjectInitialized); HInstruction* goto_entry = new (GetAllocator()) HGoto(); + entry->AddInstruction(bool_value); entry->AddInstruction(cls); entry->AddInstruction(new_inst); entry->AddInstruction(goto_entry); - ManuallyBuildEnvFor(cls, {}); + ArenaVector<HInstruction*> current_locals({}, GetAllocator()->Adapter(kArenaAllocInstruction)); + ManuallyBuildEnvFor(cls, ¤t_locals); new_inst->CopyEnvironmentFrom(cls->GetEnvironment()); HInstruction* if_inst = new (GetAllocator()) HIf(bool_value); entry_post->AddInstruction(if_inst); - HInstruction* write_left_pre = MakeIFieldSet(new_inst, c1, MemberOffset(32)); + HInstruction* write_left_pre = new (GetAllocator()) HInstanceFieldSet(new_inst, + c1, + nullptr, + DataType::Type::kInt32, + MemberOffset(32), + false, + 0, + 0, + graph_->GetDexFile(), + 0); HInstruction* goto_left_pre = new (GetAllocator()) HGoto(); left_pre->AddInstruction(write_left_pre); left_pre->AddInstruction(goto_left_pre); HInstruction* suspend_left_loop = new (GetAllocator()) HSuspendCheck(); - HInstruction* call_left_loop = MakeInvoke(DataType::Type::kBool, { new_inst }); + HInstruction* call_left_loop = new (GetAllocator()) + HInvokeStaticOrDirect(GetAllocator(), + 1, + DataType::Type::kBool, + 0, + { nullptr, 0 }, + nullptr, + {}, + InvokeType::kStatic, + { nullptr, 0 }, + HInvokeStaticOrDirect::ClinitCheckRequirement::kNone); HInstruction* if_left_loop = new (GetAllocator()) HIf(call_left_loop); + call_left_loop->AsInvoke()->SetRawInputAt(0, new_inst); left_loop->AddInstruction(suspend_left_loop); left_loop->AddInstruction(call_left_loop); left_loop->AddInstruction(if_left_loop); suspend_left_loop->CopyEnvironmentFrom(cls->GetEnvironment()); call_left_loop->CopyEnvironmentFrom(cls->GetEnvironment()); - HInstruction* write_left_loop = MakeIFieldSet(new_inst, c3, MemberOffset(32)); + HInstruction* write_left_loop = new (GetAllocator()) HInstanceFieldSet(new_inst, + c3, + nullptr, + DataType::Type::kInt32, + MemberOffset(32), + false, + 0, + 0, + graph_->GetDexFile(), + 0); HInstruction* goto_left_loop = new (GetAllocator()) HGoto(); left_loop_post->AddInstruction(write_left_loop); left_loop_post->AddInstruction(goto_left_loop); - HInstruction* write_right = MakeIFieldSet(new_inst, c2, MemberOffset(32)); + HInstruction* write_right = new (GetAllocator()) HInstanceFieldSet(new_inst, + c2, + nullptr, + DataType::Type::kInt32, + MemberOffset(32), + false, + 0, + 0, + graph_->GetDexFile(), + 0); HInstruction* goto_right = new (GetAllocator()) HGoto(); right->AddInstruction(write_right); right->AddInstruction(goto_right); - HInstruction* read_return = MakeIFieldGet(new_inst, DataType::Type::kInt32, MemberOffset(32)); + HInstruction* read_return = new (GetAllocator()) HInstanceFieldGet(new_inst, + nullptr, + DataType::Type::kInt32, + MemberOffset(32), + false, + 0, + 0, + graph_->GetDexFile(), + 0); HInstruction* return_final = new (GetAllocator()) HReturn(read_return); return_block->AddInstruction(read_return); return_block->AddInstruction(return_final); - SetupExit(exit); - + HInstruction* exit_instruction = new (GetAllocator()) HExit(); + exit->AddInstruction(exit_instruction); // PerformLSE expects this to be empty. graph_->ClearDominanceInformation(); - PerformLSENoPartial(); + PerformLSE(); - EXPECT_INS_RETAINED(write_left_pre) << *write_left_pre; - EXPECT_INS_RETAINED(read_return) << *read_return; - EXPECT_INS_RETAINED(write_right) << *write_right; - EXPECT_INS_RETAINED(write_left_loop) << *write_left_loop; - EXPECT_INS_RETAINED(call_left_loop) << *call_left_loop; + EXPECT_FALSE(IsRemoved(write_left_pre)); + EXPECT_FALSE(IsRemoved(read_return)); + EXPECT_FALSE(IsRemoved(write_right)); + EXPECT_FALSE(IsRemoved(write_left_loop)); + EXPECT_FALSE(IsRemoved(call_left_loop)); } // // ENTRY @@ -4633,18 +3205,17 @@ TEST_F(LoadStoreEliminationTest, PartialLoadPreserved3) { // return obj.field; // EXIT TEST_F(LoadStoreEliminationTest, PartialLoadPreserved4) { - VariableSizedHandleScope vshs(Thread::Current()); - CreateGraph(&vshs); + InitGraph(); AdjacencyListGraph blks(SetupFromAdjacencyList("entry", "exit", - {{"entry", "entry_post"}, - {"entry_post", "right"}, - {"right", "return_block"}, - {"entry_post", "left_pre"}, - {"left_pre", "left_loop"}, - {"left_loop", "left_loop"}, - {"left_loop", "return_block"}, - {"return_block", "exit"}})); + { { "entry", "entry_post" }, + { "entry_post", "right" }, + { "right", "return_block" }, + { "entry_post", "left_pre" }, + { "left_pre", "left_loop" }, + { "left_loop", "left_loop" }, + { "left_loop", "return_block" }, + { "return_block", "exit" } })); #define GET_BLOCK(name) HBasicBlock* name = blks.Get(#name) GET_BLOCK(entry); GET_BLOCK(entry_post); @@ -4658,31 +3229,73 @@ TEST_F(LoadStoreEliminationTest, PartialLoadPreserved4) { if (left_loop->GetSuccessors()[0] != return_block) { left_loop->SwapSuccessors(); } - HInstruction* bool_value = MakeParam(DataType::Type::kBool); + HInstruction* bool_value = new (GetAllocator()) + HParameterValue(graph_->GetDexFile(), dex::TypeIndex(1), 1, DataType::Type::kBool); HInstruction* c1 = graph_->GetIntConstant(1); HInstruction* c2 = graph_->GetIntConstant(2); HInstruction* c3 = graph_->GetIntConstant(3); - - HInstruction* cls = MakeClassLoad(); - HInstruction* new_inst = MakeNewInstance(cls); + HInstruction* cls = new (GetAllocator()) HLoadClass(graph_->GetCurrentMethod(), + dex::TypeIndex(10), + graph_->GetDexFile(), + ScopedNullHandle<mirror::Class>(), + false, + 0, + false); + HInstruction* new_inst = + new (GetAllocator()) HNewInstance(cls, + 0, + dex::TypeIndex(10), + graph_->GetDexFile(), + false, + QuickEntrypointEnum::kQuickAllocObjectInitialized); HInstruction* goto_entry = new (GetAllocator()) HGoto(); + entry->AddInstruction(bool_value); entry->AddInstruction(cls); entry->AddInstruction(new_inst); entry->AddInstruction(goto_entry); - ManuallyBuildEnvFor(cls, {}); + ArenaVector<HInstruction*> current_locals({}, GetAllocator()->Adapter(kArenaAllocInstruction)); + ManuallyBuildEnvFor(cls, ¤t_locals); new_inst->CopyEnvironmentFrom(cls->GetEnvironment()); HInstruction* if_inst = new (GetAllocator()) HIf(bool_value); entry_post->AddInstruction(if_inst); - HInstruction* write_left_pre = MakeIFieldSet(new_inst, c1, MemberOffset(32)); + HInstruction* write_left_pre = new (GetAllocator()) HInstanceFieldSet(new_inst, + c1, + nullptr, + DataType::Type::kInt32, + MemberOffset(32), + false, + 0, + 0, + graph_->GetDexFile(), + 0); HInstruction* goto_left_pre = new (GetAllocator()) HGoto(); left_pre->AddInstruction(write_left_pre); left_pre->AddInstruction(goto_left_pre); HInstruction* suspend_left_loop = new (GetAllocator()) HSuspendCheck(); - HInstruction* call_left_loop = MakeInvoke(DataType::Type::kBool, {}); - HInstruction* write_left_loop = MakeIFieldSet(new_inst, c3, MemberOffset(32)); + HInstruction* call_left_loop = new (GetAllocator()) + HInvokeStaticOrDirect(GetAllocator(), + 0, + DataType::Type::kBool, + 0, + { nullptr, 0 }, + nullptr, + {}, + InvokeType::kStatic, + { nullptr, 0 }, + HInvokeStaticOrDirect::ClinitCheckRequirement::kNone); + HInstruction* write_left_loop = new (GetAllocator()) HInstanceFieldSet(new_inst, + c3, + nullptr, + DataType::Type::kInt32, + MemberOffset(32), + false, + 0, + 0, + graph_->GetDexFile(), + 0); HInstruction* if_left_loop = new (GetAllocator()) HIf(call_left_loop); left_loop->AddInstruction(suspend_left_loop); left_loop->AddInstruction(call_left_loop); @@ -4691,31 +3304,59 @@ TEST_F(LoadStoreEliminationTest, PartialLoadPreserved4) { suspend_left_loop->CopyEnvironmentFrom(cls->GetEnvironment()); call_left_loop->CopyEnvironmentFrom(cls->GetEnvironment()); - HInstruction* write_right = MakeIFieldSet(new_inst, c2, MemberOffset(32)); - HInstruction* call_right = MakeInvoke(DataType::Type::kBool, { new_inst }); + HInstruction* write_right = new (GetAllocator()) HInstanceFieldSet(new_inst, + c2, + nullptr, + DataType::Type::kInt32, + MemberOffset(32), + false, + 0, + 0, + graph_->GetDexFile(), + 0); + HInstruction* call_right = new (GetAllocator()) + HInvokeStaticOrDirect(GetAllocator(), + 1, + DataType::Type::kBool, + 0, + { nullptr, 0 }, + nullptr, + {}, + InvokeType::kStatic, + { nullptr, 0 }, + HInvokeStaticOrDirect::ClinitCheckRequirement::kNone); HInstruction* goto_right = new (GetAllocator()) HGoto(); + call_right->AsInvoke()->SetRawInputAt(0, new_inst); right->AddInstruction(write_right); right->AddInstruction(call_right); right->AddInstruction(goto_right); call_right->CopyEnvironmentFrom(cls->GetEnvironment()); - HInstruction* read_return = MakeIFieldGet(new_inst, DataType::Type::kInt32, MemberOffset(32)); + HInstruction* read_return = new (GetAllocator()) HInstanceFieldGet(new_inst, + nullptr, + DataType::Type::kInt32, + MemberOffset(32), + false, + 0, + 0, + graph_->GetDexFile(), + 0); HInstruction* return_final = new (GetAllocator()) HReturn(read_return); return_block->AddInstruction(read_return); return_block->AddInstruction(return_final); - SetupExit(exit); - + HInstruction* exit_instruction = new (GetAllocator()) HExit(); + exit->AddInstruction(exit_instruction); // PerformLSE expects this to be empty. graph_->ClearDominanceInformation(); - PerformLSENoPartial(); - - EXPECT_INS_RETAINED(read_return); - EXPECT_INS_RETAINED(write_right); - EXPECT_INS_RETAINED(write_left_loop); - EXPECT_INS_RETAINED(call_left_loop); - EXPECT_INS_REMOVED(write_left_pre); - EXPECT_INS_RETAINED(call_right); + PerformLSE(); + + EXPECT_FALSE(IsRemoved(read_return)); + EXPECT_FALSE(IsRemoved(write_right)); + EXPECT_FALSE(IsRemoved(write_left_loop)); + EXPECT_FALSE(IsRemoved(call_left_loop)); + EXPECT_TRUE(IsRemoved(write_left_pre)); + EXPECT_FALSE(IsRemoved(call_right)); } // // ENTRY @@ -4738,15 +3379,14 @@ TEST_F(LoadStoreEliminationTest, PartialLoadPreserved4) { // ELIMINATE // return obj.field TEST_F(LoadStoreEliminationTest, PartialLoadPreserved5) { - VariableSizedHandleScope vshs(Thread::Current()); - CreateGraph(&vshs); + InitGraph(); AdjacencyListGraph blks(SetupFromAdjacencyList("entry", "exit", - {{"entry", "left"}, - {"entry", "right"}, - {"left", "breturn"}, - {"right", "breturn"}, - {"breturn", "exit"}})); + { { "entry", "left" }, + { "entry", "right" }, + { "left", "breturn" }, + { "right", "breturn" }, + { "breturn", "exit" } })); #define GET_BLOCK(name) HBasicBlock* name = blks.Get(#name) GET_BLOCK(entry); GET_BLOCK(exit); @@ -4754,23 +3394,67 @@ TEST_F(LoadStoreEliminationTest, PartialLoadPreserved5) { GET_BLOCK(left); GET_BLOCK(right); #undef GET_BLOCK - HInstruction* bool_value = MakeParam(DataType::Type::kBool); + HInstruction* bool_value = new (GetAllocator()) + HParameterValue(graph_->GetDexFile(), dex::TypeIndex(1), 1, DataType::Type::kBool); HInstruction* c1 = graph_->GetIntConstant(1); HInstruction* c2 = graph_->GetIntConstant(2); - - HInstruction* cls = MakeClassLoad(); - HInstruction* new_inst = MakeNewInstance(cls); + HInstruction* cls = new (GetAllocator()) HLoadClass(graph_->GetCurrentMethod(), + dex::TypeIndex(10), + graph_->GetDexFile(), + ScopedNullHandle<mirror::Class>(), + false, + 0, + false); + HInstruction* new_inst = + new (GetAllocator()) HNewInstance(cls, + 0, + dex::TypeIndex(10), + graph_->GetDexFile(), + false, + QuickEntrypointEnum::kQuickAllocObjectInitialized); HInstruction* if_inst = new (GetAllocator()) HIf(bool_value); + entry->AddInstruction(bool_value); entry->AddInstruction(cls); entry->AddInstruction(new_inst); entry->AddInstruction(if_inst); - ManuallyBuildEnvFor(cls, {}); + ArenaVector<HInstruction*> current_locals({}, GetAllocator()->Adapter(kArenaAllocInstruction)); + ManuallyBuildEnvFor(cls, ¤t_locals); new_inst->CopyEnvironmentFrom(cls->GetEnvironment()); - HInstruction* call_left = MakeInvoke(DataType::Type::kVoid, { new_inst }); - HInstruction* write_left = MakeIFieldSet(new_inst, c1, MemberOffset(32)); - HInstruction* call2_left = MakeInvoke(DataType::Type::kVoid, {}); + HInstruction* call_left = new (GetAllocator()) + HInvokeStaticOrDirect(GetAllocator(), + 1, + DataType::Type::kVoid, + 0, + { nullptr, 0 }, + nullptr, + {}, + InvokeType::kStatic, + { nullptr, 0 }, + HInvokeStaticOrDirect::ClinitCheckRequirement::kNone); + HInstruction* write_left = new (GetAllocator()) HInstanceFieldSet(new_inst, + c1, + nullptr, + DataType::Type::kInt32, + MemberOffset(32), + false, + 0, + 0, + graph_->GetDexFile(), + 0); + HInstruction* call2_left = new (GetAllocator()) + HInvokeStaticOrDirect(GetAllocator(), + 0, + DataType::Type::kVoid, + 0, + { nullptr, 0 }, + nullptr, + {}, + InvokeType::kStatic, + { nullptr, 0 }, + HInvokeStaticOrDirect::ClinitCheckRequirement::kNone); HInstruction* goto_left = new (GetAllocator()) HGoto(); + call_left->AsInvoke()->SetRawInputAt(0, new_inst); left->AddInstruction(call_left); left->AddInstruction(write_left); left->AddInstruction(call2_left); @@ -4778,30 +3462,57 @@ TEST_F(LoadStoreEliminationTest, PartialLoadPreserved5) { call_left->CopyEnvironmentFrom(cls->GetEnvironment()); call2_left->CopyEnvironmentFrom(cls->GetEnvironment()); - HInstruction* write_right = MakeIFieldSet(new_inst, c2, MemberOffset(32)); - HInstruction* call_right = MakeInvoke(DataType::Type::kVoid, {}); + HInstruction* write_right = new (GetAllocator()) HInstanceFieldSet(new_inst, + c2, + nullptr, + DataType::Type::kInt32, + MemberOffset(32), + false, + 0, + 0, + graph_->GetDexFile(), + 0); + HInstruction* call_right = new (GetAllocator()) + HInvokeStaticOrDirect(GetAllocator(), + 0, + DataType::Type::kVoid, + 0, + { nullptr, 0 }, + nullptr, + {}, + InvokeType::kStatic, + { nullptr, 0 }, + HInvokeStaticOrDirect::ClinitCheckRequirement::kNone); HInstruction* goto_right = new (GetAllocator()) HGoto(); right->AddInstruction(write_right); right->AddInstruction(call_right); right->AddInstruction(goto_right); call_right->CopyEnvironmentFrom(cls->GetEnvironment()); - HInstruction* read_bottom = MakeIFieldGet(new_inst, DataType::Type::kInt32, MemberOffset(32)); + HInstruction* read_bottom = new (GetAllocator()) HInstanceFieldGet(new_inst, + nullptr, + DataType::Type::kInt32, + MemberOffset(32), + false, + 0, + 0, + graph_->GetDexFile(), + 0); HInstruction* return_exit = new (GetAllocator()) HReturn(read_bottom); breturn->AddInstruction(read_bottom); breturn->AddInstruction(return_exit); - SetupExit(exit); - + HInstruction* exit_instruction = new (GetAllocator()) HExit(); + exit->AddInstruction(exit_instruction); // PerformLSE expects this to be empty. graph_->ClearDominanceInformation(); - PerformLSENoPartial(); + PerformLSE(); - EXPECT_INS_RETAINED(read_bottom); - EXPECT_INS_RETAINED(write_right); - EXPECT_INS_RETAINED(write_left); - EXPECT_INS_RETAINED(call_left); - EXPECT_INS_RETAINED(call_right); + EXPECT_FALSE(IsRemoved(read_bottom)); + EXPECT_FALSE(IsRemoved(write_right)); + EXPECT_FALSE(IsRemoved(write_left)); + EXPECT_FALSE(IsRemoved(call_left)); + EXPECT_FALSE(IsRemoved(call_right)); } // // ENTRY @@ -4823,14 +3534,14 @@ TEST_F(LoadStoreEliminationTest, PartialLoadPreserved5) { // ELIMINATE // return obj.field TEST_F(LoadStoreEliminationTest, PartialLoadPreserved6) { - CreateGraph(); + InitGraph(); AdjacencyListGraph blks(SetupFromAdjacencyList("entry", "exit", - {{"entry", "left"}, - {"entry", "right"}, - {"left", "breturn"}, - {"right", "breturn"}, - {"breturn", "exit"}})); + { { "entry", "left" }, + { "entry", "right" }, + { "left", "breturn" }, + { "right", "breturn" }, + { "breturn", "exit" } })); #define GET_BLOCK(name) HBasicBlock* name = blks.Get(#name) GET_BLOCK(entry); GET_BLOCK(exit); @@ -4838,3090 +3549,125 @@ TEST_F(LoadStoreEliminationTest, PartialLoadPreserved6) { GET_BLOCK(left); GET_BLOCK(right); #undef GET_BLOCK - HInstruction* bool_value = MakeParam(DataType::Type::kBool); + HInstruction* bool_value = new (GetAllocator()) + HParameterValue(graph_->GetDexFile(), dex::TypeIndex(1), 1, DataType::Type::kBool); HInstruction* c1 = graph_->GetIntConstant(1); HInstruction* c2 = graph_->GetIntConstant(2); HInstruction* c3 = graph_->GetIntConstant(3); - - HInstruction* cls = MakeClassLoad(); - HInstruction* new_inst = MakeNewInstance(cls); - HInstruction* write_entry = MakeIFieldSet(new_inst, c3, MemberOffset(32)); - HInstruction* call_entry = MakeInvoke(DataType::Type::kVoid, {}); + HInstruction* cls = new (GetAllocator()) HLoadClass(graph_->GetCurrentMethod(), + dex::TypeIndex(10), + graph_->GetDexFile(), + ScopedNullHandle<mirror::Class>(), + false, + 0, + false); + HInstruction* new_inst = + new (GetAllocator()) HNewInstance(cls, + 0, + dex::TypeIndex(10), + graph_->GetDexFile(), + false, + QuickEntrypointEnum::kQuickAllocObjectInitialized); + HInstruction* write_entry = new (GetAllocator()) HInstanceFieldSet(new_inst, + c3, + nullptr, + DataType::Type::kInt32, + MemberOffset(32), + false, + 0, + 0, + graph_->GetDexFile(), + 0); + HInstruction* call_entry = new (GetAllocator()) + HInvokeStaticOrDirect(GetAllocator(), + 0, + DataType::Type::kVoid, + 0, + { nullptr, 0 }, + nullptr, + {}, + InvokeType::kStatic, + { nullptr, 0 }, + HInvokeStaticOrDirect::ClinitCheckRequirement::kNone); HInstruction* if_inst = new (GetAllocator()) HIf(bool_value); + entry->AddInstruction(bool_value); entry->AddInstruction(cls); entry->AddInstruction(new_inst); entry->AddInstruction(write_entry); entry->AddInstruction(call_entry); entry->AddInstruction(if_inst); - ManuallyBuildEnvFor(cls, {}); + ArenaVector<HInstruction*> current_locals({}, GetAllocator()->Adapter(kArenaAllocInstruction)); + ManuallyBuildEnvFor(cls, ¤t_locals); new_inst->CopyEnvironmentFrom(cls->GetEnvironment()); call_entry->CopyEnvironmentFrom(cls->GetEnvironment()); - HInstruction* call_left = MakeInvoke(DataType::Type::kVoid, { new_inst }); - HInstruction* write_left = MakeIFieldSet(new_inst, c1, MemberOffset(32)); + HInstruction* call_left = new (GetAllocator()) + HInvokeStaticOrDirect(GetAllocator(), + 1, + DataType::Type::kVoid, + 0, + { nullptr, 0 }, + nullptr, + {}, + InvokeType::kStatic, + { nullptr, 0 }, + HInvokeStaticOrDirect::ClinitCheckRequirement::kNone); + HInstruction* write_left = new (GetAllocator()) HInstanceFieldSet(new_inst, + c1, + nullptr, + DataType::Type::kInt32, + MemberOffset(32), + false, + 0, + 0, + graph_->GetDexFile(), + 0); HInstruction* goto_left = new (GetAllocator()) HGoto(); + call_left->AsInvoke()->SetRawInputAt(0, new_inst); left->AddInstruction(call_left); left->AddInstruction(write_left); left->AddInstruction(goto_left); call_left->CopyEnvironmentFrom(cls->GetEnvironment()); - HInstruction* write_right = MakeIFieldSet(new_inst, c2, MemberOffset(32)); - HInstruction* goto_right = new (GetAllocator()) HGoto(); - right->AddInstruction(write_right); - right->AddInstruction(goto_right); - - HInstruction* read_bottom = MakeIFieldGet(new_inst, DataType::Type::kInt32, MemberOffset(32)); - HInstruction* return_exit = new (GetAllocator()) HReturn(read_bottom); - breturn->AddInstruction(read_bottom); - breturn->AddInstruction(return_exit); - - SetupExit(exit); - - // PerformLSE expects this to be empty. - graph_->ClearDominanceInformation(); - - LOG(INFO) << "Pre LSE " << blks; - PerformLSENoPartial(); - LOG(INFO) << "Post LSE " << blks; - - EXPECT_INS_REMOVED(read_bottom); - EXPECT_INS_REMOVED(write_right); - EXPECT_INS_RETAINED(write_entry); - EXPECT_INS_RETAINED(write_left); - EXPECT_INS_RETAINED(call_left); - EXPECT_INS_RETAINED(call_entry); -} - -// // ENTRY -// // MOVED TO MATERIALIZATION BLOCK -// obj = new Obj(); -// ELIMINATE, moved to materialization block. Kept by escape. -// obj.field = 3; -// // Make sure this graph isn't broken -// if (obj ==/!= (STATIC.VALUE|obj|null)) { -// // partial_BLOCK -// // REMOVE (either from unreachable or normal PHI creation) -// obj.field = 4; -// } -// if (parameter_value) { -// // LEFT -// // DO NOT ELIMINATE -// escape(obj); -// } else { -// // RIGHT -// // ELIMINATE -// obj.field = 2; -// } -// EXIT -// PREDICATED GET -// return obj.field -TEST_P(PartialComparisonTestGroup, PartialComparisonBeforeCohort) { - VariableSizedHandleScope vshs(Thread::Current()); - CreateGraph(/*handles=*/&vshs); - AdjacencyListGraph blks(SetupFromAdjacencyList("entry", - "exit", - {{"entry", "critical_break"}, - {"entry", "partial"}, - {"partial", "merge"}, - {"critical_break", "merge"}, - {"merge", "left"}, - {"merge", "right"}, - {"left", "breturn"}, - {"right", "breturn"}, - {"breturn", "exit"}})); -#define GET_BLOCK(name) HBasicBlock* name = blks.Get(#name) - GET_BLOCK(entry); - GET_BLOCK(merge); - GET_BLOCK(partial); - GET_BLOCK(critical_break); - GET_BLOCK(exit); - GET_BLOCK(breturn); - GET_BLOCK(left); - GET_BLOCK(right); -#undef GET_BLOCK - HInstruction* bool_value = MakeParam(DataType::Type::kBool); - HInstruction* c2 = graph_->GetIntConstant(2); - HInstruction* c3 = graph_->GetIntConstant(3); - HInstruction* c4 = graph_->GetIntConstant(4); - - HInstruction* cls = MakeClassLoad(); - HInstruction* new_inst = MakeNewInstance(cls); - HInstruction* write_entry = MakeIFieldSet(new_inst, c3, MemberOffset(32)); - ComparisonInstructions cmp_instructions = GetComparisonInstructions(new_inst); - HInstruction* if_inst = new (GetAllocator()) HIf(cmp_instructions.cmp_); - entry->AddInstruction(cls); - entry->AddInstruction(new_inst); - entry->AddInstruction(write_entry); - cmp_instructions.AddSetup(entry); - entry->AddInstruction(cmp_instructions.cmp_); - entry->AddInstruction(if_inst); - ManuallyBuildEnvFor(cls, {}); - cmp_instructions.AddEnvironment(cls->GetEnvironment()); - new_inst->CopyEnvironmentFrom(cls->GetEnvironment()); - - HInstruction* write_partial = MakeIFieldSet(new_inst, c4, MemberOffset(32)); - HInstruction* goto_partial = new (GetAllocator()) HGoto(); - partial->AddInstruction(write_partial); - partial->AddInstruction(goto_partial); - - HInstruction* goto_crit_break = new (GetAllocator()) HGoto(); - critical_break->AddInstruction(goto_crit_break); - - HInstruction* if_merge = new (GetAllocator()) HIf(bool_value); - merge->AddInstruction(if_merge); - - HInstruction* call_left = MakeInvoke(DataType::Type::kVoid, { new_inst }); - HInstruction* goto_left = new (GetAllocator()) HGoto(); - left->AddInstruction(call_left); - left->AddInstruction(goto_left); - call_left->CopyEnvironmentFrom(cls->GetEnvironment()); - - HInstruction* write_right = MakeIFieldSet(new_inst, c2, MemberOffset(32)); - HInstruction* goto_right = new (GetAllocator()) HGoto(); - right->AddInstruction(write_right); - right->AddInstruction(goto_right); - - HInstruction* read_bottom = MakeIFieldGet(new_inst, DataType::Type::kInt32, MemberOffset(32)); - HInstruction* return_exit = new (GetAllocator()) HReturn(read_bottom); - breturn->AddInstruction(read_bottom); - breturn->AddInstruction(return_exit); - - SetupExit(exit); - - // PerformLSE expects this to be empty. - graph_->ClearDominanceInformation(); - - LOG(INFO) << "Pre LSE " << blks; - PerformLSEWithPartial(); - LOG(INFO) << "Post LSE " << blks; - - std::vector<HPhi*> merges; - HPredicatedInstanceFieldGet* pred_get; - HInstanceFieldSet* init_set; - std::tie(pred_get, init_set) = - FindSingleInstructions<HPredicatedInstanceFieldGet, HInstanceFieldSet>(graph_); - std::tie(merges) = FindAllInstructions<HPhi>(graph_); - ASSERT_EQ(merges.size(), 3u); - HPhi* merge_value_return = FindOrNull(merges.begin(), merges.end(), [&](HPhi* p) { - return p->GetType() == DataType::Type::kInt32 && p->GetBlock() == breturn; - }); - HPhi* merge_value_top = FindOrNull(merges.begin(), merges.end(), [&](HPhi* p) { - return p->GetType() == DataType::Type::kInt32 && p->GetBlock() != breturn; - }); - HPhi* merge_alloc = FindOrNull(merges.begin(), merges.end(), [](HPhi* p) { - return p->GetType() == DataType::Type::kReference; - }); - EXPECT_INS_REMOVED(read_bottom); - EXPECT_INS_REMOVED(write_entry); - EXPECT_INS_REMOVED(write_partial); - EXPECT_INS_RETAINED(call_left); - CheckFinalInstruction(if_inst->InputAt(0), ComparisonPlacement::kBeforeEscape); - EXPECT_INS_EQ(init_set->InputAt(1), merge_value_top); - EXPECT_INS_EQ(pred_get->GetTarget(), merge_alloc); - EXPECT_INS_EQ(pred_get->GetDefaultValue(), merge_value_return); -} - -// // ENTRY -// // MOVED TO MATERIALIZATION BLOCK -// obj = new Obj(); -// ELIMINATE, moved to materialization block. Kept by escape. -// obj.field = 3; -// // Make sure this graph isn't broken -// if (parameter_value) { -// if (obj ==/!= (STATIC.VALUE|obj|null)) { -// // partial_BLOCK -// obj.field = 4; -// } -// // LEFT -// // DO NOT ELIMINATE -// escape(obj); -// } else { -// // RIGHT -// // ELIMINATE -// obj.field = 2; -// } -// EXIT -// PREDICATED GET -// return obj.field -TEST_P(PartialComparisonTestGroup, PartialComparisonInCohortBeforeEscape) { - VariableSizedHandleScope vshs(Thread::Current()); - CreateGraph(/*handles=*/&vshs); - AdjacencyListGraph blks(SetupFromAdjacencyList("entry", - "exit", - {{"entry", "left_begin"}, - {"left_begin", "partial"}, - {"left_begin", "left_crit_break"}, - {"left_crit_break", "left"}, - {"partial", "left"}, - {"entry", "right"}, - {"left", "breturn"}, - {"right", "breturn"}, - {"breturn", "exit"}})); -#define GET_BLOCK(name) HBasicBlock* name = blks.Get(#name) - GET_BLOCK(entry); - GET_BLOCK(partial); - GET_BLOCK(left_begin); - GET_BLOCK(exit); - GET_BLOCK(breturn); - GET_BLOCK(left); - GET_BLOCK(left_crit_break); - GET_BLOCK(right); -#undef GET_BLOCK - EnsurePredecessorOrder(left, {left_crit_break, partial}); - HInstruction* bool_value = MakeParam(DataType::Type::kBool); - HInstruction* c2 = graph_->GetIntConstant(2); - HInstruction* c3 = graph_->GetIntConstant(3); - HInstruction* c4 = graph_->GetIntConstant(4); - - HInstruction* cls = MakeClassLoad(); - HInstruction* new_inst = MakeNewInstance(cls); - HInstruction* write_entry = MakeIFieldSet(new_inst, c3, MemberOffset(32)); - HInstruction* if_inst = new (GetAllocator()) HIf(bool_value); - entry->AddInstruction(cls); - entry->AddInstruction(new_inst); - entry->AddInstruction(write_entry); - entry->AddInstruction(if_inst); - ManuallyBuildEnvFor(cls, {}); - new_inst->CopyEnvironmentFrom(cls->GetEnvironment()); - - ComparisonInstructions cmp_instructions = GetComparisonInstructions(new_inst); - HInstruction* if_left_begin = new (GetAllocator()) HIf(cmp_instructions.cmp_); - cmp_instructions.AddSetup(left_begin); - left_begin->AddInstruction(cmp_instructions.cmp_); - left_begin->AddInstruction(if_left_begin); - cmp_instructions.AddEnvironment(cls->GetEnvironment()); - - left_crit_break->AddInstruction(new (GetAllocator()) HGoto()); - - HInstruction* write_partial = MakeIFieldSet(new_inst, c4, MemberOffset(32)); - HInstruction* goto_partial = new (GetAllocator()) HGoto(); - partial->AddInstruction(write_partial); - partial->AddInstruction(goto_partial); - - HInstruction* call_left = MakeInvoke(DataType::Type::kVoid, { new_inst }); - HInstruction* goto_left = new (GetAllocator()) HGoto(); - left->AddInstruction(call_left); - left->AddInstruction(goto_left); - call_left->CopyEnvironmentFrom(cls->GetEnvironment()); - - HInstruction* write_right = MakeIFieldSet(new_inst, c2, MemberOffset(32)); - HInstruction* goto_right = new (GetAllocator()) HGoto(); - right->AddInstruction(write_right); - right->AddInstruction(goto_right); - - HInstruction* read_bottom = MakeIFieldGet(new_inst, DataType::Type::kInt32, MemberOffset(32)); - HInstruction* return_exit = new (GetAllocator()) HReturn(read_bottom); - breturn->AddInstruction(read_bottom); - breturn->AddInstruction(return_exit); - - SetupExit(exit); - - // PerformLSE expects this to be empty. - graph_->ClearDominanceInformation(); - LOG(INFO) << "Pre LSE " << blks; - PerformLSEWithPartial(); - LOG(INFO) << "Post LSE " << blks; - - std::vector<HPhi*> merges; - HInstanceFieldSet* init_set = - FindSingleInstruction<HInstanceFieldSet>(graph_, left_begin->GetSinglePredecessor()); - HInstanceFieldSet* partial_set = FindSingleInstruction<HInstanceFieldSet>(graph_, partial); - HPredicatedInstanceFieldGet* pred_get = - FindSingleInstruction<HPredicatedInstanceFieldGet>(graph_); - std::tie(merges) = FindAllInstructions<HPhi>(graph_); - ASSERT_EQ(merges.size(), 2u); - HPhi* merge_value_return = FindOrNull(merges.begin(), merges.end(), [&](HPhi* p) { - return p->GetType() == DataType::Type::kInt32; - }); - HPhi* merge_alloc = FindOrNull(merges.begin(), merges.end(), [](HPhi* p) { - return p->GetType() == DataType::Type::kReference; - }); - EXPECT_EQ(merge_value_return->GetBlock(), breturn) - << blks.GetName(merge_value_return->GetBlock()); - EXPECT_INS_REMOVED(read_bottom); - EXPECT_INS_REMOVED(write_entry); - EXPECT_INS_RETAINED(write_partial); - EXPECT_INS_RETAINED(call_left); - CheckFinalInstruction(if_left_begin->InputAt(0), ComparisonPlacement::kInEscape); - EXPECT_INS_EQ(init_set->InputAt(1), c3); - EXPECT_INS_EQ(partial_set->InputAt(0), init_set->InputAt(0)); - EXPECT_INS_EQ(partial_set->InputAt(1), c4); - EXPECT_INS_EQ(pred_get->GetTarget(), merge_alloc); - EXPECT_INS_EQ(pred_get->GetDefaultValue(), merge_value_return); -} - -// // ENTRY -// // MOVED TO MATERIALIZATION BLOCK -// obj = new Obj(); -// ELIMINATE, moved to materialization block. Kept by escape. -// obj.field = 3; -// // Make sure this graph isn't broken -// if (parameter_value) { -// // LEFT -// // DO NOT ELIMINATE -// escape(obj); -// } else { -// // RIGHT -// // ELIMINATE -// obj.field = 2; -// } -// if (obj ==/!= (STATIC.VALUE|obj|null)) { -// // partial_BLOCK -// obj.field = 4; -// } -// EXIT -// PREDICATED GET -// return obj.field -TEST_P(PartialComparisonTestGroup, PartialComparisonAfterCohort) { - VariableSizedHandleScope vshs(Thread::Current()); - CreateGraph(/*handles=*/&vshs); - AdjacencyListGraph blks(SetupFromAdjacencyList("entry", - "exit", - {{"entry", "left"}, - {"entry", "right"}, - {"left", "merge"}, - {"right", "merge"}, - {"merge", "critical_break"}, - {"critical_break", "breturn"}, - {"merge", "partial"}, - {"partial", "breturn"}, - {"breturn", "exit"}})); -#define GET_BLOCK(name) HBasicBlock* name = blks.Get(#name) - GET_BLOCK(entry); - GET_BLOCK(partial); - GET_BLOCK(critical_break); - GET_BLOCK(merge); - GET_BLOCK(exit); - GET_BLOCK(breturn); - GET_BLOCK(left); - GET_BLOCK(right); -#undef GET_BLOCK - EnsurePredecessorOrder(breturn, {critical_break, partial}); - HInstruction* bool_value = MakeParam(DataType::Type::kBool); - HInstruction* c2 = graph_->GetIntConstant(2); - HInstruction* c3 = graph_->GetIntConstant(3); - HInstruction* c4 = graph_->GetIntConstant(4); - - HInstruction* cls = MakeClassLoad(); - HInstruction* new_inst = MakeNewInstance(cls); - HInstruction* write_entry = MakeIFieldSet(new_inst, c3, MemberOffset(32)); - HInstruction* if_inst = new (GetAllocator()) HIf(bool_value); - entry->AddInstruction(cls); - entry->AddInstruction(new_inst); - entry->AddInstruction(write_entry); - entry->AddInstruction(if_inst); - ManuallyBuildEnvFor(cls, {}); - new_inst->CopyEnvironmentFrom(cls->GetEnvironment()); - - HInstruction* call_left = MakeInvoke(DataType::Type::kVoid, { new_inst }); - HInstruction* goto_left = new (GetAllocator()) HGoto(); - left->AddInstruction(call_left); - left->AddInstruction(goto_left); - call_left->CopyEnvironmentFrom(cls->GetEnvironment()); - - HInstruction* write_right = MakeIFieldSet(new_inst, c2, MemberOffset(32)); - HInstruction* goto_right = new (GetAllocator()) HGoto(); - right->AddInstruction(write_right); - right->AddInstruction(goto_right); - - ComparisonInstructions cmp_instructions = GetComparisonInstructions(new_inst); - HInstruction* if_merge = new (GetAllocator()) HIf(cmp_instructions.cmp_); - cmp_instructions.AddSetup(merge); - merge->AddInstruction(cmp_instructions.cmp_); - merge->AddInstruction(if_merge); - cmp_instructions.AddEnvironment(cls->GetEnvironment()); - - HInstanceFieldSet* write_partial = MakeIFieldSet(new_inst, c4, MemberOffset(32)); - HInstruction* goto_partial = new (GetAllocator()) HGoto(); - partial->AddInstruction(write_partial); - partial->AddInstruction(goto_partial); - - HInstruction* goto_crit_break = new (GetAllocator()) HGoto(); - critical_break->AddInstruction(goto_crit_break); - - HInstruction* read_bottom = MakeIFieldGet(new_inst, DataType::Type::kInt32, MemberOffset(32)); - HInstruction* return_exit = new (GetAllocator()) HReturn(read_bottom); - breturn->AddInstruction(read_bottom); - breturn->AddInstruction(return_exit); - - SetupExit(exit); - - // PerformLSE expects this to be empty. - graph_->ClearDominanceInformation(); - LOG(INFO) << "Pre LSE " << blks; - PerformLSEWithPartial(); - LOG(INFO) << "Post LSE " << blks; - - std::vector<HPhi*> merges; - HInstanceFieldSet* init_set = - FindSingleInstruction<HInstanceFieldSet>(graph_, left->GetSinglePredecessor()); - HPredicatedInstanceFieldGet* pred_get = - FindSingleInstruction<HPredicatedInstanceFieldGet>(graph_); - std::tie(merges) = FindAllInstructions<HPhi>(graph_); - ASSERT_EQ(merges.size(), 3u); - HPhi* merge_value_return = FindOrNull(merges.begin(), merges.end(), [&](HPhi* p) { - return p->GetType() == DataType::Type::kInt32 && p->GetBlock() == breturn; - }); - HPhi* merge_alloc = FindOrNull(merges.begin(), merges.end(), [](HPhi* p) { - return p->GetType() == DataType::Type::kReference; - }); - EXPECT_INS_REMOVED(read_bottom); - EXPECT_INS_REMOVED(write_entry); - EXPECT_INS_RETAINED(write_partial); - EXPECT_TRUE(write_partial->GetIsPredicatedSet()); - EXPECT_INS_RETAINED(call_left); - CheckFinalInstruction(if_merge->InputAt(0), ComparisonPlacement::kAfterEscape); - EXPECT_INS_EQ(init_set->InputAt(1), c3); - ASSERT_TRUE(write_partial->InputAt(0)->IsPhi()); - EXPECT_INS_EQ(write_partial->InputAt(0)->AsPhi()->InputAt(0), init_set->InputAt(0)); - EXPECT_INS_EQ(write_partial->InputAt(1), c4); - EXPECT_INS_EQ(pred_get->GetTarget(), merge_alloc); - EXPECT_INS_EQ(pred_get->GetDefaultValue(), merge_value_return); -} - -// // ENTRY -// // MOVED TO MATERIALIZATION BLOCK -// obj = new Obj(); -// ELIMINATE, moved to materialization block. Kept by escape. -// obj.field = 3; -// // Make sure this graph isn't broken -// if (parameter_value) { -// // LEFT -// // DO NOT ELIMINATE -// escape(obj); -// if (obj ==/!= (STATIC.VALUE|obj|null)) { -// // partial_BLOCK -// obj.field = 4; -// } -// } else { -// // RIGHT -// // ELIMINATE -// obj.field = 2; -// } -// EXIT -// PREDICATED GET -// return obj.field -TEST_P(PartialComparisonTestGroup, PartialComparisonInCohortAfterEscape) { - PartialComparisonKind kind = GetParam(); - VariableSizedHandleScope vshs(Thread::Current()); - CreateGraph(/*handles=*/&vshs); - AdjacencyListGraph blks(SetupFromAdjacencyList("entry", - "exit", - {{"entry", "left"}, - {"left", "partial"}, - {"partial", "left_end"}, - {"left", "left_crit_break"}, - {"left_crit_break", "left_end"}, - {"left_end", "breturn"}, - {"entry", "right"}, - {"right", "breturn"}, - {"breturn", "exit"}})); -#define GET_BLOCK(name) HBasicBlock* name = blks.Get(#name) - GET_BLOCK(entry); - GET_BLOCK(partial); - GET_BLOCK(left_end); - GET_BLOCK(exit); - GET_BLOCK(breturn); - GET_BLOCK(left); - GET_BLOCK(left_crit_break); - GET_BLOCK(right); -#undef GET_BLOCK - HInstruction* bool_value = MakeParam(DataType::Type::kBool); - HInstruction* c2 = graph_->GetIntConstant(2); - HInstruction* c3 = graph_->GetIntConstant(3); - HInstruction* c4 = graph_->GetIntConstant(4); - - HInstruction* cls = MakeClassLoad(); - HInstruction* new_inst = MakeNewInstance(cls); - HInstruction* write_entry = MakeIFieldSet(new_inst, c3, MemberOffset(32)); - HInstruction* if_inst = new (GetAllocator()) HIf(bool_value); - entry->AddInstruction(cls); - entry->AddInstruction(new_inst); - entry->AddInstruction(write_entry); - entry->AddInstruction(if_inst); - ManuallyBuildEnvFor(cls, {}); - new_inst->CopyEnvironmentFrom(cls->GetEnvironment()); - - HInstruction* call_left = MakeInvoke(DataType::Type::kVoid, { new_inst }); - ComparisonInstructions cmp_instructions = GetComparisonInstructions(new_inst); - HInstruction* if_left = new (GetAllocator()) HIf(cmp_instructions.cmp_); - left->AddInstruction(call_left); - cmp_instructions.AddSetup(left); - left->AddInstruction(cmp_instructions.cmp_); - left->AddInstruction(if_left); - call_left->CopyEnvironmentFrom(cls->GetEnvironment()); - cmp_instructions.AddEnvironment(cls->GetEnvironment()); - if (if_left->AsIf()->IfTrueSuccessor() != partial) { - left->SwapSuccessors(); - } - - HInstruction* write_partial = MakeIFieldSet(new_inst, c4, MemberOffset(32)); - HInstruction* goto_partial = new (GetAllocator()) HGoto(); - partial->AddInstruction(write_partial); - partial->AddInstruction(goto_partial); - - HInstruction* goto_left_crit_break = new (GetAllocator()) HGoto(); - left_crit_break->AddInstruction(goto_left_crit_break); - - HInstruction* goto_left_end = new (GetAllocator()) HGoto(); - left_end->AddInstruction(goto_left_end); - - HInstruction* write_right = MakeIFieldSet(new_inst, c2, MemberOffset(32)); - HInstruction* goto_right = new (GetAllocator()) HGoto(); - right->AddInstruction(write_right); - right->AddInstruction(goto_right); - - HInstruction* read_bottom = MakeIFieldGet(new_inst, DataType::Type::kInt32, MemberOffset(32)); - HInstruction* return_exit = new (GetAllocator()) HReturn(read_bottom); - breturn->AddInstruction(read_bottom); - breturn->AddInstruction(return_exit); - - SetupExit(exit); - - // PerformLSE expects this to be empty. - graph_->ClearDominanceInformation(); - - LOG(INFO) << "Pre LSE " << blks; - PerformLSEWithPartial(); - LOG(INFO) << "Post LSE " << blks; - - std::vector<HPhi*> merges; - std::vector<HInstanceFieldSet*> sets; - HPredicatedInstanceFieldGet* pred_get = - FindSingleInstruction<HPredicatedInstanceFieldGet>(graph_); - std::tie(merges, sets) = FindAllInstructions<HPhi, HInstanceFieldSet>(graph_); - ASSERT_EQ(merges.size(), 2u); - ASSERT_EQ(sets.size(), 2u); - HInstanceFieldSet* init_set = FindOrNull(sets.begin(), sets.end(), [&](HInstanceFieldSet* s) { - return s->GetBlock()->GetSingleSuccessor() == left; - }); - EXPECT_INS_EQ(init_set->InputAt(1), c3); - HPhi* merge_value_return = FindOrNull(merges.begin(), merges.end(), [&](HPhi* p) { - return p->GetType() == DataType::Type::kInt32 && p->GetBlock() == breturn; - }); - HPhi* merge_alloc = FindOrNull(merges.begin(), merges.end(), [](HPhi* p) { - return p->GetType() == DataType::Type::kReference; - }); - EXPECT_INS_REMOVED(read_bottom); - EXPECT_INS_REMOVED(write_entry); - if (kind.IsPossiblyTrue()) { - EXPECT_INS_RETAINED(write_partial); - EXPECT_TRUE(std::find(sets.begin(), sets.end(), write_partial) != sets.end()); - } - EXPECT_INS_RETAINED(call_left); - CheckFinalInstruction(if_left->InputAt(0), ComparisonPlacement::kInEscape); - EXPECT_INS_EQ(pred_get->GetTarget(), merge_alloc); - EXPECT_INS_EQ(pred_get->GetDefaultValue(), merge_value_return); -} - -INSTANTIATE_TEST_SUITE_P( - LoadStoreEliminationTest, - PartialComparisonTestGroup, - testing::Values(PartialComparisonKind{PartialComparisonKind::Type::kEquals, - PartialComparisonKind::Target::kNull, - PartialComparisonKind::Position::kLeft}, - PartialComparisonKind{PartialComparisonKind::Type::kEquals, - PartialComparisonKind::Target::kNull, - PartialComparisonKind::Position::kRight}, - PartialComparisonKind{PartialComparisonKind::Type::kEquals, - PartialComparisonKind::Target::kValue, - PartialComparisonKind::Position::kLeft}, - PartialComparisonKind{PartialComparisonKind::Type::kEquals, - PartialComparisonKind::Target::kValue, - PartialComparisonKind::Position::kRight}, - PartialComparisonKind{PartialComparisonKind::Type::kEquals, - PartialComparisonKind::Target::kSelf, - PartialComparisonKind::Position::kLeft}, - PartialComparisonKind{PartialComparisonKind::Type::kNotEquals, - PartialComparisonKind::Target::kNull, - PartialComparisonKind::Position::kLeft}, - PartialComparisonKind{PartialComparisonKind::Type::kNotEquals, - PartialComparisonKind::Target::kNull, - PartialComparisonKind::Position::kRight}, - PartialComparisonKind{PartialComparisonKind::Type::kNotEquals, - PartialComparisonKind::Target::kSelf, - PartialComparisonKind::Position::kLeft}, - PartialComparisonKind{PartialComparisonKind::Type::kNotEquals, - PartialComparisonKind::Target::kValue, - PartialComparisonKind::Position::kLeft}, - PartialComparisonKind{PartialComparisonKind::Type::kNotEquals, - PartialComparisonKind::Target::kValue, - PartialComparisonKind::Position::kRight})); - -// // ENTRY -// obj = new Obj(); -// if (parameter_value) { -// // LEFT -// escape(obj); -// } else { -// // RIGHT -// // ELIMINATE -// obj.field = 2; -// } -// EXIT -// predicated-ELIMINATE -// obj.field = 3; -TEST_F(LoadStoreEliminationTest, PredicatedStore1) { - VariableSizedHandleScope vshs(Thread::Current()); - InitGraph(/*handles=*/&vshs); - AdjacencyListGraph blks(SetupFromAdjacencyList("entry", - "exit", - {{"entry", "left"}, - {"entry", "right"}, - {"left", "breturn"}, - {"right", "breturn"}, - {"breturn", "exit"}})); -#define GET_BLOCK(name) HBasicBlock* name = blks.Get(#name) - GET_BLOCK(entry); - GET_BLOCK(exit); - GET_BLOCK(breturn); - GET_BLOCK(left); - GET_BLOCK(right); -#undef GET_BLOCK - EnsurePredecessorOrder(breturn, {left, right}); - HInstruction* bool_value = MakeParam(DataType::Type::kBool); - HInstruction* null_const = graph_->GetNullConstant(); - HInstruction* c2 = graph_->GetIntConstant(2); - HInstruction* c3 = graph_->GetIntConstant(3); - - HInstruction* cls = MakeClassLoad(); - HInstruction* new_inst = MakeNewInstance(cls); - HInstruction* if_inst = new (GetAllocator()) HIf(bool_value); - entry->AddInstruction(cls); - entry->AddInstruction(new_inst); - entry->AddInstruction(if_inst); - ManuallyBuildEnvFor(cls, {}); - new_inst->CopyEnvironmentFrom(cls->GetEnvironment()); - - HInstruction* call_left = MakeInvoke(DataType::Type::kVoid, { new_inst }); - HInstruction* goto_left = new (GetAllocator()) HGoto(); - left->AddInstruction(call_left); - left->AddInstruction(goto_left); - call_left->CopyEnvironmentFrom(cls->GetEnvironment()); - - HInstruction* write_right = MakeIFieldSet(new_inst, c2, MemberOffset(32)); - HInstruction* goto_right = new (GetAllocator()) HGoto(); - right->AddInstruction(write_right); - right->AddInstruction(goto_right); - - HInstruction* write_bottom = MakeIFieldSet(new_inst, c3, MemberOffset(32)); - HInstruction* return_exit = new (GetAllocator()) HReturnVoid(); - breturn->AddInstruction(write_bottom); - breturn->AddInstruction(return_exit); - - SetupExit(exit); - - // PerformLSE expects this to be empty. - graph_->ClearDominanceInformation(); - - LOG(INFO) << "Pre LSE " << blks; - PerformLSEWithPartial(); - LOG(INFO) << "Post LSE " << blks; - - EXPECT_INS_RETAINED(write_bottom); - EXPECT_TRUE(write_bottom->AsInstanceFieldSet()->GetIsPredicatedSet()); - EXPECT_INS_REMOVED(write_right); - EXPECT_INS_RETAINED(call_left); - HPhi* merge_alloc = FindSingleInstruction<HPhi>(graph_, breturn); - ASSERT_NE(merge_alloc, nullptr); - EXPECT_TRUE(merge_alloc->InputAt(0)->IsNewInstance()) << *merge_alloc; - EXPECT_EQ(merge_alloc->InputAt(0)->InputAt(0), cls) << *merge_alloc << " cls? " << *cls; - EXPECT_EQ(merge_alloc->InputAt(1), null_const); -} - -// // ENTRY -// obj = new Obj(); -// obj.field = 3; -// if (parameter_value) { -// // LEFT -// escape(obj); -// } else { -// // RIGHT -// // ELIMINATE -// obj.field = 2; -// } -// // MERGE -// if (second_param) { -// // NON_ESCAPE -// obj.field = 1; -// noescape(); -// } -// EXIT -// predicated-ELIMINATE -// obj.field = 4; -TEST_F(LoadStoreEliminationTest, PredicatedStore2) { - VariableSizedHandleScope vshs(Thread::Current()); - CreateGraph(/*handles=*/&vshs); - AdjacencyListGraph blks(SetupFromAdjacencyList("entry", - "exit", - {{"entry", "left"}, - {"entry", "right"}, - {"left", "merge"}, - {"right", "merge"}, - {"merge", "non_escape"}, - {"non_escape", "breturn"}, - {"merge", "merge_crit_break"}, - {"merge_crit_break", "breturn"}, - {"breturn", "exit"}})); -#define GET_BLOCK(name) HBasicBlock* name = blks.Get(#name) - GET_BLOCK(entry); - GET_BLOCK(exit); - GET_BLOCK(breturn); - GET_BLOCK(left); - GET_BLOCK(right); - GET_BLOCK(merge); - GET_BLOCK(merge_crit_break); - GET_BLOCK(non_escape); -#undef GET_BLOCK - EnsurePredecessorOrder(merge, {left, right}); - EnsurePredecessorOrder(breturn, {merge_crit_break, non_escape}); - HInstruction* bool_value = MakeParam(DataType::Type::kBool); - HInstruction* bool_value2 = MakeParam(DataType::Type::kBool); - HInstruction* null_const = graph_->GetNullConstant(); - HInstruction* c1 = graph_->GetIntConstant(3); - HInstruction* c2 = graph_->GetIntConstant(2); - HInstruction* c3 = graph_->GetIntConstant(3); - HInstruction* c4 = graph_->GetIntConstant(4); - - HInstruction* cls = MakeClassLoad(); - HInstruction* new_inst = MakeNewInstance(cls); - HInstruction* write_entry = MakeIFieldSet(new_inst, c3, MemberOffset(32)); - HInstruction* if_inst = new (GetAllocator()) HIf(bool_value); - entry->AddInstruction(cls); - entry->AddInstruction(new_inst); - entry->AddInstruction(write_entry); - entry->AddInstruction(if_inst); - ManuallyBuildEnvFor(cls, {}); - new_inst->CopyEnvironmentFrom(cls->GetEnvironment()); - - HInstruction* call_left = MakeInvoke(DataType::Type::kVoid, { new_inst }); - HInstruction* goto_left = new (GetAllocator()) HGoto(); - left->AddInstruction(call_left); - left->AddInstruction(goto_left); - call_left->CopyEnvironmentFrom(cls->GetEnvironment()); - - HInstruction* write_right = MakeIFieldSet(new_inst, c2, MemberOffset(32)); - HInstruction* goto_right = new (GetAllocator()) HGoto(); - right->AddInstruction(write_right); - right->AddInstruction(goto_right); - - HInstruction* merge_if = new (GetAllocator()) HIf(bool_value2); - merge->AddInstruction(merge_if); - - merge_crit_break->AddInstruction(new (GetAllocator()) HGoto()); - - HInstruction* write_non_escape = MakeIFieldSet(new_inst, c1, MemberOffset(32)); - HInstruction* non_escape_call = MakeInvoke(DataType::Type::kVoid, {}); - HInstruction* non_escape_goto = new (GetAllocator()) HGoto(); - non_escape->AddInstruction(write_non_escape); - non_escape->AddInstruction(non_escape_call); - non_escape->AddInstruction(non_escape_goto); - non_escape_call->CopyEnvironmentFrom(cls->GetEnvironment()); - - HInstruction* write_bottom = MakeIFieldSet(new_inst, c4, MemberOffset(32)); - HInstruction* return_exit = new (GetAllocator()) HReturnVoid(); - breturn->AddInstruction(write_bottom); - breturn->AddInstruction(return_exit); - - SetupExit(exit); - - // PerformLSE expects this to be empty. - graph_->ClearDominanceInformation(); - LOG(INFO) << "Pre LSE " << blks; - PerformLSEWithPartial(); - LOG(INFO) << "Post LSE " << blks; - - EXPECT_INS_RETAINED(write_bottom); - EXPECT_TRUE(write_bottom->AsInstanceFieldSet()->GetIsPredicatedSet()) << *write_bottom; - EXPECT_INS_REMOVED(write_right); - EXPECT_INS_RETAINED(call_left); - HInstanceFieldSet* pred_set = FindSingleInstruction<HInstanceFieldSet>(graph_, breturn); - HPhi* merge_alloc = FindSingleInstruction<HPhi>(graph_); - ASSERT_NE(merge_alloc, nullptr); - EXPECT_TRUE(merge_alloc->InputAt(0)->IsNewInstance()) << *merge_alloc; - EXPECT_INS_EQ(merge_alloc->InputAt(0)->InputAt(0), cls) << " phi is: " << *merge_alloc; - EXPECT_INS_EQ(merge_alloc->InputAt(1), null_const); - ASSERT_NE(pred_set, nullptr); - EXPECT_TRUE(pred_set->GetIsPredicatedSet()) << *pred_set; - EXPECT_INS_EQ(pred_set->InputAt(0), merge_alloc); -} - -// // ENTRY -// obj = new Obj(); -// obj.field = 3; -// if (parameter_value) { -// // LEFT -// escape(obj); -// } else { -// // RIGHT -// // ELIMINATE -// obj.field = 2; -// } -// EXIT -// predicated-ELIMINATE -// return obj.field -TEST_F(LoadStoreEliminationTest, PredicatedLoad1) { - VariableSizedHandleScope vshs(Thread::Current()); - CreateGraph(/*handles=*/&vshs); - AdjacencyListGraph blks(SetupFromAdjacencyList("entry", - "exit", - {{"entry", "left"}, - {"entry", "right"}, - {"left", "breturn"}, - {"right", "breturn"}, - {"breturn", "exit"}})); -#define GET_BLOCK(name) HBasicBlock* name = blks.Get(#name) - GET_BLOCK(entry); - GET_BLOCK(exit); - GET_BLOCK(breturn); - GET_BLOCK(left); - GET_BLOCK(right); -#undef GET_BLOCK - EnsurePredecessorOrder(breturn, {left, right}); - HInstruction* bool_value = MakeParam(DataType::Type::kBool); - HInstruction* null_const = graph_->GetNullConstant(); - HInstruction* c2 = graph_->GetIntConstant(2); - HInstruction* c3 = graph_->GetIntConstant(3); - - HInstruction* cls = MakeClassLoad(); - HInstruction* new_inst = MakeNewInstance(cls); - HInstruction* write_entry = MakeIFieldSet(new_inst, c3, MemberOffset(32)); - HInstruction* if_inst = new (GetAllocator()) HIf(bool_value); - entry->AddInstruction(cls); - entry->AddInstruction(new_inst); - entry->AddInstruction(write_entry); - entry->AddInstruction(if_inst); - ManuallyBuildEnvFor(cls, {}); - new_inst->CopyEnvironmentFrom(cls->GetEnvironment()); - - HInstruction* call_left = MakeInvoke(DataType::Type::kVoid, { new_inst }); - HInstruction* goto_left = new (GetAllocator()) HGoto(); - left->AddInstruction(call_left); - left->AddInstruction(goto_left); - call_left->CopyEnvironmentFrom(cls->GetEnvironment()); - - HInstruction* write_right = MakeIFieldSet(new_inst, c2, MemberOffset(32)); + HInstruction* write_right = new (GetAllocator()) HInstanceFieldSet(new_inst, + c2, + nullptr, + DataType::Type::kInt32, + MemberOffset(32), + false, + 0, + 0, + graph_->GetDexFile(), + 0); HInstruction* goto_right = new (GetAllocator()) HGoto(); right->AddInstruction(write_right); right->AddInstruction(goto_right); - HInstruction* read_bottom = MakeIFieldGet(new_inst, DataType::Type::kInt32, MemberOffset(32)); - HInstruction* return_exit = new (GetAllocator()) HReturn(read_bottom); - breturn->AddInstruction(read_bottom); - breturn->AddInstruction(return_exit); - - SetupExit(exit); - - // PerformLSE expects this to be empty. - graph_->ClearDominanceInformation(); - LOG(INFO) << "Pre LSE " << blks; - PerformLSEWithPartial(); - LOG(INFO) << "Post LSE " << blks; - - EXPECT_INS_REMOVED(read_bottom); - EXPECT_INS_REMOVED(write_right); - EXPECT_INS_RETAINED(call_left); - std::vector<HPhi*> merges; - HPredicatedInstanceFieldGet* pred_get = - FindSingleInstruction<HPredicatedInstanceFieldGet>(graph_, breturn); - std::tie(merges) = FindAllInstructions<HPhi>(graph_, breturn); - ASSERT_EQ(merges.size(), 2u); - HPhi* merge_value_return = FindOrNull( - merges.begin(), merges.end(), [](HPhi* p) { return p->GetType() == DataType::Type::kInt32; }); - HPhi* merge_alloc = FindOrNull(merges.begin(), merges.end(), [](HPhi* p) { - return p->GetType() == DataType::Type::kReference; - }); - ASSERT_NE(merge_alloc, nullptr); - EXPECT_TRUE(merge_alloc->InputAt(0)->IsNewInstance()) << *merge_alloc; - EXPECT_EQ(merge_alloc->InputAt(0)->InputAt(0), cls) << *merge_alloc << " cls? " << *cls; - EXPECT_EQ(merge_alloc->InputAt(1), null_const); - ASSERT_NE(pred_get, nullptr); - EXPECT_INS_EQ(pred_get->GetTarget(), merge_alloc); - EXPECT_INS_EQ(pred_get->GetDefaultValue(), merge_value_return) << " pred-get is: " << *pred_get; - EXPECT_INS_EQ(merge_value_return->InputAt(0), graph_->GetIntConstant(0)) - << " merge val is: " << *merge_value_return; - EXPECT_INS_EQ(merge_value_return->InputAt(1), c2) << " merge val is: " << *merge_value_return; -} - -// // ENTRY -// obj1 = new Obj1(); -// obj2 = new Obj2(); -// obj1.field = 3; -// obj2.field = 13; -// if (parameter_value) { -// // LEFT -// escape(obj1); -// escape(obj2); -// } else { -// // RIGHT -// // ELIMINATE -// obj1.field = 2; -// obj2.field = 12; -// } -// EXIT -// predicated-ELIMINATE -// return obj1.field + obj2.field -TEST_F(LoadStoreEliminationTest, MultiPredicatedLoad1) { - VariableSizedHandleScope vshs(Thread::Current()); - CreateGraph(/*handles=*/&vshs); - AdjacencyListGraph blks(SetupFromAdjacencyList("entry", - "exit", - {{"entry", "left"}, - {"entry", "right"}, - {"left", "breturn"}, - {"right", "breturn"}, - {"breturn", "exit"}})); -#define GET_BLOCK(name) HBasicBlock* name = blks.Get(#name) - GET_BLOCK(entry); - GET_BLOCK(exit); - GET_BLOCK(breturn); - GET_BLOCK(left); - GET_BLOCK(right); -#undef GET_BLOCK - EnsurePredecessorOrder(breturn, {left, right}); - HInstruction* bool_value = MakeParam(DataType::Type::kBool); - HInstruction* c2 = graph_->GetIntConstant(2); - HInstruction* c3 = graph_->GetIntConstant(3); - HInstruction* c12 = graph_->GetIntConstant(12); - HInstruction* c13 = graph_->GetIntConstant(13); - - HInstruction* cls1 = MakeClassLoad(); - HInstruction* cls2 = MakeClassLoad(); - HInstruction* new_inst1 = MakeNewInstance(cls1); - HInstruction* new_inst2 = MakeNewInstance(cls2); - HInstruction* write_entry1 = MakeIFieldSet(new_inst1, c3, MemberOffset(32)); - HInstruction* write_entry2 = MakeIFieldSet(new_inst2, c13, MemberOffset(32)); - HInstruction* if_inst = new (GetAllocator()) HIf(bool_value); - entry->AddInstruction(cls1); - entry->AddInstruction(cls2); - entry->AddInstruction(new_inst1); - entry->AddInstruction(new_inst2); - entry->AddInstruction(write_entry1); - entry->AddInstruction(write_entry2); - entry->AddInstruction(if_inst); - ManuallyBuildEnvFor(cls1, {}); - cls2->CopyEnvironmentFrom(cls1->GetEnvironment()); - new_inst1->CopyEnvironmentFrom(cls1->GetEnvironment()); - new_inst2->CopyEnvironmentFrom(cls1->GetEnvironment()); - - HInstruction* call_left1 = MakeInvoke(DataType::Type::kVoid, { new_inst1 }); - HInstruction* call_left2 = MakeInvoke(DataType::Type::kVoid, { new_inst2 }); - HInstruction* goto_left = new (GetAllocator()) HGoto(); - left->AddInstruction(call_left1); - left->AddInstruction(call_left2); - left->AddInstruction(goto_left); - call_left1->CopyEnvironmentFrom(cls1->GetEnvironment()); - call_left2->CopyEnvironmentFrom(cls1->GetEnvironment()); - - HInstruction* write_right1 = MakeIFieldSet(new_inst1, c2, MemberOffset(32)); - HInstruction* write_right2 = MakeIFieldSet(new_inst2, c12, MemberOffset(32)); - HInstruction* goto_right = new (GetAllocator()) HGoto(); - right->AddInstruction(write_right1); - right->AddInstruction(write_right2); - right->AddInstruction(goto_right); - - HInstruction* read_bottom1 = MakeIFieldGet(new_inst1, DataType::Type::kInt32, MemberOffset(32)); - HInstruction* read_bottom2 = MakeIFieldGet(new_inst2, DataType::Type::kInt32, MemberOffset(32)); - HInstruction* combine = - new (GetAllocator()) HAdd(DataType::Type::kInt32, read_bottom1, read_bottom2); - HInstruction* return_exit = new (GetAllocator()) HReturn(combine); - breturn->AddInstruction(read_bottom1); - breturn->AddInstruction(read_bottom2); - breturn->AddInstruction(combine); - breturn->AddInstruction(return_exit); - - SetupExit(exit); - - // PerformLSE expects this to be empty. - graph_->ClearDominanceInformation(); - LOG(INFO) << "Pre LSE " << blks; - PerformLSEWithPartial(); - LOG(INFO) << "Post LSE " << blks; - - EXPECT_INS_REMOVED(read_bottom1); - EXPECT_INS_REMOVED(read_bottom2); - EXPECT_INS_REMOVED(write_right1); - EXPECT_INS_REMOVED(write_right2); - EXPECT_INS_RETAINED(call_left1); - EXPECT_INS_RETAINED(call_left2); - std::vector<HPhi*> merges; - std::vector<HPredicatedInstanceFieldGet*> pred_gets; - std::tie(merges, pred_gets) = - FindAllInstructions<HPhi, HPredicatedInstanceFieldGet>(graph_, breturn); - ASSERT_EQ(merges.size(), 4u); - ASSERT_EQ(pred_gets.size(), 2u); - HPhi* merge_value_return1 = FindOrNull(merges.begin(), merges.end(), [&](HPhi* p) { - return p->GetType() == DataType::Type::kInt32 && p->InputAt(1) == c2; - }); - HPhi* merge_value_return2 = FindOrNull(merges.begin(), merges.end(), [&](HPhi* p) { - return p->GetType() == DataType::Type::kInt32 && p->InputAt(1) == c12; - }); - HPhi* merge_alloc1 = FindOrNull(merges.begin(), merges.end(), [&](HPhi* p) { - return p->GetType() == DataType::Type::kReference && - p->InputAt(0)->IsNewInstance() && - p->InputAt(0)->InputAt(0) == cls1; - }); - HPhi* merge_alloc2 = FindOrNull(merges.begin(), merges.end(), [&](HPhi* p) { - return p->GetType() == DataType::Type::kReference && - p->InputAt(0)->IsNewInstance() && - p->InputAt(0)->InputAt(0) == cls2; - }); - ASSERT_NE(merge_alloc1, nullptr); - ASSERT_NE(merge_alloc2, nullptr); - EXPECT_EQ(merge_alloc1->InputAt(1), graph_->GetNullConstant()); - EXPECT_EQ(merge_alloc2->InputAt(1), graph_->GetNullConstant()); - HPredicatedInstanceFieldGet* pred_get1 = - FindOrNull(pred_gets.begin(), pred_gets.end(), [&](HPredicatedInstanceFieldGet* pg) { - return pg->GetTarget() == merge_alloc1; - }); - HPredicatedInstanceFieldGet* pred_get2 = - FindOrNull(pred_gets.begin(), pred_gets.end(), [&](HPredicatedInstanceFieldGet* pg) { - return pg->GetTarget() == merge_alloc2; - }); - ASSERT_NE(pred_get1, nullptr); - EXPECT_INS_EQ(pred_get1->GetTarget(), merge_alloc1); - EXPECT_INS_EQ(pred_get1->GetDefaultValue(), merge_value_return1) - << " pred-get is: " << *pred_get1; - EXPECT_INS_EQ(merge_value_return1->InputAt(0), graph_->GetIntConstant(0)) - << " merge val is: " << *merge_value_return1; - EXPECT_INS_EQ(merge_value_return1->InputAt(1), c2) << " merge val is: " << *merge_value_return1; - ASSERT_NE(pred_get2, nullptr); - EXPECT_INS_EQ(pred_get2->GetTarget(), merge_alloc2); - EXPECT_INS_EQ(pred_get2->GetDefaultValue(), merge_value_return2) - << " pred-get is: " << *pred_get2; - EXPECT_INS_EQ(merge_value_return2->InputAt(0), graph_->GetIntConstant(0)) - << " merge val is: " << *merge_value_return1; - EXPECT_INS_EQ(merge_value_return2->InputAt(1), c12) << " merge val is: " << *merge_value_return1; -} - -// // ENTRY -// obj1 = new Obj1(); -// obj2 = new Obj2(); -// obj1.field = 3; -// obj2.field = 13; -// if (parameter_value) { -// // LEFT -// escape(obj1); -// // ELIMINATE -// obj2.field = 12; -// } else { -// // RIGHT -// // ELIMINATE -// obj1.field = 2; -// escape(obj2); -// } -// EXIT -// predicated-ELIMINATE -// return obj1.field + obj2.field -TEST_F(LoadStoreEliminationTest, MultiPredicatedLoad2) { - VariableSizedHandleScope vshs(Thread::Current()); - CreateGraph(/*handles=*/&vshs); - AdjacencyListGraph blks(SetupFromAdjacencyList("entry", - "exit", - {{"entry", "left"}, - {"entry", "right"}, - {"left", "breturn"}, - {"right", "breturn"}, - {"breturn", "exit"}})); -#define GET_BLOCK(name) HBasicBlock* name = blks.Get(#name) - GET_BLOCK(entry); - GET_BLOCK(exit); - GET_BLOCK(breturn); - GET_BLOCK(left); - GET_BLOCK(right); -#undef GET_BLOCK - EnsurePredecessorOrder(breturn, {left, right}); - HInstruction* bool_value = MakeParam(DataType::Type::kBool); - HInstruction* c2 = graph_->GetIntConstant(2); - HInstruction* c3 = graph_->GetIntConstant(3); - HInstruction* c12 = graph_->GetIntConstant(12); - HInstruction* c13 = graph_->GetIntConstant(13); - - HInstruction* cls1 = MakeClassLoad(); - HInstruction* cls2 = MakeClassLoad(); - HInstruction* new_inst1 = MakeNewInstance(cls1); - HInstruction* new_inst2 = MakeNewInstance(cls2); - HInstruction* write_entry1 = MakeIFieldSet(new_inst1, c3, MemberOffset(32)); - HInstruction* write_entry2 = MakeIFieldSet(new_inst2, c13, MemberOffset(32)); - HInstruction* if_inst = new (GetAllocator()) HIf(bool_value); - entry->AddInstruction(cls1); - entry->AddInstruction(cls2); - entry->AddInstruction(new_inst1); - entry->AddInstruction(new_inst2); - entry->AddInstruction(write_entry1); - entry->AddInstruction(write_entry2); - entry->AddInstruction(if_inst); - ManuallyBuildEnvFor(cls1, {}); - cls2->CopyEnvironmentFrom(cls1->GetEnvironment()); - new_inst1->CopyEnvironmentFrom(cls1->GetEnvironment()); - new_inst2->CopyEnvironmentFrom(cls1->GetEnvironment()); - - HInstruction* call_left1 = MakeInvoke(DataType::Type::kVoid, { new_inst1 }); - HInstruction* write_left2 = MakeIFieldSet(new_inst2, c12, MemberOffset(32)); - HInstruction* goto_left = new (GetAllocator()) HGoto(); - left->AddInstruction(call_left1); - left->AddInstruction(write_left2); - left->AddInstruction(goto_left); - call_left1->CopyEnvironmentFrom(cls1->GetEnvironment()); - - HInstruction* write_right1 = MakeIFieldSet(new_inst1, c2, MemberOffset(32)); - HInstruction* call_right2 = MakeInvoke(DataType::Type::kVoid, { new_inst2 }); - HInstruction* goto_right = new (GetAllocator()) HGoto(); - right->AddInstruction(write_right1); - right->AddInstruction(call_right2); - right->AddInstruction(goto_right); - call_right2->CopyEnvironmentFrom(cls1->GetEnvironment()); - - HInstruction* read_bottom1 = MakeIFieldGet(new_inst1, DataType::Type::kInt32, MemberOffset(32)); - HInstruction* read_bottom2 = MakeIFieldGet(new_inst2, DataType::Type::kInt32, MemberOffset(32)); - HInstruction* combine = - new (GetAllocator()) HAdd(DataType::Type::kInt32, read_bottom1, read_bottom2); - HInstruction* return_exit = new (GetAllocator()) HReturn(combine); - breturn->AddInstruction(read_bottom1); - breturn->AddInstruction(read_bottom2); - breturn->AddInstruction(combine); - breturn->AddInstruction(return_exit); - - SetupExit(exit); - - // PerformLSE expects this to be empty. - graph_->ClearDominanceInformation(); - LOG(INFO) << "Pre LSE " << blks; - PerformLSEWithPartial(); - LOG(INFO) << "Post LSE " << blks; - - EXPECT_INS_REMOVED(read_bottom1); - EXPECT_INS_REMOVED(read_bottom2); - EXPECT_INS_REMOVED(write_right1); - EXPECT_INS_REMOVED(write_left2); - EXPECT_INS_RETAINED(call_left1); - EXPECT_INS_RETAINED(call_right2); - std::vector<HPhi*> merges; - std::vector<HPredicatedInstanceFieldGet*> pred_gets; - std::tie(merges, pred_gets) = - FindAllInstructions<HPhi, HPredicatedInstanceFieldGet>(graph_, breturn); - ASSERT_EQ(merges.size(), 4u); - ASSERT_EQ(pred_gets.size(), 2u); - HPhi* merge_value_return1 = FindOrNull(merges.begin(), merges.end(), [&](HPhi* p) { - return p->GetType() == DataType::Type::kInt32 && p->InputAt(1) == c2; - }); - HPhi* merge_value_return2 = FindOrNull(merges.begin(), merges.end(), [&](HPhi* p) { - return p->GetType() == DataType::Type::kInt32 && p->InputAt(0) == c12; - }); - HPhi* merge_alloc1 = FindOrNull(merges.begin(), merges.end(), [&](HPhi* p) { - return p->GetType() == DataType::Type::kReference && p->InputAt(1)->IsNullConstant(); - }); - HPhi* merge_alloc2 = FindOrNull(merges.begin(), merges.end(), [&](HPhi* p) { - return p->GetType() == DataType::Type::kReference && p->InputAt(0)->IsNullConstant(); - }); - ASSERT_NE(merge_alloc1, nullptr); - ASSERT_NE(merge_alloc2, nullptr); - EXPECT_TRUE(merge_alloc1->InputAt(0)->IsNewInstance()) << *merge_alloc1; - EXPECT_INS_EQ(merge_alloc1->InputAt(0)->InputAt(0), cls1) << *merge_alloc1; - EXPECT_INS_EQ(merge_alloc1->InputAt(1), graph_->GetNullConstant()); - EXPECT_TRUE(merge_alloc2->InputAt(1)->IsNewInstance()) << *merge_alloc2; - EXPECT_INS_EQ(merge_alloc2->InputAt(1)->InputAt(0), cls2) << *merge_alloc2; - EXPECT_INS_EQ(merge_alloc2->InputAt(0), graph_->GetNullConstant()); - HPredicatedInstanceFieldGet* pred_get1 = - FindOrNull(pred_gets.begin(), pred_gets.end(), [&](HPredicatedInstanceFieldGet* pg) { - return pg->GetTarget() == merge_alloc1; - }); - HPredicatedInstanceFieldGet* pred_get2 = - FindOrNull(pred_gets.begin(), pred_gets.end(), [&](HPredicatedInstanceFieldGet* pg) { - return pg->GetTarget() == merge_alloc2; - }); - ASSERT_NE(pred_get1, nullptr); - EXPECT_INS_EQ(pred_get1->GetTarget(), merge_alloc1); - EXPECT_INS_EQ(pred_get1->GetDefaultValue(), merge_value_return1) - << " pred-get is: " << *pred_get1; - EXPECT_INS_EQ(merge_value_return1->InputAt(0), graph_->GetIntConstant(0)) - << " merge val is: " << *merge_value_return1; - EXPECT_INS_EQ(merge_value_return1->InputAt(1), c2) << " merge val is: " << *merge_value_return1; - ASSERT_NE(pred_get2, nullptr); - EXPECT_INS_EQ(pred_get2->GetTarget(), merge_alloc2); - EXPECT_INS_EQ(pred_get2->GetDefaultValue(), merge_value_return2) - << " pred-get is: " << *pred_get2; - EXPECT_INS_EQ(merge_value_return2->InputAt(1), graph_->GetIntConstant(0)) - << " merge val is: " << *merge_value_return1; - EXPECT_INS_EQ(merge_value_return2->InputAt(0), c12) << " merge val is: " << *merge_value_return1; -} - -// Based on structure seen in `java.util.List -// java.util.Collections.checkedList(java.util.List, java.lang.Class)` -// Incorrect accounting would cause attempts to materialize both obj1 and obj2 -// in each of the materialization blocks. -// // ENTRY -// Obj obj; -// if (param1) { -// // needs to be moved after param2 check -// obj1 = new Obj1(); -// obj1.foo = 33; -// if (param2) { -// return obj1.foo; -// } -// obj = obj1; -// } else { -// obj2 = new Obj2(); -// obj2.foo = 44; -// if (param2) { -// return obj2.foo; -// } -// obj = obj2; -// } -// EXIT -// // obj = PHI[obj1, obj2] -// // NB The phi acts as an escape for both obj1 and obj2 meaning as far as the -// // LSA is concerned the escape frontier is left_crit_break->breturn and -// // right_crit_break->breturn for both even though only one of the objects is -// // actually live at each edge. -// // TODO In the future we really should track liveness through PHIs which would -// // allow us to entirely remove the allocation in this test. -// return obj.foo; -TEST_F(LoadStoreEliminationTest, MultiPredicatedLoad3) { - VariableSizedHandleScope vshs(Thread::Current()); - CreateGraph(/*handles=*/&vshs); - AdjacencyListGraph blks(SetupFromAdjacencyList("entry", - "exit", - {{"entry", "left"}, - {"left", "left_end"}, - {"left_end", "breturn"}, - {"left", "left_exit_early"}, - {"left_exit_early", "exit"}, - {"entry", "right"}, - {"right", "right_end"}, - {"right_end", "breturn"}, - {"right", "right_exit_early"}, - {"right_exit_early", "exit"}, - {"breturn", "exit"}})); -#define GET_BLOCK(name) HBasicBlock* name = blks.Get(#name) - GET_BLOCK(entry); - GET_BLOCK(exit); - GET_BLOCK(breturn); - GET_BLOCK(left); - GET_BLOCK(left_end); - GET_BLOCK(left_exit_early); - GET_BLOCK(right); - GET_BLOCK(right_end); - GET_BLOCK(right_exit_early); -#undef GET_BLOCK - EnsurePredecessorOrder(breturn, {left_end, right_end}); - HInstruction* param1 = MakeParam(DataType::Type::kBool); - HInstruction* param2 = MakeParam(DataType::Type::kBool); - HInstruction* c33 = graph_->GetIntConstant(33); - HInstruction* c44 = graph_->GetIntConstant(44); - - HInstruction* if_inst = new (GetAllocator()) HIf(param1); - entry->AddInstruction(if_inst); - - HInstruction* cls1 = MakeClassLoad(); - HInstruction* new_inst1 = MakeNewInstance(cls1); - HInstruction* write1 = MakeIFieldSet(new_inst1, c33, MemberOffset(32)); - HInstruction* if_left = new (GetAllocator()) HIf(param2); - left->AddInstruction(cls1); - left->AddInstruction(new_inst1); - left->AddInstruction(write1); - left->AddInstruction(if_left); - ManuallyBuildEnvFor(cls1, {}); - new_inst1->CopyEnvironmentFrom(cls1->GetEnvironment()); - - left_end->AddInstruction(new (GetAllocator()) HGoto()); - - HInstruction* early_exit_left_read = - MakeIFieldGet(new_inst1, DataType::Type::kInt32, MemberOffset(32)); - HInstruction* early_exit_left_return = new (GetAllocator()) HReturn(early_exit_left_read); - left_exit_early->AddInstruction(early_exit_left_read); - left_exit_early->AddInstruction(early_exit_left_return); - - HInstruction* cls2 = MakeClassLoad(); - HInstruction* new_inst2 = MakeNewInstance(cls2); - HInstruction* write2 = MakeIFieldSet(new_inst2, c44, MemberOffset(32)); - HInstruction* if_right = new (GetAllocator()) HIf(param2); - right->AddInstruction(cls2); - right->AddInstruction(new_inst2); - right->AddInstruction(write2); - right->AddInstruction(if_right); - cls2->CopyEnvironmentFrom(cls1->GetEnvironment()); - new_inst2->CopyEnvironmentFrom(cls2->GetEnvironment()); - - right_end->AddInstruction(new (GetAllocator()) HGoto()); - - HInstruction* early_exit_right_read = - MakeIFieldGet(new_inst2, DataType::Type::kInt32, MemberOffset(32)); - HInstruction* early_exit_right_return = new (GetAllocator()) HReturn(early_exit_right_read); - right_exit_early->AddInstruction(early_exit_right_read); - right_exit_early->AddInstruction(early_exit_right_return); - - HPhi* bottom_phi = MakePhi({new_inst1, new_inst2}); - HInstruction* read_bottom = MakeIFieldGet(bottom_phi, DataType::Type::kInt32, MemberOffset(32)); + HInstruction* read_bottom = new (GetAllocator()) HInstanceFieldGet(new_inst, + nullptr, + DataType::Type::kInt32, + MemberOffset(32), + false, + 0, + 0, + graph_->GetDexFile(), + 0); HInstruction* return_exit = new (GetAllocator()) HReturn(read_bottom); - breturn->AddPhi(bottom_phi); breturn->AddInstruction(read_bottom); breturn->AddInstruction(return_exit); - SetupExit(exit); - + HInstruction* exit_instruction = new (GetAllocator()) HExit(); + exit->AddInstruction(exit_instruction); // PerformLSE expects this to be empty. graph_->ClearDominanceInformation(); - LOG(INFO) << "Pre LSE " << blks; - PerformLSEWithPartial(); - LOG(INFO) << "Post LSE " << blks; - - EXPECT_INS_REMOVED(early_exit_left_read); - EXPECT_INS_REMOVED(early_exit_right_read); - EXPECT_INS_RETAINED(bottom_phi); - EXPECT_INS_RETAINED(read_bottom); - EXPECT_INS_EQ(early_exit_left_return->InputAt(0), c33); - EXPECT_INS_EQ(early_exit_right_return->InputAt(0), c44); - // These assert there is only 1 HNewInstance in the given blocks. - HNewInstance* moved_ni1 = - FindSingleInstruction<HNewInstance>(graph_, left_end->GetSinglePredecessor()); - HNewInstance* moved_ni2 = - FindSingleInstruction<HNewInstance>(graph_, right_end->GetSinglePredecessor()); - ASSERT_NE(moved_ni1, nullptr); - ASSERT_NE(moved_ni2, nullptr); - EXPECT_INS_EQ(bottom_phi->InputAt(0), moved_ni1); - EXPECT_INS_EQ(bottom_phi->InputAt(1), moved_ni2); -} - -// Based on structure seen in `java.util.Set java.util.Collections$UnmodifiableMap.entrySet()` -// We end up having to update a PHI generated by normal LSE. -// // ENTRY -// Obj obj_init = param_obj.BAR; -// if (param1) { -// Obj other = new Obj(); -// other.foo = 42; -// if (param2) { -// return other.foo; -// } else { -// param_obj.BAR = other; -// } -// } else { } -// EXIT -// LSE Turns this into PHI[obj_init, other] -// read_bottom = param_obj.BAR; -// // won't be changed. The escape happens with .BAR set so this is in escaping cohort. -// return read_bottom.foo; -TEST_F(LoadStoreEliminationTest, MultiPredicatedLoad4) { - VariableSizedHandleScope vshs(Thread::Current()); - CreateGraph(/*handles=*/&vshs); - AdjacencyListGraph blks(SetupFromAdjacencyList("entry", - "exit", - {{"entry", "left"}, - {"left", "left_early_return"}, - {"left_early_return", "exit"}, - {"left", "left_write_escape"}, - {"left_write_escape", "breturn"}, - {"entry", "right"}, - {"right", "breturn"}, - {"breturn", "exit"}})); -#define GET_BLOCK(name) HBasicBlock* name = blks.Get(#name) - GET_BLOCK(entry); - GET_BLOCK(exit); - GET_BLOCK(breturn); - GET_BLOCK(left); - GET_BLOCK(left_early_return); - GET_BLOCK(left_write_escape); - GET_BLOCK(right); -#undef GET_BLOCK - MemberOffset foo_offset = MemberOffset(32); - MemberOffset bar_offset = MemberOffset(20); - EnsurePredecessorOrder(breturn, {left_write_escape, right}); - HInstruction* c42 = graph_->GetIntConstant(42); - HInstruction* param1 = MakeParam(DataType::Type::kBool); - HInstruction* param2 = MakeParam(DataType::Type::kBool); - HInstruction* param_obj = MakeParam(DataType::Type::kReference); - - HInstruction* get_initial = MakeIFieldGet(param_obj, DataType::Type::kReference, bar_offset); - HInstruction* if_inst = new (GetAllocator()) HIf(param1); - entry->AddInstruction(get_initial); - entry->AddInstruction(if_inst); - - HInstruction* cls1 = MakeClassLoad(); - HInstruction* new_inst1 = MakeNewInstance(cls1); - HInstruction* write1 = MakeIFieldSet(new_inst1, c42, foo_offset); - HInstruction* if_left = new (GetAllocator()) HIf(param2); - left->AddInstruction(cls1); - left->AddInstruction(new_inst1); - left->AddInstruction(write1); - left->AddInstruction(if_left); - ManuallyBuildEnvFor(cls1, {}); - new_inst1->CopyEnvironmentFrom(cls1->GetEnvironment()); - - HInstruction* read_early_return = MakeIFieldGet(new_inst1, DataType::Type::kInt32, foo_offset); - HInstruction* return_early = new (GetAllocator()) HReturn(read_early_return); - left_early_return->AddInstruction(read_early_return); - left_early_return->AddInstruction(return_early); - - HInstruction* write_escape = MakeIFieldSet(param_obj, new_inst1, bar_offset); - HInstruction* write_goto = new (GetAllocator()) HGoto(); - left_write_escape->AddInstruction(write_escape); - left_write_escape->AddInstruction(write_goto); - - right->AddInstruction(new (GetAllocator()) HGoto()); - - HInstruction* read_bottom = MakeIFieldGet(param_obj, DataType::Type::kReference, bar_offset); - HInstruction* final_read = MakeIFieldGet(read_bottom, DataType::Type::kInt32, foo_offset); - HInstruction* return_exit = new (GetAllocator()) HReturn(final_read); - breturn->AddInstruction(read_bottom); - breturn->AddInstruction(final_read); - breturn->AddInstruction(return_exit); - - SetupExit(exit); - - // PerformLSE expects this to be empty. - graph_->ClearDominanceInformation(); - LOG(INFO) << "Pre LSE " << blks; - PerformLSEWithPartial(); - LOG(INFO) << "Post LSE " << blks; - - EXPECT_INS_REMOVED(read_bottom); - EXPECT_INS_REMOVED(read_early_return); - EXPECT_INS_EQ(return_early->InputAt(0), c42); - EXPECT_INS_RETAINED(final_read); - HNewInstance* moved_ni = - FindSingleInstruction<HNewInstance>(graph_, left_write_escape->GetSinglePredecessor()); - EXPECT_TRUE(final_read->InputAt(0)->IsPhi()); - EXPECT_INS_EQ(final_read->InputAt(0)->InputAt(0), moved_ni); - EXPECT_INS_EQ(final_read->InputAt(0)->InputAt(1), get_initial); -} - -// // ENTRY -// obj = new Obj(); -// obj.field = 3; -// if (parameter_value) { -// // LEFT -// escape(obj); -// } else { -// // RIGHT -// // ELIMINATE -// obj.field = 2; -// } -// // MERGE -// if (second_param) { -// // NON_ESCAPE -// obj.field = 1; -// noescape(); -// } -// EXIT -// predicated-ELIMINATE -// return obj.field -TEST_F(LoadStoreEliminationTest, PredicatedLoad2) { - VariableSizedHandleScope vshs(Thread::Current()); - CreateGraph(/*handles=*/&vshs); - AdjacencyListGraph blks(SetupFromAdjacencyList("entry", - "exit", - {{"entry", "left"}, - {"entry", "right"}, - {"left", "merge"}, - {"right", "merge"}, - {"merge", "non_escape"}, - {"non_escape", "breturn"}, - {"merge", "crit_break"}, - {"crit_break", "breturn"}, - {"breturn", "exit"}})); -#define GET_BLOCK(name) HBasicBlock* name = blks.Get(#name) - GET_BLOCK(entry); - GET_BLOCK(exit); - GET_BLOCK(breturn); - GET_BLOCK(left); - GET_BLOCK(right); - GET_BLOCK(merge); - GET_BLOCK(non_escape); - GET_BLOCK(crit_break); -#undef GET_BLOCK - EnsurePredecessorOrder(merge, {left, right}); - EnsurePredecessorOrder(breturn, {crit_break, non_escape}); - HInstruction* bool_value = MakeParam(DataType::Type::kBool); - HInstruction* bool_value2 = MakeParam(DataType::Type::kBool); - HInstruction* null_const = graph_->GetNullConstant(); - HInstruction* c1 = graph_->GetIntConstant(1); - HInstruction* c2 = graph_->GetIntConstant(2); - HInstruction* c3 = graph_->GetIntConstant(3); - - HInstruction* cls = MakeClassLoad(); - HInstruction* new_inst = MakeNewInstance(cls); - HInstruction* write_entry = MakeIFieldSet(new_inst, c3, MemberOffset(32)); - HInstruction* if_inst = new (GetAllocator()) HIf(bool_value); - entry->AddInstruction(cls); - entry->AddInstruction(new_inst); - entry->AddInstruction(write_entry); - entry->AddInstruction(if_inst); - ManuallyBuildEnvFor(cls, {}); - new_inst->CopyEnvironmentFrom(cls->GetEnvironment()); - - HInstruction* call_left = MakeInvoke(DataType::Type::kVoid, { new_inst }); - HInstruction* goto_left = new (GetAllocator()) HGoto(); - left->AddInstruction(call_left); - left->AddInstruction(goto_left); - call_left->CopyEnvironmentFrom(cls->GetEnvironment()); - - HInstruction* write_right = MakeIFieldSet(new_inst, c2, MemberOffset(32)); - HInstruction* goto_right = new (GetAllocator()) HGoto(); - right->AddInstruction(write_right); - right->AddInstruction(goto_right); - - HInstruction* merge_if = new (GetAllocator()) HIf(bool_value2); - merge->AddInstruction(merge_if); - - crit_break->AddInstruction(new (GetAllocator()) HGoto()); - - HInstruction* write_non_escape = MakeIFieldSet(new_inst, c1, MemberOffset(32)); - HInstruction* non_escape_call = MakeInvoke(DataType::Type::kVoid, {}); - HInstruction* non_escape_goto = new (GetAllocator()) HGoto(); - non_escape->AddInstruction(write_non_escape); - non_escape->AddInstruction(non_escape_call); - non_escape->AddInstruction(non_escape_goto); - non_escape_call->CopyEnvironmentFrom(cls->GetEnvironment()); - - HInstruction* read_bottom = MakeIFieldGet(new_inst, DataType::Type::kInt32, MemberOffset(32)); - HInstruction* return_exit = new (GetAllocator()) HReturn(read_bottom); - breturn->AddInstruction(read_bottom); - breturn->AddInstruction(return_exit); - - SetupExit(exit); - - // PerformLSE expects this to be empty. - graph_->ClearDominanceInformation(); - LOG(INFO) << "Pre LSE " << blks; - PerformLSEWithPartial(); - LOG(INFO) << "Post LSE " << blks; - - EXPECT_INS_REMOVED(read_bottom); - EXPECT_INS_REMOVED(write_right); - EXPECT_INS_RETAINED(call_left); - std::vector<HPhi*> merges; - HPredicatedInstanceFieldGet* pred_get = - FindSingleInstruction<HPredicatedInstanceFieldGet>(graph_, breturn); - std::tie(merges) = FindAllInstructions<HPhi>(graph_); - ASSERT_EQ(merges.size(), 3u); - HPhi* merge_value_return = FindOrNull(merges.begin(), merges.end(), [&](HPhi* p) { - return p->GetType() == DataType::Type::kInt32 && p->GetBlock() == breturn; - }); - HPhi* merge_value_merge = FindOrNull(merges.begin(), merges.end(), [&](HPhi* p) { - return p->GetType() == DataType::Type::kInt32 && p->GetBlock() != breturn; - }); - HPhi* merge_alloc = FindOrNull(merges.begin(), merges.end(), [](HPhi* p) { - return p->GetType() == DataType::Type::kReference; - }); - ASSERT_NE(merge_alloc, nullptr); - EXPECT_TRUE(merge_alloc->InputAt(0)->IsNewInstance()) << *merge_alloc; - EXPECT_INS_EQ(merge_alloc->InputAt(0)->InputAt(0), cls) - << " phi is: " << merge_alloc->DumpWithArgs(); - EXPECT_INS_EQ(merge_alloc->InputAt(1), null_const); - ASSERT_NE(pred_get, nullptr); - EXPECT_INS_EQ(pred_get->GetTarget(), merge_alloc); - EXPECT_INS_EQ(pred_get->GetDefaultValue(), merge_value_return) - << "get is " << pred_get->DumpWithArgs(); - EXPECT_INS_EQ(merge_value_return->InputAt(0), merge_value_merge) - << " phi is: " << *merge_value_return; - EXPECT_INS_EQ(merge_value_return->InputAt(1), c1) - << " phi is: " << merge_value_return->DumpWithArgs(); - EXPECT_INS_EQ(merge_value_merge->InputAt(0), graph_->GetIntConstant(0)) - << " phi is: " << *merge_value_merge; - EXPECT_INS_EQ(merge_value_merge->InputAt(1), c2) - << " phi is: " << merge_value_merge->DumpWithArgs(); -} - -// // ENTRY -// obj = new Obj(); -// obj.field = 3; -// if (parameter_value) { -// // LEFT -// escape(obj); -// } else { -// // RIGHT -// // ELIMINATE -// obj.field = 2; -// } -// // MERGE -// if (second_param) { -// // NON_ESCAPE -// obj.field = 1; -// } -// noescape(); -// EXIT -// predicated-ELIMINATE -// return obj.field -TEST_F(LoadStoreEliminationTest, PredicatedLoad3) { - VariableSizedHandleScope vshs(Thread::Current()); - CreateGraph(/*handles=*/&vshs); - AdjacencyListGraph blks(SetupFromAdjacencyList("entry", - "exit", - {{"entry", "left"}, - {"entry", "right"}, - {"left", "merge"}, - {"right", "merge"}, - {"merge", "non_escape"}, - {"non_escape", "breturn"}, - {"merge", "crit_break"}, - {"crit_break", "breturn"}, - {"breturn", "exit"}})); -#define GET_BLOCK(name) HBasicBlock* name = blks.Get(#name) - GET_BLOCK(entry); - GET_BLOCK(exit); - GET_BLOCK(breturn); - GET_BLOCK(left); - GET_BLOCK(right); - GET_BLOCK(merge); - GET_BLOCK(crit_break); - GET_BLOCK(non_escape); -#undef GET_BLOCK - EnsurePredecessorOrder(merge, {left, right}); - EnsurePredecessorOrder(breturn, {crit_break, non_escape}); - HInstruction* bool_value = MakeParam(DataType::Type::kBool); - HInstruction* bool_value2 = MakeParam(DataType::Type::kBool); - HInstruction* null_const = graph_->GetNullConstant(); - HInstruction* c1 = graph_->GetIntConstant(1); - HInstruction* c2 = graph_->GetIntConstant(2); - HInstruction* c3 = graph_->GetIntConstant(3); - - HInstruction* cls = MakeClassLoad(); - HInstruction* new_inst = MakeNewInstance(cls); - HInstruction* write_entry = MakeIFieldSet(new_inst, c3, MemberOffset(32)); - HInstruction* if_inst = new (GetAllocator()) HIf(bool_value); - entry->AddInstruction(cls); - entry->AddInstruction(new_inst); - entry->AddInstruction(write_entry); - entry->AddInstruction(if_inst); - ManuallyBuildEnvFor(cls, {}); - new_inst->CopyEnvironmentFrom(cls->GetEnvironment()); - - HInstruction* call_left = MakeInvoke(DataType::Type::kVoid, { new_inst }); - HInstruction* goto_left = new (GetAllocator()) HGoto(); - left->AddInstruction(call_left); - left->AddInstruction(goto_left); - call_left->CopyEnvironmentFrom(cls->GetEnvironment()); - - HInstruction* write_right = MakeIFieldSet(new_inst, c2, MemberOffset(32)); - HInstruction* goto_right = new (GetAllocator()) HGoto(); - right->AddInstruction(write_right); - right->AddInstruction(goto_right); - - HInstruction* merge_if = new (GetAllocator()) HIf(bool_value2); - merge->AddInstruction(merge_if); - - HInstruction* write_non_escape = MakeIFieldSet(new_inst, c1, MemberOffset(32)); - HInstruction* non_escape_goto = new (GetAllocator()) HGoto(); - non_escape->AddInstruction(write_non_escape); - non_escape->AddInstruction(non_escape_goto); - - crit_break->AddInstruction(new (GetAllocator()) HGoto()); - - HInstruction* bottom_call = MakeInvoke(DataType::Type::kVoid, {}); - HInstruction* read_bottom = MakeIFieldGet(new_inst, DataType::Type::kInt32, MemberOffset(32)); - HInstruction* return_exit = new (GetAllocator()) HReturn(read_bottom); - breturn->AddInstruction(bottom_call); - breturn->AddInstruction(read_bottom); - breturn->AddInstruction(return_exit); - bottom_call->CopyEnvironmentFrom(cls->GetEnvironment()); - - SetupExit(exit); - - // PerformLSE expects this to be empty. - graph_->ClearDominanceInformation(); - LOG(INFO) << "Pre LSE " << blks; - PerformLSEWithPartial(); - LOG(INFO) << "Post LSE " << blks; - - EXPECT_INS_REMOVED(read_bottom); - EXPECT_INS_REMOVED(write_right); - EXPECT_INS_RETAINED(call_left); - std::vector<HPhi*> merges; - HPredicatedInstanceFieldGet* pred_get = - FindSingleInstruction<HPredicatedInstanceFieldGet>(graph_, breturn); - std::tie(merges) = FindAllInstructions<HPhi>(graph_); - ASSERT_EQ(merges.size(), 3u); - HPhi* merge_value_return = FindOrNull(merges.begin(), merges.end(), [&](HPhi* p) { - return p->GetType() == DataType::Type::kInt32 && p->GetBlock() == breturn; - }); - HPhi* merge_value_merge = FindOrNull(merges.begin(), merges.end(), [&](HPhi* p) { - return p->GetType() == DataType::Type::kInt32 && p->GetBlock() != breturn; - }); - HPhi* merge_alloc = FindOrNull(merges.begin(), merges.end(), [](HPhi* p) { - return p->GetType() == DataType::Type::kReference; - }); - ASSERT_NE(merge_alloc, nullptr); - EXPECT_TRUE(merge_alloc->InputAt(0)->IsNewInstance()) << merge_alloc->DumpWithArgs(); - EXPECT_INS_EQ(merge_alloc->InputAt(0)->InputAt(0), cls) - << " phi is: " << merge_alloc->DumpWithArgs(); - EXPECT_INS_EQ(merge_alloc->InputAt(1), null_const); - ASSERT_NE(pred_get, nullptr); - EXPECT_INS_EQ(pred_get->GetTarget(), merge_alloc); - EXPECT_INS_EQ(pred_get->GetDefaultValue(), merge_value_return) - << "get is " << pred_get->DumpWithArgs(); - EXPECT_INS_EQ(merge_value_return->InputAt(0), merge_value_merge) - << " phi is: " << *merge_value_return; - EXPECT_INS_EQ(merge_value_return->InputAt(1), c1) << " phi is: " << *merge_value_return; - EXPECT_INS_EQ(merge_value_merge->InputAt(0), graph_->GetIntConstant(0)) - << " phi is: " << *merge_value_merge; - EXPECT_INS_EQ(merge_value_merge->InputAt(1), c2) << " phi is: " << *merge_value_merge; -} - -// // ENTRY -// obj = new Obj(); -// // ALL should be kept -// switch (parameter_value) { -// case 1: -// // Case1 -// obj.field = 1; -// call_func(obj); -// break; -// case 2: -// // Case2 -// obj.field = 2; -// call_func(obj); -// break; -// default: -// // Case3 -// obj.field = 3; -// do { -// if (test2()) { } else { obj.field = 5; } -// } while (test()); -// break; -// } -// EXIT -// // predicated-ELIMINATE -// return obj.field -TEST_F(LoadStoreEliminationTest, PartialLoopPhis1) { - VariableSizedHandleScope vshs(Thread::Current()); - CreateGraph(/*handles=*/&vshs); - AdjacencyListGraph blks(SetupFromAdjacencyList("entry", - "exit", - {{"entry", "bswitch"}, - {"bswitch", "case1"}, - {"bswitch", "case2"}, - {"bswitch", "case3"}, - {"case1", "breturn"}, - {"case2", "breturn"}, - {"case3", "loop_pre_header"}, - {"loop_pre_header", "loop_header"}, - {"loop_header", "loop_body"}, - {"loop_body", "loop_if_left"}, - {"loop_body", "loop_if_right"}, - {"loop_if_left", "loop_merge"}, - {"loop_if_right", "loop_merge"}, - {"loop_merge", "loop_end"}, - {"loop_end", "loop_header"}, - {"loop_end", "critical_break"}, - {"critical_break", "breturn"}, - {"breturn", "exit"}})); -#define GET_BLOCK(name) HBasicBlock* name = blks.Get(#name) - GET_BLOCK(entry); - GET_BLOCK(bswitch); - GET_BLOCK(exit); - GET_BLOCK(breturn); - GET_BLOCK(case1); - GET_BLOCK(case2); - GET_BLOCK(case3); - - GET_BLOCK(loop_pre_header); - GET_BLOCK(loop_header); - GET_BLOCK(loop_body); - GET_BLOCK(loop_if_left); - GET_BLOCK(loop_if_right); - GET_BLOCK(loop_merge); - GET_BLOCK(loop_end); - GET_BLOCK(critical_break); -#undef GET_BLOCK - EnsurePredecessorOrder(breturn, {case1, case2, critical_break}); - EnsurePredecessorOrder(loop_header, {loop_pre_header, loop_end}); - EnsurePredecessorOrder(loop_merge, {loop_if_left, loop_if_right}); - CHECK_SUBROUTINE_FAILURE(); - HInstruction* switch_val = MakeParam(DataType::Type::kInt32); - HInstruction* c1 = graph_->GetIntConstant(1); - HInstruction* c2 = graph_->GetIntConstant(2); - HInstruction* c3 = graph_->GetIntConstant(3); - HInstruction* c5 = graph_->GetIntConstant(5); - - HInstruction* cls = MakeClassLoad(); - HInstruction* new_inst = MakeNewInstance(cls); - HInstruction* entry_goto = new (GetAllocator()) HGoto(); - entry->AddInstruction(cls); - entry->AddInstruction(new_inst); - entry->AddInstruction(entry_goto); - ManuallyBuildEnvFor(cls, {}); - new_inst->CopyEnvironmentFrom(cls->GetEnvironment()); - - HInstruction* switch_inst = new (GetAllocator()) HPackedSwitch(0, 2, switch_val); - bswitch->AddInstruction(switch_inst); - - HInstruction* write_c1 = MakeIFieldSet(new_inst, c1, MemberOffset(32)); - HInstruction* call_c1 = MakeInvoke(DataType::Type::kVoid, { new_inst }); - HInstruction* goto_c1 = new (GetAllocator()) HGoto(); - case1->AddInstruction(write_c1); - case1->AddInstruction(call_c1); - case1->AddInstruction(goto_c1); - call_c1->CopyEnvironmentFrom(cls->GetEnvironment()); - - HInstruction* write_c2 = MakeIFieldSet(new_inst, c2, MemberOffset(32)); - HInstruction* call_c2 = MakeInvoke(DataType::Type::kVoid, { new_inst }); - HInstruction* goto_c2 = new (GetAllocator()) HGoto(); - case2->AddInstruction(write_c2); - case2->AddInstruction(call_c2); - case2->AddInstruction(goto_c2); - call_c2->CopyEnvironmentFrom(cls->GetEnvironment()); - - HInstruction* write_c3 = MakeIFieldSet(new_inst, c3, MemberOffset(32)); - HInstruction* goto_c3 = new (GetAllocator()) HGoto(); - case3->AddInstruction(write_c3); - case3->AddInstruction(goto_c3); - - HInstruction* goto_preheader = new (GetAllocator()) HGoto(); - loop_pre_header->AddInstruction(goto_preheader); - - HInstruction* suspend_check_header = new (GetAllocator()) HSuspendCheck(); - HInstruction* goto_header = new (GetAllocator()) HGoto(); - loop_header->AddInstruction(suspend_check_header); - loop_header->AddInstruction(goto_header); - suspend_check_header->CopyEnvironmentFrom(cls->GetEnvironment()); - - HInstruction* call_loop_body = MakeInvoke(DataType::Type::kBool, {}); - HInstruction* if_loop_body = new (GetAllocator()) HIf(call_loop_body); - loop_body->AddInstruction(call_loop_body); - loop_body->AddInstruction(if_loop_body); - call_loop_body->CopyEnvironmentFrom(cls->GetEnvironment()); - - HInstruction* goto_loop_left = new (GetAllocator()) HGoto(); - loop_if_left->AddInstruction(goto_loop_left); - - HInstruction* write_loop_right = MakeIFieldSet(new_inst, c5, MemberOffset(32)); - HInstruction* goto_loop_right = new (GetAllocator()) HGoto(); - loop_if_right->AddInstruction(write_loop_right); - loop_if_right->AddInstruction(goto_loop_right); - - HInstruction* goto_loop_merge = new (GetAllocator()) HGoto(); - loop_merge->AddInstruction(goto_loop_merge); - - HInstruction* call_end = MakeInvoke(DataType::Type::kBool, {}); - HInstruction* if_end = new (GetAllocator()) HIf(call_end); - loop_end->AddInstruction(call_end); - loop_end->AddInstruction(if_end); - call_end->CopyEnvironmentFrom(cls->GetEnvironment()); - - HInstruction* goto_critical_break = new (GetAllocator()) HGoto(); - critical_break->AddInstruction(goto_critical_break); - - HInstruction* read_bottom = MakeIFieldGet(new_inst, DataType::Type::kInt32, MemberOffset(32)); - HInstruction* return_exit = new (GetAllocator()) HReturn(read_bottom); - breturn->AddInstruction(read_bottom); - breturn->AddInstruction(return_exit); - - SetupExit(exit); - - // PerformLSE expects this to be empty. - graph_->ClearDominanceInformation(); - LOG(INFO) << "Pre LSE " << blks; - PerformLSEWithPartial(); - LOG(INFO) << "Post LSE " << blks; - - HPredicatedInstanceFieldGet* pred_get = - FindSingleInstruction<HPredicatedInstanceFieldGet>(graph_, breturn); - EXPECT_INS_REMOVED(read_bottom) << *read_bottom; - ASSERT_TRUE(pred_get != nullptr); - HPhi* inst_return_phi = pred_get->GetTarget()->AsPhi(); - ASSERT_TRUE(inst_return_phi != nullptr) << pred_get->GetTarget()->DumpWithArgs(); - EXPECT_INS_EQ(inst_return_phi->InputAt(0), - FindSingleInstruction<HNewInstance>(graph_, case1->GetSinglePredecessor())); - EXPECT_INS_EQ(inst_return_phi->InputAt(1), - FindSingleInstruction<HNewInstance>(graph_, case2->GetSinglePredecessor())); - EXPECT_INS_EQ(inst_return_phi->InputAt(2), graph_->GetNullConstant()); - HPhi* inst_value_phi = pred_get->GetDefaultValue()->AsPhi(); - ASSERT_TRUE(inst_value_phi != nullptr) << pred_get->GetDefaultValue()->DumpWithArgs(); - EXPECT_INS_EQ(inst_value_phi->InputAt(0), graph_->GetIntConstant(0)); - EXPECT_INS_EQ(inst_value_phi->InputAt(1), graph_->GetIntConstant(0)); - HPhi* loop_merge_phi = FindSingleInstruction<HPhi>(graph_, loop_merge); - ASSERT_TRUE(loop_merge_phi != nullptr); - HPhi* loop_header_phi = FindSingleInstruction<HPhi>(graph_, loop_header); - ASSERT_TRUE(loop_header_phi != nullptr); - EXPECT_INS_EQ(loop_header_phi->InputAt(0), c3); - EXPECT_INS_EQ(loop_header_phi->InputAt(1), loop_merge_phi); - EXPECT_INS_EQ(loop_merge_phi->InputAt(0), loop_header_phi); - EXPECT_INS_EQ(loop_merge_phi->InputAt(1), c5); - EXPECT_INS_EQ(inst_value_phi->InputAt(2), loop_merge_phi); - EXPECT_INS_RETAINED(write_c1) << *write_c1; - EXPECT_INS_RETAINED(write_c2) << *write_c2; - EXPECT_INS_REMOVED(write_c3) << *write_c3; - EXPECT_INS_REMOVED(write_loop_right) << *write_loop_right; -} - -// // ENTRY -// obj = new Obj(); -// switch (parameter_value) { -// case 1: -// // Case1 -// obj.field = 1; -// call_func(obj); -// break; -// case 2: -// // Case2 -// obj.field = 2; -// call_func(obj); -// break; -// default: -// // Case3 -// obj.field = 3; -// while (!test()) { -// if (test2()) { } else { obj.field = 5; } -// } -// break; -// } -// EXIT -// // predicated-ELIMINATE -// return obj.field -TEST_F(LoadStoreEliminationTest, PartialLoopPhis2) { - VariableSizedHandleScope vshs(Thread::Current()); - CreateGraph(/*handles=*/&vshs); - AdjacencyListGraph blks(SetupFromAdjacencyList("entry", - "exit", - {{"entry", "bswitch"}, - {"bswitch", "case1"}, - {"bswitch", "case2"}, - {"bswitch", "case3"}, - {"case1", "breturn"}, - {"case2", "breturn"}, - {"case3", "loop_pre_header"}, - - {"loop_pre_header", "loop_header"}, - {"loop_header", "critical_break"}, - {"loop_header", "loop_body"}, - {"loop_body", "loop_if_left"}, - {"loop_body", "loop_if_right"}, - {"loop_if_left", "loop_merge"}, - {"loop_if_right", "loop_merge"}, - {"loop_merge", "loop_header"}, - - {"critical_break", "breturn"}, - {"breturn", "exit"}})); -#define GET_BLOCK(name) HBasicBlock* name = blks.Get(#name) - GET_BLOCK(entry); - GET_BLOCK(bswitch); - GET_BLOCK(exit); - GET_BLOCK(breturn); - GET_BLOCK(case1); - GET_BLOCK(case2); - GET_BLOCK(case3); - - GET_BLOCK(loop_pre_header); - GET_BLOCK(loop_header); - GET_BLOCK(loop_body); - GET_BLOCK(loop_if_left); - GET_BLOCK(loop_if_right); - GET_BLOCK(loop_merge); - GET_BLOCK(critical_break); -#undef GET_BLOCK - EnsurePredecessorOrder(breturn, {case1, case2, critical_break}); - EnsurePredecessorOrder(loop_header, {loop_pre_header, loop_merge}); - EnsurePredecessorOrder(loop_merge, {loop_if_left, loop_if_right}); - CHECK_SUBROUTINE_FAILURE(); - HInstruction* switch_val = MakeParam(DataType::Type::kInt32); - HInstruction* c1 = graph_->GetIntConstant(1); - HInstruction* c2 = graph_->GetIntConstant(2); - HInstruction* c3 = graph_->GetIntConstant(3); - HInstruction* c5 = graph_->GetIntConstant(5); - - HInstruction* cls = MakeClassLoad(); - HInstruction* new_inst = MakeNewInstance(cls); - HInstruction* entry_goto = new (GetAllocator()) HGoto(); - entry->AddInstruction(cls); - entry->AddInstruction(new_inst); - entry->AddInstruction(entry_goto); - ManuallyBuildEnvFor(cls, {}); - new_inst->CopyEnvironmentFrom(cls->GetEnvironment()); - - HInstruction* switch_inst = new (GetAllocator()) HPackedSwitch(0, 2, switch_val); - bswitch->AddInstruction(switch_inst); - - HInstruction* write_c1 = MakeIFieldSet(new_inst, c1, MemberOffset(32)); - HInstruction* call_c1 = MakeInvoke(DataType::Type::kVoid, { new_inst }); - HInstruction* goto_c1 = new (GetAllocator()) HGoto(); - case1->AddInstruction(write_c1); - case1->AddInstruction(call_c1); - case1->AddInstruction(goto_c1); - call_c1->CopyEnvironmentFrom(cls->GetEnvironment()); - - HInstruction* write_c2 = MakeIFieldSet(new_inst, c2, MemberOffset(32)); - HInstruction* call_c2 = MakeInvoke(DataType::Type::kVoid, { new_inst }); - HInstruction* goto_c2 = new (GetAllocator()) HGoto(); - case2->AddInstruction(write_c2); - case2->AddInstruction(call_c2); - case2->AddInstruction(goto_c2); - call_c2->CopyEnvironmentFrom(cls->GetEnvironment()); - - HInstruction* write_c3 = MakeIFieldSet(new_inst, c3, MemberOffset(32)); - HInstruction* goto_c3 = new (GetAllocator()) HGoto(); - case3->AddInstruction(write_c3); - case3->AddInstruction(goto_c3); - - HInstruction* goto_preheader = new (GetAllocator()) HGoto(); - loop_pre_header->AddInstruction(goto_preheader); - - HInstruction* suspend_check_header = new (GetAllocator()) HSuspendCheck(); - HInstruction* call_header = MakeInvoke(DataType::Type::kBool, {}); - HInstruction* if_header = new (GetAllocator()) HIf(call_header); - loop_header->AddInstruction(suspend_check_header); - loop_header->AddInstruction(call_header); - loop_header->AddInstruction(if_header); - call_header->CopyEnvironmentFrom(cls->GetEnvironment()); - suspend_check_header->CopyEnvironmentFrom(cls->GetEnvironment()); - - HInstruction* call_loop_body = MakeInvoke(DataType::Type::kBool, {}); - HInstruction* if_loop_body = new (GetAllocator()) HIf(call_loop_body); - loop_body->AddInstruction(call_loop_body); - loop_body->AddInstruction(if_loop_body); - call_loop_body->CopyEnvironmentFrom(cls->GetEnvironment()); - - HInstruction* goto_loop_left = new (GetAllocator()) HGoto(); - loop_if_left->AddInstruction(goto_loop_left); - - HInstruction* write_loop_right = MakeIFieldSet(new_inst, c5, MemberOffset(32)); - HInstruction* goto_loop_right = new (GetAllocator()) HGoto(); - loop_if_right->AddInstruction(write_loop_right); - loop_if_right->AddInstruction(goto_loop_right); - - HInstruction* goto_loop_merge = new (GetAllocator()) HGoto(); - loop_merge->AddInstruction(goto_loop_merge); - - HInstruction* goto_critical_break = new (GetAllocator()) HGoto(); - critical_break->AddInstruction(goto_critical_break); - - HInstruction* read_bottom = MakeIFieldGet(new_inst, DataType::Type::kInt32, MemberOffset(32)); - HInstruction* return_exit = new (GetAllocator()) HReturn(read_bottom); - breturn->AddInstruction(read_bottom); - breturn->AddInstruction(return_exit); - - SetupExit(exit); - - // PerformLSE expects this to be empty. - graph_->ClearDominanceInformation(); - LOG(INFO) << "Pre LSE " << blks; - PerformLSEWithPartial(); - LOG(INFO) << "Post LSE " << blks; - - HPredicatedInstanceFieldGet* pred_get = - FindSingleInstruction<HPredicatedInstanceFieldGet>(graph_, breturn); - EXPECT_INS_REMOVED(read_bottom) << *read_bottom; - ASSERT_TRUE(pred_get != nullptr); - HPhi* inst_return_phi = pred_get->GetTarget()->AsPhi(); - ASSERT_TRUE(inst_return_phi != nullptr) << pred_get->GetTarget()->DumpWithArgs(); - EXPECT_INS_EQ(inst_return_phi->InputAt(0), - FindSingleInstruction<HNewInstance>(graph_, case1->GetSinglePredecessor())); - EXPECT_INS_EQ(inst_return_phi->InputAt(1), - FindSingleInstruction<HNewInstance>(graph_, case2->GetSinglePredecessor())); - EXPECT_INS_EQ(inst_return_phi->InputAt(2), graph_->GetNullConstant()); - HPhi* inst_value_phi = pred_get->GetDefaultValue()->AsPhi(); - ASSERT_TRUE(inst_value_phi != nullptr) << pred_get->GetDefaultValue()->DumpWithArgs(); - EXPECT_INS_EQ(inst_value_phi->InputAt(0), graph_->GetIntConstant(0)); - EXPECT_INS_EQ(inst_value_phi->InputAt(1), graph_->GetIntConstant(0)); - HPhi* loop_merge_phi = FindSingleInstruction<HPhi>(graph_, loop_merge); - ASSERT_TRUE(loop_merge_phi != nullptr); - HPhi* loop_header_phi = FindSingleInstruction<HPhi>(graph_, loop_header); - ASSERT_TRUE(loop_header_phi != nullptr); - EXPECT_INS_EQ(loop_header_phi->InputAt(0), c3); - EXPECT_INS_EQ(loop_header_phi->InputAt(1), loop_merge_phi); - EXPECT_INS_EQ(loop_merge_phi->InputAt(0), loop_header_phi); - EXPECT_INS_EQ(loop_merge_phi->InputAt(1), c5); - EXPECT_INS_EQ(inst_value_phi->InputAt(2), loop_header_phi); - EXPECT_INS_RETAINED(write_c1) << *write_c1; - EXPECT_INS_RETAINED(write_c2) << *write_c2; - EXPECT_INS_REMOVED(write_c3) << *write_c3; - EXPECT_INS_REMOVED(write_loop_right) << *write_loop_right; -} - -// // ENTRY -// obj = new Obj(); -// obj.field = 3; -// while (!test()) { -// if (test2()) { } else { obj.field = 5; } -// } -// if (parameter_value) { -// escape(obj); -// } -// EXIT -// return obj.field -TEST_F(LoadStoreEliminationTest, PartialLoopPhis3) { - VariableSizedHandleScope vshs(Thread::Current()); - CreateGraph(/*handles=*/&vshs); - AdjacencyListGraph blks(SetupFromAdjacencyList("entry", - "exit", - {{"entry", "loop_pre_header"}, - - {"loop_pre_header", "loop_header"}, - {"loop_header", "escape_check"}, - {"loop_header", "loop_body"}, - {"loop_body", "loop_if_left"}, - {"loop_body", "loop_if_right"}, - {"loop_if_left", "loop_merge"}, - {"loop_if_right", "loop_merge"}, - {"loop_merge", "loop_header"}, - - {"escape_check", "escape"}, - {"escape_check", "no_escape"}, - {"no_escape", "breturn"}, - {"escape", "breturn"}, - {"breturn", "exit"}})); -#define GET_BLOCK(name) HBasicBlock* name = blks.Get(#name) - GET_BLOCK(entry); - GET_BLOCK(exit); - GET_BLOCK(breturn); - GET_BLOCK(no_escape); - GET_BLOCK(escape); - GET_BLOCK(escape_check); - - GET_BLOCK(loop_pre_header); - GET_BLOCK(loop_header); - GET_BLOCK(loop_body); - GET_BLOCK(loop_if_left); - GET_BLOCK(loop_if_right); - GET_BLOCK(loop_merge); -#undef GET_BLOCK - EnsurePredecessorOrder(breturn, {no_escape, escape}); - EnsurePredecessorOrder(loop_header, {loop_pre_header, loop_merge}); - EnsurePredecessorOrder(loop_merge, {loop_if_left, loop_if_right}); - CHECK_SUBROUTINE_FAILURE(); - HInstruction* bool_val = MakeParam(DataType::Type::kBool); - HInstruction* c3 = graph_->GetIntConstant(3); - HInstruction* c5 = graph_->GetIntConstant(5); - - HInstruction* cls = MakeClassLoad(); - HInstruction* new_inst = MakeNewInstance(cls); - HInstruction* entry_goto = new (GetAllocator()) HGoto(); - entry->AddInstruction(cls); - entry->AddInstruction(new_inst); - entry->AddInstruction(entry_goto); - ManuallyBuildEnvFor(cls, {}); - new_inst->CopyEnvironmentFrom(cls->GetEnvironment()); - - HInstruction* write_pre_header = MakeIFieldSet(new_inst, c3, MemberOffset(32)); - HInstruction* goto_preheader = new (GetAllocator()) HGoto(); - loop_pre_header->AddInstruction(write_pre_header); - loop_pre_header->AddInstruction(goto_preheader); - - HInstruction* suspend_check_header = new (GetAllocator()) HSuspendCheck(); - HInstruction* call_header = MakeInvoke(DataType::Type::kBool, {}); - HInstruction* if_header = new (GetAllocator()) HIf(call_header); - loop_header->AddInstruction(suspend_check_header); - loop_header->AddInstruction(call_header); - loop_header->AddInstruction(if_header); - call_header->CopyEnvironmentFrom(cls->GetEnvironment()); - suspend_check_header->CopyEnvironmentFrom(cls->GetEnvironment()); - - HInstruction* call_loop_body = MakeInvoke(DataType::Type::kBool, {}); - HInstruction* if_loop_body = new (GetAllocator()) HIf(call_loop_body); - loop_body->AddInstruction(call_loop_body); - loop_body->AddInstruction(if_loop_body); - call_loop_body->CopyEnvironmentFrom(cls->GetEnvironment()); - - HInstruction* goto_loop_left = new (GetAllocator()) HGoto(); - loop_if_left->AddInstruction(goto_loop_left); - - HInstruction* write_loop_right = MakeIFieldSet(new_inst, c5, MemberOffset(32)); - HInstruction* goto_loop_right = new (GetAllocator()) HGoto(); - loop_if_right->AddInstruction(write_loop_right); - loop_if_right->AddInstruction(goto_loop_right); - - HInstruction* goto_loop_merge = new (GetAllocator()) HGoto(); - loop_merge->AddInstruction(goto_loop_merge); - - HInstruction* if_esc_check = new (GetAllocator()) HIf(bool_val); - escape_check->AddInstruction(if_esc_check); - - HInstruction* call_escape = MakeInvoke(DataType::Type::kVoid, { new_inst }); - HInstruction* goto_escape = new (GetAllocator()) HGoto(); - escape->AddInstruction(call_escape); - escape->AddInstruction(goto_escape); - call_escape->CopyEnvironmentFrom(cls->GetEnvironment()); - - HInstruction* goto_no_escape = new (GetAllocator()) HGoto(); - no_escape->AddInstruction(goto_no_escape); - - HInstruction* read_bottom = MakeIFieldGet(new_inst, DataType::Type::kInt32, MemberOffset(32)); - HInstruction* return_exit = new (GetAllocator()) HReturn(read_bottom); - breturn->AddInstruction(read_bottom); - breturn->AddInstruction(return_exit); - - SetupExit(exit); - - // PerformLSE expects this to be empty. - graph_->ClearDominanceInformation(); - LOG(INFO) << "Pre LSE " << blks; - PerformLSEWithPartial(); - LOG(INFO) << "Post LSE " << blks; - - HPredicatedInstanceFieldGet* pred_get = - FindSingleInstruction<HPredicatedInstanceFieldGet>(graph_, breturn); - EXPECT_INS_REMOVED(read_bottom) << *read_bottom; - ASSERT_TRUE(pred_get != nullptr); - HPhi* inst_return_phi = pred_get->GetTarget()->AsPhi(); - ASSERT_TRUE(inst_return_phi != nullptr) << pred_get->GetTarget()->DumpWithArgs(); - EXPECT_INS_EQ(inst_return_phi->InputAt(0), graph_->GetNullConstant()); - EXPECT_INS_EQ(inst_return_phi->InputAt(1), - FindSingleInstruction<HNewInstance>(graph_, escape->GetSinglePredecessor())); - HPhi* inst_value_phi = pred_get->GetDefaultValue()->AsPhi(); - ASSERT_TRUE(inst_value_phi != nullptr) << pred_get->GetDefaultValue()->DumpWithArgs(); - HPhi* loop_header_phi = FindSingleInstruction<HPhi>(graph_, loop_header); - HPhi* loop_merge_phi = FindSingleInstruction<HPhi>(graph_, loop_merge); - EXPECT_INS_EQ(inst_value_phi->InputAt(0), loop_header_phi); - EXPECT_INS_EQ(inst_value_phi->InputAt(1), graph_->GetIntConstant(0)); - EXPECT_INS_EQ(loop_header_phi->InputAt(0), c3); - EXPECT_INS_EQ(loop_header_phi->InputAt(1), loop_merge_phi); - EXPECT_INS_EQ(loop_merge_phi->InputAt(0), loop_header_phi); - EXPECT_INS_EQ(loop_merge_phi->InputAt(1), c5); - HInstanceFieldSet* mat_set = - FindSingleInstruction<HInstanceFieldSet>(graph_, escape->GetSinglePredecessor()); - ASSERT_NE(mat_set, nullptr); - EXPECT_INS_EQ(mat_set->InputAt(1), loop_header_phi); - EXPECT_INS_REMOVED(write_loop_right) << *write_loop_right; - EXPECT_INS_REMOVED(write_pre_header) << *write_pre_header; -} - -// // ENTRY -// obj = new Obj(); -// if (parameter_value) { -// escape(obj); -// } -// obj.field = 3; -// while (!test()) { -// if (test2()) { } else { obj.field = 5; } -// } -// EXIT -// return obj.field -TEST_F(LoadStoreEliminationTest, PartialLoopPhis4) { - VariableSizedHandleScope vshs(Thread::Current()); - CreateGraph(/*handles=*/&vshs); - AdjacencyListGraph blks(SetupFromAdjacencyList("entry", - "exit", - {{"entry", "escape_check"}, - {"escape_check", "escape"}, - {"escape_check", "no_escape"}, - {"no_escape", "loop_pre_header"}, - {"escape", "loop_pre_header"}, - - {"loop_pre_header", "loop_header"}, - {"loop_header", "breturn"}, - {"loop_header", "loop_body"}, - {"loop_body", "loop_if_left"}, - {"loop_body", "loop_if_right"}, - {"loop_if_left", "loop_merge"}, - {"loop_if_right", "loop_merge"}, - {"loop_merge", "loop_header"}, - - {"breturn", "exit"}})); -#define GET_BLOCK(name) HBasicBlock* name = blks.Get(#name) - GET_BLOCK(entry); - GET_BLOCK(exit); - GET_BLOCK(breturn); - GET_BLOCK(no_escape); - GET_BLOCK(escape); - GET_BLOCK(escape_check); - - GET_BLOCK(loop_pre_header); - GET_BLOCK(loop_header); - GET_BLOCK(loop_body); - GET_BLOCK(loop_if_left); - GET_BLOCK(loop_if_right); - GET_BLOCK(loop_merge); -#undef GET_BLOCK - EnsurePredecessorOrder(loop_pre_header, {no_escape, escape}); - EnsurePredecessorOrder(loop_header, {loop_pre_header, loop_merge}); - EnsurePredecessorOrder(loop_merge, {loop_if_left, loop_if_right}); - CHECK_SUBROUTINE_FAILURE(); - HInstruction* bool_val = MakeParam(DataType::Type::kBool); - HInstruction* c3 = graph_->GetIntConstant(3); - HInstruction* c5 = graph_->GetIntConstant(5); - - HInstruction* cls = MakeClassLoad(); - HInstruction* new_inst = MakeNewInstance(cls); - HInstruction* entry_goto = new (GetAllocator()) HGoto(); - entry->AddInstruction(cls); - entry->AddInstruction(new_inst); - entry->AddInstruction(entry_goto); - ManuallyBuildEnvFor(cls, {}); - new_inst->CopyEnvironmentFrom(cls->GetEnvironment()); - - HInstruction* if_esc_check = new (GetAllocator()) HIf(bool_val); - escape_check->AddInstruction(if_esc_check); - - HInstruction* call_escape = MakeInvoke(DataType::Type::kVoid, { new_inst }); - HInstruction* goto_escape = new (GetAllocator()) HGoto(); - escape->AddInstruction(call_escape); - escape->AddInstruction(goto_escape); - call_escape->CopyEnvironmentFrom(cls->GetEnvironment()); - - HInstruction* goto_no_escape = new (GetAllocator()) HGoto(); - no_escape->AddInstruction(goto_no_escape); - - HInstruction* write_pre_header = MakeIFieldSet(new_inst, c3, MemberOffset(32)); - HInstruction* goto_preheader = new (GetAllocator()) HGoto(); - loop_pre_header->AddInstruction(write_pre_header); - loop_pre_header->AddInstruction(goto_preheader); - - HInstruction* suspend_check_header = new (GetAllocator()) HSuspendCheck(); - HInstruction* call_header = MakeInvoke(DataType::Type::kBool, {}); - HInstruction* if_header = new (GetAllocator()) HIf(call_header); - loop_header->AddInstruction(suspend_check_header); - loop_header->AddInstruction(call_header); - loop_header->AddInstruction(if_header); - call_header->CopyEnvironmentFrom(cls->GetEnvironment()); - suspend_check_header->CopyEnvironmentFrom(cls->GetEnvironment()); - - HInstruction* call_loop_body = MakeInvoke(DataType::Type::kBool, {}); - HInstruction* if_loop_body = new (GetAllocator()) HIf(call_loop_body); - loop_body->AddInstruction(call_loop_body); - loop_body->AddInstruction(if_loop_body); - call_loop_body->CopyEnvironmentFrom(cls->GetEnvironment()); - - HInstruction* goto_loop_left = new (GetAllocator()) HGoto(); - loop_if_left->AddInstruction(goto_loop_left); - - HInstruction* write_loop_right = MakeIFieldSet(new_inst, c5, MemberOffset(32)); - HInstruction* goto_loop_right = new (GetAllocator()) HGoto(); - loop_if_right->AddInstruction(write_loop_right); - loop_if_right->AddInstruction(goto_loop_right); - - HInstruction* goto_loop_merge = new (GetAllocator()) HGoto(); - loop_merge->AddInstruction(goto_loop_merge); - - HInstruction* read_bottom = MakeIFieldGet(new_inst, DataType::Type::kInt32, MemberOffset(32)); - HInstruction* return_exit = new (GetAllocator()) HReturn(read_bottom); - breturn->AddInstruction(read_bottom); - breturn->AddInstruction(return_exit); - - SetupExit(exit); - - // PerformLSE expects this to be empty. - graph_->ClearDominanceInformation(); - LOG(INFO) << "Pre LSE " << blks; - PerformLSEWithPartial(); - LOG(INFO) << "Post LSE " << blks; - - HPredicatedInstanceFieldGet* pred_get = - FindSingleInstruction<HPredicatedInstanceFieldGet>(graph_, breturn); - EXPECT_INS_REMOVED(read_bottom) << *read_bottom; - ASSERT_TRUE(pred_get != nullptr); - HPhi* inst_return_phi = pred_get->GetTarget()->AsPhi(); - ASSERT_TRUE(inst_return_phi != nullptr) << pred_get->GetTarget()->DumpWithArgs(); - EXPECT_INS_EQ(inst_return_phi->InputAt(0), graph_->GetNullConstant()); - EXPECT_INS_EQ(inst_return_phi->InputAt(1), - FindSingleInstruction<HNewInstance>(graph_, escape->GetSinglePredecessor())); - HPhi* inst_value_phi = pred_get->GetDefaultValue()->AsPhi(); - ASSERT_TRUE(inst_value_phi != nullptr) << pred_get->GetDefaultValue()->DumpWithArgs(); - HPhi* loop_header_phi = FindSingleInstruction<HPhi>(graph_, loop_header); - HPhi* loop_merge_phi = FindSingleInstruction<HPhi>(graph_, loop_merge); - EXPECT_INS_EQ(inst_value_phi, loop_header_phi); - EXPECT_INS_EQ(loop_header_phi->InputAt(0), c3); - EXPECT_INS_EQ(loop_header_phi->InputAt(1), loop_merge_phi); - EXPECT_INS_EQ(loop_merge_phi->InputAt(0), loop_header_phi); - EXPECT_INS_EQ(loop_merge_phi->InputAt(1), c5); - EXPECT_INS_RETAINED(write_loop_right) << *write_loop_right; - EXPECT_TRUE(write_loop_right->AsInstanceFieldSet()->GetIsPredicatedSet()) << *write_loop_right; - EXPECT_INS_RETAINED(write_pre_header) << *write_pre_header; - EXPECT_TRUE(write_pre_header->AsInstanceFieldSet()->GetIsPredicatedSet()) << *write_pre_header; -} - -// // ENTRY -// obj = new Obj(); -// obj.field = 3; -// while (!test()) { -// if (test2()) { } else { obj.field += 5; } -// } -// if (parameter_value) { -// escape(obj); -// } -// EXIT -// return obj.field -TEST_F(LoadStoreEliminationTest, PartialLoopPhis5) { - VariableSizedHandleScope vshs(Thread::Current()); - CreateGraph(/*handles=*/&vshs); - AdjacencyListGraph blks(SetupFromAdjacencyList("entry", - "exit", - {{"entry", "loop_pre_header"}, - {"loop_pre_header", "loop_header"}, - {"loop_header", "escape_check"}, - {"loop_header", "loop_body"}, - {"loop_body", "loop_if_left"}, - {"loop_body", "loop_if_right"}, - {"loop_if_left", "loop_merge"}, - {"loop_if_right", "loop_merge"}, - {"loop_merge", "loop_header"}, - {"escape_check", "escape"}, - {"escape_check", "no_escape"}, - {"no_escape", "breturn"}, - {"escape", "breturn"}, - {"breturn", "exit"}})); -#define GET_BLOCK(name) HBasicBlock* name = blks.Get(#name) - GET_BLOCK(entry); - GET_BLOCK(exit); - GET_BLOCK(breturn); - GET_BLOCK(no_escape); - GET_BLOCK(escape); - GET_BLOCK(escape_check); - - GET_BLOCK(loop_pre_header); - GET_BLOCK(loop_header); - GET_BLOCK(loop_body); - GET_BLOCK(loop_if_left); - GET_BLOCK(loop_if_right); - GET_BLOCK(loop_merge); -#undef GET_BLOCK - EnsurePredecessorOrder(breturn, {no_escape, escape}); - EnsurePredecessorOrder(loop_header, {loop_pre_header, loop_merge}); - EnsurePredecessorOrder(loop_merge, {loop_if_left, loop_if_right}); - CHECK_SUBROUTINE_FAILURE(); - HInstruction* bool_val = MakeParam(DataType::Type::kBool); - HInstruction* c3 = graph_->GetIntConstant(3); - HInstruction* c5 = graph_->GetIntConstant(5); - - HInstruction* cls = MakeClassLoad(); - HInstruction* new_inst = MakeNewInstance(cls); - HInstruction* entry_goto = new (GetAllocator()) HGoto(); - entry->AddInstruction(cls); - entry->AddInstruction(new_inst); - entry->AddInstruction(entry_goto); - ManuallyBuildEnvFor(cls, {}); - new_inst->CopyEnvironmentFrom(cls->GetEnvironment()); - - HInstruction* write_pre_header = MakeIFieldSet(new_inst, c3, MemberOffset(32)); - HInstruction* goto_preheader = new (GetAllocator()) HGoto(); - loop_pre_header->AddInstruction(write_pre_header); - loop_pre_header->AddInstruction(goto_preheader); - - HInstruction* suspend_check_header = new (GetAllocator()) HSuspendCheck(); - HInstruction* call_header = MakeInvoke(DataType::Type::kBool, {}); - HInstruction* if_header = new (GetAllocator()) HIf(call_header); - loop_header->AddInstruction(suspend_check_header); - loop_header->AddInstruction(call_header); - loop_header->AddInstruction(if_header); - call_header->CopyEnvironmentFrom(cls->GetEnvironment()); - suspend_check_header->CopyEnvironmentFrom(cls->GetEnvironment()); - - HInstruction* call_loop_body = MakeInvoke(DataType::Type::kBool, {}); - HInstruction* if_loop_body = new (GetAllocator()) HIf(call_loop_body); - loop_body->AddInstruction(call_loop_body); - loop_body->AddInstruction(if_loop_body); - call_loop_body->CopyEnvironmentFrom(cls->GetEnvironment()); - - HInstruction* goto_loop_left = new (GetAllocator()) HGoto(); - loop_if_left->AddInstruction(goto_loop_left); - - HInstruction* read_loop_right = MakeIFieldGet(new_inst, DataType::Type::kInt32, MemberOffset(32)); - HInstruction* add_loop_right = - new (GetAllocator()) HAdd(DataType::Type::kInt32, read_loop_right, c5); - HInstruction* write_loop_right = MakeIFieldSet(new_inst, add_loop_right, MemberOffset(32)); - HInstruction* goto_loop_right = new (GetAllocator()) HGoto(); - loop_if_right->AddInstruction(read_loop_right); - loop_if_right->AddInstruction(add_loop_right); - loop_if_right->AddInstruction(write_loop_right); - loop_if_right->AddInstruction(goto_loop_right); - - HInstruction* goto_loop_merge = new (GetAllocator()) HGoto(); - loop_merge->AddInstruction(goto_loop_merge); - - HInstruction* if_esc_check = new (GetAllocator()) HIf(bool_val); - escape_check->AddInstruction(if_esc_check); - - HInstruction* call_escape = MakeInvoke(DataType::Type::kVoid, { new_inst }); - HInstruction* goto_escape = new (GetAllocator()) HGoto(); - escape->AddInstruction(call_escape); - escape->AddInstruction(goto_escape); - call_escape->CopyEnvironmentFrom(cls->GetEnvironment()); - - HInstruction* goto_no_escape = new (GetAllocator()) HGoto(); - no_escape->AddInstruction(goto_no_escape); - - HInstruction* read_bottom = MakeIFieldGet(new_inst, DataType::Type::kInt32, MemberOffset(32)); - HInstruction* return_exit = new (GetAllocator()) HReturn(read_bottom); - breturn->AddInstruction(read_bottom); - breturn->AddInstruction(return_exit); - - SetupExit(exit); - - // PerformLSE expects this to be empty. - graph_->ClearDominanceInformation(); - LOG(INFO) << "Pre LSE " << blks; - PerformLSEWithPartial(); - LOG(INFO) << "Post LSE " << blks; - - HPredicatedInstanceFieldGet* pred_get = - FindSingleInstruction<HPredicatedInstanceFieldGet>(graph_, breturn); - EXPECT_INS_REMOVED(read_bottom) << *read_bottom; - ASSERT_TRUE(pred_get != nullptr); - HPhi* inst_return_phi = pred_get->GetTarget()->AsPhi(); - ASSERT_TRUE(inst_return_phi != nullptr) << pred_get->GetTarget()->DumpWithArgs(); - EXPECT_INS_EQ(inst_return_phi->InputAt(0), graph_->GetNullConstant()); - EXPECT_INS_EQ(inst_return_phi->InputAt(1), - FindSingleInstruction<HNewInstance>(graph_, escape->GetSinglePredecessor())); - HPhi* inst_value_phi = pred_get->GetDefaultValue()->AsPhi(); - ASSERT_TRUE(inst_value_phi != nullptr) << pred_get->GetDefaultValue()->DumpWithArgs(); - HPhi* loop_header_phi = FindSingleInstruction<HPhi>(graph_, loop_header); - HPhi* loop_merge_phi = FindSingleInstruction<HPhi>(graph_, loop_merge); - EXPECT_INS_EQ(inst_value_phi->InputAt(0), loop_header_phi); - EXPECT_INS_EQ(inst_value_phi->InputAt(1), graph_->GetIntConstant(0)); - EXPECT_INS_EQ(loop_header_phi->InputAt(0), c3); - EXPECT_INS_EQ(loop_header_phi->InputAt(1), loop_merge_phi); - EXPECT_INS_EQ(loop_merge_phi->InputAt(0), loop_header_phi); - EXPECT_INS_EQ(loop_merge_phi->InputAt(1), add_loop_right); - EXPECT_INS_EQ(add_loop_right->InputAt(0), loop_header_phi); - EXPECT_INS_EQ(add_loop_right->InputAt(1), c5); - HInstanceFieldSet* mat_set = - FindSingleInstruction<HInstanceFieldSet>(graph_, escape->GetSinglePredecessor()); - ASSERT_NE(mat_set, nullptr); - EXPECT_INS_EQ(mat_set->InputAt(1), loop_header_phi); - EXPECT_INS_REMOVED(write_loop_right) << *write_loop_right; - EXPECT_INS_REMOVED(write_pre_header) << *write_pre_header; -} - -// TODO This should really be in an Instruction simplifier Gtest but (1) that -// doesn't exist and (2) we should move this simplification to directly in the -// LSE pass since there is more information then. -// // ENTRY -// obj = new Obj(); -// obj.field = 3; -// if (param) { -// escape(obj); -// } else { -// obj.field = 10; -// } -// return obj.field; -TEST_F(LoadStoreEliminationTest, SimplifyTest) { - VariableSizedHandleScope vshs(Thread::Current()); - CreateGraph(&vshs); - AdjacencyListGraph blks(SetupFromAdjacencyList("entry", - "exit", - {{"entry", "left"}, - {"entry", "right"}, - {"left", "breturn"}, - {"right", "breturn"}, - {"breturn", "exit"}})); -#define GET_BLOCK(name) HBasicBlock* name = blks.Get(#name) - GET_BLOCK(entry); - GET_BLOCK(exit); - GET_BLOCK(breturn); - GET_BLOCK(left); - GET_BLOCK(right); -#undef GET_BLOCK - EnsurePredecessorOrder(breturn, {left, right}); - - HInstruction* bool_value = MakeParam(DataType::Type::kBool); - HInstruction* c3 = graph_->GetIntConstant(3); - HInstruction* c10 = graph_->GetIntConstant(10); - - HInstruction* cls = MakeClassLoad(); - HInstruction* new_inst = MakeNewInstance(cls); - HInstruction* write_start = MakeIFieldSet(new_inst, c3, MemberOffset(32)); - HInstruction* if_inst = new (GetAllocator()) HIf(bool_value); - entry->AddInstruction(cls); - entry->AddInstruction(new_inst); - entry->AddInstruction(write_start); - entry->AddInstruction(if_inst); - ManuallyBuildEnvFor(cls, {}); - new_inst->CopyEnvironmentFrom(cls->GetEnvironment()); - - HInstruction* call_left = MakeInvoke(DataType::Type::kVoid, { new_inst }); - HInstruction* goto_left = new (GetAllocator()) HGoto(); - left->AddInstruction(call_left); - left->AddInstruction(goto_left); - call_left->CopyEnvironmentFrom(cls->GetEnvironment()); - - HInstruction* write_right = MakeIFieldSet(new_inst, c10, MemberOffset(32)); - HInstruction* goto_right = new (GetAllocator()) HGoto(); - right->AddInstruction(write_right); - right->AddInstruction(goto_right); - - HInstruction* read_end = MakeIFieldGet(new_inst, DataType::Type::kInt32, MemberOffset(32)); - HInstruction* return_exit = new (GetAllocator()) HReturn(read_end); - breturn->AddInstruction(read_end); - breturn->AddInstruction(return_exit); - - SetupExit(exit); - - // PerformLSE expects this to be empty. - graph_->ClearDominanceInformation(); - LOG(INFO) << "Pre LSE " << blks; PerformLSE(); - // Run the code-simplifier too - LOG(INFO) << "Pre simplification " << blks; - InstructionSimplifier simp(graph_, /*codegen=*/nullptr); - simp.Run(); - - LOG(INFO) << "Post LSE " << blks; - - EXPECT_INS_REMOVED(write_right); - EXPECT_INS_REMOVED(write_start); - EXPECT_INS_REMOVED(read_end); - EXPECT_INS_RETAINED(call_left); - - HPredicatedInstanceFieldGet* pred_get = - FindSingleInstruction<HPredicatedInstanceFieldGet>(graph_, breturn); - ASSERT_NE(pred_get, nullptr); - EXPECT_INS_EQ(pred_get->GetDefaultValue(), c10); -} - - -// TODO This should really be in an Instruction simplifier Gtest but (1) that -// doesn't exist and (2) we should move this simplification to directly in the -// LSE pass since there is more information then. -// -// This checks that we don't replace phis when the replacement isn't valid at -// that point (i.e. it doesn't dominate) -// // ENTRY -// obj = new Obj(); -// obj.field = 3; -// if (param) { -// escape(obj); -// } else { -// obj.field = noescape(); -// } -// return obj.field; -TEST_F(LoadStoreEliminationTest, SimplifyTest2) { - VariableSizedHandleScope vshs(Thread::Current()); - CreateGraph(&vshs); - AdjacencyListGraph blks(SetupFromAdjacencyList("entry", - "exit", - {{"entry", "left"}, - {"entry", "right"}, - {"left", "breturn"}, - {"right", "breturn"}, - {"breturn", "exit"}})); -#define GET_BLOCK(name) HBasicBlock* name = blks.Get(#name) - GET_BLOCK(entry); - GET_BLOCK(exit); - GET_BLOCK(breturn); - GET_BLOCK(left); - GET_BLOCK(right); -#undef GET_BLOCK - EnsurePredecessorOrder(breturn, {left, right}); - - HInstruction* bool_value = MakeParam(DataType::Type::kBool); - HInstruction* c3 = graph_->GetIntConstant(3); - - HInstruction* cls = MakeClassLoad(); - HInstruction* new_inst = MakeNewInstance(cls); - HInstruction* write_start = MakeIFieldSet(new_inst, c3, MemberOffset(32)); - HInstruction* if_inst = new (GetAllocator()) HIf(bool_value); - entry->AddInstruction(cls); - entry->AddInstruction(new_inst); - entry->AddInstruction(write_start); - entry->AddInstruction(if_inst); - ManuallyBuildEnvFor(cls, {}); - new_inst->CopyEnvironmentFrom(cls->GetEnvironment()); - - HInstruction* call_left = MakeInvoke(DataType::Type::kVoid, {new_inst}); - HInstruction* goto_left = new (GetAllocator()) HGoto(); - left->AddInstruction(call_left); - left->AddInstruction(goto_left); - call_left->CopyEnvironmentFrom(cls->GetEnvironment()); - - HInstruction* call_right = MakeInvoke(DataType::Type::kInt32, {}); - HInstruction* write_right = MakeIFieldSet(new_inst, call_right, MemberOffset(32)); - HInstruction* goto_right = new (GetAllocator()) HGoto(); - right->AddInstruction(call_right); - right->AddInstruction(write_right); - right->AddInstruction(goto_right); - call_right->CopyEnvironmentFrom(cls->GetEnvironment()); - - HInstruction* read_end = MakeIFieldGet(new_inst, DataType::Type::kInt32, MemberOffset(32)); - HInstruction* return_exit = new (GetAllocator()) HReturn(read_end); - breturn->AddInstruction(read_end); - breturn->AddInstruction(return_exit); - - SetupExit(exit); - - // PerformLSE expects this to be empty. - graph_->ClearDominanceInformation(); - LOG(INFO) << "Pre LSE " << blks; - PerformLSE(); - - // Run the code-simplifier too - LOG(INFO) << "Pre simplification " << blks; - InstructionSimplifier simp(graph_, /*codegen=*/nullptr); - simp.Run(); - - LOG(INFO) << "Post LSE " << blks; - - EXPECT_INS_REMOVED(write_right); - EXPECT_INS_REMOVED(write_start); - EXPECT_INS_REMOVED(read_end); - EXPECT_INS_RETAINED(call_left); - EXPECT_INS_RETAINED(call_right); - EXPECT_EQ(call_right->GetBlock(), right); - - HPredicatedInstanceFieldGet* pred_get = - FindSingleInstruction<HPredicatedInstanceFieldGet>(graph_, breturn); - ASSERT_NE(pred_get, nullptr); - EXPECT_TRUE(pred_get->GetDefaultValue()->IsPhi()) << pred_get->DumpWithArgs(); - EXPECT_INS_EQ(pred_get->GetDefaultValue()->InputAt(0), graph_->GetIntConstant(0)) - << pred_get->DumpWithArgs(); - EXPECT_INS_EQ(pred_get->GetDefaultValue()->InputAt(1), call_right) << pred_get->DumpWithArgs(); -} - -// TODO This should really be in an Instruction simplifier Gtest but (1) that -// doesn't exist and (2) we should move this simplification to directly in the -// LSE pass since there is more information then. -// -// This checks that we replace phis even when there are multiple replacements as -// long as they are equal -// // ENTRY -// obj = new Obj(); -// obj.field = 3; -// switch (param) { -// case 1: -// escape(obj); -// break; -// case 2: -// obj.field = 10; -// break; -// case 3: -// obj.field = 10; -// break; -// } -// return obj.field; -TEST_F(LoadStoreEliminationTest, SimplifyTest3) { - VariableSizedHandleScope vshs(Thread::Current()); - CreateGraph(&vshs); - AdjacencyListGraph blks(SetupFromAdjacencyList("entry", - "exit", - {{"entry", "case1"}, - {"entry", "case2"}, - {"entry", "case3"}, - {"case1", "breturn"}, - {"case2", "breturn"}, - {"case3", "breturn"}, - {"breturn", "exit"}})); -#define GET_BLOCK(name) HBasicBlock* name = blks.Get(#name) - GET_BLOCK(entry); - GET_BLOCK(exit); - GET_BLOCK(breturn); - GET_BLOCK(case1); - GET_BLOCK(case2); - GET_BLOCK(case3); -#undef GET_BLOCK - EnsurePredecessorOrder(breturn, {case1, case2, case3}); - - HInstruction* int_val = MakeParam(DataType::Type::kInt32); - HInstruction* c3 = graph_->GetIntConstant(3); - HInstruction* c10 = graph_->GetIntConstant(10); - - HInstruction* cls = MakeClassLoad(); - HInstruction* new_inst = MakeNewInstance(cls); - HInstruction* write_start = MakeIFieldSet(new_inst, c3, MemberOffset(32)); - HInstruction* switch_inst = new (GetAllocator()) HPackedSwitch(0, 2, int_val); - entry->AddInstruction(cls); - entry->AddInstruction(new_inst); - entry->AddInstruction(write_start); - entry->AddInstruction(switch_inst); - ManuallyBuildEnvFor(cls, {}); - new_inst->CopyEnvironmentFrom(cls->GetEnvironment()); - - HInstruction* call_case1 = MakeInvoke(DataType::Type::kVoid, {new_inst}); - HInstruction* goto_case1 = new (GetAllocator()) HGoto(); - case1->AddInstruction(call_case1); - case1->AddInstruction(goto_case1); - call_case1->CopyEnvironmentFrom(cls->GetEnvironment()); - - HInstruction* write_case2 = MakeIFieldSet(new_inst, c10, MemberOffset(32)); - HInstruction* goto_case2 = new (GetAllocator()) HGoto(); - case2->AddInstruction(write_case2); - case2->AddInstruction(goto_case2); - - HInstruction* write_case3 = MakeIFieldSet(new_inst, c10, MemberOffset(32)); - HInstruction* goto_case3 = new (GetAllocator()) HGoto(); - case3->AddInstruction(write_case3); - case3->AddInstruction(goto_case3); - - HInstruction* read_end = MakeIFieldGet(new_inst, DataType::Type::kInt32, MemberOffset(32)); - HInstruction* return_exit = new (GetAllocator()) HReturn(read_end); - breturn->AddInstruction(read_end); - breturn->AddInstruction(return_exit); - - SetupExit(exit); - - // PerformLSE expects this to be empty. - graph_->ClearDominanceInformation(); - LOG(INFO) << "Pre LSE " << blks; - PerformLSE(); - - // Run the code-simplifier too - LOG(INFO) << "Pre simplification " << blks; - InstructionSimplifier simp(graph_, /*codegen=*/nullptr); - simp.Run(); - - LOG(INFO) << "Post LSE " << blks; - - EXPECT_INS_REMOVED(write_case2); - EXPECT_INS_REMOVED(write_case3); - EXPECT_INS_REMOVED(write_start); - EXPECT_INS_REMOVED(read_end); - EXPECT_INS_RETAINED(call_case1); - - HPredicatedInstanceFieldGet* pred_get = - FindSingleInstruction<HPredicatedInstanceFieldGet>(graph_, breturn); - ASSERT_NE(pred_get, nullptr); - EXPECT_INS_EQ(pred_get->GetDefaultValue(), c10) - << pred_get->DumpWithArgs(); -} - -// TODO This should really be in an Instruction simplifier Gtest but (1) that -// doesn't exist and (2) we should move this simplification to directly in the -// LSE pass since there is more information then. -// -// This checks that we don't replace phis even when there are multiple -// replacements if they are not equal -// // ENTRY -// obj = new Obj(); -// obj.field = 3; -// switch (param) { -// case 1: -// escape(obj); -// break; -// case 2: -// obj.field = 10; -// break; -// case 3: -// obj.field = 20; -// break; -// } -// return obj.field; -TEST_F(LoadStoreEliminationTest, SimplifyTest4) { - VariableSizedHandleScope vshs(Thread::Current()); - CreateGraph(&vshs); - AdjacencyListGraph blks(SetupFromAdjacencyList("entry", - "exit", - {{"entry", "case1"}, - {"entry", "case2"}, - {"entry", "case3"}, - {"case1", "breturn"}, - {"case2", "breturn"}, - {"case3", "breturn"}, - {"breturn", "exit"}})); -#define GET_BLOCK(name) HBasicBlock* name = blks.Get(#name) - GET_BLOCK(entry); - GET_BLOCK(exit); - GET_BLOCK(breturn); - GET_BLOCK(case1); - GET_BLOCK(case2); - GET_BLOCK(case3); -#undef GET_BLOCK - EnsurePredecessorOrder(breturn, {case1, case2, case3}); - - HInstruction* int_val = MakeParam(DataType::Type::kInt32); - HInstruction* c3 = graph_->GetIntConstant(3); - HInstruction* c10 = graph_->GetIntConstant(10); - HInstruction* c20 = graph_->GetIntConstant(20); - - HInstruction* cls = MakeClassLoad(); - HInstruction* new_inst = MakeNewInstance(cls); - HInstruction* write_start = MakeIFieldSet(new_inst, c3, MemberOffset(32)); - HInstruction* switch_inst = new (GetAllocator()) HPackedSwitch(0, 2, int_val); - entry->AddInstruction(cls); - entry->AddInstruction(new_inst); - entry->AddInstruction(write_start); - entry->AddInstruction(switch_inst); - ManuallyBuildEnvFor(cls, {}); - new_inst->CopyEnvironmentFrom(cls->GetEnvironment()); - - HInstruction* call_case1 = MakeInvoke(DataType::Type::kVoid, {new_inst}); - HInstruction* goto_case1 = new (GetAllocator()) HGoto(); - case1->AddInstruction(call_case1); - case1->AddInstruction(goto_case1); - call_case1->CopyEnvironmentFrom(cls->GetEnvironment()); - - HInstruction* write_case2 = MakeIFieldSet(new_inst, c10, MemberOffset(32)); - HInstruction* goto_case2 = new (GetAllocator()) HGoto(); - case2->AddInstruction(write_case2); - case2->AddInstruction(goto_case2); - - HInstruction* write_case3 = MakeIFieldSet(new_inst, c20, MemberOffset(32)); - HInstruction* goto_case3 = new (GetAllocator()) HGoto(); - case3->AddInstruction(write_case3); - case3->AddInstruction(goto_case3); - - HInstruction* read_end = MakeIFieldGet(new_inst, DataType::Type::kInt32, MemberOffset(32)); - HInstruction* return_exit = new (GetAllocator()) HReturn(read_end); - breturn->AddInstruction(read_end); - breturn->AddInstruction(return_exit); - - SetupExit(exit); - - // PerformLSE expects this to be empty. - graph_->ClearDominanceInformation(); - LOG(INFO) << "Pre LSE " << blks; - PerformLSE(); - - // Run the code-simplifier too - LOG(INFO) << "Pre simplification " << blks; - InstructionSimplifier simp(graph_, /*codegen=*/nullptr); - simp.Run(); - - LOG(INFO) << "Post LSE " << blks; - - EXPECT_INS_REMOVED(write_case2); - EXPECT_INS_REMOVED(write_case3); - EXPECT_INS_REMOVED(write_start); - EXPECT_INS_REMOVED(read_end); - EXPECT_INS_RETAINED(call_case1); - - HPredicatedInstanceFieldGet* pred_get = - FindSingleInstruction<HPredicatedInstanceFieldGet>(graph_, breturn); - ASSERT_NE(pred_get, nullptr); - EXPECT_TRUE(pred_get->GetDefaultValue()->IsPhi()) - << pred_get->DumpWithArgs(); - EXPECT_INS_EQ(pred_get->GetDefaultValue()->InputAt(0), graph_->GetIntConstant(0)); - EXPECT_INS_EQ(pred_get->GetDefaultValue()->InputAt(1), c10); - EXPECT_INS_EQ(pred_get->GetDefaultValue()->InputAt(2), c20); -} - -// Make sure that irreducible loops don't screw up Partial LSE. We can't pull -// phis through them so we need to treat them as escapes. -// TODO We should be able to do better than this? Need to do some research. -// // ENTRY -// obj = new Obj(); -// obj.foo = 11; -// if (param1) { -// } else { -// // irreducible loop here. NB the objdoesn't actually escape -// obj.foo = 33; -// if (param2) { -// goto inner; -// } else { -// while (test()) { -// if (test()) { -// obj.foo = 66; -// } else { -// } -// inner: -// } -// } -// } -// return obj.foo; -TEST_F(LoadStoreEliminationTest, PartialIrreducibleLoop) { - VariableSizedHandleScope vshs(Thread::Current()); - CreateGraph(&vshs); - AdjacencyListGraph blks(SetupFromAdjacencyList("start", - "exit", - {{"start", "entry"}, - {"entry", "left"}, - {"entry", "right"}, - {"left", "breturn"}, - - {"right", "right_crit_break_loop"}, - {"right_crit_break_loop", "loop_header"}, - {"right", "right_crit_break_end"}, - {"right_crit_break_end", "loop_end"}, - - {"loop_header", "loop_body"}, - {"loop_body", "loop_left"}, - {"loop_body", "loop_right"}, - {"loop_left", "loop_end"}, - {"loop_right", "loop_end"}, - {"loop_end", "loop_header"}, - {"loop_header", "loop_header_crit_break"}, - {"loop_header_crit_break", "breturn"}, - - {"breturn", "exit"}})); -#define GET_BLOCK(name) HBasicBlock* name = blks.Get(#name) - GET_BLOCK(start); - GET_BLOCK(entry); - GET_BLOCK(exit); - GET_BLOCK(breturn); - GET_BLOCK(left); - GET_BLOCK(right); - GET_BLOCK(right_crit_break_end); - GET_BLOCK(right_crit_break_loop); - GET_BLOCK(loop_header); - GET_BLOCK(loop_header_crit_break); - GET_BLOCK(loop_body); - GET_BLOCK(loop_left); - GET_BLOCK(loop_right); - GET_BLOCK(loop_end); -#undef GET_BLOCK - EnsurePredecessorOrder(breturn, {left, loop_header_crit_break}); - HInstruction* c11 = graph_->GetIntConstant(11); - HInstruction* c33 = graph_->GetIntConstant(33); - HInstruction* c66 = graph_->GetIntConstant(66); - HInstruction* param1 = MakeParam(DataType::Type::kBool); - HInstruction* param2 = MakeParam(DataType::Type::kBool); - - HInstruction* suspend = new (GetAllocator()) HSuspendCheck(); - HInstruction* start_goto = new (GetAllocator()) HGoto(); - start->AddInstruction(suspend); - start->AddInstruction(start_goto); - ManuallyBuildEnvFor(suspend, {}); - - HInstruction* cls = MakeClassLoad(); - HInstruction* new_inst = MakeNewInstance(cls); - HInstruction* write_start = MakeIFieldSet(new_inst, c11, MemberOffset(32)); - HInstruction* if_inst = new (GetAllocator()) HIf(param1); - entry->AddInstruction(cls); - entry->AddInstruction(new_inst); - entry->AddInstruction(write_start); - entry->AddInstruction(if_inst); - ManuallyBuildEnvFor(cls, {}); - new_inst->CopyEnvironmentFrom(cls->GetEnvironment()); - - left->AddInstruction(new (GetAllocator()) HGoto()); - - right->AddInstruction(MakeIFieldSet(new_inst, c33, MemberOffset(32))); - right->AddInstruction(new (GetAllocator()) HIf(param2)); - - right_crit_break_end->AddInstruction(new (GetAllocator()) HGoto()); - right_crit_break_loop->AddInstruction(new (GetAllocator()) HGoto()); - - HInstruction* header_suspend = new (GetAllocator()) HSuspendCheck(); - HInstruction* header_invoke = MakeInvoke(DataType::Type::kBool, {}); - HInstruction* header_if = new (GetAllocator()) HIf(header_invoke); - loop_header->AddInstruction(header_suspend); - loop_header->AddInstruction(header_invoke); - loop_header->AddInstruction(header_if); - header_suspend->CopyEnvironmentFrom(cls->GetEnvironment()); - header_invoke->CopyEnvironmentFrom(cls->GetEnvironment()); - - HInstruction* body_invoke = MakeInvoke(DataType::Type::kBool, {}); - HInstruction* body_if = new (GetAllocator()) HIf(body_invoke); - loop_body->AddInstruction(body_invoke); - loop_body->AddInstruction(body_if); - body_invoke->CopyEnvironmentFrom(cls->GetEnvironment()); - - HInstruction* left_set = MakeIFieldSet(new_inst, c66, MemberOffset(32)); - HInstruction* left_goto = MakeIFieldSet(new_inst, c66, MemberOffset(32)); - loop_left->AddInstruction(left_set); - loop_left->AddInstruction(left_goto); - - loop_right->AddInstruction(new (GetAllocator()) HGoto()); - - loop_end->AddInstruction(new (GetAllocator()) HGoto()); - - HInstruction* read_end = MakeIFieldGet(new_inst, DataType::Type::kInt32, MemberOffset(32)); - HInstruction* return_exit = new (GetAllocator()) HReturn(read_end); - breturn->AddInstruction(read_end); - breturn->AddInstruction(return_exit); - - SetupExit(exit); - - // PerformLSE expects this to be empty. - graph_->ClearDominanceInformation(); - LOG(INFO) << "Pre LSE " << blks; - PerformLSE(); - LOG(INFO) << "Post LSE " << blks; - - EXPECT_TRUE(loop_header->IsLoopHeader()); - EXPECT_TRUE(loop_header->GetLoopInformation()->IsIrreducible()); - - EXPECT_INS_RETAINED(left_set); - EXPECT_INS_REMOVED(write_start); - EXPECT_INS_REMOVED(read_end); - - HPredicatedInstanceFieldGet* pred_get = - FindSingleInstruction<HPredicatedInstanceFieldGet>(graph_, breturn); - ASSERT_NE(pred_get, nullptr); - ASSERT_TRUE(pred_get->GetDefaultValue()->IsPhi()) << pred_get->DumpWithArgs(); - EXPECT_INS_EQ(pred_get->GetDefaultValue()->InputAt(0), c11); - EXPECT_INS_EQ(pred_get->GetDefaultValue()->InputAt(1), graph_->GetIntConstant(0)); - ASSERT_TRUE(pred_get->GetTarget()->IsPhi()) << pred_get->DumpWithArgs(); - EXPECT_INS_EQ(pred_get->GetTarget()->InputAt(0), graph_->GetNullConstant()); - HNewInstance* mat = FindSingleInstruction<HNewInstance>(graph_, right->GetSinglePredecessor()); - ASSERT_NE(mat, nullptr); - EXPECT_INS_EQ(pred_get->GetTarget()->InputAt(1), mat); + EXPECT_TRUE(IsRemoved(read_bottom)); + EXPECT_TRUE(IsRemoved(write_right)); + EXPECT_FALSE(IsRemoved(write_entry)); + EXPECT_FALSE(IsRemoved(write_left)); + EXPECT_FALSE(IsRemoved(call_left)); + EXPECT_FALSE(IsRemoved(call_entry)); } } // namespace art diff --git a/compiler/optimizing/loop_analysis.cc b/compiler/optimizing/loop_analysis.cc index a776c37f36..78505171cb 100644 --- a/compiler/optimizing/loop_analysis.cc +++ b/compiler/optimizing/loop_analysis.cc @@ -214,9 +214,6 @@ class X86_64LoopHelper : public ArchDefaultLoopHelper { return 3; case HInstruction::InstructionKind::kIf: return 2; - case HInstruction::InstructionKind::kPredicatedInstanceFieldGet: - // test + cond-jump + IFieldGet - return 4; case HInstruction::InstructionKind::kInstanceFieldGet: return 2; case HInstruction::InstructionKind::kInstanceFieldSet: diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h index 9e0f515ba9..73db7e541f 100644 --- a/compiler/optimizing/nodes.h +++ b/compiler/optimizing/nodes.h @@ -72,7 +72,6 @@ class HParameterValue; class HPhi; class HSuspendCheck; class HTryBoundary; -class FieldInfo; class LiveInterval; class LocationSummary; class SlowPathCode; @@ -1098,10 +1097,6 @@ class HBasicBlock : public ArenaObject<kArenaAllocBasicBlock> { return predecessors_; } - size_t GetNumberOfPredecessors() const { - return GetPredecessors().size(); - } - const ArenaVector<HBasicBlock*>& GetSuccessors() const { return successors_; } @@ -1444,8 +1439,6 @@ class HBasicBlock : public ArenaObject<kArenaAllocBasicBlock> { friend class HGraph; friend class HInstruction; - // Allow manual control of the ordering of predecessors/successors - friend class OptimizingUnitTestHelper; DISALLOW_COPY_AND_ASSIGN(HBasicBlock); }; @@ -1510,7 +1503,6 @@ class HLoopInformationOutwardIterator : public ValueObject { M(If, Instruction) \ M(InstanceFieldGet, Instruction) \ M(InstanceFieldSet, Instruction) \ - M(PredicatedInstanceFieldGet, Instruction) \ M(InstanceOf, Instruction) \ M(IntConstant, Constant) \ M(IntermediateAddress, Instruction) \ @@ -1688,7 +1680,8 @@ FOR_EACH_INSTRUCTION(FORWARD_DECLARATION) H##type& operator=(const H##type&) = delete; \ public: -#define DEFAULT_COPY_CONSTRUCTOR(type) H##type(const H##type& other) = default; +#define DEFAULT_COPY_CONSTRUCTOR(type) \ + explicit H##type(const H##type& other) = default; template <typename T> class HUseListNode : public ArenaObject<kArenaAllocUseListNode>, @@ -2112,23 +2105,6 @@ class HEnvironment : public ArenaObject<kArenaAllocEnvironment> { return GetParent() != nullptr; } - class EnvInputSelector { - public: - explicit EnvInputSelector(const HEnvironment* e) : env_(e) {} - HInstruction* operator()(size_t s) const { - return env_->GetInstructionAt(s); - } - private: - const HEnvironment* env_; - }; - - using HConstEnvInputRef = TransformIterator<CountIter, EnvInputSelector>; - IterationRange<HConstEnvInputRef> GetEnvInputs() const { - IterationRange<CountIter> range(Range(Size())); - return MakeIterationRange(MakeTransformIterator(range.begin(), EnvInputSelector(this)), - MakeTransformIterator(range.end(), EnvInputSelector(this))); - } - private: ArenaVector<HUserRecord<HEnvironment*>> vregs_; ArenaVector<Location> locations_; @@ -2146,40 +2122,6 @@ class HEnvironment : public ArenaObject<kArenaAllocEnvironment> { std::ostream& operator<<(std::ostream& os, const HInstruction& rhs); -// Iterates over the Environments -class HEnvironmentIterator : public ValueObject, - public std::iterator<std::forward_iterator_tag, HEnvironment*> { - public: - explicit HEnvironmentIterator(HEnvironment* cur) : cur_(cur) {} - - HEnvironment* operator*() const { - return cur_; - } - - HEnvironmentIterator& operator++() { - DCHECK(cur_ != nullptr); - cur_ = cur_->GetParent(); - return *this; - } - - HEnvironmentIterator operator++(int) { - HEnvironmentIterator prev(*this); - ++(*this); - return prev; - } - - bool operator==(const HEnvironmentIterator& other) const { - return other.cur_ == cur_; - } - - bool operator!=(const HEnvironmentIterator& other) const { - return !(*this == other); - } - - private: - HEnvironment* cur_; -}; - class HInstruction : public ArenaObject<kArenaAllocInstruction> { public: #define DECLARE_KIND(type, super) k##type, @@ -2298,10 +2240,6 @@ class HInstruction : public ArenaObject<kArenaAllocInstruction> { // Does the instruction always throw an exception unconditionally? virtual bool AlwaysThrows() const { return false; } - // Will this instruction only cause async exceptions if it causes any at all? - virtual bool OnlyThrowsAsyncExceptions() const { - return false; - } bool CanThrowIntoCatchBlock() const { return CanThrow() && block_->IsTryBlock(); } @@ -2423,10 +2361,6 @@ class HInstruction : public ArenaObject<kArenaAllocInstruction> { bool HasEnvironment() const { return environment_ != nullptr; } HEnvironment* GetEnvironment() const { return environment_; } - IterationRange<HEnvironmentIterator> GetAllEnvironments() const { - return MakeIterationRange(HEnvironmentIterator(GetEnvironment()), - HEnvironmentIterator(nullptr)); - } // Set the `environment_` field. Raw because this method does not // update the uses lists. void SetRawEnvironment(HEnvironment* environment) { @@ -2527,17 +2461,6 @@ class HInstruction : public ArenaObject<kArenaAllocInstruction> { UNREACHABLE(); } - virtual bool IsFieldAccess() const { - return false; - } - - virtual const FieldInfo& GetFieldInfo() const { - CHECK(IsFieldAccess()) << "Only callable on field accessors not " << DebugName() << " " - << *this; - LOG(FATAL) << "Must be overridden by field accessors. Not implemented by " << *this; - UNREACHABLE(); - } - // Return whether instruction can be cloned (copied). virtual bool IsClonable() const { return false; } @@ -2773,16 +2696,12 @@ class HInstruction : public ArenaObject<kArenaAllocInstruction> { friend class HGraph; friend class HInstructionList; }; - std::ostream& operator<<(std::ostream& os, HInstruction::InstructionKind rhs); std::ostream& operator<<(std::ostream& os, const HInstruction::NoArgsDump rhs); std::ostream& operator<<(std::ostream& os, const HInstruction::ArgsDump rhs); std::ostream& operator<<(std::ostream& os, const HUseList<HInstruction*>& lst); std::ostream& operator<<(std::ostream& os, const HUseList<HEnvironment*>& lst); -// Forward declarations for friends -template <typename InnerIter> struct HSTLInstructionIterator; - // Iterates over the instructions, while preserving the next instruction // in case the current instruction gets removed from the list by the user // of this iterator. @@ -2801,12 +2720,10 @@ class HInstructionIterator : public ValueObject { } private: - HInstructionIterator() : instruction_(nullptr), next_(nullptr) {} - HInstruction* instruction_; HInstruction* next_; - friend struct HSTLInstructionIterator<HInstructionIterator>; + DISALLOW_COPY_AND_ASSIGN(HInstructionIterator); }; // Iterates over the instructions without saving the next instruction, @@ -2825,11 +2742,9 @@ class HInstructionIteratorHandleChanges : public ValueObject { } private: - HInstructionIteratorHandleChanges() : instruction_(nullptr) {} - HInstruction* instruction_; - friend struct HSTLInstructionIterator<HInstructionIteratorHandleChanges>; + DISALLOW_COPY_AND_ASSIGN(HInstructionIteratorHandleChanges); }; @@ -2848,63 +2763,12 @@ class HBackwardInstructionIterator : public ValueObject { } private: - HBackwardInstructionIterator() : instruction_(nullptr), next_(nullptr) {} - HInstruction* instruction_; HInstruction* next_; - friend struct HSTLInstructionIterator<HBackwardInstructionIterator>; -}; - -template <typename InnerIter> -struct HSTLInstructionIterator : public ValueObject, - public std::iterator<std::forward_iterator_tag, HInstruction*> { - public: - static_assert(std::is_same_v<InnerIter, HBackwardInstructionIterator> || - std::is_same_v<InnerIter, HInstructionIterator> || - std::is_same_v<InnerIter, HInstructionIteratorHandleChanges>, - "Unknown wrapped iterator!"); - - explicit HSTLInstructionIterator(InnerIter inner) : inner_(inner) {} - HInstruction* operator*() const { - DCHECK(inner_.Current() != nullptr); - return inner_.Current(); - } - - HSTLInstructionIterator<InnerIter>& operator++() { - DCHECK(*this != HSTLInstructionIterator<InnerIter>::EndIter()); - inner_.Advance(); - return *this; - } - - HSTLInstructionIterator<InnerIter> operator++(int) { - HSTLInstructionIterator<InnerIter> prev(*this); - ++(*this); - return prev; - } - - bool operator==(const HSTLInstructionIterator<InnerIter>& other) const { - return inner_.Current() == other.inner_.Current(); - } - - bool operator!=(const HSTLInstructionIterator<InnerIter>& other) const { - return !(*this == other); - } - - static HSTLInstructionIterator<InnerIter> EndIter() { - return HSTLInstructionIterator<InnerIter>(InnerIter()); - } - - private: - InnerIter inner_; + DISALLOW_COPY_AND_ASSIGN(HBackwardInstructionIterator); }; -template <typename InnerIter> -IterationRange<HSTLInstructionIterator<InnerIter>> MakeSTLInstructionIteratorRange(InnerIter iter) { - return MakeIterationRange(HSTLInstructionIterator<InnerIter>(iter), - HSTLInstructionIterator<InnerIter>::EndIter()); -} - class HVariableInputSizeInstruction : public HInstruction { public: using HInstruction::GetInputRecords; // Keep the const version visible. @@ -4481,16 +4345,11 @@ class HNewInstance final : public HExpression<1> { dex_file_(dex_file), entrypoint_(entrypoint) { SetPackedFlag<kFlagFinalizable>(finalizable); - SetPackedFlag<kFlagPartialMaterialization>(false); SetRawInputAt(0, cls); } bool IsClonable() const override { return true; } - void SetPartialMaterialization() { - SetPackedFlag<kFlagPartialMaterialization>(true); - } - dex::TypeIndex GetTypeIndex() const { return type_index_; } const DexFile& GetDexFile() const { return dex_file_; } @@ -4499,9 +4358,6 @@ class HNewInstance final : public HExpression<1> { // Can throw errors when out-of-memory or if it's not instantiable/accessible. bool CanThrow() const override { return true; } - bool OnlyThrowsAsyncExceptions() const override { - return !IsFinalizable() && !NeedsChecks(); - } bool NeedsChecks() const { return entrypoint_ == kQuickAllocObjectWithChecks; @@ -4511,10 +4367,6 @@ class HNewInstance final : public HExpression<1> { bool CanBeNull() const override { return false; } - bool IsPartialMaterialization() const { - return GetPackedFlag<kFlagPartialMaterialization>(); - } - QuickEntrypointEnum GetEntrypoint() const { return entrypoint_; } void SetEntrypoint(QuickEntrypointEnum entrypoint) { @@ -4539,8 +4391,7 @@ class HNewInstance final : public HExpression<1> { private: static constexpr size_t kFlagFinalizable = kNumberOfGenericPackedBits; - static constexpr size_t kFlagPartialMaterialization = kFlagFinalizable + 1; - static constexpr size_t kNumberOfNewInstancePackedBits = kFlagPartialMaterialization + 1; + static constexpr size_t kNumberOfNewInstancePackedBits = kFlagFinalizable + 1; static_assert(kNumberOfNewInstancePackedBits <= kMaxNumberOfPackedBits, "Too many packed fields."); @@ -6114,23 +5965,6 @@ class FieldInfo : public ValueObject { const DexFile& GetDexFile() const { return dex_file_; } bool IsVolatile() const { return is_volatile_; } - bool Equals(const FieldInfo& other) const { - return field_ == other.field_ && - field_offset_ == other.field_offset_ && - field_type_ == other.field_type_ && - is_volatile_ == other.is_volatile_ && - index_ == other.index_ && - declaring_class_def_index_ == other.declaring_class_def_index_ && - &dex_file_ == &other.dex_file_; - } - - std::ostream& Dump(std::ostream& os) const { - os << field_ << ", off: " << field_offset_ << ", type: " << field_type_ - << ", volatile: " << std::boolalpha << is_volatile_ << ", index_: " << std::dec << index_ - << ", declaring_class: " << declaring_class_def_index_ << ", dex: " << dex_file_; - return os; - } - private: ArtField* const field_; const MemberOffset field_offset_; @@ -6141,14 +5975,6 @@ class FieldInfo : public ValueObject { const DexFile& dex_file_; }; -inline bool operator==(const FieldInfo& a, const FieldInfo& b) { - return a.Equals(b); -} - -inline std::ostream& operator<<(std::ostream& os, const FieldInfo& a) { - return a.Dump(os); -} - class HInstanceFieldGet final : public HExpression<1> { public: HInstanceFieldGet(HInstruction* value, @@ -6190,8 +6016,7 @@ class HInstanceFieldGet final : public HExpression<1> { return (HInstruction::ComputeHashCode() << 7) | GetFieldOffset().SizeValue(); } - bool IsFieldAccess() const override { return true; } - const FieldInfo& GetFieldInfo() const override { return field_info_; } + const FieldInfo& GetFieldInfo() const { return field_info_; } MemberOffset GetFieldOffset() const { return field_info_.GetFieldOffset(); } DataType::Type GetFieldType() const { return field_info_.GetFieldType(); } bool IsVolatile() const { return field_info_.IsVolatile(); } @@ -6212,96 +6037,6 @@ class HInstanceFieldGet final : public HExpression<1> { const FieldInfo field_info_; }; -class HPredicatedInstanceFieldGet final : public HExpression<2> { - public: - HPredicatedInstanceFieldGet(HInstanceFieldGet* orig, - HInstruction* target, - HInstruction* default_val) - : HExpression(kPredicatedInstanceFieldGet, - orig->GetFieldType(), - orig->GetSideEffects(), - orig->GetDexPc()), - field_info_(orig->GetFieldInfo()) { - // NB Default-val is at 0 so we can avoid doing a move. - SetRawInputAt(1, target); - SetRawInputAt(0, default_val); - } - - HPredicatedInstanceFieldGet(HInstruction* value, - ArtField* field, - HInstruction* default_value, - DataType::Type field_type, - MemberOffset field_offset, - bool is_volatile, - uint32_t field_idx, - uint16_t declaring_class_def_index, - const DexFile& dex_file, - uint32_t dex_pc) - : HExpression(kPredicatedInstanceFieldGet, - field_type, - SideEffects::FieldReadOfType(field_type, is_volatile), - dex_pc), - field_info_(field, - field_offset, - field_type, - is_volatile, - field_idx, - declaring_class_def_index, - dex_file) { - SetRawInputAt(0, value); - SetRawInputAt(1, default_value); - } - - bool IsClonable() const override { - return true; - } - bool CanBeMoved() const override { - return !IsVolatile(); - } - - HInstruction* GetDefaultValue() const { - return InputAt(0); - } - HInstruction* GetTarget() const { - return InputAt(1); - } - - bool InstructionDataEquals(const HInstruction* other) const override { - const HPredicatedInstanceFieldGet* other_get = other->AsPredicatedInstanceFieldGet(); - return GetFieldOffset().SizeValue() == other_get->GetFieldOffset().SizeValue() && - GetDefaultValue() == other_get->GetDefaultValue(); - } - - bool CanDoImplicitNullCheckOn(HInstruction* obj) const override { - return (obj == InputAt(0)) && art::CanDoImplicitNullCheckOn(GetFieldOffset().Uint32Value()); - } - - size_t ComputeHashCode() const override { - return (HInstruction::ComputeHashCode() << 7) | GetFieldOffset().SizeValue(); - } - - bool IsFieldAccess() const override { return true; } - const FieldInfo& GetFieldInfo() const override { return field_info_; } - MemberOffset GetFieldOffset() const { return field_info_.GetFieldOffset(); } - DataType::Type GetFieldType() const { return field_info_.GetFieldType(); } - bool IsVolatile() const { return field_info_.IsVolatile(); } - - void SetType(DataType::Type new_type) { - DCHECK(DataType::IsIntegralType(GetType())); - DCHECK(DataType::IsIntegralType(new_type)); - DCHECK_EQ(DataType::Size(GetType()), DataType::Size(new_type)); - SetPackedField<TypeField>(new_type); - } - - DECLARE_INSTRUCTION(PredicatedInstanceFieldGet); - - protected: - DEFAULT_COPY_CONSTRUCTOR(PredicatedInstanceFieldGet); - - private: - const FieldInfo field_info_; -}; - class HInstanceFieldSet final : public HExpression<2> { public: HInstanceFieldSet(HInstruction* object, @@ -6325,7 +6060,6 @@ class HInstanceFieldSet final : public HExpression<2> { declaring_class_def_index, dex_file) { SetPackedFlag<kFlagValueCanBeNull>(true); - SetPackedFlag<kFlagIsPredicatedSet>(false); SetRawInputAt(0, object); SetRawInputAt(1, value); } @@ -6336,16 +6070,13 @@ class HInstanceFieldSet final : public HExpression<2> { return (obj == InputAt(0)) && art::CanDoImplicitNullCheckOn(GetFieldOffset().Uint32Value()); } - bool IsFieldAccess() const override { return true; } - const FieldInfo& GetFieldInfo() const override { return field_info_; } + const FieldInfo& GetFieldInfo() const { return field_info_; } MemberOffset GetFieldOffset() const { return field_info_.GetFieldOffset(); } DataType::Type GetFieldType() const { return field_info_.GetFieldType(); } bool IsVolatile() const { return field_info_.IsVolatile(); } HInstruction* GetValue() const { return InputAt(1); } bool GetValueCanBeNull() const { return GetPackedFlag<kFlagValueCanBeNull>(); } void ClearValueCanBeNull() { SetPackedFlag<kFlagValueCanBeNull>(false); } - bool GetIsPredicatedSet() const { return GetPackedFlag<kFlagIsPredicatedSet>(); } - void SetIsPredicatedSet(bool value = true) { SetPackedFlag<kFlagIsPredicatedSet>(value); } DECLARE_INSTRUCTION(InstanceFieldSet); @@ -6354,8 +6085,7 @@ class HInstanceFieldSet final : public HExpression<2> { private: static constexpr size_t kFlagValueCanBeNull = kNumberOfGenericPackedBits; - static constexpr size_t kFlagIsPredicatedSet = kFlagValueCanBeNull + 1; - static constexpr size_t kNumberOfInstanceFieldSetPackedBits = kFlagIsPredicatedSet + 1; + static constexpr size_t kNumberOfInstanceFieldSetPackedBits = kFlagValueCanBeNull + 1; static_assert(kNumberOfInstanceFieldSetPackedBits <= kMaxNumberOfPackedBits, "Too many packed fields."); @@ -7286,8 +7016,7 @@ class HStaticFieldGet final : public HExpression<1> { return (HInstruction::ComputeHashCode() << 7) | GetFieldOffset().SizeValue(); } - bool IsFieldAccess() const override { return true; } - const FieldInfo& GetFieldInfo() const override { return field_info_; } + const FieldInfo& GetFieldInfo() const { return field_info_; } MemberOffset GetFieldOffset() const { return field_info_.GetFieldOffset(); } DataType::Type GetFieldType() const { return field_info_.GetFieldType(); } bool IsVolatile() const { return field_info_.IsVolatile(); } @@ -7336,8 +7065,7 @@ class HStaticFieldSet final : public HExpression<2> { } bool IsClonable() const override { return true; } - bool IsFieldAccess() const override { return true; } - const FieldInfo& GetFieldInfo() const override { return field_info_; } + const FieldInfo& GetFieldInfo() const { return field_info_; } MemberOffset GetFieldOffset() const { return field_info_.GetFieldOffset(); } DataType::Type GetFieldType() const { return field_info_.GetFieldType(); } bool IsVolatile() const { return field_info_.IsVolatile(); } @@ -8256,7 +7984,7 @@ class HParallelMove final : public HExpression<0> { DCHECK(!destination.OverlapsWith(move.GetDestination())) << "Overlapped destination for two moves in a parallel move: " << move.GetSource() << " ==> " << move.GetDestination() << " and " - << source << " ==> " << destination << " for " << *instruction; + << source << " ==> " << destination; } } moves_.emplace_back(source, destination, type, instruction); diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc index 8cd34cf68f..ac241aa9c9 100644 --- a/compiler/optimizing/optimizing_compiler.cc +++ b/compiler/optimizing/optimizing_compiler.cc @@ -194,9 +194,7 @@ class PassObserver : public ValueObject { GraphChecker checker(graph_, codegen_); last_seen_graph_size_ = checker.Run(pass_change, last_seen_graph_size_); if (!checker.IsValid()) { - LOG(FATAL_WITHOUT_ABORT) << "Error after " << pass_name << "(" << graph_->PrettyMethod() - << "): " << *graph_; - LOG(FATAL) << "(" << pass_name << "): " << Dumpable<GraphChecker>(checker); + LOG(FATAL) << "Error after " << pass_name << ": " << Dumpable<GraphChecker>(checker); } } } diff --git a/compiler/optimizing/optimizing_compiler_stats.h b/compiler/optimizing/optimizing_compiler_stats.h index a2f71cfdf3..4322eb72a0 100644 --- a/compiler/optimizing/optimizing_compiler_stats.h +++ b/compiler/optimizing/optimizing_compiler_stats.h @@ -113,9 +113,6 @@ enum class MethodCompilationStat { kNonPartialLoadRemoved, kPartialLSEPossible, kPartialStoreRemoved, - kPartialAllocationMoved, - kPredicatedLoadAdded, - kPredicatedStoreAdded, kLastStat }; std::ostream& operator<<(std::ostream& os, MethodCompilationStat rhs); diff --git a/compiler/optimizing/optimizing_unit_test.h b/compiler/optimizing/optimizing_unit_test.h index cf97c41983..89b606d9d2 100644 --- a/compiler/optimizing/optimizing_unit_test.h +++ b/compiler/optimizing/optimizing_unit_test.h @@ -18,10 +18,8 @@ #define ART_COMPILER_OPTIMIZING_OPTIMIZING_UNIT_TEST_H_ #include <memory> -#include <string_view> #include <vector> -#include "base/indenter.h" #include "base/malloc_arena_pool.h" #include "base/scoped_arena_allocator.h" #include "builder.h" @@ -32,9 +30,7 @@ #include "dex/standard_dex_file.h" #include "driver/dex_compilation_unit.h" #include "graph_checker.h" -#include "gtest/gtest.h" #include "handle_scope-inl.h" -#include "handle_scope.h" #include "mirror/class_loader.h" #include "mirror/dex_cache.h" #include "nodes.h" @@ -42,6 +38,8 @@ #include "ssa_builder.h" #include "ssa_liveness_analysis.h" +#include "gtest/gtest.h" + namespace art { #define NUM_INSTRUCTIONS(...) \ @@ -185,8 +183,8 @@ class OptimizingUnitTestHelper { } } - void InitGraph(VariableSizedHandleScope* handles = nullptr) { - CreateGraph(handles); + void InitGraph() { + CreateGraph(); entry_block_ = AddNewBlock(); return_block_ = AddNewBlock(); exit_block_ = AddNewBlock(); @@ -248,48 +246,6 @@ class OptimizingUnitTestHelper { return environment; } - void EnsurePredecessorOrder(HBasicBlock* target, std::initializer_list<HBasicBlock*> preds) { - // Make sure the given preds and block predecessors have the same blocks. - BitVector bv(preds.size(), false, Allocator::GetMallocAllocator()); - auto preds_and_idx = ZipCount(MakeIterationRange(target->GetPredecessors())); - bool correct_preds = preds.size() == target->GetPredecessors().size() && - std::all_of(preds.begin(), preds.end(), [&](HBasicBlock* pred) { - return std::any_of(preds_and_idx.begin(), - preds_and_idx.end(), - // Make sure every target predecessor is used only - // once. - [&](std::pair<HBasicBlock*, uint32_t> cur) { - if (cur.first == pred && !bv.IsBitSet(cur.second)) { - bv.SetBit(cur.second); - return true; - } else { - return false; - } - }); - }) && - bv.NumSetBits() == preds.size(); - auto dump_list = [](auto it) { - std::ostringstream oss; - oss << "["; - bool first = true; - for (HBasicBlock* b : it) { - if (!first) { - oss << ", "; - } - first = false; - oss << b->GetBlockId(); - } - oss << "]"; - return oss.str(); - }; - ASSERT_TRUE(correct_preds) << "Predecessors of " << target->GetBlockId() << " are " - << dump_list(target->GetPredecessors()) << " not " - << dump_list(preds); - if (correct_preds) { - std::copy(preds.begin(), preds.end(), target->predecessors_.begin()); - } - } - protected: bool CheckGraph(HGraph* graph, bool check_ref_type_info, std::ostream& oss) { GraphChecker checker(graph); @@ -386,34 +342,12 @@ class AdjacencyListGraph { AdjacencyListGraph& operator=(AdjacencyListGraph&&) = default; AdjacencyListGraph& operator=(const AdjacencyListGraph&) = default; - std::ostream& Dump(std::ostream& os) const { - struct Namer : public BlockNamer { - public: - explicit Namer(const AdjacencyListGraph& alg) : BlockNamer(), alg_(alg) {} - std::ostream& PrintName(std::ostream& os, HBasicBlock* blk) const override { - if (alg_.HasBlock(blk)) { - return os << alg_.GetName(blk) << " (" << blk->GetBlockId() << ")"; - } else { - return os << "<Unnamed B" << blk->GetBlockId() << ">"; - } - } - - const AdjacencyListGraph& alg_; - }; - Namer namer(*this); - return graph_->Dump(os, namer); - } - private: HGraph* graph_; SafeMap<const std::string_view, HBasicBlock*> name_to_block_; SafeMap<const HBasicBlock*, const std::string_view> block_to_name_; }; -inline std::ostream& operator<<(std::ostream& oss, const AdjacencyListGraph& alg) { - return alg.Dump(oss); -} - } // namespace art #endif // ART_COMPILER_OPTIMIZING_OPTIMIZING_UNIT_TEST_H_ diff --git a/compiler/optimizing/prepare_for_register_allocation.cc b/compiler/optimizing/prepare_for_register_allocation.cc index c2f3d0e741..d5edc3da36 100644 --- a/compiler/optimizing/prepare_for_register_allocation.cc +++ b/compiler/optimizing/prepare_for_register_allocation.cc @@ -268,10 +268,6 @@ bool PrepareForRegisterAllocation::CanMoveClinitCheck(HInstruction* input, return false; } - if (user->IsNewInstance() && user->AsNewInstance()->IsPartialMaterialization()) { - return false; - } - // Now do a thorough environment check that this is really coming from the same instruction in // the same inlined graph. Unfortunately, we have to go through the whole environment chain. HEnvironment* user_environment = user->GetEnvironment(); @@ -300,8 +296,8 @@ bool PrepareForRegisterAllocation::CanMoveClinitCheck(HInstruction* input, if (kIsDebugBuild) { for (HInstruction* between = input->GetNext(); between != user; between = between->GetNext()) { CHECK(between != nullptr); // User must be after input in the same block. - CHECK(!between->CanThrow()) << *between << " User: " << *user; - CHECK(!between->HasSideEffects()) << *between << " User: " << *user; + CHECK(!between->CanThrow()); + CHECK(!between->HasSideEffects()); } } return true; diff --git a/compiler/optimizing/reference_type_propagation.cc b/compiler/optimizing/reference_type_propagation.cc index 1b2f71f7a7..953329d5b2 100644 --- a/compiler/optimizing/reference_type_propagation.cc +++ b/compiler/optimizing/reference_type_propagation.cc @@ -67,7 +67,6 @@ class ReferenceTypePropagation::RTPVisitor : public HGraphDelegateVisitor { void VisitLoadException(HLoadException* instr) override; void VisitNewArray(HNewArray* instr) override; void VisitParameterValue(HParameterValue* instr) override; - void VisitPredicatedInstanceFieldGet(HPredicatedInstanceFieldGet* instr) override; void VisitInstanceFieldGet(HInstanceFieldGet* instr) override; void VisitStaticFieldGet(HStaticFieldGet* instr) override; void VisitUnresolvedInstanceFieldGet(HUnresolvedInstanceFieldGet* instr) override; @@ -314,8 +313,10 @@ static void BoundTypeForClassCheck(HInstruction* check) { return; } - HInstruction* field_get = (load_class == input_one) ? input_two : input_one; - if (!field_get->IsInstanceFieldGet() && !field_get->IsPredicatedInstanceFieldGet()) { + HInstanceFieldGet* field_get = (load_class == input_one) + ? input_two->AsInstanceFieldGet() + : input_one->AsInstanceFieldGet(); + if (field_get == nullptr) { return; } HInstruction* receiver = field_get->InputAt(0); @@ -623,11 +624,6 @@ void ReferenceTypePropagation::RTPVisitor::UpdateFieldAccessTypeInfo(HInstructio SetClassAsTypeInfo(instr, klass, /* is_exact= */ false); } -void ReferenceTypePropagation::RTPVisitor::VisitPredicatedInstanceFieldGet( - HPredicatedInstanceFieldGet* instr) { - UpdateFieldAccessTypeInfo(instr, instr->GetFieldInfo()); -} - void ReferenceTypePropagation::RTPVisitor::VisitInstanceFieldGet(HInstanceFieldGet* instr) { UpdateFieldAccessTypeInfo(instr, instr->GetFieldInfo()); } diff --git a/compiler/optimizing/scheduler.cc b/compiler/optimizing/scheduler.cc index 7140e2424a..c1891de69a 100644 --- a/compiler/optimizing/scheduler.cc +++ b/compiler/optimizing/scheduler.cc @@ -14,14 +14,13 @@ * limitations under the License. */ -#include "scheduler.h" - #include <string> +#include "scheduler.h" + #include "base/scoped_arena_allocator.h" #include "base/scoped_arena_containers.h" #include "data_type-inl.h" -#include "optimizing/load_store_analysis.h" #include "prepare_for_register_allocation.h" #ifdef ART_ENABLE_CODEGEN_arm64 @@ -108,7 +107,6 @@ static bool IsArrayAccess(const HInstruction* instruction) { static bool IsInstanceFieldAccess(const HInstruction* instruction) { return instruction->IsInstanceFieldGet() || instruction->IsInstanceFieldSet() || - instruction->IsPredicatedInstanceFieldGet() || instruction->IsUnresolvedInstanceFieldGet() || instruction->IsUnresolvedInstanceFieldSet(); } @@ -123,7 +121,6 @@ static bool IsStaticFieldAccess(const HInstruction* instruction) { static bool IsResolvedFieldAccess(const HInstruction* instruction) { return instruction->IsInstanceFieldGet() || instruction->IsInstanceFieldSet() || - instruction->IsPredicatedInstanceFieldGet() || instruction->IsStaticFieldGet() || instruction->IsStaticFieldSet(); } @@ -140,7 +137,18 @@ static bool IsFieldAccess(const HInstruction* instruction) { } static const FieldInfo* GetFieldInfo(const HInstruction* instruction) { - return &instruction->GetFieldInfo(); + if (instruction->IsInstanceFieldGet()) { + return &instruction->AsInstanceFieldGet()->GetFieldInfo(); + } else if (instruction->IsInstanceFieldSet()) { + return &instruction->AsInstanceFieldSet()->GetFieldInfo(); + } else if (instruction->IsStaticFieldGet()) { + return &instruction->AsStaticFieldGet()->GetFieldInfo(); + } else if (instruction->IsStaticFieldSet()) { + return &instruction->AsStaticFieldSet()->GetFieldInfo(); + } else { + LOG(FATAL) << "Unexpected field access type"; + UNREACHABLE(); + } } size_t SideEffectDependencyAnalysis::MemoryDependencyAnalysis::FieldAccessHeapLocation( @@ -552,7 +560,7 @@ void HScheduler::Schedule(HGraph* graph) { // should run the analysis or not. const HeapLocationCollector* heap_location_collector = nullptr; ScopedArenaAllocator allocator(graph->GetArenaStack()); - LoadStoreAnalysis lsa(graph, /*stats=*/nullptr, &allocator, LoadStoreAnalysisType::kBasic); + LoadStoreAnalysis lsa(graph, /*stats=*/nullptr, &allocator, /*for_elimination=*/false); if (!only_optimize_loop_blocks_ || graph->HasLoops()) { lsa.Run(); heap_location_collector = &lsa.GetHeapLocationCollector(); @@ -722,37 +730,35 @@ bool HScheduler::IsSchedulable(const HInstruction* instruction) const { // TODO: Some of the instructions above may be safe to schedule (maybe as // scheduling barriers). return instruction->IsArrayGet() || - instruction->IsArraySet() || - instruction->IsArrayLength() || - instruction->IsBoundType() || - instruction->IsBoundsCheck() || - instruction->IsCheckCast() || - instruction->IsClassTableGet() || - instruction->IsCurrentMethod() || - instruction->IsDivZeroCheck() || - (instruction->IsInstanceFieldGet() && !instruction->AsInstanceFieldGet()->IsVolatile()) || - (instruction->IsPredicatedInstanceFieldGet() && - !instruction->AsPredicatedInstanceFieldGet()->IsVolatile()) || - (instruction->IsInstanceFieldSet() && !instruction->AsInstanceFieldSet()->IsVolatile()) || - instruction->IsInstanceOf() || - instruction->IsInvokeInterface() || - instruction->IsInvokeStaticOrDirect() || - instruction->IsInvokeUnresolved() || - instruction->IsInvokeVirtual() || - instruction->IsLoadString() || - instruction->IsNewArray() || - instruction->IsNewInstance() || - instruction->IsNullCheck() || - instruction->IsPackedSwitch() || - instruction->IsParameterValue() || - instruction->IsPhi() || - instruction->IsReturn() || - instruction->IsReturnVoid() || - instruction->IsSelect() || - (instruction->IsStaticFieldGet() && !instruction->AsStaticFieldGet()->IsVolatile()) || - (instruction->IsStaticFieldSet() && !instruction->AsStaticFieldSet()->IsVolatile()) || - instruction->IsSuspendCheck() || - instruction->IsTypeConversion(); + instruction->IsArraySet() || + instruction->IsArrayLength() || + instruction->IsBoundType() || + instruction->IsBoundsCheck() || + instruction->IsCheckCast() || + instruction->IsClassTableGet() || + instruction->IsCurrentMethod() || + instruction->IsDivZeroCheck() || + (instruction->IsInstanceFieldGet() && !instruction->AsInstanceFieldGet()->IsVolatile()) || + (instruction->IsInstanceFieldSet() && !instruction->AsInstanceFieldSet()->IsVolatile()) || + instruction->IsInstanceOf() || + instruction->IsInvokeInterface() || + instruction->IsInvokeStaticOrDirect() || + instruction->IsInvokeUnresolved() || + instruction->IsInvokeVirtual() || + instruction->IsLoadString() || + instruction->IsNewArray() || + instruction->IsNewInstance() || + instruction->IsNullCheck() || + instruction->IsPackedSwitch() || + instruction->IsParameterValue() || + instruction->IsPhi() || + instruction->IsReturn() || + instruction->IsReturnVoid() || + instruction->IsSelect() || + (instruction->IsStaticFieldGet() && !instruction->AsStaticFieldGet()->IsVolatile()) || + (instruction->IsStaticFieldSet() && !instruction->AsStaticFieldSet()->IsVolatile()) || + instruction->IsSuspendCheck() || + instruction->IsTypeConversion(); } bool HScheduler::IsSchedulable(const HBasicBlock* block) const { diff --git a/compiler/optimizing/scheduler_arm.cc b/compiler/optimizing/scheduler_arm.cc index f9004d867b..858a555e97 100644 --- a/compiler/optimizing/scheduler_arm.cc +++ b/compiler/optimizing/scheduler_arm.cc @@ -853,11 +853,6 @@ void SchedulingLatencyVisitorARM::VisitDiv(HDiv* instruction) { } } -void SchedulingLatencyVisitorARM::VisitPredicatedInstanceFieldGet( - HPredicatedInstanceFieldGet* instruction) { - HandleFieldGetLatencies(instruction, instruction->GetFieldInfo()); -} - void SchedulingLatencyVisitorARM::VisitInstanceFieldGet(HInstanceFieldGet* instruction) { HandleFieldGetLatencies(instruction, instruction->GetFieldInfo()); } @@ -918,9 +913,7 @@ void SchedulingLatencyVisitorARM::VisitRem(HRem* instruction) { void SchedulingLatencyVisitorARM::HandleFieldGetLatencies(HInstruction* instruction, const FieldInfo& field_info) { - DCHECK(instruction->IsInstanceFieldGet() || - instruction->IsStaticFieldGet() || - instruction->IsPredicatedInstanceFieldGet()); + DCHECK(instruction->IsInstanceFieldGet() || instruction->IsStaticFieldGet()); DCHECK(codegen_ != nullptr); bool is_volatile = field_info.IsVolatile(); DataType::Type field_type = field_info.GetFieldType(); diff --git a/compiler/optimizing/scheduler_arm.h b/compiler/optimizing/scheduler_arm.h index d11222d9f4..4c7a3bb4d6 100644 --- a/compiler/optimizing/scheduler_arm.h +++ b/compiler/optimizing/scheduler_arm.h @@ -61,37 +61,36 @@ class SchedulingLatencyVisitorARM : public SchedulingLatencyVisitor { // We add a second unused parameter to be able to use this macro like the others // defined in `nodes.h`. -#define FOR_EACH_SCHEDULED_ARM_INSTRUCTION(M) \ - M(ArrayGet, unused) \ - M(ArrayLength, unused) \ - M(ArraySet, unused) \ - M(Add, unused) \ - M(Sub, unused) \ - M(And, unused) \ - M(Or, unused) \ - M(Ror, unused) \ - M(Xor, unused) \ - M(Shl, unused) \ - M(Shr, unused) \ - M(UShr, unused) \ - M(Mul, unused) \ - M(Div, unused) \ - M(Condition, unused) \ - M(Compare, unused) \ - M(BoundsCheck, unused) \ - M(PredicatedInstanceFieldGet, unused) \ - M(InstanceFieldGet, unused) \ - M(InstanceFieldSet, unused) \ - M(InstanceOf, unused) \ - M(Invoke, unused) \ - M(LoadString, unused) \ - M(NewArray, unused) \ - M(NewInstance, unused) \ - M(Rem, unused) \ - M(StaticFieldGet, unused) \ - M(StaticFieldSet, unused) \ - M(SuspendCheck, unused) \ - M(TypeConversion, unused) +#define FOR_EACH_SCHEDULED_ARM_INSTRUCTION(M) \ + M(ArrayGet , unused) \ + M(ArrayLength , unused) \ + M(ArraySet , unused) \ + M(Add , unused) \ + M(Sub , unused) \ + M(And , unused) \ + M(Or , unused) \ + M(Ror , unused) \ + M(Xor , unused) \ + M(Shl , unused) \ + M(Shr , unused) \ + M(UShr , unused) \ + M(Mul , unused) \ + M(Div , unused) \ + M(Condition , unused) \ + M(Compare , unused) \ + M(BoundsCheck , unused) \ + M(InstanceFieldGet , unused) \ + M(InstanceFieldSet , unused) \ + M(InstanceOf , unused) \ + M(Invoke , unused) \ + M(LoadString , unused) \ + M(NewArray , unused) \ + M(NewInstance , unused) \ + M(Rem , unused) \ + M(StaticFieldGet , unused) \ + M(StaticFieldSet , unused) \ + M(SuspendCheck , unused) \ + M(TypeConversion , unused) #define FOR_EACH_SCHEDULED_SHARED_INSTRUCTION(M) \ M(BitwiseNegatedRight, unused) \ diff --git a/compiler/optimizing/scheduler_test.cc b/compiler/optimizing/scheduler_test.cc index a1cc202a89..c166a46082 100644 --- a/compiler/optimizing/scheduler_test.cc +++ b/compiler/optimizing/scheduler_test.cc @@ -274,7 +274,7 @@ class SchedulerTest : public OptimizingUnitTest { } HeapLocationCollector heap_location_collector( - graph_, GetScopedAllocator(), LoadStoreAnalysisType::kBasic); + graph_, GetScopedAllocator(), /*for_partial_elimination=*/false); heap_location_collector.VisitBasicBlock(entry); heap_location_collector.BuildAliasingMatrix(); TestSchedulingGraph scheduling_graph(GetScopedAllocator(), &heap_location_collector); diff --git a/runtime/offsets.h b/runtime/offsets.h index cc18bf4f74..2f36fe6142 100644 --- a/runtime/offsets.h +++ b/runtime/offsets.h @@ -40,9 +40,6 @@ class Offset { constexpr bool operator==(Offset o) const { return SizeValue() == o.SizeValue(); } - constexpr bool operator!=(Offset o) const { - return !(*this == o); - } protected: size_t val_; diff --git a/test/530-checker-lse/src/Main.java b/test/530-checker-lse/src/Main.java index a30378271c..f250aa59fb 100644 --- a/test/530-checker-lse/src/Main.java +++ b/test/530-checker-lse/src/Main.java @@ -64,20 +64,6 @@ class TestClass3 { boolean test1 = true; } -// Chosen to have different values with (x + 1) * 10 and (x - 1) * 10. This -// means we can easily make sure that different code is in fact executed on -// escape and non-escape paths. -// Negative so that high-bits will be set for all the 64-bit values allowing us -// to easily check for truncation. -class TestClass4 { - float floatField = -3.0f; - double doubleField = -3.0d; - short shortField = -3; - int intField = -3; - byte byteField = -3; - long longField = -3l; -} - class Finalizable { static boolean sVisited = false; static final int VALUE1 = 0xbeef; @@ -97,20 +83,9 @@ interface Filter { } public class Main { - static void $noinline$Escape4(TestClass4 o) { - o.floatField += 1.0f; - o.doubleField += 1.0d; - o.byteField += 1; - o.shortField += 1; - o.intField += 1; - o.longField += 1; - } static Object ESCAPE = null; static void $noinline$Escape(TestClass o) { - if (o == null) { - return; - } ESCAPE = o; o.next.i++; } @@ -3817,248 +3792,6 @@ public class Main { return res; } - /// CHECK-START: int Main.$noinline$testPartialEscape2(TestClass, boolean) load_store_elimination (before) - /// CHECK-DAG: ParameterValue - /// CHECK-DAG: NewInstance - /// CHECK-DAG: InvokeStaticOrDirect - /// CHECK-DAG: InvokeStaticOrDirect - /// CHECK-DAG: InvokeStaticOrDirect - /// CHECK-DAG: InstanceFieldSet - /// CHECK-DAG: InstanceFieldSet - /// CHECK-DAG: InstanceFieldSet - /// CHECK-DAG: InstanceFieldGet - /// CHECK-DAG: InstanceFieldGet - // - /// CHECK-START: int Main.$noinline$testPartialEscape2(TestClass, boolean) load_store_elimination (after) - /// CHECK-DAG: ParameterValue - /// CHECK-DAG: NewInstance - /// CHECK-DAG: Phi - // - /// CHECK-START: int Main.$noinline$testPartialEscape2(TestClass, boolean) load_store_elimination (after) - /// CHECK: InvokeStaticOrDirect - /// CHECK: InvokeStaticOrDirect - /// CHECK: InvokeStaticOrDirect - // - /// CHECK-NOT: InvokeStaticOrDirect - - /// CHECK-START: int Main.$noinline$testPartialEscape2(TestClass, boolean) load_store_elimination (after) - /// CHECK: InstanceFieldSet predicated:false - /// CHECK-NOT: InstanceFieldSet predicated:false - // - /// CHECK-START: int Main.$noinline$testPartialEscape2(TestClass, boolean) load_store_elimination (after) - /// CHECK: InstanceFieldSet predicated:true - /// CHECK-NOT: InstanceFieldSet predicated:true - // - /// CHECK-START: int Main.$noinline$testPartialEscape2(TestClass, boolean) load_store_elimination (after) - /// CHECK: InstanceFieldGet - // - /// CHECK-NOT: InstanceFieldGet - // - /// CHECK-START: int Main.$noinline$testPartialEscape2(TestClass, boolean) load_store_elimination (after) - /// CHECK: PredicatedInstanceFieldGet - // - /// CHECK-NOT: PredicatedInstanceFieldGet - private static int $noinline$testPartialEscape2(TestClass obj, boolean escape) { - TestClass i = new SubTestClass(); - if ($noinline$getBoolean(escape)) { - i.next = obj; - $noinline$Escape(i); - } else { - i.next = obj; - } - $noinline$clobberObservables(); - // Predicated-get - TestClass res = i.next; - // Predicated-set - i.next = null; - return res.i; - } - - /// CHECK-START: float Main.$noinline$testPartialEscape3_float(boolean) load_store_elimination (before) - /// CHECK-NOT: Phi - /// CHECK-NOT: PredicatedInstanceFieldGet - // - /// CHECK-START: float Main.$noinline$testPartialEscape3_float(boolean) load_store_elimination (after) - /// CHECK: Phi - /// CHECK: Phi - /// CHECK-NOT: Phi - // - /// CHECK-START: float Main.$noinline$testPartialEscape3_float(boolean) load_store_elimination (after) - /// CHECK: InstanceFieldSet predicated:true - /// CHECK-NOT: InstanceFieldSet predicated:true - // - /// CHECK-START: float Main.$noinline$testPartialEscape3_float(boolean) load_store_elimination (after) - /// CHECK: PredicatedInstanceFieldGet - /// CHECK-NOT: PredicatedInstanceFieldGet - private static float $noinline$testPartialEscape3_float(boolean escape) { - TestClass4 tc = new TestClass4(); - if ($noinline$getBoolean(escape)) { - $noinline$Escape4(tc); - } else { - tc.floatField -= 1f; - } - // Partial escape - $noinline$clobberObservables(); - // Predicated set - tc.floatField *= 10; - // Predicated get - return tc.floatField; - } - - /// CHECK-START: double Main.$noinline$testPartialEscape3_double(boolean) load_store_elimination (before) - /// CHECK-NOT: Phi - /// CHECK-NOT: PredicatedInstanceFieldGet - // - /// CHECK-START: double Main.$noinline$testPartialEscape3_double(boolean) load_store_elimination (after) - /// CHECK: Phi - /// CHECK: Phi - /// CHECK-NOT: Phi - // - /// CHECK-START: double Main.$noinline$testPartialEscape3_double(boolean) load_store_elimination (after) - /// CHECK: InstanceFieldSet predicated:true - /// CHECK-NOT: InstanceFieldSet predicated:true - // - /// CHECK-START: double Main.$noinline$testPartialEscape3_double(boolean) load_store_elimination (after) - /// CHECK: PredicatedInstanceFieldGet - /// CHECK-NOT: PredicatedInstanceFieldGet - private static double $noinline$testPartialEscape3_double(boolean escape) { - TestClass4 tc = new TestClass4(); - if ($noinline$getBoolean(escape)) { - $noinline$Escape4(tc); - } else { - tc.doubleField -= 1d; - } - // Partial escape - $noinline$clobberObservables(); - // Predicated set - tc.doubleField *= 10; - // Predicated get - return tc.doubleField; - } - - /// CHECK-START: short Main.$noinline$testPartialEscape3_short(boolean) load_store_elimination (before) - /// CHECK-NOT: Phi - /// CHECK-NOT: PredicatedInstanceFieldGet - // - /// CHECK-START: short Main.$noinline$testPartialEscape3_short(boolean) load_store_elimination (after) - /// CHECK: Phi - /// CHECK: Phi - /// CHECK-NOT: Phi - // - /// CHECK-START: short Main.$noinline$testPartialEscape3_short(boolean) load_store_elimination (after) - /// CHECK: InstanceFieldSet predicated:true - /// CHECK-NOT: InstanceFieldSet predicated:true - // - /// CHECK-START: short Main.$noinline$testPartialEscape3_short(boolean) load_store_elimination (after) - /// CHECK: PredicatedInstanceFieldGet - /// CHECK-NOT: PredicatedInstanceFieldGet - private static short $noinline$testPartialEscape3_short(boolean escape) { - TestClass4 tc = new TestClass4(); - if ($noinline$getBoolean(escape)) { - $noinline$Escape4(tc); - } else { - tc.shortField -= 1; - } - // Partial escape - $noinline$clobberObservables(); - // Predicated set - tc.shortField *= 10; - // Predicated get - return tc.shortField; - } - - /// CHECK-START: byte Main.$noinline$testPartialEscape3_byte(boolean) load_store_elimination (before) - /// CHECK-NOT: Phi - /// CHECK-NOT: PredicatedInstanceFieldGet - // - /// CHECK-START: byte Main.$noinline$testPartialEscape3_byte(boolean) load_store_elimination (after) - /// CHECK: Phi - /// CHECK: Phi - /// CHECK-NOT: Phi - // - /// CHECK-START: byte Main.$noinline$testPartialEscape3_byte(boolean) load_store_elimination (after) - /// CHECK: InstanceFieldSet predicated:true - /// CHECK-NOT: InstanceFieldSet predicated:true - // - /// CHECK-START: byte Main.$noinline$testPartialEscape3_byte(boolean) load_store_elimination (after) - /// CHECK: PredicatedInstanceFieldGet - /// CHECK-NOT: PredicatedInstanceFieldGet - private static byte $noinline$testPartialEscape3_byte(boolean escape) { - TestClass4 tc = new TestClass4(); - if ($noinline$getBoolean(escape)) { - $noinline$Escape4(tc); - } else { - tc.byteField -= 1; - } - // Partial escape - $noinline$clobberObservables(); - // Predicated set - tc.byteField *= 10; - // Predicated get - return tc.byteField; - } - - /// CHECK-START: int Main.$noinline$testPartialEscape3_int(boolean) load_store_elimination (before) - /// CHECK-NOT: Phi - /// CHECK-NOT: PredicatedInstanceFieldGet - // - /// CHECK-START: int Main.$noinline$testPartialEscape3_int(boolean) load_store_elimination (after) - /// CHECK: Phi - /// CHECK: Phi - /// CHECK-NOT: Phi - // - /// CHECK-START: int Main.$noinline$testPartialEscape3_int(boolean) load_store_elimination (after) - /// CHECK: InstanceFieldSet predicated:true - /// CHECK-NOT: InstanceFieldSet predicated:true - // - /// CHECK-START: int Main.$noinline$testPartialEscape3_int(boolean) load_store_elimination (after) - /// CHECK: PredicatedInstanceFieldGet - /// CHECK-NOT: PredicatedInstanceFieldGet - private static int $noinline$testPartialEscape3_int(boolean escape) { - TestClass4 tc = new TestClass4(); - if ($noinline$getBoolean(escape)) { - $noinline$Escape4(tc); - } else { - tc.intField -= 1; - } - // Partial escape - $noinline$clobberObservables(); - // Predicated set - tc.intField *= 10; - // Predicated get - return tc.intField; - } - - /// CHECK-START: long Main.$noinline$testPartialEscape3_long(boolean) load_store_elimination (before) - /// CHECK-NOT: Phi - /// CHECK-NOT: PredicatedInstanceFieldGet - // - /// CHECK-START: long Main.$noinline$testPartialEscape3_long(boolean) load_store_elimination (after) - /// CHECK: Phi - /// CHECK: Phi - /// CHECK-NOT: Phi - // - /// CHECK-START: long Main.$noinline$testPartialEscape3_long(boolean) load_store_elimination (after) - /// CHECK: InstanceFieldSet predicated:true - /// CHECK-NOT: InstanceFieldSet predicated:true - // - /// CHECK-START: long Main.$noinline$testPartialEscape3_long(boolean) load_store_elimination (after) - /// CHECK: PredicatedInstanceFieldGet - /// CHECK-NOT: PredicatedInstanceFieldGet - private static long $noinline$testPartialEscape3_long(boolean escape) { - TestClass4 tc = new TestClass4(); - if ($noinline$getBoolean(escape)) { - $noinline$Escape4(tc); - } else { - tc.longField -= 1; - } - // Partial escape - $noinline$clobberObservables(); - // Predicated set - tc.longField *= 10; - // Predicated get - return tc.longField; - } private static void $noinline$clobberObservables() {} @@ -4463,19 +4196,5 @@ public class Main { assertLongEquals(testOverlapLoop(50), 7778742049l); assertIntEquals($noinline$testPartialEscape1(new TestClass(), true), 1); assertIntEquals($noinline$testPartialEscape1(new TestClass(), false), 0); - assertIntEquals($noinline$testPartialEscape2(new TestClass(), true), 1); - assertIntEquals($noinline$testPartialEscape2(new TestClass(), false), 0); - assertDoubleEquals($noinline$testPartialEscape3_double(true), -20d); - assertDoubleEquals($noinline$testPartialEscape3_double(false), -40d); - assertFloatEquals($noinline$testPartialEscape3_float(true), -20f); - assertFloatEquals($noinline$testPartialEscape3_float(false), -40f); - assertIntEquals($noinline$testPartialEscape3_int(true), -20); - assertIntEquals($noinline$testPartialEscape3_int(false), -40); - assertIntEquals($noinline$testPartialEscape3_byte(true), -20); - assertIntEquals($noinline$testPartialEscape3_byte(false), -40); - assertIntEquals($noinline$testPartialEscape3_short(true), -20); - assertIntEquals($noinline$testPartialEscape3_short(false), -40); - assertLongEquals($noinline$testPartialEscape3_long(true), -20); - assertLongEquals($noinline$testPartialEscape3_long(false), -40); } } diff --git a/test/639-checker-code-sinking/src/Main.java b/test/639-checker-code-sinking/src/Main.java index 28fa57cfbf..8efac92c34 100644 --- a/test/639-checker-code-sinking/src/Main.java +++ b/test/639-checker-code-sinking/src/Main.java @@ -100,20 +100,13 @@ public class Main { } } - // NB It might seem that we'd move the allocation and ifield-set but those are - // already moved into the throw block by a combo of partial-LSE and DCE. - // Instead all that is actually moved is the LoadClass. Also note the - // LoadClass can only be moved since it refers to the 'Main' class itself, - // meaning there's no need for any clinit/actual loading. - // /// CHECK-START: void Main.testFieldStores(boolean) code_sinking (before) /// CHECK: <<Int42:i\d+>> IntConstant 42 - /// CHECK: begin_block /// CHECK: <<LoadClass:l\d+>> LoadClass class_name:Main - /// CHECK: If - /// CHECK: begin_block /// CHECK: <<NewInstance:l\d+>> NewInstance [<<LoadClass>>] /// CHECK: InstanceFieldSet [<<NewInstance>>,<<Int42>>] + /// CHECK: If + /// CHECK: begin_block /// CHECK: Throw /// CHECK-START: void Main.testFieldStores(boolean) code_sinking (after) @@ -121,17 +114,15 @@ public class Main { /// CHECK-NOT: NewInstance /// CHECK: If /// CHECK: begin_block + /// CHECK: <<Error:l\d+>> LoadClass class_name:java.lang.Error /// CHECK: <<LoadClass:l\d+>> LoadClass class_name:Main /// CHECK-NOT: begin_block /// CHECK: <<NewInstance:l\d+>> NewInstance [<<LoadClass>>] /// CHECK-NOT: begin_block /// CHECK: InstanceFieldSet [<<NewInstance>>,<<Int42>>] /// CHECK-NOT: begin_block - /// CHECK: <<Error:l\d+>> LoadClass class_name:java.lang.Error - /// CHECK-NOT: begin_block - /// CHECK: <<Throw:l\d+>> NewInstance [<<Error>>] - /// CHECK-NOT: begin_block - /// CHECK: Throw [<<Throw>>] + /// CHECK: NewInstance [<<Error>>] + /// CHECK: Throw public static void testFieldStores(boolean doThrow) { Main m = new Main(); m.intField = 42; @@ -315,26 +306,16 @@ public class Main { } // Test that we preserve the order of stores. - // NB It might seem that we'd move the allocation and ifield-set but those are - // already moved into the throw block by a combo of partial-LSE and DCE. - // Instead all that is actually moved is the LoadClass. Also note the - // LoadClass can only be moved since it refers to the 'Main' class itself, - // meaning there's no need for any clinit/actual loading. - // /// CHECK-START: void Main.testStoreStore(boolean) code_sinking (before) /// CHECK: <<Int42:i\d+>> IntConstant 42 /// CHECK: <<Int43:i\d+>> IntConstant 43 /// CHECK: <<LoadClass:l\d+>> LoadClass class_name:Main + /// CHECK: <<NewInstance:l\d+>> NewInstance [<<LoadClass>>] + /// CHECK: InstanceFieldSet [<<NewInstance>>,<<Int42>>] + /// CHECK: InstanceFieldSet [<<NewInstance>>,<<Int43>>] /// CHECK: If /// CHECK: begin_block - // Moved to throw block by partial-LSE and DCE. - /// CHECK: <<NewInstance:l\d+>> NewInstance [<<LoadClass>>] - // These were moved by partial LSE and order of sets is not observable and are - // in an arbitrary order. - /// CHECK-DAG: InstanceFieldSet [<<NewInstance>>,<<Int42>>] - /// CHECK-DAG: InstanceFieldSet [<<NewInstance>>,<<Int43>>] /// CHECK: Throw - /// CHECK-NOT: InstanceFieldSet /// CHECK-START: void Main.testStoreStore(boolean) code_sinking (after) /// CHECK: <<Int42:i\d+>> IntConstant 42 @@ -342,17 +323,17 @@ public class Main { /// CHECK-NOT: NewInstance /// CHECK: If /// CHECK: begin_block + /// CHECK: <<Error:l\d+>> LoadClass class_name:java.lang.Error /// CHECK: <<LoadClass:l\d+>> LoadClass class_name:Main + /// CHECK-NOT: begin_block /// CHECK: <<NewInstance:l\d+>> NewInstance [<<LoadClass>>] /// CHECK-NOT: begin_block - /// CHECK-DAG: InstanceFieldSet [<<NewInstance>>,<<Int42>>] - /// CHECK-DAG: InstanceFieldSet [<<NewInstance>>,<<Int43>>] + /// CHECK: InstanceFieldSet [<<NewInstance>>,<<Int42>>] /// CHECK-NOT: begin_block - /// CHECK: <<Error:l\d+>> LoadClass class_name:java.lang.Error + /// CHECK: InstanceFieldSet [<<NewInstance>>,<<Int43>>] /// CHECK-NOT: begin_block /// CHECK: NewInstance [<<Error>>] /// CHECK: Throw - /// CHECK-NOT: InstanceFieldSet public static void testStoreStore(boolean doThrow) { Main m = new Main(); m.intField = 42; |