普通文本  |  316行  |  10.39 KB

// 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.

#include "src/interpreter/bytecode-peephole-optimizer.h"

#include "src/objects-inl.h"
#include "src/objects.h"

namespace v8 {
namespace internal {
namespace interpreter {

BytecodePeepholeOptimizer::BytecodePeepholeOptimizer(
    BytecodePipelineStage* next_stage)
    : next_stage_(next_stage), last_(Bytecode::kIllegal, BytecodeSourceInfo()) {
  InvalidateLast();
}

// override
Handle<BytecodeArray> BytecodePeepholeOptimizer::ToBytecodeArray(
    Isolate* isolate, int register_count, int parameter_count,
    Handle<FixedArray> handler_table) {
  Flush();
  return next_stage_->ToBytecodeArray(isolate, register_count, parameter_count,
                                      handler_table);
}

// 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);
}

// override
void BytecodePeepholeOptimizer::WriteJump(BytecodeNode* node,
                                          BytecodeLabel* label) {
  // Handlers for jump bytecodes do not emit |node| as WriteJump()
  // requires the |label| and having a label argument in all action
  // handlers results in dead work in the non-jump case.
  ApplyPeepholeAction(node);
  next_stage()->WriteJump(node, label);
}

// override
void BytecodePeepholeOptimizer::Write(BytecodeNode* node) {
  // Handlers for non-jump bytecodes run to completion emitting
  // bytecode to next stage as appropriate.
  ApplyPeepholeAction(node);
}

void BytecodePeepholeOptimizer::Flush() {
  if (LastIsValid()) {
    next_stage_->Write(&last_);
    InvalidateLast();
  }
}

void BytecodePeepholeOptimizer::InvalidateLast() {
  last_.set_bytecode(Bytecode::kIllegal);
}

bool BytecodePeepholeOptimizer::LastIsValid() const {
  return last_.bytecode() != Bytecode::kIllegal;
}

void BytecodePeepholeOptimizer::SetLast(const BytecodeNode* const node) {
  // An action shouldn't leave a NOP as last bytecode unless it has
  // source position information. NOP without source information can
  // always be elided.
  DCHECK(node->bytecode() != Bytecode::kNop || node->source_info().is_valid());
  last_ = *node;
}

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 could 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(). By
  // default, the upstream bytecode generator filters out unneeded
  // expression position information so there is neglible benefit to
  // handling MAYBE specially. Hence MAYBE is treated the same as NO.
  //
  return (!last_.source_info().is_valid() ||
          !current->source_info().is_valid());
}

namespace {

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),
                        current->operand(1));
  if (last->source_info().is_valid()) {
    current->set_source_info(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),
                        current->operand(1));
  if (last->source_info().is_valid()) {
    current->set_source_info(last->source_info());
  }
}

}  // namespace

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()));

  SetLast(node);
}

void BytecodePeepholeOptimizer::UpdateLastIfSourceInfoPresentAction(
    BytecodeNode* const node, const PeepholeActionAndData* action_data) {
  DCHECK(!LastIsValid());
  DCHECK(!Bytecodes::IsJump(node->bytecode()));

  if (node->source_info().is_valid()) {
    SetLast(node);
  }
}

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.
  }
}

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);
  }
}

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 have a valid source position if the source
      // position of last() is valid (per rules in
      // CanElideLastBasedOnSourcePosition()).
      node->set_source_info(last()->source_info());
    }
    SetLast(node);
  } else {
    DefaultAction(node);
  }
}

void BytecodePeepholeOptimizer::ChangeBytecodeAction(
    BytecodeNode* const node, const PeepholeActionAndData* action_data) {
  DCHECK(LastIsValid());
  DCHECK(!Bytecodes::IsJump(node->bytecode()));

  node->replace_bytecode(action_data->bytecode);
  DefaultAction(node);
}

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);
    SetLast(node);
  } else {
    DefaultAction(node);
  }
}

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);
    SetLast(node);
  } else {
    DefaultAction(node);
  }
}

void BytecodePeepholeOptimizer::DefaultJumpAction(
    BytecodeNode* const node, const PeepholeActionAndData* action_data) {
  DCHECK(LastIsValid());
  DCHECK(Bytecodes::IsJump(node->bytecode()));

  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 (!CanElideLastBasedOnSourcePosition(node)) {
    next_stage()->Write(last());
  } else if (!node->source_info().is_valid()) {
    node->set_source_info(last()->source_info());
  }
  InvalidateLast();
}

void BytecodePeepholeOptimizer::ApplyPeepholeAction(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;
  }
}

}  // namespace interpreter
}  // namespace internal
}  // namespace v8