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