Merge V8 5.3.332.45.  DO NOT MERGE

Test: Manual

FPIIM-449

Change-Id: Id3254828b068abdea3cb10442e0172a8c9a98e03
(cherry picked from commit 13e2dadd00298019ed862f2b2fc5068bba730bcf)
diff --git a/src/interpreter/bytecode-peephole-optimizer.cc b/src/interpreter/bytecode-peephole-optimizer.cc
index 803fc23..1108d83 100644
--- a/src/interpreter/bytecode-peephole-optimizer.cc
+++ b/src/interpreter/bytecode-peephole-optimizer.cc
@@ -15,12 +15,57 @@
 BytecodePeepholeOptimizer::BytecodePeepholeOptimizer(
     ConstantArrayBuilder* constant_array_builder,
     BytecodePipelineStage* next_stage)
-    : constant_array_builder_(constant_array_builder),
-      next_stage_(next_stage),
-      last_is_discardable_(false) {
+    : constant_array_builder_(constant_array_builder), next_stage_(next_stage) {
   InvalidateLast();
 }
 
+// override
+Handle<BytecodeArray> BytecodePeepholeOptimizer::ToBytecodeArray(
+    int fixed_register_count, int parameter_count,
+    Handle<FixedArray> handler_table) {
+  Flush();
+  return next_stage_->ToBytecodeArray(fixed_register_count, parameter_count,
+                                      handler_table);
+}
+
+// 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);
+}
+
+// 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.
+  next_stage_->BindLabel(target, label);
+}
+
+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();
+  }
+}
+
 void BytecodePeepholeOptimizer::InvalidateLast() {
   last_.set_bytecode(Bytecode::kIllegal);
 }
@@ -31,51 +76,6 @@
 
 void BytecodePeepholeOptimizer::SetLast(const BytecodeNode* const node) {
   last_.Clone(node);
-  last_is_discardable_ = true;
-}
-
-// override
-size_t BytecodePeepholeOptimizer::FlushForOffset() {
-  size_t buffered_size = next_stage_->FlushForOffset();
-  if (LastIsValid()) {
-    if (last_.bytecode() == Bytecode::kNop &&
-        !last_.source_info().is_statement()) {
-      // The Nop can be dropped as it doesn't have a statement
-      // position for the debugger and doesn't have any effects by
-      // definition.
-      InvalidateLast();
-    } else {
-      buffered_size += last_.Size();
-      last_is_discardable_ = false;
-    }
-  }
-  return buffered_size;
-}
-
-// override
-void BytecodePeepholeOptimizer::FlushBasicBlock() {
-  if (LastIsValid()) {
-    next_stage_->Write(&last_);
-    InvalidateLast();
-  }
-  next_stage_->FlushBasicBlock();
-}
-
-// override
-void BytecodePeepholeOptimizer::Write(BytecodeNode* node) {
-  // Attempt optimization if there is an earlier node to optimize with.
-  if (LastIsValid()) {
-    node = Optimize(node);
-    // Only output the last node if it wasn't invalidated by the optimization.
-    if (LastIsValid()) {
-      next_stage_->Write(&last_);
-      InvalidateLast();
-    }
-  }
-
-  if (node != nullptr) {
-    SetLast(node);
-  }
 }
 
 Handle<Object> BytecodePeepholeOptimizer::GetConstantForIndexOperand(
@@ -94,22 +94,18 @@
            GetConstantForIndexOperand(&last_, 0)->IsName()));
 }
 
-void BytecodePeepholeOptimizer::UpdateCurrentBytecode(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_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();
   }
 }
 
@@ -134,15 +130,135 @@
   }
 }
 
+bool BytecodePeepholeOptimizer::CanElideLastBasedOnSourcePosition(
+    const BytecodeNode* const current) const {
+  //
+  // The rules for allowing the elision of the last bytecode based
+  // on source position are:
+  //
+  //                     C U R R E N T
+  //              +--------+--------+--------+
+  //              |  None  |  Expr  |  Stmt  |
+  //  L  +--------+--------+--------+--------+
+  //     |  None  |  YES   |  YES   |  YES   |
+  //  A  +--------+--------+--------+--------+
+  //     |  Expr  |  YES   | MAYBE  |  MAYBE |
+  //  S  +--------+--------+--------+--------+
+  //     |  Stmt  |  YES   |   NO   |   NO   |
+  //  T  +--------+--------+--------+--------+
+  //
+  // The goal is not lose any statement positions and not lose useful
+  // expression positions. Whenever the last bytecode is elided it's
+  // 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::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
+  // these can be elided, but would make this function more expensive.
+  //
+  return (!last_.source_info().is_valid() ||
+          !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->set_bytecode(Bytecode::kLdar, current->operand(0));
+}
+
+}  // 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;
+    }
+  }
+  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));
+  }
+  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::TransformCurrentBytecode(
+    BytecodeNode* const current) {
+  return RemoveToBooleanFromJump(current) ||
+         RemoveToBooleanFromLogicalNot(current);
+}
+
 bool BytecodePeepholeOptimizer::CanElideLast(
     const BytecodeNode* const current) const {
-  if (!last_is_discardable_) {
-    return false;
-  }
-
   if (last_.bytecode() == Bytecode::kNop) {
-    // Nop are placeholders for holding source position information
-    // and can be elided.
+    // Nop are placeholders for holding source position information.
     return true;
   } else if (Bytecodes::IsAccumulatorLoadWithoutEffects(current->bytecode()) &&
              Bytecodes::IsAccumulatorLoadWithoutEffects(last_.bytecode())) {
@@ -150,25 +266,58 @@
     // 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) {
-  UpdateCurrentBytecode(current);
+  TryToRemoveLastExpressionPosition(current);
+
+  if (TransformCurrentBytecode(current) ||
+      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)) {
+    return current;
+  }
+
+  if (CanElideLast(current) && CanElideLastBasedOnSourcePosition(current)) {
     if (last_.source_info().is_valid()) {
-      current->source_info().Update(last_.source_info());
+      // Current can not be valid per CanElideLastBasedOnSourcePosition().
+      current->source_info().Clone(last_.source_info());
     }
     InvalidateLast();
+    return current;
+  }
+
+  return current;
+}
+
+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();
+    }
   }
   return current;
 }