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..dc2d33b2ede6695aaac1719098e362217f595ee4 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())) { |
@@ -169,13 +152,13 @@ bool BytecodePeepholeOptimizer::CanElideLastBasedOnSourcePosition( |
// source position information is applied to the current node |
// updating it if necessary. |
// |
- // |
// The last bytecode can be elided for the MAYBE cases if the last |
// bytecode is known not to throw. If it throws, the system would |
// not have correct stack trace information. The appropriate check |
- // for this would be Bytecodes::IsWithoutExternalSideEffects(), which |
- // is checked in BytecodePeepholeOptimizer::UpdateLastAndCurrentBytecodes() |
- // to keep the check here simple. |
+ // for this would be Bytecodes::IsWithoutExternalSideEffects(), |
+ // which is checked in |
+ // BytecodePeepholeOptimizer::TransformLastAndCurrentBytecodes() to |
+ // keep the check here simple. |
// |
// In rare cases, bytecode generation produces consecutive bytecodes |
// with the same expression positions. In principle, the latter of |
@@ -185,6 +168,96 @@ bool BytecodePeepholeOptimizer::CanElideLastBasedOnSourcePosition( |
!current->source_info().is_valid()); |
} |
+namespace { |
+ |
+void TransformLdaStarToLdrLdar(Bytecode new_bytecode, BytecodeNode* const last, |
+ BytecodeNode* const current) { |
+ DCHECK_EQ(current->bytecode(), Bytecode::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 |
+ |
+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::TransformLastAndCurrentBytecodes( |
+ BytecodeNode* const current) { |
+ return RemoveToBooleanFromJump(current) || |
+ RemoveToBooleanFromLogicalNot(current) || ChangeLdaToLdr(current); |
+} |
+ |
bool BytecodePeepholeOptimizer::CanElideLast( |
const BytecodeNode* const current) const { |
if (!last_is_discardable_) { |
@@ -200,24 +273,41 @@ 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 (TransformLastAndCurrentBytecodes(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(); |
+ return current; |
} |
+ |
return current; |
} |