Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1010)

Unified Diff: src/interpreter/bytecode-peephole-optimizer.cc

Issue 2164583002: Reland "[interpeter] Move to table based peephole optimizer." (Closed) Base URL: https://chromium.googlesource.com/v8/v8.git@master
Patch Set: Nit. Created 4 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « src/interpreter/bytecode-peephole-optimizer.h ('k') | src/interpreter/bytecode-peephole-table.h » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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
« no previous file with comments | « src/interpreter/bytecode-peephole-optimizer.h ('k') | src/interpreter/bytecode-peephole-table.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698