summaryrefslogtreecommitdiff
path: root/compiler/optimizing
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/optimizing')
-rw-r--r--compiler/optimizing/builder.cc28
-rw-r--r--compiler/optimizing/builder.h1
-rw-r--r--compiler/optimizing/code_generator.cc27
-rw-r--r--compiler/optimizing/code_generator.h11
-rw-r--r--compiler/optimizing/code_generator_arm.cc55
-rw-r--r--compiler/optimizing/code_generator_arm.h8
-rw-r--r--compiler/optimizing/code_generator_arm64.cc38
-rw-r--r--compiler/optimizing/code_generator_arm64.h5
-rw-r--r--compiler/optimizing/code_generator_mips.cc33
-rw-r--r--compiler/optimizing/code_generator_mips.h4
-rw-r--r--compiler/optimizing/code_generator_mips64.cc43
-rw-r--r--compiler/optimizing/code_generator_mips64.h4
-rw-r--r--compiler/optimizing/code_generator_x86.cc38
-rw-r--r--compiler/optimizing/code_generator_x86.h4
-rw-r--r--compiler/optimizing/code_generator_x86_64.cc36
-rw-r--r--compiler/optimizing/code_generator_x86_64.h4
-rw-r--r--compiler/optimizing/common_arm64.h6
-rw-r--r--compiler/optimizing/constant_folding_test.cc51
-rw-r--r--compiler/optimizing/dead_code_elimination.cc2
-rw-r--r--compiler/optimizing/dead_code_elimination_test.cc36
-rw-r--r--compiler/optimizing/graph_checker.cc159
-rw-r--r--compiler/optimizing/graph_checker.h6
-rw-r--r--compiler/optimizing/graph_visualizer.cc2
-rw-r--r--compiler/optimizing/gvn.cc4
-rw-r--r--compiler/optimizing/induction_var_analysis.cc176
-rw-r--r--compiler/optimizing/induction_var_analysis.h32
-rw-r--r--compiler/optimizing/induction_var_analysis_test.cc175
-rw-r--r--compiler/optimizing/induction_var_range.cc111
-rw-r--r--compiler/optimizing/induction_var_range_test.cc17
-rw-r--r--compiler/optimizing/inliner.cc22
-rw-r--r--compiler/optimizing/instruction_simplifier.cc84
-rw-r--r--compiler/optimizing/intrinsics_mips.cc417
-rw-r--r--compiler/optimizing/licm.cc2
-rw-r--r--compiler/optimizing/load_store_elimination.cc24
-rw-r--r--compiler/optimizing/locations.cc2
-rw-r--r--compiler/optimizing/nodes.cc12
-rw-r--r--compiler/optimizing/nodes.h191
-rw-r--r--compiler/optimizing/optimizing_compiler.cc30
-rw-r--r--compiler/optimizing/optimizing_compiler_stats.h8
-rw-r--r--compiler/optimizing/optimizing_unit_test.h1
-rw-r--r--compiler/optimizing/prepare_for_register_allocation.cc13
-rw-r--r--compiler/optimizing/prepare_for_register_allocation.h1
-rw-r--r--compiler/optimizing/pretty_printer_test.cc22
-rw-r--r--compiler/optimizing/register_allocator.cc22
-rw-r--r--compiler/optimizing/ssa_liveness_analysis.h6
-rw-r--r--compiler/optimizing/stack_map_stream.cc6
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) {