| Index: src/interpreter/bytecode-peephole-optimizer.cc | 
| diff --git a/src/interpreter/bytecode-peephole-optimizer.cc b/src/interpreter/bytecode-peephole-optimizer.cc | 
| index cd668d4a67fa1d551a81605e7941b929e17a77af..36c252a3d7c229cb9c02832d4b5f59b4f3811a06 100644 | 
| --- a/src/interpreter/bytecode-peephole-optimizer.cc | 
| +++ b/src/interpreter/bytecode-peephole-optimizer.cc | 
| @@ -1,4 +1,4 @@ | 
| -// Copyright 2015 the V8 project authors. All rights reserved. | 
| +// Copyright 2016 the V8 project authors. All rights reserved. | 
| // Use of this source code is governed by a BSD-style license that can be | 
| // found in the LICENSE file. | 
|  | 
| @@ -29,21 +29,6 @@ Handle<BytecodeArray> BytecodePeepholeOptimizer::ToBytecodeArray( | 
| } | 
|  | 
| // override | 
| -void BytecodePeepholeOptimizer::Write(BytecodeNode* node) { | 
| -  node = OptimizeAndEmitLast(node); | 
| -  if (node != nullptr) { | 
| -    SetLast(node); | 
| -  } | 
| -} | 
| - | 
| -// override | 
| -void BytecodePeepholeOptimizer::WriteJump(BytecodeNode* node, | 
| -                                          BytecodeLabel* label) { | 
| -  node = OptimizeAndEmitLast(node); | 
| -  next_stage_->WriteJump(node, label); | 
| -} | 
| - | 
| -// override | 
| void BytecodePeepholeOptimizer::BindLabel(BytecodeLabel* label) { | 
| Flush(); | 
| next_stage_->BindLabel(label); | 
| @@ -52,14 +37,22 @@ void BytecodePeepholeOptimizer::BindLabel(BytecodeLabel* label) { | 
| // override | 
| void BytecodePeepholeOptimizer::BindLabel(const BytecodeLabel& target, | 
| BytecodeLabel* label) { | 
| -  // There is no need to flush here, it will have been flushed when |target| | 
| -  // was bound. | 
| +  // There is no need to flush here, it will have been flushed when | 
| +  // |target| was bound. | 
| next_stage_->BindLabel(target, label); | 
| } | 
|  | 
| +// override | 
| +void BytecodePeepholeOptimizer::WriteJump(BytecodeNode* node, | 
| +                                          BytecodeLabel* label) { | 
| +  Optimize(node); | 
| +  next_stage()->WriteJump(node, label); | 
| +} | 
| + | 
| +// override | 
| +void BytecodePeepholeOptimizer::Write(BytecodeNode* node) { Optimize(node); } | 
| + | 
| void BytecodePeepholeOptimizer::Flush() { | 
| -  // TODO(oth/rmcilroy): We could check CanElideLast() here to potentially | 
| -  // eliminate last rather than writing it. | 
| if (LastIsValid()) { | 
| next_stage_->Write(&last_); | 
| InvalidateLast(); | 
| @@ -86,51 +79,6 @@ Handle<Object> BytecodePeepholeOptimizer::GetConstantForIndexOperand( | 
| return constant_array_builder_->At(index_operand); | 
| } | 
|  | 
| -bool BytecodePeepholeOptimizer::LastBytecodePutsNameInAccumulator() const { | 
| -  DCHECK(LastIsValid()); | 
| -  return (last_.bytecode() == Bytecode::kTypeOf || | 
| -          last_.bytecode() == Bytecode::kToName || | 
| -          (last_.bytecode() == Bytecode::kLdaConstant && | 
| -           GetConstantForIndexOperand(&last_, 0)->IsName())); | 
| -} | 
| - | 
| -void BytecodePeepholeOptimizer::TryToRemoveLastExpressionPosition( | 
| -    const BytecodeNode* const current) { | 
| -  if (current->source_info().is_valid() && | 
| -      last_.source_info().is_expression() && | 
| -      Bytecodes::IsWithoutExternalSideEffects(last_.bytecode())) { | 
| -    // The last bytecode has been marked as expression. It has no | 
| -    // external effects so can't throw and the current bytecode is a | 
| -    // source position. Remove the expression position on the last | 
| -    // bytecode to open up potential peephole optimizations and to | 
| -    // save the memory and perf cost of storing the unneeded | 
| -    // expression position. | 
| -    last_.source_info().set_invalid(); | 
| -  } | 
| -} | 
| - | 
| -bool BytecodePeepholeOptimizer::CanElideCurrent( | 
| -    const BytecodeNode* const current) const { | 
| -  if (Bytecodes::IsLdarOrStar(last_.bytecode()) && | 
| -      Bytecodes::IsLdarOrStar(current->bytecode()) && | 
| -      current->operand(0) == last_.operand(0)) { | 
| -    // Ldar and Star make the accumulator and register hold equivalent | 
| -    // values. Only the first bytecode is needed if there's a sequence | 
| -    // of back-to-back Ldar and Star bytecodes with the same operand. | 
| -    return true; | 
| -  } else if (current->bytecode() == Bytecode::kToName && | 
| -             LastBytecodePutsNameInAccumulator()) { | 
| -    // If the previous bytecode ensured a name was in the accumulator, | 
| -    // the type coercion ToName() can be elided. | 
| -    return true; | 
| -  } else { | 
| -    // Additional candidates for eliding current: | 
| -    // (i) current is Nop. | 
| -    // (ii) ToNumber if the last puts a number in the accumulator. | 
| -    return false; | 
| -  } | 
| -} | 
| - | 
| bool BytecodePeepholeOptimizer::CanElideLastBasedOnSourcePosition( | 
| const BytecodeNode* const current) const { | 
| // | 
| @@ -156,10 +104,7 @@ bool BytecodePeepholeOptimizer::CanElideLastBasedOnSourcePosition( | 
| // 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::TransformLastAndCurrentBytecodes() to | 
| -  // keep the check here simple. | 
| +  // for this would be Bytecodes::IsWithoutExternalSideEffects(). | 
| // | 
| // In rare cases, bytecode generation produces consecutive bytecodes | 
| // with the same expression positions. In principle, the latter of | 
| @@ -189,13 +134,21 @@ void TransformLdaStarToLdrLdar(Bytecode new_bytecode, BytecodeNode* const last, | 
| current->set_bytecode(Bytecode::kLdar, current->operand(0)); | 
| } | 
|  | 
| -void TransformToBinaryOpWithSmiOnRhs(Bytecode new_bytecode, | 
| -                                     BytecodeNode* const last, | 
| -                                     BytecodeNode* const current) { | 
| -  DCHECK(Bytecodes::IsLdaSmiOrLdaZero(last->bytecode())); | 
| -  uint32_t imm_operand = | 
| -      last->bytecode() == Bytecode::kLdaSmi ? last->operand(0) : 0; | 
| -  current->set_bytecode(new_bytecode, imm_operand, current->operand(0)); | 
| +void TransformLdaSmiBinaryOpToBinaryOpWithSmi(Bytecode new_bytecode, | 
| +                                              BytecodeNode* const last, | 
| +                                              BytecodeNode* const current) { | 
| +  DCHECK_EQ(last->bytecode(), Bytecode::kLdaSmi); | 
| +  current->set_bytecode(new_bytecode, last->operand(0), current->operand(0)); | 
| +  if (last->source_info().is_valid()) { | 
| +    current->source_info().Clone(last->source_info()); | 
| +  } | 
| +} | 
| + | 
| +void TransformLdaZeroBinaryOpToBinaryOpWithZero(Bytecode new_bytecode, | 
| +                                                BytecodeNode* const last, | 
| +                                                BytecodeNode* const current) { | 
| +  DCHECK_EQ(last->bytecode(), Bytecode::kLdaZero); | 
| +  current->set_bytecode(new_bytecode, 0, current->operand(0)); | 
| if (last->source_info().is_valid()) { | 
| current->source_info().Clone(last->source_info()); | 
| } | 
| @@ -203,171 +156,191 @@ void TransformToBinaryOpWithSmiOnRhs(Bytecode new_bytecode, | 
|  | 
| }  // namespace | 
|  | 
| -bool BytecodePeepholeOptimizer::TransformLastAndCurrentBytecodes( | 
| -    BytecodeNode* const current) { | 
| -  if (current->bytecode() == Bytecode::kStar && | 
| -      !current->source_info().is_statement()) { | 
| -    // Note: If the Star is tagged with a statement position, we can't | 
| -    // perform this transform as the store to the register will | 
| -    // have the wrong ordering for stepping in the debugger. | 
| -    switch (last_.bytecode()) { | 
| -      case Bytecode::kLdaNamedProperty: | 
| -        TransformLdaStarToLdrLdar(Bytecode::kLdrNamedProperty, &last_, current); | 
| -        return true; | 
| -      case Bytecode::kLdaKeyedProperty: | 
| -        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; | 
| -    } | 
| -  } else if (Bytecodes::IsLdaSmiOrLdaZero(last_.bytecode()) && | 
| -             (!last_.source_info().is_valid() || | 
| -              !current->source_info().is_valid())) { | 
| -    switch (current->bytecode()) { | 
| -      case Bytecode::kAdd: | 
| -        TransformToBinaryOpWithSmiOnRhs(Bytecode::kAddSmi, &last_, current); | 
| -        InvalidateLast(); | 
| -        return true; | 
| -      case Bytecode::kSub: | 
| -        TransformToBinaryOpWithSmiOnRhs(Bytecode::kSubSmi, &last_, current); | 
| -        InvalidateLast(); | 
| -        return true; | 
| -      case Bytecode::kBitwiseOr: | 
| -        TransformToBinaryOpWithSmiOnRhs(Bytecode::kBitwiseOrSmi, &last_, | 
| -                                        current); | 
| -        InvalidateLast(); | 
| -        return true; | 
| -      case Bytecode::kBitwiseAnd: | 
| -        TransformToBinaryOpWithSmiOnRhs(Bytecode::kBitwiseAndSmi, &last_, | 
| -                                        current); | 
| -        InvalidateLast(); | 
| -        return true; | 
| -      case Bytecode::kShiftLeft: | 
| -        TransformToBinaryOpWithSmiOnRhs(Bytecode::kShiftLeftSmi, &last_, | 
| -                                        current); | 
| -        InvalidateLast(); | 
| -        return true; | 
| -      case Bytecode::kShiftRight: | 
| -        TransformToBinaryOpWithSmiOnRhs(Bytecode::kShiftRightSmi, &last_, | 
| -                                        current); | 
| -        InvalidateLast(); | 
| -        return true; | 
| -      default: | 
| -        break; | 
| -    } | 
| -  } | 
| +void BytecodePeepholeOptimizer::DefaultAction( | 
| +    BytecodeNode* const node, const PeepholeActionAndData* action_data) { | 
| +  DCHECK(LastIsValid()); | 
| +  DCHECK(!Bytecodes::IsJump(node->bytecode())); | 
| + | 
| +  next_stage()->Write(last()); | 
| +  SetLast(node); | 
| +} | 
| + | 
| +void BytecodePeepholeOptimizer::UpdateLastAction( | 
| +    BytecodeNode* const node, const PeepholeActionAndData* action_data) { | 
| +  DCHECK(!LastIsValid()); | 
| +  DCHECK(!Bytecodes::IsJump(node->bytecode())); | 
|  | 
| -  return false; | 
| +  SetLast(node); | 
| } | 
|  | 
| -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)); | 
| +void BytecodePeepholeOptimizer::ElideCurrentAction( | 
| +    BytecodeNode* const node, const PeepholeActionAndData* action_data) { | 
| +  DCHECK(LastIsValid()); | 
| +  DCHECK(!Bytecodes::IsJump(node->bytecode())); | 
| + | 
| +  if (node->source_info().is_valid()) { | 
| +    // Preserve the source information by replacing the node bytecode | 
| +    // with a no op bytecode. | 
| +    node->set_bytecode(Bytecode::kNop); | 
| +    DefaultAction(node); | 
| +  } else { | 
| +    // Nothing to do, keep last and wait for next bytecode to pair with it. | 
| } | 
| -  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); | 
| +void BytecodePeepholeOptimizer::ElideCurrentIfOperand0MatchesAction( | 
| +    BytecodeNode* const node, const PeepholeActionAndData* action_data) { | 
| +  DCHECK(LastIsValid()); | 
| +  DCHECK(!Bytecodes::IsJump(node->bytecode())); | 
| + | 
| +  if (last()->operand(0) == node->operand(0)) { | 
| +    ElideCurrentAction(node); | 
| +  } else { | 
| +    DefaultAction(node); | 
| } | 
| -  return can_remove; | 
| } | 
|  | 
| -bool BytecodePeepholeOptimizer::TransformCurrentBytecode( | 
| -    BytecodeNode* const current) { | 
| -  return RemoveToBooleanFromJump(current) || | 
| -         RemoveToBooleanFromLogicalNot(current); | 
| +void BytecodePeepholeOptimizer::ElideCurrentIfLoadingNameConstantAction( | 
| +    BytecodeNode* const node, const PeepholeActionAndData* action_data) { | 
| +  DCHECK_EQ(last()->bytecode(), Bytecode::kLdaConstant); | 
| +  DCHECK(!Bytecodes::IsJump(node->bytecode())); | 
| + | 
| +  if (GetConstantForIndexOperand(last(), 0)->IsName()) { | 
| +    ElideCurrentAction(node); | 
| +  } else { | 
| +    DefaultAction(node); | 
| +  } | 
| } | 
|  | 
| -bool BytecodePeepholeOptimizer::CanElideLast( | 
| -    const BytecodeNode* const current) const { | 
| -  if (last_.bytecode() == Bytecode::kNop) { | 
| -    // Nop are placeholders for holding source position information. | 
| -    return true; | 
| -  } else if (Bytecodes::IsAccumulatorLoadWithoutEffects(current->bytecode()) && | 
| -             Bytecodes::IsAccumulatorLoadWithoutEffects(last_.bytecode())) { | 
| -    // The accumulator is invisible to the debugger. If there is a sequence of | 
| -    // 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; | 
| +void BytecodePeepholeOptimizer::ElideLastAction( | 
| +    BytecodeNode* const node, const PeepholeActionAndData* action_data) { | 
| +  DCHECK(LastIsValid()); | 
| +  DCHECK(!Bytecodes::IsJump(node->bytecode())); | 
| + | 
| +  if (CanElideLastBasedOnSourcePosition(node)) { | 
| +    if (last()->source_info().is_valid()) { | 
| +      // Node can not be valid per CanElideLastBasedOnSourcePosition(). | 
| +      node->source_info().Clone(last()->source_info()); | 
| +    } | 
| +    SetLast(node); | 
| } else { | 
| -    return false; | 
| +    DefaultAction(node); | 
| } | 
| } | 
|  | 
| -BytecodeNode* BytecodePeepholeOptimizer::Optimize(BytecodeNode* current) { | 
| -  TryToRemoveLastExpressionPosition(current); | 
| -  if (TransformCurrentBytecode(current) || | 
| -      TransformLastAndCurrentBytecodes(current)) { | 
| -    return current; | 
| +void BytecodePeepholeOptimizer::ChangeBytecodeAction( | 
| +    BytecodeNode* const node, const PeepholeActionAndData* action_data) { | 
| +  DCHECK(LastIsValid()); | 
| +  DCHECK(!Bytecodes::IsJump(node->bytecode())); | 
| + | 
| +  node->set_bytecode(action_data->bytecode); | 
| +  DefaultAction(node); | 
| +} | 
| + | 
| +void BytecodePeepholeOptimizer::TransformLdaStarToLdrLdarAction( | 
| +    BytecodeNode* const node, const PeepholeActionAndData* action_data) { | 
| +  DCHECK(LastIsValid()); | 
| +  DCHECK(!Bytecodes::IsJump(node->bytecode())); | 
| + | 
| +  if (!node->source_info().is_statement()) { | 
| +    TransformLdaStarToLdrLdar(action_data->bytecode, last(), node); | 
| } | 
| +  DefaultAction(node); | 
| +} | 
|  | 
| -  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; | 
| -    } | 
| -    return current; | 
| +void BytecodePeepholeOptimizer::TransformLdaSmiBinaryOpToBinaryOpWithSmiAction( | 
| +    BytecodeNode* const node, const PeepholeActionAndData* action_data) { | 
| +  DCHECK(LastIsValid()); | 
| +  DCHECK(!Bytecodes::IsJump(node->bytecode())); | 
| + | 
| +  if (!node->source_info().is_valid() || !last()->source_info().is_valid()) { | 
| +    // Fused last and current into current. | 
| +    TransformLdaSmiBinaryOpToBinaryOpWithSmi(action_data->bytecode, last(), | 
| +                                             node); | 
| +    // Discard last. | 
| +    InvalidateLast(); | 
| +    // Immediately emit current node as nothing fuses with AddSmi, SubSmi, etc. | 
| +    next_stage()->Write(node); | 
| +  } else { | 
| +    DefaultAction(node); | 
| } | 
| +} | 
|  | 
| -  if (CanElideLast(current) && CanElideLastBasedOnSourcePosition(current)) { | 
| -    if (last_.source_info().is_valid()) { | 
| -      // Current can not be valid per CanElideLastBasedOnSourcePosition(). | 
| -      current->source_info().Clone(last_.source_info()); | 
| -    } | 
| +void BytecodePeepholeOptimizer:: | 
| +    TransformLdaZeroBinaryOpToBinaryOpWithZeroAction( | 
| +        BytecodeNode* const node, const PeepholeActionAndData* action_data) { | 
| +  DCHECK(LastIsValid()); | 
| +  DCHECK(!Bytecodes::IsJump(node->bytecode())); | 
| +  if (!node->source_info().is_valid() || !last()->source_info().is_valid()) { | 
| +    // Fused last and current into current. | 
| +    TransformLdaZeroBinaryOpToBinaryOpWithZero(action_data->bytecode, last(), | 
| +                                               node); | 
| +    // Discard last. | 
| InvalidateLast(); | 
| -    return current; | 
| +    // Immediately emit current node as nothing fuses with AddSmi, SubSmi, etc. | 
| +    next_stage()->Write(node); | 
| +  } else { | 
| +    DefaultAction(node); | 
| } | 
| +} | 
|  | 
| -  return current; | 
| +void BytecodePeepholeOptimizer::DefaultJumpAction( | 
| +    BytecodeNode* const node, const PeepholeActionAndData* action_data) { | 
| +  DCHECK(LastIsValid()); | 
| +  DCHECK(Bytecodes::IsJump(node->bytecode())); | 
| + | 
| +  next_stage()->Write(last()); | 
| +  InvalidateLast(); | 
| } | 
|  | 
| -BytecodeNode* BytecodePeepholeOptimizer::OptimizeAndEmitLast( | 
| -    BytecodeNode* current) { | 
| -  // Attempt optimization if there is an earlier node to optimize with. | 
| -  if (LastIsValid()) { | 
| -    current = Optimize(current); | 
| -    // Only output the last node if it wasn't invalidated by the optimization. | 
| -    if (LastIsValid()) { | 
| -      next_stage_->Write(&last_); | 
| -      InvalidateLast(); | 
| -    } | 
| +void BytecodePeepholeOptimizer::UpdateLastJumpAction( | 
| +    BytecodeNode* const node, const PeepholeActionAndData* action_data) { | 
| +  DCHECK(!LastIsValid()); | 
| +  DCHECK(Bytecodes::IsJump(node->bytecode())); | 
| +} | 
| + | 
| +void BytecodePeepholeOptimizer::ChangeJumpBytecodeAction( | 
| +    BytecodeNode* const node, const PeepholeActionAndData* action_data) { | 
| +  DCHECK(LastIsValid()); | 
| +  DCHECK(Bytecodes::IsJump(node->bytecode())); | 
| + | 
| +  next_stage()->Write(last()); | 
| +  InvalidateLast(); | 
| +  node->set_bytecode(action_data->bytecode, node->operand(0)); | 
| +} | 
| + | 
| +void BytecodePeepholeOptimizer::ElideLastBeforeJumpAction( | 
| +    BytecodeNode* const node, const PeepholeActionAndData* action_data) { | 
| +  DCHECK(LastIsValid()); | 
| +  DCHECK(Bytecodes::IsJump(node->bytecode())); | 
| + | 
| +  if (!node->source_info().is_valid()) { | 
| +    node->source_info().Clone(last()->source_info()); | 
| +  } else { | 
| +    next_stage()->Write(last()); | 
| +  } | 
| +  InvalidateLast(); | 
| +} | 
| + | 
| +void BytecodePeepholeOptimizer::Optimize(BytecodeNode* const node) { | 
| +  // A single table is used for looking up peephole optimization | 
| +  // matches as it is observed to have better performance. This is | 
| +  // inspite of the fact that jump bytecodes and non-jump bytecodes | 
| +  // have different processing logic, in particular a jump bytecode | 
| +  // always needs to emit the jump via WriteJump(). | 
| +  const PeepholeActionAndData* const action_data = | 
| +      PeepholeActionTable::Lookup(last()->bytecode(), node->bytecode()); | 
| +  switch (action_data->action) { | 
| +#define CASE(Action)              \ | 
| +  case PeepholeAction::k##Action: \ | 
| +    Action(node, action_data);    \ | 
| +    break; | 
| +    PEEPHOLE_ACTION_LIST(CASE) | 
| +#undef CASE | 
| +    default: | 
| +      UNREACHABLE(); | 
| +      break; | 
| } | 
| -  return current; | 
| } | 
|  | 
| }  // namespace interpreter | 
|  |