| 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..8570ddd843b317897c66d7786bf2f6073879d92a 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,37 +29,37 @@ Handle<BytecodeArray> BytecodePeepholeOptimizer::ToBytecodeArray(
|
| }
|
|
|
| // override
|
| -void BytecodePeepholeOptimizer::Write(BytecodeNode* node) {
|
| - node = OptimizeAndEmitLast(node);
|
| - if (node != nullptr) {
|
| - SetLast(node);
|
| - }
|
| +void BytecodePeepholeOptimizer::BindLabel(BytecodeLabel* label) {
|
| + Flush();
|
| + next_stage_->BindLabel(label);
|
| }
|
|
|
| // override
|
| -void BytecodePeepholeOptimizer::WriteJump(BytecodeNode* node,
|
| +void BytecodePeepholeOptimizer::BindLabel(const BytecodeLabel& target,
|
| BytecodeLabel* label) {
|
| - node = OptimizeAndEmitLast(node);
|
| - next_stage_->WriteJump(node, 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::BindLabel(BytecodeLabel* label) {
|
| - Flush();
|
| - next_stage_->BindLabel(label);
|
| +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::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::Write(BytecodeNode* node) {
|
| + // Handlers for non-jump bytecodes run to completion emitting
|
| + // bytecode to next stage as appropriate.
|
| + ApplyPeepholeAction(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();
|
| @@ -75,6 +75,11 @@ bool BytecodePeepholeOptimizer::LastIsValid() const {
|
| }
|
|
|
| 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_.Clone(node);
|
| }
|
|
|
| @@ -86,51 +91,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 {
|
| //
|
| @@ -153,17 +113,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
|
| + // 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(),
|
| - // 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.
|
| + // 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());
|
| @@ -189,13 +145,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 +167,198 @@ 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()));
|
|
|
| - return false;
|
| + next_stage()->Write(last());
|
| + 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));
|
| - }
|
| - return can_remove;
|
| +void BytecodePeepholeOptimizer::UpdateLastAction(
|
| + BytecodeNode* const node, const PeepholeActionAndData* action_data) {
|
| + DCHECK(!LastIsValid());
|
| + DCHECK(!Bytecodes::IsJump(node->bytecode()));
|
| +
|
| + SetLast(node);
|
| }
|
|
|
| -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::UpdateLastIfSourceInfoPresentAction(
|
| + BytecodeNode* const node, const PeepholeActionAndData* action_data) {
|
| + DCHECK(!LastIsValid());
|
| + DCHECK(!Bytecodes::IsJump(node->bytecode()));
|
| +
|
| + if (node->source_info().is_valid()) {
|
| + SetLast(node);
|
| }
|
| - return can_remove;
|
| }
|
|
|
| -bool BytecodePeepholeOptimizer::TransformCurrentBytecode(
|
| - BytecodeNode* const current) {
|
| - return RemoveToBooleanFromJump(current) ||
|
| - RemoveToBooleanFromLogicalNot(current);
|
| +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.
|
| + }
|
| }
|
|
|
| -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::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 {
|
| - return false;
|
| + DefaultAction(node);
|
| }
|
| }
|
|
|
| -BytecodeNode* BytecodePeepholeOptimizer::Optimize(BytecodeNode* current) {
|
| - TryToRemoveLastExpressionPosition(current);
|
| - if (TransformCurrentBytecode(current) ||
|
| - TransformLastAndCurrentBytecodes(current)) {
|
| - return 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);
|
| }
|
| +}
|
|
|
| - 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;
|
| +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->source_info().Clone(last()->source_info());
|
| }
|
| - return current;
|
| + SetLast(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());
|
| - }
|
| - InvalidateLast();
|
| - 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);
|
| +}
|
| +
|
| +void BytecodePeepholeOptimizer::TransformLdaSmiBinaryOpToBinaryOpWithSmiAction(
|
| + BytecodeNode* const node, const PeepholeActionAndData* action_data) {
|
| + DCHECK(LastIsValid());
|
| + DCHECK(!Bytecodes::IsJump(node->bytecode()));
|
|
|
| - return current;
|
| + 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);
|
| + }
|
| }
|
|
|
| -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::
|
| + 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()));
|
| + DCHECK(CanElideLastBasedOnSourcePosition(node));
|
| +
|
| + if (!node->source_info().is_valid()) {
|
| + node->source_info().Clone(last()->source_info());
|
| + } else {
|
| + next_stage()->Write(last());
|
| + }
|
| + 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;
|
| }
|
| - return current;
|
| }
|
|
|
| } // namespace interpreter
|
|
|