普通文本  |  631行  |  21.13 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-register-optimizer.h"

namespace v8 {
namespace internal {
namespace interpreter {

const uint32_t BytecodeRegisterOptimizer::kInvalidEquivalenceId;

// A class for tracking the state of a register. This class tracks
// which equivalence set a register is a member of and also whether a
// register is materialized in the bytecode stream.
class BytecodeRegisterOptimizer::RegisterInfo final : public ZoneObject {
 public:
  RegisterInfo(Register reg, uint32_t equivalence_id, bool materialized)
      : register_(reg),
        equivalence_id_(equivalence_id),
        materialized_(materialized),
        next_(this),
        prev_(this) {}

  void AddToEquivalenceSetOf(RegisterInfo* info);
  void MoveToNewEquivalenceSet(uint32_t equivalence_id, bool materialized);
  bool IsOnlyMemberOfEquivalenceSet() const;
  bool IsOnlyMaterializedMemberOfEquivalenceSet() const;
  bool IsInSameEquivalenceSet(RegisterInfo* info) const;

  // Get a member of this register's equivalence set that is
  // materialized. The materialized equivalent will be this register
  // if it is materialized. Returns nullptr if no materialized
  // equivalent exists.
  RegisterInfo* GetMaterializedEquivalent();

  // Get a member of this register's equivalence set that is
  // materialized and not register |reg|. The materialized equivalent
  // will be this register if it is materialized. Returns nullptr if
  // no materialized equivalent exists.
  RegisterInfo* GetMaterializedEquivalentOtherThan(Register reg);

  // Get a member of this register's equivalence set that is intended
  // to be materialized in place of this register (which is currently
  // materialized). The best candidate is deemed to be the register
  // with the lowest index as this permits temporary registers to be
  // removed from the bytecode stream. Returns nullptr if no candidate
  // exists.
  RegisterInfo* GetEquivalentToMaterialize();

  // Get an equivalent register. Returns this if none exists.
  RegisterInfo* GetEquivalent();

  Register register_value() const { return register_; }
  bool materialized() const { return materialized_; }
  void set_materialized(bool materialized) { materialized_ = materialized; }
  void set_equivalence_id(uint32_t equivalence_id) {
    equivalence_id_ = equivalence_id;
  }
  uint32_t equivalence_id() const { return equivalence_id_; }

 private:
  Register register_;
  uint32_t equivalence_id_;
  bool materialized_;

  // Equivalence set pointers.
  RegisterInfo* next_;
  RegisterInfo* prev_;

  DISALLOW_COPY_AND_ASSIGN(RegisterInfo);
};

void BytecodeRegisterOptimizer::RegisterInfo::AddToEquivalenceSetOf(
    RegisterInfo* info) {
  DCHECK_NE(kInvalidEquivalenceId, info->equivalence_id());
  // Fix old list
  next_->prev_ = prev_;
  prev_->next_ = next_;
  // Add to new list.
  next_ = info->next_;
  prev_ = info;
  prev_->next_ = this;
  next_->prev_ = this;
  set_equivalence_id(info->equivalence_id());
  set_materialized(false);
}

void BytecodeRegisterOptimizer::RegisterInfo::MoveToNewEquivalenceSet(
    uint32_t equivalence_id, bool materialized) {
  next_->prev_ = prev_;
  prev_->next_ = next_;
  next_ = prev_ = this;
  equivalence_id_ = equivalence_id;
  materialized_ = materialized;
}

bool BytecodeRegisterOptimizer::RegisterInfo::IsOnlyMemberOfEquivalenceSet()
    const {
  return this->next_ == this;
}

bool BytecodeRegisterOptimizer::RegisterInfo::
    IsOnlyMaterializedMemberOfEquivalenceSet() const {
  DCHECK(materialized());

  const RegisterInfo* visitor = this->next_;
  while (visitor != this) {
    if (visitor->materialized()) {
      return false;
    }
    visitor = visitor->next_;
  }
  return true;
}

bool BytecodeRegisterOptimizer::RegisterInfo::IsInSameEquivalenceSet(
    RegisterInfo* info) const {
  return equivalence_id() == info->equivalence_id();
}

BytecodeRegisterOptimizer::RegisterInfo*
BytecodeRegisterOptimizer::RegisterInfo::GetMaterializedEquivalent() {
  RegisterInfo* visitor = this;
  do {
    if (visitor->materialized()) {
      return visitor;
    }
    visitor = visitor->next_;
  } while (visitor != this);

  return nullptr;
}

BytecodeRegisterOptimizer::RegisterInfo*
BytecodeRegisterOptimizer::RegisterInfo::GetMaterializedEquivalentOtherThan(
    Register reg) {
  RegisterInfo* visitor = this;
  do {
    if (visitor->materialized() && visitor->register_value() != reg) {
      return visitor;
    }
    visitor = visitor->next_;
  } while (visitor != this);

  return nullptr;
}

BytecodeRegisterOptimizer::RegisterInfo*
BytecodeRegisterOptimizer::RegisterInfo::GetEquivalentToMaterialize() {
  DCHECK(this->materialized());
  RegisterInfo* visitor = this->next_;
  RegisterInfo* best_info = nullptr;
  while (visitor != this) {
    if (visitor->materialized()) {
      return nullptr;
    }
    if (best_info == nullptr ||
        visitor->register_value() < best_info->register_value()) {
      best_info = visitor;
    }
    visitor = visitor->next_;
  }
  return best_info;
}

BytecodeRegisterOptimizer::RegisterInfo*
BytecodeRegisterOptimizer::RegisterInfo::GetEquivalent() {
  return next_;
}

BytecodeRegisterOptimizer::BytecodeRegisterOptimizer(
    Zone* zone, TemporaryRegisterAllocator* register_allocator,
    int parameter_count, BytecodePipelineStage* next_stage)
    : accumulator_(Register::virtual_accumulator()),
      temporary_base_(register_allocator->allocation_base()),
      register_info_table_(zone),
      equivalence_id_(0),
      next_stage_(next_stage),
      flush_required_(false),
      zone_(zone) {
  register_allocator->set_observer(this);

  // Calculate offset so register index values can be mapped into
  // a vector of register metadata.
  if (parameter_count != 0) {
    register_info_table_offset_ =
        -Register::FromParameterIndex(0, parameter_count).index();
  } else {
    // TODO(oth): This path shouldn't be necessary in bytecode generated
    // from Javascript, but a set of tests do not include the JS receiver.
    register_info_table_offset_ = -accumulator_.index();
  }

  // Initialize register map for parameters, locals, and the
  // accumulator.
  register_info_table_.resize(register_info_table_offset_ +
                              static_cast<size_t>(temporary_base_.index()));
  for (size_t i = 0; i < register_info_table_.size(); ++i) {
    register_info_table_[i] = new (zone) RegisterInfo(
        RegisterFromRegisterInfoTableIndex(i), NextEquivalenceId(), true);
    DCHECK_EQ(register_info_table_[i]->register_value().index(),
              RegisterFromRegisterInfoTableIndex(i).index());
  }
  accumulator_info_ = GetRegisterInfo(accumulator_);
  DCHECK(accumulator_info_->register_value() == accumulator_);
}

// override
Handle<BytecodeArray> BytecodeRegisterOptimizer::ToBytecodeArray(
    int fixed_register_count, int parameter_count,
    Handle<FixedArray> handler_table) {
  FlushState();
  return next_stage_->ToBytecodeArray(fixed_register_count, parameter_count,
                                      handler_table);
}

// override
void BytecodeRegisterOptimizer::Write(BytecodeNode* node) {
  //
  // Transfers with observable registers as the destination will be
  // immediately materialized so the source position information will
  // be ordered correctly.
  //
  // Transfers without observable destination registers will initially
  // be emitted as Nop's with the source position. They may, or may
  // not, be materialized by the optimizer. However, the source
  // position is not lost and being attached to a Nop is fine as the
  // destination register is not observable in the debugger.
  //
  switch (node->bytecode()) {
    case Bytecode::kLdar: {
      DoLdar(node);
      return;
    }
    case Bytecode::kStar: {
      DoStar(node);
      return;
    }
    case Bytecode::kMov: {
      DoMov(node);
      return;
    }
    default:
      break;
  }

  if (Bytecodes::IsJump(node->bytecode()) ||
      node->bytecode() == Bytecode::kDebugger ||
      node->bytecode() == Bytecode::kSuspendGenerator) {
    // All state must be flushed before emitting
    // - a jump (due to how bytecode offsets for jumps are evaluated),
    // - a call to the debugger (as it can manipulate locals and parameters),
    // - a generator suspend (as this involves saving all registers).
    FlushState();
  }

  PrepareOperands(node);
  WriteToNextStage(node);
}

// override
void BytecodeRegisterOptimizer::WriteJump(BytecodeNode* node,
                                          BytecodeLabel* label) {
  FlushState();
  next_stage_->WriteJump(node, label);
}

// override
void BytecodeRegisterOptimizer::BindLabel(BytecodeLabel* label) {
  FlushState();
  next_stage_->BindLabel(label);
}

// override
void BytecodeRegisterOptimizer::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 BytecodeRegisterOptimizer::FlushState() {
  if (!flush_required_) {
    return;
  }

  // Materialize all live registers and break equivalences.
  size_t count = register_info_table_.size();
  for (size_t i = 0; i < count; ++i) {
    RegisterInfo* reg_info = register_info_table_[i];
    if (reg_info->materialized()) {
      // Walk equivalents of materialized registers, materializing
      // each equivalent register as necessary and placing in their
      // own equivalence set.
      RegisterInfo* equivalent;
      while ((equivalent = reg_info->GetEquivalent()) != reg_info) {
        if (!equivalent->materialized()) {
          OutputRegisterTransfer(reg_info, equivalent);
        }
        equivalent->MoveToNewEquivalenceSet(NextEquivalenceId(), true);
      }
    }
  }

  flush_required_ = false;
}

void BytecodeRegisterOptimizer::WriteToNextStage(BytecodeNode* node) const {
  next_stage_->Write(node);
}

void BytecodeRegisterOptimizer::WriteToNextStage(
    BytecodeNode* node, const BytecodeSourceInfo& source_info) const {
  if (source_info.is_valid()) {
    node->source_info().Clone(source_info);
  }
  next_stage_->Write(node);
}

void BytecodeRegisterOptimizer::OutputRegisterTransfer(
    RegisterInfo* input_info, RegisterInfo* output_info,
    const BytecodeSourceInfo& source_info) {
  Register input = input_info->register_value();
  Register output = output_info->register_value();
  DCHECK_NE(input.index(), output.index());

  if (input == accumulator_) {
    uint32_t operand = static_cast<uint32_t>(output.ToOperand());
    BytecodeNode node(Bytecode::kStar, operand);
    WriteToNextStage(&node, source_info);
  } else if (output == accumulator_) {
    uint32_t operand = static_cast<uint32_t>(input.ToOperand());
    BytecodeNode node(Bytecode::kLdar, operand);
    WriteToNextStage(&node, source_info);
  } else {
    uint32_t operand0 = static_cast<uint32_t>(input.ToOperand());
    uint32_t operand1 = static_cast<uint32_t>(output.ToOperand());
    BytecodeNode node(Bytecode::kMov, operand0, operand1);
    WriteToNextStage(&node, source_info);
  }
  output_info->set_materialized(true);
}

void BytecodeRegisterOptimizer::CreateMaterializedEquivalent(
    RegisterInfo* info) {
  DCHECK(info->materialized());
  RegisterInfo* unmaterialized = info->GetEquivalentToMaterialize();
  if (unmaterialized) {
    OutputRegisterTransfer(info, unmaterialized);
  }
}

BytecodeRegisterOptimizer::RegisterInfo*
BytecodeRegisterOptimizer::GetMaterializedEquivalent(RegisterInfo* info) {
  return info->materialized() ? info : info->GetMaterializedEquivalent();
}

BytecodeRegisterOptimizer::RegisterInfo*
BytecodeRegisterOptimizer::GetMaterializedEquivalentNotAccumulator(
    RegisterInfo* info) {
  if (info->materialized()) {
    return info;
  }

  RegisterInfo* result = info->GetMaterializedEquivalentOtherThan(accumulator_);
  if (result == nullptr) {
    Materialize(info);
    result = info;
  }
  DCHECK(result->register_value() != accumulator_);
  return result;
}

void BytecodeRegisterOptimizer::Materialize(RegisterInfo* info) {
  if (!info->materialized()) {
    RegisterInfo* materialized = info->GetMaterializedEquivalent();
    OutputRegisterTransfer(materialized, info);
  }
}

void BytecodeRegisterOptimizer::AddToEquivalenceSet(
    RegisterInfo* set_member, RegisterInfo* non_set_member) {
  non_set_member->AddToEquivalenceSetOf(set_member);
  // Flushing is only required when two or more registers are placed
  // in the same equivalence set.
  flush_required_ = true;
}

void BytecodeRegisterOptimizer::RegisterTransfer(
    RegisterInfo* input_info, RegisterInfo* output_info,
    const BytecodeSourceInfo& source_info) {
  // Materialize an alternate in the equivalence set that
  // |output_info| is leaving.
  if (output_info->materialized()) {
    CreateMaterializedEquivalent(output_info);
  }

  // Add |output_info| to new equivalence set.
  if (!output_info->IsInSameEquivalenceSet(input_info)) {
    AddToEquivalenceSet(input_info, output_info);
  }

  bool output_is_observable =
      RegisterIsObservable(output_info->register_value());
  if (output_is_observable) {
    // Force store to be emitted when register is observable.
    output_info->set_materialized(false);
    RegisterInfo* materialized_info = input_info->GetMaterializedEquivalent();
    OutputRegisterTransfer(materialized_info, output_info, source_info);
  } else if (source_info.is_valid()) {
    // Emit a placeholder nop to maintain source position info.
    EmitNopForSourceInfo(source_info);
  }
}

void BytecodeRegisterOptimizer::EmitNopForSourceInfo(
    const BytecodeSourceInfo& source_info) const {
  DCHECK(source_info.is_valid());
  BytecodeNode nop(Bytecode::kNop);
  nop.source_info().Clone(source_info);
  WriteToNextStage(&nop);
}

void BytecodeRegisterOptimizer::DoLdar(const BytecodeNode* const node) {
  Register input = GetRegisterInputOperand(
      0, node->bytecode(), node->operands(), node->operand_count());
  RegisterInfo* input_info = GetRegisterInfo(input);
  RegisterTransfer(input_info, accumulator_info_, node->source_info());
}

void BytecodeRegisterOptimizer::DoMov(const BytecodeNode* const node) {
  Register input = GetRegisterInputOperand(
      0, node->bytecode(), node->operands(), node->operand_count());
  RegisterInfo* input_info = GetRegisterInfo(input);
  Register output = GetRegisterOutputOperand(
      1, node->bytecode(), node->operands(), node->operand_count());
  RegisterInfo* output_info = GetOrCreateRegisterInfo(output);
  RegisterTransfer(input_info, output_info, node->source_info());
}

void BytecodeRegisterOptimizer::DoStar(const BytecodeNode* const node) {
  Register output = GetRegisterOutputOperand(
      0, node->bytecode(), node->operands(), node->operand_count());
  RegisterInfo* output_info = GetOrCreateRegisterInfo(output);
  RegisterTransfer(accumulator_info_, output_info, node->source_info());
}

void BytecodeRegisterOptimizer::PrepareRegisterOutputOperand(
    RegisterInfo* reg_info) {
  if (reg_info->materialized()) {
    CreateMaterializedEquivalent(reg_info);
  }
  reg_info->MoveToNewEquivalenceSet(NextEquivalenceId(), true);
}

void BytecodeRegisterOptimizer::PrepareRegisterRangeOutputOperand(
    Register start, int count) {
  for (int i = 0; i < count; ++i) {
    Register reg(start.index() + i);
    RegisterInfo* reg_info = GetOrCreateRegisterInfo(reg);
    PrepareRegisterOutputOperand(reg_info);
  }
}

Register BytecodeRegisterOptimizer::GetEquivalentRegisterForInputOperand(
    Register reg) {
  // For a temporary register, RegInfo state may need be created. For
  // locals and parameters, the RegInfo state is created in the
  // BytecodeRegisterOptimizer constructor.
  RegisterInfo* reg_info = GetOrCreateRegisterInfo(reg);
  if (reg_info->materialized()) {
    return reg;
  } else {
    RegisterInfo* equivalent_info =
        GetMaterializedEquivalentNotAccumulator(reg_info);
    return equivalent_info->register_value();
  }
}

void BytecodeRegisterOptimizer::PrepareRegisterInputOperand(
    BytecodeNode* const node, Register reg, int operand_index) {
  Register equivalent = GetEquivalentRegisterForInputOperand(reg);
  node->operands()[operand_index] =
      static_cast<uint32_t>(equivalent.ToOperand());
}

void BytecodeRegisterOptimizer::PrepareRegisterRangeInputOperand(Register start,
                                                                 int count) {
  for (int i = 0; i < count; ++i) {
    Register current(start.index() + i);
    RegisterInfo* input_info = GetRegisterInfo(current);
    Materialize(input_info);
  }
}

void BytecodeRegisterOptimizer::PrepareRegisterOperands(
    BytecodeNode* const node) {
  //
  // For each input operand, get a materialized equivalent if it is
  // just a single register, otherwise materialize register range.
  // Update operand_scale if necessary.
  //
  // For each output register about to be clobbered, materialize an
  // equivalent if it exists. Put each register in it's own equivalence set.
  //
  int register_operand_bitmap =
      Bytecodes::GetRegisterOperandBitmap(node->bytecode());
  const OperandType* operand_types =
      Bytecodes::GetOperandTypes(node->bytecode());
  uint32_t* operands = node->operands();
  for (int i = 0; register_operand_bitmap != 0;
       ++i, register_operand_bitmap >>= 1) {
    if ((register_operand_bitmap & 1) == 0) {
      continue;
    }
    OperandType operand_type = operand_types[i];
    int count = 0;
    if (operand_types[i + 1] == OperandType::kRegCount) {
      count = static_cast<int>(operands[i + 1]);
      if (count == 0) {
        continue;
      }
    } else {
      count = Bytecodes::GetNumberOfRegistersRepresentedBy(operand_type);
    }

    Register reg = Register::FromOperand(static_cast<int32_t>(operands[i]));
    if (Bytecodes::IsRegisterInputOperandType(operand_type)) {
      if (count == 1) {
        PrepareRegisterInputOperand(node, reg, i);
      } else if (count > 1) {
        PrepareRegisterRangeInputOperand(reg, count);
      }
    } else if (Bytecodes::IsRegisterOutputOperandType(operand_type)) {
      PrepareRegisterRangeOutputOperand(reg, count);
    }
  }
}

void BytecodeRegisterOptimizer::PrepareAccumulator(BytecodeNode* const node) {
  // Materialize the accumulator if it is read by the bytecode. The
  // accumulator is special and no other register can be materialized
  // in it's place.
  if (Bytecodes::ReadsAccumulator(node->bytecode()) &&
      !accumulator_info_->materialized()) {
    Materialize(accumulator_info_);
  }

  // Materialize an equivalent to the accumulator if it will be
  // clobbered when the bytecode is dispatched.
  if (Bytecodes::WritesAccumulator(node->bytecode())) {
    PrepareRegisterOutputOperand(accumulator_info_);
  }
}

void BytecodeRegisterOptimizer::PrepareOperands(BytecodeNode* const node) {
  PrepareAccumulator(node);
  PrepareRegisterOperands(node);
}

// static
Register BytecodeRegisterOptimizer::GetRegisterInputOperand(
    int index, Bytecode bytecode, const uint32_t* operands, int operand_count) {
  DCHECK_LT(index, operand_count);
  DCHECK(Bytecodes::IsRegisterInputOperandType(
      Bytecodes::GetOperandType(bytecode, index)));
  return OperandToRegister(operands[index]);
}

// static
Register BytecodeRegisterOptimizer::GetRegisterOutputOperand(
    int index, Bytecode bytecode, const uint32_t* operands, int operand_count) {
  DCHECK_LT(index, operand_count);
  DCHECK(Bytecodes::IsRegisterOutputOperandType(
      Bytecodes::GetOperandType(bytecode, index)));
  return OperandToRegister(operands[index]);
}

BytecodeRegisterOptimizer::RegisterInfo*
BytecodeRegisterOptimizer::GetRegisterInfo(Register reg) {
  size_t index = GetRegisterInfoTableIndex(reg);
  return (index < register_info_table_.size()) ? register_info_table_[index]
                                               : nullptr;
}

BytecodeRegisterOptimizer::RegisterInfo*
BytecodeRegisterOptimizer::GetOrCreateRegisterInfo(Register reg) {
  size_t index = GetRegisterInfoTableIndex(reg);
  return index < register_info_table_.size() ? register_info_table_[index]
                                             : NewRegisterInfo(reg);
}

BytecodeRegisterOptimizer::RegisterInfo*
BytecodeRegisterOptimizer::NewRegisterInfo(Register reg) {
  size_t index = GetRegisterInfoTableIndex(reg);
  DCHECK_GE(index, register_info_table_.size());
  GrowRegisterMap(reg);
  return register_info_table_[index];
}

void BytecodeRegisterOptimizer::GrowRegisterMap(Register reg) {
  DCHECK(RegisterIsTemporary(reg));
  size_t index = GetRegisterInfoTableIndex(reg);
  DCHECK_GE(index, register_info_table_.size());
  size_t new_size = index + 1;
  size_t old_size = register_info_table_.size();
  register_info_table_.resize(new_size);
  for (size_t i = old_size; i < new_size; ++i) {
    register_info_table_[i] = new (zone()) RegisterInfo(
        RegisterFromRegisterInfoTableIndex(i), NextEquivalenceId(), false);
  }
}

void BytecodeRegisterOptimizer::TemporaryRegisterFreeEvent(Register reg) {
  RegisterInfo* info = GetRegisterInfo(reg);
  if (info != nullptr) {
    // If register is materialized and part of equivalence set, make
    // sure another member of the set holds the value before the
    // temporary register is removed.
    if (info->materialized()) {
      CreateMaterializedEquivalent(info);
    }
    info->MoveToNewEquivalenceSet(kInvalidEquivalenceId, false);
  }
}

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