diff options
Diffstat (limited to 'compiler/optimizing')
46 files changed, 1387 insertions, 592 deletions
diff --git a/compiler/optimizing/builder.cc b/compiler/optimizing/builder.cc index 57660c2623..1b62531cb1 100644 --- a/compiler/optimizing/builder.cc +++ b/compiler/optimizing/builder.cc @@ -106,7 +106,6 @@ void HGraphBuilder::If_22t(const Instruction& instruction, uint32_t dex_pc) { HBasicBlock* fallthrough_target = FindBlockStartingAt(dex_pc + instruction.SizeInCodeUnits()); DCHECK(branch_target != nullptr); DCHECK(fallthrough_target != nullptr); - PotentiallyAddSuspendCheck(branch_target, dex_pc); HInstruction* first = LoadLocal(instruction.VRegA(), Primitive::kPrimInt, dex_pc); HInstruction* second = LoadLocal(instruction.VRegB(), Primitive::kPrimInt, dex_pc); T* comparison = new (arena_) T(first, second, dex_pc); @@ -125,7 +124,6 @@ void HGraphBuilder::If_21t(const Instruction& instruction, uint32_t dex_pc) { HBasicBlock* fallthrough_target = FindBlockStartingAt(dex_pc + instruction.SizeInCodeUnits()); DCHECK(branch_target != nullptr); DCHECK(fallthrough_target != nullptr); - PotentiallyAddSuspendCheck(branch_target, dex_pc); HInstruction* value = LoadLocal(instruction.VRegA(), Primitive::kPrimInt, dex_pc); T* comparison = new (arena_) T(value, graph_->GetIntConstant(0, dex_pc), dex_pc); current_block_->AddInstruction(comparison); @@ -145,8 +143,8 @@ void HGraphBuilder::MaybeRecordStat(MethodCompilationStat compilation_stat) { bool HGraphBuilder::SkipCompilation(const DexFile::CodeItem& code_item, size_t number_of_branches) { const CompilerOptions& compiler_options = compiler_driver_->GetCompilerOptions(); - CompilerOptions::CompilerFilter compiler_filter = compiler_options.GetCompilerFilter(); - if (compiler_filter == CompilerOptions::kEverything) { + CompilerFilter::Filter compiler_filter = compiler_options.GetCompilerFilter(); + if (compiler_filter == CompilerFilter::kEverything) { return false; } @@ -367,7 +365,8 @@ GraphAnalysisResult HGraphBuilder::BuildGraph(const DexFile::CodeItem& code_item ArenaBitVector* native_debug_info_locations; if (native_debuggable) { const uint32_t num_instructions = code_item.insns_size_in_code_units_; - native_debug_info_locations = new (arena_) ArenaBitVector (arena_, num_instructions, false); + native_debug_info_locations = + ArenaBitVector::Create(arena_, num_instructions, false, kArenaAllocGraphBuilder); FindNativeDebugInfoLocations(code_item, native_debug_info_locations); } @@ -1787,7 +1786,6 @@ void HGraphBuilder::BuildSwitchCaseHelper(const Instruction& instruction, size_t int32_t target_offset, uint32_t dex_pc) { HBasicBlock* case_target = FindBlockStartingAt(dex_pc + target_offset); DCHECK(case_target != nullptr); - PotentiallyAddSuspendCheck(case_target, dex_pc); // The current case's value. HInstruction* this_case_value = graph_->GetIntConstant(case_value_int, dex_pc); @@ -1823,23 +1821,6 @@ void HGraphBuilder::BuildSwitchCaseHelper(const Instruction& instruction, size_t } } -void HGraphBuilder::PotentiallyAddSuspendCheck(HBasicBlock* target, uint32_t dex_pc) { - int32_t target_offset = target->GetDexPc() - dex_pc; - if (target_offset <= 0) { - // DX generates back edges to the first encountered return. We can save - // time of later passes by not adding redundant suspend checks. - HInstruction* last_in_target = target->GetLastInstruction(); - if (last_in_target != nullptr && - (last_in_target->IsReturn() || last_in_target->IsReturnVoid())) { - return; - } - - // Add a suspend check to backward branches which may potentially loop. We - // can remove them after we recognize loops in the graph. - current_block_->AddInstruction(new (arena_) HSuspendCheck(dex_pc)); - } -} - bool HGraphBuilder::CanDecodeQuickenedInfo() const { return interpreter_metadata_ != nullptr; } @@ -1971,7 +1952,6 @@ bool HGraphBuilder::AnalyzeDexInstruction(const Instruction& instruction, uint32 int32_t offset = instruction.GetTargetOffset(); HBasicBlock* target = FindBlockStartingAt(offset + dex_pc); DCHECK(target != nullptr); - PotentiallyAddSuspendCheck(target, dex_pc); current_block_->AddInstruction(new (arena_) HGoto(dex_pc)); current_block_->AddSuccessor(target); current_block_ = nullptr; diff --git a/compiler/optimizing/builder.h b/compiler/optimizing/builder.h index e3dd0e8216..48f5316222 100644 --- a/compiler/optimizing/builder.h +++ b/compiler/optimizing/builder.h @@ -133,7 +133,6 @@ class HGraphBuilder : public ValueObject { HLocal* GetLocalAt(uint32_t register_index) const; void UpdateLocal(uint32_t register_index, HInstruction* instruction, uint32_t dex_pc) const; HInstruction* LoadLocal(uint32_t register_index, Primitive::Type type, uint32_t dex_pc) const; - void PotentiallyAddSuspendCheck(HBasicBlock* target, uint32_t dex_pc); void InitializeParameters(uint16_t number_of_parameters); // Returns whether the current method needs access check for the type. diff --git a/compiler/optimizing/code_generator.cc b/compiler/optimizing/code_generator.cc index 039c3c562b..32869ec0b4 100644 --- a/compiler/optimizing/code_generator.cc +++ b/compiler/optimizing/code_generator.cc @@ -196,7 +196,7 @@ void CodeGenerator::GenerateSlowPaths() { code_start = GetAssembler()->CodeSize(); } // Record the dex pc at start of slow path (required for java line number mapping). - MaybeRecordNativeDebugInfo(nullptr /* instruction */, slow_path->GetDexPc()); + MaybeRecordNativeDebugInfo(slow_path->GetInstruction(), slow_path->GetDexPc(), slow_path); slow_path->EmitNativeCode(this); if (disasm_info_ != nullptr) { disasm_info_->AddSlowPathInterval(slow_path, code_start, GetAssembler()->CodeSize()); @@ -234,6 +234,12 @@ void CodeGenerator::Compile(CodeAllocator* allocator) { MaybeRecordNativeDebugInfo(nullptr /* instruction */, block->GetDexPc()); for (HInstructionIterator it(block->GetInstructions()); !it.Done(); it.Advance()) { HInstruction* current = it.Current(); + if (current->HasEnvironment()) { + // Create stackmap for HNativeDebugInfo or any instruction which calls native code. + // Note that we need correct mapping for the native PC of the call instruction, + // so the runtime's stackmap is not sufficient since it is at PC after the call. + MaybeRecordNativeDebugInfo(current, block->GetDexPc()); + } DisassemblyScope disassembly_scope(current, *this); DCHECK(CheckTypeConsistency(current)); current->Accept(instruction_visitor); @@ -823,13 +829,15 @@ bool CodeGenerator::HasStackMapAtCurrentPc() { return count > 0 && stack_map_stream_.GetStackMap(count - 1).native_pc_offset == pc; } -void CodeGenerator::MaybeRecordNativeDebugInfo(HInstruction* instruction, uint32_t dex_pc) { +void CodeGenerator::MaybeRecordNativeDebugInfo(HInstruction* instruction, + uint32_t dex_pc, + SlowPathCode* slow_path) { if (GetCompilerOptions().GetNativeDebuggable() && dex_pc != kNoDexPc) { if (HasStackMapAtCurrentPc()) { // Ensure that we do not collide with the stack map of the previous instruction. GenerateNop(); } - RecordPcInfo(instruction, dex_pc); + RecordPcInfo(instruction, dex_pc, slow_path); } } @@ -848,7 +856,8 @@ void CodeGenerator::RecordCatchBlockInfo() { uint32_t register_mask = 0; // Not used. // The stack mask is not used, so we leave it empty. - ArenaBitVector* stack_mask = new (arena) ArenaBitVector(arena, 0, /* expandable */ true); + ArenaBitVector* stack_mask = + ArenaBitVector::Create(arena, 0, /* expandable */ true, kArenaAllocCodeGenerator); stack_map_stream_.BeginStackMapEntry(dex_pc, native_pc, @@ -1110,6 +1119,16 @@ void CodeGenerator::MaybeRecordImplicitNullCheck(HInstruction* instr) { } } +void CodeGenerator::GenerateNullCheck(HNullCheck* instruction) { + if (IsImplicitNullCheckAllowed(instruction)) { + MaybeRecordStat(kImplicitNullCheckGenerated); + GenerateImplicitNullCheck(instruction); + } else { + MaybeRecordStat(kExplicitNullCheckGenerated); + GenerateExplicitNullCheck(instruction); + } +} + void CodeGenerator::ClearSpillSlotsFromLoopPhisInStackMap(HSuspendCheck* suspend_check) const { LocationSummary* locations = suspend_check->GetLocations(); HBasicBlock* block = suspend_check->GetBlock(); diff --git a/compiler/optimizing/code_generator.h b/compiler/optimizing/code_generator.h index 9297fc956f..e56323ff0f 100644 --- a/compiler/optimizing/code_generator.h +++ b/compiler/optimizing/code_generator.h @@ -106,6 +106,10 @@ class SlowPathCode : public ArenaObject<kArenaAllocSlowPaths> { Label* GetEntryLabel() { return &entry_label_; } Label* GetExitLabel() { return &exit_label_; } + HInstruction* GetInstruction() const { + return instruction_; + } + uint32_t GetDexPc() const { return instruction_ != nullptr ? instruction_->GetDexPc() : kNoDexPc; } @@ -274,10 +278,15 @@ class CodeGenerator { // Check whether we have already recorded mapping at this PC. bool HasStackMapAtCurrentPc(); // Record extra stack maps if we support native debugging. - void MaybeRecordNativeDebugInfo(HInstruction* instruction, uint32_t dex_pc); + void MaybeRecordNativeDebugInfo(HInstruction* instruction, + uint32_t dex_pc, + SlowPathCode* slow_path = nullptr); bool CanMoveNullCheckToUser(HNullCheck* null_check); void MaybeRecordImplicitNullCheck(HInstruction* instruction); + void GenerateNullCheck(HNullCheck* null_check); + virtual void GenerateImplicitNullCheck(HNullCheck* null_check) = 0; + virtual void GenerateExplicitNullCheck(HNullCheck* null_check) = 0; // Records a stack map which the runtime might use to set catch phi values // during exception delivery. diff --git a/compiler/optimizing/code_generator_arm.cc b/compiler/optimizing/code_generator_arm.cc index 23528b50ff..3a18a0d7e7 100644 --- a/compiler/optimizing/code_generator_arm.cc +++ b/compiler/optimizing/code_generator_arm.cc @@ -1543,8 +1543,8 @@ void LocationsBuilderARM::VisitNativeDebugInfo(HNativeDebugInfo* info) { new (GetGraph()->GetArena()) LocationSummary(info); } -void InstructionCodeGeneratorARM::VisitNativeDebugInfo(HNativeDebugInfo* info) { - codegen_->MaybeRecordNativeDebugInfo(info, info->GetDexPc()); +void InstructionCodeGeneratorARM::VisitNativeDebugInfo(HNativeDebugInfo*) { + // MaybeRecordNativeDebugInfo is already called implicitly in CodeGenerator::Compile. } void CodeGeneratorARM::GenerateNop() { @@ -3221,7 +3221,7 @@ void InstructionCodeGeneratorARM::HandleLongRotate(LocationSummary* locations) { if (rhs.IsConstant()) { uint64_t rot = CodeGenerator::GetInt64ValueOf(rhs.GetConstant()); // Map all rotations to +ve. equivalents on the interval [0,63]. - rot &= kMaxLongShiftValue; + rot &= kMaxLongShiftDistance; // For rotates over a word in size, 'pre-rotate' by 32-bits to keep rotate // logic below to a simple pair of binary orr. // (e.g. 34 bits == in_reg swap + 2 bits right.) @@ -3273,7 +3273,8 @@ void InstructionCodeGeneratorARM::HandleLongRotate(LocationSummary* locations) { __ Bind(&end); } } -void LocationsBuilderARM::HandleRotate(HRor* ror) { + +void LocationsBuilderARM::VisitRor(HRor* ror) { LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(ror, LocationSummary::kNoCall); switch (ror->GetResultType()) { @@ -3300,7 +3301,7 @@ void LocationsBuilderARM::HandleRotate(HRor* ror) { } } -void InstructionCodeGeneratorARM::HandleRotate(HRor* ror) { +void InstructionCodeGeneratorARM::VisitRor(HRor* ror) { LocationSummary* locations = ror->GetLocations(); Primitive::Type type = ror->GetResultType(); switch (type) { @@ -3318,14 +3319,6 @@ void InstructionCodeGeneratorARM::HandleRotate(HRor* ror) { } } -void LocationsBuilderARM::VisitRor(HRor* op) { - HandleRotate(op); -} - -void InstructionCodeGeneratorARM::VisitRor(HRor* op) { - HandleRotate(op); -} - void LocationsBuilderARM::HandleShift(HBinaryOperation* op) { DCHECK(op->IsShl() || op->IsShr() || op->IsUShr()); @@ -3381,7 +3374,7 @@ void InstructionCodeGeneratorARM::HandleShift(HBinaryOperation* op) { if (second.IsRegister()) { Register second_reg = second.AsRegister<Register>(); // ARM doesn't mask the shift count so we need to do it ourselves. - __ and_(out_reg, second_reg, ShifterOperand(kMaxIntShiftValue)); + __ and_(out_reg, second_reg, ShifterOperand(kMaxIntShiftDistance)); if (op->IsShl()) { __ Lsl(out_reg, first_reg, out_reg); } else if (op->IsShr()) { @@ -3391,7 +3384,7 @@ void InstructionCodeGeneratorARM::HandleShift(HBinaryOperation* op) { } } else { int32_t cst = second.GetConstant()->AsIntConstant()->GetValue(); - uint32_t shift_value = static_cast<uint32_t>(cst & kMaxIntShiftValue); + uint32_t shift_value = cst & kMaxIntShiftDistance; if (shift_value == 0) { // ARM does not support shifting with 0 immediate. __ Mov(out_reg, first_reg); } else if (op->IsShl()) { @@ -3417,7 +3410,7 @@ void InstructionCodeGeneratorARM::HandleShift(HBinaryOperation* op) { Register second_reg = second.AsRegister<Register>(); if (op->IsShl()) { - __ and_(o_l, second_reg, ShifterOperand(kMaxLongShiftValue)); + __ and_(o_l, second_reg, ShifterOperand(kMaxLongShiftDistance)); // Shift the high part __ Lsl(o_h, high, o_l); // Shift the low part and `or` what overflew on the high part @@ -3431,7 +3424,7 @@ void InstructionCodeGeneratorARM::HandleShift(HBinaryOperation* op) { // Shift the low part __ Lsl(o_l, low, o_l); } else if (op->IsShr()) { - __ and_(o_h, second_reg, ShifterOperand(kMaxLongShiftValue)); + __ and_(o_h, second_reg, ShifterOperand(kMaxLongShiftDistance)); // Shift the low part __ Lsr(o_l, low, o_h); // Shift the high part and `or` what underflew on the low part @@ -3445,7 +3438,7 @@ void InstructionCodeGeneratorARM::HandleShift(HBinaryOperation* op) { // Shift the high part __ Asr(o_h, high, o_h); } else { - __ and_(o_h, second_reg, ShifterOperand(kMaxLongShiftValue)); + __ and_(o_h, second_reg, ShifterOperand(kMaxLongShiftDistance)); // same as Shr except we use `Lsr`s and not `Asr`s __ Lsr(o_l, low, o_h); __ rsb(temp, o_h, ShifterOperand(kArmBitsPerWord)); @@ -3461,7 +3454,7 @@ void InstructionCodeGeneratorARM::HandleShift(HBinaryOperation* op) { DCHECK_NE(o_l, high); DCHECK_NE(o_h, low); int32_t cst = second.GetConstant()->AsIntConstant()->GetValue(); - uint32_t shift_value = static_cast<uint32_t>(cst & kMaxLongShiftValue); + uint32_t shift_value = cst & kMaxLongShiftDistance; if (shift_value > 32) { if (op->IsShl()) { __ Lsl(o_h, low, shift_value - 32); @@ -3672,6 +3665,10 @@ void LocationsBuilderARM::VisitCompare(HCompare* compare) { LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(compare, LocationSummary::kNoCall); switch (compare->InputAt(0)->GetType()) { + case Primitive::kPrimBoolean: + case Primitive::kPrimByte: + case Primitive::kPrimShort: + case Primitive::kPrimChar: case Primitive::kPrimInt: case Primitive::kPrimLong: { locations->SetInAt(0, Location::RequiresRegister()); @@ -3702,6 +3699,10 @@ void InstructionCodeGeneratorARM::VisitCompare(HCompare* compare) { Primitive::Type type = compare->InputAt(0)->GetType(); Condition less_cond; switch (type) { + case Primitive::kPrimBoolean: + case Primitive::kPrimByte: + case Primitive::kPrimShort: + case Primitive::kPrimChar: case Primitive::kPrimInt: { __ LoadImmediate(out, 0); __ cmp(left.AsRegister<Register>(), @@ -4285,19 +4286,19 @@ void LocationsBuilderARM::VisitNullCheck(HNullCheck* instruction) { } } -void InstructionCodeGeneratorARM::GenerateImplicitNullCheck(HNullCheck* instruction) { - if (codegen_->CanMoveNullCheckToUser(instruction)) { +void CodeGeneratorARM::GenerateImplicitNullCheck(HNullCheck* instruction) { + if (CanMoveNullCheckToUser(instruction)) { return; } Location obj = instruction->GetLocations()->InAt(0); __ LoadFromOffset(kLoadWord, IP, obj.AsRegister<Register>(), 0); - codegen_->RecordPcInfo(instruction, instruction->GetDexPc()); + RecordPcInfo(instruction, instruction->GetDexPc()); } -void InstructionCodeGeneratorARM::GenerateExplicitNullCheck(HNullCheck* instruction) { +void CodeGeneratorARM::GenerateExplicitNullCheck(HNullCheck* instruction) { SlowPathCode* slow_path = new (GetGraph()->GetArena()) NullCheckSlowPathARM(instruction); - codegen_->AddSlowPath(slow_path); + AddSlowPath(slow_path); LocationSummary* locations = instruction->GetLocations(); Location obj = locations->InAt(0); @@ -4306,11 +4307,7 @@ void InstructionCodeGeneratorARM::GenerateExplicitNullCheck(HNullCheck* instruct } void InstructionCodeGeneratorARM::VisitNullCheck(HNullCheck* instruction) { - if (codegen_->IsImplicitNullCheckAllowed(instruction)) { - GenerateImplicitNullCheck(instruction); - } else { - GenerateExplicitNullCheck(instruction); - } + codegen_->GenerateNullCheck(instruction); } void LocationsBuilderARM::VisitArrayGet(HArrayGet* instruction) { diff --git a/compiler/optimizing/code_generator_arm.h b/compiler/optimizing/code_generator_arm.h index 06e7c0015c..cc4aa144c0 100644 --- a/compiler/optimizing/code_generator_arm.h +++ b/compiler/optimizing/code_generator_arm.h @@ -174,7 +174,6 @@ class LocationsBuilderARM : public HGraphVisitor { void HandleCondition(HCondition* condition); void HandleIntegerRotate(LocationSummary* locations); void HandleLongRotate(LocationSummary* locations); - void HandleRotate(HRor* ror); void HandleShift(HBinaryOperation* operation); void HandleFieldSet(HInstruction* instruction, const FieldInfo& field_info); void HandleFieldGet(HInstruction* instruction, const FieldInfo& field_info); @@ -222,7 +221,6 @@ class InstructionCodeGeneratorARM : public InstructionCodeGenerator { void HandleCondition(HCondition* condition); void HandleIntegerRotate(LocationSummary* locations); void HandleLongRotate(LocationSummary* locations); - void HandleRotate(HRor* ror); void HandleShift(HBinaryOperation* operation); void GenerateWideAtomicStore(Register addr, uint32_t offset, @@ -274,9 +272,6 @@ class InstructionCodeGeneratorARM : public InstructionCodeGenerator { Location root, Register obj, uint32_t offset); - - void GenerateImplicitNullCheck(HNullCheck* instruction); - void GenerateExplicitNullCheck(HNullCheck* instruction); void GenerateTestAndBranch(HInstruction* instruction, size_t condition_input_index, Label* true_target, @@ -514,6 +509,9 @@ class CodeGeneratorARM : public CodeGenerator { void GenerateNop(); + void GenerateImplicitNullCheck(HNullCheck* instruction); + void GenerateExplicitNullCheck(HNullCheck* instruction); + private: // Factored implementation of GenerateFieldLoadWithBakerReadBarrier // and GenerateArrayLoadWithBakerReadBarrier. diff --git a/compiler/optimizing/code_generator_arm64.cc b/compiler/optimizing/code_generator_arm64.cc index a04918a601..1f577b38b7 100644 --- a/compiler/optimizing/code_generator_arm64.cc +++ b/compiler/optimizing/code_generator_arm64.cc @@ -1818,9 +1818,8 @@ void InstructionCodeGeneratorARM64::HandleShift(HBinaryOperation* instr) { Register lhs = InputRegisterAt(instr, 0); Operand rhs = InputOperandAt(instr, 1); if (rhs.IsImmediate()) { - uint32_t shift_value = (type == Primitive::kPrimInt) - ? static_cast<uint32_t>(rhs.immediate() & kMaxIntShiftValue) - : static_cast<uint32_t>(rhs.immediate() & kMaxLongShiftValue); + uint32_t shift_value = rhs.immediate() & + (type == Primitive::kPrimInt ? kMaxIntShiftDistance : kMaxLongShiftDistance); if (instr->IsShl()) { __ Lsl(dst, lhs, shift_value); } else if (instr->IsShr()) { @@ -1921,9 +1920,8 @@ void InstructionCodeGeneratorARM64::VisitArm64DataProcWithShifterOp( // conversion) can have a different type from the current instruction's type, // so we manually indicate the type. Register right_reg = RegisterFrom(instruction->GetLocations()->InAt(1), type); - int64_t shift_amount = (type == Primitive::kPrimInt) - ? static_cast<uint32_t>(instruction->GetShiftAmount() & kMaxIntShiftValue) - : static_cast<uint32_t>(instruction->GetShiftAmount() & kMaxLongShiftValue); + int64_t shift_amount = instruction->GetShiftAmount() & + (type == Primitive::kPrimInt ? kMaxIntShiftDistance : kMaxLongShiftDistance); Operand right_operand(0); @@ -2414,6 +2412,10 @@ void LocationsBuilderARM64::VisitCompare(HCompare* compare) { new (GetGraph()->GetArena()) LocationSummary(compare, LocationSummary::kNoCall); Primitive::Type in_type = compare->InputAt(0)->GetType(); switch (in_type) { + case Primitive::kPrimBoolean: + case Primitive::kPrimByte: + case Primitive::kPrimShort: + case Primitive::kPrimChar: case Primitive::kPrimInt: case Primitive::kPrimLong: { locations->SetInAt(0, Location::RequiresRegister()); @@ -2443,6 +2445,10 @@ void InstructionCodeGeneratorARM64::VisitCompare(HCompare* compare) { // 1 if: left > right // -1 if: left < right switch (in_type) { + case Primitive::kPrimBoolean: + case Primitive::kPrimByte: + case Primitive::kPrimShort: + case Primitive::kPrimChar: case Primitive::kPrimInt: case Primitive::kPrimLong: { Register result = OutputRegister(compare); @@ -3083,8 +3089,8 @@ void LocationsBuilderARM64::VisitNativeDebugInfo(HNativeDebugInfo* info) { new (GetGraph()->GetArena()) LocationSummary(info); } -void InstructionCodeGeneratorARM64::VisitNativeDebugInfo(HNativeDebugInfo* info) { - codegen_->MaybeRecordNativeDebugInfo(info, info->GetDexPc()); +void InstructionCodeGeneratorARM64::VisitNativeDebugInfo(HNativeDebugInfo*) { + // MaybeRecordNativeDebugInfo is already called implicitly in CodeGenerator::Compile. } void CodeGeneratorARM64::GenerateNop() { @@ -4192,20 +4198,20 @@ void LocationsBuilderARM64::VisitNullCheck(HNullCheck* instruction) { } } -void InstructionCodeGeneratorARM64::GenerateImplicitNullCheck(HNullCheck* instruction) { - if (codegen_->CanMoveNullCheckToUser(instruction)) { +void CodeGeneratorARM64::GenerateImplicitNullCheck(HNullCheck* instruction) { + if (CanMoveNullCheckToUser(instruction)) { return; } BlockPoolsScope block_pools(GetVIXLAssembler()); Location obj = instruction->GetLocations()->InAt(0); __ Ldr(wzr, HeapOperandFrom(obj, Offset(0))); - codegen_->RecordPcInfo(instruction, instruction->GetDexPc()); + RecordPcInfo(instruction, instruction->GetDexPc()); } -void InstructionCodeGeneratorARM64::GenerateExplicitNullCheck(HNullCheck* instruction) { +void CodeGeneratorARM64::GenerateExplicitNullCheck(HNullCheck* instruction) { SlowPathCodeARM64* slow_path = new (GetGraph()->GetArena()) NullCheckSlowPathARM64(instruction); - codegen_->AddSlowPath(slow_path); + AddSlowPath(slow_path); LocationSummary* locations = instruction->GetLocations(); Location obj = locations->InAt(0); @@ -4214,11 +4220,7 @@ void InstructionCodeGeneratorARM64::GenerateExplicitNullCheck(HNullCheck* instru } void InstructionCodeGeneratorARM64::VisitNullCheck(HNullCheck* instruction) { - if (codegen_->IsImplicitNullCheckAllowed(instruction)) { - GenerateImplicitNullCheck(instruction); - } else { - GenerateExplicitNullCheck(instruction); - } + codegen_->GenerateNullCheck(instruction); } void LocationsBuilderARM64::VisitOr(HOr* instruction) { diff --git a/compiler/optimizing/code_generator_arm64.h b/compiler/optimizing/code_generator_arm64.h index 0af825bccd..cf9dc1bdc1 100644 --- a/compiler/optimizing/code_generator_arm64.h +++ b/compiler/optimizing/code_generator_arm64.h @@ -261,8 +261,6 @@ class InstructionCodeGeneratorARM64 : public InstructionCodeGenerator { void GenerateFcmp(HInstruction* instruction); void HandleShift(HBinaryOperation* instr); - void GenerateImplicitNullCheck(HNullCheck* instruction); - void GenerateExplicitNullCheck(HNullCheck* instruction); void GenerateTestAndBranch(HInstruction* instruction, size_t condition_input_index, vixl::Label* true_target, @@ -540,6 +538,9 @@ class CodeGeneratorARM64 : public CodeGenerator { void GenerateNop(); + void GenerateImplicitNullCheck(HNullCheck* instruction); + void GenerateExplicitNullCheck(HNullCheck* instruction); + private: // Factored implementation of GenerateFieldLoadWithBakerReadBarrier // and GenerateArrayLoadWithBakerReadBarrier. diff --git a/compiler/optimizing/code_generator_mips.cc b/compiler/optimizing/code_generator_mips.cc index 4b2a461131..a29d8394db 100644 --- a/compiler/optimizing/code_generator_mips.cc +++ b/compiler/optimizing/code_generator_mips.cc @@ -1455,9 +1455,8 @@ void InstructionCodeGeneratorMIPS::HandleShift(HBinaryOperation* instr) { bool use_imm = rhs_location.IsConstant(); Register rhs_reg = use_imm ? ZERO : rhs_location.AsRegister<Register>(); int64_t rhs_imm = use_imm ? CodeGenerator::GetInt64ValueOf(rhs_location.GetConstant()) : 0; - const uint32_t shift_mask = (type == Primitive::kPrimInt) - ? kMaxIntShiftValue - : kMaxLongShiftValue; + const uint32_t shift_mask = + (type == Primitive::kPrimInt) ? kMaxIntShiftDistance : kMaxLongShiftDistance; const uint32_t shift_value = rhs_imm & shift_mask; // Are the INS (Insert Bit Field) and ROTR instructions supported? bool has_ins_rotr = codegen_->GetInstructionSetFeatures().IsMipsIsaRevGreaterThanEqual2(); @@ -2070,6 +2069,10 @@ void LocationsBuilderMIPS::VisitCompare(HCompare* compare) { new (GetGraph()->GetArena()) LocationSummary(compare, LocationSummary::kNoCall); switch (in_type) { + case Primitive::kPrimBoolean: + case Primitive::kPrimByte: + case Primitive::kPrimShort: + case Primitive::kPrimChar: case Primitive::kPrimInt: case Primitive::kPrimLong: locations->SetInAt(0, Location::RequiresRegister()); @@ -2100,6 +2103,10 @@ void InstructionCodeGeneratorMIPS::VisitCompare(HCompare* instruction) { // 1 if: left > right // -1 if: left < right switch (in_type) { + case Primitive::kPrimBoolean: + case Primitive::kPrimByte: + case Primitive::kPrimShort: + case Primitive::kPrimChar: case Primitive::kPrimInt: { Register lhs = locations->InAt(0).AsRegister<Register>(); Register rhs = locations->InAt(1).AsRegister<Register>(); @@ -3394,8 +3401,8 @@ void LocationsBuilderMIPS::VisitNativeDebugInfo(HNativeDebugInfo* info) { new (GetGraph()->GetArena()) LocationSummary(info); } -void InstructionCodeGeneratorMIPS::VisitNativeDebugInfo(HNativeDebugInfo* info) { - codegen_->MaybeRecordNativeDebugInfo(info, info->GetDexPc()); +void InstructionCodeGeneratorMIPS::VisitNativeDebugInfo(HNativeDebugInfo*) { + // MaybeRecordNativeDebugInfo is already called implicitly in CodeGenerator::Compile. } void CodeGeneratorMIPS::GenerateNop() { @@ -4396,19 +4403,19 @@ void LocationsBuilderMIPS::VisitNullCheck(HNullCheck* instruction) { } } -void InstructionCodeGeneratorMIPS::GenerateImplicitNullCheck(HNullCheck* instruction) { - if (codegen_->CanMoveNullCheckToUser(instruction)) { +void CodeGeneratorMIPS::GenerateImplicitNullCheck(HNullCheck* instruction) { + if (CanMoveNullCheckToUser(instruction)) { return; } Location obj = instruction->GetLocations()->InAt(0); __ Lw(ZERO, obj.AsRegister<Register>(), 0); - codegen_->RecordPcInfo(instruction, instruction->GetDexPc()); + RecordPcInfo(instruction, instruction->GetDexPc()); } -void InstructionCodeGeneratorMIPS::GenerateExplicitNullCheck(HNullCheck* instruction) { +void CodeGeneratorMIPS::GenerateExplicitNullCheck(HNullCheck* instruction) { SlowPathCodeMIPS* slow_path = new (GetGraph()->GetArena()) NullCheckSlowPathMIPS(instruction); - codegen_->AddSlowPath(slow_path); + AddSlowPath(slow_path); Location obj = instruction->GetLocations()->InAt(0); @@ -4416,11 +4423,7 @@ void InstructionCodeGeneratorMIPS::GenerateExplicitNullCheck(HNullCheck* instruc } void InstructionCodeGeneratorMIPS::VisitNullCheck(HNullCheck* instruction) { - if (codegen_->IsImplicitNullCheckAllowed(instruction)) { - GenerateImplicitNullCheck(instruction); - } else { - GenerateExplicitNullCheck(instruction); - } + codegen_->GenerateNullCheck(instruction); } void LocationsBuilderMIPS::VisitOr(HOr* instruction) { diff --git a/compiler/optimizing/code_generator_mips.h b/compiler/optimizing/code_generator_mips.h index 605c794421..b720573897 100644 --- a/compiler/optimizing/code_generator_mips.h +++ b/compiler/optimizing/code_generator_mips.h @@ -226,8 +226,6 @@ class InstructionCodeGeneratorMIPS : public InstructionCodeGenerator { void HandleShift(HBinaryOperation* operation); void HandleFieldSet(HInstruction* instruction, const FieldInfo& field_info, uint32_t dex_pc); void HandleFieldGet(HInstruction* instruction, const FieldInfo& field_info, uint32_t dex_pc); - void GenerateImplicitNullCheck(HNullCheck* instruction); - void GenerateExplicitNullCheck(HNullCheck* instruction); void GenerateIntCompare(IfCondition cond, LocationSummary* locations); void GenerateIntCompareAndBranch(IfCondition cond, LocationSummary* locations, @@ -362,6 +360,8 @@ class CodeGeneratorMIPS : public CodeGenerator { } void GenerateNop(); + void GenerateImplicitNullCheck(HNullCheck* instruction); + void GenerateExplicitNullCheck(HNullCheck* instruction); private: // Labels for each block that will be compiled. diff --git a/compiler/optimizing/code_generator_mips64.cc b/compiler/optimizing/code_generator_mips64.cc index 9c8c4660f2..72ef49966e 100644 --- a/compiler/optimizing/code_generator_mips64.cc +++ b/compiler/optimizing/code_generator_mips64.cc @@ -1208,9 +1208,8 @@ void InstructionCodeGeneratorMIPS64::HandleShift(HBinaryOperation* instr) { } if (use_imm) { - uint32_t shift_value = (type == Primitive::kPrimInt) - ? static_cast<uint32_t>(rhs_imm & kMaxIntShiftValue) - : static_cast<uint32_t>(rhs_imm & kMaxLongShiftValue); + uint32_t shift_value = rhs_imm & + (type == Primitive::kPrimInt ? kMaxIntShiftDistance : kMaxLongShiftDistance); if (shift_value == 0) { if (dst != lhs) { @@ -1691,6 +1690,10 @@ void LocationsBuilderMIPS64::VisitCompare(HCompare* compare) { LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(compare); switch (in_type) { + case Primitive::kPrimBoolean: + case Primitive::kPrimByte: + case Primitive::kPrimShort: + case Primitive::kPrimChar: case Primitive::kPrimInt: case Primitive::kPrimLong: locations->SetInAt(0, Location::RequiresRegister()); @@ -1719,6 +1722,10 @@ void InstructionCodeGeneratorMIPS64::VisitCompare(HCompare* instruction) { // 1 if: left > right // -1 if: left < right switch (in_type) { + case Primitive::kPrimBoolean: + case Primitive::kPrimByte: + case Primitive::kPrimShort: + case Primitive::kPrimChar: case Primitive::kPrimInt: case Primitive::kPrimLong: { GpuRegister lhs = locations->InAt(0).AsRegister<GpuRegister>(); @@ -1726,17 +1733,17 @@ void InstructionCodeGeneratorMIPS64::VisitCompare(HCompare* instruction) { bool use_imm = rhs_location.IsConstant(); GpuRegister rhs = ZERO; if (use_imm) { - if (in_type == Primitive::kPrimInt) { - int32_t value = CodeGenerator::GetInt32ValueOf(rhs_location.GetConstant()->AsConstant()); + if (in_type == Primitive::kPrimLong) { + int64_t value = CodeGenerator::GetInt64ValueOf(rhs_location.GetConstant()->AsConstant()); if (value != 0) { rhs = AT; - __ LoadConst32(rhs, value); + __ LoadConst64(rhs, value); } } else { - int64_t value = CodeGenerator::GetInt64ValueOf(rhs_location.GetConstant()->AsConstant()); + int32_t value = CodeGenerator::GetInt32ValueOf(rhs_location.GetConstant()->AsConstant()); if (value != 0) { rhs = AT; - __ LoadConst64(rhs, value); + __ LoadConst32(rhs, value); } } } else { @@ -2718,8 +2725,8 @@ void LocationsBuilderMIPS64::VisitNativeDebugInfo(HNativeDebugInfo* info) { new (GetGraph()->GetArena()) LocationSummary(info); } -void InstructionCodeGeneratorMIPS64::VisitNativeDebugInfo(HNativeDebugInfo* info) { - codegen_->MaybeRecordNativeDebugInfo(info, info->GetDexPc()); +void InstructionCodeGeneratorMIPS64::VisitNativeDebugInfo(HNativeDebugInfo*) { + // MaybeRecordNativeDebugInfo is already called implicitly in CodeGenerator::Compile. } void CodeGeneratorMIPS64::GenerateNop() { @@ -3550,19 +3557,19 @@ void LocationsBuilderMIPS64::VisitNullCheck(HNullCheck* instruction) { } } -void InstructionCodeGeneratorMIPS64::GenerateImplicitNullCheck(HNullCheck* instruction) { - if (codegen_->CanMoveNullCheckToUser(instruction)) { +void CodeGeneratorMIPS64::GenerateImplicitNullCheck(HNullCheck* instruction) { + if (CanMoveNullCheckToUser(instruction)) { return; } Location obj = instruction->GetLocations()->InAt(0); __ Lw(ZERO, obj.AsRegister<GpuRegister>(), 0); - codegen_->RecordPcInfo(instruction, instruction->GetDexPc()); + RecordPcInfo(instruction, instruction->GetDexPc()); } -void InstructionCodeGeneratorMIPS64::GenerateExplicitNullCheck(HNullCheck* instruction) { +void CodeGeneratorMIPS64::GenerateExplicitNullCheck(HNullCheck* instruction) { SlowPathCodeMIPS64* slow_path = new (GetGraph()->GetArena()) NullCheckSlowPathMIPS64(instruction); - codegen_->AddSlowPath(slow_path); + AddSlowPath(slow_path); Location obj = instruction->GetLocations()->InAt(0); @@ -3570,11 +3577,7 @@ void InstructionCodeGeneratorMIPS64::GenerateExplicitNullCheck(HNullCheck* instr } void InstructionCodeGeneratorMIPS64::VisitNullCheck(HNullCheck* instruction) { - if (codegen_->IsImplicitNullCheckAllowed(instruction)) { - GenerateImplicitNullCheck(instruction); - } else { - GenerateExplicitNullCheck(instruction); - } + codegen_->GenerateNullCheck(instruction); } void LocationsBuilderMIPS64::VisitOr(HOr* instruction) { diff --git a/compiler/optimizing/code_generator_mips64.h b/compiler/optimizing/code_generator_mips64.h index ba9eaff46f..9464a140ad 100644 --- a/compiler/optimizing/code_generator_mips64.h +++ b/compiler/optimizing/code_generator_mips64.h @@ -228,8 +228,6 @@ class InstructionCodeGeneratorMIPS64 : public InstructionCodeGenerator { const FieldInfo& field_info, bool value_can_be_null); void HandleFieldGet(HInstruction* instruction, const FieldInfo& field_info); - void GenerateImplicitNullCheck(HNullCheck* instruction); - void GenerateExplicitNullCheck(HNullCheck* instruction); void GenerateTestAndBranch(HInstruction* instruction, size_t condition_input_index, Mips64Label* true_target, @@ -354,6 +352,8 @@ class CodeGeneratorMIPS64 : public CodeGenerator { } void GenerateNop(); + void GenerateImplicitNullCheck(HNullCheck* instruction); + void GenerateExplicitNullCheck(HNullCheck* instruction); private: // Labels for each block that will be compiled. diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc index 33a977b66e..394f4eef68 100644 --- a/compiler/optimizing/code_generator_x86.cc +++ b/compiler/optimizing/code_generator_x86.cc @@ -1636,8 +1636,8 @@ void LocationsBuilderX86::VisitNativeDebugInfo(HNativeDebugInfo* info) { new (GetGraph()->GetArena()) LocationSummary(info); } -void InstructionCodeGeneratorX86::VisitNativeDebugInfo(HNativeDebugInfo* info) { - codegen_->MaybeRecordNativeDebugInfo(info, info->GetDexPc()); +void InstructionCodeGeneratorX86::VisitNativeDebugInfo(HNativeDebugInfo*) { + // MaybeRecordNativeDebugInfo is already called implicitly in CodeGenerator::Compile. } void CodeGeneratorX86::GenerateNop() { @@ -3775,7 +3775,7 @@ void InstructionCodeGeneratorX86::HandleShift(HBinaryOperation* op) { __ shrl(first_reg, second_reg); } } else { - int32_t shift = second.GetConstant()->AsIntConstant()->GetValue() & kMaxIntShiftValue; + int32_t shift = second.GetConstant()->AsIntConstant()->GetValue() & kMaxIntShiftDistance; if (shift == 0) { return; } @@ -3803,7 +3803,7 @@ void InstructionCodeGeneratorX86::HandleShift(HBinaryOperation* op) { } } else { // Shift by a constant. - int shift = second.GetConstant()->AsIntConstant()->GetValue() & kMaxLongShiftValue; + int32_t shift = second.GetConstant()->AsIntConstant()->GetValue() & kMaxLongShiftDistance; // Nothing to do if the shift is 0, as the input is already the output. if (shift != 0) { if (op->IsShl()) { @@ -3960,7 +3960,7 @@ void InstructionCodeGeneratorX86::VisitRor(HRor* ror) { Register second_reg = second.AsRegister<Register>(); __ rorl(first_reg, second_reg); } else { - Immediate imm(second.GetConstant()->AsIntConstant()->GetValue() & kMaxIntShiftValue); + Immediate imm(second.GetConstant()->AsIntConstant()->GetValue() & kMaxIntShiftDistance); __ rorl(first_reg, imm); } return; @@ -3981,8 +3981,7 @@ void InstructionCodeGeneratorX86::VisitRor(HRor* ror) { __ cmovl(kNotEqual, first_reg_hi, first_reg_lo); __ cmovl(kNotEqual, first_reg_lo, temp_reg); } else { - int32_t shift_amt = - CodeGenerator::GetInt64ValueOf(second.GetConstant()) & kMaxLongShiftValue; + int32_t shift_amt = second.GetConstant()->AsIntConstant()->GetValue() & kMaxLongShiftDistance; if (shift_amt == 0) { // Already fine. return; @@ -4186,6 +4185,10 @@ void LocationsBuilderX86::VisitCompare(HCompare* compare) { LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(compare, LocationSummary::kNoCall); switch (compare->InputAt(0)->GetType()) { + case Primitive::kPrimBoolean: + case Primitive::kPrimByte: + case Primitive::kPrimShort: + case Primitive::kPrimChar: case Primitive::kPrimInt: case Primitive::kPrimLong: { locations->SetInAt(0, Location::RequiresRegister()); @@ -4221,6 +4224,10 @@ void InstructionCodeGeneratorX86::VisitCompare(HCompare* compare) { Condition less_cond = kLess; switch (compare->InputAt(0)->GetType()) { + case Primitive::kPrimBoolean: + case Primitive::kPrimByte: + case Primitive::kPrimShort: + case Primitive::kPrimChar: case Primitive::kPrimInt: { GenerateIntCompare(left, right); break; @@ -4788,6 +4795,7 @@ void InstructionCodeGeneratorX86::HandleFieldSet(HInstruction* instruction, int32_t v = CodeGenerator::GetInt32ValueOf(value.GetConstant()); __ movl(Address(base, offset), Immediate(v)); } else { + DCHECK(value.IsRegister()) << value; __ movl(Address(base, offset), value.AsRegister<Register>()); } break; @@ -4973,20 +4981,20 @@ void LocationsBuilderX86::VisitNullCheck(HNullCheck* instruction) { } } -void InstructionCodeGeneratorX86::GenerateImplicitNullCheck(HNullCheck* instruction) { - if (codegen_->CanMoveNullCheckToUser(instruction)) { +void CodeGeneratorX86::GenerateImplicitNullCheck(HNullCheck* instruction) { + if (CanMoveNullCheckToUser(instruction)) { return; } LocationSummary* locations = instruction->GetLocations(); Location obj = locations->InAt(0); __ testl(EAX, Address(obj.AsRegister<Register>(), 0)); - codegen_->RecordPcInfo(instruction, instruction->GetDexPc()); + RecordPcInfo(instruction, instruction->GetDexPc()); } -void InstructionCodeGeneratorX86::GenerateExplicitNullCheck(HNullCheck* instruction) { +void CodeGeneratorX86::GenerateExplicitNullCheck(HNullCheck* instruction) { SlowPathCode* slow_path = new (GetGraph()->GetArena()) NullCheckSlowPathX86(instruction); - codegen_->AddSlowPath(slow_path); + AddSlowPath(slow_path); LocationSummary* locations = instruction->GetLocations(); Location obj = locations->InAt(0); @@ -5005,11 +5013,7 @@ void InstructionCodeGeneratorX86::GenerateExplicitNullCheck(HNullCheck* instruct } void InstructionCodeGeneratorX86::VisitNullCheck(HNullCheck* instruction) { - if (codegen_->IsImplicitNullCheckAllowed(instruction)) { - GenerateImplicitNullCheck(instruction); - } else { - GenerateExplicitNullCheck(instruction); - } + codegen_->GenerateNullCheck(instruction); } void LocationsBuilderX86::VisitArrayGet(HArrayGet* instruction) { diff --git a/compiler/optimizing/code_generator_x86.h b/compiler/optimizing/code_generator_x86.h index 0795f3b530..c397899892 100644 --- a/compiler/optimizing/code_generator_x86.h +++ b/compiler/optimizing/code_generator_x86.h @@ -271,8 +271,6 @@ class InstructionCodeGeneratorX86 : public InstructionCodeGenerator { void PushOntoFPStack(Location source, uint32_t temp_offset, uint32_t stack_adjustment, bool is_fp, bool is_wide); - void GenerateImplicitNullCheck(HNullCheck* instruction); - void GenerateExplicitNullCheck(HNullCheck* instruction); template<class LabelType> void GenerateTestAndBranch(HInstruction* instruction, size_t condition_input_index, @@ -541,6 +539,8 @@ class CodeGeneratorX86 : public CodeGenerator { } void GenerateNop(); + void GenerateImplicitNullCheck(HNullCheck* instruction); + void GenerateExplicitNullCheck(HNullCheck* instruction); private: // Factored implementation of GenerateFieldLoadWithBakerReadBarrier diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc index 3d1fe7fdc6..d24b5bbd56 100644 --- a/compiler/optimizing/code_generator_x86_64.cc +++ b/compiler/optimizing/code_generator_x86_64.cc @@ -1617,8 +1617,8 @@ void LocationsBuilderX86_64::VisitNativeDebugInfo(HNativeDebugInfo* info) { new (GetGraph()->GetArena()) LocationSummary(info); } -void InstructionCodeGeneratorX86_64::VisitNativeDebugInfo(HNativeDebugInfo* info) { - codegen_->MaybeRecordNativeDebugInfo(info, info->GetDexPc()); +void InstructionCodeGeneratorX86_64::VisitNativeDebugInfo(HNativeDebugInfo*) { + // MaybeRecordNativeDebugInfo is already called implicitly in CodeGenerator::Compile. } void CodeGeneratorX86_64::GenerateNop() { @@ -1860,6 +1860,10 @@ void LocationsBuilderX86_64::VisitCompare(HCompare* compare) { LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(compare, LocationSummary::kNoCall); switch (compare->InputAt(0)->GetType()) { + case Primitive::kPrimBoolean: + case Primitive::kPrimByte: + case Primitive::kPrimShort: + case Primitive::kPrimChar: case Primitive::kPrimInt: case Primitive::kPrimLong: { locations->SetInAt(0, Location::RequiresRegister()); @@ -1890,6 +1894,10 @@ void InstructionCodeGeneratorX86_64::VisitCompare(HCompare* compare) { Condition less_cond = kLess; switch (type) { + case Primitive::kPrimBoolean: + case Primitive::kPrimByte: + case Primitive::kPrimShort: + case Primitive::kPrimChar: case Primitive::kPrimInt: { CpuRegister left_reg = left.AsRegister<CpuRegister>(); if (right.IsConstant()) { @@ -3791,7 +3799,7 @@ void InstructionCodeGeneratorX86_64::HandleShift(HBinaryOperation* op) { __ shrl(first_reg, second_reg); } } else { - Immediate imm(second.GetConstant()->AsIntConstant()->GetValue() & kMaxIntShiftValue); + Immediate imm(second.GetConstant()->AsIntConstant()->GetValue() & kMaxIntShiftDistance); if (op->IsShl()) { __ shll(first_reg, imm); } else if (op->IsShr()) { @@ -3813,7 +3821,7 @@ void InstructionCodeGeneratorX86_64::HandleShift(HBinaryOperation* op) { __ shrq(first_reg, second_reg); } } else { - Immediate imm(second.GetConstant()->AsIntConstant()->GetValue() & kMaxLongShiftValue); + Immediate imm(second.GetConstant()->AsIntConstant()->GetValue() & kMaxLongShiftDistance); if (op->IsShl()) { __ shlq(first_reg, imm); } else if (op->IsShr()) { @@ -3860,7 +3868,7 @@ void InstructionCodeGeneratorX86_64::VisitRor(HRor* ror) { CpuRegister second_reg = second.AsRegister<CpuRegister>(); __ rorl(first_reg, second_reg); } else { - Immediate imm(second.GetConstant()->AsIntConstant()->GetValue() & kMaxIntShiftValue); + Immediate imm(second.GetConstant()->AsIntConstant()->GetValue() & kMaxIntShiftDistance); __ rorl(first_reg, imm); } break; @@ -3869,7 +3877,7 @@ void InstructionCodeGeneratorX86_64::VisitRor(HRor* ror) { CpuRegister second_reg = second.AsRegister<CpuRegister>(); __ rorq(first_reg, second_reg); } else { - Immediate imm(second.GetConstant()->AsIntConstant()->GetValue() & kMaxLongShiftValue); + Immediate imm(second.GetConstant()->AsIntConstant()->GetValue() & kMaxLongShiftDistance); __ rorq(first_reg, imm); } break; @@ -4488,20 +4496,20 @@ void LocationsBuilderX86_64::VisitNullCheck(HNullCheck* instruction) { } } -void InstructionCodeGeneratorX86_64::GenerateImplicitNullCheck(HNullCheck* instruction) { - if (codegen_->CanMoveNullCheckToUser(instruction)) { +void CodeGeneratorX86_64::GenerateImplicitNullCheck(HNullCheck* instruction) { + if (CanMoveNullCheckToUser(instruction)) { return; } LocationSummary* locations = instruction->GetLocations(); Location obj = locations->InAt(0); __ testl(CpuRegister(RAX), Address(obj.AsRegister<CpuRegister>(), 0)); - codegen_->RecordPcInfo(instruction, instruction->GetDexPc()); + RecordPcInfo(instruction, instruction->GetDexPc()); } -void InstructionCodeGeneratorX86_64::GenerateExplicitNullCheck(HNullCheck* instruction) { +void CodeGeneratorX86_64::GenerateExplicitNullCheck(HNullCheck* instruction) { SlowPathCode* slow_path = new (GetGraph()->GetArena()) NullCheckSlowPathX86_64(instruction); - codegen_->AddSlowPath(slow_path); + AddSlowPath(slow_path); LocationSummary* locations = instruction->GetLocations(); Location obj = locations->InAt(0); @@ -4520,11 +4528,7 @@ void InstructionCodeGeneratorX86_64::GenerateExplicitNullCheck(HNullCheck* instr } void InstructionCodeGeneratorX86_64::VisitNullCheck(HNullCheck* instruction) { - if (codegen_->IsImplicitNullCheckAllowed(instruction)) { - GenerateImplicitNullCheck(instruction); - } else { - GenerateExplicitNullCheck(instruction); - } + codegen_->GenerateNullCheck(instruction); } void LocationsBuilderX86_64::VisitArrayGet(HArrayGet* instruction) { diff --git a/compiler/optimizing/code_generator_x86_64.h b/compiler/optimizing/code_generator_x86_64.h index b3d27e194a..c3fce6e824 100644 --- a/compiler/optimizing/code_generator_x86_64.h +++ b/compiler/optimizing/code_generator_x86_64.h @@ -260,8 +260,6 @@ class InstructionCodeGeneratorX86_64 : public InstructionCodeGenerator { CpuRegister obj, uint32_t offset); - void GenerateImplicitNullCheck(HNullCheck* instruction); - void GenerateExplicitNullCheck(HNullCheck* instruction); void PushOntoFPStack(Location source, uint32_t temp_offset, uint32_t stack_adjustment, bool is_float); void GenerateCompareTest(HCondition* condition); @@ -514,6 +512,8 @@ class CodeGeneratorX86_64 : public CodeGenerator { } void GenerateNop(); + void GenerateImplicitNullCheck(HNullCheck* instruction); + void GenerateExplicitNullCheck(HNullCheck* instruction); private: // Factored implementation of GenerateFieldLoadWithBakerReadBarrier diff --git a/compiler/optimizing/common_arm64.h b/compiler/optimizing/common_arm64.h index 10d83439fd..6c551945e9 100644 --- a/compiler/optimizing/common_arm64.h +++ b/compiler/optimizing/common_arm64.h @@ -194,7 +194,8 @@ static inline vixl::Operand OperandFromMemOperand(const vixl::MemOperand& mem_op } static bool CanEncodeConstantAsImmediate(HConstant* constant, HInstruction* instr) { - DCHECK(constant->IsIntConstant() || constant->IsLongConstant() || constant->IsNullConstant()); + DCHECK(constant->IsIntConstant() || constant->IsLongConstant() || constant->IsNullConstant()) + << constant->DebugName(); // For single uses we let VIXL handle the constant generation since it will // use registers that are not managed by the register allocator (wip0, wip1). @@ -221,7 +222,8 @@ static bool CanEncodeConstantAsImmediate(HConstant* constant, HInstruction* inst instr->IsBoundsCheck() || instr->IsCompare() || instr->IsCondition() || - instr->IsSub()); + instr->IsSub()) + << instr->DebugName(); // Uses aliases of ADD/SUB instructions. // If `value` does not fit but `-value` does, VIXL will automatically use // the 'opposite' instruction. diff --git a/compiler/optimizing/constant_folding_test.cc b/compiler/optimizing/constant_folding_test.cc index 9c69f8c75b..1e54a0adfa 100644 --- a/compiler/optimizing/constant_folding_test.cc +++ b/compiler/optimizing/constant_folding_test.cc @@ -598,42 +598,41 @@ TEST_F(ConstantFoldingTest, IntConstantFoldingAndJumps) { " 5: IntConstant [9]\n" // v1 <- 2 " 13: IntConstant [14]\n" // const 5 " 18: IntConstant [19]\n" // const 4 - " 24: IntConstant [25]\n" // const 8 - " 30: SuspendCheck\n" - " 31: Goto 1\n" + " 23: IntConstant [24]\n" // const 8 + " 29: SuspendCheck\n" + " 30: Goto 1\n" "BasicBlock 1, pred: 0, succ: 3\n" " 9: Add(3, 5) [19]\n" // v2 <- v0 + v1 = 1 + 2 = 3 " 11: Goto 3\n" // goto L2 "BasicBlock 2, pred: 3, succ: 4\n" // L1: - " 14: Add(19, 13) [25]\n" // v1 <- v0 + 3 = 7 + 5 = 12 + " 14: Add(19, 13) [24]\n" // v1 <- v0 + 3 = 7 + 5 = 12 " 16: Goto 4\n" // goto L3 "BasicBlock 3, pred: 1, succ: 2\n" // L2: " 19: Add(9, 18) [14]\n" // v0 <- v2 + 2 = 3 + 4 = 7 - " 21: SuspendCheck\n" - " 22: Goto 2\n" // goto L1 + " 21: Goto 2\n" // goto L1 "BasicBlock 4, pred: 2, succ: 5\n" // L3: - " 25: Add(14, 24) [28]\n" // v2 <- v1 + 4 = 12 + 8 = 20 - " 28: Return(25)\n" // return v2 + " 24: Add(14, 23) [27]\n" // v2 <- v1 + 4 = 12 + 8 = 20 + " 27: Return(24)\n" // return v2 "BasicBlock 5, pred: 4\n" - " 29: Exit\n"; + " 28: Exit\n"; // Expected difference after constant folding. diff_t expected_cf_diff = { { " 3: IntConstant [9]\n", " 3: IntConstant\n" }, - { " 5: IntConstant [9]\n", " 5: IntConstant []\n" }, + { " 5: IntConstant [9]\n", " 5: IntConstant\n" }, { " 13: IntConstant [14]\n", " 13: IntConstant\n" }, { " 18: IntConstant [19]\n", " 18: IntConstant\n" }, - { " 24: IntConstant [25]\n", " 24: IntConstant\n" }, - { " 30: SuspendCheck\n", " 30: SuspendCheck\n" - " 32: IntConstant []\n" - " 33: IntConstant []\n" - " 34: IntConstant\n" - " 35: IntConstant [28]\n" }, + { " 23: IntConstant [24]\n", " 23: IntConstant\n" }, + { " 29: SuspendCheck\n", " 29: SuspendCheck\n" + " 31: IntConstant\n" + " 32: IntConstant\n" + " 33: IntConstant\n" + " 34: IntConstant [27]\n" }, { " 9: Add(3, 5) [19]\n", removed }, - { " 14: Add(19, 13) [25]\n", removed }, + { " 14: Add(19, 13) [24]\n", removed }, { " 19: Add(9, 18) [14]\n", removed }, - { " 25: Add(14, 24) [28]\n", removed }, - { " 28: Return(25)\n", " 28: Return(35)\n"} + { " 24: Add(14, 23) [27]\n", removed }, + { " 27: Return(24)\n", " 27: Return(34)\n"} }; std::string expected_after_cf = Patch(expected_before, expected_cf_diff); @@ -656,17 +655,13 @@ TEST_F(ConstantFoldingTest, IntConstantFoldingAndJumps) { // Expected difference after dead code elimination. std::string expected_after_dce = "BasicBlock 0, succ: 1\n" - " 5: IntConstant []\n" - " 30: SuspendCheck\n" - " 32: IntConstant []\n" - " 33: IntConstant []\n" - " 35: IntConstant [28]\n" - " 31: Goto 1\n" + " 29: SuspendCheck\n" + " 34: IntConstant [27]\n" + " 30: Goto 1\n" "BasicBlock 1, pred: 0, succ: 5\n" - " 21: SuspendCheck\n" - " 28: Return(35)\n" + " 27: Return(34)\n" "BasicBlock 5, pred: 1\n" - " 29: Exit\n"; + " 28: Exit\n"; TestCode(data, expected_before, diff --git a/compiler/optimizing/dead_code_elimination.cc b/compiler/optimizing/dead_code_elimination.cc index 5b41736e74..5f11024996 100644 --- a/compiler/optimizing/dead_code_elimination.cc +++ b/compiler/optimizing/dead_code_elimination.cc @@ -97,7 +97,7 @@ void HDeadCodeElimination::RemoveDeadBlocks() { } // Classify blocks as reachable/unreachable. ArenaAllocator* allocator = graph_->GetArena(); - ArenaBitVector live_blocks(allocator, graph_->GetBlocks().size(), false); + ArenaBitVector live_blocks(allocator, graph_->GetBlocks().size(), false, kArenaAllocDCE); MarkReachableBlocks(graph_, &live_blocks); bool removed_one_or_more_blocks = false; diff --git a/compiler/optimizing/dead_code_elimination_test.cc b/compiler/optimizing/dead_code_elimination_test.cc index 930795b4f6..83e724ba29 100644 --- a/compiler/optimizing/dead_code_elimination_test.cc +++ b/compiler/optimizing/dead_code_elimination_test.cc @@ -149,44 +149,32 @@ TEST_F(DeadCodeEliminationTest, AdditionsAndInconditionalJumps) { " 5: IntConstant [9]\n" " 13: IntConstant [14]\n" " 18: IntConstant [19]\n" - " 24: IntConstant [25]\n" - " 29: SuspendCheck\n" - " 30: Goto 1\n" + " 23: IntConstant [24]\n" + " 28: SuspendCheck\n" + " 29: Goto 1\n" "BasicBlock 1, pred: 0, succ: 3\n" " 9: Add(3, 5) [19]\n" " 11: Goto 3\n" "BasicBlock 2, pred: 3, succ: 4\n" - " 14: Add(19, 13) [25]\n" + " 14: Add(19, 13) [24]\n" " 16: Goto 4\n" "BasicBlock 3, pred: 1, succ: 2\n" " 19: Add(9, 18) [14]\n" - " 21: SuspendCheck\n" - " 22: Goto 2\n" + " 21: Goto 2\n" "BasicBlock 4, pred: 2, succ: 5\n" - " 25: Add(14, 24)\n" - " 27: ReturnVoid\n" + " 24: Add(14, 23)\n" + " 26: ReturnVoid\n" "BasicBlock 5, pred: 4\n" - " 28: Exit\n"; + " 27: Exit\n"; - // The SuspendCheck instruction following this Add instruction - // inserts the latter in an environment, thus making it "used" and - // therefore non removable. It ensures that some other Add and - // IntConstant instructions cannot be removed, as they are direct - // or indirect inputs of the initial Add instruction. std::string expected_after = "BasicBlock 0, succ: 1\n" - " 3: IntConstant [9]\n" - " 5: IntConstant [9]\n" - " 18: IntConstant [19]\n" - " 29: SuspendCheck\n" - " 30: Goto 1\n" + " 28: SuspendCheck\n" + " 29: Goto 1\n" "BasicBlock 1, pred: 0, succ: 5\n" - " 9: Add(3, 5) [19]\n" - " 19: Add(9, 18) []\n" - " 21: SuspendCheck\n" - " 27: ReturnVoid\n" + " 26: ReturnVoid\n" "BasicBlock 5, pred: 1\n" - " 28: Exit\n"; + " 27: Exit\n"; TestCode(data, expected_before, expected_after); } diff --git a/compiler/optimizing/graph_checker.cc b/compiler/optimizing/graph_checker.cc index 4a49c83611..528fe44669 100644 --- a/compiler/optimizing/graph_checker.cc +++ b/compiler/optimizing/graph_checker.cc @@ -17,7 +17,6 @@ #include "graph_checker.h" #include <algorithm> -#include <map> #include <string> #include <sstream> @@ -32,19 +31,19 @@ void GraphChecker::VisitBasicBlock(HBasicBlock* block) { current_block_ = block; // Check consistency with respect to predecessors of `block`. - ArenaSafeMap<HBasicBlock*, size_t> predecessors_count( - std::less<HBasicBlock*>(), GetGraph()->GetArena()->Adapter(kArenaAllocGraphChecker)); - for (HBasicBlock* p : block->GetPredecessors()) { - auto it = predecessors_count.find(p); - if (it != predecessors_count.end()) { - ++it->second; - } else { - predecessors_count.Put(p, 1u); + // Note: Counting duplicates with a sorted vector uses up to 6x less memory + // than ArenaSafeMap<HBasicBlock*, size_t>. + ArenaVector<HBasicBlock*> sorted_predecessors( + block->GetPredecessors().begin(), + block->GetPredecessors().end(), + GetGraph()->GetArena()->Adapter(kArenaAllocGraphChecker)); + std::sort(sorted_predecessors.begin(), sorted_predecessors.end()); + for (auto it = sorted_predecessors.begin(), end = sorted_predecessors.end(); it != end; ) { + HBasicBlock* p = *it++; + size_t p_count_in_block_predecessors = 1u; + for (; it != end && *it == p; ++it) { + ++p_count_in_block_predecessors; } - } - for (auto& pc : predecessors_count) { - HBasicBlock* p = pc.first; - size_t p_count_in_block_predecessors = pc.second; size_t block_count_in_p_successors = std::count(p->GetSuccessors().begin(), p->GetSuccessors().end(), block); if (p_count_in_block_predecessors != block_count_in_p_successors) { @@ -57,19 +56,19 @@ void GraphChecker::VisitBasicBlock(HBasicBlock* block) { } // Check consistency with respect to successors of `block`. - ArenaSafeMap<HBasicBlock*, size_t> successors_count( - std::less<HBasicBlock*>(), GetGraph()->GetArena()->Adapter(kArenaAllocGraphChecker)); - for (HBasicBlock* s : block->GetSuccessors()) { - auto it = successors_count.find(s); - if (it != successors_count.end()) { - ++it->second; - } else { - successors_count.Put(s, 1u); + // Note: Counting duplicates with a sorted vector uses up to 6x less memory + // than ArenaSafeMap<HBasicBlock*, size_t>. + ArenaVector<HBasicBlock*> sorted_successors( + block->GetSuccessors().begin(), + block->GetSuccessors().end(), + GetGraph()->GetArena()->Adapter(kArenaAllocGraphChecker)); + std::sort(sorted_successors.begin(), sorted_successors.end()); + for (auto it = sorted_successors.begin(), end = sorted_successors.end(); it != end; ) { + HBasicBlock* s = *it++; + size_t s_count_in_block_successors = 1u; + for (; it != end && *it == s; ++it) { + ++s_count_in_block_successors; } - } - for (auto& sc : successors_count) { - HBasicBlock* s = sc.first; - size_t s_count_in_block_successors = sc.second; size_t block_count_in_s_predecessors = std::count(s->GetPredecessors().begin(), s->GetPredecessors().end(), block); if (s_count_in_block_successors != block_count_in_s_predecessors) { @@ -661,19 +660,6 @@ void GraphChecker::HandleLoop(HBasicBlock* loop_header) { } } -static Primitive::Type PrimitiveKind(Primitive::Type type) { - switch (type) { - case Primitive::kPrimBoolean: - case Primitive::kPrimByte: - case Primitive::kPrimShort: - case Primitive::kPrimChar: - case Primitive::kPrimInt: - return Primitive::kPrimInt; - default: - return type; - } -} - static bool IsSameSizeConstant(HInstruction* insn1, HInstruction* insn2) { return insn1->IsConstant() && insn2->IsConstant() @@ -716,10 +702,10 @@ void GraphChecker::VisitPhi(HPhi* phi) { // Ensure that the inputs have the same primitive kind as the phi. for (size_t i = 0, e = phi->InputCount(); i < e; ++i) { HInstruction* input = phi->InputAt(i); - if (PrimitiveKind(input->GetType()) != PrimitiveKind(phi->GetType())) { + if (Primitive::PrimitiveKind(input->GetType()) != Primitive::PrimitiveKind(phi->GetType())) { AddError(StringPrintf( "Input %d at index %zu of phi %d from block %d does not have the " - "same type as the phi: %s versus %s", + "same kind as the phi: %s versus %s", input->GetId(), i, phi->GetId(), phi->GetBlock()->GetBlockId(), Primitive::PrettyDescriptor(input->GetType()), Primitive::PrettyDescriptor(phi->GetType()))); @@ -825,7 +811,10 @@ void GraphChecker::VisitPhi(HPhi* phi) { phi->GetRegNumber(), type_str.str().c_str())); } else { - ArenaBitVector visited(GetGraph()->GetArena(), 0, /* expandable */ true); + ArenaBitVector visited(GetGraph()->GetArena(), + 0, + /* expandable */ true, + kArenaAllocGraphChecker); if (!IsConstantEquivalent(phi, other_phi, &visited)) { AddError(StringPrintf("Two phis (%d and %d) found for VReg %d but they " "are not equivalents of constants.", @@ -910,9 +899,9 @@ void GraphChecker::VisitCondition(HCondition* op) { } HInstruction* lhs = op->InputAt(0); HInstruction* rhs = op->InputAt(1); - if (PrimitiveKind(lhs->GetType()) != PrimitiveKind(rhs->GetType())) { + if (Primitive::PrimitiveKind(lhs->GetType()) != Primitive::PrimitiveKind(rhs->GetType())) { AddError(StringPrintf( - "Condition %s %d has inputs of different types: %s, and %s.", + "Condition %s %d has inputs of different kinds: %s, and %s.", op->DebugName(), op->GetId(), Primitive::PrettyDescriptor(lhs->GetType()), Primitive::PrettyDescriptor(rhs->GetType()))); @@ -930,44 +919,74 @@ void GraphChecker::VisitCondition(HCondition* op) { } } +void GraphChecker::VisitNeg(HNeg* instruction) { + VisitInstruction(instruction); + Primitive::Type input_type = instruction->InputAt(0)->GetType(); + Primitive::Type result_type = instruction->GetType(); + if (result_type != Primitive::PrimitiveKind(input_type)) { + AddError(StringPrintf("Binary operation %s %d has a result type different " + "from its input kind: %s vs %s.", + instruction->DebugName(), instruction->GetId(), + Primitive::PrettyDescriptor(result_type), + Primitive::PrettyDescriptor(input_type))); + } +} + void GraphChecker::VisitBinaryOperation(HBinaryOperation* op) { VisitInstruction(op); + Primitive::Type lhs_type = op->InputAt(0)->GetType(); + Primitive::Type rhs_type = op->InputAt(1)->GetType(); + Primitive::Type result_type = op->GetType(); + + // Type consistency between inputs. if (op->IsUShr() || op->IsShr() || op->IsShl() || op->IsRor()) { - if (PrimitiveKind(op->InputAt(1)->GetType()) != Primitive::kPrimInt) { - AddError(StringPrintf( - "Shift operation %s %d has a non-int kind second input: " - "%s of type %s.", - op->DebugName(), op->GetId(), - op->InputAt(1)->DebugName(), - Primitive::PrettyDescriptor(op->InputAt(1)->GetType()))); + if (Primitive::PrimitiveKind(rhs_type) != Primitive::kPrimInt) { + AddError(StringPrintf("Shift/rotate operation %s %d has a non-int kind second input: " + "%s of type %s.", + op->DebugName(), op->GetId(), + op->InputAt(1)->DebugName(), + Primitive::PrettyDescriptor(rhs_type))); } } else { - if (PrimitiveKind(op->InputAt(0)->GetType()) != PrimitiveKind(op->InputAt(1)->GetType())) { - AddError(StringPrintf( - "Binary operation %s %d has inputs of different types: " - "%s, and %s.", - op->DebugName(), op->GetId(), - Primitive::PrettyDescriptor(op->InputAt(0)->GetType()), - Primitive::PrettyDescriptor(op->InputAt(1)->GetType()))); + if (Primitive::PrimitiveKind(lhs_type) != Primitive::PrimitiveKind(rhs_type)) { + AddError(StringPrintf("Binary operation %s %d has inputs of different kinds: %s, and %s.", + op->DebugName(), op->GetId(), + Primitive::PrettyDescriptor(lhs_type), + Primitive::PrettyDescriptor(rhs_type))); } } + // Type consistency between result and input(s). if (op->IsCompare()) { - if (op->GetType() != Primitive::kPrimInt) { - AddError(StringPrintf( - "Compare operation %d has a non-int result type: %s.", - op->GetId(), - Primitive::PrettyDescriptor(op->GetType()))); + if (result_type != Primitive::kPrimInt) { + AddError(StringPrintf("Compare operation %d has a non-int result type: %s.", + op->GetId(), + Primitive::PrettyDescriptor(result_type))); + } + } else if (op->IsUShr() || op->IsShr() || op->IsShl() || op->IsRor()) { + // Only check the first input (value), as the second one (distance) + // must invariably be of kind `int`. + if (result_type != Primitive::PrimitiveKind(lhs_type)) { + AddError(StringPrintf("Shift/rotate operation %s %d has a result type different " + "from its left-hand side (value) input kind: %s vs %s.", + op->DebugName(), op->GetId(), + Primitive::PrettyDescriptor(result_type), + Primitive::PrettyDescriptor(lhs_type))); } } else { - // Use the first input, so that we can also make this check for shift operations. - if (PrimitiveKind(op->GetType()) != PrimitiveKind(op->InputAt(0)->GetType())) { - AddError(StringPrintf( - "Binary operation %s %d has a result type different " - "from its input type: %s vs %s.", - op->DebugName(), op->GetId(), - Primitive::PrettyDescriptor(op->GetType()), - Primitive::PrettyDescriptor(op->InputAt(0)->GetType()))); + if (Primitive::PrimitiveKind(result_type) != Primitive::PrimitiveKind(lhs_type)) { + AddError(StringPrintf("Binary operation %s %d has a result kind different " + "from its left-hand side input kind: %s vs %s.", + op->DebugName(), op->GetId(), + Primitive::PrettyDescriptor(result_type), + Primitive::PrettyDescriptor(lhs_type))); + } + if (Primitive::PrimitiveKind(result_type) != Primitive::PrimitiveKind(rhs_type)) { + AddError(StringPrintf("Binary operation %s %d has a result kind different " + "from its right-hand side input kind: %s vs %s.", + op->DebugName(), op->GetId(), + Primitive::PrettyDescriptor(result_type), + Primitive::PrettyDescriptor(rhs_type))); } } } diff --git a/compiler/optimizing/graph_checker.h b/compiler/optimizing/graph_checker.h index 52252cd3d4..27d5621887 100644 --- a/compiler/optimizing/graph_checker.h +++ b/compiler/optimizing/graph_checker.h @@ -30,7 +30,10 @@ class GraphChecker : public HGraphDelegateVisitor { : HGraphDelegateVisitor(graph), errors_(graph->GetArena()->Adapter(kArenaAllocGraphChecker)), dump_prefix_(dump_prefix), - seen_ids_(graph->GetArena(), graph->GetCurrentInstructionId(), false) {} + seen_ids_(graph->GetArena(), + graph->GetCurrentInstructionId(), + false, + kArenaAllocGraphChecker) {} // Check the whole graph (in reverse post-order). void Run() { @@ -56,6 +59,7 @@ class GraphChecker : public HGraphDelegateVisitor { void VisitInstanceOf(HInstanceOf* check) OVERRIDE; void VisitInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) OVERRIDE; void VisitLoadException(HLoadException* load) OVERRIDE; + void VisitNeg(HNeg* instruction) OVERRIDE; void VisitPackedSwitch(HPackedSwitch* instruction) OVERRIDE; void VisitReturn(HReturn* ret) OVERRIDE; void VisitReturnVoid(HReturnVoid* ret) OVERRIDE; diff --git a/compiler/optimizing/graph_visualizer.cc b/compiler/optimizing/graph_visualizer.cc index 4f1e90cd7f..3a9d242df2 100644 --- a/compiler/optimizing/graph_visualizer.cc +++ b/compiler/optimizing/graph_visualizer.cc @@ -382,6 +382,8 @@ class HGraphVisualizerPrinter : public HGraphDelegateVisitor { void VisitArraySet(HArraySet* array_set) OVERRIDE { StartAttributeStream("value_can_be_null") << std::boolalpha << array_set->GetValueCanBeNull() << std::noboolalpha; + StartAttributeStream("needs_type_check") << std::boolalpha + << array_set->NeedsTypeCheck() << std::noboolalpha; } void VisitCompare(HCompare* compare) OVERRIDE { diff --git a/compiler/optimizing/gvn.cc b/compiler/optimizing/gvn.cc index b4922789d4..f7eb2adc6c 100644 --- a/compiler/optimizing/gvn.cc +++ b/compiler/optimizing/gvn.cc @@ -40,7 +40,7 @@ class ValueSet : public ArenaObject<kArenaAllocGvn> { : allocator_(allocator), num_buckets_(kMinimumNumberOfBuckets), buckets_(allocator->AllocArray<Node*>(num_buckets_, kArenaAllocGvn)), - buckets_owned_(allocator, num_buckets_, false), + buckets_owned_(allocator, num_buckets_, false, kArenaAllocGvn), num_entries_(0) { // ArenaAllocator returns zeroed memory, so no need to set buckets to null. DCHECK(IsPowerOfTwo(num_buckets_)); @@ -53,7 +53,7 @@ class ValueSet : public ArenaObject<kArenaAllocGvn> { : allocator_(allocator), num_buckets_(to_copy.IdealBucketCount()), buckets_(allocator->AllocArray<Node*>(num_buckets_, kArenaAllocGvn)), - buckets_owned_(allocator, num_buckets_, false), + buckets_owned_(allocator, num_buckets_, false, kArenaAllocGvn), num_entries_(to_copy.num_entries_) { // ArenaAllocator returns zeroed memory, so entries of buckets_ and // buckets_owned_ are initialized to null and false, respectively. diff --git a/compiler/optimizing/induction_var_analysis.cc b/compiler/optimizing/induction_var_analysis.cc index 82a898a9f1..266cb10ab3 100644 --- a/compiler/optimizing/induction_var_analysis.cc +++ b/compiler/optimizing/induction_var_analysis.cc @@ -53,6 +53,32 @@ static void RotateEntryPhiFirst(HLoopInformation* loop, } } +/** + * Returns true if the from/to types denote a narrowing, integral conversion (precision loss). + */ +static bool IsNarrowingIntegralConversion(Primitive::Type from, Primitive::Type to) { + switch (from) { + case Primitive::kPrimLong: + return to == Primitive::kPrimByte || to == Primitive::kPrimShort + || to == Primitive::kPrimChar || to == Primitive::kPrimInt; + case Primitive::kPrimInt: + return to == Primitive::kPrimByte || to == Primitive::kPrimShort + || to == Primitive::kPrimChar; + case Primitive::kPrimChar: + case Primitive::kPrimShort: + return to == Primitive::kPrimByte; + default: + return false; + } +} + +/** + * Returns narrowest data type. + */ +static Primitive::Type Narrowest(Primitive::Type type1, Primitive::Type type2) { + return Primitive::ComponentSize(type1) <= Primitive::ComponentSize(type2) ? type1 : type2; +} + // // Class methods. // @@ -148,6 +174,9 @@ void HInductionVarAnalysis::VisitNode(HLoopInformation* loop, HInstruction* inst } } + // Type of induction. + type_ = scc_[0]->GetType(); + // Classify the SCC. if (scc_.size() == 1 && !scc_[0]->IsLoopHeaderPhi()) { ClassifyTrivial(loop, scc_[0]); @@ -197,14 +226,13 @@ void HInductionVarAnalysis::ClassifyTrivial(HLoopInformation* loop, HInstruction instruction->InputAt(0)->GetType()); } else if (instruction->IsNeg()) { info = TransferNeg(LookupInfo(loop, instruction->InputAt(0))); + } else if (instruction->IsTypeConversion()) { + info = TransferCnv(LookupInfo(loop, instruction->InputAt(0)), + instruction->AsTypeConversion()->GetInputType(), + instruction->AsTypeConversion()->GetResultType()); + } else if (instruction->IsBoundsCheck()) { info = LookupInfo(loop, instruction->InputAt(0)); // Pass-through. - } else if (instruction->IsTypeConversion()) { - HTypeConversion* conversion = instruction->AsTypeConversion(); - // TODO: accept different conversion scenarios. - if (conversion->GetResultType() == conversion->GetInputType()) { - info = LookupInfo(loop, conversion->GetInput()); - } } // Successfully classified? @@ -239,7 +267,7 @@ void HInductionVarAnalysis::ClassifyNonTrivial(HLoopInformation* loop) { if (size == 1) { InductionInfo* update = TransferPhi(loop, phi, /* input_index */ 1); if (update != nullptr) { - AssignInfo(loop, phi, CreateInduction(kWrapAround, initial, update)); + AssignInfo(loop, phi, CreateInduction(kWrapAround, initial, update, type_)); } return; } @@ -257,6 +285,8 @@ void HInductionVarAnalysis::ClassifyNonTrivial(HLoopInformation* loop) { } else if (instruction->IsSub()) { update = SolveAddSub( loop, phi, instruction, instruction->InputAt(0), instruction->InputAt(1), kSub, true); + } else if (instruction->IsTypeConversion()) { + update = SolveCnv(instruction->AsTypeConversion()); } if (update == nullptr) { return; @@ -271,7 +301,7 @@ void HInductionVarAnalysis::ClassifyNonTrivial(HLoopInformation* loop) { case kInvariant: // Classify first phi and then the rest of the cycle "on-demand". // Statements are scanned in order. - AssignInfo(loop, phi, CreateInduction(kLinear, induction, initial)); + AssignInfo(loop, phi, CreateInduction(kLinear, induction, initial, type_)); for (size_t i = 1; i < size; i++) { ClassifyTrivial(loop, scc_[i]); } @@ -301,9 +331,10 @@ HInductionVarAnalysis::InductionInfo* HInductionVarAnalysis::RotatePeriodicInduc // (b, c, d, e, a) // in preparation of assigning this to the previous variable in the sequence. if (induction->induction_class == kInvariant) { - return CreateInduction(kPeriodic, induction, last); + return CreateInduction(kPeriodic, induction, last, type_); } - return CreateInduction(kPeriodic, induction->op_a, RotatePeriodicInduction(induction->op_b, last)); + return CreateInduction( + kPeriodic, induction->op_a, RotatePeriodicInduction(induction->op_b, last), type_); } HInductionVarAnalysis::InductionInfo* HInductionVarAnalysis::TransferPhi(HLoopInformation* loop, @@ -332,8 +363,10 @@ HInductionVarAnalysis::InductionInfo* HInductionVarAnalysis::TransferAddSub(Indu if (a->induction_class == kInvariant && b->induction_class == kInvariant) { return CreateInvariantOp(op, a, b); } else if (a->induction_class == kLinear && b->induction_class == kLinear) { - return CreateInduction( - kLinear, TransferAddSub(a->op_a, b->op_a, op), TransferAddSub(a->op_b, b->op_b, op)); + return CreateInduction(kLinear, + TransferAddSub(a->op_a, b->op_a, op), + TransferAddSub(a->op_b, b->op_b, op), + type_); } else if (a->induction_class == kInvariant) { InductionInfo* new_a = b->op_a; InductionInfo* new_b = TransferAddSub(a, b->op_b, op); @@ -343,7 +376,7 @@ HInductionVarAnalysis::InductionInfo* HInductionVarAnalysis::TransferAddSub(Indu } else if (op == kSub) { // Negation required. new_a = TransferNeg(new_a); } - return CreateInduction(b->induction_class, new_a, new_b); + return CreateInduction(b->induction_class, new_a, new_b, type_); } else if (b->induction_class == kInvariant) { InductionInfo* new_a = a->op_a; InductionInfo* new_b = TransferAddSub(a->op_b, b, op); @@ -351,7 +384,7 @@ HInductionVarAnalysis::InductionInfo* HInductionVarAnalysis::TransferAddSub(Indu DCHECK(a->induction_class == kWrapAround || a->induction_class == kPeriodic); new_a = TransferAddSub(new_a, b, op); } - return CreateInduction(a->induction_class, new_a, new_b); + return CreateInduction(a->induction_class, new_a, new_b, type_); } } return nullptr; @@ -366,9 +399,15 @@ HInductionVarAnalysis::InductionInfo* HInductionVarAnalysis::TransferMul(Inducti if (a->induction_class == kInvariant && b->induction_class == kInvariant) { return CreateInvariantOp(kMul, a, b); } else if (a->induction_class == kInvariant) { - return CreateInduction(b->induction_class, TransferMul(a, b->op_a), TransferMul(a, b->op_b)); + return CreateInduction(b->induction_class, + TransferMul(a, b->op_a), + TransferMul(a, b->op_b), + type_); } else if (b->induction_class == kInvariant) { - return CreateInduction(a->induction_class, TransferMul(a->op_a, b), TransferMul(a->op_b, b)); + return CreateInduction(a->induction_class, + TransferMul(a->op_a, b), + TransferMul(a->op_b, b), + type_); } } return nullptr; @@ -400,7 +439,24 @@ HInductionVarAnalysis::InductionInfo* HInductionVarAnalysis::TransferNeg(Inducti if (a->induction_class == kInvariant) { return CreateInvariantOp(kNeg, nullptr, a); } - return CreateInduction(a->induction_class, TransferNeg(a->op_a), TransferNeg(a->op_b)); + return CreateInduction(a->induction_class, TransferNeg(a->op_a), TransferNeg(a->op_b), type_); + } + return nullptr; +} + +HInductionVarAnalysis::InductionInfo* HInductionVarAnalysis::TransferCnv(InductionInfo* a, + Primitive::Type from, + Primitive::Type to) { + if (a != nullptr) { + // Allow narrowing conversion in certain cases. + if (IsNarrowingIntegralConversion(from, to)) { + if (a->induction_class == kLinear) { + if (a->type == to || (a->type == from && IsNarrowingIntegralConversion(from, to))) { + return CreateInduction(kLinear, a->op_a, a->op_b, to); + } + } + // TODO: other cases useful too? + } } return nullptr; } @@ -442,11 +498,11 @@ HInductionVarAnalysis::InductionInfo* HInductionVarAnalysis::SolvePhiAllInputs( if (a != nullptr && a->induction_class == kInvariant) { if (phi->InputAt(1) == entry_phi) { InductionInfo* initial = LookupInfo(loop, entry_phi->InputAt(0)); - return CreateInduction(kPeriodic, a, initial); + return CreateInduction(kPeriodic, a, initial, type_); } InductionInfo* b = SolvePhi(phi, /* input_index */ 1); if (b != nullptr && b->induction_class == kPeriodic) { - return CreateInduction(kPeriodic, a, b); + return CreateInduction(kPeriodic, a, b, type_); } } } @@ -489,7 +545,7 @@ HInductionVarAnalysis::InductionInfo* HInductionVarAnalysis::SolveAddSub(HLoopIn InductionInfo* a = LookupInfo(loop, x); if (a != nullptr && a->induction_class == kInvariant) { InductionInfo* initial = LookupInfo(loop, entry_phi->InputAt(0)); - return CreateInduction(kPeriodic, CreateInvariantOp(kSub, a, initial), initial); + return CreateInduction(kPeriodic, CreateInvariantOp(kSub, a, initial), initial, type_); } } } @@ -497,6 +553,21 @@ HInductionVarAnalysis::InductionInfo* HInductionVarAnalysis::SolveAddSub(HLoopIn return nullptr; } +HInductionVarAnalysis::InductionInfo* HInductionVarAnalysis::SolveCnv(HTypeConversion* conversion) { + Primitive::Type from = conversion->GetInputType(); + Primitive::Type to = conversion->GetResultType(); + // A narrowing conversion is allowed within the cycle of a linear induction, provided that the + // narrowest encountered type is recorded with the induction to account for the precision loss. + if (IsNarrowingIntegralConversion(from, to)) { + auto it = cycle_.find(conversion->GetInput()); + if (it != cycle_.end() && it->second->induction_class == kInvariant) { + type_ = Narrowest(type_, to); + return it->second; + } + } + return nullptr; +} + void HInductionVarAnalysis::VisitControl(HLoopInformation* loop) { HInstruction* control = loop->GetHeader()->GetLastInstruction(); if (control->IsIf()) { @@ -512,12 +583,10 @@ void HInductionVarAnalysis::VisitControl(HLoopInformation* loop) { InductionInfo* a = LookupInfo(loop, condition->InputAt(0)); InductionInfo* b = LookupInfo(loop, condition->InputAt(1)); Primitive::Type type = condition->InputAt(0)->GetType(); - // Determine if the loop control uses integral arithmetic and an if-exit (X outside) or an - // if-iterate (X inside), always expressed as if-iterate when passing into VisitCondition(). - if (type != Primitive::kPrimInt && type != Primitive::kPrimLong) { - // Loop control is not 32/64-bit integral. - } else if (a == nullptr || b == nullptr) { - // Loop control is not a sequence. + // Determine if the loop control uses a known sequence on an if-exit (X outside) or on + // an if-iterate (X inside), expressed as if-iterate when passed into VisitCondition(). + if (a == nullptr || b == nullptr) { + return; // Loop control is not a sequence. } else if (if_true->GetLoopInformation() != loop && if_false->GetLoopInformation() == loop) { VisitCondition(loop, a, b, type, condition->GetOppositeCondition()); } else if (if_true->GetLoopInformation() == loop && if_false->GetLoopInformation() != loop) { @@ -559,6 +628,14 @@ void HInductionVarAnalysis::VisitCondition(HLoopInformation* loop, (stride_value == -1 && IsTaken(lower_expr, upper_expr, kCondGE)))) { cmp = stride_value > 0 ? kCondLT : kCondGT; } + // Only accept integral condition. A mismatch between the type of condition and the induction + // is only allowed if the, necessarily narrower, induction range fits the narrower control. + if (type != Primitive::kPrimInt && type != Primitive::kPrimLong) { + return; // not integral + } else if (type != a->type && + !FitsNarrowerControl(lower_expr, upper_expr, stride_value, a->type, cmp)) { + return; // mismatched type + } // Normalize a linear loop control with a nonzero stride: // stride > 0, either i < U or i <= U // stride < 0, either i > U or i >= U @@ -640,7 +717,7 @@ void HInductionVarAnalysis::VisitTripCount(HLoopInformation* loop, InductionInfo* taken_test = CreateInvariantOp(op, lower_expr, upper_expr); AssignInfo(loop, loop->GetHeader()->GetLastInstruction(), - CreateTripCount(tcKind, trip_count, taken_test)); + CreateTripCount(tcKind, trip_count, taken_test, type)); } bool HInductionVarAnalysis::IsTaken(InductionInfo* lower_expr, @@ -675,10 +752,8 @@ bool HInductionVarAnalysis::IsFinite(InductionInfo* upper_expr, int64_t stride_value, Primitive::Type type, IfCondition cmp) { - const int64_t min = type == Primitive::kPrimInt ? std::numeric_limits<int32_t>::min() - : std::numeric_limits<int64_t>::min(); - const int64_t max = type == Primitive::kPrimInt ? std::numeric_limits<int32_t>::max() - : std::numeric_limits<int64_t>::max(); + const int64_t min = Primitive::MinValueOfIntegralType(type); + const int64_t max = Primitive::MaxValueOfIntegralType(type); // Some rules under which it is certain at compile-time that the loop is finite. int64_t value; switch (cmp) { @@ -698,6 +773,31 @@ bool HInductionVarAnalysis::IsFinite(InductionInfo* upper_expr, return false; // not certain, may be infinite } +bool HInductionVarAnalysis::FitsNarrowerControl(InductionInfo* lower_expr, + InductionInfo* upper_expr, + int64_t stride_value, + Primitive::Type type, + IfCondition cmp) { + int64_t min = Primitive::MinValueOfIntegralType(type); + int64_t max = Primitive::MaxValueOfIntegralType(type); + // Inclusive test need one extra. + if (stride_value != 1 && stride_value != -1) { + return false; // non-unit stride + } else if (cmp == kCondLE) { + max--; + } else if (cmp == kCondGE) { + min++; + } + // Do both bounds fit the range? + // Note: The `value` is initialized to please valgrind - the compiler can reorder + // the return value check with the `value` check, b/27651442 . + int64_t value = 0; + return IsAtLeast(lower_expr, &value) && value >= min && + IsAtMost(lower_expr, &value) && value <= max && + IsAtLeast(upper_expr, &value) && value >= min && + IsAtMost(upper_expr, &value) && value <= max; +} + void HInductionVarAnalysis::AssignInfo(HLoopInformation* loop, HInstruction* instruction, InductionInfo* info) { @@ -794,7 +894,7 @@ HInductionVarAnalysis::InductionInfo* HInductionVarAnalysis::CreateSimplifiedInv return CreateSimplifiedInvariant(kSub, b->op_b, b->op_a); } } - return new (graph_->GetArena()) InductionInfo(kInvariant, op, a, b, nullptr); + return new (graph_->GetArena()) InductionInfo(kInvariant, op, a, b, nullptr, b->type); } bool HInductionVarAnalysis::IsExact(InductionInfo* info, int64_t* value) { @@ -856,18 +956,22 @@ std::string HInductionVarAnalysis::InductionToString(InductionInfo* info) { case kTripCountInBodyUnsafe: inv += " (TC-body-unsafe) "; break; } inv += InductionToString(info->op_b); - return inv + ")"; + inv += ")"; + return inv; } else { DCHECK(info->operation == kNop); if (info->induction_class == kLinear) { return "(" + InductionToString(info->op_a) + " * i + " + - InductionToString(info->op_b) + ")"; + InductionToString(info->op_b) + "):" + + Primitive::PrettyDescriptor(info->type); } else if (info->induction_class == kWrapAround) { return "wrap(" + InductionToString(info->op_a) + ", " + - InductionToString(info->op_b) + ")"; + InductionToString(info->op_b) + "):" + + Primitive::PrettyDescriptor(info->type); } else if (info->induction_class == kPeriodic) { return "periodic(" + InductionToString(info->op_a) + ", " + - InductionToString(info->op_b) + ")"; + InductionToString(info->op_b) + "):" + + Primitive::PrettyDescriptor(info->type); } } } diff --git a/compiler/optimizing/induction_var_analysis.h b/compiler/optimizing/induction_var_analysis.h index 94d2646aec..f1965f07b2 100644 --- a/compiler/optimizing/induction_var_analysis.h +++ b/compiler/optimizing/induction_var_analysis.h @@ -97,17 +97,20 @@ class HInductionVarAnalysis : public HOptimization { InductionOp op, InductionInfo* a, InductionInfo* b, - HInstruction* f) + HInstruction* f, + Primitive::Type t) : induction_class(ic), operation(op), op_a(a), op_b(b), - fetch(f) {} + fetch(f), + type(t) {} InductionClass induction_class; InductionOp operation; InductionInfo* op_a; InductionInfo* op_b; HInstruction* fetch; + Primitive::Type type; // precision of induction }; bool IsVisitedNode(HInstruction* instruction) const { @@ -121,17 +124,24 @@ class HInductionVarAnalysis : public HOptimization { InductionInfo* CreateInvariantFetch(HInstruction* f) { DCHECK(f != nullptr); - return new (graph_->GetArena()) InductionInfo(kInvariant, kFetch, nullptr, nullptr, f); + return new (graph_->GetArena()) + InductionInfo(kInvariant, kFetch, nullptr, nullptr, f, f->GetType()); } - InductionInfo* CreateTripCount(InductionOp op, InductionInfo* a, InductionInfo* b) { + InductionInfo* CreateTripCount(InductionOp op, + InductionInfo* a, + InductionInfo* b, + Primitive::Type type) { DCHECK(a != nullptr); - return new (graph_->GetArena()) InductionInfo(kInvariant, op, a, b, nullptr); + return new (graph_->GetArena()) InductionInfo(kInvariant, op, a, b, nullptr, type); } - InductionInfo* CreateInduction(InductionClass ic, InductionInfo* a, InductionInfo* b) { + InductionInfo* CreateInduction(InductionClass ic, + InductionInfo* a, + InductionInfo* b, + Primitive::Type type) { DCHECK(a != nullptr && b != nullptr); - return new (graph_->GetArena()) InductionInfo(ic, kNop, a, b, nullptr); + return new (graph_->GetArena()) InductionInfo(ic, kNop, a, b, nullptr, type); } // Methods for analysis. @@ -148,6 +158,7 @@ class HInductionVarAnalysis : public HOptimization { InductionInfo* TransferMul(InductionInfo* a, InductionInfo* b); InductionInfo* TransferShl(InductionInfo* a, InductionInfo* b, Primitive::Type type); InductionInfo* TransferNeg(InductionInfo* a); + InductionInfo* TransferCnv(InductionInfo* a, Primitive::Type from, Primitive::Type to); // Solvers. InductionInfo* SolvePhi(HInstruction* phi, size_t input_index); @@ -161,6 +172,7 @@ class HInductionVarAnalysis : public HOptimization { HInstruction* y, InductionOp op, bool is_first_call); + InductionInfo* SolveCnv(HTypeConversion* conversion); // Trip count information. void VisitControl(HLoopInformation* loop); @@ -181,6 +193,11 @@ class HInductionVarAnalysis : public HOptimization { int64_t stride_value, Primitive::Type type, IfCondition cmp); + bool FitsNarrowerControl(InductionInfo* lower_expr, + InductionInfo* upper_expr, + int64_t stride_value, + Primitive::Type type, + IfCondition cmp); // Assign and lookup. void AssignInfo(HLoopInformation* loop, HInstruction* instruction, InductionInfo* info); @@ -205,6 +222,7 @@ class HInductionVarAnalysis : public HOptimization { ArenaVector<HInstruction*> scc_; ArenaSafeMap<HInstruction*, NodeInfo> map_; ArenaSafeMap<HInstruction*, InductionInfo*> cycle_; + Primitive::Type type_; /** * Maintains the results of the analysis as a mapping from loops to a mapping from instructions diff --git a/compiler/optimizing/induction_var_analysis_test.cc b/compiler/optimizing/induction_var_analysis_test.cc index 89e4690de2..0fbb67d0d9 100644 --- a/compiler/optimizing/induction_var_analysis_test.cc +++ b/compiler/optimizing/induction_var_analysis_test.cc @@ -202,6 +202,7 @@ TEST_F(InductionVarAnalysisTest, ProperLoopSetup) { // } BuildLoopNest(10); graph_->BuildDominatorTree(); + ASSERT_EQ(entry_->GetLoopInformation(), nullptr); for (int d = 0; d < 1; d++) { ASSERT_EQ(loop_preheader_[d]->GetLoopInformation(), @@ -224,8 +225,8 @@ TEST_F(InductionVarAnalysisTest, FindBasicInduction) { HInstruction* store = InsertArrayStore(basic_[0], 0); PerformInductionVarAnalysis(); - EXPECT_STREQ("((1) * i + (0))", GetInductionInfo(store->InputAt(1), 0).c_str()); - EXPECT_STREQ("((1) * i + (1))", GetInductionInfo(increment_[0], 0).c_str()); + EXPECT_STREQ("((1) * i + (0)):PrimInt", GetInductionInfo(store->InputAt(1), 0).c_str()); + EXPECT_STREQ("((1) * i + (1)):PrimInt", GetInductionInfo(increment_[0], 0).c_str()); // Trip-count. EXPECT_STREQ("((100) (TC-loop) ((0) < (100)))", @@ -254,11 +255,11 @@ TEST_F(InductionVarAnalysisTest, FindDerivedInduction) { new (&allocator_) HNeg(Primitive::kPrimInt, basic_[0]), 0); PerformInductionVarAnalysis(); - EXPECT_STREQ("((1) * i + (100))", GetInductionInfo(add, 0).c_str()); - EXPECT_STREQ("(( - (1)) * i + (100))", GetInductionInfo(sub, 0).c_str()); - EXPECT_STREQ("((100) * i + (0))", GetInductionInfo(mul, 0).c_str()); - EXPECT_STREQ("((2) * i + (0))", GetInductionInfo(shl, 0).c_str()); - EXPECT_STREQ("(( - (1)) * i + (0))", GetInductionInfo(neg, 0).c_str()); + EXPECT_STREQ("((1) * i + (100)):PrimInt", GetInductionInfo(add, 0).c_str()); + EXPECT_STREQ("(( - (1)) * i + (100)):PrimInt", GetInductionInfo(sub, 0).c_str()); + EXPECT_STREQ("((100) * i + (0)):PrimInt", GetInductionInfo(mul, 0).c_str()); + EXPECT_STREQ("((2) * i + (0)):PrimInt", GetInductionInfo(shl, 0).c_str()); + EXPECT_STREQ("(( - (1)) * i + (0)):PrimInt", GetInductionInfo(neg, 0).c_str()); } TEST_F(InductionVarAnalysisTest, FindChainInduction) { @@ -283,9 +284,9 @@ TEST_F(InductionVarAnalysisTest, FindChainInduction) { k->AddInput(sub); PerformInductionVarAnalysis(); - EXPECT_STREQ("(((100) - (1)) * i + (100))", + EXPECT_STREQ("(((100) - (1)) * i + (100)):PrimInt", GetInductionInfo(store1->InputAt(1), 0).c_str()); - EXPECT_STREQ("(((100) - (1)) * i + ((100) - (1)))", + EXPECT_STREQ("(((100) - (1)) * i + ((100) - (1))):PrimInt", GetInductionInfo(store2->InputAt(1), 0).c_str()); } @@ -318,7 +319,7 @@ TEST_F(InductionVarAnalysisTest, FindTwoWayBasicInduction) { k_header->AddInput(k_body); PerformInductionVarAnalysis(); - EXPECT_STREQ("((1) * i + (1))", GetInductionInfo(store->InputAt(1), 0).c_str()); + EXPECT_STREQ("((1) * i + (1)):PrimInt", GetInductionInfo(store->InputAt(1), 0).c_str()); } TEST_F(InductionVarAnalysisTest, FindTwoWayDerivedInduction) { @@ -345,7 +346,7 @@ TEST_F(InductionVarAnalysisTest, FindTwoWayDerivedInduction) { HInstruction* store = InsertArrayStore(k, 0); PerformInductionVarAnalysis(); - EXPECT_STREQ("((1) * i + (1))", GetInductionInfo(store->InputAt(1), 0).c_str()); + EXPECT_STREQ("((1) * i + (1)):PrimInt", GetInductionInfo(store->InputAt(1), 0).c_str()); } TEST_F(InductionVarAnalysisTest, FindFirstOrderWrapAroundInduction) { @@ -365,7 +366,7 @@ TEST_F(InductionVarAnalysisTest, FindFirstOrderWrapAroundInduction) { k->AddInput(sub); PerformInductionVarAnalysis(); - EXPECT_STREQ("wrap((0), (( - (1)) * i + (100)))", + EXPECT_STREQ("wrap((0), (( - (1)) * i + (100)):PrimInt):PrimInt", GetInductionInfo(store->InputAt(1), 0).c_str()); } @@ -391,7 +392,7 @@ TEST_F(InductionVarAnalysisTest, FindSecondOrderWrapAroundInduction) { t->AddInput(sub); PerformInductionVarAnalysis(); - EXPECT_STREQ("wrap((0), wrap((100), (( - (1)) * i + (100))))", + EXPECT_STREQ("wrap((0), wrap((100), (( - (1)) * i + (100)):PrimInt):PrimInt):PrimInt", GetInductionInfo(store->InputAt(1), 0).c_str()); } @@ -424,11 +425,16 @@ TEST_F(InductionVarAnalysisTest, FindWrapAroundDerivedInduction) { InsertInstruction(new (&allocator_) HShl(Primitive::kPrimInt, basic_[0], constant1_), 0)); PerformInductionVarAnalysis(); - EXPECT_STREQ("wrap((100), ((2) * i + (100)))", GetInductionInfo(add, 0).c_str()); - EXPECT_STREQ("wrap(((0) - (100)), ((2) * i + ((0) - (100))))", GetInductionInfo(sub, 0).c_str()); - EXPECT_STREQ("wrap((0), (((2) * (100)) * i + (0)))", GetInductionInfo(mul, 0).c_str()); - EXPECT_STREQ("wrap((0), (((2) * (2)) * i + (0)))", GetInductionInfo(shl, 0).c_str()); - EXPECT_STREQ("wrap((0), (( - (2)) * i + (0)))", GetInductionInfo(neg, 0).c_str()); + EXPECT_STREQ("wrap((100), ((2) * i + (100)):PrimInt):PrimInt", + GetInductionInfo(add, 0).c_str()); + EXPECT_STREQ("wrap(((0) - (100)), ((2) * i + ((0) - (100))):PrimInt):PrimInt", + GetInductionInfo(sub, 0).c_str()); + EXPECT_STREQ("wrap((0), (((2) * (100)) * i + (0)):PrimInt):PrimInt", + GetInductionInfo(mul, 0).c_str()); + EXPECT_STREQ("wrap((0), (((2) * (2)) * i + (0)):PrimInt):PrimInt", + GetInductionInfo(shl, 0).c_str()); + EXPECT_STREQ("wrap((0), (( - (2)) * i + (0)):PrimInt):PrimInt", + GetInductionInfo(neg, 0).c_str()); } TEST_F(InductionVarAnalysisTest, FindPeriodicInduction) { @@ -455,8 +461,8 @@ TEST_F(InductionVarAnalysisTest, FindPeriodicInduction) { t->AddInput(k); PerformInductionVarAnalysis(); - EXPECT_STREQ("periodic((0), (100))", GetInductionInfo(store1->InputAt(1), 0).c_str()); - EXPECT_STREQ("periodic((100), (0))", GetInductionInfo(store2->InputAt(1), 0).c_str()); + EXPECT_STREQ("periodic((0), (100)):PrimInt", GetInductionInfo(store1->InputAt(1), 0).c_str()); + EXPECT_STREQ("periodic((100), (0)):PrimInt", GetInductionInfo(store2->InputAt(1), 0).c_str()); } TEST_F(InductionVarAnalysisTest, FindIdiomaticPeriodicInduction) { @@ -476,8 +482,8 @@ TEST_F(InductionVarAnalysisTest, FindIdiomaticPeriodicInduction) { k->AddInput(sub); PerformInductionVarAnalysis(); - EXPECT_STREQ("periodic((0), (1))", GetInductionInfo(store->InputAt(1), 0).c_str()); - EXPECT_STREQ("periodic((1), (0))", GetInductionInfo(sub, 0).c_str()); + EXPECT_STREQ("periodic((0), (1)):PrimInt", GetInductionInfo(store->InputAt(1), 0).c_str()); + EXPECT_STREQ("periodic((1), (0)):PrimInt", GetInductionInfo(sub, 0).c_str()); } TEST_F(InductionVarAnalysisTest, FindDerivedPeriodicInduction) { @@ -512,11 +518,11 @@ TEST_F(InductionVarAnalysisTest, FindDerivedPeriodicInduction) { new (&allocator_) HNeg(Primitive::kPrimInt, k_body), 0); PerformInductionVarAnalysis(); - EXPECT_STREQ("periodic(((1) + (100)), (100))", GetInductionInfo(add, 0).c_str()); - EXPECT_STREQ("periodic(((1) - (100)), ((0) - (100)))", GetInductionInfo(sub, 0).c_str()); - EXPECT_STREQ("periodic((100), (0))", GetInductionInfo(mul, 0).c_str()); - EXPECT_STREQ("periodic((2), (0))", GetInductionInfo(shl, 0).c_str()); - EXPECT_STREQ("periodic(( - (1)), (0))", GetInductionInfo(neg, 0).c_str()); + EXPECT_STREQ("periodic(((1) + (100)), (100)):PrimInt", GetInductionInfo(add, 0).c_str()); + EXPECT_STREQ("periodic(((1) - (100)), ((0) - (100))):PrimInt", GetInductionInfo(sub, 0).c_str()); + EXPECT_STREQ("periodic((100), (0)):PrimInt", GetInductionInfo(mul, 0).c_str()); + EXPECT_STREQ("periodic((2), (0)):PrimInt", GetInductionInfo(shl, 0).c_str()); + EXPECT_STREQ("periodic(( - (1)), (0)):PrimInt", GetInductionInfo(neg, 0).c_str()); } TEST_F(InductionVarAnalysisTest, FindDeepLoopInduction) { @@ -549,7 +555,7 @@ TEST_F(InductionVarAnalysisTest, FindDeepLoopInduction) { // Avoid exact phi number, since that depends on the SSA building phase. std::regex r("\\(\\(1\\) \\* i \\+ " - "\\(\\(1\\) \\+ \\(\\d+:Phi\\)\\)\\)"); + "\\(\\(1\\) \\+ \\(\\d+:Phi\\)\\)\\):PrimInt"); for (int d = 0; d < 10; d++) { if (d == 9) { @@ -557,11 +563,122 @@ TEST_F(InductionVarAnalysisTest, FindDeepLoopInduction) { } else { EXPECT_STREQ("", GetInductionInfo(store->InputAt(1), d).c_str()); } - EXPECT_STREQ("((1) * i + (1))", GetInductionInfo(increment_[d], d).c_str()); + EXPECT_STREQ("((1) * i + (1)):PrimInt", GetInductionInfo(increment_[d], d).c_str()); // Trip-count. EXPECT_STREQ("((100) (TC-loop) ((0) < (100)))", GetInductionInfo(loop_header_[d]->GetLastInstruction(), d).c_str()); } } +TEST_F(InductionVarAnalysisTest, ByteLoopControl1) { + // Setup: + // for (byte i = -128; i < 127; i++) { // just fits! + // } + BuildLoopNest(1); + basic_[0]->ReplaceInput(graph_->GetIntConstant(-128), 0); + HInstruction* ifs = loop_header_[0]->GetLastInstruction()->GetPrevious(); + ifs->ReplaceInput(graph_->GetIntConstant(127), 1); + HInstruction* conv = new(&allocator_) HTypeConversion(Primitive::kPrimByte, increment_[0], -1); + loop_body_[0]->InsertInstructionBefore(conv, increment_[0]->GetNext()); + basic_[0]->ReplaceInput(conv, 1); + PerformInductionVarAnalysis(); + + EXPECT_STREQ("((1) * i + ((-128) + (1))):PrimByte", GetInductionInfo(increment_[0], 0).c_str()); + // Trip-count. + EXPECT_STREQ("(((127) - (-128)) (TC-loop) ((-128) < (127)))", + GetInductionInfo(loop_header_[0]->GetLastInstruction(), 0).c_str()); +} + +TEST_F(InductionVarAnalysisTest, ByteLoopControl2) { + // Setup: + // for (byte i = -128; i < 128; i++) { // infinite loop! + // } + BuildLoopNest(1); + basic_[0]->ReplaceInput(graph_->GetIntConstant(-128), 0); + HInstruction* ifs = loop_header_[0]->GetLastInstruction()->GetPrevious(); + ifs->ReplaceInput(graph_->GetIntConstant(128), 1); + HInstruction* conv = new(&allocator_) HTypeConversion(Primitive::kPrimByte, increment_[0], -1); + loop_body_[0]->InsertInstructionBefore(conv, increment_[0]->GetNext()); + basic_[0]->ReplaceInput(conv, 1); + PerformInductionVarAnalysis(); + + EXPECT_STREQ("((1) * i + ((-128) + (1))):PrimByte", GetInductionInfo(increment_[0], 0).c_str()); + // Trip-count undefined. + EXPECT_STREQ("", GetInductionInfo(loop_header_[0]->GetLastInstruction(), 0).c_str()); +} + +TEST_F(InductionVarAnalysisTest, ShortLoopControl1) { + // Setup: + // for (short i = -32768; i < 32767; i++) { // just fits! + // } + BuildLoopNest(1); + basic_[0]->ReplaceInput(graph_->GetIntConstant(-32768), 0); + HInstruction* ifs = loop_header_[0]->GetLastInstruction()->GetPrevious(); + ifs->ReplaceInput(graph_->GetIntConstant(32767), 1); + HInstruction* conv = new(&allocator_) HTypeConversion(Primitive::kPrimShort, increment_[0], -1); + loop_body_[0]->InsertInstructionBefore(conv, increment_[0]->GetNext()); + basic_[0]->ReplaceInput(conv, 1); + PerformInductionVarAnalysis(); + + EXPECT_STREQ("((1) * i + ((-32768) + (1))):PrimShort", + GetInductionInfo(increment_[0], 0).c_str()); + // Trip-count. + EXPECT_STREQ("(((32767) - (-32768)) (TC-loop) ((-32768) < (32767)))", + GetInductionInfo(loop_header_[0]->GetLastInstruction(), 0).c_str()); +} + +TEST_F(InductionVarAnalysisTest, ShortLoopControl2) { + // Setup: + // for (short i = -32768; i < 32768; i++) { // infinite loop! + // } + BuildLoopNest(1); + basic_[0]->ReplaceInput(graph_->GetIntConstant(-32768), 0); + HInstruction* ifs = loop_header_[0]->GetLastInstruction()->GetPrevious(); + ifs->ReplaceInput(graph_->GetIntConstant(32768), 1); + HInstruction* conv = new(&allocator_) HTypeConversion(Primitive::kPrimShort, increment_[0], -1); + loop_body_[0]->InsertInstructionBefore(conv, increment_[0]->GetNext()); + basic_[0]->ReplaceInput(conv, 1); + PerformInductionVarAnalysis(); + + EXPECT_STREQ("((1) * i + ((-32768) + (1))):PrimShort", + GetInductionInfo(increment_[0], 0).c_str()); + // Trip-count undefined. + EXPECT_STREQ("", GetInductionInfo(loop_header_[0]->GetLastInstruction(), 0).c_str()); +} + +TEST_F(InductionVarAnalysisTest, CharLoopControl1) { + // Setup: + // for (char i = 0; i < 65535; i++) { // just fits! + // } + BuildLoopNest(1); + HInstruction* ifs = loop_header_[0]->GetLastInstruction()->GetPrevious(); + ifs->ReplaceInput(graph_->GetIntConstant(65535), 1); + HInstruction* conv = new(&allocator_) HTypeConversion(Primitive::kPrimChar, increment_[0], -1); + loop_body_[0]->InsertInstructionBefore(conv, increment_[0]->GetNext()); + basic_[0]->ReplaceInput(conv, 1); + PerformInductionVarAnalysis(); + + EXPECT_STREQ("((1) * i + (1)):PrimChar", GetInductionInfo(increment_[0], 0).c_str()); + // Trip-count. + EXPECT_STREQ("((65535) (TC-loop) ((0) < (65535)))", + GetInductionInfo(loop_header_[0]->GetLastInstruction(), 0).c_str()); +} + +TEST_F(InductionVarAnalysisTest, CharLoopControl2) { + // Setup: + // for (char i = 0; i < 65536; i++) { // infinite loop! + // } + BuildLoopNest(1); + HInstruction* ifs = loop_header_[0]->GetLastInstruction()->GetPrevious(); + ifs->ReplaceInput(graph_->GetIntConstant(65536), 1); + HInstruction* conv = new(&allocator_) HTypeConversion(Primitive::kPrimChar, increment_[0], -1); + loop_body_[0]->InsertInstructionBefore(conv, increment_[0]->GetNext()); + basic_[0]->ReplaceInput(conv, 1); + PerformInductionVarAnalysis(); + + EXPECT_STREQ("((1) * i + (1)):PrimChar", GetInductionInfo(increment_[0], 0).c_str()); + // Trip-count undefined. + EXPECT_STREQ("", GetInductionInfo(loop_header_[0]->GetLastInstruction(), 0).c_str()); +} + } // namespace art diff --git a/compiler/optimizing/induction_var_range.cc b/compiler/optimizing/induction_var_range.cc index f9b6910acd..bc920d96b5 100644 --- a/compiler/optimizing/induction_var_range.cc +++ b/compiler/optimizing/induction_var_range.cc @@ -58,13 +58,13 @@ static bool IsIntAndGet(HInstruction* instruction, int64_t* value) { } /** - * An upper bound a * (length / a) + b, where a > 0, can be conservatively rewritten as length + b + * An upper bound a * (length / a) + b, where a >= 1, can be conservatively rewritten as length + b * because length >= 0 is true. This makes it more likely the bound is useful to clients. */ static InductionVarRange::Value SimplifyMax(InductionVarRange::Value v) { int64_t value; if (v.is_known && - v.a_constant > 1 && + v.a_constant >= 1 && v.instruction->IsDiv() && v.instruction->InputAt(0)->IsArrayLength() && IsIntAndGet(v.instruction->InputAt(1), &value) && v.a_constant == value) { @@ -73,6 +73,28 @@ static InductionVarRange::Value SimplifyMax(InductionVarRange::Value v) { return v; } +/** + * Corrects a value for type to account for arithmetic wrap-around in lower precision. + */ +static InductionVarRange::Value CorrectForType(InductionVarRange::Value v, Primitive::Type type) { + switch (type) { + case Primitive::kPrimShort: + case Primitive::kPrimChar: + case Primitive::kPrimByte: { + // Constants within range only. + // TODO: maybe some room for improvement, like allowing widening conversions + const int32_t min = Primitive::MinValueOfIntegralType(type); + const int32_t max = Primitive::MaxValueOfIntegralType(type); + return (v.is_known && v.a_constant == 0 && min <= v.b_constant && v.b_constant <= max) + ? v + : InductionVarRange::Value(); + } + default: + // At int or higher. + return v; + } +} + /** Helper method to test for a constant value. */ static bool IsConstantValue(InductionVarRange::Value v) { return v.is_known && v.a_constant == 0; @@ -114,6 +136,18 @@ bool InductionVarRange::GetInductionRange(HInstruction* context, if (info == nullptr) { return false; // no induction information } + // Type int or lower (this is not too restrictive since intended clients, like + // bounds check elimination, will have truncated higher precision induction + // at their use point already). + switch (info->type) { + case Primitive::kPrimInt: + case Primitive::kPrimShort: + case Primitive::kPrimChar: + case Primitive::kPrimByte: + break; + default: + return false; + } // Set up loop information. HBasicBlock* header = loop->GetHeader(); bool in_body = context->GetBlock() != header; @@ -128,25 +162,27 @@ bool InductionVarRange::GetInductionRange(HInstruction* context, bool InductionVarRange::RefineOuter(/*in-out*/ Value* min_val, /*in-out*/ Value* max_val) const { - Value v1_min = RefineOuter(*min_val, /* is_min */ true); - Value v2_max = RefineOuter(*max_val, /* is_min */ false); - // The refined range is safe if both sides refine the same instruction. Otherwise, since two - // different ranges are combined, the new refined range is safe to pass back to the client if - // the extremes of the computed ranges ensure no arithmetic wrap-around anomalies occur. - if (min_val->instruction != max_val->instruction) { - Value v1_max = RefineOuter(*min_val, /* is_min */ false); - Value v2_min = RefineOuter(*max_val, /* is_min */ true); - if (!IsConstantValue(v1_max) || - !IsConstantValue(v2_min) || - v1_max.b_constant > v2_min.b_constant) { - return false; + if (min_val->instruction != nullptr || max_val->instruction != nullptr) { + Value v1_min = RefineOuter(*min_val, /* is_min */ true); + Value v2_max = RefineOuter(*max_val, /* is_min */ false); + // The refined range is safe if both sides refine the same instruction. Otherwise, since two + // different ranges are combined, the new refined range is safe to pass back to the client if + // the extremes of the computed ranges ensure no arithmetic wrap-around anomalies occur. + if (min_val->instruction != max_val->instruction) { + Value v1_max = RefineOuter(*min_val, /* is_min */ false); + Value v2_min = RefineOuter(*max_val, /* is_min */ true); + if (!IsConstantValue(v1_max) || + !IsConstantValue(v2_min) || + v1_max.b_constant > v2_min.b_constant) { + return false; + } + } + // Did something change? + if (v1_min.instruction != min_val->instruction || v2_max.instruction != max_val->instruction) { + *min_val = v1_min; + *max_val = v2_max; + return true; } - } - // Did something change? - if (v1_min.instruction != min_val->instruction || v2_max.instruction != max_val->instruction) { - *min_val = v1_min; - *max_val = v2_max; - return true; } return false; } @@ -277,7 +313,12 @@ InductionVarRange::Value InductionVarRange::GetLinear(HInductionVarAnalysis::Ind if (HInductionVarAnalysis::InductionEqual(trip_expr->op_b, info->op_b)) { // Analyze cancelled trip with just the positive operand (trip_expr->op_a). HInductionVarAnalysis::InductionInfo cancelled_trip( - trip->induction_class, trip->operation, trip_expr->op_a, trip->op_b, nullptr); + trip->induction_class, + trip->operation, + trip_expr->op_a, + trip->op_b, + nullptr, + trip->type); return GetVal(&cancelled_trip, trip, in_body, is_min); } } else if (is_min && stride_value == -1) { @@ -289,9 +330,10 @@ InductionVarRange::Value InductionVarRange::GetLinear(HInductionVarAnalysis::Ind HInductionVarAnalysis::kNeg, nullptr, trip_expr->op_b, - nullptr); + nullptr, + trip->type); HInductionVarAnalysis::InductionInfo cancelled_trip( - trip->induction_class, trip->operation, &neg, trip->op_b, nullptr); + trip->induction_class, trip->operation, &neg, trip->op_b, nullptr, trip->type); return SubValue(Value(0), GetVal(&cancelled_trip, trip, in_body, !is_min)); } } @@ -322,6 +364,12 @@ InductionVarRange::Value InductionVarRange::GetFetch(HInstruction* instruction, } } else if (instruction->IsArrayLength() && instruction->InputAt(0)->IsNewArray()) { return GetFetch(instruction->InputAt(0)->InputAt(0), trip, in_body, is_min); + } else if (instruction->IsTypeConversion()) { + // Since analysis is 32-bit (or narrower) we allow a widening along the path. + if (instruction->AsTypeConversion()->GetInputType() == Primitive::kPrimInt && + instruction->AsTypeConversion()->GetResultType() == Primitive::kPrimLong) { + return GetFetch(instruction->InputAt(0), trip, in_body, is_min); + } } else if (is_min) { // Special case for finding minimum: minimum of trip-count in loop-body is 1. if (trip != nullptr && in_body && instruction == trip->op_a->fetch) { @@ -374,7 +422,7 @@ InductionVarRange::Value InductionVarRange::GetVal(HInductionVarAnalysis::Induct } break; case HInductionVarAnalysis::kLinear: { - return GetLinear(info, trip, in_body, is_min); + return CorrectForType(GetLinear(info, trip, in_body, is_min), info->type); } case HInductionVarAnalysis::kWrapAround: case HInductionVarAnalysis::kPeriodic: @@ -613,8 +661,12 @@ bool InductionVarRange::GenerateCode(HInductionVarAnalysis::InductionInfo* info, bool in_body, bool is_min) const { if (info != nullptr) { - // Handle current operation. + // Verify type safety. Primitive::Type type = Primitive::kPrimInt; + if (info->type != type) { + return false; + } + // Handle current operation. HInstruction* opa = nullptr; HInstruction* opb = nullptr; switch (info->induction_class) { @@ -667,13 +719,10 @@ bool InductionVarRange::GenerateCode(HInductionVarAnalysis::InductionInfo* info, } break; case HInductionVarAnalysis::kFetch: - if (info->fetch->GetType() == type) { - if (graph != nullptr) { - *result = info->fetch; // already in HIR - } - return true; + if (graph != nullptr) { + *result = info->fetch; // already in HIR } - break; + return true; case HInductionVarAnalysis::kTripCountInLoop: case HInductionVarAnalysis::kTripCountInLoopUnsafe: if (!in_body && !is_min) { // one extra! diff --git a/compiler/optimizing/induction_var_range_test.cc b/compiler/optimizing/induction_var_range_test.cc index c5c33bd9bc..dc04dc2c49 100644 --- a/compiler/optimizing/induction_var_range_test.cc +++ b/compiler/optimizing/induction_var_range_test.cc @@ -139,37 +139,40 @@ class InductionVarRangeTest : public CommonCompilerTest { /** Constructs a trip-count. */ HInductionVarAnalysis::InductionInfo* CreateTripCount(int32_t tc, bool in_loop, bool safe) { + Primitive::Type type = Primitive::kPrimInt; if (in_loop && safe) { return iva_->CreateTripCount( - HInductionVarAnalysis::kTripCountInLoop, CreateConst(tc), nullptr); + HInductionVarAnalysis::kTripCountInLoop, CreateConst(tc), nullptr, type); } else if (in_loop) { return iva_->CreateTripCount( - HInductionVarAnalysis::kTripCountInLoopUnsafe, CreateConst(tc), nullptr); + HInductionVarAnalysis::kTripCountInLoopUnsafe, CreateConst(tc), nullptr, type); } else if (safe) { return iva_->CreateTripCount( - HInductionVarAnalysis::kTripCountInBody, CreateConst(tc), nullptr); + HInductionVarAnalysis::kTripCountInBody, CreateConst(tc), nullptr, type); } else { return iva_->CreateTripCount( - HInductionVarAnalysis::kTripCountInBodyUnsafe, CreateConst(tc), nullptr); + HInductionVarAnalysis::kTripCountInBodyUnsafe, CreateConst(tc), nullptr, type); } } /** Constructs a linear a * i + b induction. */ HInductionVarAnalysis::InductionInfo* CreateLinear(int32_t a, int32_t b) { - return iva_->CreateInduction(HInductionVarAnalysis::kLinear, CreateConst(a), CreateConst(b)); + return iva_->CreateInduction( + HInductionVarAnalysis::kLinear, CreateConst(a), CreateConst(b), Primitive::kPrimInt); } /** Constructs a range [lo, hi] using a periodic induction. */ HInductionVarAnalysis::InductionInfo* CreateRange(int32_t lo, int32_t hi) { return iva_->CreateInduction( - HInductionVarAnalysis::kPeriodic, CreateConst(lo), CreateConst(hi)); + HInductionVarAnalysis::kPeriodic, CreateConst(lo), CreateConst(hi), Primitive::kPrimInt); } /** Constructs a wrap-around induction consisting of a constant, followed info */ HInductionVarAnalysis::InductionInfo* CreateWrapAround( int32_t initial, HInductionVarAnalysis::InductionInfo* info) { - return iva_->CreateInduction(HInductionVarAnalysis::kWrapAround, CreateConst(initial), info); + return iva_->CreateInduction( + HInductionVarAnalysis::kWrapAround, CreateConst(initial), info, Primitive::kPrimInt); } /** Constructs a wrap-around induction consisting of a constant, followed by a range. */ diff --git a/compiler/optimizing/inliner.cc b/compiler/optimizing/inliner.cc index a46478e7d6..7114dc5992 100644 --- a/compiler/optimizing/inliner.cc +++ b/compiler/optimizing/inliner.cc @@ -144,6 +144,10 @@ static ArtMethod* FindVirtualOrInterfaceTarget(HInvoke* invoke, ArtMethod* resol } else if (!resolved_method->GetDeclaringClass()->IsAssignableFrom(info.GetTypeHandle().Get())) { // The method that we're trying to call is not in the receiver's class or super classes. return nullptr; + } else if (info.GetTypeHandle()->IsErroneous()) { + // If the type is erroneous, do not go further, as we are going to query the vtable or + // imt table, that we can only safely do on non-erroneous classes. + return nullptr; } ClassLinker* cl = Runtime::Current()->GetClassLinker(); @@ -293,7 +297,11 @@ bool HInliner::TryInline(HInvoke* invoke_instruction) { } if (actual_method != nullptr) { - return TryInlineAndReplace(invoke_instruction, actual_method, /* do_rtp */ true); + bool result = TryInlineAndReplace(invoke_instruction, actual_method, /* do_rtp */ true); + if (result && !invoke_instruction->IsInvokeStaticOrDirect()) { + MaybeRecordStat(kInlinedInvokeVirtualOrInterface); + } + return result; } DCHECK(!invoke_instruction->IsInvokeStaticOrDirect()); @@ -1278,10 +1286,14 @@ void HInliner::FixUpReturnReferenceType(HInvoke* invoke_instruction, // some functionality from the reference type propagation. DCHECK(return_replacement->IsPhi()); size_t pointer_size = Runtime::Current()->GetClassLinker()->GetImagePointerSize(); - ReferenceTypeInfo::TypeHandle return_handle = - handles_->NewHandle(resolved_method->GetReturnType(true /* resolve */, pointer_size)); - return_replacement->SetReferenceTypeInfo(ReferenceTypeInfo::Create( - return_handle, return_handle->CannotBeAssignedFromOtherTypes() /* is_exact */)); + mirror::Class* cls = resolved_method->GetReturnType(false /* resolve */, pointer_size); + if (cls != nullptr) { + ReferenceTypeInfo::TypeHandle return_handle = handles_->NewHandle(cls); + return_replacement->SetReferenceTypeInfo(ReferenceTypeInfo::Create( + return_handle, return_handle->CannotBeAssignedFromOtherTypes() /* is_exact */)); + } else { + return_replacement->SetReferenceTypeInfo(graph_->GetInexactObjectRti()); + } } if (do_rtp) { diff --git a/compiler/optimizing/instruction_simplifier.cc b/compiler/optimizing/instruction_simplifier.cc index 05ee10981f..1249b48e1e 100644 --- a/compiler/optimizing/instruction_simplifier.cc +++ b/compiler/optimizing/instruction_simplifier.cc @@ -34,8 +34,12 @@ class InstructionSimplifierVisitor : public HGraphDelegateVisitor { void RecordSimplification() { simplification_occurred_ = true; simplifications_at_current_position_++; - if (stats_) { - stats_->RecordStat(kInstructionSimplifications); + MaybeRecordStat(kInstructionSimplifications); + } + + void MaybeRecordStat(MethodCompilationStat stat) { + if (stats_ != nullptr) { + stats_->RecordStat(stat); } } @@ -52,7 +56,6 @@ class InstructionSimplifierVisitor : public HGraphDelegateVisitor { bool TryDeMorganNegationFactoring(HBinaryOperation* op); void VisitShift(HBinaryOperation* shift); - void VisitSuspendCheck(HSuspendCheck* check) OVERRIDE; void VisitEqual(HEqual* equal) OVERRIDE; void VisitNotEqual(HNotEqual* equal) OVERRIDE; void VisitBooleanNot(HBooleanNot* bool_not) OVERRIDE; @@ -92,10 +95,10 @@ class InstructionSimplifierVisitor : public HGraphDelegateVisitor { bool CanEnsureNotNullAt(HInstruction* instr, HInstruction* at) const; - void SimplifyRotate(HInvoke* invoke, bool is_left); + void SimplifyRotate(HInvoke* invoke, bool is_left, Primitive::Type type); void SimplifySystemArrayCopy(HInvoke* invoke); void SimplifyStringEquals(HInvoke* invoke); - void SimplifyCompare(HInvoke* invoke, bool has_zero_op); + void SimplifyCompare(HInvoke* invoke, bool is_signum, Primitive::Type type); void SimplifyIsNaN(HInvoke* invoke); void SimplifyFP2Int(HInvoke* invoke); void SimplifyMemBarrier(HInvoke* invoke, MemBarrierKind barrier_kind); @@ -236,8 +239,9 @@ void InstructionSimplifierVisitor::VisitShift(HBinaryOperation* instruction) { if (input_cst != nullptr) { int64_t cst = Int64FromConstant(input_cst); - int64_t mask = - (input_other->GetType() == Primitive::kPrimLong) ? kMaxLongShiftValue : kMaxIntShiftValue; + int64_t mask = (input_other->GetType() == Primitive::kPrimLong) + ? kMaxLongShiftDistance + : kMaxIntShiftDistance; if ((cst & mask) == 0) { // Replace code looking like // SHL dst, src, 0 @@ -258,10 +262,8 @@ static bool IsSubRegBitsMinusOther(HSub* sub, size_t reg_bits, HInstruction* oth bool InstructionSimplifierVisitor::ReplaceRotateWithRor(HBinaryOperation* op, HUShr* ushr, HShl* shl) { - DCHECK(op->IsAdd() || op->IsXor() || op->IsOr()); - HRor* ror = new (GetGraph()->GetArena()) HRor(ushr->GetType(), - ushr->GetLeft(), - ushr->GetRight()); + DCHECK(op->IsAdd() || op->IsXor() || op->IsOr()) << op->DebugName(); + HRor* ror = new (GetGraph()->GetArena()) HRor(ushr->GetType(), ushr->GetLeft(), ushr->GetRight()); op->GetBlock()->ReplaceAndRemoveInstructionWith(op, ror); if (!ushr->HasUses()) { ushr->GetBlock()->RemoveInstruction(ushr); @@ -466,9 +468,7 @@ void InstructionSimplifierVisitor::VisitCheckCast(HCheckCast* check_cast) { if (object->IsNullConstant()) { check_cast->GetBlock()->RemoveInstruction(check_cast); - if (stats_ != nullptr) { - stats_->RecordStat(MethodCompilationStat::kRemovedCheckedCast); - } + MaybeRecordStat(MethodCompilationStat::kRemovedCheckedCast); return; } @@ -478,9 +478,7 @@ void InstructionSimplifierVisitor::VisitCheckCast(HCheckCast* check_cast) { if (TypeCheckHasKnownOutcome(load_class, object, &outcome)) { if (outcome) { check_cast->GetBlock()->RemoveInstruction(check_cast); - if (stats_ != nullptr) { - stats_->RecordStat(MethodCompilationStat::kRemovedCheckedCast); - } + MaybeRecordStat(MethodCompilationStat::kRemovedCheckedCast); if (!load_class->HasUses()) { // We cannot rely on DCE to remove the class because the `HLoadClass` thinks it can throw. // However, here we know that it cannot because the checkcast was successfull, hence @@ -510,6 +508,7 @@ void InstructionSimplifierVisitor::VisitInstanceOf(HInstanceOf* instruction) { HGraph* graph = GetGraph(); if (object->IsNullConstant()) { + MaybeRecordStat(kRemovedInstanceOf); instruction->ReplaceWith(graph->GetIntConstant(0)); instruction->GetBlock()->RemoveInstruction(instruction); RecordSimplification(); @@ -520,6 +519,7 @@ void InstructionSimplifierVisitor::VisitInstanceOf(HInstanceOf* instruction) { // the return value check with the `outcome` check, b/27651442 . bool outcome = false; if (TypeCheckHasKnownOutcome(load_class, object, &outcome)) { + MaybeRecordStat(kRemovedInstanceOf); if (outcome && can_be_null) { // Type test will succeed, we just need a null test. HNotEqual* test = new (graph->GetArena()) HNotEqual(graph->GetNullConstant(), object); @@ -554,22 +554,6 @@ void InstructionSimplifierVisitor::VisitStaticFieldSet(HStaticFieldSet* instruct } } -void InstructionSimplifierVisitor::VisitSuspendCheck(HSuspendCheck* check) { - HBasicBlock* block = check->GetBlock(); - // Currently always keep the suspend check at entry. - if (block->IsEntryBlock()) return; - - // Currently always keep suspend checks at loop entry. - if (block->IsLoopHeader() && block->GetFirstInstruction() == check) { - DCHECK(block->GetLoopInformation()->GetSuspendCheck() == check); - return; - } - - // Remove the suspend check that was added at build time for the baseline - // compiler. - block->RemoveInstruction(check); -} - static HCondition* GetOppositeConditionSwapOps(ArenaAllocator* arena, HInstruction* cond) { HInstruction *lhs = cond->InputAt(0); HInstruction *rhs = cond->InputAt(1); @@ -1230,7 +1214,7 @@ void InstructionSimplifierVisitor::VisitMul(HMul* instruction) { // with // SHL dst, src, log2(pow_of_2) HIntConstant* shift = GetGraph()->GetIntConstant(WhichPowerOf2(factor)); - HShl* shl = new(allocator) HShl(type, input_other, shift); + HShl* shl = new (allocator) HShl(type, input_other, shift); block->ReplaceAndRemoveInstructionWith(instruction, shl); RecordSimplification(); } else if (IsPowerOfTwo(factor - 1)) { @@ -1529,17 +1513,22 @@ void InstructionSimplifierVisitor::SimplifyStringEquals(HInvoke* instruction) { } } -void InstructionSimplifierVisitor::SimplifyRotate(HInvoke* invoke, bool is_left) { +void InstructionSimplifierVisitor::SimplifyRotate(HInvoke* invoke, + bool is_left, + Primitive::Type type) { DCHECK(invoke->IsInvokeStaticOrDirect()); DCHECK_EQ(invoke->GetOriginalInvokeType(), InvokeType::kStatic); HInstruction* value = invoke->InputAt(0); HInstruction* distance = invoke->InputAt(1); // Replace the invoke with an HRor. if (is_left) { - distance = new (GetGraph()->GetArena()) HNeg(distance->GetType(), distance); + // Unconditionally set the type of the negated distance to `int`, + // as shift and rotate operations expect a 32-bit (or narrower) + // value for their distance input. + distance = new (GetGraph()->GetArena()) HNeg(Primitive::kPrimInt, distance); invoke->GetBlock()->InsertInstructionBefore(distance, invoke); } - HRor* ror = new (GetGraph()->GetArena()) HRor(value->GetType(), value, distance); + HRor* ror = new (GetGraph()->GetArena()) HRor(type, value, distance); invoke->GetBlock()->ReplaceAndRemoveInstructionWith(invoke, ror); // Remove ClinitCheck and LoadClass, if possible. HInstruction* clinit = invoke->InputAt(invoke->InputCount() - 1); @@ -1617,12 +1606,13 @@ void InstructionSimplifierVisitor::SimplifySystemArrayCopy(HInvoke* instruction) } } -void InstructionSimplifierVisitor::SimplifyCompare(HInvoke* invoke, bool is_signum) { +void InstructionSimplifierVisitor::SimplifyCompare(HInvoke* invoke, + bool is_signum, + Primitive::Type type) { DCHECK(invoke->IsInvokeStaticOrDirect()); uint32_t dex_pc = invoke->GetDexPc(); HInstruction* left = invoke->InputAt(0); HInstruction* right; - Primitive::Type type = left->GetType(); if (!is_signum) { right = invoke->InputAt(1); } else if (type == Primitive::kPrimLong) { @@ -1691,20 +1681,28 @@ void InstructionSimplifierVisitor::VisitInvoke(HInvoke* instruction) { SimplifySystemArrayCopy(instruction); break; case Intrinsics::kIntegerRotateRight: + SimplifyRotate(instruction, /* is_left */ false, Primitive::kPrimInt); + break; case Intrinsics::kLongRotateRight: - SimplifyRotate(instruction, false); + SimplifyRotate(instruction, /* is_left */ false, Primitive::kPrimLong); break; case Intrinsics::kIntegerRotateLeft: + SimplifyRotate(instruction, /* is_left */ true, Primitive::kPrimInt); + break; case Intrinsics::kLongRotateLeft: - SimplifyRotate(instruction, true); + SimplifyRotate(instruction, /* is_left */ true, Primitive::kPrimLong); break; case Intrinsics::kIntegerCompare: + SimplifyCompare(instruction, /* is_signum */ false, Primitive::kPrimInt); + break; case Intrinsics::kLongCompare: - SimplifyCompare(instruction, /* is_signum */ false); + SimplifyCompare(instruction, /* is_signum */ false, Primitive::kPrimLong); break; case Intrinsics::kIntegerSignum: + SimplifyCompare(instruction, /* is_signum */ true, Primitive::kPrimInt); + break; case Intrinsics::kLongSignum: - SimplifyCompare(instruction, /* is_signum */ true); + SimplifyCompare(instruction, /* is_signum */ true, Primitive::kPrimLong); break; case Intrinsics::kFloatIsNaN: case Intrinsics::kDoubleIsNaN: diff --git a/compiler/optimizing/intrinsics_mips.cc b/compiler/optimizing/intrinsics_mips.cc index c306cf93a1..1280587276 100644 --- a/compiler/optimizing/intrinsics_mips.cc +++ b/compiler/optimizing/intrinsics_mips.cc @@ -1475,6 +1475,404 @@ void IntrinsicCodeGeneratorMIPS::VisitThreadCurrentThread(HInvoke* invoke) { Thread::PeerOffset<kMipsPointerSize>().Int32Value()); } +static void CreateIntIntIntToIntLocations(ArenaAllocator* arena, HInvoke* invoke) { + bool can_call = + invoke->GetIntrinsic() == Intrinsics::kUnsafeGetObject || + invoke->GetIntrinsic() == Intrinsics::kUnsafeGetObjectVolatile; + LocationSummary* locations = new (arena) LocationSummary(invoke, + can_call ? + LocationSummary::kCallOnSlowPath : + LocationSummary::kNoCall, + kIntrinsified); + locations->SetInAt(0, Location::NoLocation()); // Unused receiver. + locations->SetInAt(1, Location::RequiresRegister()); + locations->SetInAt(2, Location::RequiresRegister()); + locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap); +} + +static void GenUnsafeGet(HInvoke* invoke, + Primitive::Type type, + bool is_volatile, + bool is_R6, + CodeGeneratorMIPS* codegen) { + LocationSummary* locations = invoke->GetLocations(); + DCHECK((type == Primitive::kPrimInt) || + (type == Primitive::kPrimLong) || + (type == Primitive::kPrimNot)) << type; + MipsAssembler* assembler = codegen->GetAssembler(); + // Object pointer. + Register base = locations->InAt(1).AsRegister<Register>(); + // The "offset" argument is passed as a "long". Since this code is for + // a 32-bit processor, we can only use 32-bit addresses, so we only + // need the low 32-bits of offset. + Register offset_lo = invoke->GetLocations()->InAt(2).AsRegisterPairLow<Register>(); + + __ Addu(TMP, base, offset_lo); + if (is_volatile) { + __ Sync(0); + } + if (type == Primitive::kPrimLong) { + Register trg_lo = locations->Out().AsRegisterPairLow<Register>(); + Register trg_hi = locations->Out().AsRegisterPairHigh<Register>(); + + if (is_R6) { + __ Lw(trg_lo, TMP, 0); + __ Lw(trg_hi, TMP, 4); + } else { + __ Lwr(trg_lo, TMP, 0); + __ Lwl(trg_lo, TMP, 3); + __ Lwr(trg_hi, TMP, 4); + __ Lwl(trg_hi, TMP, 7); + } + } else { + Register trg = locations->Out().AsRegister<Register>(); + + if (is_R6) { + __ Lw(trg, TMP, 0); + } else { + __ Lwr(trg, TMP, 0); + __ Lwl(trg, TMP, 3); + } + } +} + +// int sun.misc.Unsafe.getInt(Object o, long offset) +void IntrinsicLocationsBuilderMIPS::VisitUnsafeGet(HInvoke* invoke) { + CreateIntIntIntToIntLocations(arena_, invoke); +} + +void IntrinsicCodeGeneratorMIPS::VisitUnsafeGet(HInvoke* invoke) { + GenUnsafeGet(invoke, Primitive::kPrimInt, /* is_volatile */ false, IsR6(), codegen_); +} + +// int sun.misc.Unsafe.getIntVolatile(Object o, long offset) +void IntrinsicLocationsBuilderMIPS::VisitUnsafeGetVolatile(HInvoke* invoke) { + CreateIntIntIntToIntLocations(arena_, invoke); +} + +void IntrinsicCodeGeneratorMIPS::VisitUnsafeGetVolatile(HInvoke* invoke) { + GenUnsafeGet(invoke, Primitive::kPrimInt, /* is_volatile */ true, IsR6(), codegen_); +} + +// long sun.misc.Unsafe.getLong(Object o, long offset) +void IntrinsicLocationsBuilderMIPS::VisitUnsafeGetLong(HInvoke* invoke) { + CreateIntIntIntToIntLocations(arena_, invoke); +} + +void IntrinsicCodeGeneratorMIPS::VisitUnsafeGetLong(HInvoke* invoke) { + GenUnsafeGet(invoke, Primitive::kPrimLong, /* is_volatile */ false, IsR6(), codegen_); +} + +// long sun.misc.Unsafe.getLongVolatile(Object o, long offset) +void IntrinsicLocationsBuilderMIPS::VisitUnsafeGetLongVolatile(HInvoke* invoke) { + CreateIntIntIntToIntLocations(arena_, invoke); +} + +void IntrinsicCodeGeneratorMIPS::VisitUnsafeGetLongVolatile(HInvoke* invoke) { + GenUnsafeGet(invoke, Primitive::kPrimLong, /* is_volatile */ true, IsR6(), codegen_); +} + +// Object sun.misc.Unsafe.getObject(Object o, long offset) +void IntrinsicLocationsBuilderMIPS::VisitUnsafeGetObject(HInvoke* invoke) { + CreateIntIntIntToIntLocations(arena_, invoke); +} + +void IntrinsicCodeGeneratorMIPS::VisitUnsafeGetObject(HInvoke* invoke) { + GenUnsafeGet(invoke, Primitive::kPrimNot, /* is_volatile */ false, IsR6(), codegen_); +} + +// Object sun.misc.Unsafe.getObjectVolatile(Object o, long offset) +void IntrinsicLocationsBuilderMIPS::VisitUnsafeGetObjectVolatile(HInvoke* invoke) { + CreateIntIntIntToIntLocations(arena_, invoke); +} + +void IntrinsicCodeGeneratorMIPS::VisitUnsafeGetObjectVolatile(HInvoke* invoke) { + GenUnsafeGet(invoke, Primitive::kPrimNot, /* is_volatile */ true, IsR6(), codegen_); +} + +static void CreateIntIntIntIntToVoidLocations(ArenaAllocator* arena, HInvoke* invoke) { + LocationSummary* locations = new (arena) LocationSummary(invoke, + LocationSummary::kNoCall, + kIntrinsified); + locations->SetInAt(0, Location::NoLocation()); // Unused receiver. + locations->SetInAt(1, Location::RequiresRegister()); + locations->SetInAt(2, Location::RequiresRegister()); + locations->SetInAt(3, Location::RequiresRegister()); +} + +static void GenUnsafePut(LocationSummary* locations, + Primitive::Type type, + bool is_volatile, + bool is_ordered, + bool is_R6, + CodeGeneratorMIPS* codegen) { + DCHECK((type == Primitive::kPrimInt) || + (type == Primitive::kPrimLong) || + (type == Primitive::kPrimNot)) << type; + MipsAssembler* assembler = codegen->GetAssembler(); + // Object pointer. + Register base = locations->InAt(1).AsRegister<Register>(); + // The "offset" argument is passed as a "long", i.e., it's 64-bits in + // size. Since this code is for a 32-bit processor, we can only use + // 32-bit addresses, so we only need the low 32-bits of offset. + Register offset_lo = locations->InAt(2).AsRegisterPairLow<Register>(); + + __ Addu(TMP, base, offset_lo); + if (is_volatile || is_ordered) { + __ Sync(0); + } + if ((type == Primitive::kPrimInt) || (type == Primitive::kPrimNot)) { + Register value = locations->InAt(3).AsRegister<Register>(); + + if (is_R6) { + __ Sw(value, TMP, 0); + } else { + __ Swr(value, TMP, 0); + __ Swl(value, TMP, 3); + } + } else { + Register value_lo = locations->InAt(3).AsRegisterPairLow<Register>(); + Register value_hi = locations->InAt(3).AsRegisterPairHigh<Register>(); + + if (is_R6) { + __ Sw(value_lo, TMP, 0); + __ Sw(value_hi, TMP, 4); + } else { + __ Swr(value_lo, TMP, 0); + __ Swl(value_lo, TMP, 3); + __ Swr(value_hi, TMP, 4); + __ Swl(value_hi, TMP, 7); + } + } + + if (is_volatile) { + __ Sync(0); + } + + if (type == Primitive::kPrimNot) { + codegen->MarkGCCard(base, locations->InAt(3).AsRegister<Register>()); + } +} + +// void sun.misc.Unsafe.putInt(Object o, long offset, int x) +void IntrinsicLocationsBuilderMIPS::VisitUnsafePut(HInvoke* invoke) { + CreateIntIntIntIntToVoidLocations(arena_, invoke); +} + +void IntrinsicCodeGeneratorMIPS::VisitUnsafePut(HInvoke* invoke) { + GenUnsafePut(invoke->GetLocations(), + Primitive::kPrimInt, + /* is_volatile */ false, + /* is_ordered */ false, + IsR6(), + codegen_); +} + +// void sun.misc.Unsafe.putOrderedInt(Object o, long offset, int x) +void IntrinsicLocationsBuilderMIPS::VisitUnsafePutOrdered(HInvoke* invoke) { + CreateIntIntIntIntToVoidLocations(arena_, invoke); +} + +void IntrinsicCodeGeneratorMIPS::VisitUnsafePutOrdered(HInvoke* invoke) { + GenUnsafePut(invoke->GetLocations(), + Primitive::kPrimInt, + /* is_volatile */ false, + /* is_ordered */ true, + IsR6(), + codegen_); +} + +// void sun.misc.Unsafe.putIntVolatile(Object o, long offset, int x) +void IntrinsicLocationsBuilderMIPS::VisitUnsafePutVolatile(HInvoke* invoke) { + CreateIntIntIntIntToVoidLocations(arena_, invoke); +} + +void IntrinsicCodeGeneratorMIPS::VisitUnsafePutVolatile(HInvoke* invoke) { + GenUnsafePut(invoke->GetLocations(), + Primitive::kPrimInt, + /* is_volatile */ true, + /* is_ordered */ false, + IsR6(), + codegen_); +} + +// void sun.misc.Unsafe.putObject(Object o, long offset, Object x) +void IntrinsicLocationsBuilderMIPS::VisitUnsafePutObject(HInvoke* invoke) { + CreateIntIntIntIntToVoidLocations(arena_, invoke); +} + +void IntrinsicCodeGeneratorMIPS::VisitUnsafePutObject(HInvoke* invoke) { + GenUnsafePut(invoke->GetLocations(), + Primitive::kPrimNot, + /* is_volatile */ false, + /* is_ordered */ false, + IsR6(), + codegen_); +} + +// void sun.misc.Unsafe.putOrderedObject(Object o, long offset, Object x) +void IntrinsicLocationsBuilderMIPS::VisitUnsafePutObjectOrdered(HInvoke* invoke) { + CreateIntIntIntIntToVoidLocations(arena_, invoke); +} + +void IntrinsicCodeGeneratorMIPS::VisitUnsafePutObjectOrdered(HInvoke* invoke) { + GenUnsafePut(invoke->GetLocations(), + Primitive::kPrimNot, + /* is_volatile */ false, + /* is_ordered */ true, + IsR6(), + codegen_); +} + +// void sun.misc.Unsafe.putObjectVolatile(Object o, long offset, Object x) +void IntrinsicLocationsBuilderMIPS::VisitUnsafePutObjectVolatile(HInvoke* invoke) { + CreateIntIntIntIntToVoidLocations(arena_, invoke); +} + +void IntrinsicCodeGeneratorMIPS::VisitUnsafePutObjectVolatile(HInvoke* invoke) { + GenUnsafePut(invoke->GetLocations(), + Primitive::kPrimNot, + /* is_volatile */ true, + /* is_ordered */ false, + IsR6(), + codegen_); +} + +// void sun.misc.Unsafe.putLong(Object o, long offset, long x) +void IntrinsicLocationsBuilderMIPS::VisitUnsafePutLong(HInvoke* invoke) { + CreateIntIntIntIntToVoidLocations(arena_, invoke); +} + +void IntrinsicCodeGeneratorMIPS::VisitUnsafePutLong(HInvoke* invoke) { + GenUnsafePut(invoke->GetLocations(), + Primitive::kPrimLong, + /* is_volatile */ false, + /* is_ordered */ false, + IsR6(), + codegen_); +} + +// void sun.misc.Unsafe.putOrderedLong(Object o, long offset, long x) +void IntrinsicLocationsBuilderMIPS::VisitUnsafePutLongOrdered(HInvoke* invoke) { + CreateIntIntIntIntToVoidLocations(arena_, invoke); +} + +void IntrinsicCodeGeneratorMIPS::VisitUnsafePutLongOrdered(HInvoke* invoke) { + GenUnsafePut(invoke->GetLocations(), + Primitive::kPrimLong, + /* is_volatile */ false, + /* is_ordered */ true, + IsR6(), + codegen_); +} + +// void sun.misc.Unsafe.putLongVolatile(Object o, long offset, long x) +void IntrinsicLocationsBuilderMIPS::VisitUnsafePutLongVolatile(HInvoke* invoke) { + CreateIntIntIntIntToVoidLocations(arena_, invoke); +} + +void IntrinsicCodeGeneratorMIPS::VisitUnsafePutLongVolatile(HInvoke* invoke) { + GenUnsafePut(invoke->GetLocations(), + Primitive::kPrimLong, + /* is_volatile */ true, + /* is_ordered */ false, + IsR6(), + codegen_); +} + +static void CreateIntIntIntIntIntToIntLocations(ArenaAllocator* arena, HInvoke* invoke) { + LocationSummary* locations = new (arena) LocationSummary(invoke, + LocationSummary::kNoCall, + kIntrinsified); + locations->SetInAt(0, Location::NoLocation()); // Unused receiver. + locations->SetInAt(1, Location::RequiresRegister()); + locations->SetInAt(2, Location::RequiresRegister()); + locations->SetInAt(3, Location::RequiresRegister()); + locations->SetInAt(4, Location::RequiresRegister()); + + locations->SetOut(Location::RequiresRegister()); +} + +static void GenCas(LocationSummary* locations, Primitive::Type type, CodeGeneratorMIPS* codegen) { + MipsAssembler* assembler = codegen->GetAssembler(); + bool isR6 = codegen->GetInstructionSetFeatures().IsR6(); + Register base = locations->InAt(1).AsRegister<Register>(); + Register offset_lo = locations->InAt(2).AsRegisterPairLow<Register>(); + Register expected = locations->InAt(3).AsRegister<Register>(); + Register value = locations->InAt(4).AsRegister<Register>(); + Register out = locations->Out().AsRegister<Register>(); + + DCHECK_NE(base, out); + DCHECK_NE(offset_lo, out); + DCHECK_NE(expected, out); + + if (type == Primitive::kPrimNot) { + // Mark card for object assuming new value is stored. + codegen->MarkGCCard(base, value); + } + + // do { + // tmp_value = [tmp_ptr] - expected; + // } while (tmp_value == 0 && failure([tmp_ptr] <- r_new_value)); + // result = tmp_value != 0; + + MipsLabel loop_head, exit_loop; + __ Addu(TMP, base, offset_lo); + __ Sync(0); + __ Bind(&loop_head); + if ((type == Primitive::kPrimInt) || (type == Primitive::kPrimNot)) { + if (isR6) { + __ LlR6(out, TMP); + } else { + __ LlR2(out, TMP); + } + } else { + LOG(FATAL) << "Unsupported op size " << type; + UNREACHABLE(); + } + __ Subu(out, out, expected); // If we didn't get the 'expected' + __ Sltiu(out, out, 1); // value, set 'out' to false, and + __ Beqz(out, &exit_loop); // return. + __ Move(out, value); // Use 'out' for the 'store conditional' instruction. + // If we use 'value' directly, we would lose 'value' + // in the case that the store fails. Whether the + // store succeeds, or fails, it will load the + // correct boolean value into the 'out' register. + // This test isn't really necessary. We only support Primitive::kPrimInt, + // Primitive::kPrimNot, and we already verified that we're working on one + // of those two types. It's left here in case the code needs to support + // other types in the future. + if ((type == Primitive::kPrimInt) || (type == Primitive::kPrimNot)) { + if (isR6) { + __ ScR6(out, TMP); + } else { + __ ScR2(out, TMP); + } + } + __ Beqz(out, &loop_head); // If we couldn't do the read-modify-write + // cycle atomically then retry. + __ Bind(&exit_loop); + __ Sync(0); +} + +// boolean sun.misc.Unsafe.compareAndSwapInt(Object o, long offset, int expected, int x) +void IntrinsicLocationsBuilderMIPS::VisitUnsafeCASInt(HInvoke* invoke) { + CreateIntIntIntIntIntToIntLocations(arena_, invoke); +} + +void IntrinsicCodeGeneratorMIPS::VisitUnsafeCASInt(HInvoke* invoke) { + GenCas(invoke->GetLocations(), Primitive::kPrimInt, codegen_); +} + +// boolean sun.misc.Unsafe.compareAndSwapObject(Object o, long offset, Object expected, Object x) +void IntrinsicLocationsBuilderMIPS::VisitUnsafeCASObject(HInvoke* invoke) { + CreateIntIntIntIntIntToIntLocations(arena_, invoke); +} + +void IntrinsicCodeGeneratorMIPS::VisitUnsafeCASObject(HInvoke* invoke) { + GenCas(invoke->GetLocations(), Primitive::kPrimNot, codegen_); +} + // char java.lang.String.charAt(int index) void IntrinsicLocationsBuilderMIPS::VisitStringCharAt(HInvoke* invoke) { LocationSummary* locations = new (arena_) LocationSummary(invoke, @@ -1482,7 +1880,7 @@ void IntrinsicLocationsBuilderMIPS::VisitStringCharAt(HInvoke* invoke) { kIntrinsified); locations->SetInAt(0, Location::RequiresRegister()); locations->SetInAt(1, Location::RequiresRegister()); - // The inputs will be considered live at the last instruction and restored. This will overwrite + // The inputs will be considered live at the last instruction and restored. This would overwrite // the output with kNoOutputOverlap. locations->SetOut(Location::RequiresRegister(), Location::kOutputOverlap); } @@ -2042,24 +2440,7 @@ UNIMPLEMENTED_INTRINSIC(MIPS, MathFloor) UNIMPLEMENTED_INTRINSIC(MIPS, MathRint) UNIMPLEMENTED_INTRINSIC(MIPS, MathRoundDouble) UNIMPLEMENTED_INTRINSIC(MIPS, MathRoundFloat) -UNIMPLEMENTED_INTRINSIC(MIPS, UnsafeGet) -UNIMPLEMENTED_INTRINSIC(MIPS, UnsafeGetVolatile) -UNIMPLEMENTED_INTRINSIC(MIPS, UnsafeGetLong) -UNIMPLEMENTED_INTRINSIC(MIPS, UnsafeGetLongVolatile) -UNIMPLEMENTED_INTRINSIC(MIPS, UnsafeGetObject) -UNIMPLEMENTED_INTRINSIC(MIPS, UnsafeGetObjectVolatile) -UNIMPLEMENTED_INTRINSIC(MIPS, UnsafePut) -UNIMPLEMENTED_INTRINSIC(MIPS, UnsafePutOrdered) -UNIMPLEMENTED_INTRINSIC(MIPS, UnsafePutVolatile) -UNIMPLEMENTED_INTRINSIC(MIPS, UnsafePutObject) -UNIMPLEMENTED_INTRINSIC(MIPS, UnsafePutObjectOrdered) -UNIMPLEMENTED_INTRINSIC(MIPS, UnsafePutObjectVolatile) -UNIMPLEMENTED_INTRINSIC(MIPS, UnsafePutLong) -UNIMPLEMENTED_INTRINSIC(MIPS, UnsafePutLongOrdered) -UNIMPLEMENTED_INTRINSIC(MIPS, UnsafePutLongVolatile) -UNIMPLEMENTED_INTRINSIC(MIPS, UnsafeCASInt) UNIMPLEMENTED_INTRINSIC(MIPS, UnsafeCASLong) -UNIMPLEMENTED_INTRINSIC(MIPS, UnsafeCASObject) UNIMPLEMENTED_INTRINSIC(MIPS, ReferenceGetReferent) UNIMPLEMENTED_INTRINSIC(MIPS, StringGetCharsNoCheck) diff --git a/compiler/optimizing/licm.cc b/compiler/optimizing/licm.cc index 33bb2e8f30..7a1e06b951 100644 --- a/compiler/optimizing/licm.cc +++ b/compiler/optimizing/licm.cc @@ -80,7 +80,7 @@ static void UpdateLoopPhisIn(HEnvironment* environment, HLoopInformation* info) void LICM::Run() { DCHECK(side_effects_.HasRun()); // Only used during debug. - ArenaBitVector visited(graph_->GetArena(), graph_->GetBlocks().size(), false); + ArenaBitVector visited(graph_->GetArena(), graph_->GetBlocks().size(), false, kArenaAllocLICM); // Post order visit to visit inner loops before outer loops. for (HPostOrderIterator it(*graph_); !it.Done(); it.Advance()) { diff --git a/compiler/optimizing/load_store_elimination.cc b/compiler/optimizing/load_store_elimination.cc index 8eaac0bbd3..e1977b1798 100644 --- a/compiler/optimizing/load_store_elimination.cc +++ b/compiler/optimizing/load_store_elimination.cc @@ -186,7 +186,10 @@ class HeapLocationCollector : public HGraphVisitor { : HGraphVisitor(graph), ref_info_array_(graph->GetArena()->Adapter(kArenaAllocLSE)), heap_locations_(graph->GetArena()->Adapter(kArenaAllocLSE)), - aliasing_matrix_(graph->GetArena(), kInitialAliasingMatrixBitVectorSize, true), + aliasing_matrix_(graph->GetArena(), + kInitialAliasingMatrixBitVectorSize, + true, + kArenaAllocLSE), has_heap_stores_(false), has_volatile_(false), has_monitor_operations_(false), @@ -728,6 +731,25 @@ class LSEVisitor : public HGraphVisitor { // This acts like GVN but with better aliasing analysis. heap_values[idx] = instruction; } else { + if (Primitive::PrimitiveKind(heap_value->GetType()) + != Primitive::PrimitiveKind(instruction->GetType())) { + // The only situation where the same heap location has different type is when + // we do an array get from a null constant. In order to stay properly typed + // we do not merge the array gets. + if (kIsDebugBuild) { + DCHECK(heap_value->IsArrayGet()) << heap_value->DebugName(); + DCHECK(instruction->IsArrayGet()) << instruction->DebugName(); + HInstruction* array = instruction->AsArrayGet()->GetArray(); + DCHECK(array->IsNullCheck()) << array->DebugName(); + HInstruction* input = HuntForOriginalReference(array->InputAt(0)); + DCHECK(input->IsNullConstant()) << input->DebugName(); + array = heap_value->AsArrayGet()->GetArray(); + DCHECK(array->IsNullCheck()) << array->DebugName(); + input = HuntForOriginalReference(array->InputAt(0)); + DCHECK(input->IsNullConstant()) << input->DebugName(); + } + return; + } removed_loads_.push_back(instruction); substitute_instructions_for_loads_.push_back(heap_value); TryRemovingNullCheck(instruction); diff --git a/compiler/optimizing/locations.cc b/compiler/optimizing/locations.cc index 1ab206f69e..83596da41a 100644 --- a/compiler/optimizing/locations.cc +++ b/compiler/optimizing/locations.cc @@ -37,7 +37,7 @@ LocationSummary::LocationSummary(HInstruction* instruction, if (NeedsSafepoint()) { ArenaAllocator* arena = instruction->GetBlock()->GetGraph()->GetArena(); - stack_mask_ = new (arena) ArenaBitVector(arena, 0, true); + stack_mask_ = ArenaBitVector::Create(arena, 0, true, kArenaAllocLocationSummary); } } diff --git a/compiler/optimizing/nodes.cc b/compiler/optimizing/nodes.cc index 6a8c813ea6..05bb901976 100644 --- a/compiler/optimizing/nodes.cc +++ b/compiler/optimizing/nodes.cc @@ -54,7 +54,7 @@ void HGraph::FindBackEdges(ArenaBitVector* visited) { DCHECK_EQ(visited->GetHighestBitSet(), -1); // Nodes that we're currently visiting, indexed by block id. - ArenaBitVector visiting(arena_, blocks_.size(), false); + ArenaBitVector visiting(arena_, blocks_.size(), false, kArenaAllocGraphBuilder); // Number of successors visited from a given node, indexed by block id. ArenaVector<size_t> successors_visited(blocks_.size(), 0u, arena_->Adapter()); // Stack of nodes that we're currently visiting (same as marked in "visiting" above). @@ -140,7 +140,7 @@ GraphAnalysisResult HGraph::BuildDominatorTree() { // collect both normal- and exceptional-flow values at the same time. SimplifyCatchBlocks(); - ArenaBitVector visited(arena_, blocks_.size(), false); + ArenaBitVector visited(arena_, blocks_.size(), false, kArenaAllocGraphBuilder); // (2) Find the back edges in the graph doing a DFS traversal. FindBackEdges(&visited); @@ -162,7 +162,7 @@ GraphAnalysisResult HGraph::BuildDominatorTree() { // (6) Compute the dominance information and the reverse post order. ComputeDominanceInformation(); - // (7) Analyze loops discover through back edge analysis, and + // (7) Analyze loops discovered through back edge analysis, and // set the loop information on each block. GraphAnalysisResult result = AnalyzeLoops(); if (result != kAnalysisSuccess) { @@ -247,7 +247,7 @@ void HGraph::ComputeDominanceInformation() { } // Populate `dominated_blocks_` information after computing all dominators. - // The potential presence of irreducible loops require to do it after. + // The potential presence of irreducible loops requires to do it after. for (HReversePostOrderIterator it(*this); !it.Done(); it.Advance()) { HBasicBlock* block = it.Current(); if (!block->IsEntryBlock()) { @@ -460,7 +460,7 @@ void HGraph::SimplifyCFG() { if (block->IsLoopHeader()) { SimplifyLoop(block); } else if (!block->IsEntryBlock() && block->GetFirstInstruction()->IsSuspendCheck()) { - // We are being called by the dead code elimiation pass, and what used to be + // We are being called by the dead code elimination pass, and what used to be // a loop got dismantled. Just remove the suspend check. block->RemoveInstruction(block->GetFirstInstruction()); } @@ -2373,7 +2373,7 @@ void HInstruction::RemoveEnvironmentUsers() { env_uses_.Clear(); } -// Returns an instruction with the opposite boolean value from 'cond'. +// Returns an instruction with the opposite Boolean value from 'cond'. HInstruction* HGraph::InsertOppositeCondition(HInstruction* cond, HInstruction* cursor) { ArenaAllocator* allocator = GetArena(); diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h index 88d39d16e5..e9a42cb0ce 100644 --- a/compiler/optimizing/nodes.h +++ b/compiler/optimizing/nodes.h @@ -72,8 +72,10 @@ static const int kDefaultNumberOfExceptionalPredecessors = 0; static const int kDefaultNumberOfDominatedBlocks = 1; static const int kDefaultNumberOfBackEdges = 1; -static constexpr uint32_t kMaxIntShiftValue = 0x1f; -static constexpr uint64_t kMaxLongShiftValue = 0x3f; +// The maximum (meaningful) distance (31) that can be used in an integer shift/rotate operation. +static constexpr int32_t kMaxIntShiftDistance = 0x1f; +// The maximum (meaningful) distance (63) that can be used in a long shift/rotate operation. +static constexpr int32_t kMaxLongShiftDistance = 0x3f; static constexpr uint32_t kUnknownFieldIndex = static_cast<uint32_t>(-1); static constexpr uint16_t kUnknownClassDefIndex = static_cast<uint16_t>(-1); @@ -645,7 +647,7 @@ class HLoopInformation : public ArenaObject<kArenaAllocLoopInfo> { irreducible_(false), back_edges_(graph->GetArena()->Adapter(kArenaAllocLoopInfoBackEdges)), // Make bit vector growable, as the number of blocks may change. - blocks_(graph->GetArena(), graph->GetBlocks().size(), true) { + blocks_(graph->GetArena(), graph->GetBlocks().size(), true, kArenaAllocLoopInfoBackEdges) { back_edges_.reserve(kDefaultNumberOfBackEdges); } @@ -3461,7 +3463,10 @@ class HAboveOrEqual : public HCondition { // Result is 0 if input0 == input1, 1 if input0 > input1, or -1 if input0 < input1. class HCompare : public HBinaryOperation { public: - HCompare(Primitive::Type type, + // Note that `comparison_type` is the type of comparison performed + // between the comparison's inputs, not the type of the instantiated + // HCompare instruction (which is always Primitive::kPrimInt). + HCompare(Primitive::Type comparison_type, HInstruction* first, HInstruction* second, ComparisonBias bias, @@ -3469,11 +3474,11 @@ class HCompare : public HBinaryOperation { : HBinaryOperation(Primitive::kPrimInt, first, second, - SideEffectsForArchRuntimeCalls(type), + SideEffectsForArchRuntimeCalls(comparison_type), dex_pc) { SetPackedField<ComparisonBiasField>(bias); - DCHECK_EQ(type, first->GetType()); - DCHECK_EQ(type, second->GetType()); + DCHECK_EQ(comparison_type, Primitive::PrimitiveKind(first->GetType())); + DCHECK_EQ(comparison_type, Primitive::PrimitiveKind(second->GetType())); } template <typename T> @@ -4176,7 +4181,9 @@ class HInvokeInterface : public HInvoke { class HNeg : public HUnaryOperation { public: HNeg(Primitive::Type result_type, HInstruction* input, uint32_t dex_pc = kNoDexPc) - : HUnaryOperation(result_type, input, dex_pc) {} + : HUnaryOperation(result_type, input, dex_pc) { + DCHECK_EQ(result_type, Primitive::PrimitiveKind(input->GetType())); + } template <typename T> T Compute(T x) const { return -x; } @@ -4473,37 +4480,39 @@ class HDivZeroCheck : public HExpression<1> { class HShl : public HBinaryOperation { public: HShl(Primitive::Type result_type, - HInstruction* left, - HInstruction* right, + HInstruction* value, + HInstruction* distance, uint32_t dex_pc = kNoDexPc) - : HBinaryOperation(result_type, left, right, SideEffects::None(), dex_pc) {} + : HBinaryOperation(result_type, value, distance, SideEffects::None(), dex_pc) { + DCHECK_EQ(result_type, Primitive::PrimitiveKind(value->GetType())); + DCHECK_EQ(Primitive::kPrimInt, Primitive::PrimitiveKind(distance->GetType())); + } - template <typename T, typename U, typename V> - T Compute(T x, U y, V max_shift_value) const { - static_assert(std::is_same<V, typename std::make_unsigned<T>::type>::value, - "V is not the unsigned integer type corresponding to T"); - return x << (y & max_shift_value); + template <typename T> + T Compute(T value, int32_t distance, int32_t max_shift_distance) const { + return value << (distance & max_shift_distance); } - HConstant* Evaluate(HIntConstant* x, HIntConstant* y) const OVERRIDE { + HConstant* Evaluate(HIntConstant* value, HIntConstant* distance) const OVERRIDE { return GetBlock()->GetGraph()->GetIntConstant( - Compute(x->GetValue(), y->GetValue(), kMaxIntShiftValue), GetDexPc()); + Compute(value->GetValue(), distance->GetValue(), kMaxIntShiftDistance), GetDexPc()); } - HConstant* Evaluate(HLongConstant* x, HIntConstant* y) const OVERRIDE { + HConstant* Evaluate(HLongConstant* value, HIntConstant* distance) const OVERRIDE { return GetBlock()->GetGraph()->GetLongConstant( - Compute(x->GetValue(), y->GetValue(), kMaxLongShiftValue), GetDexPc()); + Compute(value->GetValue(), distance->GetValue(), kMaxLongShiftDistance), GetDexPc()); } - HConstant* Evaluate(HLongConstant* x, HLongConstant* y) const OVERRIDE { - return GetBlock()->GetGraph()->GetLongConstant( - Compute(x->GetValue(), y->GetValue(), kMaxLongShiftValue), GetDexPc()); + HConstant* Evaluate(HLongConstant* value ATTRIBUTE_UNUSED, + HLongConstant* distance ATTRIBUTE_UNUSED) const OVERRIDE { + LOG(FATAL) << DebugName() << " is not defined for the (long, long) case."; + UNREACHABLE(); } - HConstant* Evaluate(HFloatConstant* x ATTRIBUTE_UNUSED, - HFloatConstant* y ATTRIBUTE_UNUSED) const OVERRIDE { + HConstant* Evaluate(HFloatConstant* value ATTRIBUTE_UNUSED, + HFloatConstant* distance ATTRIBUTE_UNUSED) const OVERRIDE { LOG(FATAL) << DebugName() << " is not defined for float values"; UNREACHABLE(); } - HConstant* Evaluate(HDoubleConstant* x ATTRIBUTE_UNUSED, - HDoubleConstant* y ATTRIBUTE_UNUSED) const OVERRIDE { + HConstant* Evaluate(HDoubleConstant* value ATTRIBUTE_UNUSED, + HDoubleConstant* distance ATTRIBUTE_UNUSED) const OVERRIDE { LOG(FATAL) << DebugName() << " is not defined for double values"; UNREACHABLE(); } @@ -4517,37 +4526,39 @@ class HShl : public HBinaryOperation { class HShr : public HBinaryOperation { public: HShr(Primitive::Type result_type, - HInstruction* left, - HInstruction* right, + HInstruction* value, + HInstruction* distance, uint32_t dex_pc = kNoDexPc) - : HBinaryOperation(result_type, left, right, SideEffects::None(), dex_pc) {} + : HBinaryOperation(result_type, value, distance, SideEffects::None(), dex_pc) { + DCHECK_EQ(result_type, Primitive::PrimitiveKind(value->GetType())); + DCHECK_EQ(Primitive::kPrimInt, Primitive::PrimitiveKind(distance->GetType())); + } - template <typename T, typename U, typename V> - T Compute(T x, U y, V max_shift_value) const { - static_assert(std::is_same<V, typename std::make_unsigned<T>::type>::value, - "V is not the unsigned integer type corresponding to T"); - return x >> (y & max_shift_value); + template <typename T> + T Compute(T value, int32_t distance, int32_t max_shift_distance) const { + return value >> (distance & max_shift_distance); } - HConstant* Evaluate(HIntConstant* x, HIntConstant* y) const OVERRIDE { + HConstant* Evaluate(HIntConstant* value, HIntConstant* distance) const OVERRIDE { return GetBlock()->GetGraph()->GetIntConstant( - Compute(x->GetValue(), y->GetValue(), kMaxIntShiftValue), GetDexPc()); + Compute(value->GetValue(), distance->GetValue(), kMaxIntShiftDistance), GetDexPc()); } - HConstant* Evaluate(HLongConstant* x, HIntConstant* y) const OVERRIDE { + HConstant* Evaluate(HLongConstant* value, HIntConstant* distance) const OVERRIDE { return GetBlock()->GetGraph()->GetLongConstant( - Compute(x->GetValue(), y->GetValue(), kMaxLongShiftValue), GetDexPc()); + Compute(value->GetValue(), distance->GetValue(), kMaxLongShiftDistance), GetDexPc()); } - HConstant* Evaluate(HLongConstant* x, HLongConstant* y) const OVERRIDE { - return GetBlock()->GetGraph()->GetLongConstant( - Compute(x->GetValue(), y->GetValue(), kMaxLongShiftValue), GetDexPc()); + HConstant* Evaluate(HLongConstant* value ATTRIBUTE_UNUSED, + HLongConstant* distance ATTRIBUTE_UNUSED) const OVERRIDE { + LOG(FATAL) << DebugName() << " is not defined for the (long, long) case."; + UNREACHABLE(); } - HConstant* Evaluate(HFloatConstant* x ATTRIBUTE_UNUSED, - HFloatConstant* y ATTRIBUTE_UNUSED) const OVERRIDE { + HConstant* Evaluate(HFloatConstant* value ATTRIBUTE_UNUSED, + HFloatConstant* distance ATTRIBUTE_UNUSED) const OVERRIDE { LOG(FATAL) << DebugName() << " is not defined for float values"; UNREACHABLE(); } - HConstant* Evaluate(HDoubleConstant* x ATTRIBUTE_UNUSED, - HDoubleConstant* y ATTRIBUTE_UNUSED) const OVERRIDE { + HConstant* Evaluate(HDoubleConstant* value ATTRIBUTE_UNUSED, + HDoubleConstant* distance ATTRIBUTE_UNUSED) const OVERRIDE { LOG(FATAL) << DebugName() << " is not defined for double values"; UNREACHABLE(); } @@ -4561,38 +4572,41 @@ class HShr : public HBinaryOperation { class HUShr : public HBinaryOperation { public: HUShr(Primitive::Type result_type, - HInstruction* left, - HInstruction* right, + HInstruction* value, + HInstruction* distance, uint32_t dex_pc = kNoDexPc) - : HBinaryOperation(result_type, left, right, SideEffects::None(), dex_pc) {} + : HBinaryOperation(result_type, value, distance, SideEffects::None(), dex_pc) { + DCHECK_EQ(result_type, Primitive::PrimitiveKind(value->GetType())); + DCHECK_EQ(Primitive::kPrimInt, Primitive::PrimitiveKind(distance->GetType())); + } - template <typename T, typename U, typename V> - T Compute(T x, U y, V max_shift_value) const { - static_assert(std::is_same<V, typename std::make_unsigned<T>::type>::value, - "V is not the unsigned integer type corresponding to T"); - V ux = static_cast<V>(x); - return static_cast<T>(ux >> (y & max_shift_value)); + template <typename T> + T Compute(T value, int32_t distance, int32_t max_shift_distance) const { + typedef typename std::make_unsigned<T>::type V; + V ux = static_cast<V>(value); + return static_cast<T>(ux >> (distance & max_shift_distance)); } - HConstant* Evaluate(HIntConstant* x, HIntConstant* y) const OVERRIDE { + HConstant* Evaluate(HIntConstant* value, HIntConstant* distance) const OVERRIDE { return GetBlock()->GetGraph()->GetIntConstant( - Compute(x->GetValue(), y->GetValue(), kMaxIntShiftValue), GetDexPc()); + Compute(value->GetValue(), distance->GetValue(), kMaxIntShiftDistance), GetDexPc()); } - HConstant* Evaluate(HLongConstant* x, HIntConstant* y) const OVERRIDE { + HConstant* Evaluate(HLongConstant* value, HIntConstant* distance) const OVERRIDE { return GetBlock()->GetGraph()->GetLongConstant( - Compute(x->GetValue(), y->GetValue(), kMaxLongShiftValue), GetDexPc()); + Compute(value->GetValue(), distance->GetValue(), kMaxLongShiftDistance), GetDexPc()); } - HConstant* Evaluate(HLongConstant* x, HLongConstant* y) const OVERRIDE { - return GetBlock()->GetGraph()->GetLongConstant( - Compute(x->GetValue(), y->GetValue(), kMaxLongShiftValue), GetDexPc()); + HConstant* Evaluate(HLongConstant* value ATTRIBUTE_UNUSED, + HLongConstant* distance ATTRIBUTE_UNUSED) const OVERRIDE { + LOG(FATAL) << DebugName() << " is not defined for the (long, long) case."; + UNREACHABLE(); } - HConstant* Evaluate(HFloatConstant* x ATTRIBUTE_UNUSED, - HFloatConstant* y ATTRIBUTE_UNUSED) const OVERRIDE { + HConstant* Evaluate(HFloatConstant* value ATTRIBUTE_UNUSED, + HFloatConstant* distance ATTRIBUTE_UNUSED) const OVERRIDE { LOG(FATAL) << DebugName() << " is not defined for float values"; UNREACHABLE(); } - HConstant* Evaluate(HDoubleConstant* x ATTRIBUTE_UNUSED, - HDoubleConstant* y ATTRIBUTE_UNUSED) const OVERRIDE { + HConstant* Evaluate(HDoubleConstant* value ATTRIBUTE_UNUSED, + HDoubleConstant* distance ATTRIBUTE_UNUSED) const OVERRIDE { LOG(FATAL) << DebugName() << " is not defined for double values"; UNREACHABLE(); } @@ -4717,41 +4731,44 @@ class HXor : public HBinaryOperation { class HRor : public HBinaryOperation { public: HRor(Primitive::Type result_type, HInstruction* value, HInstruction* distance) - : HBinaryOperation(result_type, value, distance) {} - - template <typename T, typename U, typename V> - T Compute(T x, U y, V max_shift_value) const { - static_assert(std::is_same<V, typename std::make_unsigned<T>::type>::value, - "V is not the unsigned integer type corresponding to T"); - V ux = static_cast<V>(x); - if ((y & max_shift_value) == 0) { + : HBinaryOperation(result_type, value, distance) { + DCHECK_EQ(result_type, Primitive::PrimitiveKind(value->GetType())); + DCHECK_EQ(Primitive::kPrimInt, Primitive::PrimitiveKind(distance->GetType())); + } + + template <typename T> + T Compute(T value, int32_t distance, int32_t max_shift_value) const { + typedef typename std::make_unsigned<T>::type V; + V ux = static_cast<V>(value); + if ((distance & max_shift_value) == 0) { return static_cast<T>(ux); } else { const V reg_bits = sizeof(T) * 8; - return static_cast<T>(ux >> (y & max_shift_value)) | - (x << (reg_bits - (y & max_shift_value))); + return static_cast<T>(ux >> (distance & max_shift_value)) | + (value << (reg_bits - (distance & max_shift_value))); } } - HConstant* Evaluate(HIntConstant* x, HIntConstant* y) const OVERRIDE { + HConstant* Evaluate(HIntConstant* value, HIntConstant* distance) const OVERRIDE { return GetBlock()->GetGraph()->GetIntConstant( - Compute(x->GetValue(), y->GetValue(), kMaxIntShiftValue), GetDexPc()); + Compute(value->GetValue(), distance->GetValue(), kMaxIntShiftDistance), GetDexPc()); } - HConstant* Evaluate(HLongConstant* x, HIntConstant* y) const OVERRIDE { + HConstant* Evaluate(HLongConstant* value, HIntConstant* distance) const OVERRIDE { return GetBlock()->GetGraph()->GetLongConstant( - Compute(x->GetValue(), y->GetValue(), kMaxLongShiftValue), GetDexPc()); + Compute(value->GetValue(), distance->GetValue(), kMaxLongShiftDistance), GetDexPc()); } - HConstant* Evaluate(HLongConstant* x, HLongConstant* y) const OVERRIDE { - return GetBlock()->GetGraph()->GetLongConstant( - Compute(x->GetValue(), y->GetValue(), kMaxLongShiftValue), GetDexPc()); + HConstant* Evaluate(HLongConstant* value ATTRIBUTE_UNUSED, + HLongConstant* distance ATTRIBUTE_UNUSED) const OVERRIDE { + LOG(FATAL) << DebugName() << " is not defined for the (long, long) case."; + UNREACHABLE(); } - HConstant* Evaluate(HFloatConstant* x ATTRIBUTE_UNUSED, - HFloatConstant* y ATTRIBUTE_UNUSED) const OVERRIDE { + HConstant* Evaluate(HFloatConstant* value ATTRIBUTE_UNUSED, + HFloatConstant* distance ATTRIBUTE_UNUSED) const OVERRIDE { LOG(FATAL) << DebugName() << " is not defined for float values"; UNREACHABLE(); } - HConstant* Evaluate(HDoubleConstant* x ATTRIBUTE_UNUSED, - HDoubleConstant* y ATTRIBUTE_UNUSED) const OVERRIDE { + HConstant* Evaluate(HDoubleConstant* value ATTRIBUTE_UNUSED, + HDoubleConstant* distance ATTRIBUTE_UNUSED) const OVERRIDE { LOG(FATAL) << DebugName() << " is not defined for double values"; UNREACHABLE(); } diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc index 53bd38d0ef..125c00d275 100644 --- a/compiler/optimizing/optimizing_compiler.cc +++ b/compiler/optimizing/optimizing_compiler.cc @@ -84,6 +84,8 @@ namespace art { +static constexpr size_t kArenaAllocatorMemoryReportThreshold = 8 * MB; + /** * Used by the code generator, to allocate the code in a vector. */ @@ -653,7 +655,7 @@ CodeGenerator* OptimizingCompiler::TryCompile(ArenaAllocator* arena, // code units is bigger than 128. static constexpr size_t kSpaceFilterOptimizingThreshold = 128; const CompilerOptions& compiler_options = compiler_driver->GetCompilerOptions(); - if ((compiler_options.GetCompilerFilter() == CompilerOptions::kSpace) + if ((compiler_options.GetCompilerFilter() == CompilerFilter::kSpace) && (code_item->insns_size_in_code_units_ > kSpaceFilterOptimizingThreshold)) { MaybeRecordStat(MethodCompilationStat::kNotCompiledSpaceFilter); return nullptr; @@ -698,7 +700,8 @@ CodeGenerator* OptimizingCompiler::TryCompile(ArenaAllocator* arena, CodeGenerator::Create(graph, instruction_set, *compiler_driver->GetInstructionSetFeatures(), - compiler_driver->GetCompilerOptions())); + compiler_driver->GetCompilerOptions(), + compilation_stats_.get())); if (codegen.get() == nullptr) { MaybeRecordStat(MethodCompilationStat::kNotCompiledNoCodegen); return nullptr; @@ -760,13 +763,6 @@ CodeGenerator* OptimizingCompiler::TryCompile(ArenaAllocator* arena, pass_observer.DumpDisassembly(); } - if (kArenaAllocatorCountAllocations) { - if (arena->BytesAllocated() > 4 * MB) { - MemStats mem_stats(arena->GetMemStats()); - LOG(INFO) << PrettyMethod(method_idx, dex_file) << " " << Dumpable<MemStats>(mem_stats); - } - } - return codegen.release(); } @@ -811,6 +807,13 @@ CompiledMethod* OptimizingCompiler::Compile(const DexFile::CodeItem* code_item, if (codegen.get() != nullptr) { MaybeRecordStat(MethodCompilationStat::kCompiled); method = Emit(&arena, &code_allocator, codegen.get(), compiler_driver, code_item); + + if (kArenaAllocatorCountAllocations) { + if (arena.BytesAllocated() > kArenaAllocatorMemoryReportThreshold) { + MemStats mem_stats(arena.GetMemStats()); + LOG(INFO) << PrettyMethod(method_idx, dex_file) << " " << Dumpable<MemStats>(mem_stats); + } + } } } else { if (compiler_driver->GetCompilerOptions().VerifyAtRuntime()) { @@ -889,10 +892,17 @@ bool OptimizingCompiler::JitCompile(Thread* self, if (codegen.get() == nullptr) { return false; } + + if (kArenaAllocatorCountAllocations) { + if (arena.BytesAllocated() > kArenaAllocatorMemoryReportThreshold) { + MemStats mem_stats(arena.GetMemStats()); + LOG(INFO) << PrettyMethod(method_idx, *dex_file) << " " << Dumpable<MemStats>(mem_stats); + } + } } size_t stack_map_size = codegen->ComputeStackMapsSize(); - uint8_t* stack_map_data = code_cache->ReserveData(self, stack_map_size); + uint8_t* stack_map_data = code_cache->ReserveData(self, stack_map_size, method); if (stack_map_data == nullptr) { return false; } diff --git a/compiler/optimizing/optimizing_compiler_stats.h b/compiler/optimizing/optimizing_compiler_stats.h index 179004bd40..3717926a97 100644 --- a/compiler/optimizing/optimizing_compiler_stats.h +++ b/compiler/optimizing/optimizing_compiler_stats.h @@ -60,6 +60,10 @@ enum MethodCompilationStat { kIntrinsicRecognized, kLoopInvariantMoved, kSelectGenerated, + kRemovedInstanceOf, + kInlinedInvokeVirtualOrInterface, + kImplicitNullCheckGenerated, + kExplicitNullCheckGenerated, kLastStat }; @@ -133,6 +137,10 @@ class OptimizingCompilerStats { case kIntrinsicRecognized : name = "IntrinsicRecognized"; break; case kLoopInvariantMoved : name = "LoopInvariantMoved"; break; case kSelectGenerated : name = "SelectGenerated"; break; + case kRemovedInstanceOf: name = "RemovedInstanceOf"; break; + case kInlinedInvokeVirtualOrInterface: name = "InlinedInvokeVirtualOrInterface"; break; + case kImplicitNullCheckGenerated: name = "ImplicitNullCheckGenerated"; break; + case kExplicitNullCheckGenerated: name = "ExplicitNullCheckGenerated"; break; case kLastStat: LOG(FATAL) << "invalid stat " diff --git a/compiler/optimizing/optimizing_unit_test.h b/compiler/optimizing/optimizing_unit_test.h index 0c7648edc2..0ca7305d13 100644 --- a/compiler/optimizing/optimizing_unit_test.h +++ b/compiler/optimizing/optimizing_unit_test.h @@ -20,7 +20,6 @@ #include "nodes.h" #include "builder.h" #include "common_compiler_test.h" -#include "compiler/dex/pass_manager.h" #include "dex_file.h" #include "dex_instruction.h" #include "handle_scope-inl.h" diff --git a/compiler/optimizing/prepare_for_register_allocation.cc b/compiler/optimizing/prepare_for_register_allocation.cc index 0ad104eaa7..fc72727196 100644 --- a/compiler/optimizing/prepare_for_register_allocation.cc +++ b/compiler/optimizing/prepare_for_register_allocation.cc @@ -47,6 +47,19 @@ void PrepareForRegisterAllocation::VisitBoundType(HBoundType* bound_type) { bound_type->GetBlock()->RemoveInstruction(bound_type); } +void PrepareForRegisterAllocation::VisitArraySet(HArraySet* instruction) { + HInstruction* value = instruction->GetValue(); + // PrepareForRegisterAllocation::VisitBoundType may have replaced a + // BoundType (as value input of this ArraySet) with a NullConstant. + // If so, this ArraySet no longer needs a type check. + if (value->IsNullConstant()) { + DCHECK_EQ(value->GetType(), Primitive::kPrimNot); + if (instruction->NeedsTypeCheck()) { + instruction->ClearNeedsTypeCheck(); + } + } +} + void PrepareForRegisterAllocation::VisitClinitCheck(HClinitCheck* check) { // Try to find a static invoke or a new-instance from which this check originated. HInstruction* implicit_clinit = nullptr; diff --git a/compiler/optimizing/prepare_for_register_allocation.h b/compiler/optimizing/prepare_for_register_allocation.h index c90724c251..a6791482a7 100644 --- a/compiler/optimizing/prepare_for_register_allocation.h +++ b/compiler/optimizing/prepare_for_register_allocation.h @@ -40,6 +40,7 @@ class PrepareForRegisterAllocation : public HGraphDelegateVisitor { void VisitDivZeroCheck(HDivZeroCheck* check) OVERRIDE; void VisitBoundsCheck(HBoundsCheck* check) OVERRIDE; void VisitBoundType(HBoundType* bound_type) OVERRIDE; + void VisitArraySet(HArraySet* instruction) OVERRIDE; void VisitClinitCheck(HClinitCheck* check) OVERRIDE; void VisitCondition(HCondition* condition) OVERRIDE; void VisitInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) OVERRIDE; diff --git a/compiler/optimizing/pretty_printer_test.cc b/compiler/optimizing/pretty_printer_test.cc index 2de0c1be72..d5b95d284a 100644 --- a/compiler/optimizing/pretty_printer_test.cc +++ b/compiler/optimizing/pretty_printer_test.cc @@ -135,13 +135,13 @@ TEST_F(PrettyPrinterTest, CFG3) { TEST_F(PrettyPrinterTest, CFG4) { const char* expected = "BasicBlock 0, succ: 3\n" - " 3: SuspendCheck\n" - " 4: Goto 3\n" + " 2: SuspendCheck\n" + " 3: Goto 3\n" "BasicBlock 1, pred: 3, 1, succ: 1\n" - " 0: SuspendCheck\n" - " 1: Goto 1\n" + " 5: SuspendCheck\n" + " 0: Goto 1\n" "BasicBlock 3, pred: 0, succ: 1\n" - " 5: Goto 1\n"; + " 4: Goto 1\n"; const uint16_t data1[] = ZERO_REGISTER_CODE_ITEM( Instruction::NOP, @@ -204,20 +204,20 @@ TEST_F(PrettyPrinterTest, CFG7) { const char* expected = "BasicBlock 0, succ: 1\n" " 1: IntConstant [5, 5]\n" - " 11: SuspendCheck\n" - " 12: Goto 1\n" + " 10: SuspendCheck\n" + " 11: Goto 1\n" "BasicBlock 1, pred: 0, succ: 5, 6\n" " 5: Equal(1, 1) [6]\n" " 6: If(5)\n" "BasicBlock 2, pred: 6, 3, succ: 3\n" " 7: Goto 3\n" "BasicBlock 3, pred: 5, 2, succ: 2\n" - " 8: SuspendCheck\n" - " 9: Goto 2\n" + " 14: SuspendCheck\n" + " 8: Goto 2\n" "BasicBlock 5, pred: 1, succ: 3\n" - " 13: Goto 3\n" + " 12: Goto 3\n" "BasicBlock 6, pred: 1, succ: 2\n" - " 14: Goto 2\n"; + " 13: Goto 2\n"; const uint16_t data[] = ONE_REGISTER_CODE_ITEM( Instruction::CONST_4 | 0 | 0, diff --git a/compiler/optimizing/register_allocator.cc b/compiler/optimizing/register_allocator.cc index b8d76b912e..b1f9cbcdfa 100644 --- a/compiler/optimizing/register_allocator.cc +++ b/compiler/optimizing/register_allocator.cc @@ -445,7 +445,7 @@ class AllRangesIterator : public ValueObject { bool RegisterAllocator::ValidateInternal(bool log_fatal_on_failure) const { // To simplify unit testing, we eagerly create the array of intervals, and // call the helper method. - ArenaVector<LiveInterval*> intervals(allocator_->Adapter(kArenaAllocRegisterAllocator)); + ArenaVector<LiveInterval*> intervals(allocator_->Adapter(kArenaAllocRegisterAllocatorValidate)); for (size_t i = 0; i < liveness_.GetNumberOfSsaValues(); ++i) { HInstruction* instruction = liveness_.GetInstructionFromSsaIndex(i); if (ShouldProcess(processing_core_registers_, instruction->GetLiveInterval())) { @@ -483,13 +483,21 @@ bool RegisterAllocator::ValidateIntervals(const ArenaVector<LiveInterval*>& inte ? codegen.GetNumberOfCoreRegisters() : codegen.GetNumberOfFloatingPointRegisters(); ArenaVector<ArenaBitVector*> liveness_of_values( - allocator->Adapter(kArenaAllocRegisterAllocator)); + allocator->Adapter(kArenaAllocRegisterAllocatorValidate)); liveness_of_values.reserve(number_of_registers + number_of_spill_slots); + size_t max_end = 0u; + for (LiveInterval* start_interval : intervals) { + for (AllRangesIterator it(start_interval); !it.Done(); it.Advance()) { + max_end = std::max(max_end, it.CurrentRange()->GetEnd()); + } + } + // Allocate a bit vector per register. A live interval that has a register // allocated will populate the associated bit vector based on its live ranges. for (size_t i = 0; i < number_of_registers + number_of_spill_slots; ++i) { - liveness_of_values.push_back(new (allocator) ArenaBitVector(allocator, 0, true)); + liveness_of_values.push_back( + ArenaBitVector::Create(allocator, max_end, false, kArenaAllocRegisterAllocatorValidate)); } for (LiveInterval* start_interval : intervals) { @@ -1919,7 +1927,13 @@ void RegisterAllocator::Resolve() { BitVector* live = liveness_.GetLiveInSet(*block); for (uint32_t idx : live->Indexes()) { LiveInterval* interval = liveness_.GetInstructionFromSsaIndex(idx)->GetLiveInterval(); - DCHECK(!interval->GetSiblingAt(block->GetLifetimeStart())->HasRegister()); + LiveInterval* sibling = interval->GetSiblingAt(block->GetLifetimeStart()); + // `GetSiblingAt` returns the sibling that contains a position, but there could be + // a lifetime hole in it. `CoversSlow` returns whether the interval is live at that + // position. + if ((sibling != nullptr) && sibling->CoversSlow(block->GetLifetimeStart())) { + DCHECK(!sibling->HasRegister()); + } } } } else { diff --git a/compiler/optimizing/ssa_liveness_analysis.h b/compiler/optimizing/ssa_liveness_analysis.h index a78aedcff5..97f2aeeb1e 100644 --- a/compiler/optimizing/ssa_liveness_analysis.h +++ b/compiler/optimizing/ssa_liveness_analysis.h @@ -31,9 +31,9 @@ class BlockInfo : public ArenaObject<kArenaAllocSsaLiveness> { public: BlockInfo(ArenaAllocator* allocator, const HBasicBlock& block, size_t number_of_ssa_values) : block_(block), - live_in_(allocator, number_of_ssa_values, false), - live_out_(allocator, number_of_ssa_values, false), - kill_(allocator, number_of_ssa_values, false) { + live_in_(allocator, number_of_ssa_values, false, kArenaAllocSsaLiveness), + live_out_(allocator, number_of_ssa_values, false, kArenaAllocSsaLiveness), + kill_(allocator, number_of_ssa_values, false, kArenaAllocSsaLiveness) { UNUSED(block_); live_in_.ClearAllBits(); live_out_.ClearAllBits(); diff --git a/compiler/optimizing/stack_map_stream.cc b/compiler/optimizing/stack_map_stream.cc index 54cbdf8b66..3f41e3594e 100644 --- a/compiler/optimizing/stack_map_stream.cc +++ b/compiler/optimizing/stack_map_stream.cc @@ -37,7 +37,7 @@ void StackMapStream::BeginStackMapEntry(uint32_t dex_pc, current_entry_.same_dex_register_map_as_ = kNoSameDexMapFound; if (num_dex_registers != 0) { current_entry_.live_dex_registers_mask = - new (allocator_) ArenaBitVector(allocator_, num_dex_registers, true); + ArenaBitVector::Create(allocator_, num_dex_registers, true, kArenaAllocStackMapStream); } else { current_entry_.live_dex_registers_mask = nullptr; } @@ -111,7 +111,7 @@ void StackMapStream::BeginInlineInfoEntry(uint32_t method_index, current_inline_info_.dex_register_locations_start_index = dex_register_locations_.size(); if (num_dex_registers != 0) { current_inline_info_.live_dex_registers_mask = - new (allocator_) ArenaBitVector(allocator_, num_dex_registers, true); + ArenaBitVector::Create(allocator_, num_dex_registers, true, kArenaAllocStackMapStream); } else { current_inline_info_.live_dex_registers_mask = nullptr; } @@ -256,7 +256,7 @@ void StackMapStream::FillIn(MemoryRegion region) { // Ensure we reached the end of the Dex registers location_catalog. DCHECK_EQ(location_catalog_offset, dex_register_location_catalog_region.size()); - ArenaBitVector empty_bitmask(allocator_, 0, /* expandable */ false); + ArenaBitVector empty_bitmask(allocator_, 0, /* expandable */ false, kArenaAllocStackMapStream); uintptr_t next_dex_register_map_offset = 0; uintptr_t next_inline_info_offset = 0; for (size_t i = 0, e = stack_maps_.size(); i < e; ++i) { |