summaryrefslogtreecommitdiff
path: root/compiler/optimizing
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/optimizing')
-rw-r--r--compiler/optimizing/builder.cc8
-rw-r--r--compiler/optimizing/code_generator_arm.cc29
-rw-r--r--compiler/optimizing/code_generator_arm.h2
-rw-r--r--compiler/optimizing/code_generator_arm64.cc65
-rw-r--r--compiler/optimizing/code_generator_arm64.h2
-rw-r--r--compiler/optimizing/code_generator_mips.cc2
-rw-r--r--compiler/optimizing/code_generator_x86.cc2
-rw-r--r--compiler/optimizing/code_generator_x86_64.cc2
-rw-r--r--compiler/optimizing/graph_visualizer.cc14
-rw-r--r--compiler/optimizing/induction_var_analysis.cc106
-rw-r--r--compiler/optimizing/induction_var_analysis.h4
-rw-r--r--compiler/optimizing/induction_var_range.cc367
-rw-r--r--compiler/optimizing/induction_var_range.h25
-rw-r--r--compiler/optimizing/induction_var_range_test.cc101
-rw-r--r--compiler/optimizing/inliner.cc59
-rw-r--r--compiler/optimizing/instruction_simplifier.cc84
-rw-r--r--compiler/optimizing/instruction_simplifier_arm.cc30
-rw-r--r--compiler/optimizing/instruction_simplifier_arm.h58
-rw-r--r--compiler/optimizing/instruction_simplifier_arm64.cc174
-rw-r--r--compiler/optimizing/instruction_simplifier_arm64.h9
-rw-r--r--compiler/optimizing/instruction_simplifier_shared.cc189
-rw-r--r--compiler/optimizing/instruction_simplifier_shared.h28
-rw-r--r--compiler/optimizing/intrinsics.cc4
-rw-r--r--compiler/optimizing/intrinsics_arm.cc66
-rw-r--r--compiler/optimizing/intrinsics_arm64.cc66
-rw-r--r--compiler/optimizing/intrinsics_list.h2
-rw-r--r--compiler/optimizing/intrinsics_mips.cc2
-rw-r--r--compiler/optimizing/intrinsics_mips64.cc2
-rw-r--r--compiler/optimizing/intrinsics_x86.cc2
-rw-r--r--compiler/optimizing/intrinsics_x86_64.cc2
-rw-r--r--compiler/optimizing/nodes.cc123
-rw-r--r--compiler/optimizing/nodes.h649
-rw-r--r--compiler/optimizing/nodes_arm64.h68
-rw-r--r--compiler/optimizing/nodes_shared.h58
-rw-r--r--compiler/optimizing/optimizing_compiler.cc4
-rw-r--r--compiler/optimizing/stack_map_stream.cc98
-rw-r--r--compiler/optimizing/stack_map_stream.h7
37 files changed, 1778 insertions, 735 deletions
diff --git a/compiler/optimizing/builder.cc b/compiler/optimizing/builder.cc
index 35ec7d41ff..57660c2623 100644
--- a/compiler/optimizing/builder.cc
+++ b/compiler/optimizing/builder.cc
@@ -282,7 +282,7 @@ void HGraphBuilder::InsertTryBoundaryBlocks(const DexFile::CodeItem& code_item)
// Found a predecessor not covered by the same TryItem. Insert entering
// boundary block.
HTryBoundary* try_entry =
- new (arena_) HTryBoundary(HTryBoundary::kEntry, try_block->GetDexPc());
+ new (arena_) HTryBoundary(HTryBoundary::BoundaryKind::kEntry, try_block->GetDexPc());
try_block->CreateImmediateDominator()->AddInstruction(try_entry);
LinkToCatchBlocks(try_entry, code_item, entry.second);
break;
@@ -316,7 +316,7 @@ void HGraphBuilder::InsertTryBoundaryBlocks(const DexFile::CodeItem& code_item)
// Insert TryBoundary and link to catch blocks.
HTryBoundary* try_exit =
- new (arena_) HTryBoundary(HTryBoundary::kExit, successor->GetDexPc());
+ new (arena_) HTryBoundary(HTryBoundary::BoundaryKind::kExit, successor->GetDexPc());
graph_->SplitEdge(try_block, successor)->AddInstruction(try_exit);
LinkToCatchBlocks(try_exit, code_item, entry.second);
}
@@ -2843,7 +2843,7 @@ bool HGraphBuilder::AnalyzeDexInstruction(const Instruction& instruction, uint32
case Instruction::MONITOR_ENTER: {
current_block_->AddInstruction(new (arena_) HMonitorOperation(
LoadLocal(instruction.VRegA_11x(), Primitive::kPrimNot, dex_pc),
- HMonitorOperation::kEnter,
+ HMonitorOperation::OperationKind::kEnter,
dex_pc));
break;
}
@@ -2851,7 +2851,7 @@ bool HGraphBuilder::AnalyzeDexInstruction(const Instruction& instruction, uint32
case Instruction::MONITOR_EXIT: {
current_block_->AddInstruction(new (arena_) HMonitorOperation(
LoadLocal(instruction.VRegA_11x(), Primitive::kPrimNot, dex_pc),
- HMonitorOperation::kExit,
+ HMonitorOperation::OperationKind::kExit,
dex_pc));
break;
}
diff --git a/compiler/optimizing/code_generator_arm.cc b/compiler/optimizing/code_generator_arm.cc
index cdbb9c31aa..aa9b01f30b 100644
--- a/compiler/optimizing/code_generator_arm.cc
+++ b/compiler/optimizing/code_generator_arm.cc
@@ -6413,6 +6413,33 @@ Literal* CodeGeneratorARM::DeduplicateMethodCodeLiteral(MethodReference target_m
return DeduplicateMethodLiteral(target_method, &call_patches_);
}
+void LocationsBuilderARM::VisitMultiplyAccumulate(HMultiplyAccumulate* instr) {
+ LocationSummary* locations =
+ new (GetGraph()->GetArena()) LocationSummary(instr, LocationSummary::kNoCall);
+ locations->SetInAt(HMultiplyAccumulate::kInputAccumulatorIndex,
+ Location::RequiresRegister());
+ locations->SetInAt(HMultiplyAccumulate::kInputMulLeftIndex, Location::RequiresRegister());
+ locations->SetInAt(HMultiplyAccumulate::kInputMulRightIndex, Location::RequiresRegister());
+ locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
+}
+
+void InstructionCodeGeneratorARM::VisitMultiplyAccumulate(HMultiplyAccumulate* instr) {
+ LocationSummary* locations = instr->GetLocations();
+ Register res = locations->Out().AsRegister<Register>();
+ Register accumulator =
+ locations->InAt(HMultiplyAccumulate::kInputAccumulatorIndex).AsRegister<Register>();
+ Register mul_left =
+ locations->InAt(HMultiplyAccumulate::kInputMulLeftIndex).AsRegister<Register>();
+ Register mul_right =
+ locations->InAt(HMultiplyAccumulate::kInputMulRightIndex).AsRegister<Register>();
+
+ if (instr->GetOpKind() == HInstruction::kAdd) {
+ __ mla(res, mul_left, mul_right, accumulator);
+ } else {
+ __ mls(res, mul_left, mul_right, accumulator);
+ }
+}
+
void LocationsBuilderARM::VisitBoundType(HBoundType* instruction ATTRIBUTE_UNUSED) {
// Nothing to do, this should be removed during prepare for register allocator.
LOG(FATAL) << "Unreachable";
@@ -6567,7 +6594,7 @@ void LocationsBuilderARM::VisitClassTableGet(HClassTableGet* instruction) {
void InstructionCodeGeneratorARM::VisitClassTableGet(HClassTableGet* instruction) {
LocationSummary* locations = instruction->GetLocations();
uint32_t method_offset = 0;
- if (instruction->GetTableKind() == HClassTableGet::kVTable) {
+ if (instruction->GetTableKind() == HClassTableGet::TableKind::kVTable) {
method_offset = mirror::Class::EmbeddedVTableEntryOffset(
instruction->GetIndex(), kArmPointerSize).SizeValue();
} else {
diff --git a/compiler/optimizing/code_generator_arm.h b/compiler/optimizing/code_generator_arm.h
index 2e4dc1e014..06e7c0015c 100644
--- a/compiler/optimizing/code_generator_arm.h
+++ b/compiler/optimizing/code_generator_arm.h
@@ -159,6 +159,7 @@ class LocationsBuilderARM : public HGraphVisitor {
FOR_EACH_CONCRETE_INSTRUCTION_COMMON(DECLARE_VISIT_INSTRUCTION)
FOR_EACH_CONCRETE_INSTRUCTION_ARM(DECLARE_VISIT_INSTRUCTION)
+ FOR_EACH_CONCRETE_INSTRUCTION_SHARED(DECLARE_VISIT_INSTRUCTION)
#undef DECLARE_VISIT_INSTRUCTION
@@ -197,6 +198,7 @@ class InstructionCodeGeneratorARM : public InstructionCodeGenerator {
FOR_EACH_CONCRETE_INSTRUCTION_COMMON(DECLARE_VISIT_INSTRUCTION)
FOR_EACH_CONCRETE_INSTRUCTION_ARM(DECLARE_VISIT_INSTRUCTION)
+ FOR_EACH_CONCRETE_INSTRUCTION_SHARED(DECLARE_VISIT_INSTRUCTION)
#undef DECLARE_VISIT_INSTRUCTION
diff --git a/compiler/optimizing/code_generator_arm64.cc b/compiler/optimizing/code_generator_arm64.cc
index 814f8b4d51..985dc056f6 100644
--- a/compiler/optimizing/code_generator_arm64.cc
+++ b/compiler/optimizing/code_generator_arm64.cc
@@ -1862,6 +1862,36 @@ void InstructionCodeGeneratorARM64::VisitAnd(HAnd* instruction) {
HandleBinaryOp(instruction);
}
+void LocationsBuilderARM64::VisitArm64BitwiseNegatedRight(HArm64BitwiseNegatedRight* instr) {
+ DCHECK(Primitive::IsIntegralType(instr->GetType())) << instr->GetType();
+ LocationSummary* locations = new (GetGraph()->GetArena()) LocationSummary(instr);
+ locations->SetInAt(0, Location::RequiresRegister());
+ // There is no immediate variant of negated bitwise instructions in AArch64.
+ locations->SetInAt(1, Location::RequiresRegister());
+ locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
+}
+
+void InstructionCodeGeneratorARM64::VisitArm64BitwiseNegatedRight(
+ HArm64BitwiseNegatedRight* instr) {
+ Register dst = OutputRegister(instr);
+ Register lhs = InputRegisterAt(instr, 0);
+ Register rhs = InputRegisterAt(instr, 1);
+
+ switch (instr->GetOpKind()) {
+ case HInstruction::kAnd:
+ __ Bic(dst, lhs, rhs);
+ break;
+ case HInstruction::kOr:
+ __ Orn(dst, lhs, rhs);
+ break;
+ case HInstruction::kXor:
+ __ Eon(dst, lhs, rhs);
+ break;
+ default:
+ LOG(FATAL) << "Unreachable";
+ }
+}
+
void LocationsBuilderARM64::VisitArm64DataProcWithShifterOp(
HArm64DataProcWithShifterOp* instruction) {
DCHECK(instruction->GetType() == Primitive::kPrimInt ||
@@ -1959,21 +1989,27 @@ void InstructionCodeGeneratorARM64::VisitArm64IntermediateAddress(
Operand(InputOperandAt(instruction, 1)));
}
-void LocationsBuilderARM64::VisitArm64MultiplyAccumulate(HArm64MultiplyAccumulate* instr) {
+void LocationsBuilderARM64::VisitMultiplyAccumulate(HMultiplyAccumulate* instr) {
LocationSummary* locations =
new (GetGraph()->GetArena()) LocationSummary(instr, LocationSummary::kNoCall);
- locations->SetInAt(HArm64MultiplyAccumulate::kInputAccumulatorIndex,
- Location::RequiresRegister());
- locations->SetInAt(HArm64MultiplyAccumulate::kInputMulLeftIndex, Location::RequiresRegister());
- locations->SetInAt(HArm64MultiplyAccumulate::kInputMulRightIndex, Location::RequiresRegister());
+ HInstruction* accumulator = instr->InputAt(HMultiplyAccumulate::kInputAccumulatorIndex);
+ if (instr->GetOpKind() == HInstruction::kSub &&
+ accumulator->IsConstant() &&
+ accumulator->AsConstant()->IsZero()) {
+ // Don't allocate register for Mneg instruction.
+ } else {
+ locations->SetInAt(HMultiplyAccumulate::kInputAccumulatorIndex,
+ Location::RequiresRegister());
+ }
+ locations->SetInAt(HMultiplyAccumulate::kInputMulLeftIndex, Location::RequiresRegister());
+ locations->SetInAt(HMultiplyAccumulate::kInputMulRightIndex, Location::RequiresRegister());
locations->SetOut(Location::RequiresRegister(), Location::kNoOutputOverlap);
}
-void InstructionCodeGeneratorARM64::VisitArm64MultiplyAccumulate(HArm64MultiplyAccumulate* instr) {
+void InstructionCodeGeneratorARM64::VisitMultiplyAccumulate(HMultiplyAccumulate* instr) {
Register res = OutputRegister(instr);
- Register accumulator = InputRegisterAt(instr, HArm64MultiplyAccumulate::kInputAccumulatorIndex);
- Register mul_left = InputRegisterAt(instr, HArm64MultiplyAccumulate::kInputMulLeftIndex);
- Register mul_right = InputRegisterAt(instr, HArm64MultiplyAccumulate::kInputMulRightIndex);
+ Register mul_left = InputRegisterAt(instr, HMultiplyAccumulate::kInputMulLeftIndex);
+ Register mul_right = InputRegisterAt(instr, HMultiplyAccumulate::kInputMulRightIndex);
// Avoid emitting code that could trigger Cortex A53's erratum 835769.
// This fixup should be carried out for all multiply-accumulate instructions:
@@ -1993,10 +2029,17 @@ void InstructionCodeGeneratorARM64::VisitArm64MultiplyAccumulate(HArm64MultiplyA
}
if (instr->GetOpKind() == HInstruction::kAdd) {
+ Register accumulator = InputRegisterAt(instr, HMultiplyAccumulate::kInputAccumulatorIndex);
__ Madd(res, mul_left, mul_right, accumulator);
} else {
DCHECK(instr->GetOpKind() == HInstruction::kSub);
- __ Msub(res, mul_left, mul_right, accumulator);
+ HInstruction* accum_instr = instr->InputAt(HMultiplyAccumulate::kInputAccumulatorIndex);
+ if (accum_instr->IsConstant() && accum_instr->AsConstant()->IsZero()) {
+ __ Mneg(res, mul_left, mul_right);
+ } else {
+ Register accumulator = InputRegisterAt(instr, HMultiplyAccumulate::kInputAccumulatorIndex);
+ __ Msub(res, mul_left, mul_right, accumulator);
+ }
}
}
@@ -5017,7 +5060,7 @@ void LocationsBuilderARM64::VisitClassTableGet(HClassTableGet* instruction) {
void InstructionCodeGeneratorARM64::VisitClassTableGet(HClassTableGet* instruction) {
LocationSummary* locations = instruction->GetLocations();
uint32_t method_offset = 0;
- if (instruction->GetTableKind() == HClassTableGet::kVTable) {
+ if (instruction->GetTableKind() == HClassTableGet::TableKind::kVTable) {
method_offset = mirror::Class::EmbeddedVTableEntryOffset(
instruction->GetIndex(), kArm64PointerSize).SizeValue();
} else {
diff --git a/compiler/optimizing/code_generator_arm64.h b/compiler/optimizing/code_generator_arm64.h
index 3527261835..10f1e7f008 100644
--- a/compiler/optimizing/code_generator_arm64.h
+++ b/compiler/optimizing/code_generator_arm64.h
@@ -196,6 +196,7 @@ class InstructionCodeGeneratorARM64 : public InstructionCodeGenerator {
FOR_EACH_CONCRETE_INSTRUCTION_COMMON(DECLARE_VISIT_INSTRUCTION)
FOR_EACH_CONCRETE_INSTRUCTION_ARM64(DECLARE_VISIT_INSTRUCTION)
+ FOR_EACH_CONCRETE_INSTRUCTION_SHARED(DECLARE_VISIT_INSTRUCTION)
#undef DECLARE_VISIT_INSTRUCTION
@@ -285,6 +286,7 @@ class LocationsBuilderARM64 : public HGraphVisitor {
FOR_EACH_CONCRETE_INSTRUCTION_COMMON(DECLARE_VISIT_INSTRUCTION)
FOR_EACH_CONCRETE_INSTRUCTION_ARM64(DECLARE_VISIT_INSTRUCTION)
+ FOR_EACH_CONCRETE_INSTRUCTION_SHARED(DECLARE_VISIT_INSTRUCTION)
#undef DECLARE_VISIT_INSTRUCTION
diff --git a/compiler/optimizing/code_generator_mips.cc b/compiler/optimizing/code_generator_mips.cc
index 8d3d94b79d..f3c12efd8d 100644
--- a/compiler/optimizing/code_generator_mips.cc
+++ b/compiler/optimizing/code_generator_mips.cc
@@ -5245,7 +5245,7 @@ void LocationsBuilderMIPS::VisitClassTableGet(HClassTableGet* instruction) {
void InstructionCodeGeneratorMIPS::VisitClassTableGet(HClassTableGet* instruction) {
LocationSummary* locations = instruction->GetLocations();
uint32_t method_offset = 0;
- if (instruction->GetTableKind() == HClassTableGet::kVTable) {
+ if (instruction->GetTableKind() == HClassTableGet::TableKind::kVTable) {
method_offset = mirror::Class::EmbeddedVTableEntryOffset(
instruction->GetIndex(), kMipsPointerSize).SizeValue();
} else {
diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc
index 88e42f3faf..6b4a18c688 100644
--- a/compiler/optimizing/code_generator_x86.cc
+++ b/compiler/optimizing/code_generator_x86.cc
@@ -4127,7 +4127,7 @@ void LocationsBuilderX86::VisitClassTableGet(HClassTableGet* instruction) {
void InstructionCodeGeneratorX86::VisitClassTableGet(HClassTableGet* instruction) {
LocationSummary* locations = instruction->GetLocations();
uint32_t method_offset = 0;
- if (instruction->GetTableKind() == HClassTableGet::kVTable) {
+ if (instruction->GetTableKind() == HClassTableGet::TableKind::kVTable) {
method_offset = mirror::Class::EmbeddedVTableEntryOffset(
instruction->GetIndex(), kX86PointerSize).SizeValue();
} else {
diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc
index bb24c6f59c..c132663016 100644
--- a/compiler/optimizing/code_generator_x86_64.cc
+++ b/compiler/optimizing/code_generator_x86_64.cc
@@ -3998,7 +3998,7 @@ void LocationsBuilderX86_64::VisitClassTableGet(HClassTableGet* instruction) {
void InstructionCodeGeneratorX86_64::VisitClassTableGet(HClassTableGet* instruction) {
LocationSummary* locations = instruction->GetLocations();
uint32_t method_offset = 0;
- if (instruction->GetTableKind() == HClassTableGet::kVTable) {
+ if (instruction->GetTableKind() == HClassTableGet::TableKind::kVTable) {
method_offset = mirror::Class::EmbeddedVTableEntryOffset(
instruction->GetIndex(), kX86_64PointerSize).SizeValue();
} else {
diff --git a/compiler/optimizing/graph_visualizer.cc b/compiler/optimizing/graph_visualizer.cc
index c0263e4e5b..b9638f2027 100644
--- a/compiler/optimizing/graph_visualizer.cc
+++ b/compiler/optimizing/graph_visualizer.cc
@@ -436,17 +436,23 @@ class HGraphVisualizerPrinter : public HGraphDelegateVisitor {
StartAttributeStream("kind") << (try_boundary->IsEntry() ? "entry" : "exit");
}
+#if defined(ART_ENABLE_CODEGEN_arm) || defined(ART_ENABLE_CODEGEN_arm64)
+ void VisitMultiplyAccumulate(HMultiplyAccumulate* instruction) OVERRIDE {
+ StartAttributeStream("kind") << instruction->GetOpKind();
+ }
+#endif
+
#ifdef ART_ENABLE_CODEGEN_arm64
+ void VisitArm64BitwiseNegatedRight(HArm64BitwiseNegatedRight* instruction) OVERRIDE {
+ StartAttributeStream("kind") << instruction->GetOpKind();
+ }
+
void VisitArm64DataProcWithShifterOp(HArm64DataProcWithShifterOp* instruction) OVERRIDE {
StartAttributeStream("kind") << instruction->GetInstrKind() << "+" << instruction->GetOpKind();
if (HArm64DataProcWithShifterOp::IsShiftOp(instruction->GetOpKind())) {
StartAttributeStream("shift") << instruction->GetShiftAmount();
}
}
-
- void VisitArm64MultiplyAccumulate(HArm64MultiplyAccumulate* instruction) OVERRIDE {
- StartAttributeStream("kind") << instruction->GetOpKind();
- }
#endif
bool IsPass(const char* name) {
diff --git a/compiler/optimizing/induction_var_analysis.cc b/compiler/optimizing/induction_var_analysis.cc
index 37f2d79536..82a898a9f1 100644
--- a/compiler/optimizing/induction_var_analysis.cc
+++ b/compiler/optimizing/induction_var_analysis.cc
@@ -379,7 +379,7 @@ HInductionVarAnalysis::InductionInfo* HInductionVarAnalysis::TransferShl(Inducti
Primitive::Type type) {
// Transfer over a shift left: treat shift by restricted constant as equivalent multiplication.
int64_t value = -1;
- if (a != nullptr && IsIntAndGet(b, &value)) {
+ if (a != nullptr && IsExact(b, &value)) {
// Obtain the constant needed for the multiplication. This yields an existing instruction
// if the constants is already there. Otherwise, this has a side effect on the HIR.
// The restriction on the shift factor avoids generating a negative constant
@@ -546,14 +546,17 @@ void HInductionVarAnalysis::VisitCondition(HLoopInformation* loop,
// Analyze condition with induction at left-hand-side (e.g. i < U).
InductionInfo* lower_expr = a->op_b;
InductionInfo* upper_expr = b;
- InductionInfo* stride = a->op_a;
+ InductionInfo* stride_expr = a->op_a;
+ // Constant stride?
int64_t stride_value = 0;
- if (!IsIntAndGet(stride, &stride_value)) {
+ if (!IsExact(stride_expr, &stride_value)) {
return;
}
- // Rewrite condition i != U into i < U or i > U if end condition is reached exactly.
- if (cmp == kCondNE && ((stride_value == +1 && IsTaken(lower_expr, upper_expr, kCondLT)) ||
- (stride_value == -1 && IsTaken(lower_expr, upper_expr, kCondGT)))) {
+ // Rewrite condition i != U into strict end condition i < U or i > U if this end condition
+ // is reached exactly (tested by verifying if the loop has a unit stride and the non-strict
+ // condition would be always taken).
+ if (cmp == kCondNE && ((stride_value == +1 && IsTaken(lower_expr, upper_expr, kCondLE)) ||
+ (stride_value == -1 && IsTaken(lower_expr, upper_expr, kCondGE)))) {
cmp = stride_value > 0 ? kCondLT : kCondGT;
}
// Normalize a linear loop control with a nonzero stride:
@@ -561,7 +564,7 @@ void HInductionVarAnalysis::VisitCondition(HLoopInformation* loop,
// stride < 0, either i > U or i >= U
if ((stride_value > 0 && (cmp == kCondLT || cmp == kCondLE)) ||
(stride_value < 0 && (cmp == kCondGT || cmp == kCondGE))) {
- VisitTripCount(loop, lower_expr, upper_expr, stride, stride_value, type, cmp);
+ VisitTripCount(loop, lower_expr, upper_expr, stride_expr, stride_value, type, cmp);
}
}
}
@@ -569,7 +572,7 @@ void HInductionVarAnalysis::VisitCondition(HLoopInformation* loop,
void HInductionVarAnalysis::VisitTripCount(HLoopInformation* loop,
InductionInfo* lower_expr,
InductionInfo* upper_expr,
- InductionInfo* stride,
+ InductionInfo* stride_expr,
int64_t stride_value,
Primitive::Type type,
IfCondition cmp) {
@@ -612,9 +615,10 @@ void HInductionVarAnalysis::VisitTripCount(HLoopInformation* loop,
trip_count = CreateInvariantOp(kAdd, trip_count, CreateConstant(1, type));
}
// Compensate for stride.
- trip_count = CreateInvariantOp(kAdd, trip_count, stride);
+ trip_count = CreateInvariantOp(kAdd, trip_count, stride_expr);
}
- trip_count = CreateInvariantOp(kDiv, CreateInvariantOp(kSub, trip_count, lower_expr), stride);
+ trip_count = CreateInvariantOp(
+ kDiv, CreateInvariantOp(kSub, trip_count, lower_expr), stride_expr);
// Assign the trip-count expression to the loop control. Clients that use the information
// should be aware that the expression is only valid under the conditions listed above.
InductionOp tcKind = kTripCountInBodyUnsafe; // needs both tests
@@ -644,14 +648,25 @@ bool HInductionVarAnalysis::IsTaken(InductionInfo* lower_expr,
IfCondition cmp) {
int64_t lower_value;
int64_t upper_value;
- if (IsIntAndGet(lower_expr, &lower_value) && IsIntAndGet(upper_expr, &upper_value)) {
- switch (cmp) {
- case kCondLT: return lower_value < upper_value;
- case kCondLE: return lower_value <= upper_value;
- case kCondGT: return lower_value > upper_value;
- case kCondGE: return lower_value >= upper_value;
- default: LOG(FATAL) << "CONDITION UNREACHABLE";
- }
+ switch (cmp) {
+ case kCondLT:
+ return IsAtMost(lower_expr, &lower_value)
+ && IsAtLeast(upper_expr, &upper_value)
+ && lower_value < upper_value;
+ case kCondLE:
+ return IsAtMost(lower_expr, &lower_value)
+ && IsAtLeast(upper_expr, &upper_value)
+ && lower_value <= upper_value;
+ case kCondGT:
+ return IsAtLeast(lower_expr, &lower_value)
+ && IsAtMost(upper_expr, &upper_value)
+ && lower_value > upper_value;
+ case kCondGE:
+ return IsAtLeast(lower_expr, &lower_value)
+ && IsAtMost(upper_expr, &upper_value)
+ && lower_value >= upper_value;
+ default:
+ LOG(FATAL) << "CONDITION UNREACHABLE";
}
return false; // not certain, may be untaken
}
@@ -660,25 +675,23 @@ 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 = 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();
// Some rules under which it is certain at compile-time that the loop is finite.
int64_t value;
switch (cmp) {
case kCondLT:
return stride_value == 1 ||
- (IsIntAndGet(upper_expr, &value) && value <= (max - stride_value + 1));
+ (IsAtMost(upper_expr, &value) && value <= (max - stride_value + 1));
case kCondLE:
- return (IsIntAndGet(upper_expr, &value) && value <= (max - stride_value));
+ return (IsAtMost(upper_expr, &value) && value <= (max - stride_value));
case kCondGT:
return stride_value == -1 ||
- (IsIntAndGet(upper_expr, &value) && value >= (min - stride_value - 1));
+ (IsAtLeast(upper_expr, &value) && value >= (min - stride_value - 1));
case kCondGE:
- return (IsIntAndGet(upper_expr, &value) && value >= (min - stride_value));
+ return (IsAtLeast(upper_expr, &value) && value >= (min - stride_value));
default:
LOG(FATAL) << "CONDITION UNREACHABLE";
}
@@ -733,7 +746,7 @@ HInductionVarAnalysis::InductionInfo* HInductionVarAnalysis::CreateSimplifiedInv
// More exhaustive simplifications are done by later phases once induction nodes are
// translated back into HIR code (e.g. by loop optimizations or BCE).
int64_t value = -1;
- if (IsIntAndGet(a, &value)) {
+ if (IsExact(a, &value)) {
if (value == 0) {
// Simplify 0 + b = b, 0 * b = 0.
if (op == kAdd) {
@@ -750,7 +763,7 @@ HInductionVarAnalysis::InductionInfo* HInductionVarAnalysis::CreateSimplifiedInv
}
}
}
- if (IsIntAndGet(b, &value)) {
+ if (IsExact(b, &value)) {
if (value == 0) {
// Simplify a + 0 = a, a - 0 = a, a * 0 = 0, -0 = 0.
if (op == kAdd || op == kSub) {
@@ -784,29 +797,16 @@ HInductionVarAnalysis::InductionInfo* HInductionVarAnalysis::CreateSimplifiedInv
return new (graph_->GetArena()) InductionInfo(kInvariant, op, a, b, nullptr);
}
-bool HInductionVarAnalysis::IsIntAndGet(InductionInfo* info, int64_t* value) {
- if (info != nullptr && info->induction_class == kInvariant) {
- // A direct constant fetch.
- if (info->operation == kFetch) {
- DCHECK(info->fetch);
- if (info->fetch->IsIntConstant()) {
- *value = info->fetch->AsIntConstant()->GetValue();
- return true;
- } else if (info->fetch->IsLongConstant()) {
- *value = info->fetch->AsLongConstant()->GetValue();
- return true;
- }
- }
- // Use range analysis to resolve compound values.
- InductionVarRange range(this);
- int32_t min_val = 0;
- int32_t max_val = 0;
- if (range.IsConstantRange(info, &min_val, &max_val) && min_val == max_val) {
- *value = min_val;
- return true;
- }
- }
- return false;
+bool HInductionVarAnalysis::IsExact(InductionInfo* info, int64_t* value) {
+ return InductionVarRange(this).IsConstant(info, InductionVarRange::kExact, value);
+}
+
+bool HInductionVarAnalysis::IsAtMost(InductionInfo* info, int64_t* value) {
+ return InductionVarRange(this).IsConstant(info, InductionVarRange::kAtMost, value);
+}
+
+bool HInductionVarAnalysis::IsAtLeast(InductionInfo* info, int64_t* value) {
+ return InductionVarRange(this).IsConstant(info, InductionVarRange::kAtLeast, value);
}
bool HInductionVarAnalysis::InductionEqual(InductionInfo* info1,
diff --git a/compiler/optimizing/induction_var_analysis.h b/compiler/optimizing/induction_var_analysis.h
index 84d5d82568..94d2646aec 100644
--- a/compiler/optimizing/induction_var_analysis.h
+++ b/compiler/optimizing/induction_var_analysis.h
@@ -189,7 +189,9 @@ class HInductionVarAnalysis : public HOptimization {
InductionInfo* CreateSimplifiedInvariant(InductionOp op, InductionInfo* a, InductionInfo* b);
// Constants.
- bool IsIntAndGet(InductionInfo* info, int64_t* value);
+ bool IsExact(InductionInfo* info, /*out*/ int64_t* value);
+ bool IsAtMost(InductionInfo* info, /*out*/ int64_t* value);
+ bool IsAtLeast(InductionInfo* info, /*out*/ int64_t* value);
// Helpers.
static bool InductionEqual(InductionInfo* info1, InductionInfo* info2);
diff --git a/compiler/optimizing/induction_var_range.cc b/compiler/optimizing/induction_var_range.cc
index 9566c29adf..f9b6910acd 100644
--- a/compiler/optimizing/induction_var_range.cc
+++ b/compiler/optimizing/induction_var_range.cc
@@ -45,17 +45,14 @@ static bool IsSafeDiv(int32_t c1, int32_t c2) {
return c2 != 0 && CanLongValueFitIntoInt(static_cast<int64_t>(c1) / static_cast<int64_t>(c2));
}
-/** Returns true for 32/64-bit integral constant. */
-static bool IsIntAndGet(HInstruction* instruction, int32_t* value) {
+/** Returns true for 32/64-bit constant instruction. */
+static bool IsIntAndGet(HInstruction* instruction, int64_t* value) {
if (instruction->IsIntConstant()) {
*value = instruction->AsIntConstant()->GetValue();
return true;
} else if (instruction->IsLongConstant()) {
- const int64_t c = instruction->AsLongConstant()->GetValue();
- if (CanLongValueFitIntoInt(c)) {
- *value = static_cast<int32_t>(c);
- return true;
- }
+ *value = instruction->AsLongConstant()->GetValue();
+ return true;
}
return false;
}
@@ -65,8 +62,9 @@ static bool IsIntAndGet(HInstruction* instruction, int32_t* value) {
* because length >= 0 is true. This makes it more likely the bound is useful to clients.
*/
static InductionVarRange::Value SimplifyMax(InductionVarRange::Value v) {
- int32_t value;
- if (v.a_constant > 1 &&
+ int64_t value;
+ if (v.is_known &&
+ v.a_constant > 1 &&
v.instruction->IsDiv() &&
v.instruction->InputAt(0)->IsArrayLength() &&
IsIntAndGet(v.instruction->InputAt(1), &value) && v.a_constant == value) {
@@ -75,6 +73,16 @@ static InductionVarRange::Value SimplifyMax(InductionVarRange::Value v) {
return v;
}
+/** Helper method to test for a constant value. */
+static bool IsConstantValue(InductionVarRange::Value v) {
+ return v.is_known && v.a_constant == 0;
+}
+
+/** Helper method to test for same constant value. */
+static bool IsSameConstantValue(InductionVarRange::Value v1, InductionVarRange::Value v2) {
+ return IsConstantValue(v1) && IsConstantValue(v2) && v1.b_constant == v2.b_constant;
+}
+
/** Helper method to insert an instruction. */
static HInstruction* Insert(HBasicBlock* block, HInstruction* instruction) {
DCHECK(block != nullptr);
@@ -99,29 +107,45 @@ bool InductionVarRange::GetInductionRange(HInstruction* context,
/*out*/Value* max_val,
/*out*/bool* needs_finite_test) {
HLoopInformation* loop = context->GetBlock()->GetLoopInformation(); // closest enveloping loop
- if (loop != nullptr) {
- // Set up loop information.
- HBasicBlock* header = loop->GetHeader();
- bool in_body = context->GetBlock() != header;
- HInductionVarAnalysis::InductionInfo* info =
- induction_analysis_->LookupInfo(loop, instruction);
- HInductionVarAnalysis::InductionInfo* trip =
- induction_analysis_->LookupInfo(loop, header->GetLastInstruction());
- // Find range.
- *min_val = GetVal(info, trip, in_body, /* is_min */ true);
- *max_val = SimplifyMax(GetVal(info, trip, in_body, /* is_min */ false));
- *needs_finite_test = NeedsTripCount(info) && IsUnsafeTripCount(trip);
- return true;
+ if (loop == nullptr) {
+ return false; // no loop
+ }
+ HInductionVarAnalysis::InductionInfo* info = induction_analysis_->LookupInfo(loop, instruction);
+ if (info == nullptr) {
+ return false; // no induction information
}
- return false; // Nothing known
+ // Set up loop information.
+ HBasicBlock* header = loop->GetHeader();
+ bool in_body = context->GetBlock() != header;
+ HInductionVarAnalysis::InductionInfo* trip =
+ induction_analysis_->LookupInfo(loop, header->GetLastInstruction());
+ // Find range.
+ *min_val = GetVal(info, trip, in_body, /* is_min */ true);
+ *max_val = SimplifyMax(GetVal(info, trip, in_body, /* is_min */ false));
+ *needs_finite_test = NeedsTripCount(info) && IsUnsafeTripCount(trip);
+ return true;
}
-bool InductionVarRange::RefineOuter(/*in-out*/Value* min_val, /*in-out*/Value* max_val) const {
- Value v1 = RefineOuter(*min_val, /* is_min */ true);
- Value v2 = RefineOuter(*max_val, /* is_min */ false);
- if (v1.instruction != min_val->instruction || v2.instruction != max_val->instruction) {
- *min_val = v1;
- *max_val = v2;
+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;
+ }
+ }
+ // 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;
@@ -164,6 +188,46 @@ void InductionVarRange::GenerateTakenTest(HInstruction* context,
// Private class methods.
//
+bool InductionVarRange::IsConstant(HInductionVarAnalysis::InductionInfo* info,
+ ConstantRequest request,
+ /*out*/ int64_t *value) const {
+ if (info != nullptr) {
+ // A direct 32-bit or 64-bit constant fetch. This immediately satisfies
+ // any of the three requests (kExact, kAtMost, and KAtLeast).
+ if (info->induction_class == HInductionVarAnalysis::kInvariant &&
+ info->operation == HInductionVarAnalysis::kFetch) {
+ if (IsIntAndGet(info->fetch, value)) {
+ return true;
+ }
+ }
+ // Try range analysis while traversing outward on loops.
+ bool in_body = true; // no known trip count
+ Value v_min = GetVal(info, nullptr, in_body, /* is_min */ true);
+ Value v_max = GetVal(info, nullptr, in_body, /* is_min */ false);
+ do {
+ // Make sure *both* extremes are known to avoid arithmetic wrap-around anomalies.
+ if (IsConstantValue(v_min) && IsConstantValue(v_max) && v_min.b_constant <= v_max.b_constant) {
+ if ((request == kExact && v_min.b_constant == v_max.b_constant) || request == kAtMost) {
+ *value = v_max.b_constant;
+ return true;
+ } else if (request == kAtLeast) {
+ *value = v_min.b_constant;
+ return true;
+ }
+ }
+ } while (RefineOuter(&v_min, &v_max));
+ // Exploit array length + c >= c, with c <= 0 to avoid arithmetic wrap-around anomalies
+ // (e.g. array length == maxint and c == 1 would yield minint).
+ if (request == kAtLeast) {
+ if (v_min.a_constant == 1 && v_min.b_constant <= 0 && v_min.instruction->IsArrayLength()) {
+ *value = v_min.b_constant;
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
bool InductionVarRange::NeedsTripCount(HInductionVarAnalysis::InductionInfo* info) const {
if (info != nullptr) {
if (info->induction_class == HInductionVarAnalysis::kLinear) {
@@ -206,12 +270,10 @@ InductionVarRange::Value InductionVarRange::GetLinear(HInductionVarAnalysis::Ind
if (trip != nullptr) {
HInductionVarAnalysis::InductionInfo* trip_expr = trip->op_a;
if (trip_expr->operation == HInductionVarAnalysis::kSub) {
- int32_t min_value = 0;
- int32_t stride_value = 0;
- if (IsConstantRange(info->op_a, &min_value, &stride_value) && min_value == stride_value) {
+ int64_t stride_value = 0;
+ if (IsConstant(info->op_a, kExact, &stride_value)) {
if (!is_min && stride_value == 1) {
- // Test original trip's negative operand (trip_expr->op_b) against
- // the offset of the linear induction.
+ // Test original trip's negative operand (trip_expr->op_b) against offset of induction.
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(
@@ -219,8 +281,7 @@ InductionVarRange::Value InductionVarRange::GetLinear(HInductionVarAnalysis::Ind
return GetVal(&cancelled_trip, trip, in_body, is_min);
}
} else if (is_min && stride_value == -1) {
- // Test original trip's positive operand (trip_expr->op_a) against
- // the offset of the linear induction.
+ // Test original trip's positive operand (trip_expr->op_a) against offset of induction.
if (HInductionVarAnalysis::InductionEqual(trip_expr->op_a, info->op_b)) {
// Analyze cancelled trip with just the negative operand (trip_expr->op_b).
HInductionVarAnalysis::InductionInfo neg(
@@ -248,14 +309,16 @@ InductionVarRange::Value InductionVarRange::GetFetch(HInstruction* instruction,
bool is_min) const {
// Detect constants and chase the fetch a bit deeper into the HIR tree, so that it becomes
// more likely range analysis will compare the same instructions as terminal nodes.
- int32_t value;
- if (IsIntAndGet(instruction, &value)) {
- return Value(value);
+ int64_t value;
+ if (IsIntAndGet(instruction, &value) && CanLongValueFitIntoInt(value)) {
+ return Value(static_cast<int32_t>(value));
} else if (instruction->IsAdd()) {
- if (IsIntAndGet(instruction->InputAt(0), &value)) {
- return AddValue(Value(value), GetFetch(instruction->InputAt(1), trip, in_body, is_min));
- } else if (IsIntAndGet(instruction->InputAt(1), &value)) {
- return AddValue(GetFetch(instruction->InputAt(0), trip, in_body, is_min), Value(value));
+ if (IsIntAndGet(instruction->InputAt(0), &value) && CanLongValueFitIntoInt(value)) {
+ return AddValue(Value(static_cast<int32_t>(value)),
+ GetFetch(instruction->InputAt(1), trip, in_body, is_min));
+ } else if (IsIntAndGet(instruction->InputAt(1), &value) && CanLongValueFitIntoInt(value)) {
+ return AddValue(GetFetch(instruction->InputAt(0), trip, in_body, is_min),
+ Value(static_cast<int32_t>(value)));
}
} else if (instruction->IsArrayLength() && instruction->InputAt(0)->IsNewArray()) {
return GetFetch(instruction->InputAt(0)->InputAt(0), trip, in_body, is_min);
@@ -331,29 +394,30 @@ InductionVarRange::Value InductionVarRange::GetMul(HInductionVarAnalysis::Induct
Value v1_max = GetVal(info1, trip, in_body, /* is_min */ false);
Value v2_min = GetVal(info2, trip, in_body, /* is_min */ true);
Value v2_max = GetVal(info2, trip, in_body, /* is_min */ false);
- // Try to refine certain failure.
- if (v1_min.a_constant && v1_max.a_constant) {
- v1_min = RefineOuter(v1_min, /* is_min */ true);
- v1_max = RefineOuter(v1_max, /* is_min */ false);
- }
- // Positive or negative range?
- if (v1_min.is_known && v1_min.a_constant == 0 && v1_min.b_constant >= 0) {
- // Positive range vs. positive or negative range.
- if (v2_min.is_known && v2_min.a_constant == 0 && v2_min.b_constant >= 0) {
- return is_min ? MulValue(v1_min, v2_min)
- : MulValue(v1_max, v2_max);
- } else if (v2_max.is_known && v2_max.a_constant == 0 && v2_max.b_constant <= 0) {
- return is_min ? MulValue(v1_max, v2_min)
- : MulValue(v1_min, v2_max);
+ // Try to refine first operand.
+ if (!IsConstantValue(v1_min) && !IsConstantValue(v1_max)) {
+ RefineOuter(&v1_min, &v1_max);
+ }
+ // Constant times range.
+ if (IsSameConstantValue(v1_min, v1_max)) {
+ return MulRangeAndConstant(v2_min, v2_max, v1_min, is_min);
+ } else if (IsSameConstantValue(v2_min, v2_max)) {
+ return MulRangeAndConstant(v1_min, v1_max, v2_min, is_min);
+ }
+ // Positive range vs. positive or negative range.
+ if (IsConstantValue(v1_min) && v1_min.b_constant >= 0) {
+ if (IsConstantValue(v2_min) && v2_min.b_constant >= 0) {
+ return is_min ? MulValue(v1_min, v2_min) : MulValue(v1_max, v2_max);
+ } else if (IsConstantValue(v2_max) && v2_max.b_constant <= 0) {
+ return is_min ? MulValue(v1_max, v2_min) : MulValue(v1_min, v2_max);
}
- } else if (v1_max.is_known && v1_max.a_constant == 0 && v1_max.b_constant <= 0) {
- // Negative range vs. positive or negative range.
- if (v2_min.is_known && v2_min.a_constant == 0 && v2_min.b_constant >= 0) {
- return is_min ? MulValue(v1_min, v2_max)
- : MulValue(v1_max, v2_min);
- } else if (v2_max.is_known && v2_max.a_constant == 0 && v2_max.b_constant <= 0) {
- return is_min ? MulValue(v1_max, v2_max)
- : MulValue(v1_min, v2_min);
+ }
+ // Negative range vs. positive or negative range.
+ if (IsConstantValue(v1_max) && v1_max.b_constant <= 0) {
+ if (IsConstantValue(v2_min) && v2_min.b_constant >= 0) {
+ return is_min ? MulValue(v1_min, v2_max) : MulValue(v1_max, v2_min);
+ } else if (IsConstantValue(v2_max) && v2_max.b_constant <= 0) {
+ return is_min ? MulValue(v1_max, v2_max) : MulValue(v1_min, v2_min);
}
}
return Value();
@@ -368,43 +432,41 @@ InductionVarRange::Value InductionVarRange::GetDiv(HInductionVarAnalysis::Induct
Value v1_max = GetVal(info1, trip, in_body, /* is_min */ false);
Value v2_min = GetVal(info2, trip, in_body, /* is_min */ true);
Value v2_max = GetVal(info2, trip, in_body, /* is_min */ false);
- // Positive or negative range?
- if (v1_min.is_known && v1_min.a_constant == 0 && v1_min.b_constant >= 0) {
- // Positive range vs. positive or negative range.
- if (v2_min.is_known && v2_min.a_constant == 0 && v2_min.b_constant >= 0) {
- return is_min ? DivValue(v1_min, v2_max)
- : DivValue(v1_max, v2_min);
- } else if (v2_max.is_known && v2_max.a_constant == 0 && v2_max.b_constant <= 0) {
- return is_min ? DivValue(v1_max, v2_max)
- : DivValue(v1_min, v2_min);
+ // Range divided by constant.
+ if (IsSameConstantValue(v2_min, v2_max)) {
+ return DivRangeAndConstant(v1_min, v1_max, v2_min, is_min);
+ }
+ // Positive range vs. positive or negative range.
+ if (IsConstantValue(v1_min) && v1_min.b_constant >= 0) {
+ if (IsConstantValue(v2_min) && v2_min.b_constant >= 0) {
+ return is_min ? DivValue(v1_min, v2_max) : DivValue(v1_max, v2_min);
+ } else if (IsConstantValue(v2_max) && v2_max.b_constant <= 0) {
+ return is_min ? DivValue(v1_max, v2_max) : DivValue(v1_min, v2_min);
}
- } else if (v1_max.is_known && v1_max.a_constant == 0 && v1_max.b_constant <= 0) {
- // Negative range vs. positive or negative range.
- if (v2_min.is_known && v2_min.a_constant == 0 && v2_min.b_constant >= 0) {
- return is_min ? DivValue(v1_min, v2_min)
- : DivValue(v1_max, v2_max);
- } else if (v2_max.is_known && v2_max.a_constant == 0 && v2_max.b_constant <= 0) {
- return is_min ? DivValue(v1_max, v2_min)
- : DivValue(v1_min, v2_max);
+ }
+ // Negative range vs. positive or negative range.
+ if (IsConstantValue(v1_max) && v1_max.b_constant <= 0) {
+ if (IsConstantValue(v2_min) && v2_min.b_constant >= 0) {
+ return is_min ? DivValue(v1_min, v2_min) : DivValue(v1_max, v2_max);
+ } else if (IsConstantValue(v2_max) && v2_max.b_constant <= 0) {
+ return is_min ? DivValue(v1_max, v2_min) : DivValue(v1_min, v2_max);
}
}
return Value();
}
-bool InductionVarRange::IsConstantRange(HInductionVarAnalysis::InductionInfo* info,
- int32_t *min_value,
- int32_t *max_value) const {
- bool in_body = true; // no known trip count
- Value v_min = GetVal(info, nullptr, in_body, /* is_min */ true);
- Value v_max = GetVal(info, nullptr, in_body, /* is_min */ false);
- do {
- if (v_min.is_known && v_min.a_constant == 0 && v_max.is_known && v_max.a_constant == 0) {
- *min_value = v_min.b_constant;
- *max_value = v_max.b_constant;
- return true;
- }
- } while (RefineOuter(&v_min, &v_max));
- return false;
+InductionVarRange::Value InductionVarRange::MulRangeAndConstant(Value v_min,
+ Value v_max,
+ Value c,
+ bool is_min) const {
+ return is_min == (c.b_constant >= 0) ? MulValue(v_min, c) : MulValue(v_max, c);
+}
+
+InductionVarRange::Value InductionVarRange::DivRangeAndConstant(Value v_min,
+ Value v_max,
+ Value c,
+ bool is_min) const {
+ return is_min == (c.b_constant >= 0) ? DivValue(v_min, c) : DivValue(v_max, c);
}
InductionVarRange::Value InductionVarRange::AddValue(Value v1, Value v2) const {
@@ -471,22 +533,25 @@ InductionVarRange::Value InductionVarRange::MergeVal(Value v1, Value v2, bool is
}
InductionVarRange::Value InductionVarRange::RefineOuter(Value v, bool is_min) const {
- if (v.instruction != nullptr) {
- HLoopInformation* loop =
- v.instruction->GetBlock()->GetLoopInformation(); // closest enveloping loop
- if (loop != nullptr) {
- // Set up loop information.
- bool in_body = true; // use is always in body of outer loop
- HInductionVarAnalysis::InductionInfo* info =
- induction_analysis_->LookupInfo(loop, v.instruction);
- HInductionVarAnalysis::InductionInfo* trip =
- induction_analysis_->LookupInfo(loop, loop->GetHeader()->GetLastInstruction());
- // Try to refine "a x instruction + b" with outer loop range information on instruction.
- return AddValue(MulValue(Value(v.a_constant), GetVal(info, trip, in_body, is_min)),
- Value(v.b_constant));
- }
+ if (v.instruction == nullptr) {
+ return v; // nothing to refine
}
- return v;
+ HLoopInformation* loop =
+ v.instruction->GetBlock()->GetLoopInformation(); // closest enveloping loop
+ if (loop == nullptr) {
+ return v; // no loop
+ }
+ HInductionVarAnalysis::InductionInfo* info = induction_analysis_->LookupInfo(loop, v.instruction);
+ if (info == nullptr) {
+ return v; // no induction information
+ }
+ // Set up loop information.
+ HBasicBlock* header = loop->GetHeader();
+ bool in_body = true; // inner always in more outer
+ HInductionVarAnalysis::InductionInfo* trip =
+ induction_analysis_->LookupInfo(loop, header->GetLastInstruction());
+ // Try to refine "a x instruction + b" with outer loop range information on instruction.
+ return AddValue(MulValue(Value(v.a_constant), GetVal(info, trip, in_body, is_min)), Value(v.b_constant));
}
bool InductionVarRange::GenerateCode(HInstruction* context,
@@ -499,44 +564,45 @@ bool InductionVarRange::GenerateCode(HInstruction* context,
/*out*/bool* needs_finite_test,
/*out*/bool* needs_taken_test) const {
HLoopInformation* loop = context->GetBlock()->GetLoopInformation(); // closest enveloping loop
- if (loop != nullptr) {
- // Set up loop information.
- HBasicBlock* header = loop->GetHeader();
- bool in_body = context->GetBlock() != header;
- HInductionVarAnalysis::InductionInfo* info =
- induction_analysis_->LookupInfo(loop, instruction);
- if (info == nullptr) {
- return false; // nothing to analyze
- }
- HInductionVarAnalysis::InductionInfo* trip =
- induction_analysis_->LookupInfo(loop, header->GetLastInstruction());
- // Determine what tests are needed. A finite test is needed if the evaluation code uses the
- // trip-count and the loop maybe unsafe (because in such cases, the index could "overshoot"
- // the computed range). A taken test is needed for any unknown trip-count, even if evaluation
- // code does not use the trip-count explicitly (since there could be an implicit relation
- // between e.g. an invariant subscript and a not-taken condition).
- *needs_finite_test = NeedsTripCount(info) && IsUnsafeTripCount(trip);
- *needs_taken_test = IsBodyTripCount(trip);
- // Code generation for taken test: generate the code when requested or otherwise analyze
- // if code generation is feasible when taken test is needed.
- if (taken_test != nullptr) {
- return GenerateCode(
- trip->op_b, nullptr, graph, block, taken_test, in_body, /* is_min */ false);
- } else if (*needs_taken_test) {
- if (!GenerateCode(
- trip->op_b, nullptr, nullptr, nullptr, nullptr, in_body, /* is_min */ false)) {
- return false;
- }
+ if (loop == nullptr) {
+ return false; // no loop
+ }
+ HInductionVarAnalysis::InductionInfo* info = induction_analysis_->LookupInfo(loop, instruction);
+ if (info == nullptr) {
+ return false; // no induction information
+ }
+ // Set up loop information.
+ HBasicBlock* header = loop->GetHeader();
+ bool in_body = context->GetBlock() != header;
+ HInductionVarAnalysis::InductionInfo* trip =
+ induction_analysis_->LookupInfo(loop, header->GetLastInstruction());
+ if (trip == nullptr) {
+ return false; // codegen relies on trip count
+ }
+ // Determine what tests are needed. A finite test is needed if the evaluation code uses the
+ // trip-count and the loop maybe unsafe (because in such cases, the index could "overshoot"
+ // the computed range). A taken test is needed for any unknown trip-count, even if evaluation
+ // code does not use the trip-count explicitly (since there could be an implicit relation
+ // between e.g. an invariant subscript and a not-taken condition).
+ *needs_finite_test = NeedsTripCount(info) && IsUnsafeTripCount(trip);
+ *needs_taken_test = IsBodyTripCount(trip);
+ // Code generation for taken test: generate the code when requested or otherwise analyze
+ // if code generation is feasible when taken test is needed.
+ if (taken_test != nullptr) {
+ return GenerateCode(trip->op_b, nullptr, graph, block, taken_test, in_body, /* is_min */ false);
+ } else if (*needs_taken_test) {
+ if (!GenerateCode(
+ trip->op_b, nullptr, nullptr, nullptr, nullptr, in_body, /* is_min */ false)) {
+ return false;
}
- // Code generation for lower and upper.
- return
- // Success on lower if invariant (not set), or code can be generated.
- ((info->induction_class == HInductionVarAnalysis::kInvariant) ||
- GenerateCode(info, trip, graph, block, lower, in_body, /* is_min */ true)) &&
- // And success on upper.
- GenerateCode(info, trip, graph, block, upper, in_body, /* is_min */ false);
}
- return false;
+ // Code generation for lower and upper.
+ return
+ // Success on lower if invariant (not set), or code can be generated.
+ ((info->induction_class == HInductionVarAnalysis::kInvariant) ||
+ GenerateCode(info, trip, graph, block, lower, in_body, /* is_min */ true)) &&
+ // And success on upper.
+ GenerateCode(info, trip, graph, block, upper, in_body, /* is_min */ false);
}
bool InductionVarRange::GenerateCode(HInductionVarAnalysis::InductionInfo* info,
@@ -639,9 +705,8 @@ bool InductionVarRange::GenerateCode(HInductionVarAnalysis::InductionInfo* info,
case HInductionVarAnalysis::kLinear: {
// Linear induction a * i + b, for normalized 0 <= i < TC. Restrict to unit stride only
// to avoid arithmetic wrap-around situations that are hard to guard against.
- int32_t min_value = 0;
- int32_t stride_value = 0;
- if (IsConstantRange(info->op_a, &min_value, &stride_value) && min_value == stride_value) {
+ int64_t stride_value = 0;
+ if (IsConstant(info->op_a, kExact, &stride_value)) {
if (stride_value == 1 || stride_value == -1) {
const bool is_min_a = stride_value == 1 ? is_min : !is_min;
if (GenerateCode(trip, trip, graph, block, &opa, in_body, is_min_a) &&
@@ -666,7 +731,7 @@ bool InductionVarRange::GenerateCode(HInductionVarAnalysis::InductionInfo* info,
// Wrap-around and periodic inductions are restricted to constants only, so that extreme
// values are easy to test at runtime without complications of arithmetic wrap-around.
Value extreme = GetVal(info, trip, in_body, is_min);
- if (extreme.is_known && extreme.a_constant == 0) {
+ if (IsConstantValue(extreme)) {
if (graph != nullptr) {
*result = graph->GetIntConstant(extreme.b_constant);
}
diff --git a/compiler/optimizing/induction_var_range.h b/compiler/optimizing/induction_var_range.h
index 3cb7b4bfd5..0af41560ff 100644
--- a/compiler/optimizing/induction_var_range.h
+++ b/compiler/optimizing/induction_var_range.h
@@ -69,7 +69,8 @@ class InductionVarRange {
/*out*/ bool* needs_finite_test);
/** Refines the values with induction of next outer loop. Returns true on change. */
- bool RefineOuter(/*in-out*/Value* min_val, /*in-out*/Value* max_val) const;
+ bool RefineOuter(/*in-out*/ Value* min_val,
+ /*in-out*/ Value* max_val) const;
/**
* Returns true if range analysis is able to generate code for the lower and upper
@@ -116,6 +117,23 @@ class InductionVarRange {
/*out*/ HInstruction** taken_test);
private:
+ /*
+ * Enum used in IsConstant() request.
+ */
+ enum ConstantRequest {
+ kExact,
+ kAtMost,
+ kAtLeast
+ };
+
+ /**
+ * Returns true if exact or upper/lower bound on the given induction
+ * information is known as a 64-bit constant, which is returned in value.
+ */
+ bool IsConstant(HInductionVarAnalysis::InductionInfo* info,
+ ConstantRequest request,
+ /*out*/ int64_t *value) const;
+
bool NeedsTripCount(HInductionVarAnalysis::InductionInfo* info) const;
bool IsBodyTripCount(HInductionVarAnalysis::InductionInfo* trip) const;
bool IsUnsafeTripCount(HInductionVarAnalysis::InductionInfo* trip) const;
@@ -143,9 +161,8 @@ class InductionVarRange {
bool in_body,
bool is_min) const;
- bool IsConstantRange(HInductionVarAnalysis::InductionInfo* info,
- int32_t *min_value,
- int32_t *max_value) const;
+ Value MulRangeAndConstant(Value v1, Value v2, Value c, bool is_min) const;
+ Value DivRangeAndConstant(Value v1, Value v2, Value c, bool is_min) const;
Value AddValue(Value v1, Value v2) const;
Value SubValue(Value v1, Value v2) const;
diff --git a/compiler/optimizing/induction_var_range_test.cc b/compiler/optimizing/induction_var_range_test.cc
index 55a654e301..c5c33bd9bc 100644
--- a/compiler/optimizing/induction_var_range_test.cc
+++ b/compiler/optimizing/induction_var_range_test.cc
@@ -215,10 +215,16 @@ class InductionVarRangeTest : public CommonCompilerTest {
return range_.GetDiv(info1, info2, nullptr, /* in_body */ true, is_min);
}
- bool IsConstantRange(HInductionVarAnalysis::InductionInfo* info,
- int32_t* min_value,
- int32_t* max_value) {
- return range_.IsConstantRange(info, min_value, max_value);
+ bool IsExact(HInductionVarAnalysis::InductionInfo* info, int64_t* value) {
+ return range_.IsConstant(info, InductionVarRange::kExact, value);
+ }
+
+ bool IsAtMost(HInductionVarAnalysis::InductionInfo* info, int64_t* value) {
+ return range_.IsConstant(info, InductionVarRange::kAtMost, value);
+ }
+
+ bool IsAtLeast(HInductionVarAnalysis::InductionInfo* info, int64_t* value) {
+ return range_.IsConstant(info, InductionVarRange::kAtLeast, value);
}
Value AddValue(Value v1, Value v2) { return range_.AddValue(v1, v2); }
@@ -249,6 +255,34 @@ class InductionVarRangeTest : public CommonCompilerTest {
// Tests on private methods.
//
+TEST_F(InductionVarRangeTest, IsConstant) {
+ int64_t value;
+ // Constant.
+ EXPECT_TRUE(IsExact(CreateConst(12345), &value));
+ EXPECT_EQ(12345, value);
+ EXPECT_TRUE(IsAtMost(CreateConst(12345), &value));
+ EXPECT_EQ(12345, value);
+ EXPECT_TRUE(IsAtLeast(CreateConst(12345), &value));
+ EXPECT_EQ(12345, value);
+ // Constant trivial range.
+ EXPECT_TRUE(IsExact(CreateRange(111, 111), &value));
+ EXPECT_EQ(111, value);
+ EXPECT_TRUE(IsAtMost(CreateRange(111, 111), &value));
+ EXPECT_EQ(111, value);
+ EXPECT_TRUE(IsAtLeast(CreateRange(111, 111), &value));
+ EXPECT_EQ(111, value);
+ // Constant non-trivial range.
+ EXPECT_FALSE(IsExact(CreateRange(11, 22), &value));
+ EXPECT_TRUE(IsAtMost(CreateRange(11, 22), &value));
+ EXPECT_EQ(22, value);
+ EXPECT_TRUE(IsAtLeast(CreateRange(11, 22), &value));
+ EXPECT_EQ(11, value);
+ // Symbolic.
+ EXPECT_FALSE(IsExact(CreateFetch(x_), &value));
+ EXPECT_FALSE(IsAtMost(CreateFetch(x_), &value));
+ EXPECT_FALSE(IsAtLeast(CreateFetch(x_), &value));
+}
+
TEST_F(InductionVarRangeTest, TripCountProperties) {
EXPECT_FALSE(NeedsTripCount(nullptr));
EXPECT_FALSE(NeedsTripCount(CreateConst(1)));
@@ -367,6 +401,10 @@ TEST_F(InductionVarRangeTest, GetMinMaxPeriodic) {
}
TEST_F(InductionVarRangeTest, GetMulMin) {
+ ExpectEqual(Value(-14), GetMul(CreateConst(2), CreateRange(-7, 8), true));
+ ExpectEqual(Value(-16), GetMul(CreateConst(-2), CreateRange(-7, 8), true));
+ ExpectEqual(Value(-14), GetMul(CreateRange(-7, 8), CreateConst(2), true));
+ ExpectEqual(Value(-16), GetMul(CreateRange(-7, 8), CreateConst(-2), true));
ExpectEqual(Value(6), GetMul(CreateRange(2, 10), CreateRange(3, 5), true));
ExpectEqual(Value(-50), GetMul(CreateRange(2, 10), CreateRange(-5, -3), true));
ExpectEqual(Value(), GetMul(CreateRange(2, 10), CreateRange(-1, 1), true));
@@ -379,6 +417,10 @@ TEST_F(InductionVarRangeTest, GetMulMin) {
}
TEST_F(InductionVarRangeTest, GetMulMax) {
+ ExpectEqual(Value(16), GetMul(CreateConst(2), CreateRange(-7, 8), false));
+ ExpectEqual(Value(14), GetMul(CreateConst(-2), CreateRange(-7, 8), false));
+ ExpectEqual(Value(16), GetMul(CreateRange(-7, 8), CreateConst(2), false));
+ ExpectEqual(Value(14), GetMul(CreateRange(-7, 8), CreateConst(-2), false));
ExpectEqual(Value(50), GetMul(CreateRange(2, 10), CreateRange(3, 5), false));
ExpectEqual(Value(-6), GetMul(CreateRange(2, 10), CreateRange(-5, -3), false));
ExpectEqual(Value(), GetMul(CreateRange(2, 10), CreateRange(-1, 1), false));
@@ -391,6 +433,8 @@ TEST_F(InductionVarRangeTest, GetMulMax) {
}
TEST_F(InductionVarRangeTest, GetDivMin) {
+ ExpectEqual(Value(-5), GetDiv(CreateRange(-10, 20), CreateConst(2), true));
+ ExpectEqual(Value(-10), GetDiv(CreateRange(-10, 20), CreateConst(-2), true));
ExpectEqual(Value(10), GetDiv(CreateRange(40, 1000), CreateRange(2, 4), true));
ExpectEqual(Value(-500), GetDiv(CreateRange(40, 1000), CreateRange(-4, -2), true));
ExpectEqual(Value(), GetDiv(CreateRange(40, 1000), CreateRange(-1, 1), true));
@@ -403,6 +447,8 @@ TEST_F(InductionVarRangeTest, GetDivMin) {
}
TEST_F(InductionVarRangeTest, GetDivMax) {
+ ExpectEqual(Value(10), GetDiv(CreateRange(-10, 20), CreateConst(2), false));
+ ExpectEqual(Value(5), GetDiv(CreateRange(-10, 20), CreateConst(-2), false));
ExpectEqual(Value(500), GetDiv(CreateRange(40, 1000), CreateRange(2, 4), false));
ExpectEqual(Value(-10), GetDiv(CreateRange(40, 1000), CreateRange(-4, -2), false));
ExpectEqual(Value(), GetDiv(CreateRange(40, 1000), CreateRange(-1, 1), false));
@@ -414,18 +460,6 @@ TEST_F(InductionVarRangeTest, GetDivMax) {
ExpectEqual(Value(), GetDiv(CreateRange(-1, 1), CreateRange(-1, 1), false));
}
-TEST_F(InductionVarRangeTest, IsConstantRange) {
- int32_t min_value;
- int32_t max_value;
- ASSERT_TRUE(IsConstantRange(CreateConst(12345), &min_value, &max_value));
- EXPECT_EQ(12345, min_value);
- EXPECT_EQ(12345, max_value);
- ASSERT_TRUE(IsConstantRange(CreateRange(1, 2), &min_value, &max_value));
- EXPECT_EQ(1, min_value);
- EXPECT_EQ(2, max_value);
- EXPECT_FALSE(IsConstantRange(CreateFetch(x_), &min_value, &max_value));
-}
-
TEST_F(InductionVarRangeTest, AddValue) {
ExpectEqual(Value(110), AddValue(Value(10), Value(100)));
ExpectEqual(Value(-5), AddValue(Value(x_, 1, -4), Value(x_, -1, -1)));
@@ -459,6 +493,24 @@ TEST_F(InductionVarRangeTest, MulValue) {
ExpectEqual(Value(), MulValue(Value(90000), Value(-90000))); // unsafe
}
+TEST_F(InductionVarRangeTest, MulValueSpecial) {
+ const int32_t min_value = std::numeric_limits<int32_t>::min();
+ const int32_t max_value = std::numeric_limits<int32_t>::max();
+
+ // Unsafe.
+ ExpectEqual(Value(), MulValue(Value(min_value), Value(min_value)));
+ ExpectEqual(Value(), MulValue(Value(min_value), Value(-1)));
+ ExpectEqual(Value(), MulValue(Value(min_value), Value(max_value)));
+ ExpectEqual(Value(), MulValue(Value(max_value), Value(max_value)));
+
+ // Safe.
+ ExpectEqual(Value(min_value), MulValue(Value(min_value), Value(1)));
+ ExpectEqual(Value(max_value), MulValue(Value(max_value), Value(1)));
+ ExpectEqual(Value(-max_value), MulValue(Value(max_value), Value(-1)));
+ ExpectEqual(Value(-1), MulValue(Value(1), Value(-1)));
+ ExpectEqual(Value(1), MulValue(Value(-1), Value(-1)));
+}
+
TEST_F(InductionVarRangeTest, DivValue) {
ExpectEqual(Value(25), DivValue(Value(100), Value(4)));
ExpectEqual(Value(), DivValue(Value(x_, 1, -4), Value(x_, 1, -1)));
@@ -468,6 +520,23 @@ TEST_F(InductionVarRangeTest, DivValue) {
ExpectEqual(Value(), DivValue(Value(1), Value(0))); // unsafe
}
+TEST_F(InductionVarRangeTest, DivValueSpecial) {
+ const int32_t min_value = std::numeric_limits<int32_t>::min();
+ const int32_t max_value = std::numeric_limits<int32_t>::max();
+
+ // Unsafe.
+ ExpectEqual(Value(), DivValue(Value(min_value), Value(-1)));
+
+ // Safe.
+ ExpectEqual(Value(1), DivValue(Value(min_value), Value(min_value)));
+ ExpectEqual(Value(1), DivValue(Value(max_value), Value(max_value)));
+ ExpectEqual(Value(min_value), DivValue(Value(min_value), Value(1)));
+ ExpectEqual(Value(max_value), DivValue(Value(max_value), Value(1)));
+ ExpectEqual(Value(-max_value), DivValue(Value(max_value), Value(-1)));
+ ExpectEqual(Value(-1), DivValue(Value(1), Value(-1)));
+ ExpectEqual(Value(1), DivValue(Value(-1), Value(-1)));
+}
+
TEST_F(InductionVarRangeTest, MinValue) {
ExpectEqual(Value(10), MinValue(Value(10), Value(100)));
ExpectEqual(Value(x_, 1, -4), MinValue(Value(x_, 1, -4), Value(x_, 1, -1)));
diff --git a/compiler/optimizing/inliner.cc b/compiler/optimizing/inliner.cc
index 02a1acc240..3f67e481f9 100644
--- a/compiler/optimizing/inliner.cc
+++ b/compiler/optimizing/inliner.cc
@@ -562,31 +562,16 @@ void HInliner::CreateDiamondPatternForPolymorphicInline(HInstruction* compare,
graph_->reverse_post_order_[++index] = otherwise;
graph_->reverse_post_order_[++index] = merge;
- // Set the loop information of the newly created blocks.
- HLoopInformation* loop_info = cursor_block->GetLoopInformation();
- if (loop_info != nullptr) {
- then->SetLoopInformation(cursor_block->GetLoopInformation());
- merge->SetLoopInformation(cursor_block->GetLoopInformation());
- otherwise->SetLoopInformation(cursor_block->GetLoopInformation());
- for (HLoopInformationOutwardIterator loop_it(*cursor_block);
- !loop_it.Done();
- loop_it.Advance()) {
- loop_it.Current()->Add(then);
- loop_it.Current()->Add(merge);
- loop_it.Current()->Add(otherwise);
- }
- // In case the original invoke location was a back edge, we need to update
- // the loop to now have the merge block as a back edge.
- if (loop_info->IsBackEdge(*original_invoke_block)) {
- loop_info->RemoveBackEdge(original_invoke_block);
- loop_info->AddBackEdge(merge);
- }
- }
- // Set the try/catch information of the newly created blocks.
- then->SetTryCatchInformation(cursor_block->GetTryCatchInformation());
- merge->SetTryCatchInformation(cursor_block->GetTryCatchInformation());
- otherwise->SetTryCatchInformation(cursor_block->GetTryCatchInformation());
+ graph_->UpdateLoopAndTryInformationOfNewBlock(
+ then, original_invoke_block, /* replace_if_back_edge */ false);
+ graph_->UpdateLoopAndTryInformationOfNewBlock(
+ otherwise, original_invoke_block, /* replace_if_back_edge */ false);
+
+ // In case the original invoke location was a back edge, we need to update
+ // the loop to now have the merge block as a back edge.
+ graph_->UpdateLoopAndTryInformationOfNewBlock(
+ merge, original_invoke_block, /* replace_if_back_edge */ true);
}
bool HInliner::TryInlinePolymorphicCallToSameTarget(HInvoke* invoke_instruction,
@@ -603,6 +588,10 @@ bool HInliner::TryInlinePolymorphicCallToSameTarget(HInvoke* invoke_instruction,
DCHECK(resolved_method != nullptr);
ArtMethod* actual_method = nullptr;
+ size_t method_index = invoke_instruction->IsInvokeVirtual()
+ ? invoke_instruction->AsInvokeVirtual()->GetVTableIndex()
+ : invoke_instruction->AsInvokeInterface()->GetImtIndex();
+
// Check whether we are actually calling the same method among
// the different types seen.
for (size_t i = 0; i < InlineCache::kIndividualCacheSize; ++i) {
@@ -611,13 +600,18 @@ bool HInliner::TryInlinePolymorphicCallToSameTarget(HInvoke* invoke_instruction,
}
ArtMethod* new_method = nullptr;
if (invoke_instruction->IsInvokeInterface()) {
- new_method = ic.GetTypeAt(i)->FindVirtualMethodForInterface(
- resolved_method, pointer_size);
+ new_method = ic.GetTypeAt(i)->GetEmbeddedImTableEntry(
+ method_index % mirror::Class::kImtSize, pointer_size);
+ if (new_method->IsRuntimeMethod()) {
+ // Bail out as soon as we see a conflict trampoline in one of the target's
+ // interface table.
+ return false;
+ }
} else {
DCHECK(invoke_instruction->IsInvokeVirtual());
- new_method = ic.GetTypeAt(i)->FindVirtualMethodForVirtual(
- resolved_method, pointer_size);
+ new_method = ic.GetTypeAt(i)->GetEmbeddedVTableEntry(method_index, pointer_size);
}
+ DCHECK(new_method != nullptr);
if (actual_method == nullptr) {
actual_method = new_method;
} else if (actual_method != new_method) {
@@ -641,18 +635,15 @@ bool HInliner::TryInlinePolymorphicCallToSameTarget(HInvoke* invoke_instruction,
HInstanceFieldGet* receiver_class = BuildGetReceiverClass(
class_linker, receiver, invoke_instruction->GetDexPc());
- size_t method_offset = invoke_instruction->IsInvokeVirtual()
- ? actual_method->GetVtableIndex()
- : invoke_instruction->AsInvokeInterface()->GetImtIndex();
-
Primitive::Type type = Is64BitInstructionSet(graph_->GetInstructionSet())
? Primitive::kPrimLong
: Primitive::kPrimInt;
HClassTableGet* class_table_get = new (graph_->GetArena()) HClassTableGet(
receiver_class,
type,
- invoke_instruction->IsInvokeVirtual() ? HClassTableGet::kVTable : HClassTableGet::kIMTable,
- method_offset,
+ invoke_instruction->IsInvokeVirtual() ? HClassTableGet::TableKind::kVTable
+ : HClassTableGet::TableKind::kIMTable,
+ method_index,
invoke_instruction->GetDexPc());
HConstant* constant;
diff --git a/compiler/optimizing/instruction_simplifier.cc b/compiler/optimizing/instruction_simplifier.cc
index 13d3f752c3..f8a9a94e62 100644
--- a/compiler/optimizing/instruction_simplifier.cc
+++ b/compiler/optimizing/instruction_simplifier.cc
@@ -93,6 +93,7 @@ class InstructionSimplifierVisitor : public HGraphDelegateVisitor {
void SimplifyStringEquals(HInvoke* invoke);
void SimplifyCompare(HInvoke* invoke, bool has_zero_op);
void SimplifyIsNaN(HInvoke* invoke);
+ void SimplifyFP2Int(HInvoke* invoke);
OptimizingCompilerStats* stats_;
bool simplification_occurred_ = false;
@@ -1562,26 +1563,71 @@ void InstructionSimplifierVisitor::SimplifyIsNaN(HInvoke* invoke) {
invoke->GetBlock()->ReplaceAndRemoveInstructionWith(invoke, condition);
}
+void InstructionSimplifierVisitor::SimplifyFP2Int(HInvoke* invoke) {
+ DCHECK(invoke->IsInvokeStaticOrDirect());
+ uint32_t dex_pc = invoke->GetDexPc();
+ HInstruction* x = invoke->InputAt(0);
+ Primitive::Type type = x->GetType();
+ // Set proper bit pattern for NaN and replace intrinsic with raw version.
+ HInstruction* nan;
+ if (type == Primitive::kPrimDouble) {
+ nan = GetGraph()->GetLongConstant(0x7ff8000000000000L);
+ invoke->SetIntrinsic(Intrinsics::kDoubleDoubleToRawLongBits,
+ kNeedsEnvironmentOrCache,
+ kNoSideEffects,
+ kNoThrow);
+ } else {
+ DCHECK_EQ(type, Primitive::kPrimFloat);
+ nan = GetGraph()->GetIntConstant(0x7fc00000);
+ invoke->SetIntrinsic(Intrinsics::kFloatFloatToRawIntBits,
+ kNeedsEnvironmentOrCache,
+ kNoSideEffects,
+ kNoThrow);
+ }
+ // Test IsNaN(x), which is the same as x != x.
+ HCondition* condition = new (GetGraph()->GetArena()) HNotEqual(x, x, dex_pc);
+ condition->SetBias(ComparisonBias::kLtBias);
+ invoke->GetBlock()->InsertInstructionBefore(condition, invoke->GetNext());
+ // Select between the two.
+ HInstruction* select = new (GetGraph()->GetArena()) HSelect(condition, nan, invoke, dex_pc);
+ invoke->GetBlock()->InsertInstructionBefore(select, condition->GetNext());
+ invoke->ReplaceWithExceptInReplacementAtIndex(select, 0); // false at index 0
+}
+
void InstructionSimplifierVisitor::VisitInvoke(HInvoke* instruction) {
- if (instruction->GetIntrinsic() == Intrinsics::kStringEquals) {
- SimplifyStringEquals(instruction);
- } else if (instruction->GetIntrinsic() == Intrinsics::kSystemArrayCopy) {
- SimplifySystemArrayCopy(instruction);
- } else if (instruction->GetIntrinsic() == Intrinsics::kIntegerRotateRight ||
- instruction->GetIntrinsic() == Intrinsics::kLongRotateRight) {
- SimplifyRotate(instruction, false);
- } else if (instruction->GetIntrinsic() == Intrinsics::kIntegerRotateLeft ||
- instruction->GetIntrinsic() == Intrinsics::kLongRotateLeft) {
- SimplifyRotate(instruction, true);
- } else if (instruction->GetIntrinsic() == Intrinsics::kIntegerCompare ||
- instruction->GetIntrinsic() == Intrinsics::kLongCompare) {
- SimplifyCompare(instruction, /* is_signum */ false);
- } else if (instruction->GetIntrinsic() == Intrinsics::kIntegerSignum ||
- instruction->GetIntrinsic() == Intrinsics::kLongSignum) {
- SimplifyCompare(instruction, /* is_signum */ true);
- } else if (instruction->GetIntrinsic() == Intrinsics::kFloatIsNaN ||
- instruction->GetIntrinsic() == Intrinsics::kDoubleIsNaN) {
- SimplifyIsNaN(instruction);
+ switch (instruction->GetIntrinsic()) {
+ case Intrinsics::kStringEquals:
+ SimplifyStringEquals(instruction);
+ break;
+ case Intrinsics::kSystemArrayCopy:
+ SimplifySystemArrayCopy(instruction);
+ break;
+ case Intrinsics::kIntegerRotateRight:
+ case Intrinsics::kLongRotateRight:
+ SimplifyRotate(instruction, false);
+ break;
+ case Intrinsics::kIntegerRotateLeft:
+ case Intrinsics::kLongRotateLeft:
+ SimplifyRotate(instruction, true);
+ break;
+ case Intrinsics::kIntegerCompare:
+ case Intrinsics::kLongCompare:
+ SimplifyCompare(instruction, /* is_signum */ false);
+ break;
+ case Intrinsics::kIntegerSignum:
+ case Intrinsics::kLongSignum:
+ SimplifyCompare(instruction, /* is_signum */ true);
+ break;
+ case Intrinsics::kFloatIsNaN:
+ case Intrinsics::kDoubleIsNaN:
+ SimplifyIsNaN(instruction);
+ break;
+ case Intrinsics::kFloatFloatToIntBits:
+ case Intrinsics::kDoubleDoubleToLongBits:
+ SimplifyFP2Int(instruction);
+ break;
+ default:
+ break;
}
}
diff --git a/compiler/optimizing/instruction_simplifier_arm.cc b/compiler/optimizing/instruction_simplifier_arm.cc
new file mode 100644
index 0000000000..db1f9a79aa
--- /dev/null
+++ b/compiler/optimizing/instruction_simplifier_arm.cc
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "instruction_simplifier_arm.h"
+#include "instruction_simplifier_shared.h"
+
+namespace art {
+namespace arm {
+
+void InstructionSimplifierArmVisitor::VisitMul(HMul* instruction) {
+ if (TryCombineMultiplyAccumulate(instruction, kArm)) {
+ RecordSimplification();
+ }
+}
+
+} // namespace arm
+} // namespace art
diff --git a/compiler/optimizing/instruction_simplifier_arm.h b/compiler/optimizing/instruction_simplifier_arm.h
new file mode 100644
index 0000000000..379b95d6ae
--- /dev/null
+++ b/compiler/optimizing/instruction_simplifier_arm.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_COMPILER_OPTIMIZING_INSTRUCTION_SIMPLIFIER_ARM_H_
+#define ART_COMPILER_OPTIMIZING_INSTRUCTION_SIMPLIFIER_ARM_H_
+
+#include "nodes.h"
+#include "optimization.h"
+
+namespace art {
+namespace arm {
+
+class InstructionSimplifierArmVisitor : public HGraphVisitor {
+ public:
+ InstructionSimplifierArmVisitor(HGraph* graph, OptimizingCompilerStats* stats)
+ : HGraphVisitor(graph), stats_(stats) {}
+
+ private:
+ void RecordSimplification() {
+ if (stats_ != nullptr) {
+ stats_->RecordStat(kInstructionSimplificationsArch);
+ }
+ }
+
+ void VisitMul(HMul* instruction) OVERRIDE;
+
+ OptimizingCompilerStats* stats_;
+};
+
+
+class InstructionSimplifierArm : public HOptimization {
+ public:
+ InstructionSimplifierArm(HGraph* graph, OptimizingCompilerStats* stats)
+ : HOptimization(graph, "instruction_simplifier_arm", stats) {}
+
+ void Run() OVERRIDE {
+ InstructionSimplifierArmVisitor visitor(graph_, stats_);
+ visitor.VisitReversePostOrder();
+ }
+};
+
+} // namespace arm
+} // namespace art
+
+#endif // ART_COMPILER_OPTIMIZING_INSTRUCTION_SIMPLIFIER_ARM_H_
diff --git a/compiler/optimizing/instruction_simplifier_arm64.cc b/compiler/optimizing/instruction_simplifier_arm64.cc
index 4bcfc54791..c2bbdccc29 100644
--- a/compiler/optimizing/instruction_simplifier_arm64.cc
+++ b/compiler/optimizing/instruction_simplifier_arm64.cc
@@ -17,6 +17,7 @@
#include "instruction_simplifier_arm64.h"
#include "common_arm64.h"
+#include "instruction_simplifier_shared.h"
#include "mirror/array-inl.h"
namespace art {
@@ -179,67 +180,53 @@ bool InstructionSimplifierArm64Visitor::TryMergeIntoUsersShifterOperand(HInstruc
return true;
}
-bool InstructionSimplifierArm64Visitor::TrySimpleMultiplyAccumulatePatterns(
- HMul* mul, HBinaryOperation* input_binop, HInstruction* input_other) {
- DCHECK(Primitive::IsIntOrLongType(mul->GetType()));
- DCHECK(input_binop->IsAdd() || input_binop->IsSub());
- DCHECK_NE(input_binop, input_other);
- if (!input_binop->HasOnlyOneNonEnvironmentUse()) {
- return false;
- }
-
- // Try to interpret patterns like
- // a * (b <+/-> 1)
- // as
- // (a * b) <+/-> a
- HInstruction* input_a = input_other;
- HInstruction* input_b = nullptr; // Set to a non-null value if we found a pattern to optimize.
- HInstruction::InstructionKind op_kind;
-
- if (input_binop->IsAdd()) {
- if ((input_binop->GetConstantRight() != nullptr) && input_binop->GetConstantRight()->IsOne()) {
- // Interpret
- // a * (b + 1)
- // as
- // (a * b) + a
- input_b = input_binop->GetLeastConstantLeft();
- op_kind = HInstruction::kAdd;
- }
- } else {
- DCHECK(input_binop->IsSub());
- if (input_binop->GetRight()->IsConstant() &&
- input_binop->GetRight()->AsConstant()->IsMinusOne()) {
- // Interpret
- // a * (b - (-1))
- // as
- // a + (a * b)
- input_b = input_binop->GetLeft();
- op_kind = HInstruction::kAdd;
- } else if (input_binop->GetLeft()->IsConstant() &&
- input_binop->GetLeft()->AsConstant()->IsOne()) {
- // Interpret
- // a * (1 - b)
- // as
- // a - (a * b)
- input_b = input_binop->GetRight();
- op_kind = HInstruction::kSub;
+bool InstructionSimplifierArm64Visitor::TryMergeNegatedInput(HBinaryOperation* op) {
+ DCHECK(op->IsAnd() || op->IsOr() || op->IsXor()) << op->DebugName();
+ HInstruction* left = op->GetLeft();
+ HInstruction* right = op->GetRight();
+
+ // Only consider the case where there is exactly one Not, with 2 Not's De
+ // Morgan's laws should be applied instead.
+ if (left->IsNot() ^ right->IsNot()) {
+ HInstruction* hnot = (left->IsNot() ? left : right);
+ HInstruction* hother = (left->IsNot() ? right : left);
+
+ // Only do the simplification if the Not has only one use and can thus be
+ // safely removed. Even though ARM64 negated bitwise operations do not have
+ // an immediate variant (only register), we still do the simplification when
+ // `hother` is a constant, because it removes an instruction if the constant
+ // cannot be encoded as an immediate:
+ // mov r0, #large_constant
+ // neg r2, r1
+ // and r0, r0, r2
+ // becomes:
+ // mov r0, #large_constant
+ // bic r0, r0, r1
+ if (hnot->HasOnlyOneNonEnvironmentUse()) {
+ // Replace code looking like
+ // NOT tmp, mask
+ // AND dst, src, tmp (respectively ORR, EOR)
+ // with
+ // BIC dst, src, mask (respectively ORN, EON)
+ HInstruction* src = hnot->AsNot()->GetInput();
+
+ HArm64BitwiseNegatedRight* neg_op = new (GetGraph()->GetArena())
+ HArm64BitwiseNegatedRight(op->GetType(), op->GetKind(), hother, src, op->GetDexPc());
+
+ op->GetBlock()->ReplaceAndRemoveInstructionWith(op, neg_op);
+ hnot->GetBlock()->RemoveInstruction(hnot);
+ RecordSimplification();
+ return true;
}
}
- if (input_b == nullptr) {
- // We did not find a pattern we can optimize.
- return false;
- }
-
- HArm64MultiplyAccumulate* mulacc = new(GetGraph()->GetArena()) HArm64MultiplyAccumulate(
- mul->GetType(), op_kind, input_a, input_a, input_b, mul->GetDexPc());
-
- mul->GetBlock()->ReplaceAndRemoveInstructionWith(mul, mulacc);
- input_binop->GetBlock()->RemoveInstruction(input_binop);
-
return false;
}
+void InstructionSimplifierArm64Visitor::VisitAnd(HAnd* instruction) {
+ TryMergeNegatedInput(instruction);
+}
+
void InstructionSimplifierArm64Visitor::VisitArrayGet(HArrayGet* instruction) {
TryExtractArrayAccessAddress(instruction,
instruction->GetArray(),
@@ -255,76 +242,13 @@ void InstructionSimplifierArm64Visitor::VisitArraySet(HArraySet* instruction) {
}
void InstructionSimplifierArm64Visitor::VisitMul(HMul* instruction) {
- Primitive::Type type = instruction->GetType();
- if (!Primitive::IsIntOrLongType(type)) {
- return;
- }
-
- HInstruction* use = instruction->HasNonEnvironmentUses()
- ? instruction->GetUses().GetFirst()->GetUser()
- : nullptr;
-
- if (instruction->HasOnlyOneNonEnvironmentUse() && (use->IsAdd() || use->IsSub())) {
- // Replace code looking like
- // MUL tmp, x, y
- // SUB dst, acc, tmp
- // with
- // MULSUB dst, acc, x, y
- // Note that we do not want to (unconditionally) perform the merge when the
- // multiplication has multiple uses and it can be merged in all of them.
- // Multiple uses could happen on the same control-flow path, and we would
- // then increase the amount of work. In the future we could try to evaluate
- // whether all uses are on different control-flow paths (using dominance and
- // reverse-dominance information) and only perform the merge when they are.
- HInstruction* accumulator = nullptr;
- HBinaryOperation* binop = use->AsBinaryOperation();
- HInstruction* binop_left = binop->GetLeft();
- HInstruction* binop_right = binop->GetRight();
- // Be careful after GVN. This should not happen since the `HMul` has only
- // one use.
- DCHECK_NE(binop_left, binop_right);
- if (binop_right == instruction) {
- accumulator = binop_left;
- } else if (use->IsAdd()) {
- DCHECK_EQ(binop_left, instruction);
- accumulator = binop_right;
- }
-
- if (accumulator != nullptr) {
- HArm64MultiplyAccumulate* mulacc =
- new (GetGraph()->GetArena()) HArm64MultiplyAccumulate(type,
- binop->GetKind(),
- accumulator,
- instruction->GetLeft(),
- instruction->GetRight());
-
- binop->GetBlock()->ReplaceAndRemoveInstructionWith(binop, mulacc);
- DCHECK(!instruction->HasUses());
- instruction->GetBlock()->RemoveInstruction(instruction);
- RecordSimplification();
- return;
- }
- }
-
- // Use multiply accumulate instruction for a few simple patterns.
- // We prefer not applying the following transformations if the left and
- // right inputs perform the same operation.
- // We rely on GVN having squashed the inputs if appropriate. However the
- // results are still correct even if that did not happen.
- if (instruction->GetLeft() == instruction->GetRight()) {
- return;
+ if (TryCombineMultiplyAccumulate(instruction, kArm64)) {
+ RecordSimplification();
}
+}
- HInstruction* left = instruction->GetLeft();
- HInstruction* right = instruction->GetRight();
- if ((right->IsAdd() || right->IsSub()) &&
- TrySimpleMultiplyAccumulatePatterns(instruction, right->AsBinaryOperation(), left)) {
- return;
- }
- if ((left->IsAdd() || left->IsSub()) &&
- TrySimpleMultiplyAccumulatePatterns(instruction, left->AsBinaryOperation(), right)) {
- return;
- }
+void InstructionSimplifierArm64Visitor::VisitOr(HOr* instruction) {
+ TryMergeNegatedInput(instruction);
}
void InstructionSimplifierArm64Visitor::VisitShl(HShl* instruction) {
@@ -359,5 +283,9 @@ void InstructionSimplifierArm64Visitor::VisitUShr(HUShr* instruction) {
}
}
+void InstructionSimplifierArm64Visitor::VisitXor(HXor* instruction) {
+ TryMergeNegatedInput(instruction);
+}
+
} // namespace arm64
} // namespace art
diff --git a/compiler/optimizing/instruction_simplifier_arm64.h b/compiler/optimizing/instruction_simplifier_arm64.h
index b7f490bb8c..cf8458713f 100644
--- a/compiler/optimizing/instruction_simplifier_arm64.h
+++ b/compiler/optimizing/instruction_simplifier_arm64.h
@@ -51,18 +51,21 @@ class InstructionSimplifierArm64Visitor : public HGraphVisitor {
return TryMergeIntoShifterOperand(use, bitfield_op, true);
}
- bool TrySimpleMultiplyAccumulatePatterns(HMul* mul,
- HBinaryOperation* input_binop,
- HInstruction* input_other);
+ // For bitwise operations (And/Or/Xor) with a negated input, try to use
+ // a negated bitwise instruction.
+ bool TryMergeNegatedInput(HBinaryOperation* op);
// HInstruction visitors, sorted alphabetically.
+ void VisitAnd(HAnd* instruction) OVERRIDE;
void VisitArrayGet(HArrayGet* instruction) OVERRIDE;
void VisitArraySet(HArraySet* instruction) OVERRIDE;
void VisitMul(HMul* instruction) OVERRIDE;
+ void VisitOr(HOr* instruction) OVERRIDE;
void VisitShl(HShl* instruction) OVERRIDE;
void VisitShr(HShr* instruction) OVERRIDE;
void VisitTypeConversion(HTypeConversion* instruction) OVERRIDE;
void VisitUShr(HUShr* instruction) OVERRIDE;
+ void VisitXor(HXor* instruction) OVERRIDE;
OptimizingCompilerStats* stats_;
};
diff --git a/compiler/optimizing/instruction_simplifier_shared.cc b/compiler/optimizing/instruction_simplifier_shared.cc
new file mode 100644
index 0000000000..45d196fa6d
--- /dev/null
+++ b/compiler/optimizing/instruction_simplifier_shared.cc
@@ -0,0 +1,189 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "instruction_simplifier_shared.h"
+
+namespace art {
+
+namespace {
+
+bool TrySimpleMultiplyAccumulatePatterns(HMul* mul,
+ HBinaryOperation* input_binop,
+ HInstruction* input_other) {
+ DCHECK(Primitive::IsIntOrLongType(mul->GetType()));
+ DCHECK(input_binop->IsAdd() || input_binop->IsSub());
+ DCHECK_NE(input_binop, input_other);
+ if (!input_binop->HasOnlyOneNonEnvironmentUse()) {
+ return false;
+ }
+
+ // Try to interpret patterns like
+ // a * (b <+/-> 1)
+ // as
+ // (a * b) <+/-> a
+ HInstruction* input_a = input_other;
+ HInstruction* input_b = nullptr; // Set to a non-null value if we found a pattern to optimize.
+ HInstruction::InstructionKind op_kind;
+
+ if (input_binop->IsAdd()) {
+ if ((input_binop->GetConstantRight() != nullptr) && input_binop->GetConstantRight()->IsOne()) {
+ // Interpret
+ // a * (b + 1)
+ // as
+ // (a * b) + a
+ input_b = input_binop->GetLeastConstantLeft();
+ op_kind = HInstruction::kAdd;
+ }
+ } else {
+ DCHECK(input_binop->IsSub());
+ if (input_binop->GetRight()->IsConstant() &&
+ input_binop->GetRight()->AsConstant()->IsMinusOne()) {
+ // Interpret
+ // a * (b - (-1))
+ // as
+ // a + (a * b)
+ input_b = input_binop->GetLeft();
+ op_kind = HInstruction::kAdd;
+ } else if (input_binop->GetLeft()->IsConstant() &&
+ input_binop->GetLeft()->AsConstant()->IsOne()) {
+ // Interpret
+ // a * (1 - b)
+ // as
+ // a - (a * b)
+ input_b = input_binop->GetRight();
+ op_kind = HInstruction::kSub;
+ }
+ }
+
+ if (input_b == nullptr) {
+ // We did not find a pattern we can optimize.
+ return false;
+ }
+
+ ArenaAllocator* arena = mul->GetBlock()->GetGraph()->GetArena();
+ HMultiplyAccumulate* mulacc = new(arena) HMultiplyAccumulate(
+ mul->GetType(), op_kind, input_a, input_a, input_b, mul->GetDexPc());
+
+ mul->GetBlock()->ReplaceAndRemoveInstructionWith(mul, mulacc);
+ input_binop->GetBlock()->RemoveInstruction(input_binop);
+
+ return true;
+}
+
+} // namespace
+
+bool TryCombineMultiplyAccumulate(HMul* mul, InstructionSet isa) {
+ Primitive::Type type = mul->GetType();
+ switch (isa) {
+ case kArm:
+ case kThumb2:
+ if (type != Primitive::kPrimInt) {
+ return false;
+ }
+ break;
+ case kArm64:
+ if (!Primitive::IsIntOrLongType(type)) {
+ return false;
+ }
+ break;
+ default:
+ return false;
+ }
+
+ HInstruction* use = mul->HasNonEnvironmentUses()
+ ? mul->GetUses().GetFirst()->GetUser()
+ : nullptr;
+
+ ArenaAllocator* arena = mul->GetBlock()->GetGraph()->GetArena();
+
+ if (mul->HasOnlyOneNonEnvironmentUse()) {
+ if (use->IsAdd() || use->IsSub()) {
+ // Replace code looking like
+ // MUL tmp, x, y
+ // SUB dst, acc, tmp
+ // with
+ // MULSUB dst, acc, x, y
+ // Note that we do not want to (unconditionally) perform the merge when the
+ // multiplication has multiple uses and it can be merged in all of them.
+ // Multiple uses could happen on the same control-flow path, and we would
+ // then increase the amount of work. In the future we could try to evaluate
+ // whether all uses are on different control-flow paths (using dominance and
+ // reverse-dominance information) and only perform the merge when they are.
+ HInstruction* accumulator = nullptr;
+ HBinaryOperation* binop = use->AsBinaryOperation();
+ HInstruction* binop_left = binop->GetLeft();
+ HInstruction* binop_right = binop->GetRight();
+ // Be careful after GVN. This should not happen since the `HMul` has only
+ // one use.
+ DCHECK_NE(binop_left, binop_right);
+ if (binop_right == mul) {
+ accumulator = binop_left;
+ } else if (use->IsAdd()) {
+ DCHECK_EQ(binop_left, mul);
+ accumulator = binop_right;
+ }
+
+ if (accumulator != nullptr) {
+ HMultiplyAccumulate* mulacc =
+ new (arena) HMultiplyAccumulate(type,
+ binop->GetKind(),
+ accumulator,
+ mul->GetLeft(),
+ mul->GetRight());
+
+ binop->GetBlock()->ReplaceAndRemoveInstructionWith(binop, mulacc);
+ DCHECK(!mul->HasUses());
+ mul->GetBlock()->RemoveInstruction(mul);
+ return true;
+ }
+ } else if (use->IsNeg() && isa != kArm) {
+ HMultiplyAccumulate* mulacc =
+ new (arena) HMultiplyAccumulate(type,
+ HInstruction::kSub,
+ mul->GetBlock()->GetGraph()->GetConstant(type, 0),
+ mul->GetLeft(),
+ mul->GetRight());
+
+ use->GetBlock()->ReplaceAndRemoveInstructionWith(use, mulacc);
+ DCHECK(!mul->HasUses());
+ mul->GetBlock()->RemoveInstruction(mul);
+ return true;
+ }
+ }
+
+ // Use multiply accumulate instruction for a few simple patterns.
+ // We prefer not applying the following transformations if the left and
+ // right inputs perform the same operation.
+ // We rely on GVN having squashed the inputs if appropriate. However the
+ // results are still correct even if that did not happen.
+ if (mul->GetLeft() == mul->GetRight()) {
+ return false;
+ }
+
+ HInstruction* left = mul->GetLeft();
+ HInstruction* right = mul->GetRight();
+ if ((right->IsAdd() || right->IsSub()) &&
+ TrySimpleMultiplyAccumulatePatterns(mul, right->AsBinaryOperation(), left)) {
+ return true;
+ }
+ if ((left->IsAdd() || left->IsSub()) &&
+ TrySimpleMultiplyAccumulatePatterns(mul, left->AsBinaryOperation(), right)) {
+ return true;
+ }
+ return false;
+}
+
+} // namespace art
diff --git a/compiler/optimizing/instruction_simplifier_shared.h b/compiler/optimizing/instruction_simplifier_shared.h
new file mode 100644
index 0000000000..9832ecc058
--- /dev/null
+++ b/compiler/optimizing/instruction_simplifier_shared.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_COMPILER_OPTIMIZING_INSTRUCTION_SIMPLIFIER_SHARED_H_
+#define ART_COMPILER_OPTIMIZING_INSTRUCTION_SIMPLIFIER_SHARED_H_
+
+#include "nodes.h"
+
+namespace art {
+
+bool TryCombineMultiplyAccumulate(HMul* mul, InstructionSet isa);
+
+} // namespace art
+
+#endif // ART_COMPILER_OPTIMIZING_INSTRUCTION_SIMPLIFIER_SHARED_H_
diff --git a/compiler/optimizing/intrinsics.cc b/compiler/optimizing/intrinsics.cc
index 316e86b4c9..3ed0278871 100644
--- a/compiler/optimizing/intrinsics.cc
+++ b/compiler/optimizing/intrinsics.cc
@@ -130,6 +130,10 @@ static Intrinsics GetIntrinsic(InlineMethod method) {
case kIntrinsicFloatCvt:
return ((method.d.data & kIntrinsicFlagToFloatingPoint) == 0) ?
Intrinsics::kFloatFloatToRawIntBits : Intrinsics::kFloatIntBitsToFloat;
+ case kIntrinsicFloat2Int:
+ return Intrinsics::kFloatFloatToIntBits;
+ case kIntrinsicDouble2Long:
+ return Intrinsics::kDoubleDoubleToLongBits;
// Floating-point tests.
case kIntrinsicFloatIsInfinite:
diff --git a/compiler/optimizing/intrinsics_arm.cc b/compiler/optimizing/intrinsics_arm.cc
index 8cbdcbbcaf..4ce919ee39 100644
--- a/compiler/optimizing/intrinsics_arm.cc
+++ b/compiler/optimizing/intrinsics_arm.cc
@@ -1909,6 +1909,69 @@ void IntrinsicCodeGeneratorARM::VisitShortReverseBytes(HInvoke* invoke) {
__ revsh(out, in);
}
+void IntrinsicLocationsBuilderARM::VisitStringGetCharsNoCheck(HInvoke* invoke) {
+ LocationSummary* locations = new (arena_) LocationSummary(invoke,
+ LocationSummary::kNoCall,
+ kIntrinsified);
+ locations->SetInAt(0, Location::RequiresRegister());
+ locations->SetInAt(1, Location::RequiresRegister());
+ locations->SetInAt(2, Location::RequiresRegister());
+ locations->SetInAt(3, Location::RequiresRegister());
+ locations->SetInAt(4, Location::RequiresRegister());
+
+ locations->AddTemp(Location::RequiresRegister());
+ locations->AddTemp(Location::RequiresRegister());
+ locations->AddTemp(Location::RequiresRegister());
+ locations->AddTemp(Location::RequiresRegister());
+}
+
+void IntrinsicCodeGeneratorARM::VisitStringGetCharsNoCheck(HInvoke* invoke) {
+ ArmAssembler* assembler = GetAssembler();
+ LocationSummary* locations = invoke->GetLocations();
+
+ // Check assumption that sizeof(Char) is 2 (used in scaling below).
+ const size_t char_size = Primitive::ComponentSize(Primitive::kPrimChar);
+ DCHECK_EQ(char_size, 2u);
+
+ // Location of data in char array buffer.
+ const uint32_t data_offset = mirror::Array::DataOffset(char_size).Uint32Value();
+
+ // Location of char array data in string.
+ const uint32_t value_offset = mirror::String::ValueOffset().Uint32Value();
+
+ // void getCharsNoCheck(int srcBegin, int srcEnd, char[] dst, int dstBegin);
+ // Since getChars() calls getCharsNoCheck() - we use registers rather than constants.
+ Register srcObj = locations->InAt(0).AsRegister<Register>();
+ Register srcBegin = locations->InAt(1).AsRegister<Register>();
+ Register srcEnd = locations->InAt(2).AsRegister<Register>();
+ Register dstObj = locations->InAt(3).AsRegister<Register>();
+ Register dstBegin = locations->InAt(4).AsRegister<Register>();
+
+ Register src_ptr = locations->GetTemp(0).AsRegister<Register>();
+ Register src_ptr_end = locations->GetTemp(1).AsRegister<Register>();
+ Register dst_ptr = locations->GetTemp(2).AsRegister<Register>();
+ Register tmp = locations->GetTemp(3).AsRegister<Register>();
+
+ // src range to copy.
+ __ add(src_ptr, srcObj, ShifterOperand(value_offset));
+ __ add(src_ptr_end, src_ptr, ShifterOperand(srcEnd, LSL, 1));
+ __ add(src_ptr, src_ptr, ShifterOperand(srcBegin, LSL, 1));
+
+ // dst to be copied.
+ __ add(dst_ptr, dstObj, ShifterOperand(data_offset));
+ __ add(dst_ptr, dst_ptr, ShifterOperand(dstBegin, LSL, 1));
+
+ // Do the copy.
+ Label loop, done;
+ __ Bind(&loop);
+ __ cmp(src_ptr, ShifterOperand(src_ptr_end));
+ __ b(&done, EQ);
+ __ ldrh(tmp, Address(src_ptr, char_size, Address::PostIndex));
+ __ strh(tmp, Address(dst_ptr, char_size, Address::PostIndex));
+ __ b(&loop);
+ __ Bind(&done);
+}
+
// Unimplemented intrinsics.
#define UNIMPLEMENTED_INTRINSIC(Name) \
@@ -1933,7 +1996,6 @@ UNIMPLEMENTED_INTRINSIC(MathRoundFloat) // Could be done by changing rounding
UNIMPLEMENTED_INTRINSIC(UnsafeCASLong) // High register pressure.
UNIMPLEMENTED_INTRINSIC(SystemArrayCopyChar)
UNIMPLEMENTED_INTRINSIC(ReferenceGetReferent)
-UNIMPLEMENTED_INTRINSIC(StringGetCharsNoCheck)
UNIMPLEMENTED_INTRINSIC(FloatIsInfinite)
UNIMPLEMENTED_INTRINSIC(DoubleIsInfinite)
@@ -1944,6 +2006,8 @@ UNIMPLEMENTED_INTRINSIC(IntegerLowestOneBit)
UNIMPLEMENTED_INTRINSIC(LongLowestOneBit)
// Handled as HIR instructions.
+UNIMPLEMENTED_INTRINSIC(FloatFloatToIntBits)
+UNIMPLEMENTED_INTRINSIC(DoubleDoubleToLongBits)
UNIMPLEMENTED_INTRINSIC(FloatIsNaN)
UNIMPLEMENTED_INTRINSIC(DoubleIsNaN)
UNIMPLEMENTED_INTRINSIC(IntegerRotateLeft)
diff --git a/compiler/optimizing/intrinsics_arm64.cc b/compiler/optimizing/intrinsics_arm64.cc
index b5f15fe22d..4be1695a94 100644
--- a/compiler/optimizing/intrinsics_arm64.cc
+++ b/compiler/optimizing/intrinsics_arm64.cc
@@ -1602,6 +1602,69 @@ void IntrinsicCodeGeneratorARM64::VisitMathNextAfter(HInvoke* invoke) {
GenFPToFPCall(invoke, GetVIXLAssembler(), codegen_, kQuickNextAfter);
}
+void IntrinsicLocationsBuilderARM64::VisitStringGetCharsNoCheck(HInvoke* invoke) {
+ LocationSummary* locations = new (arena_) LocationSummary(invoke,
+ LocationSummary::kNoCall,
+ kIntrinsified);
+ locations->SetInAt(0, Location::RequiresRegister());
+ locations->SetInAt(1, Location::RequiresRegister());
+ locations->SetInAt(2, Location::RequiresRegister());
+ locations->SetInAt(3, Location::RequiresRegister());
+ locations->SetInAt(4, Location::RequiresRegister());
+
+ locations->AddTemp(Location::RequiresRegister());
+ locations->AddTemp(Location::RequiresRegister());
+}
+
+void IntrinsicCodeGeneratorARM64::VisitStringGetCharsNoCheck(HInvoke* invoke) {
+ vixl::MacroAssembler* masm = GetVIXLAssembler();
+ LocationSummary* locations = invoke->GetLocations();
+
+ // Check assumption that sizeof(Char) is 2 (used in scaling below).
+ const size_t char_size = Primitive::ComponentSize(Primitive::kPrimChar);
+ DCHECK_EQ(char_size, 2u);
+
+ // Location of data in char array buffer.
+ const uint32_t data_offset = mirror::Array::DataOffset(char_size).Uint32Value();
+
+ // Location of char array data in string.
+ const uint32_t value_offset = mirror::String::ValueOffset().Uint32Value();
+
+ // void getCharsNoCheck(int srcBegin, int srcEnd, char[] dst, int dstBegin);
+ // Since getChars() calls getCharsNoCheck() - we use registers rather than constants.
+ Register srcObj = XRegisterFrom(locations->InAt(0));
+ Register srcBegin = XRegisterFrom(locations->InAt(1));
+ Register srcEnd = XRegisterFrom(locations->InAt(2));
+ Register dstObj = XRegisterFrom(locations->InAt(3));
+ Register dstBegin = XRegisterFrom(locations->InAt(4));
+
+ Register src_ptr = XRegisterFrom(locations->GetTemp(0));
+ Register src_ptr_end = XRegisterFrom(locations->GetTemp(1));
+
+ UseScratchRegisterScope temps(masm);
+ Register dst_ptr = temps.AcquireX();
+ Register tmp = temps.AcquireW();
+
+ // src range to copy.
+ __ Add(src_ptr, srcObj, Operand(value_offset));
+ __ Add(src_ptr_end, src_ptr, Operand(srcEnd, LSL, 1));
+ __ Add(src_ptr, src_ptr, Operand(srcBegin, LSL, 1));
+
+ // dst to be copied.
+ __ Add(dst_ptr, dstObj, Operand(data_offset));
+ __ Add(dst_ptr, dst_ptr, Operand(dstBegin, LSL, 1));
+
+ // Do the copy.
+ vixl::Label loop, done;
+ __ Bind(&loop);
+ __ Cmp(src_ptr, src_ptr_end);
+ __ B(&done, eq);
+ __ Ldrh(tmp, MemOperand(src_ptr, char_size, vixl::PostIndex));
+ __ Strh(tmp, MemOperand(dst_ptr, char_size, vixl::PostIndex));
+ __ B(&loop);
+ __ Bind(&done);
+}
+
// Unimplemented intrinsics.
#define UNIMPLEMENTED_INTRINSIC(Name) \
@@ -1615,7 +1678,6 @@ UNIMPLEMENTED_INTRINSIC(LongBitCount)
UNIMPLEMENTED_INTRINSIC(SystemArrayCopyChar)
UNIMPLEMENTED_INTRINSIC(SystemArrayCopy)
UNIMPLEMENTED_INTRINSIC(ReferenceGetReferent)
-UNIMPLEMENTED_INTRINSIC(StringGetCharsNoCheck)
UNIMPLEMENTED_INTRINSIC(FloatIsInfinite)
UNIMPLEMENTED_INTRINSIC(DoubleIsInfinite)
@@ -1626,6 +1688,8 @@ UNIMPLEMENTED_INTRINSIC(IntegerLowestOneBit)
UNIMPLEMENTED_INTRINSIC(LongLowestOneBit)
// Handled as HIR instructions.
+UNIMPLEMENTED_INTRINSIC(FloatFloatToIntBits)
+UNIMPLEMENTED_INTRINSIC(DoubleDoubleToLongBits)
UNIMPLEMENTED_INTRINSIC(FloatIsNaN)
UNIMPLEMENTED_INTRINSIC(DoubleIsNaN)
UNIMPLEMENTED_INTRINSIC(IntegerRotateLeft)
diff --git a/compiler/optimizing/intrinsics_list.h b/compiler/optimizing/intrinsics_list.h
index 88217b308e..e1aea924cf 100644
--- a/compiler/optimizing/intrinsics_list.h
+++ b/compiler/optimizing/intrinsics_list.h
@@ -23,10 +23,12 @@
#define INTRINSICS_LIST(V) \
V(DoubleDoubleToRawLongBits, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow) \
+ V(DoubleDoubleToLongBits, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow) \
V(DoubleIsInfinite, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow) \
V(DoubleIsNaN, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow) \
V(DoubleLongBitsToDouble, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow) \
V(FloatFloatToRawIntBits, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow) \
+ V(FloatFloatToIntBits, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow) \
V(FloatIsInfinite, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow) \
V(FloatIsNaN, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow) \
V(FloatIntBitsToFloat, kStatic, kNeedsEnvironmentOrCache, kNoSideEffects, kNoThrow) \
diff --git a/compiler/optimizing/intrinsics_mips.cc b/compiler/optimizing/intrinsics_mips.cc
index 2f183c3a62..a737d8100a 100644
--- a/compiler/optimizing/intrinsics_mips.cc
+++ b/compiler/optimizing/intrinsics_mips.cc
@@ -1772,6 +1772,8 @@ UNIMPLEMENTED_INTRINSIC(IntegerLowestOneBit)
UNIMPLEMENTED_INTRINSIC(LongLowestOneBit)
// Handled as HIR instructions.
+UNIMPLEMENTED_INTRINSIC(FloatFloatToIntBits)
+UNIMPLEMENTED_INTRINSIC(DoubleDoubleToLongBits)
UNIMPLEMENTED_INTRINSIC(FloatIsNaN)
UNIMPLEMENTED_INTRINSIC(DoubleIsNaN)
UNIMPLEMENTED_INTRINSIC(IntegerCompare)
diff --git a/compiler/optimizing/intrinsics_mips64.cc b/compiler/optimizing/intrinsics_mips64.cc
index bd4f5329da..ca2652b74a 100644
--- a/compiler/optimizing/intrinsics_mips64.cc
+++ b/compiler/optimizing/intrinsics_mips64.cc
@@ -1824,6 +1824,8 @@ UNIMPLEMENTED_INTRINSIC(IntegerLowestOneBit)
UNIMPLEMENTED_INTRINSIC(LongLowestOneBit)
// Handled as HIR instructions.
+UNIMPLEMENTED_INTRINSIC(FloatFloatToIntBits)
+UNIMPLEMENTED_INTRINSIC(DoubleDoubleToLongBits)
UNIMPLEMENTED_INTRINSIC(FloatIsNaN)
UNIMPLEMENTED_INTRINSIC(DoubleIsNaN)
UNIMPLEMENTED_INTRINSIC(IntegerCompare)
diff --git a/compiler/optimizing/intrinsics_x86.cc b/compiler/optimizing/intrinsics_x86.cc
index 260a8773fb..0df4553f56 100644
--- a/compiler/optimizing/intrinsics_x86.cc
+++ b/compiler/optimizing/intrinsics_x86.cc
@@ -2642,6 +2642,8 @@ UNIMPLEMENTED_INTRINSIC(IntegerLowestOneBit)
UNIMPLEMENTED_INTRINSIC(LongLowestOneBit)
// Handled as HIR instructions.
+UNIMPLEMENTED_INTRINSIC(FloatFloatToIntBits)
+UNIMPLEMENTED_INTRINSIC(DoubleDoubleToLongBits)
UNIMPLEMENTED_INTRINSIC(FloatIsNaN)
UNIMPLEMENTED_INTRINSIC(DoubleIsNaN)
UNIMPLEMENTED_INTRINSIC(IntegerRotateLeft)
diff --git a/compiler/optimizing/intrinsics_x86_64.cc b/compiler/optimizing/intrinsics_x86_64.cc
index 93e8c00e5a..2a9e684d11 100644
--- a/compiler/optimizing/intrinsics_x86_64.cc
+++ b/compiler/optimizing/intrinsics_x86_64.cc
@@ -2719,6 +2719,8 @@ UNIMPLEMENTED_INTRINSIC(FloatIsInfinite)
UNIMPLEMENTED_INTRINSIC(DoubleIsInfinite)
// Handled as HIR instructions.
+UNIMPLEMENTED_INTRINSIC(FloatFloatToIntBits)
+UNIMPLEMENTED_INTRINSIC(DoubleDoubleToLongBits)
UNIMPLEMENTED_INTRINSIC(FloatIsNaN)
UNIMPLEMENTED_INTRINSIC(DoubleIsNaN)
UNIMPLEMENTED_INTRINSIC(IntegerRotateLeft)
diff --git a/compiler/optimizing/nodes.cc b/compiler/optimizing/nodes.cc
index f9acb089ee..87b9c022df 100644
--- a/compiler/optimizing/nodes.cc
+++ b/compiler/optimizing/nodes.cc
@@ -1877,6 +1877,42 @@ void HGraph::DeleteDeadEmptyBlock(HBasicBlock* block) {
blocks_[block->GetBlockId()] = nullptr;
}
+void HGraph::UpdateLoopAndTryInformationOfNewBlock(HBasicBlock* block,
+ HBasicBlock* reference,
+ bool replace_if_back_edge) {
+ if (block->IsLoopHeader()) {
+ // Clear the information of which blocks are contained in that loop. Since the
+ // information is stored as a bit vector based on block ids, we have to update
+ // it, as those block ids were specific to the callee graph and we are now adding
+ // these blocks to the caller graph.
+ block->GetLoopInformation()->ClearAllBlocks();
+ }
+
+ // If not already in a loop, update the loop information.
+ if (!block->IsInLoop()) {
+ block->SetLoopInformation(reference->GetLoopInformation());
+ }
+
+ // If the block is in a loop, update all its outward loops.
+ HLoopInformation* loop_info = block->GetLoopInformation();
+ if (loop_info != nullptr) {
+ for (HLoopInformationOutwardIterator loop_it(*block);
+ !loop_it.Done();
+ loop_it.Advance()) {
+ loop_it.Current()->Add(block);
+ }
+ if (replace_if_back_edge && loop_info->IsBackEdge(*reference)) {
+ loop_info->ReplaceBackEdge(reference, block);
+ }
+ }
+
+ // Copy TryCatchInformation if `reference` is a try block, not if it is a catch block.
+ TryCatchInformation* try_catch_info = reference->IsTryBlock()
+ ? reference->GetTryCatchInformation()
+ : nullptr;
+ block->SetTryCatchInformation(try_catch_info);
+}
+
HInstruction* HGraph::InlineInto(HGraph* outer_graph, HInvoke* invoke) {
DCHECK(HasExitBlock()) << "Unimplemented scenario";
// Update the environments in this graph to have the invoke's environment
@@ -1991,10 +2027,6 @@ HInstruction* HGraph::InlineInto(HGraph* outer_graph, HInvoke* invoke) {
size_t index_of_at = IndexOfElement(outer_graph->reverse_post_order_, at);
MakeRoomFor(&outer_graph->reverse_post_order_, blocks_added, index_of_at);
- HLoopInformation* loop_info = at->GetLoopInformation();
- // Copy TryCatchInformation if `at` is a try block, not if it is a catch block.
- TryCatchInformation* try_catch_info = at->IsTryBlock() ? at->GetTryCatchInformation() : nullptr;
-
// Do a reverse post order of the blocks in the callee and do (1), (2), (3)
// and (4) to the blocks that apply.
for (HReversePostOrderIterator it(*this); !it.Done(); it.Advance()) {
@@ -2005,23 +2037,7 @@ HInstruction* HGraph::InlineInto(HGraph* outer_graph, HInvoke* invoke) {
current->SetGraph(outer_graph);
outer_graph->AddBlock(current);
outer_graph->reverse_post_order_[++index_of_at] = current;
- if (!current->IsInLoop()) {
- current->SetLoopInformation(loop_info);
- } else if (current->IsLoopHeader()) {
- // Clear the information of which blocks are contained in that loop. Since the
- // information is stored as a bit vector based on block ids, we have to update
- // it, as those block ids were specific to the callee graph and we are now adding
- // these blocks to the caller graph.
- current->GetLoopInformation()->ClearAllBlocks();
- }
- if (current->IsInLoop()) {
- for (HLoopInformationOutwardIterator loop_it(*current);
- !loop_it.Done();
- loop_it.Advance()) {
- loop_it.Current()->Add(current);
- }
- }
- current->SetTryCatchInformation(try_catch_info);
+ UpdateLoopAndTryInformationOfNewBlock(current, at, /* replace_if_back_edge */ false);
}
}
@@ -2029,20 +2045,9 @@ HInstruction* HGraph::InlineInto(HGraph* outer_graph, HInvoke* invoke) {
to->SetGraph(outer_graph);
outer_graph->AddBlock(to);
outer_graph->reverse_post_order_[++index_of_at] = to;
- if (loop_info != nullptr) {
- if (!to->IsInLoop()) {
- to->SetLoopInformation(loop_info);
- }
- for (HLoopInformationOutwardIterator loop_it(*at); !loop_it.Done(); loop_it.Advance()) {
- loop_it.Current()->Add(to);
- }
- if (loop_info->IsBackEdge(*at)) {
- // Only `to` can become a back edge, as the inlined blocks
- // are predecessors of `to`.
- loop_info->ReplaceBackEdge(at, to);
- }
- }
- to->SetTryCatchInformation(try_catch_info);
+ // Only `to` can become a back edge, as the inlined blocks
+ // are predecessors of `to`.
+ UpdateLoopAndTryInformationOfNewBlock(to, at, /* replace_if_back_edge */ true);
}
// Update the next instruction id of the outer graph, so that instructions
@@ -2157,32 +2162,17 @@ void HGraph::TransformLoopHeaderForBCE(HBasicBlock* header) {
reverse_post_order_[index_of_header++] = false_block;
reverse_post_order_[index_of_header++] = new_pre_header;
- // Fix loop information.
- HLoopInformation* loop_info = old_pre_header->GetLoopInformation();
- if (loop_info != nullptr) {
- if_block->SetLoopInformation(loop_info);
- true_block->SetLoopInformation(loop_info);
- false_block->SetLoopInformation(loop_info);
- new_pre_header->SetLoopInformation(loop_info);
- // Add blocks to all enveloping loops.
- for (HLoopInformationOutwardIterator loop_it(*old_pre_header);
- !loop_it.Done();
- loop_it.Advance()) {
- loop_it.Current()->Add(if_block);
- loop_it.Current()->Add(true_block);
- loop_it.Current()->Add(false_block);
- loop_it.Current()->Add(new_pre_header);
- }
- }
-
- // Fix try/catch information.
- TryCatchInformation* try_catch_info = old_pre_header->IsTryBlock()
- ? old_pre_header->GetTryCatchInformation()
- : nullptr;
- if_block->SetTryCatchInformation(try_catch_info);
- true_block->SetTryCatchInformation(try_catch_info);
- false_block->SetTryCatchInformation(try_catch_info);
- new_pre_header->SetTryCatchInformation(try_catch_info);
+ // The pre_header can never be a back edge of a loop.
+ DCHECK((old_pre_header->GetLoopInformation() == nullptr) ||
+ !old_pre_header->GetLoopInformation()->IsBackEdge(*old_pre_header));
+ UpdateLoopAndTryInformationOfNewBlock(
+ if_block, old_pre_header, /* replace_if_back_edge */ false);
+ UpdateLoopAndTryInformationOfNewBlock(
+ true_block, old_pre_header, /* replace_if_back_edge */ false);
+ UpdateLoopAndTryInformationOfNewBlock(
+ false_block, old_pre_header, /* replace_if_back_edge */ false);
+ UpdateLoopAndTryInformationOfNewBlock(
+ new_pre_header, old_pre_header, /* replace_if_back_edge */ false);
}
static void CheckAgainstUpperBound(ReferenceTypeInfo rti, ReferenceTypeInfo upper_bound_rti)
@@ -2206,7 +2196,8 @@ void HInstruction::SetReferenceTypeInfo(ReferenceTypeInfo rti) {
CheckAgainstUpperBound(rti, AsBoundType()->GetUpperBound());
}
}
- reference_type_info_ = rti;
+ reference_type_handle_ = rti.GetTypeHandle();
+ SetPackedFlag<kFlagReferenceTypeIsExact>(rti.IsExact());
}
void HBoundType::SetUpperBound(const ReferenceTypeInfo& upper_bound, bool can_be_null) {
@@ -2217,17 +2208,15 @@ void HBoundType::SetUpperBound(const ReferenceTypeInfo& upper_bound, bool can_be
CheckAgainstUpperBound(GetReferenceTypeInfo(), upper_bound);
}
upper_bound_ = upper_bound;
- upper_can_be_null_ = can_be_null;
+ SetPackedFlag<kFlagUpperCanBeNull>(can_be_null);
}
-ReferenceTypeInfo::ReferenceTypeInfo() : type_handle_(TypeHandle()), is_exact_(false) {}
-
-ReferenceTypeInfo::ReferenceTypeInfo(TypeHandle type_handle, bool is_exact)
- : type_handle_(type_handle), is_exact_(is_exact) {
+ReferenceTypeInfo ReferenceTypeInfo::Create(TypeHandle type_handle, bool is_exact) {
if (kIsDebugBuild) {
ScopedObjectAccess soa(Thread::Current());
DCHECK(IsValidHandle(type_handle));
}
+ return ReferenceTypeInfo(type_handle, is_exact);
}
std::ostream& operator<<(std::ostream& os, const ReferenceTypeInfo& rhs) {
diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h
index b355883a72..e3dbe16547 100644
--- a/compiler/optimizing/nodes.h
+++ b/compiler/optimizing/nodes.h
@@ -154,8 +154,9 @@ class ReferenceTypeInfo : ValueObject {
public:
typedef Handle<mirror::Class> TypeHandle;
- static ReferenceTypeInfo Create(TypeHandle type_handle, bool is_exact) {
- // The constructor will check that the type_handle is valid.
+ static ReferenceTypeInfo Create(TypeHandle type_handle, bool is_exact);
+
+ static ReferenceTypeInfo CreateUnchecked(TypeHandle type_handle, bool is_exact) {
return ReferenceTypeInfo(type_handle, is_exact);
}
@@ -254,8 +255,9 @@ class ReferenceTypeInfo : ValueObject {
}
private:
- ReferenceTypeInfo();
- ReferenceTypeInfo(TypeHandle type_handle, bool is_exact);
+ ReferenceTypeInfo() : type_handle_(TypeHandle()), is_exact_(false) {}
+ ReferenceTypeInfo(TypeHandle type_handle, bool is_exact)
+ : type_handle_(type_handle), is_exact_(is_exact) { }
// The class of the object.
TypeHandle type_handle_;
@@ -351,6 +353,13 @@ class HGraph : public ArenaObject<kArenaAllocGraph> {
// and removing the invoke instruction.
HInstruction* InlineInto(HGraph* outer_graph, HInvoke* invoke);
+ // Update the loop and try membership of `block`, which was spawned from `reference`.
+ // In case `reference` is a back edge, `replace_if_back_edge` notifies whether `block`
+ // should be the new back edge.
+ void UpdateLoopAndTryInformationOfNewBlock(HBasicBlock* block,
+ HBasicBlock* reference,
+ bool replace_if_back_edge);
+
// Need to add a couple of blocks to test if the loop body is entered and
// put deoptimization instructions, etc.
void TransformLoopHeaderForBCE(HBasicBlock* header);
@@ -1247,6 +1256,16 @@ class HLoopInformationOutwardIterator : public ValueObject {
M(UShr, BinaryOperation) \
M(Xor, BinaryOperation) \
+/*
+ * Instructions, shared across several (not all) architectures.
+ */
+#if !defined(ART_ENABLE_CODEGEN_arm) && !defined(ART_ENABLE_CODEGEN_arm64)
+#define FOR_EACH_CONCRETE_INSTRUCTION_SHARED(M)
+#else
+#define FOR_EACH_CONCRETE_INSTRUCTION_SHARED(M) \
+ M(MultiplyAccumulate, Instruction)
+#endif
+
#ifndef ART_ENABLE_CODEGEN_arm
#define FOR_EACH_CONCRETE_INSTRUCTION_ARM(M)
#else
@@ -1258,9 +1277,9 @@ class HLoopInformationOutwardIterator : public ValueObject {
#define FOR_EACH_CONCRETE_INSTRUCTION_ARM64(M)
#else
#define FOR_EACH_CONCRETE_INSTRUCTION_ARM64(M) \
+ M(Arm64BitwiseNegatedRight, Instruction) \
M(Arm64DataProcWithShifterOp, Instruction) \
- M(Arm64IntermediateAddress, Instruction) \
- M(Arm64MultiplyAccumulate, Instruction)
+ M(Arm64IntermediateAddress, Instruction)
#endif
#define FOR_EACH_CONCRETE_INSTRUCTION_MIPS(M)
@@ -1281,6 +1300,7 @@ class HLoopInformationOutwardIterator : public ValueObject {
#define FOR_EACH_CONCRETE_INSTRUCTION(M) \
FOR_EACH_CONCRETE_INSTRUCTION_COMMON(M) \
+ FOR_EACH_CONCRETE_INSTRUCTION_SHARED(M) \
FOR_EACH_CONCRETE_INSTRUCTION_ARM(M) \
FOR_EACH_CONCRETE_INSTRUCTION_ARM64(M) \
FOR_EACH_CONCRETE_INSTRUCTION_MIPS(M) \
@@ -1837,13 +1857,15 @@ class HInstruction : public ArenaObject<kArenaAllocInstruction> {
dex_pc_(dex_pc),
id_(-1),
ssa_index_(-1),
- emitted_at_use_site_(false),
+ packed_fields_(0u),
environment_(nullptr),
locations_(nullptr),
live_interval_(nullptr),
lifetime_position_(kNoLifetime),
side_effects_(side_effects),
- reference_type_info_(ReferenceTypeInfo::CreateInvalid()) {}
+ reference_type_handle_(ReferenceTypeInfo::CreateInvalid().GetTypeHandle()) {
+ SetPackedFlag<kFlagReferenceTypeIsExact>(ReferenceTypeInfo::CreateInvalid().IsExact());
+ }
virtual ~HInstruction() {}
@@ -1912,7 +1934,8 @@ class HInstruction : public ArenaObject<kArenaAllocInstruction> {
ReferenceTypeInfo GetReferenceTypeInfo() const {
DCHECK_EQ(GetType(), Primitive::kPrimNot);
- return reference_type_info_;
+ return ReferenceTypeInfo::CreateUnchecked(reference_type_handle_,
+ GetPackedFlag<kFlagReferenceTypeIsExact>());;
}
void AddUseAt(HInstruction* user, size_t index) {
@@ -2101,13 +2124,45 @@ class HInstruction : public ArenaObject<kArenaAllocInstruction> {
// The caller must ensure that this is safe to do.
void RemoveEnvironmentUsers();
- bool IsEmittedAtUseSite() const { return emitted_at_use_site_; }
- void MarkEmittedAtUseSite() { emitted_at_use_site_ = true; }
+ bool IsEmittedAtUseSite() const { return GetPackedFlag<kFlagEmittedAtUseSite>(); }
+ void MarkEmittedAtUseSite() { SetPackedFlag<kFlagEmittedAtUseSite>(true); }
protected:
+ // If set, the machine code for this instruction is assumed to be generated by
+ // its users. Used by liveness analysis to compute use positions accordingly.
+ static constexpr size_t kFlagEmittedAtUseSite = 0u;
+ static constexpr size_t kFlagReferenceTypeIsExact = kFlagEmittedAtUseSite + 1;
+ static constexpr size_t kNumberOfGenericPackedBits = kFlagReferenceTypeIsExact + 1;
+ static constexpr size_t kMaxNumberOfPackedBits = sizeof(uint32_t) * kBitsPerByte;
+
virtual const HUserRecord<HInstruction*> InputRecordAt(size_t i) const = 0;
virtual void SetRawInputRecordAt(size_t index, const HUserRecord<HInstruction*>& input) = 0;
+ uint32_t GetPackedFields() const {
+ return packed_fields_;
+ }
+
+ template <size_t flag>
+ bool GetPackedFlag() const {
+ return (packed_fields_ & (1u << flag)) != 0u;
+ }
+
+ template <size_t flag>
+ void SetPackedFlag(bool value = true) {
+ packed_fields_ = (packed_fields_ & ~(1u << flag)) | ((value ? 1u : 0u) << flag);
+ }
+
+ template <typename BitFieldType>
+ typename BitFieldType::value_type GetPackedField() const {
+ return BitFieldType::Decode(packed_fields_);
+ }
+
+ template <typename BitFieldType>
+ void SetPackedField(typename BitFieldType::value_type value) {
+ DCHECK(IsUint<BitFieldType::size>(static_cast<uintptr_t>(value)));
+ packed_fields_ = BitFieldType::Update(value, packed_fields_);
+ }
+
private:
void RemoveEnvironmentUser(HUseListNode<HEnvironment*>* use_node) { env_uses_.Remove(use_node); }
@@ -2124,9 +2179,8 @@ class HInstruction : public ArenaObject<kArenaAllocInstruction> {
// When doing liveness analysis, instructions that have uses get an SSA index.
int ssa_index_;
- // If set, the machine code for this instruction is assumed to be generated by
- // its users. Used by liveness analysis to compute use positions accordingly.
- bool emitted_at_use_site_;
+ // Packed fields.
+ uint32_t packed_fields_;
// List of instructions that have this instruction as input.
HUseList<HInstruction*> uses_;
@@ -2150,8 +2204,10 @@ class HInstruction : public ArenaObject<kArenaAllocInstruction> {
SideEffects side_effects_;
+ // The reference handle part of the reference type info.
+ // The IsExact() flag is stored in packed fields.
// TODO: for primitive types this should be marked as invalid.
- ReferenceTypeInfo reference_type_info_;
+ ReferenceTypeInfo::TypeHandle reference_type_handle_;
friend class GraphChecker;
friend class HBasicBlock;
@@ -2277,13 +2333,23 @@ template<intptr_t N>
class HExpression : public HTemplateInstruction<N> {
public:
HExpression<N>(Primitive::Type type, SideEffects side_effects, uint32_t dex_pc)
- : HTemplateInstruction<N>(side_effects, dex_pc), type_(type) {}
+ : HTemplateInstruction<N>(side_effects, dex_pc) {
+ this->template SetPackedField<TypeField>(type);
+ }
virtual ~HExpression() {}
- Primitive::Type GetType() const OVERRIDE { return type_; }
+ Primitive::Type GetType() const OVERRIDE {
+ return TypeField::Decode(this->GetPackedFields());
+ }
protected:
- Primitive::Type type_;
+ static constexpr size_t kFieldType = HInstruction::kNumberOfGenericPackedBits;
+ static constexpr size_t kFieldTypeSize =
+ MinimumBitsToStore(static_cast<size_t>(Primitive::kPrimLast));
+ static constexpr size_t kNumberOfExpressionPackedBits = kFieldType + kFieldTypeSize;
+ static_assert(kNumberOfExpressionPackedBits <= HInstruction::kMaxNumberOfPackedBits,
+ "Too many packed fields.");
+ using TypeField = BitField<Primitive::Type, kFieldType, kFieldTypeSize>;
};
// Represents dex's RETURN_VOID opcode. A HReturnVoid is a control flow
@@ -2573,13 +2639,16 @@ class HIf : public HTemplateInstruction<1> {
// higher indices in no particular order.
class HTryBoundary : public HTemplateInstruction<0> {
public:
- enum BoundaryKind {
+ enum class BoundaryKind {
kEntry,
kExit,
+ kLast = kExit
};
explicit HTryBoundary(BoundaryKind kind, uint32_t dex_pc = kNoDexPc)
- : HTemplateInstruction(SideEffects::None(), dex_pc), kind_(kind) {}
+ : HTemplateInstruction(SideEffects::None(), dex_pc) {
+ SetPackedField<BoundaryKindField>(kind);
+ }
bool IsControlFlow() const OVERRIDE { return true; }
@@ -2605,14 +2674,22 @@ class HTryBoundary : public HTemplateInstruction<0> {
}
}
- bool IsEntry() const { return kind_ == BoundaryKind::kEntry; }
+ BoundaryKind GetBoundaryKind() const { return GetPackedField<BoundaryKindField>(); }
+ bool IsEntry() const { return GetBoundaryKind() == BoundaryKind::kEntry; }
bool HasSameExceptionHandlersAs(const HTryBoundary& other) const;
DECLARE_INSTRUCTION(TryBoundary);
private:
- const BoundaryKind kind_;
+ static constexpr size_t kFieldBoundaryKind = kNumberOfGenericPackedBits;
+ static constexpr size_t kFieldBoundaryKindSize =
+ MinimumBitsToStore(static_cast<size_t>(BoundaryKind::kLast));
+ static constexpr size_t kNumberOfTryBoundaryPackedBits =
+ kFieldBoundaryKind + kFieldBoundaryKindSize;
+ static_assert(kNumberOfTryBoundaryPackedBits <= kMaxNumberOfPackedBits,
+ "Too many packed fields.");
+ using BoundaryKindField = BitField<BoundaryKind, kFieldBoundaryKind, kFieldBoundaryKindSize>;
DISALLOW_COPY_AND_ASSIGN(HTryBoundary);
};
@@ -2658,9 +2735,10 @@ class HCurrentMethod : public HExpression<0> {
// of a class.
class HClassTableGet : public HExpression<1> {
public:
- enum TableKind {
+ enum class TableKind {
kVTable,
kIMTable,
+ kLast = kIMTable
};
HClassTableGet(HInstruction* cls,
Primitive::Type type,
@@ -2668,26 +2746,33 @@ class HClassTableGet : public HExpression<1> {
size_t index,
uint32_t dex_pc)
: HExpression(type, SideEffects::None(), dex_pc),
- index_(index),
- table_kind_(kind) {
+ index_(index) {
+ SetPackedField<TableKindField>(kind);
SetRawInputAt(0, cls);
}
bool CanBeMoved() const OVERRIDE { return true; }
bool InstructionDataEquals(HInstruction* other) const OVERRIDE {
return other->AsClassTableGet()->GetIndex() == index_ &&
- other->AsClassTableGet()->GetTableKind() == table_kind_;
+ other->AsClassTableGet()->GetPackedFields() == GetPackedFields();
}
- TableKind GetTableKind() const { return table_kind_; }
+ TableKind GetTableKind() const { return GetPackedField<TableKindField>(); }
size_t GetIndex() const { return index_; }
DECLARE_INSTRUCTION(ClassTableGet);
private:
+ static constexpr size_t kFieldTableKind = kNumberOfExpressionPackedBits;
+ static constexpr size_t kFieldTableKindSize =
+ MinimumBitsToStore(static_cast<size_t>(TableKind::kLast));
+ static constexpr size_t kNumberOfClassTableGetPackedBits = kFieldTableKind + kFieldTableKindSize;
+ static_assert(kNumberOfClassTableGetPackedBits <= kMaxNumberOfPackedBits,
+ "Too many packed fields.");
+ using TableKindField = BitField<TableKind, kFieldTableKind, kFieldTableKind>;
+
// The index of the ArtMethod in the table.
const size_t index_;
- const TableKind table_kind_;
DISALLOW_COPY_AND_ASSIGN(HClassTableGet);
};
@@ -2854,6 +2939,7 @@ enum class ComparisonBias {
kNoBias, // bias is not applicable (i.e. for long operation)
kGtBias, // return 1 for NaN comparisons
kLtBias, // return -1 for NaN comparisons
+ kLast = kLtBias
};
std::ostream& operator<<(std::ostream& os, const ComparisonBias& rhs);
@@ -2861,8 +2947,9 @@ std::ostream& operator<<(std::ostream& os, const ComparisonBias& rhs);
class HCondition : public HBinaryOperation {
public:
HCondition(HInstruction* first, HInstruction* second, uint32_t dex_pc = kNoDexPc)
- : HBinaryOperation(Primitive::kPrimBoolean, first, second, SideEffects::None(), dex_pc),
- bias_(ComparisonBias::kNoBias) {}
+ : HBinaryOperation(Primitive::kPrimBoolean, first, second, SideEffects::None(), dex_pc) {
+ SetPackedField<ComparisonBiasField>(ComparisonBias::kNoBias);
+ }
// For code generation purposes, returns whether this instruction is just before
// `instruction`, and disregard moves in between.
@@ -2874,12 +2961,12 @@ class HCondition : public HBinaryOperation {
virtual IfCondition GetOppositeCondition() const = 0;
- bool IsGtBias() const { return bias_ == ComparisonBias::kGtBias; }
- ComparisonBias GetBias() const { return bias_; }
- void SetBias(ComparisonBias bias) { bias_ = bias; }
+ bool IsGtBias() const { return GetBias() == ComparisonBias::kGtBias; }
+ ComparisonBias GetBias() const { return GetPackedField<ComparisonBiasField>(); }
+ void SetBias(ComparisonBias bias) { SetPackedField<ComparisonBiasField>(bias); }
bool InstructionDataEquals(HInstruction* other) const OVERRIDE {
- return bias_ == other->AsCondition()->bias_;
+ return GetPackedFields() == other->AsCondition()->GetPackedFields();
}
bool IsFPConditionTrueIfNaN() const {
@@ -2895,6 +2982,16 @@ class HCondition : public HBinaryOperation {
}
protected:
+ // Needed if we merge a HCompare into a HCondition.
+ static constexpr size_t kFieldComparisonBias = kNumberOfExpressionPackedBits;
+ static constexpr size_t kFieldComparisonBiasSize =
+ MinimumBitsToStore(static_cast<size_t>(ComparisonBias::kLast));
+ static constexpr size_t kNumberOfConditionPackedBits =
+ kFieldComparisonBias + kFieldComparisonBiasSize;
+ static_assert(kNumberOfConditionPackedBits <= kMaxNumberOfPackedBits, "Too many packed fields.");
+ using ComparisonBiasField =
+ BitField<ComparisonBias, kFieldComparisonBias, kFieldComparisonBiasSize>;
+
template <typename T>
int32_t Compare(T x, T y) const { return x > y ? 1 : (x < y ? -1 : 0); }
@@ -2912,9 +3009,6 @@ class HCondition : public HBinaryOperation {
}
private:
- // Needed if we merge a HCompare into a HCondition.
- ComparisonBias bias_;
-
DISALLOW_COPY_AND_ASSIGN(HCondition);
};
@@ -3327,8 +3421,8 @@ class HCompare : public HBinaryOperation {
first,
second,
SideEffectsForArchRuntimeCalls(type),
- dex_pc),
- bias_(bias) {
+ dex_pc) {
+ SetPackedField<ComparisonBiasField>(bias);
DCHECK_EQ(type, first->GetType());
DCHECK_EQ(type, second->GetType());
}
@@ -3363,16 +3457,16 @@ class HCompare : public HBinaryOperation {
}
bool InstructionDataEquals(HInstruction* other) const OVERRIDE {
- return bias_ == other->AsCompare()->bias_;
+ return GetPackedFields() == other->AsCompare()->GetPackedFields();
}
- ComparisonBias GetBias() const { return bias_; }
+ ComparisonBias GetBias() const { return GetPackedField<ComparisonBiasField>(); }
// Does this compare instruction have a "gt bias" (vs an "lt bias")?
- // Only meaninfgul for floating-point comparisons.
+ // Only meaningful for floating-point comparisons.
bool IsGtBias() const {
DCHECK(Primitive::IsFloatingPointType(InputAt(0)->GetType())) << InputAt(0)->GetType();
- return bias_ == ComparisonBias::kGtBias;
+ return GetBias() == ComparisonBias::kGtBias;
}
static SideEffects SideEffectsForArchRuntimeCalls(Primitive::Type type) {
@@ -3383,6 +3477,15 @@ class HCompare : public HBinaryOperation {
DECLARE_INSTRUCTION(Compare);
protected:
+ static constexpr size_t kFieldComparisonBias = kNumberOfExpressionPackedBits;
+ static constexpr size_t kFieldComparisonBiasSize =
+ MinimumBitsToStore(static_cast<size_t>(ComparisonBias::kLast));
+ static constexpr size_t kNumberOfComparePackedBits =
+ kFieldComparisonBias + kFieldComparisonBiasSize;
+ static_assert(kNumberOfComparePackedBits <= kMaxNumberOfPackedBits, "Too many packed fields.");
+ using ComparisonBiasField =
+ BitField<ComparisonBias, kFieldComparisonBias, kFieldComparisonBiasSize>;
+
// Return an integer constant containing the result of a comparison evaluated at compile time.
HIntConstant* MakeConstantComparison(int32_t value, uint32_t dex_pc) const {
DCHECK(value == -1 || value == 0 || value == 1) << value;
@@ -3390,8 +3493,6 @@ class HCompare : public HBinaryOperation {
}
private:
- const ComparisonBias bias_;
-
DISALLOW_COPY_AND_ASSIGN(HCompare);
};
@@ -3459,9 +3560,9 @@ class HNewInstance : public HExpression<2> {
: HExpression(Primitive::kPrimNot, SideEffects::CanTriggerGC(), dex_pc),
type_index_(type_index),
dex_file_(dex_file),
- can_throw_(can_throw),
- finalizable_(finalizable),
entrypoint_(entrypoint) {
+ SetPackedFlag<kFlagCanThrow>(can_throw);
+ SetPackedFlag<kFlagFinalizable>(finalizable);
SetRawInputAt(0, cls);
SetRawInputAt(1, current_method);
}
@@ -3475,9 +3576,9 @@ class HNewInstance : public HExpression<2> {
// It may throw when called on type that's not instantiable/accessible.
// It can throw OOME.
// TODO: distinguish between the two cases so we can for example allow allocation elimination.
- bool CanThrow() const OVERRIDE { return can_throw_ || true; }
+ bool CanThrow() const OVERRIDE { return GetPackedFlag<kFlagCanThrow>() || true; }
- bool IsFinalizable() const { return finalizable_; }
+ bool IsFinalizable() const { return GetPackedFlag<kFlagFinalizable>(); }
bool CanBeNull() const OVERRIDE { return false; }
@@ -3492,10 +3593,14 @@ class HNewInstance : public HExpression<2> {
DECLARE_INSTRUCTION(NewInstance);
private:
+ static constexpr size_t kFlagCanThrow = kNumberOfExpressionPackedBits;
+ static constexpr size_t kFlagFinalizable = kFlagCanThrow + 1;
+ static constexpr size_t kNumberOfNewInstancePackedBits = kFlagFinalizable + 1;
+ static_assert(kNumberOfNewInstancePackedBits <= kMaxNumberOfPackedBits,
+ "Too many packed fields.");
+
const uint16_t type_index_;
const DexFile& dex_file_;
- const bool can_throw_;
- const bool finalizable_;
QuickEntrypointEnum entrypoint_;
DISALLOW_COPY_AND_ASSIGN(HNewInstance);
@@ -3545,12 +3650,14 @@ class HInvoke : public HInstruction {
// inputs at the end of their list of inputs.
uint32_t GetNumberOfArguments() const { return number_of_arguments_; }
- Primitive::Type GetType() const OVERRIDE { return return_type_; }
+ Primitive::Type GetType() const OVERRIDE { return GetPackedField<ReturnTypeField>(); }
uint32_t GetDexMethodIndex() const { return dex_method_index_; }
const DexFile& GetDexFile() const { return GetEnvironment()->GetDexFile(); }
- InvokeType GetOriginalInvokeType() const { return original_invoke_type_; }
+ InvokeType GetOriginalInvokeType() const {
+ return GetPackedField<OriginalInvokeTypeField>();
+ }
Intrinsics GetIntrinsic() const {
return intrinsic_;
@@ -3565,7 +3672,7 @@ class HInvoke : public HInstruction {
return GetEnvironment()->IsFromInlinedInvoke();
}
- bool CanThrow() const OVERRIDE { return can_throw_; }
+ bool CanThrow() const OVERRIDE { return GetPackedFlag<kFlagCanThrow>(); }
bool CanBeMoved() const OVERRIDE { return IsIntrinsic(); }
@@ -3586,6 +3693,20 @@ class HInvoke : public HInstruction {
DECLARE_ABSTRACT_INSTRUCTION(Invoke);
protected:
+ static constexpr size_t kFieldOriginalInvokeType = kNumberOfGenericPackedBits;
+ static constexpr size_t kFieldOriginalInvokeTypeSize =
+ MinimumBitsToStore(static_cast<size_t>(kMaxInvokeType));
+ static constexpr size_t kFieldReturnType =
+ kFieldOriginalInvokeType + kFieldOriginalInvokeTypeSize;
+ static constexpr size_t kFieldReturnTypeSize =
+ MinimumBitsToStore(static_cast<size_t>(Primitive::kPrimLast));
+ static constexpr size_t kFlagCanThrow = kFieldReturnType + kFieldReturnTypeSize;
+ static constexpr size_t kNumberOfInvokePackedBits = kFlagCanThrow + 1;
+ static_assert(kNumberOfInvokePackedBits <= kMaxNumberOfPackedBits, "Too many packed fields.");
+ using OriginalInvokeTypeField =
+ BitField<InvokeType, kFieldOriginalInvokeType, kFieldOriginalInvokeTypeSize>;
+ using ReturnTypeField = BitField<Primitive::Type, kFieldReturnType, kFieldReturnTypeSize>;
+
HInvoke(ArenaAllocator* arena,
uint32_t number_of_arguments,
uint32_t number_of_other_inputs,
@@ -3598,12 +3719,12 @@ class HInvoke : public HInstruction {
number_of_arguments_(number_of_arguments),
inputs_(number_of_arguments + number_of_other_inputs,
arena->Adapter(kArenaAllocInvokeInputs)),
- return_type_(return_type),
dex_method_index_(dex_method_index),
- original_invoke_type_(original_invoke_type),
- can_throw_(true),
intrinsic_(Intrinsics::kNone),
intrinsic_optimizations_(0) {
+ SetPackedField<ReturnTypeField>(return_type);
+ SetPackedField<OriginalInvokeTypeField>(original_invoke_type);
+ SetPackedFlag<kFlagCanThrow>(true);
}
const HUserRecord<HInstruction*> InputRecordAt(size_t index) const OVERRIDE {
@@ -3614,14 +3735,11 @@ class HInvoke : public HInstruction {
inputs_[index] = input;
}
- void SetCanThrow(bool can_throw) { can_throw_ = can_throw; }
+ void SetCanThrow(bool can_throw) { SetPackedFlag<kFlagCanThrow>(can_throw); }
uint32_t number_of_arguments_;
ArenaVector<HUserRecord<HInstruction*>> inputs_;
- const Primitive::Type return_type_;
const uint32_t dex_method_index_;
- const InvokeType original_invoke_type_;
- bool can_throw_;
Intrinsics intrinsic_;
// A magic word holding optimizations for intrinsics. See intrinsics.h.
@@ -3662,6 +3780,7 @@ class HInvokeStaticOrDirect : public HInvoke {
kNone, // Class already initialized.
kExplicit, // Static call having explicit clinit check as last input.
kImplicit, // Static call implicitly requiring a clinit check.
+ kLast = kImplicit
};
// Determines how to load the target ArtMethod*.
@@ -3682,7 +3801,7 @@ class HInvokeStaticOrDirect : public HInvoke {
// the image relocatable or not.
kDirectAddressWithFixup,
- // Load from resoved methods array in the dex cache using a PC-relative load.
+ // Load from resolved methods array in the dex cache using a PC-relative load.
// Used when we need to use the dex cache, for example for invoke-static that
// may cause class initialization (the entry may point to a resolution method),
// and we know that we can access the dex cache arrays using a PC-relative load.
@@ -3754,10 +3873,11 @@ class HInvokeStaticOrDirect : public HInvoke {
dex_pc,
method_index,
original_invoke_type),
- optimized_invoke_type_(optimized_invoke_type),
- clinit_check_requirement_(clinit_check_requirement),
target_method_(target_method),
- dispatch_info_(dispatch_info) { }
+ dispatch_info_(dispatch_info) {
+ SetPackedField<OptimizedInvokeTypeField>(optimized_invoke_type);
+ SetPackedField<ClinitCheckRequirementField>(clinit_check_requirement);
+ }
void SetDispatchInfo(const DispatchInfo& dispatch_info) {
bool had_current_method_input = HasCurrentMethodInput();
@@ -3789,7 +3909,7 @@ class HInvokeStaticOrDirect : public HInvoke {
}
bool CanBeNull() const OVERRIDE {
- return return_type_ == Primitive::kPrimNot && !IsStringInit();
+ return GetPackedField<ReturnTypeField>() == Primitive::kPrimNot && !IsStringInit();
}
// Get the index of the special input, if any.
@@ -3800,9 +3920,12 @@ class HInvokeStaticOrDirect : public HInvoke {
uint32_t GetSpecialInputIndex() const { return GetNumberOfArguments(); }
bool HasSpecialInput() const { return GetNumberOfArguments() != InputCount(); }
- InvokeType GetOptimizedInvokeType() const { return optimized_invoke_type_; }
+ InvokeType GetOptimizedInvokeType() const {
+ return GetPackedField<OptimizedInvokeTypeField>();
+ }
+
void SetOptimizedInvokeType(InvokeType invoke_type) {
- optimized_invoke_type_ = invoke_type;
+ SetPackedField<OptimizedInvokeTypeField>(invoke_type);
}
MethodLoadKind GetMethodLoadKind() const { return dispatch_info_.method_load_kind; }
@@ -3849,7 +3972,9 @@ class HInvokeStaticOrDirect : public HInvoke {
return dispatch_info_.direct_code_ptr;
}
- ClinitCheckRequirement GetClinitCheckRequirement() const { return clinit_check_requirement_; }
+ ClinitCheckRequirement GetClinitCheckRequirement() const {
+ return GetPackedField<ClinitCheckRequirementField>();
+ }
// Is this instruction a call to a static method?
bool IsStatic() const {
@@ -3867,7 +3992,7 @@ class HInvokeStaticOrDirect : public HInvoke {
DCHECK(last_input->IsLoadClass() || last_input->IsClinitCheck()) << last_input->DebugName();
RemoveAsUserOfInput(last_input_index);
inputs_.pop_back();
- clinit_check_requirement_ = new_requirement;
+ SetPackedField<ClinitCheckRequirementField>(new_requirement);
DCHECK(!IsStaticWithExplicitClinitCheck());
}
@@ -3883,13 +4008,13 @@ class HInvokeStaticOrDirect : public HInvoke {
// Is this a call to a static method whose declaring class has an
// explicit initialization check in the graph?
bool IsStaticWithExplicitClinitCheck() const {
- return IsStatic() && (clinit_check_requirement_ == ClinitCheckRequirement::kExplicit);
+ return IsStatic() && (GetClinitCheckRequirement() == ClinitCheckRequirement::kExplicit);
}
// Is this a call to a static method whose declaring class has an
// implicit intialization check requirement?
bool IsStaticWithImplicitClinitCheck() const {
- return IsStatic() && (clinit_check_requirement_ == ClinitCheckRequirement::kImplicit);
+ return IsStatic() && (GetClinitCheckRequirement() == ClinitCheckRequirement::kImplicit);
}
// Does this method load kind need the current method as an input?
@@ -3918,8 +4043,23 @@ class HInvokeStaticOrDirect : public HInvoke {
void RemoveInputAt(size_t index);
private:
- InvokeType optimized_invoke_type_;
- ClinitCheckRequirement clinit_check_requirement_;
+ static constexpr size_t kFieldOptimizedInvokeType = kNumberOfInvokePackedBits;
+ static constexpr size_t kFieldOptimizedInvokeTypeSize =
+ MinimumBitsToStore(static_cast<size_t>(kMaxInvokeType));
+ static constexpr size_t kFieldClinitCheckRequirement =
+ kFieldOptimizedInvokeType + kFieldOptimizedInvokeTypeSize;
+ static constexpr size_t kFieldClinitCheckRequirementSize =
+ MinimumBitsToStore(static_cast<size_t>(ClinitCheckRequirement::kLast));
+ static constexpr size_t kNumberOfInvokeStaticOrDirectPackedBits =
+ kFieldClinitCheckRequirement + kFieldClinitCheckRequirementSize;
+ static_assert(kNumberOfInvokeStaticOrDirectPackedBits <= kMaxNumberOfPackedBits,
+ "Too many packed fields.");
+ using OptimizedInvokeTypeField =
+ BitField<InvokeType, kFieldOptimizedInvokeType, kFieldOptimizedInvokeTypeSize>;
+ using ClinitCheckRequirementField = BitField<ClinitCheckRequirement,
+ kFieldClinitCheckRequirement,
+ kFieldClinitCheckRequirementSize>;
+
// The target method may refer to different dex file or method index than the original
// invoke. This happens for sharpened calls and for calls where a method was redeclared
// in derived class to increase visibility.
@@ -4585,32 +4725,35 @@ class HParameterValue : public HExpression<0> {
: HExpression(parameter_type, SideEffects::None(), kNoDexPc),
dex_file_(dex_file),
type_index_(type_index),
- index_(index),
- is_this_(is_this),
- can_be_null_(!is_this) {}
+ index_(index) {
+ SetPackedFlag<kFlagIsThis>(is_this);
+ SetPackedFlag<kFlagCanBeNull>(!is_this);
+ }
const DexFile& GetDexFile() const { return dex_file_; }
uint16_t GetTypeIndex() const { return type_index_; }
uint8_t GetIndex() const { return index_; }
- bool IsThis() const { return is_this_; }
+ bool IsThis() const { return GetPackedFlag<kFlagIsThis>(); }
- bool CanBeNull() const OVERRIDE { return can_be_null_; }
- void SetCanBeNull(bool can_be_null) { can_be_null_ = can_be_null; }
+ bool CanBeNull() const OVERRIDE { return GetPackedFlag<kFlagCanBeNull>(); }
+ void SetCanBeNull(bool can_be_null) { SetPackedFlag<kFlagCanBeNull>(can_be_null); }
DECLARE_INSTRUCTION(ParameterValue);
private:
+ // Whether or not the parameter value corresponds to 'this' argument.
+ static constexpr size_t kFlagIsThis = kNumberOfExpressionPackedBits;
+ static constexpr size_t kFlagCanBeNull = kFlagIsThis + 1;
+ static constexpr size_t kNumberOfParameterValuePackedBits = kFlagCanBeNull + 1;
+ static_assert(kNumberOfParameterValuePackedBits <= kMaxNumberOfPackedBits,
+ "Too many packed fields.");
+
const DexFile& dex_file_;
const uint16_t type_index_;
// The index of this parameter in the parameters list. Must be less
// than HGraph::number_of_in_vregs_.
const uint8_t index_;
- // Whether or not the parameter value corresponds to 'this' argument.
- const bool is_this_;
-
- bool can_be_null_;
-
DISALLOW_COPY_AND_ASSIGN(HParameterValue);
};
@@ -4735,14 +4878,14 @@ class HPhi : public HInstruction {
uint32_t dex_pc = kNoDexPc)
: HInstruction(SideEffects::None(), dex_pc),
inputs_(number_of_inputs, arena->Adapter(kArenaAllocPhiInputs)),
- reg_number_(reg_number),
- type_(ToPhiType(type)),
- // Phis are constructed live and marked dead if conflicting or unused.
- // Individual steps of SsaBuilder should assume that if a phi has been
- // marked dead, it can be ignored and will be removed by SsaPhiElimination.
- is_live_(true),
- can_be_null_(true) {
- DCHECK_NE(type_, Primitive::kPrimVoid);
+ reg_number_(reg_number) {
+ SetPackedField<TypeField>(ToPhiType(type));
+ DCHECK_NE(GetType(), Primitive::kPrimVoid);
+ // Phis are constructed live and marked dead if conflicting or unused.
+ // Individual steps of SsaBuilder should assume that if a phi has been
+ // marked dead, it can be ignored and will be removed by SsaPhiElimination.
+ SetPackedFlag<kFlagIsLive>(true);
+ SetPackedFlag<kFlagCanBeNull>(true);
}
// Returns a type equivalent to the given `type`, but that a `HPhi` can hold.
@@ -4765,27 +4908,27 @@ class HPhi : public HInstruction {
void AddInput(HInstruction* input);
void RemoveInputAt(size_t index);
- Primitive::Type GetType() const OVERRIDE { return type_; }
+ Primitive::Type GetType() const OVERRIDE { return GetPackedField<TypeField>(); }
void SetType(Primitive::Type new_type) {
// Make sure that only valid type changes occur. The following are allowed:
// (1) int -> float/ref (primitive type propagation),
// (2) long -> double (primitive type propagation).
- DCHECK(type_ == new_type ||
- (type_ == Primitive::kPrimInt && new_type == Primitive::kPrimFloat) ||
- (type_ == Primitive::kPrimInt && new_type == Primitive::kPrimNot) ||
- (type_ == Primitive::kPrimLong && new_type == Primitive::kPrimDouble));
- type_ = new_type;
+ DCHECK(GetType() == new_type ||
+ (GetType() == Primitive::kPrimInt && new_type == Primitive::kPrimFloat) ||
+ (GetType() == Primitive::kPrimInt && new_type == Primitive::kPrimNot) ||
+ (GetType() == Primitive::kPrimLong && new_type == Primitive::kPrimDouble));
+ SetPackedField<TypeField>(new_type);
}
- bool CanBeNull() const OVERRIDE { return can_be_null_; }
- void SetCanBeNull(bool can_be_null) { can_be_null_ = can_be_null; }
+ bool CanBeNull() const OVERRIDE { return GetPackedFlag<kFlagCanBeNull>(); }
+ void SetCanBeNull(bool can_be_null) { SetPackedFlag<kFlagCanBeNull>(can_be_null); }
uint32_t GetRegNumber() const { return reg_number_; }
- void SetDead() { is_live_ = false; }
- void SetLive() { is_live_ = true; }
- bool IsDead() const { return !is_live_; }
- bool IsLive() const { return is_live_; }
+ void SetDead() { SetPackedFlag<kFlagIsLive>(false); }
+ void SetLive() { SetPackedFlag<kFlagIsLive>(true); }
+ bool IsDead() const { return !IsLive(); }
+ bool IsLive() const { return GetPackedFlag<kFlagIsLive>(); }
bool IsVRegEquivalentOf(HInstruction* other) const {
return other != nullptr
@@ -4820,11 +4963,17 @@ class HPhi : public HInstruction {
}
private:
+ static constexpr size_t kFieldType = HInstruction::kNumberOfGenericPackedBits;
+ static constexpr size_t kFieldTypeSize =
+ MinimumBitsToStore(static_cast<size_t>(Primitive::kPrimLast));
+ static constexpr size_t kFlagIsLive = kFieldType + kFieldTypeSize;
+ static constexpr size_t kFlagCanBeNull = kFlagIsLive + 1;
+ static constexpr size_t kNumberOfPhiPackedBits = kFlagCanBeNull + 1;
+ static_assert(kNumberOfPhiPackedBits <= kMaxNumberOfPackedBits, "Too many packed fields.");
+ using TypeField = BitField<Primitive::Type, kFieldType, kFieldTypeSize>;
+
ArenaVector<HUserRecord<HInstruction*> > inputs_;
const uint32_t reg_number_;
- Primitive::Type type_;
- bool is_live_;
- bool can_be_null_;
DISALLOW_COPY_AND_ASSIGN(HPhi);
};
@@ -4963,8 +5112,8 @@ class HInstanceFieldSet : public HTemplateInstruction<2> {
field_idx,
declaring_class_def_index,
dex_file,
- dex_cache),
- value_can_be_null_(true) {
+ dex_cache) {
+ SetPackedFlag<kFlagValueCanBeNull>(true);
SetRawInputAt(0, object);
SetRawInputAt(1, value);
}
@@ -4978,14 +5127,18 @@ class HInstanceFieldSet : public HTemplateInstruction<2> {
Primitive::Type GetFieldType() const { return field_info_.GetFieldType(); }
bool IsVolatile() const { return field_info_.IsVolatile(); }
HInstruction* GetValue() const { return InputAt(1); }
- bool GetValueCanBeNull() const { return value_can_be_null_; }
- void ClearValueCanBeNull() { value_can_be_null_ = false; }
+ bool GetValueCanBeNull() const { return GetPackedFlag<kFlagValueCanBeNull>(); }
+ void ClearValueCanBeNull() { SetPackedFlag<kFlagValueCanBeNull>(false); }
DECLARE_INSTRUCTION(InstanceFieldSet);
private:
+ static constexpr size_t kFlagValueCanBeNull = kNumberOfGenericPackedBits;
+ static constexpr size_t kNumberOfInstanceFieldSetPackedBits = kFlagValueCanBeNull + 1;
+ static_assert(kNumberOfInstanceFieldSetPackedBits <= kMaxNumberOfPackedBits,
+ "Too many packed fields.");
+
const FieldInfo field_info_;
- bool value_can_be_null_;
DISALLOW_COPY_AND_ASSIGN(HInstanceFieldSet);
};
@@ -5054,11 +5207,11 @@ class HArraySet : public HTemplateInstruction<3> {
SideEffects::ArrayWriteOfType(expected_component_type).Union(
SideEffectsForArchRuntimeCalls(value->GetType())).Union(
additional_side_effects),
- dex_pc),
- expected_component_type_(expected_component_type),
- needs_type_check_(value->GetType() == Primitive::kPrimNot),
- value_can_be_null_(true),
- static_type_of_array_is_object_array_(false) {
+ dex_pc) {
+ SetPackedField<ExpectedComponentTypeField>(expected_component_type);
+ SetPackedFlag<kFlagNeedsTypeCheck>(value->GetType() == Primitive::kPrimNot);
+ SetPackedFlag<kFlagValueCanBeNull>(true);
+ SetPackedFlag<kFlagStaticTypeOfArrayIsObjectArray>(false);
SetRawInputAt(0, array);
SetRawInputAt(1, index);
SetRawInputAt(2, value);
@@ -5066,11 +5219,11 @@ class HArraySet : public HTemplateInstruction<3> {
bool NeedsEnvironment() const OVERRIDE {
// We call a runtime method to throw ArrayStoreException.
- return needs_type_check_;
+ return NeedsTypeCheck();
}
// Can throw ArrayStoreException.
- bool CanThrow() const OVERRIDE { return needs_type_check_; }
+ bool CanThrow() const OVERRIDE { return NeedsTypeCheck(); }
bool CanDoImplicitNullCheckOn(HInstruction* obj ATTRIBUTE_UNUSED) const OVERRIDE {
// TODO: Same as for ArrayGet.
@@ -5078,20 +5231,22 @@ class HArraySet : public HTemplateInstruction<3> {
}
void ClearNeedsTypeCheck() {
- needs_type_check_ = false;
+ SetPackedFlag<kFlagNeedsTypeCheck>(false);
}
void ClearValueCanBeNull() {
- value_can_be_null_ = false;
+ SetPackedFlag<kFlagValueCanBeNull>(false);
}
void SetStaticTypeOfArrayIsObjectArray() {
- static_type_of_array_is_object_array_ = true;
+ SetPackedFlag<kFlagStaticTypeOfArrayIsObjectArray>(true);
}
- bool GetValueCanBeNull() const { return value_can_be_null_; }
- bool NeedsTypeCheck() const { return needs_type_check_; }
- bool StaticTypeOfArrayIsObjectArray() const { return static_type_of_array_is_object_array_; }
+ bool GetValueCanBeNull() const { return GetPackedFlag<kFlagValueCanBeNull>(); }
+ bool NeedsTypeCheck() const { return GetPackedFlag<kFlagNeedsTypeCheck>(); }
+ bool StaticTypeOfArrayIsObjectArray() const {
+ return GetPackedFlag<kFlagStaticTypeOfArrayIsObjectArray>();
+ }
HInstruction* GetArray() const { return InputAt(0); }
HInstruction* GetIndex() const { return InputAt(1); }
@@ -5105,11 +5260,11 @@ class HArraySet : public HTemplateInstruction<3> {
Primitive::Type value_type = GetValue()->GetType();
return ((value_type == Primitive::kPrimFloat) || (value_type == Primitive::kPrimDouble))
? value_type
- : expected_component_type_;
+ : GetRawExpectedComponentType();
}
Primitive::Type GetRawExpectedComponentType() const {
- return expected_component_type_;
+ return GetPackedField<ExpectedComponentTypeField>();
}
static SideEffects SideEffectsForArchRuntimeCalls(Primitive::Type value_type) {
@@ -5119,12 +5274,20 @@ class HArraySet : public HTemplateInstruction<3> {
DECLARE_INSTRUCTION(ArraySet);
private:
- const Primitive::Type expected_component_type_;
- bool needs_type_check_;
- bool value_can_be_null_;
+ static constexpr size_t kFieldExpectedComponentType = kNumberOfGenericPackedBits;
+ static constexpr size_t kFieldExpectedComponentTypeSize =
+ MinimumBitsToStore(static_cast<size_t>(Primitive::kPrimLast));
+ static constexpr size_t kFlagNeedsTypeCheck =
+ kFieldExpectedComponentType + kFieldExpectedComponentTypeSize;
+ static constexpr size_t kFlagValueCanBeNull = kFlagNeedsTypeCheck + 1;
// Cached information for the reference_type_info_ so that codegen
// does not need to inspect the static type.
- bool static_type_of_array_is_object_array_;
+ static constexpr size_t kFlagStaticTypeOfArrayIsObjectArray = kFlagValueCanBeNull + 1;
+ static constexpr size_t kNumberOfArraySetPackedBits =
+ kFlagStaticTypeOfArrayIsObjectArray + 1;
+ static_assert(kNumberOfArraySetPackedBits <= kMaxNumberOfPackedBits, "Too many packed fields.");
+ using ExpectedComponentTypeField =
+ BitField<Primitive::Type, kFieldExpectedComponentType, kFieldExpectedComponentTypeSize>;
DISALLOW_COPY_AND_ASSIGN(HArraySet);
};
@@ -5234,14 +5397,15 @@ class HLoadClass : public HExpression<1> {
: HExpression(Primitive::kPrimNot, SideEffectsForArchRuntimeCalls(), dex_pc),
type_index_(type_index),
dex_file_(dex_file),
- is_referrers_class_(is_referrers_class),
- generate_clinit_check_(false),
- needs_access_check_(needs_access_check),
- is_in_dex_cache_(is_in_dex_cache),
loaded_class_rti_(ReferenceTypeInfo::CreateInvalid()) {
// Referrers class should not need access check. We never inline unverified
// methods so we can't possibly end up in this situation.
- DCHECK(!is_referrers_class_ || !needs_access_check_);
+ DCHECK(!is_referrers_class || !needs_access_check);
+
+ SetPackedFlag<kFlagIsReferrersClass>(is_referrers_class);
+ SetPackedFlag<kFlagNeedsAccessCheck>(needs_access_check);
+ SetPackedFlag<kFlagIsInDexCache>(is_in_dex_cache);
+ SetPackedFlag<kFlagGenerateClInitCheck>(false);
SetRawInputAt(0, current_method);
}
@@ -5252,39 +5416,31 @@ class HLoadClass : public HExpression<1> {
// Whether or not we need to generate the clinit check is processed in
// prepare_for_register_allocator based on existing HInvokes and HClinitChecks.
return other->AsLoadClass()->type_index_ == type_index_ &&
- other->AsLoadClass()->needs_access_check_ == needs_access_check_;
+ other->AsLoadClass()->GetPackedFields() == GetPackedFields();
}
size_t ComputeHashCode() const OVERRIDE { return type_index_; }
uint16_t GetTypeIndex() const { return type_index_; }
- bool IsReferrersClass() const { return is_referrers_class_; }
bool CanBeNull() const OVERRIDE { return false; }
bool NeedsEnvironment() const OVERRIDE {
return CanCallRuntime();
}
- bool MustGenerateClinitCheck() const {
- return generate_clinit_check_;
- }
-
void SetMustGenerateClinitCheck(bool generate_clinit_check) {
// The entrypoint the code generator is going to call does not do
// clinit of the class.
DCHECK(!NeedsAccessCheck());
- generate_clinit_check_ = generate_clinit_check;
+ SetPackedFlag<kFlagGenerateClInitCheck>(generate_clinit_check);
}
bool CanCallRuntime() const {
return MustGenerateClinitCheck() ||
- (!is_referrers_class_ && !is_in_dex_cache_) ||
- needs_access_check_;
+ (!IsReferrersClass() && !IsInDexCache()) ||
+ NeedsAccessCheck();
}
- bool NeedsAccessCheck() const {
- return needs_access_check_;
- }
bool CanThrow() const OVERRIDE {
return CanCallRuntime();
@@ -5302,25 +5458,31 @@ class HLoadClass : public HExpression<1> {
const DexFile& GetDexFile() { return dex_file_; }
- bool NeedsDexCacheOfDeclaringClass() const OVERRIDE { return !is_referrers_class_; }
+ bool NeedsDexCacheOfDeclaringClass() const OVERRIDE { return !IsReferrersClass(); }
static SideEffects SideEffectsForArchRuntimeCalls() {
return SideEffects::CanTriggerGC();
}
- bool IsInDexCache() const { return is_in_dex_cache_; }
+ bool IsReferrersClass() const { return GetPackedFlag<kFlagIsReferrersClass>(); }
+ bool NeedsAccessCheck() const { return GetPackedFlag<kFlagNeedsAccessCheck>(); }
+ bool IsInDexCache() const { return GetPackedFlag<kFlagIsInDexCache>(); }
+ bool MustGenerateClinitCheck() const { return GetPackedFlag<kFlagGenerateClInitCheck>(); }
DECLARE_INSTRUCTION(LoadClass);
private:
- const uint16_t type_index_;
- const DexFile& dex_file_;
- const bool is_referrers_class_;
+ static constexpr size_t kFlagIsReferrersClass = kNumberOfExpressionPackedBits;
+ static constexpr size_t kFlagNeedsAccessCheck = kFlagIsReferrersClass + 1;
+ static constexpr size_t kFlagIsInDexCache = kFlagNeedsAccessCheck + 1;
// Whether this instruction must generate the initialization check.
// Used for code generation.
- bool generate_clinit_check_;
- const bool needs_access_check_;
- const bool is_in_dex_cache_;
+ static constexpr size_t kFlagGenerateClInitCheck = kFlagIsInDexCache + 1;
+ static constexpr size_t kNumberOfLoadClassPackedBits = kFlagGenerateClInitCheck + 1;
+ static_assert(kNumberOfLoadClassPackedBits < kMaxNumberOfPackedBits, "Too many packed fields.");
+
+ const uint16_t type_index_;
+ const DexFile& dex_file_;
ReferenceTypeInfo loaded_class_rti_;
@@ -5334,8 +5496,8 @@ class HLoadString : public HExpression<1> {
uint32_t dex_pc,
bool is_in_dex_cache)
: HExpression(Primitive::kPrimNot, SideEffectsForArchRuntimeCalls(), dex_pc),
- string_index_(string_index),
- is_in_dex_cache_(is_in_dex_cache) {
+ string_index_(string_index) {
+ SetPackedFlag<kFlagIsInDexCache>(is_in_dex_cache);
SetRawInputAt(0, current_method);
}
@@ -5354,18 +5516,22 @@ class HLoadString : public HExpression<1> {
bool NeedsDexCacheOfDeclaringClass() const OVERRIDE { return true; }
bool CanBeNull() const OVERRIDE { return false; }
- bool IsInDexCache() const { return is_in_dex_cache_; }
bool CanThrow() const OVERRIDE { return !IsInDexCache(); }
static SideEffects SideEffectsForArchRuntimeCalls() {
return SideEffects::CanTriggerGC();
}
+ bool IsInDexCache() const { return GetPackedFlag<kFlagIsInDexCache>(); }
+
DECLARE_INSTRUCTION(LoadString);
private:
+ static constexpr size_t kFlagIsInDexCache = kNumberOfExpressionPackedBits;
+ static constexpr size_t kNumberOfLoadStringPackedBits = kFlagIsInDexCache + 1;
+ static_assert(kNumberOfLoadStringPackedBits <= kMaxNumberOfPackedBits, "Too many packed fields.");
+
const uint32_t string_index_;
- const bool is_in_dex_cache_;
DISALLOW_COPY_AND_ASSIGN(HLoadString);
};
@@ -5472,8 +5638,8 @@ class HStaticFieldSet : public HTemplateInstruction<2> {
field_idx,
declaring_class_def_index,
dex_file,
- dex_cache),
- value_can_be_null_(true) {
+ dex_cache) {
+ SetPackedFlag<kFlagValueCanBeNull>(true);
SetRawInputAt(0, cls);
SetRawInputAt(1, value);
}
@@ -5484,14 +5650,18 @@ class HStaticFieldSet : public HTemplateInstruction<2> {
bool IsVolatile() const { return field_info_.IsVolatile(); }
HInstruction* GetValue() const { return InputAt(1); }
- bool GetValueCanBeNull() const { return value_can_be_null_; }
- void ClearValueCanBeNull() { value_can_be_null_ = false; }
+ bool GetValueCanBeNull() const { return GetPackedFlag<kFlagValueCanBeNull>(); }
+ void ClearValueCanBeNull() { SetPackedFlag<kFlagValueCanBeNull>(false); }
DECLARE_INSTRUCTION(StaticFieldSet);
private:
+ static constexpr size_t kFlagValueCanBeNull = kNumberOfGenericPackedBits;
+ static constexpr size_t kNumberOfStaticFieldSetPackedBits = kFlagValueCanBeNull + 1;
+ static_assert(kNumberOfStaticFieldSetPackedBits <= kMaxNumberOfPackedBits,
+ "Too many packed fields.");
+
const FieldInfo field_info_;
- bool value_can_be_null_;
DISALLOW_COPY_AND_ASSIGN(HStaticFieldSet);
};
@@ -5529,8 +5699,8 @@ class HUnresolvedInstanceFieldSet : public HTemplateInstruction<2> {
uint32_t field_index,
uint32_t dex_pc)
: HTemplateInstruction(SideEffects::AllExceptGCDependency(), dex_pc),
- field_type_(field_type),
field_index_(field_index) {
+ SetPackedField<FieldTypeField>(field_type);
DCHECK_EQ(field_type, value->GetType());
SetRawInputAt(0, obj);
SetRawInputAt(1, value);
@@ -5539,13 +5709,21 @@ class HUnresolvedInstanceFieldSet : public HTemplateInstruction<2> {
bool NeedsEnvironment() const OVERRIDE { return true; }
bool CanThrow() const OVERRIDE { return true; }
- Primitive::Type GetFieldType() const { return field_type_; }
+ Primitive::Type GetFieldType() const { return GetPackedField<FieldTypeField>(); }
uint32_t GetFieldIndex() const { return field_index_; }
DECLARE_INSTRUCTION(UnresolvedInstanceFieldSet);
private:
- const Primitive::Type field_type_;
+ static constexpr size_t kFieldFieldType = HInstruction::kNumberOfGenericPackedBits;
+ static constexpr size_t kFieldFieldTypeSize =
+ MinimumBitsToStore(static_cast<size_t>(Primitive::kPrimLast));
+ static constexpr size_t kNumberOfUnresolvedStaticFieldSetPackedBits =
+ kFieldFieldType + kFieldFieldTypeSize;
+ static_assert(kNumberOfUnresolvedStaticFieldSetPackedBits <= HInstruction::kMaxNumberOfPackedBits,
+ "Too many packed fields.");
+ using FieldTypeField = BitField<Primitive::Type, kFieldFieldType, kFieldFieldTypeSize>;
+
const uint32_t field_index_;
DISALLOW_COPY_AND_ASSIGN(HUnresolvedInstanceFieldSet);
@@ -5581,8 +5759,8 @@ class HUnresolvedStaticFieldSet : public HTemplateInstruction<1> {
uint32_t field_index,
uint32_t dex_pc)
: HTemplateInstruction(SideEffects::AllExceptGCDependency(), dex_pc),
- field_type_(field_type),
field_index_(field_index) {
+ SetPackedField<FieldTypeField>(field_type);
DCHECK_EQ(field_type, value->GetType());
SetRawInputAt(0, value);
}
@@ -5590,13 +5768,21 @@ class HUnresolvedStaticFieldSet : public HTemplateInstruction<1> {
bool NeedsEnvironment() const OVERRIDE { return true; }
bool CanThrow() const OVERRIDE { return true; }
- Primitive::Type GetFieldType() const { return field_type_; }
+ Primitive::Type GetFieldType() const { return GetPackedField<FieldTypeField>(); }
uint32_t GetFieldIndex() const { return field_index_; }
DECLARE_INSTRUCTION(UnresolvedStaticFieldSet);
private:
- const Primitive::Type field_type_;
+ static constexpr size_t kFieldFieldType = HInstruction::kNumberOfGenericPackedBits;
+ static constexpr size_t kFieldFieldTypeSize =
+ MinimumBitsToStore(static_cast<size_t>(Primitive::kPrimLast));
+ static constexpr size_t kNumberOfUnresolvedStaticFieldSetPackedBits =
+ kFieldFieldType + kFieldFieldTypeSize;
+ static_assert(kNumberOfUnresolvedStaticFieldSetPackedBits <= HInstruction::kMaxNumberOfPackedBits,
+ "Too many packed fields.");
+ using FieldTypeField = BitField<Primitive::Type, kFieldFieldType, kFieldFieldTypeSize>;
+
const uint32_t field_index_;
DISALLOW_COPY_AND_ASSIGN(HUnresolvedStaticFieldSet);
@@ -5660,7 +5846,8 @@ enum class TypeCheckKind {
kAbstractClassCheck, // Can just walk the super class chain, starting one up.
kInterfaceCheck, // No optimization yet when checking against an interface.
kArrayObjectCheck, // Can just check if the array is not primitive.
- kArrayCheck // No optimization yet when checking against a generic array.
+ kArrayCheck, // No optimization yet when checking against a generic array.
+ kLast = kArrayCheck
};
std::ostream& operator<<(std::ostream& os, TypeCheckKind rhs);
@@ -5673,9 +5860,9 @@ class HInstanceOf : public HExpression<2> {
uint32_t dex_pc)
: HExpression(Primitive::kPrimBoolean,
SideEffectsForArchRuntimeCalls(check_kind),
- dex_pc),
- check_kind_(check_kind),
- must_do_null_check_(true) {
+ dex_pc) {
+ SetPackedField<TypeCheckKindField>(check_kind);
+ SetPackedFlag<kFlagMustDoNullCheck>(true);
SetRawInputAt(0, object);
SetRawInputAt(1, constant);
}
@@ -5687,16 +5874,14 @@ class HInstanceOf : public HExpression<2> {
}
bool NeedsEnvironment() const OVERRIDE {
- return CanCallRuntime(check_kind_);
+ return CanCallRuntime(GetTypeCheckKind());
}
- bool IsExactCheck() const { return check_kind_ == TypeCheckKind::kExactCheck; }
-
- TypeCheckKind GetTypeCheckKind() const { return check_kind_; }
-
// Used only in code generation.
- bool MustDoNullCheck() const { return must_do_null_check_; }
- void ClearMustDoNullCheck() { must_do_null_check_ = false; }
+ bool MustDoNullCheck() const { return GetPackedFlag<kFlagMustDoNullCheck>(); }
+ void ClearMustDoNullCheck() { SetPackedFlag<kFlagMustDoNullCheck>(false); }
+ TypeCheckKind GetTypeCheckKind() const { return GetPackedField<TypeCheckKindField>(); }
+ bool IsExactCheck() const { return GetTypeCheckKind() == TypeCheckKind::kExactCheck; }
static bool CanCallRuntime(TypeCheckKind check_kind) {
// Mips currently does runtime calls for any other checks.
@@ -5710,8 +5895,13 @@ class HInstanceOf : public HExpression<2> {
DECLARE_INSTRUCTION(InstanceOf);
private:
- const TypeCheckKind check_kind_;
- bool must_do_null_check_;
+ static constexpr size_t kFieldTypeCheckKind = kNumberOfExpressionPackedBits;
+ static constexpr size_t kFieldTypeCheckKindSize =
+ MinimumBitsToStore(static_cast<size_t>(TypeCheckKind::kLast));
+ static constexpr size_t kFlagMustDoNullCheck = kFieldTypeCheckKind + kFieldTypeCheckKindSize;
+ static constexpr size_t kNumberOfInstanceOfPackedBits = kFlagMustDoNullCheck + 1;
+ static_assert(kNumberOfInstanceOfPackedBits <= kMaxNumberOfPackedBits, "Too many packed fields.");
+ using TypeCheckKindField = BitField<TypeCheckKind, kFieldTypeCheckKind, kFieldTypeCheckKindSize>;
DISALLOW_COPY_AND_ASSIGN(HInstanceOf);
};
@@ -5720,28 +5910,35 @@ class HBoundType : public HExpression<1> {
public:
HBoundType(HInstruction* input, uint32_t dex_pc = kNoDexPc)
: HExpression(Primitive::kPrimNot, SideEffects::None(), dex_pc),
- upper_bound_(ReferenceTypeInfo::CreateInvalid()),
- upper_can_be_null_(true),
- can_be_null_(true) {
+ upper_bound_(ReferenceTypeInfo::CreateInvalid()) {
+ SetPackedFlag<kFlagUpperCanBeNull>(true);
+ SetPackedFlag<kFlagCanBeNull>(true);
DCHECK_EQ(input->GetType(), Primitive::kPrimNot);
SetRawInputAt(0, input);
}
// {Get,Set}Upper* should only be used in reference type propagation.
const ReferenceTypeInfo& GetUpperBound() const { return upper_bound_; }
- bool GetUpperCanBeNull() const { return upper_can_be_null_; }
+ bool GetUpperCanBeNull() const { return GetPackedFlag<kFlagUpperCanBeNull>(); }
void SetUpperBound(const ReferenceTypeInfo& upper_bound, bool can_be_null);
void SetCanBeNull(bool can_be_null) {
- DCHECK(upper_can_be_null_ || !can_be_null);
- can_be_null_ = can_be_null;
+ DCHECK(GetUpperCanBeNull() || !can_be_null);
+ SetPackedFlag<kFlagCanBeNull>(can_be_null);
}
- bool CanBeNull() const OVERRIDE { return can_be_null_; }
+ bool CanBeNull() const OVERRIDE { return GetPackedFlag<kFlagCanBeNull>(); }
DECLARE_INSTRUCTION(BoundType);
private:
+ // Represents the top constraint that can_be_null_ cannot exceed (i.e. if this
+ // is false then CanBeNull() cannot be true).
+ static constexpr size_t kFlagUpperCanBeNull = kNumberOfExpressionPackedBits;
+ static constexpr size_t kFlagCanBeNull = kFlagUpperCanBeNull + 1;
+ static constexpr size_t kNumberOfBoundTypePackedBits = kFlagCanBeNull + 1;
+ static_assert(kNumberOfBoundTypePackedBits <= kMaxNumberOfPackedBits, "Too many packed fields.");
+
// Encodes the most upper class that this instruction can have. In other words
// it is always the case that GetUpperBound().IsSupertypeOf(GetReferenceType()).
// It is used to bound the type in cases like:
@@ -5749,10 +5946,6 @@ class HBoundType : public HExpression<1> {
// // uper_bound_ will be ClassX
// }
ReferenceTypeInfo upper_bound_;
- // Represents the top constraint that can_be_null_ cannot exceed (i.e. if this
- // is false then can_be_null_ cannot be true).
- bool upper_can_be_null_;
- bool can_be_null_;
DISALLOW_COPY_AND_ASSIGN(HBoundType);
};
@@ -5763,9 +5956,9 @@ class HCheckCast : public HTemplateInstruction<2> {
HLoadClass* constant,
TypeCheckKind check_kind,
uint32_t dex_pc)
- : HTemplateInstruction(SideEffects::CanTriggerGC(), dex_pc),
- check_kind_(check_kind),
- must_do_null_check_(true) {
+ : HTemplateInstruction(SideEffects::CanTriggerGC(), dex_pc) {
+ SetPackedField<TypeCheckKindField>(check_kind);
+ SetPackedFlag<kFlagMustDoNullCheck>(true);
SetRawInputAt(0, object);
SetRawInputAt(1, constant);
}
@@ -5783,17 +5976,21 @@ class HCheckCast : public HTemplateInstruction<2> {
bool CanThrow() const OVERRIDE { return true; }
- bool MustDoNullCheck() const { return must_do_null_check_; }
- void ClearMustDoNullCheck() { must_do_null_check_ = false; }
- TypeCheckKind GetTypeCheckKind() const { return check_kind_; }
-
- bool IsExactCheck() const { return check_kind_ == TypeCheckKind::kExactCheck; }
+ bool MustDoNullCheck() const { return GetPackedFlag<kFlagMustDoNullCheck>(); }
+ void ClearMustDoNullCheck() { SetPackedFlag<kFlagMustDoNullCheck>(false); }
+ TypeCheckKind GetTypeCheckKind() const { return GetPackedField<TypeCheckKindField>(); }
+ bool IsExactCheck() const { return GetTypeCheckKind() == TypeCheckKind::kExactCheck; }
DECLARE_INSTRUCTION(CheckCast);
private:
- const TypeCheckKind check_kind_;
- bool must_do_null_check_;
+ static constexpr size_t kFieldTypeCheckKind = kNumberOfGenericPackedBits;
+ static constexpr size_t kFieldTypeCheckKindSize =
+ MinimumBitsToStore(static_cast<size_t>(TypeCheckKind::kLast));
+ static constexpr size_t kFlagMustDoNullCheck = kFieldTypeCheckKind + kFieldTypeCheckKindSize;
+ static constexpr size_t kNumberOfCheckCastPackedBits = kFlagMustDoNullCheck + 1;
+ static_assert(kNumberOfCheckCastPackedBits <= kMaxNumberOfPackedBits, "Too many packed fields.");
+ using TypeCheckKindField = BitField<TypeCheckKind, kFieldTypeCheckKind, kFieldTypeCheckKindSize>;
DISALLOW_COPY_AND_ASSIGN(HCheckCast);
};
@@ -5802,30 +5999,40 @@ class HMemoryBarrier : public HTemplateInstruction<0> {
public:
explicit HMemoryBarrier(MemBarrierKind barrier_kind, uint32_t dex_pc = kNoDexPc)
: HTemplateInstruction(
- SideEffects::AllWritesAndReads(), dex_pc), // Assume write/read on all fields/arrays.
- barrier_kind_(barrier_kind) {}
+ SideEffects::AllWritesAndReads(), dex_pc) { // Assume write/read on all fields/arrays.
+ SetPackedField<BarrierKindField>(barrier_kind);
+ }
- MemBarrierKind GetBarrierKind() { return barrier_kind_; }
+ MemBarrierKind GetBarrierKind() { return GetPackedField<BarrierKindField>(); }
DECLARE_INSTRUCTION(MemoryBarrier);
private:
- const MemBarrierKind barrier_kind_;
+ static constexpr size_t kFieldBarrierKind = HInstruction::kNumberOfGenericPackedBits;
+ static constexpr size_t kFieldBarrierKindSize =
+ MinimumBitsToStore(static_cast<size_t>(kLastBarrierKind));
+ static constexpr size_t kNumberOfMemoryBarrierPackedBits =
+ kFieldBarrierKind + kFieldBarrierKindSize;
+ static_assert(kNumberOfMemoryBarrierPackedBits <= kMaxNumberOfPackedBits,
+ "Too many packed fields.");
+ using BarrierKindField = BitField<MemBarrierKind, kFieldBarrierKind, kFieldBarrierKindSize>;
DISALLOW_COPY_AND_ASSIGN(HMemoryBarrier);
};
class HMonitorOperation : public HTemplateInstruction<1> {
public:
- enum OperationKind {
+ enum class OperationKind {
kEnter,
kExit,
+ kLast = kExit
};
HMonitorOperation(HInstruction* object, OperationKind kind, uint32_t dex_pc)
: HTemplateInstruction(
- SideEffects::AllExceptGCDependency(), dex_pc), // Assume write/read on all fields/arrays.
- kind_(kind) {
+ SideEffects::AllExceptGCDependency(), // Assume write/read on all fields/arrays.
+ dex_pc) {
+ SetPackedField<OperationKindField>(kind);
SetRawInputAt(0, object);
}
@@ -5839,13 +6046,20 @@ class HMonitorOperation : public HTemplateInstruction<1> {
return IsEnter();
}
-
- bool IsEnter() const { return kind_ == kEnter; }
+ OperationKind GetOperationKind() const { return GetPackedField<OperationKindField>(); }
+ bool IsEnter() const { return GetOperationKind() == OperationKind::kEnter; }
DECLARE_INSTRUCTION(MonitorOperation);
private:
- const OperationKind kind_;
+ static constexpr size_t kFieldOperationKind = HInstruction::kNumberOfGenericPackedBits;
+ static constexpr size_t kFieldOperationKindSize =
+ MinimumBitsToStore(static_cast<size_t>(OperationKind::kLast));
+ static constexpr size_t kNumberOfMonitorOperationPackedBits =
+ kFieldOperationKind + kFieldOperationKindSize;
+ static_assert(kNumberOfMonitorOperationPackedBits <= HInstruction::kMaxNumberOfPackedBits,
+ "Too many packed fields.");
+ using OperationKindField = BitField<OperationKind, kFieldOperationKind, kFieldOperationKindSize>;
private:
DISALLOW_COPY_AND_ASSIGN(HMonitorOperation);
@@ -6022,6 +6236,9 @@ class HParallelMove : public HTemplateInstruction<0> {
} // namespace art
+#if defined(ART_ENABLE_CODEGEN_arm) || defined(ART_ENABLE_CODEGEN_arm64)
+#include "nodes_shared.h"
+#endif
#ifdef ART_ENABLE_CODEGEN_arm
#include "nodes_arm.h"
#endif
diff --git a/compiler/optimizing/nodes_arm64.h b/compiler/optimizing/nodes_arm64.h
index 445cdab191..75a71e78b8 100644
--- a/compiler/optimizing/nodes_arm64.h
+++ b/compiler/optimizing/nodes_arm64.h
@@ -118,38 +118,64 @@ class HArm64IntermediateAddress : public HExpression<2> {
DISALLOW_COPY_AND_ASSIGN(HArm64IntermediateAddress);
};
-class HArm64MultiplyAccumulate : public HExpression<3> {
+class HArm64BitwiseNegatedRight : public HBinaryOperation {
public:
- HArm64MultiplyAccumulate(Primitive::Type type,
- InstructionKind op,
- HInstruction* accumulator,
- HInstruction* mul_left,
- HInstruction* mul_right,
- uint32_t dex_pc = kNoDexPc)
- : HExpression(type, SideEffects::None(), dex_pc), op_kind_(op) {
- SetRawInputAt(kInputAccumulatorIndex, accumulator);
- SetRawInputAt(kInputMulLeftIndex, mul_left);
- SetRawInputAt(kInputMulRightIndex, mul_right);
+ HArm64BitwiseNegatedRight(Primitive::Type result_type,
+ InstructionKind op,
+ HInstruction* left,
+ HInstruction* right,
+ uint32_t dex_pc = kNoDexPc)
+ : HBinaryOperation(result_type, left, right, SideEffects::None(), dex_pc),
+ op_kind_(op) {
+ DCHECK(op == HInstruction::kAnd || op == HInstruction::kOr || op == HInstruction::kXor) << op;
}
- static constexpr int kInputAccumulatorIndex = 0;
- static constexpr int kInputMulLeftIndex = 1;
- static constexpr int kInputMulRightIndex = 2;
+ template <typename T, typename U>
+ auto Compute(T x, U y) const -> decltype(x & ~y) {
+ static_assert(std::is_same<decltype(x & ~y), decltype(x | ~y)>::value &&
+ std::is_same<decltype(x & ~y), decltype(x ^ ~y)>::value,
+ "Inconsistent negated bitwise types");
+ switch (op_kind_) {
+ case HInstruction::kAnd:
+ return x & ~y;
+ case HInstruction::kOr:
+ return x | ~y;
+ case HInstruction::kXor:
+ return x ^ ~y;
+ default:
+ LOG(FATAL) << "Unreachable";
+ UNREACHABLE();
+ }
+ }
- bool CanBeMoved() const OVERRIDE { return true; }
- bool InstructionDataEquals(HInstruction* other) const OVERRIDE {
- return op_kind_ == other->AsArm64MultiplyAccumulate()->op_kind_;
+ HConstant* Evaluate(HIntConstant* x, HIntConstant* y) const OVERRIDE {
+ return GetBlock()->GetGraph()->GetIntConstant(
+ Compute(x->GetValue(), y->GetValue()), GetDexPc());
+ }
+ HConstant* Evaluate(HLongConstant* x, HLongConstant* y) const OVERRIDE {
+ return GetBlock()->GetGraph()->GetLongConstant(
+ Compute(x->GetValue(), y->GetValue()), GetDexPc());
+ }
+ HConstant* Evaluate(HFloatConstant* x ATTRIBUTE_UNUSED,
+ HFloatConstant* y 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 {
+ LOG(FATAL) << DebugName() << " is not defined for double values";
+ UNREACHABLE();
}
InstructionKind GetOpKind() const { return op_kind_; }
- DECLARE_INSTRUCTION(Arm64MultiplyAccumulate);
+ DECLARE_INSTRUCTION(Arm64BitwiseNegatedRight);
private:
- // Indicates if this is a MADD or MSUB.
- InstructionKind op_kind_;
+ // Specifies the bitwise operation, which will be then negated.
+ const InstructionKind op_kind_;
- DISALLOW_COPY_AND_ASSIGN(HArm64MultiplyAccumulate);
+ DISALLOW_COPY_AND_ASSIGN(HArm64BitwiseNegatedRight);
};
} // namespace art
diff --git a/compiler/optimizing/nodes_shared.h b/compiler/optimizing/nodes_shared.h
new file mode 100644
index 0000000000..b04b622838
--- /dev/null
+++ b/compiler/optimizing/nodes_shared.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ART_COMPILER_OPTIMIZING_NODES_SHARED_H_
+#define ART_COMPILER_OPTIMIZING_NODES_SHARED_H_
+
+namespace art {
+
+class HMultiplyAccumulate : public HExpression<3> {
+ public:
+ HMultiplyAccumulate(Primitive::Type type,
+ InstructionKind op,
+ HInstruction* accumulator,
+ HInstruction* mul_left,
+ HInstruction* mul_right,
+ uint32_t dex_pc = kNoDexPc)
+ : HExpression(type, SideEffects::None(), dex_pc), op_kind_(op) {
+ SetRawInputAt(kInputAccumulatorIndex, accumulator);
+ SetRawInputAt(kInputMulLeftIndex, mul_left);
+ SetRawInputAt(kInputMulRightIndex, mul_right);
+ }
+
+ static constexpr int kInputAccumulatorIndex = 0;
+ static constexpr int kInputMulLeftIndex = 1;
+ static constexpr int kInputMulRightIndex = 2;
+
+ bool CanBeMoved() const OVERRIDE { return true; }
+ bool InstructionDataEquals(HInstruction* other) const OVERRIDE {
+ return op_kind_ == other->AsMultiplyAccumulate()->op_kind_;
+ }
+
+ InstructionKind GetOpKind() const { return op_kind_; }
+
+ DECLARE_INSTRUCTION(MultiplyAccumulate);
+
+ private:
+ // Indicates if this is a MADD or MSUB.
+ const InstructionKind op_kind_;
+
+ DISALLOW_COPY_AND_ASSIGN(HMultiplyAccumulate);
+};
+
+} // namespace art
+
+#endif // ART_COMPILER_OPTIMIZING_NODES_SHARED_H_
diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc
index b1891c979e..5a9f2583fd 100644
--- a/compiler/optimizing/optimizing_compiler.cc
+++ b/compiler/optimizing/optimizing_compiler.cc
@@ -60,6 +60,7 @@
#include "induction_var_analysis.h"
#include "inliner.h"
#include "instruction_simplifier.h"
+#include "instruction_simplifier_arm.h"
#include "intrinsics.h"
#include "jit/debugger_interface.h"
#include "jit/jit_code_cache.h"
@@ -438,7 +439,10 @@ static void RunArchOptimizations(InstructionSet instruction_set,
case kThumb2:
case kArm: {
arm::DexCacheArrayFixups* fixups = new (arena) arm::DexCacheArrayFixups(graph, stats);
+ arm::InstructionSimplifierArm* simplifier =
+ new (arena) arm::InstructionSimplifierArm(graph, stats);
HOptimization* arm_optimizations[] = {
+ simplifier,
fixups
};
RunOptimizations(arm_optimizations, arraysize(arm_optimizations), pass_observer);
diff --git a/compiler/optimizing/stack_map_stream.cc b/compiler/optimizing/stack_map_stream.cc
index 4784de1380..54cbdf8b66 100644
--- a/compiler/optimizing/stack_map_stream.cc
+++ b/compiler/optimizing/stack_map_stream.cc
@@ -63,8 +63,7 @@ void StackMapStream::EndStackMapEntry() {
void StackMapStream::AddDexRegisterEntry(DexRegisterLocation::Kind kind, int32_t value) {
if (kind != DexRegisterLocation::Kind::kNone) {
// Ensure we only use non-compressed location kind at this stage.
- DCHECK(DexRegisterLocation::IsShortLocationKind(kind))
- << DexRegisterLocation::PrettyDescriptor(kind);
+ DCHECK(DexRegisterLocation::IsShortLocationKind(kind)) << kind;
DexRegisterLocation location(kind, value);
// Look for Dex register `location` in the location catalog (using the
@@ -257,6 +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);
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) {
@@ -268,6 +268,9 @@ void StackMapStream::FillIn(MemoryRegion region) {
stack_map.SetRegisterMask(stack_map_encoding_, entry.register_mask);
if (entry.sp_mask != nullptr) {
stack_map.SetStackMask(stack_map_encoding_, *entry.sp_mask);
+ } else {
+ // The MemoryRegion does not have to be zeroed, so make sure we clear the bits.
+ stack_map.SetStackMask(stack_map_encoding_, empty_bitmask);
}
if (entry.num_dex_registers == 0 || (entry.live_dex_registers_mask->NumSetBits() == 0)) {
@@ -344,6 +347,11 @@ void StackMapStream::FillIn(MemoryRegion region) {
}
}
}
+
+ // Verify all written data in debug build.
+ if (kIsDebugBuild) {
+ CheckCodeInfo(region);
+ }
}
void StackMapStream::FillInDexRegisterMap(DexRegisterMap dex_register_map,
@@ -423,4 +431,90 @@ bool StackMapStream::HaveTheSameDexMaps(const StackMapEntry& a, const StackMapEn
return true;
}
+// Helper for CheckCodeInfo - check that register map has the expected content.
+void StackMapStream::CheckDexRegisterMap(const CodeInfo& code_info,
+ const DexRegisterMap& dex_register_map,
+ size_t num_dex_registers,
+ BitVector* live_dex_registers_mask,
+ size_t dex_register_locations_index) const {
+ StackMapEncoding encoding = code_info.ExtractEncoding();
+ for (size_t reg = 0; reg < num_dex_registers; reg++) {
+ // Find the location we tried to encode.
+ DexRegisterLocation expected = DexRegisterLocation::None();
+ if (live_dex_registers_mask->IsBitSet(reg)) {
+ size_t catalog_index = dex_register_locations_[dex_register_locations_index++];
+ expected = location_catalog_entries_[catalog_index];
+ }
+ // Compare to the seen location.
+ if (expected.GetKind() == DexRegisterLocation::Kind::kNone) {
+ DCHECK(!dex_register_map.IsValid() || !dex_register_map.IsDexRegisterLive(reg));
+ } else {
+ DCHECK(dex_register_map.IsDexRegisterLive(reg));
+ DexRegisterLocation seen = dex_register_map.GetDexRegisterLocation(
+ reg, num_dex_registers, code_info, encoding);
+ DCHECK_EQ(expected.GetKind(), seen.GetKind());
+ DCHECK_EQ(expected.GetValue(), seen.GetValue());
+ }
+ }
+ if (num_dex_registers == 0) {
+ DCHECK(!dex_register_map.IsValid());
+ }
+}
+
+// Check that all StackMapStream inputs are correctly encoded by trying to read them back.
+void StackMapStream::CheckCodeInfo(MemoryRegion region) const {
+ CodeInfo code_info(region);
+ StackMapEncoding encoding = code_info.ExtractEncoding();
+ DCHECK_EQ(code_info.GetNumberOfStackMaps(), stack_maps_.size());
+ for (size_t s = 0; s < stack_maps_.size(); ++s) {
+ const StackMap stack_map = code_info.GetStackMapAt(s, encoding);
+ StackMapEntry entry = stack_maps_[s];
+
+ // Check main stack map fields.
+ DCHECK_EQ(stack_map.GetNativePcOffset(encoding), entry.native_pc_offset);
+ DCHECK_EQ(stack_map.GetDexPc(encoding), entry.dex_pc);
+ DCHECK_EQ(stack_map.GetRegisterMask(encoding), entry.register_mask);
+ MemoryRegion stack_mask = stack_map.GetStackMask(encoding);
+ if (entry.sp_mask != nullptr) {
+ DCHECK_GE(stack_mask.size_in_bits(), entry.sp_mask->GetNumberOfBits());
+ for (size_t b = 0; b < stack_mask.size_in_bits(); b++) {
+ DCHECK_EQ(stack_mask.LoadBit(b), entry.sp_mask->IsBitSet(b));
+ }
+ } else {
+ for (size_t b = 0; b < stack_mask.size_in_bits(); b++) {
+ DCHECK_EQ(stack_mask.LoadBit(b), 0u);
+ }
+ }
+
+ CheckDexRegisterMap(code_info,
+ code_info.GetDexRegisterMapOf(
+ stack_map, encoding, entry.num_dex_registers),
+ entry.num_dex_registers,
+ entry.live_dex_registers_mask,
+ entry.dex_register_locations_start_index);
+
+ // Check inline info.
+ DCHECK_EQ(stack_map.HasInlineInfo(encoding), (entry.inlining_depth != 0));
+ if (entry.inlining_depth != 0) {
+ InlineInfo inline_info = code_info.GetInlineInfoOf(stack_map, encoding);
+ DCHECK_EQ(inline_info.GetDepth(), entry.inlining_depth);
+ for (size_t d = 0; d < entry.inlining_depth; ++d) {
+ size_t inline_info_index = entry.inline_infos_start_index + d;
+ DCHECK_LT(inline_info_index, inline_infos_.size());
+ InlineInfoEntry inline_entry = inline_infos_[inline_info_index];
+ DCHECK_EQ(inline_info.GetDexPcAtDepth(d), inline_entry.dex_pc);
+ DCHECK_EQ(inline_info.GetMethodIndexAtDepth(d), inline_entry.method_index);
+ DCHECK_EQ(inline_info.GetInvokeTypeAtDepth(d), inline_entry.invoke_type);
+
+ CheckDexRegisterMap(code_info,
+ code_info.GetDexRegisterMapAtDepth(
+ d, inline_info, encoding, inline_entry.num_dex_registers),
+ inline_entry.num_dex_registers,
+ inline_entry.live_dex_registers_mask,
+ inline_entry.dex_register_locations_start_index);
+ }
+ }
+ }
+}
+
} // namespace art
diff --git a/compiler/optimizing/stack_map_stream.h b/compiler/optimizing/stack_map_stream.h
index fc27a2b446..016a911424 100644
--- a/compiler/optimizing/stack_map_stream.h
+++ b/compiler/optimizing/stack_map_stream.h
@@ -167,6 +167,13 @@ class StackMapStream : public ValueObject {
const BitVector& live_dex_registers_mask,
uint32_t start_index_in_dex_register_locations) const;
+ void CheckDexRegisterMap(const CodeInfo& code_info,
+ const DexRegisterMap& dex_register_map,
+ size_t num_dex_registers,
+ BitVector* live_dex_registers_mask,
+ size_t dex_register_locations_index) const;
+ void CheckCodeInfo(MemoryRegion region) const;
+
ArenaAllocator* allocator_;
ArenaVector<StackMapEntry> stack_maps_;