Chromium Code Reviews| Index: src/interpreter/bytecode-peephole-optimizer.cc |
| diff --git a/src/interpreter/bytecode-peephole-optimizer.cc b/src/interpreter/bytecode-peephole-optimizer.cc |
| index 9683e301e8b5a345c708183d652354479271afa7..c8e02413c5c5bd98aa51829870e65c64463b4419 100644 |
| --- a/src/interpreter/bytecode-peephole-optimizer.cc |
| +++ b/src/interpreter/bytecode-peephole-optimizer.cc |
| @@ -94,25 +94,8 @@ bool BytecodePeepholeOptimizer::LastBytecodePutsNameInAccumulator() const { |
| GetConstantForIndexOperand(&last_, 0)->IsName())); |
| } |
| -void BytecodePeepholeOptimizer::UpdateLastAndCurrentBytecodes( |
| - BytecodeNode* current) { |
| - if (Bytecodes::IsJumpIfToBoolean(current->bytecode()) && |
| - Bytecodes::WritesBooleanToAccumulator(last_.bytecode())) { |
| - // Conditional jumps with boolean conditions are emitted in |
| - // ToBoolean form by the bytecode array builder, |
| - // i.e. JumpIfToBooleanTrue rather JumpIfTrue. The ToBoolean element |
| - // can be removed if the previous bytecode put a boolean value in |
| - // the accumulator. |
| - Bytecode jump = Bytecodes::GetJumpWithoutToBoolean(current->bytecode()); |
| - current->set_bytecode(jump, current->operand(0), current->operand_scale()); |
| - } else if (current->bytecode() == Bytecode::kToBooleanLogicalNot && |
| - Bytecodes::WritesBooleanToAccumulator(last_.bytecode())) { |
| - // Logical-nots are emitted in ToBoolean form by the bytecode array |
| - // builder, The ToBoolean element can be removed if the previous bytecode |
| - // put a boolean value in the accumulator. |
| - current->set_bytecode(Bytecode::kLogicalNot); |
| - } |
| - |
| +void BytecodePeepholeOptimizer::TryToRemoveLastExpressionPosition( |
| + const BytecodeNode* const current) { |
| if (current->source_info().is_statement() && |
| last_.source_info().is_expression() && |
| Bytecodes::IsWithoutExternalSideEffects(last_.bytecode())) { |
| @@ -185,6 +168,94 @@ bool BytecodePeepholeOptimizer::CanElideLastBasedOnSourcePosition( |
| !current->source_info().is_valid()); |
| } |
| +namespace { |
|
rmcilroy
2016/05/24 14:00:11
nit - newline after {
oth
2016/05/24 15:07:14
Done.
|
| +void TransformLdaStarToLdrLdar(Bytecode new_bytecode, BytecodeNode* const last, |
| + BytecodeNode* const current) { |
| + DCHECK_EQ(current->bytecode(), Bytecodes::kStar); |
| + // |
| + // An example transformation here would be: |
| + // |
| + // LdaGlobal i0, i1 ____\ LdrGlobal i0, i1, R |
| + // Star R ====/ Ldar R |
| + // |
| + // which loads a global value into both a register and the |
| + // accumulator. However, in the second form the Ldar can often be |
| + // peephole optimized away unlike the Star in the first form. |
| + // |
| + last->Transform(new_bytecode, current->operand(0), current->operand_scale()); |
| + current->set_bytecode(Bytecode::kLdar, current->operand(0), |
| + current->operand_scale()); |
| + |
| + // If there was a source position on |current| transfer it to the |
| + // updated |last| to maintain the debugger's causal view. ie. if an |
| + // expression position LdrGlobal is the bytecode that could throw |
| + // and if a statement position it needs to be placed before the |
| + // store to R occurs. |
| + last->source_info().Update(current->source_info()); |
| + current->source_info().set_invalid(); |
| +} |
| +} // namespace |
|
rmcilroy
2016/05/24 14:00:11
nit - newline above
oth
2016/05/24 15:07:15
Done.
|
| + |
| +bool BytecodePeepholeOptimizer::ChangeLdaToLdr(BytecodeNode* const current) { |
| + if (current->bytecode() == Bytecode::kStar) { |
| + switch (last_.bytecode()) { |
| + case Bytecode::kLoadIC: |
| + TransformLdaStarToLdrLdar(Bytecode::kLdrNamedProperty, &last_, current); |
| + return true; |
| + case Bytecode::kKeyedLoadIC: |
| + TransformLdaStarToLdrLdar(Bytecode::kLdrKeyedProperty, &last_, current); |
| + return true; |
| + case Bytecode::kLdaGlobal: |
| + TransformLdaStarToLdrLdar(Bytecode::kLdrGlobal, &last_, current); |
| + return true; |
| + case Bytecode::kLdaContextSlot: |
| + TransformLdaStarToLdrLdar(Bytecode::kLdrContextSlot, &last_, current); |
| + return true; |
| + case Bytecode::kLdaUndefined: |
| + TransformLdaStarToLdrLdar(Bytecode::kLdrUndefined, &last_, current); |
| + return true; |
| + default: |
| + break; |
| + } |
| + } |
| + return false; |
| +} |
| + |
| +bool BytecodePeepholeOptimizer::RemoveToBooleanFromJump( |
| + BytecodeNode* const current) { |
| + bool can_remove = Bytecodes::IsJumpIfToBoolean(current->bytecode()) && |
| + Bytecodes::WritesBooleanToAccumulator(last_.bytecode()); |
| + if (can_remove) { |
| + // Conditional jumps with boolean conditions are emiitted in |
| + // ToBoolean form by the bytecode array builder, |
| + // i.e. JumpIfToBooleanTrue rather JumpIfTrue. The ToBoolean |
| + // element can be removed if the previous bytecode put a boolean |
| + // value in the accumulator. |
| + Bytecode jump = Bytecodes::GetJumpWithoutToBoolean(current->bytecode()); |
| + current->set_bytecode(jump, current->operand(0), current->operand_scale()); |
| + } |
| + return can_remove; |
| +} |
| + |
| +bool BytecodePeepholeOptimizer::RemoveToBooleanFromLogicalNot( |
| + BytecodeNode* const current) { |
| + bool can_remove = current->bytecode() == Bytecode::kToBooleanLogicalNot && |
| + Bytecodes::WritesBooleanToAccumulator(last_.bytecode()); |
| + if (can_remove) { |
| + // Logical-nots are emitted in ToBoolean form by the bytecode array |
| + // builder, The ToBoolean element can be removed if the previous bytecode |
| + // put a boolean value in the accumulator. |
| + current->set_bytecode(Bytecode::kLogicalNot); |
| + } |
| + return can_remove; |
| +} |
| + |
| +bool BytecodePeepholeOptimizer::UpdateLastAndCurrentBytecodes( |
| + BytecodeNode* const current) { |
| + return RemoveToBooleanFromJump(current) || |
| + RemoveToBooleanFromLogicalNot(current) || ChangeLdaToLdr(current); |
| +} |
| + |
| bool BytecodePeepholeOptimizer::CanElideLast( |
| const BytecodeNode* const current) const { |
| if (!last_is_discardable_) { |
| @@ -200,21 +271,36 @@ bool BytecodePeepholeOptimizer::CanElideLast( |
| // consecutive accumulator loads (that don't have side effects) then only |
| // the final load is potentially visible. |
| return true; |
| + } else if (Bytecodes::GetAccumulatorUse(current->bytecode()) == |
| + AccumulatorUse::kWrite && |
| + Bytecodes::IsAccumulatorLoadWithoutEffects(last_.bytecode())) { |
| + // The current instruction clobbers the accumulator without reading it. The |
| + // load in the last instruction can be elided as it has no effect. |
| + return true; |
| } else { |
| return false; |
| } |
| } |
| BytecodeNode* BytecodePeepholeOptimizer::Optimize(BytecodeNode* current) { |
| - UpdateLastAndCurrentBytecodes(current); |
| + TryToRemoveLastExpressionPosition(current); |
| + |
| + if (UpdateLastAndCurrentBytecodes(current)) { |
| + return current; |
| + } |
| + |
| if (CanElideCurrent(current)) { |
| if (current->source_info().is_valid()) { |
| + // Preserve the source information by replacing the current bytecode |
| + // with a no op bytecode. |
| current->set_bytecode(Bytecode::kNop); |
| } else { |
| current = nullptr; |
| } |
| - } else if (CanElideLast(current) && |
| - CanElideLastBasedOnSourcePosition(current)) { |
| + return current; |
| + } |
| + |
| + if (CanElideLast(current) && CanElideLastBasedOnSourcePosition(current)) { |
| current->source_info().Update(last_.source_info()); |
| InvalidateLast(); |
|
rmcilroy
2016/05/24 14:00:11
Please add "return current" here too ensure we don
oth
2016/05/24 15:07:15
Done.
|
| } |