summaryrefslogtreecommitdiff
path: root/compiler
diff options
context:
space:
mode:
Diffstat (limited to 'compiler')
-rw-r--r--compiler/dex/quick/arm/call_arm.cc103
-rw-r--r--compiler/dex/quick/arm64/call_arm64.cc32
-rw-r--r--compiler/driver/compiler_options.h4
-rw-r--r--compiler/image_writer.cc6
-rw-r--r--compiler/optimizing/bounds_check_elimination.cc94
-rw-r--r--compiler/optimizing/code_generator_arm.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/common_arm64.h10
-rw-r--r--compiler/optimizing/gvn.cc9
-rw-r--r--compiler/optimizing/nodes.h50
11 files changed, 259 insertions, 55 deletions
diff --git a/compiler/dex/quick/arm/call_arm.cc b/compiler/dex/quick/arm/call_arm.cc
index 9cf005bc48..1a9dbeae0f 100644
--- a/compiler/dex/quick/arm/call_arm.cc
+++ b/compiler/dex/quick/arm/call_arm.cc
@@ -161,7 +161,11 @@ void ArmMir2Lir::GenMonitorEnter(int opt_flags, RegLocation rl_src) {
NewLIR3(kThumb2Ldrex, rs_r1.GetReg(), rs_r0.GetReg(),
mirror::Object::MonitorOffset().Int32Value() >> 2);
MarkPossibleNullPointerException(opt_flags);
- LIR* not_unlocked_branch = OpCmpImmBranch(kCondNe, rs_r1, 0, NULL);
+ // Zero out the read barrier bits.
+ OpRegRegImm(kOpAnd, rs_r3, rs_r1, LockWord::kReadBarrierStateMaskShiftedToggled);
+ LIR* not_unlocked_branch = OpCmpImmBranch(kCondNe, rs_r3, 0, NULL);
+ // r1 is zero except for the rb bits here. Copy the read barrier bits into r2.
+ OpRegRegReg(kOpOr, rs_r2, rs_r2, rs_r1);
NewLIR4(kThumb2Strex, rs_r1.GetReg(), rs_r2.GetReg(), rs_r0.GetReg(),
mirror::Object::MonitorOffset().Int32Value() >> 2);
LIR* lock_success_branch = OpCmpImmBranch(kCondEq, rs_r1, 0, NULL);
@@ -189,7 +193,14 @@ void ArmMir2Lir::GenMonitorEnter(int opt_flags, RegLocation rl_src) {
NewLIR3(kThumb2Ldrex, rs_r1.GetReg(), rs_r0.GetReg(),
mirror::Object::MonitorOffset().Int32Value() >> 2);
MarkPossibleNullPointerException(opt_flags);
- OpRegImm(kOpCmp, rs_r1, 0);
+ // Zero out the read barrier bits.
+ OpRegRegImm(kOpAnd, rs_r3, rs_r1, LockWord::kReadBarrierStateMaskShiftedToggled);
+ // r1 will be zero except for the rb bits if the following
+ // cmp-and-branch branches to eq where r2 will be used. Copy the
+ // read barrier bits into r2.
+ OpRegRegReg(kOpOr, rs_r2, rs_r2, rs_r1);
+ OpRegImm(kOpCmp, rs_r3, 0);
+
LIR* it = OpIT(kCondEq, "");
NewLIR4(kThumb2Strex/*eq*/, rs_r1.GetReg(), rs_r2.GetReg(), rs_r0.GetReg(),
mirror::Object::MonitorOffset().Int32Value() >> 2);
@@ -228,14 +239,28 @@ void ArmMir2Lir::GenMonitorExit(int opt_flags, RegLocation rl_src) {
null_check_branch = OpCmpImmBranch(kCondEq, rs_r0, 0, NULL);
}
}
- Load32Disp(rs_r0, mirror::Object::MonitorOffset().Int32Value(), rs_r1);
+ if (!kUseReadBarrier) {
+ Load32Disp(rs_r0, mirror::Object::MonitorOffset().Int32Value(), rs_r1); // Get lock
+ } else {
+ NewLIR3(kThumb2Ldrex, rs_r1.GetReg(), rs_r0.GetReg(),
+ mirror::Object::MonitorOffset().Int32Value() >> 2);
+ }
MarkPossibleNullPointerException(opt_flags);
- LoadConstantNoClobber(rs_r3, 0);
- LIR* slow_unlock_branch = OpCmpBranch(kCondNe, rs_r1, rs_r2, NULL);
+ // Zero out the read barrier bits.
+ OpRegRegImm(kOpAnd, rs_r3, rs_r1, LockWord::kReadBarrierStateMaskShiftedToggled);
+ // Zero out except the read barrier bits.
+ OpRegRegImm(kOpAnd, rs_r1, rs_r1, LockWord::kReadBarrierStateMaskShifted);
+ LIR* slow_unlock_branch = OpCmpBranch(kCondNe, rs_r3, rs_r2, NULL);
GenMemBarrier(kAnyStore);
- Store32Disp(rs_r0, mirror::Object::MonitorOffset().Int32Value(), rs_r3);
- LIR* unlock_success_branch = OpUnconditionalBranch(NULL);
-
+ LIR* unlock_success_branch;
+ if (!kUseReadBarrier) {
+ Store32Disp(rs_r0, mirror::Object::MonitorOffset().Int32Value(), rs_r1);
+ unlock_success_branch = OpUnconditionalBranch(NULL);
+ } else {
+ NewLIR4(kThumb2Strex, rs_r2.GetReg(), rs_r1.GetReg(), rs_r0.GetReg(),
+ mirror::Object::MonitorOffset().Int32Value() >> 2);
+ unlock_success_branch = OpCmpImmBranch(kCondEq, rs_r2, 0, NULL);
+ }
LIR* slow_path_target = NewLIR0(kPseudoTargetLabel);
slow_unlock_branch->target = slow_path_target;
if (null_check_branch != nullptr) {
@@ -253,25 +278,57 @@ void ArmMir2Lir::GenMonitorExit(int opt_flags, RegLocation rl_src) {
} else {
// Explicit null-check as slow-path is entered using an IT.
GenNullCheck(rs_r0, opt_flags);
- Load32Disp(rs_r0, mirror::Object::MonitorOffset().Int32Value(), rs_r1); // Get lock
+ if (!kUseReadBarrier) {
+ Load32Disp(rs_r0, mirror::Object::MonitorOffset().Int32Value(), rs_r1); // Get lock
+ } else {
+ // If we use read barriers, we need to use atomic instructions.
+ NewLIR3(kThumb2Ldrex, rs_r1.GetReg(), rs_r0.GetReg(),
+ mirror::Object::MonitorOffset().Int32Value() >> 2);
+ }
MarkPossibleNullPointerException(opt_flags);
Load32Disp(rs_rARM_SELF, Thread::ThinLockIdOffset<4>().Int32Value(), rs_r2);
- LoadConstantNoClobber(rs_r3, 0);
+ // Zero out the read barrier bits.
+ OpRegRegImm(kOpAnd, rs_r3, rs_r1, LockWord::kReadBarrierStateMaskShiftedToggled);
+ // Zero out except the read barrier bits.
+ OpRegRegImm(kOpAnd, rs_r1, rs_r1, LockWord::kReadBarrierStateMaskShifted);
// Is lock unheld on lock or held by us (==thread_id) on unlock?
- OpRegReg(kOpCmp, rs_r1, rs_r2);
-
- LIR* it = OpIT(kCondEq, "EE");
- if (GenMemBarrier(kAnyStore)) {
- UpdateIT(it, "TEE");
+ OpRegReg(kOpCmp, rs_r3, rs_r2);
+ if (!kUseReadBarrier) {
+ LIR* it = OpIT(kCondEq, "EE");
+ if (GenMemBarrier(kAnyStore)) {
+ UpdateIT(it, "TEE");
+ }
+ Store32Disp/*eq*/(rs_r0, mirror::Object::MonitorOffset().Int32Value(), rs_r1);
+ // Go expensive route - UnlockObjectFromCode(obj);
+ LoadWordDisp/*ne*/(rs_rARM_SELF, QUICK_ENTRYPOINT_OFFSET(4, pUnlockObject).Int32Value(),
+ rs_rARM_LR);
+ ClobberCallerSave();
+ LIR* call_inst = OpReg(kOpBlx/*ne*/, rs_rARM_LR);
+ OpEndIT(it);
+ MarkSafepointPC(call_inst);
+ } else {
+ // If we use read barriers, we need to use atomic instructions.
+ LIR* it = OpIT(kCondEq, "");
+ if (GenMemBarrier(kAnyStore)) {
+ UpdateIT(it, "T");
+ }
+ NewLIR4/*eq*/(kThumb2Strex, rs_r2.GetReg(), rs_r1.GetReg(), rs_r0.GetReg(),
+ mirror::Object::MonitorOffset().Int32Value() >> 2);
+ OpEndIT(it);
+ // Since we know r2 wasn't zero before the above it instruction,
+ // if r2 is zero here, we know r3 was equal to r2 and the strex
+ // suceeded (we're done). Otherwise (either r3 wasn't equal to r2
+ // or the strex failed), call the entrypoint.
+ OpRegImm(kOpCmp, rs_r2, 0);
+ LIR* it2 = OpIT(kCondNe, "T");
+ // Go expensive route - UnlockObjectFromCode(obj);
+ LoadWordDisp/*ne*/(rs_rARM_SELF, QUICK_ENTRYPOINT_OFFSET(4, pUnlockObject).Int32Value(),
+ rs_rARM_LR);
+ ClobberCallerSave();
+ LIR* call_inst = OpReg(kOpBlx/*ne*/, rs_rARM_LR);
+ OpEndIT(it2);
+ MarkSafepointPC(call_inst);
}
- Store32Disp/*eq*/(rs_r0, mirror::Object::MonitorOffset().Int32Value(), rs_r3);
- // Go expensive route - UnlockObjectFromCode(obj);
- LoadWordDisp/*ne*/(rs_rARM_SELF, QUICK_ENTRYPOINT_OFFSET(4, pUnlockObject).Int32Value(),
- rs_rARM_LR);
- ClobberCallerSave();
- LIR* call_inst = OpReg(kOpBlx/*ne*/, rs_rARM_LR);
- OpEndIT(it);
- MarkSafepointPC(call_inst);
}
}
diff --git a/compiler/dex/quick/arm64/call_arm64.cc b/compiler/dex/quick/arm64/call_arm64.cc
index 24e8fdff80..15edcc5142 100644
--- a/compiler/dex/quick/arm64/call_arm64.cc
+++ b/compiler/dex/quick/arm64/call_arm64.cc
@@ -172,7 +172,12 @@ void Arm64Mir2Lir::GenMonitorEnter(int opt_flags, RegLocation rl_src) {
OpRegRegImm(kOpAdd, rs_x2, rs_x0, mirror::Object::MonitorOffset().Int32Value());
NewLIR2(kA64Ldxr2rX, rw3, rx2);
MarkPossibleNullPointerException(opt_flags);
- LIR* not_unlocked_branch = OpCmpImmBranch(kCondNe, rs_w3, 0, NULL);
+ // Zero out the read barrier bits.
+ OpRegRegImm(kOpAnd, rs_w2, rs_w3, LockWord::kReadBarrierStateMaskShiftedToggled);
+ LIR* not_unlocked_branch = OpCmpImmBranch(kCondNe, rs_w2, 0, NULL);
+ // w3 is zero except for the rb bits here. Copy the read barrier bits into w1.
+ OpRegRegReg(kOpOr, rs_w1, rs_w1, rs_w3);
+ OpRegRegImm(kOpAdd, rs_x2, rs_x0, mirror::Object::MonitorOffset().Int32Value());
NewLIR3(kA64Stxr3wrX, rw3, rw1, rx2);
LIR* lock_success_branch = OpCmpImmBranch(kCondEq, rs_w3, 0, NULL);
@@ -217,13 +222,28 @@ void Arm64Mir2Lir::GenMonitorExit(int opt_flags, RegLocation rl_src) {
}
}
Load32Disp(rs_xSELF, Thread::ThinLockIdOffset<8>().Int32Value(), rs_w1);
- Load32Disp(rs_x0, mirror::Object::MonitorOffset().Int32Value(), rs_w2);
+ if (!kUseReadBarrier) {
+ Load32Disp(rs_x0, mirror::Object::MonitorOffset().Int32Value(), rs_w2);
+ } else {
+ OpRegRegImm(kOpAdd, rs_x3, rs_x0, mirror::Object::MonitorOffset().Int32Value());
+ NewLIR2(kA64Ldxr2rX, rw2, rx3);
+ }
MarkPossibleNullPointerException(opt_flags);
- LIR* slow_unlock_branch = OpCmpBranch(kCondNe, rs_w1, rs_w2, NULL);
+ // Zero out the read barrier bits.
+ OpRegRegImm(kOpAnd, rs_w3, rs_w2, LockWord::kReadBarrierStateMaskShiftedToggled);
+ // Zero out except the read barrier bits.
+ OpRegRegImm(kOpAnd, rs_w2, rs_w2, LockWord::kReadBarrierStateMaskShifted);
+ LIR* slow_unlock_branch = OpCmpBranch(kCondNe, rs_w3, rs_w1, NULL);
GenMemBarrier(kAnyStore);
- Store32Disp(rs_x0, mirror::Object::MonitorOffset().Int32Value(), rs_wzr);
- LIR* unlock_success_branch = OpUnconditionalBranch(NULL);
-
+ LIR* unlock_success_branch;
+ if (!kUseReadBarrier) {
+ Store32Disp(rs_x0, mirror::Object::MonitorOffset().Int32Value(), rs_w2);
+ unlock_success_branch = OpUnconditionalBranch(NULL);
+ } else {
+ OpRegRegImm(kOpAdd, rs_x3, rs_x0, mirror::Object::MonitorOffset().Int32Value());
+ NewLIR3(kA64Stxr3wrX, rw1, rw2, rx3);
+ unlock_success_branch = OpCmpImmBranch(kCondEq, rs_w1, 0, NULL);
+ }
LIR* slow_path_target = NewLIR0(kPseudoTargetLabel);
slow_unlock_branch->target = slow_path_target;
if (null_check_branch != nullptr) {
diff --git a/compiler/driver/compiler_options.h b/compiler/driver/compiler_options.h
index 122ae4b575..0683d185e6 100644
--- a/compiler/driver/compiler_options.h
+++ b/compiler/driver/compiler_options.h
@@ -41,11 +41,7 @@ class CompilerOptions FINAL {
};
// Guide heuristics to determine whether to compile method if profile data not available.
-#if ART_SMALL_MODE
- static const CompilerFilter kDefaultCompilerFilter = kInterpretOnly;
-#else
static const CompilerFilter kDefaultCompilerFilter = kSpeed;
-#endif
static const size_t kDefaultHugeMethodThreshold = 10000;
static const size_t kDefaultLargeMethodThreshold = 600;
static const size_t kDefaultSmallMethodThreshold = 60;
diff --git a/compiler/image_writer.cc b/compiler/image_writer.cc
index f5f9320532..b4732c87c8 100644
--- a/compiler/image_writer.cc
+++ b/compiler/image_writer.cc
@@ -909,7 +909,9 @@ void ImageWriter::CopyAndFixupObjects() {
heap->VisitObjects(CopyAndFixupObjectsCallback, this);
// Fix up the object previously had hash codes.
for (const std::pair<mirror::Object*, uint32_t>& hash_pair : saved_hashes_) {
- hash_pair.first->SetLockWord(LockWord::FromHashCode(hash_pair.second), false);
+ Object* obj = hash_pair.first;
+ DCHECK_EQ(obj->GetLockWord(false).ReadBarrierState(), 0U);
+ obj->SetLockWord(LockWord::FromHashCode(hash_pair.second, 0U), false);
}
saved_hashes_.clear();
}
@@ -935,7 +937,7 @@ void ImageWriter::CopyAndFixupObjectsCallback(Object* obj, void* arg) {
Object* copy = reinterpret_cast<Object*>(dst);
// Write in a hash code of objects which have inflated monitors or a hash code in their monitor
// word.
- copy->SetLockWord(LockWord(), false);
+ copy->SetLockWord(LockWord::Default(), false);
image_writer->FixupObject(obj, copy);
}
diff --git a/compiler/optimizing/bounds_check_elimination.cc b/compiler/optimizing/bounds_check_elimination.cc
index deaeb8ef47..4ca364867d 100644
--- a/compiler/optimizing/bounds_check_elimination.cc
+++ b/compiler/optimizing/bounds_check_elimination.cc
@@ -261,8 +261,8 @@ class ValueRange : public ArenaObject<kArenaAllocMisc> {
virtual ~ValueRange() {}
- virtual const MonotonicValueRange* AsMonotonicValueRange() const { return nullptr; }
- bool IsMonotonicValueRange() const {
+ virtual MonotonicValueRange* AsMonotonicValueRange() { return nullptr; }
+ bool IsMonotonicValueRange() {
return AsMonotonicValueRange() != nullptr;
}
@@ -345,7 +345,11 @@ class MonotonicValueRange : public ValueRange {
virtual ~MonotonicValueRange() {}
- const MonotonicValueRange* AsMonotonicValueRange() const OVERRIDE { return this; }
+ int32_t GetIncrement() const { return increment_; }
+
+ ValueBound GetBound() const { return bound_; }
+
+ MonotonicValueRange* AsMonotonicValueRange() OVERRIDE { return this; }
// If it's certain that this value range fits in other_range.
bool FitsIn(ValueRange* other_range) const OVERRIDE {
@@ -494,6 +498,73 @@ class BCEVisitor : public HGraphVisitor {
}
}
+ // Special case that we may simultaneously narrow two MonotonicValueRange's to
+ // regular value ranges.
+ void HandleIfBetweenTwoMonotonicValueRanges(HIf* instruction,
+ HInstruction* left,
+ HInstruction* right,
+ IfCondition cond,
+ MonotonicValueRange* left_range,
+ MonotonicValueRange* right_range) {
+ DCHECK(left->IsLoopHeaderPhi());
+ DCHECK(right->IsLoopHeaderPhi());
+ if (instruction->GetBlock() != left->GetBlock()) {
+ // Comparison needs to be in loop header to make sure it's done after each
+ // increment/decrement.
+ return;
+ }
+
+ // Handle common cases which also don't have overflow/underflow concerns.
+ if (left_range->GetIncrement() == 1 &&
+ left_range->GetBound().IsConstant() &&
+ right_range->GetIncrement() == -1 &&
+ right_range->GetBound().IsRelatedToArrayLength() &&
+ right_range->GetBound().GetConstant() < 0) {
+ HBasicBlock* successor = nullptr;
+ int32_t left_compensation = 0;
+ int32_t right_compensation = 0;
+ if (cond == kCondLT) {
+ left_compensation = -1;
+ right_compensation = 1;
+ successor = instruction->IfTrueSuccessor();
+ } else if (cond == kCondLE) {
+ successor = instruction->IfTrueSuccessor();
+ } else if (cond == kCondGT) {
+ successor = instruction->IfFalseSuccessor();
+ } else if (cond == kCondGE) {
+ left_compensation = -1;
+ right_compensation = 1;
+ successor = instruction->IfFalseSuccessor();
+ } else {
+ // We don't handle '=='/'!=' test in case left and right can cross and
+ // miss each other.
+ return;
+ }
+
+ if (successor != nullptr) {
+ bool overflow;
+ bool underflow;
+ ValueRange* new_left_range = new (GetGraph()->GetArena()) ValueRange(
+ GetGraph()->GetArena(),
+ left_range->GetBound(),
+ right_range->GetBound().Add(left_compensation, &overflow, &underflow));
+ if (!overflow && !underflow) {
+ ApplyRangeFromComparison(left, instruction->GetBlock(), successor,
+ new_left_range);
+ }
+
+ ValueRange* new_right_range = new (GetGraph()->GetArena()) ValueRange(
+ GetGraph()->GetArena(),
+ left_range->GetBound().Add(right_compensation, &overflow, &underflow),
+ right_range->GetBound());
+ if (!overflow && !underflow) {
+ ApplyRangeFromComparison(right, instruction->GetBlock(), successor,
+ new_right_range);
+ }
+ }
+ }
+ }
+
// Handle "if (left cmp_cond right)".
void HandleIf(HIf* instruction, HInstruction* left, HInstruction* right, IfCondition cond) {
HBasicBlock* block = instruction->GetBlock();
@@ -515,10 +586,19 @@ class BCEVisitor : public HGraphVisitor {
if (!found) {
// No constant or array.length+c format bound found.
// For i<j, we can still use j's upper bound as i's upper bound. Same for lower.
- ValueRange* range = LookupValueRange(right, block);
- if (range != nullptr) {
- lower = range->GetLower();
- upper = range->GetUpper();
+ ValueRange* right_range = LookupValueRange(right, block);
+ if (right_range != nullptr) {
+ if (right_range->IsMonotonicValueRange()) {
+ ValueRange* left_range = LookupValueRange(left, block);
+ if (left_range != nullptr && left_range->IsMonotonicValueRange()) {
+ HandleIfBetweenTwoMonotonicValueRanges(instruction, left, right, cond,
+ left_range->AsMonotonicValueRange(),
+ right_range->AsMonotonicValueRange());
+ return;
+ }
+ }
+ lower = right_range->GetLower();
+ upper = right_range->GetUpper();
} else {
lower = ValueBound::Min();
upper = ValueBound::Max();
diff --git a/compiler/optimizing/code_generator_arm.cc b/compiler/optimizing/code_generator_arm.cc
index cda5c1a99c..07cc41a8d5 100644
--- a/compiler/optimizing/code_generator_arm.cc
+++ b/compiler/optimizing/code_generator_arm.cc
@@ -988,7 +988,7 @@ void InstructionCodeGeneratorARM::VisitCondition(HCondition* comp) {
__ cmp(left, ShifterOperand(locations->InAt(1).AsRegister<Register>()));
} else {
DCHECK(locations->InAt(1).IsConstant());
- int32_t value = locations->InAt(1).GetConstant()->AsIntConstant()->GetValue();
+ int32_t value = CodeGenerator::GetInt32ValueOf(locations->InAt(1).GetConstant());
ShifterOperand operand;
if (GetAssembler()->ShifterOperandCanHold(R0, left, CMP, value, &operand)) {
__ cmp(left, operand);
diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc
index 116dd158d1..3c8f62c789 100644
--- a/compiler/optimizing/code_generator_x86.cc
+++ b/compiler/optimizing/code_generator_x86.cc
@@ -922,7 +922,7 @@ void InstructionCodeGeneratorX86::VisitCondition(HCondition* comp) {
if (rhs.IsRegister()) {
__ cmpl(lhs.AsRegister<Register>(), rhs.AsRegister<Register>());
} else if (rhs.IsConstant()) {
- int32_t constant = rhs.GetConstant()->AsIntConstant()->GetValue();
+ int32_t constant = CodeGenerator::GetInt32ValueOf(rhs.GetConstant());
if (constant == 0) {
__ testl(lhs.AsRegister<Register>(), lhs.AsRegister<Register>());
} else {
diff --git a/compiler/optimizing/code_generator_x86_64.cc b/compiler/optimizing/code_generator_x86_64.cc
index adc022a2ce..6365bca319 100644
--- a/compiler/optimizing/code_generator_x86_64.cc
+++ b/compiler/optimizing/code_generator_x86_64.cc
@@ -894,7 +894,7 @@ void InstructionCodeGeneratorX86_64::VisitCondition(HCondition* comp) {
if (rhs.IsRegister()) {
__ cmpl(lhs.AsRegister<CpuRegister>(), rhs.AsRegister<CpuRegister>());
} else if (rhs.IsConstant()) {
- int32_t constant = rhs.GetConstant()->AsIntConstant()->GetValue();
+ int32_t constant = CodeGenerator::GetInt32ValueOf(rhs.GetConstant());
if (constant == 0) {
__ testl(lhs.AsRegister<CpuRegister>(), lhs.AsRegister<CpuRegister>());
} else {
diff --git a/compiler/optimizing/common_arm64.h b/compiler/optimizing/common_arm64.h
index 007324eb68..9447d3b816 100644
--- a/compiler/optimizing/common_arm64.h
+++ b/compiler/optimizing/common_arm64.h
@@ -118,8 +118,14 @@ static inline vixl::CPURegister InputCPURegisterAt(HInstruction* instr, int inde
static inline int64_t Int64ConstantFrom(Location location) {
HConstant* instr = location.GetConstant();
- return instr->IsIntConstant() ? instr->AsIntConstant()->GetValue()
- : instr->AsLongConstant()->GetValue();
+ if (instr->IsIntConstant()) {
+ return instr->AsIntConstant()->GetValue();
+ } else if (instr->IsNullConstant()) {
+ return 0;
+ } else {
+ DCHECK(instr->IsLongConstant());
+ return instr->AsLongConstant()->GetValue();
+ }
}
static inline vixl::Operand OperandFrom(Location location, Primitive::Type type) {
diff --git a/compiler/optimizing/gvn.cc b/compiler/optimizing/gvn.cc
index cb448c883f..ea65dc0780 100644
--- a/compiler/optimizing/gvn.cc
+++ b/compiler/optimizing/gvn.cc
@@ -299,8 +299,17 @@ void GlobalValueNumberer::VisitBasicBlock(HBasicBlock* block) {
// Save the next instruction in case `current` is removed from the graph.
HInstruction* next = current->GetNext();
if (current->CanBeMoved()) {
+ if (current->IsBinaryOperation() && current->AsBinaryOperation()->IsCommutative()) {
+ // For commutative ops, (x op y) will be treated the same as (y op x)
+ // after fixed ordering.
+ current->AsBinaryOperation()->OrderInputs();
+ }
HInstruction* existing = set->Lookup(current);
if (existing != nullptr) {
+ // This replacement doesn't make more OrderInputs() necessary since
+ // current is either used by an instruction that it dominates,
+ // which hasn't been visited yet due to the order we visit instructions.
+ // Or current is used by a phi, and we don't do OrderInputs() on a phi anyway.
current->ReplaceWith(existing);
current->GetBlock()->RemoveInstruction(current);
} else {
diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h
index 7e075644ef..98076a05f2 100644
--- a/compiler/optimizing/nodes.h
+++ b/compiler/optimizing/nodes.h
@@ -1500,7 +1500,39 @@ class HBinaryOperation : public HExpression<2> {
HInstruction* GetRight() const { return InputAt(1); }
Primitive::Type GetResultType() const { return GetType(); }
- virtual bool IsCommutative() { return false; }
+ virtual bool IsCommutative() const { return false; }
+
+ // Put constant on the right.
+ // Returns whether order is changed.
+ bool OrderInputsWithConstantOnTheRight() {
+ HInstruction* left = InputAt(0);
+ HInstruction* right = InputAt(1);
+ if (left->IsConstant() && !right->IsConstant()) {
+ ReplaceInput(right, 0);
+ ReplaceInput(left, 1);
+ return true;
+ }
+ return false;
+ }
+
+ // Order inputs by instruction id, but favor constant on the right side.
+ // This helps GVN for commutative ops.
+ void OrderInputs() {
+ DCHECK(IsCommutative());
+ HInstruction* left = InputAt(0);
+ HInstruction* right = InputAt(1);
+ if (left == right || (!left->IsConstant() && right->IsConstant())) {
+ return;
+ }
+ if (OrderInputsWithConstantOnTheRight()) {
+ return;
+ }
+ // Order according to instruction id.
+ if (left->GetId() > right->GetId()) {
+ ReplaceInput(right, 0);
+ ReplaceInput(left, 1);
+ }
+ }
virtual bool CanBeMoved() const { return true; }
virtual bool InstructionDataEquals(HInstruction* other) const {
@@ -1529,8 +1561,6 @@ class HCondition : public HBinaryOperation {
: HBinaryOperation(Primitive::kPrimBoolean, first, second),
needs_materialization_(true) {}
- virtual bool IsCommutative() { return true; }
-
bool NeedsMaterialization() const { return needs_materialization_; }
void ClearNeedsMaterialization() { needs_materialization_ = false; }
@@ -1556,6 +1586,8 @@ class HEqual : public HCondition {
HEqual(HInstruction* first, HInstruction* second)
: HCondition(first, second) {}
+ bool IsCommutative() const OVERRIDE { return true; }
+
virtual int32_t Evaluate(int32_t x, int32_t y) const OVERRIDE {
return x == y ? 1 : 0;
}
@@ -1578,6 +1610,8 @@ class HNotEqual : public HCondition {
HNotEqual(HInstruction* first, HInstruction* second)
: HCondition(first, second) {}
+ bool IsCommutative() const OVERRIDE { return true; }
+
virtual int32_t Evaluate(int32_t x, int32_t y) const OVERRIDE {
return x != y ? 1 : 0;
}
@@ -2136,7 +2170,7 @@ class HAdd : public HBinaryOperation {
HAdd(Primitive::Type result_type, HInstruction* left, HInstruction* right)
: HBinaryOperation(result_type, left, right) {}
- virtual bool IsCommutative() { return true; }
+ virtual bool IsCommutative() const OVERRIDE { return true; }
virtual int32_t Evaluate(int32_t x, int32_t y) const OVERRIDE {
return x + y;
@@ -2174,7 +2208,7 @@ class HMul : public HBinaryOperation {
HMul(Primitive::Type result_type, HInstruction* left, HInstruction* right)
: HBinaryOperation(result_type, left, right) {}
- virtual bool IsCommutative() { return true; }
+ virtual bool IsCommutative() const OVERRIDE { return true; }
virtual int32_t Evaluate(int32_t x, int32_t y) const { return x * y; }
virtual int64_t Evaluate(int64_t x, int64_t y) const { return x * y; }
@@ -2323,7 +2357,7 @@ class HAnd : public HBinaryOperation {
HAnd(Primitive::Type result_type, HInstruction* left, HInstruction* right)
: HBinaryOperation(result_type, left, right) {}
- bool IsCommutative() OVERRIDE { return true; }
+ bool IsCommutative() const OVERRIDE { return true; }
int32_t Evaluate(int32_t x, int32_t y) const OVERRIDE { return x & y; }
int64_t Evaluate(int64_t x, int64_t y) const OVERRIDE { return x & y; }
@@ -2339,7 +2373,7 @@ class HOr : public HBinaryOperation {
HOr(Primitive::Type result_type, HInstruction* left, HInstruction* right)
: HBinaryOperation(result_type, left, right) {}
- bool IsCommutative() OVERRIDE { return true; }
+ bool IsCommutative() const OVERRIDE { return true; }
int32_t Evaluate(int32_t x, int32_t y) const OVERRIDE { return x | y; }
int64_t Evaluate(int64_t x, int64_t y) const OVERRIDE { return x | y; }
@@ -2355,7 +2389,7 @@ class HXor : public HBinaryOperation {
HXor(Primitive::Type result_type, HInstruction* left, HInstruction* right)
: HBinaryOperation(result_type, left, right) {}
- bool IsCommutative() OVERRIDE { return true; }
+ bool IsCommutative() const OVERRIDE { return true; }
int32_t Evaluate(int32_t x, int32_t y) const OVERRIDE { return x ^ y; }
int64_t Evaluate(int64_t x, int64_t y) const OVERRIDE { return x ^ y; }