diff options
Diffstat (limited to 'compiler')
-rw-r--r-- | compiler/dex/quick/arm/call_arm.cc | 103 | ||||
-rw-r--r-- | compiler/dex/quick/arm64/call_arm64.cc | 32 | ||||
-rw-r--r-- | compiler/driver/compiler_options.h | 4 | ||||
-rw-r--r-- | compiler/image_writer.cc | 6 | ||||
-rw-r--r-- | compiler/optimizing/bounds_check_elimination.cc | 94 | ||||
-rw-r--r-- | compiler/optimizing/code_generator_arm.cc | 2 | ||||
-rw-r--r-- | compiler/optimizing/code_generator_x86.cc | 2 | ||||
-rw-r--r-- | compiler/optimizing/code_generator_x86_64.cc | 2 | ||||
-rw-r--r-- | compiler/optimizing/common_arm64.h | 10 | ||||
-rw-r--r-- | compiler/optimizing/gvn.cc | 9 | ||||
-rw-r--r-- | compiler/optimizing/nodes.h | 50 |
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; } |