From f4234422b93b21a286b0f31799009bcbe8b90b9e Mon Sep 17 00:00:00 2001 From: oth Date: Mon, 18 Jul 2016 01:32:43 -0700 Subject: [PATCH] [interpeter] Move to table based peephole optimizer. Introduces a lookup table for peephole optimizations. Fixes some tests using BytecodePeepholeOptimizer::Write() that should have been update to use BytecodePeepholeOptimizer::WriteJump(). BUG=v8:4280 LOG=N Review-Url: https://codereview.chromium.org/2118183002 Cr-Commit-Position: refs/heads/master@{#37819} --- BUILD.gn | 49 ++ .../bytecode-peephole-optimizer.cc | 457 +++++++++--------- src/interpreter/bytecode-peephole-optimizer.h | 29 +- src/interpreter/bytecode-peephole-table.h | 73 +++ src/interpreter/bytecodes.cc | 10 +- src/interpreter/bytecodes.h | 6 +- src/interpreter/mkpeephole.cc | 387 +++++++++++++++ src/v8.gyp | 41 ++ .../ConstVariable.golden | 3 +- .../bytecode_expectations/LetVariable.golden | 3 +- .../bytecode-peephole-optimizer-unittest.cc | 35 +- 11 files changed, 810 insertions(+), 283 deletions(-) create mode 100644 src/interpreter/bytecode-peephole-table.h create mode 100644 src/interpreter/mkpeephole.cc diff --git a/BUILD.gn b/BUILD.gn index 24f339044f..245c63a49d 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -86,6 +86,7 @@ if (v8_enable_gdbjit == "") { } } +v8_generated_peephole_source = "$target_gen_dir/bytecode-peephole-table.cc" v8_random_seed = "314159265" v8_toolset_for_shell = "host" @@ -649,6 +650,25 @@ action("run_mksnapshot") { } } +action("run_mkpeephole") { + visibility = [ ":*" ] # Only targets in this file can depend on this. + + deps = [ ":mkpeephole($v8_snapshot_toolchain)" ] + + outputs = [ v8_generated_peephole_source ] + + sources = [] + + script = "tools/run.py" + + args = [ + "./" + rebase_path(get_label_info(":mkpeephole($v8_snapshot_toolchain)", + "root_out_dir") + "/mkpeephole", + root_build_dir), + rebase_path(v8_generated_peephole_source, root_build_dir), + ] +} + action("v8_dump_build_config") { script = "tools/testrunner/utils/dump_build_config.py" outputs = [ @@ -1311,6 +1331,7 @@ v8_source_set("v8_base") { "src/interpreter/bytecode-label.h", "src/interpreter/bytecode-peephole-optimizer.cc", "src/interpreter/bytecode-peephole-optimizer.h", + "src/interpreter/bytecode-peephole-table.h", "src/interpreter/bytecode-pipeline.cc", "src/interpreter/bytecode-pipeline.h", "src/interpreter/bytecode-register-allocator.cc", @@ -1909,6 +1930,9 @@ v8_source_set("v8_base") { ":v8_libsampler", ] + sources += [ v8_generated_peephole_source ] + deps += [ ":run_mkpeephole" ] + if (is_win) { # TODO(jschuh): crbug.com/167187 fix size_t to int truncations. cflags = [ "/wd4267" ] @@ -2132,6 +2156,31 @@ if (current_toolchain == v8_snapshot_toolchain) { } } +v8_executable("mkpeephole") { + # mkpeephole needs to be built for the build host so the peephole lookup + # table can built during build. The table depends on the properties of + # bytecodes that are described in bytecodes.{cc,h}. + visibility = [ ":*" ] # Only targets in this file can depend on this. + + sources = [ + "src/interpreter/bytecode-peephole-optimizer.h", + "src/interpreter/bytecodes.cc", + "src/interpreter/bytecodes.h", + "src/interpreter/mkpeephole.cc", + ] + + configs = [ + ":external_config", + ":internal_config", + ] + + deps = [ + ":v8_libbase", + "//build/config/sanitizers:deps", + "//build/win:default_exe_manifest", + ] +} + ############################################################################### # Public targets # diff --git a/src/interpreter/bytecode-peephole-optimizer.cc b/src/interpreter/bytecode-peephole-optimizer.cc index cd668d4a67..8570ddd843 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. @@ -28,21 +28,6 @@ Handle BytecodePeepholeOptimizer::ToBytecodeArray( 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(); @@ -52,14 +37,29 @@ void BytecodePeepholeOptimizer::BindLabel(BytecodeLabel* 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. + // 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() { - // 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 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())); + + 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); } - - 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; -} +void BytecodePeepholeOptimizer::ElideCurrentAction( + BytecodeNode* const node, const PeepholeActionAndData* action_data) { + DCHECK(LastIsValid()); + DCHECK(!Bytecodes::IsJump(node->bytecode())); -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_.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; + 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 { - return false; + // Nothing to do, keep last and wait for next bytecode to pair with it. } } -BytecodeNode* BytecodePeepholeOptimizer::Optimize(BytecodeNode* current) { - TryToRemoveLastExpressionPosition(current); - if (TransformCurrentBytecode(current) || - TransformLastAndCurrentBytecodes(current)) { - return current; - } +void BytecodePeepholeOptimizer::ElideCurrentIfOperand0MatchesAction( + BytecodeNode* const node, const PeepholeActionAndData* action_data) { + DCHECK(LastIsValid()); + DCHECK(!Bytecodes::IsJump(node->bytecode())); - 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; - } - return current; + if (last()->operand(0) == node->operand(0)) { + ElideCurrentAction(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; - } - - 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(); - } +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); + } +} + +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()); + } + SetLast(node); + } else { + DefaultAction(node); + } +} + +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())); + + 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())); + 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 diff --git a/src/interpreter/bytecode-peephole-optimizer.h b/src/interpreter/bytecode-peephole-optimizer.h index e6ada2aa1e..95c9d57795 100644 --- a/src/interpreter/bytecode-peephole-optimizer.h +++ b/src/interpreter/bytecode-peephole-optimizer.h @@ -1,10 +1,11 @@ -// 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. #ifndef V8_INTERPRETER_BYTECODE_PEEPHOLE_OPTIMIZER_H_ #define V8_INTERPRETER_BYTECODE_PEEPHOLE_OPTIMIZER_H_ +#include "src/interpreter/bytecode-peephole-table.h" #include "src/interpreter/bytecode-pipeline.h" namespace v8 { @@ -12,6 +13,7 @@ namespace internal { namespace interpreter { class ConstantArrayBuilder; +class BytecodePeepholeActionAndData; // An optimization stage for performing peephole optimizations on // generated bytecode. The optimizer may buffer one bytecode @@ -32,31 +34,26 @@ class BytecodePeepholeOptimizer final : public BytecodePipelineStage, Handle handler_table) override; private: - BytecodeNode* OptimizeAndEmitLast(BytecodeNode* current); - BytecodeNode* Optimize(BytecodeNode* current); - void Flush(); +#define DECLARE_ACTION(Action) \ + void Action(BytecodeNode* const node, \ + const PeepholeActionAndData* const action_data = nullptr); + PEEPHOLE_ACTION_LIST(DECLARE_ACTION) +#undef DECLARE_ACTION - void TryToRemoveLastExpressionPosition(const BytecodeNode* const current); - bool TransformCurrentBytecode(BytecodeNode* const current); - bool TransformLastAndCurrentBytecodes(BytecodeNode* const current); - bool CanElideCurrent(const BytecodeNode* const current) const; - bool CanElideLast(const BytecodeNode* const current) const; + void ApplyPeepholeAction(BytecodeNode* const node); + void Flush(); bool CanElideLastBasedOnSourcePosition( const BytecodeNode* const current) const; - - // Simple substitution methods. - bool RemoveToBooleanFromJump(BytecodeNode* const current); - bool RemoveToBooleanFromLogicalNot(BytecodeNode* const current); - void InvalidateLast(); bool LastIsValid() const; void SetLast(const BytecodeNode* const node); - bool LastBytecodePutsNameInAccumulator() const; - Handle GetConstantForIndexOperand(const BytecodeNode* const node, int index) const; + BytecodePipelineStage* next_stage() const { return next_stage_; } + BytecodeNode* last() { return &last_; } + ConstantArrayBuilder* constant_array_builder_; BytecodePipelineStage* next_stage_; BytecodeNode last_; diff --git a/src/interpreter/bytecode-peephole-table.h b/src/interpreter/bytecode-peephole-table.h new file mode 100644 index 0000000000..7c0059e8cc --- /dev/null +++ b/src/interpreter/bytecode-peephole-table.h @@ -0,0 +1,73 @@ +// 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. + +#ifndef V8_INTERPRETER_BYTECODE_PEEPHOLE_TABLE_H_ +#define V8_INTERPRETER_BYTECODE_PEEPHOLE_TABLE_H_ + +#include "src/interpreter/bytecodes.h" + +namespace v8 { +namespace internal { +namespace interpreter { + +#define PEEPHOLE_NON_JUMP_ACTION_LIST(V) \ + V(DefaultAction) \ + V(UpdateLastAction) \ + V(UpdateLastIfSourceInfoPresentAction) \ + V(ElideCurrentAction) \ + V(ElideCurrentIfOperand0MatchesAction) \ + V(ElideCurrentIfLoadingNameConstantAction) \ + V(ElideLastAction) \ + V(ChangeBytecodeAction) \ + V(TransformLdaStarToLdrLdarAction) \ + V(TransformLdaSmiBinaryOpToBinaryOpWithSmiAction) \ + V(TransformLdaZeroBinaryOpToBinaryOpWithZeroAction) + +#define PEEPHOLE_JUMP_ACTION_LIST(V) \ + V(DefaultJumpAction) \ + V(UpdateLastJumpAction) \ + V(ChangeJumpBytecodeAction) \ + V(ElideLastBeforeJumpAction) + +#define PEEPHOLE_ACTION_LIST(V) \ + PEEPHOLE_NON_JUMP_ACTION_LIST(V) \ + PEEPHOLE_JUMP_ACTION_LIST(V) + +// Actions to take when a pair of bytes is encountered. A handler +// exists for each action. +enum class PeepholeAction : uint8_t { +#define DECLARE_PEEPHOLE_ACTION(Action) k##Action, + PEEPHOLE_ACTION_LIST(DECLARE_PEEPHOLE_ACTION) +#undef DECLARE_PEEPHOLE_ACTION +}; + +// Tuple of action to take when pair of bytecodes is encountered and +// optional data to invoke handler with. +struct PeepholeActionAndData final { + // Action to take when tuple of bytecodes encountered. + PeepholeAction action; + + // Replacement bytecode (if valid). + Bytecode bytecode; +}; + +// Lookup table for matching pairs of bytecodes to peephole optimization +// actions. The contents of the table are generated by mkpeephole.cc. +struct PeepholeActionTable final { + public: + static const PeepholeActionAndData* Lookup(Bytecode last, Bytecode current); + + private: + static const size_t kNumberOfBytecodes = + static_cast(Bytecode::kLast) + 1; + + static const PeepholeActionAndData row_data_[][kNumberOfBytecodes]; + static const PeepholeActionAndData* const row_[kNumberOfBytecodes]; +}; + +} // namespace interpreter +} // namespace internal +} // namespace v8 + +#endif // V8_INTERPRETER_BYTECODE_PEEPHOLE_TABLE_H_ diff --git a/src/interpreter/bytecodes.cc b/src/interpreter/bytecodes.cc index e0c90f8ab9..525136a95f 100644 --- a/src/interpreter/bytecodes.cc +++ b/src/interpreter/bytecodes.cc @@ -476,11 +476,6 @@ bool Bytecodes::IsLdarOrStar(Bytecode bytecode) { return bytecode == Bytecode::kLdar || bytecode == Bytecode::kStar; } -// static -bool Bytecodes::IsLdaSmiOrLdaZero(Bytecode bytecode) { - return bytecode == Bytecode::kLdaSmi || bytecode == Bytecode::kLdaZero; -} - // static bool Bytecodes::IsBytecodeWithScalableOperands(Bytecode bytecode) { switch (bytecode) { @@ -508,6 +503,11 @@ bool Bytecodes::IsPrefixScalingBytecode(Bytecode bytecode) { } } +// static +bool Bytecodes::PutsNameInAccumulator(Bytecode bytecode) { + return bytecode == Bytecode::kToName || bytecode == Bytecode::kTypeOf; +} + // static bool Bytecodes::IsJumpOrReturn(Bytecode bytecode) { return bytecode == Bytecode::kReturn || IsJump(bytecode); diff --git a/src/interpreter/bytecodes.h b/src/interpreter/bytecodes.h index 43aeb30d3b..75c38af166 100644 --- a/src/interpreter/bytecodes.h +++ b/src/interpreter/bytecodes.h @@ -514,15 +514,15 @@ class Bytecodes final { // Returns true if the bytecode is Ldar or Star. static bool IsLdarOrStar(Bytecode bytecode); - // Returns true if the bytecode is LdaSmi or LdaZero. - static bool IsLdaSmiOrLdaZero(Bytecode bytecode); - // Returns true if the bytecode has wider operand forms. static bool IsBytecodeWithScalableOperands(Bytecode bytecode); // Returns true if the bytecode is a scaling prefix bytecode. static bool IsPrefixScalingBytecode(Bytecode bytecode); + // Returns true if |bytecode| puts a name in the accumulator. + static bool PutsNameInAccumulator(Bytecode bytecode); + // Returns true if |operand_type| is any type of register operand. static bool IsRegisterOperandType(OperandType operand_type); diff --git a/src/interpreter/mkpeephole.cc b/src/interpreter/mkpeephole.cc new file mode 100644 index 0000000000..d89390d45d --- /dev/null +++ b/src/interpreter/mkpeephole.cc @@ -0,0 +1,387 @@ +// 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 +#include +#include +#include +#include + +#include "src/globals.h" +#include "src/interpreter/bytecode-peephole-table.h" +#include "src/interpreter/bytecodes.h" + +namespace v8 { +namespace internal { + +namespace interpreter { + +const char* ActionName(PeepholeAction action) { + switch (action) { +#define CASE(Name) \ + case PeepholeAction::k##Name: \ + return "PeepholeAction::k" #Name; + PEEPHOLE_ACTION_LIST(CASE) +#undef CASE + default: + UNREACHABLE(); + return ""; + } +} + +std::string BytecodeName(Bytecode bytecode) { + return "Bytecode::k" + std::string(Bytecodes::ToString(bytecode)); +} + +class PeepholeActionTableWriter final { + public: + static const size_t kNumberOfBytecodes = + static_cast(Bytecode::kLast) + 1; + typedef std::array Row; + + void BuildTable(); + void Write(std::ostream& os); + + private: + static const char* kIndent; + static const char* kNamespaceElements[]; + + void WriteHeader(std::ostream& os); + void WriteIncludeFiles(std::ostream& os); + void WriteClassMethods(std::ostream& os); + void WriteUniqueRows(std::ostream& os); + void WriteRowMap(std::ostream& os); + void WriteRow(std::ostream& os, size_t row_index); + void WriteOpenNamespace(std::ostream& os); + void WriteCloseNamespace(std::ostream& os); + + PeepholeActionAndData LookupActionAndData(Bytecode last, Bytecode current); + void BuildRow(Bytecode last, Row* row); + size_t HashRow(const Row* row); + void InsertRow(size_t row_index, const Row* const row, size_t row_hash, + std::map* hash_to_row_map); + bool RowsEqual(const Row* const first, const Row* const second); + + std::vector* table() { return &table_; } + + // Table of unique rows. + std::vector table_; + + // Mapping of row index to unique row index. + std::array row_map_; +}; + +const char* PeepholeActionTableWriter::kIndent = " "; +const char* PeepholeActionTableWriter::kNamespaceElements[] = {"v8", "internal", + "interpreter"}; + +// static +PeepholeActionAndData PeepholeActionTableWriter::LookupActionAndData( + Bytecode last, Bytecode current) { + // Optimize various accumulator loads followed by store accumulator + // to an equivalent register load and loading the accumulator with + // the register. The latter accumulator load can often be elided as + // it is side-effect free and often followed by another accumulator + // load so can be elided. + if (current == Bytecode::kStar) { + switch (last) { + case Bytecode::kLdaNamedProperty: + return {PeepholeAction::kTransformLdaStarToLdrLdarAction, + Bytecode::kLdrNamedProperty}; + case Bytecode::kLdaKeyedProperty: + return {PeepholeAction::kTransformLdaStarToLdrLdarAction, + Bytecode::kLdrKeyedProperty}; + case Bytecode::kLdaGlobal: + return {PeepholeAction::kTransformLdaStarToLdrLdarAction, + Bytecode::kLdrGlobal}; + case Bytecode::kLdaContextSlot: + return {PeepholeAction::kTransformLdaStarToLdrLdarAction, + Bytecode::kLdrContextSlot}; + case Bytecode::kLdaUndefined: + return {PeepholeAction::kTransformLdaStarToLdrLdarAction, + Bytecode::kLdrUndefined}; + default: + break; + } + } + + // ToName optimizations: remove unnecessary ToName bytecodes. + if (current == Bytecode::kToName) { + if (last == Bytecode::kLdaConstant) { + return {PeepholeAction::kElideCurrentIfLoadingNameConstantAction, + Bytecode::kIllegal}; + } else if (Bytecodes::PutsNameInAccumulator(last)) { + return {PeepholeAction::kElideCurrentAction, Bytecode::kIllegal}; + } + } + + // Nop are placeholders for holding source position information and can be + // elided if there is no source information. + if (last == Bytecode::kNop) { + if (Bytecodes::IsJump(current)) { + return {PeepholeAction::kElideLastBeforeJumpAction, Bytecode::kIllegal}; + } else { + return {PeepholeAction::kElideLastAction, Bytecode::kIllegal}; + } + } + + // 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. + if (Bytecodes::IsAccumulatorLoadWithoutEffects(last) && + Bytecodes::IsAccumulatorLoadWithoutEffects(current)) { + return {PeepholeAction::kElideLastAction, Bytecode::kIllegal}; + } + + // The current instruction clobbers the accumulator without reading + // it. The load in the last instruction can be elided as it has no + // effect. + if (Bytecodes::IsAccumulatorLoadWithoutEffects(last) && + Bytecodes::GetAccumulatorUse(current) == AccumulatorUse::kWrite) { + return {PeepholeAction::kElideLastAction, Bytecode::kIllegal}; + } + + // 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. + if (Bytecodes::IsLdarOrStar(last) && Bytecodes::IsLdarOrStar(current)) { + return {PeepholeAction::kElideCurrentIfOperand0MatchesAction, + Bytecode::kIllegal}; + } + + // Remove ToBoolean coercion from conditional jumps where possible. + if (Bytecodes::WritesBooleanToAccumulator(last)) { + if (Bytecodes::IsJumpIfToBoolean(current)) { + return {PeepholeAction::kChangeJumpBytecodeAction, + Bytecodes::GetJumpWithoutToBoolean(current)}; + } else if (current == Bytecode::kToBooleanLogicalNot) { + return {PeepholeAction::kChangeBytecodeAction, Bytecode::kLogicalNot}; + } + } + + // Fuse LdaSmi followed by binary op to produce binary op with a + // immediate integer argument. This savaes on dispatches and size. + if (last == Bytecode::kLdaSmi) { + switch (current) { + case Bytecode::kAdd: + return {PeepholeAction::kTransformLdaSmiBinaryOpToBinaryOpWithSmiAction, + Bytecode::kAddSmi}; + case Bytecode::kSub: + return {PeepholeAction::kTransformLdaSmiBinaryOpToBinaryOpWithSmiAction, + Bytecode::kSubSmi}; + case Bytecode::kBitwiseAnd: + return {PeepholeAction::kTransformLdaSmiBinaryOpToBinaryOpWithSmiAction, + Bytecode::kBitwiseAndSmi}; + case Bytecode::kBitwiseOr: + return {PeepholeAction::kTransformLdaSmiBinaryOpToBinaryOpWithSmiAction, + Bytecode::kBitwiseOrSmi}; + case Bytecode::kShiftLeft: + return {PeepholeAction::kTransformLdaSmiBinaryOpToBinaryOpWithSmiAction, + Bytecode::kShiftLeftSmi}; + case Bytecode::kShiftRight: + return {PeepholeAction::kTransformLdaSmiBinaryOpToBinaryOpWithSmiAction, + Bytecode::kShiftRightSmi}; + default: + break; + } + } + + // Fuse LdaZero followed by binary op to produce binary op with a + // zero immediate argument. This saves dispatches, but not size. + if (last == Bytecode::kLdaZero) { + switch (current) { + case Bytecode::kAdd: + return { + PeepholeAction::kTransformLdaZeroBinaryOpToBinaryOpWithZeroAction, + Bytecode::kAddSmi}; + case Bytecode::kSub: + return { + PeepholeAction::kTransformLdaZeroBinaryOpToBinaryOpWithZeroAction, + Bytecode::kSubSmi}; + case Bytecode::kBitwiseAnd: + return { + PeepholeAction::kTransformLdaZeroBinaryOpToBinaryOpWithZeroAction, + Bytecode::kBitwiseAndSmi}; + case Bytecode::kBitwiseOr: + return { + PeepholeAction::kTransformLdaZeroBinaryOpToBinaryOpWithZeroAction, + Bytecode::kBitwiseOrSmi}; + case Bytecode::kShiftLeft: + return { + PeepholeAction::kTransformLdaZeroBinaryOpToBinaryOpWithZeroAction, + Bytecode::kShiftLeftSmi}; + case Bytecode::kShiftRight: + return { + PeepholeAction::kTransformLdaZeroBinaryOpToBinaryOpWithZeroAction, + Bytecode::kShiftRightSmi}; + default: + break; + } + } + + // If there is no last bytecode to optimize against, store the incoming + // bytecode or for jumps emit incoming bytecode immediately. + if (last == Bytecode::kIllegal) { + if (Bytecodes::IsJump(current)) { + return {PeepholeAction::kUpdateLastJumpAction, Bytecode::kIllegal}; + } else if (current == Bytecode::kNop) { + return {PeepholeAction::kUpdateLastIfSourceInfoPresentAction, + Bytecode::kIllegal}; + } else { + return {PeepholeAction::kUpdateLastAction, Bytecode::kIllegal}; + } + } + + // No matches, take the default action. + if (Bytecodes::IsJump(current)) { + return {PeepholeAction::kDefaultJumpAction, Bytecode::kIllegal}; + } else { + return {PeepholeAction::kDefaultAction, Bytecode::kIllegal}; + } +} + +void PeepholeActionTableWriter::Write(std::ostream& os) { + WriteHeader(os); + WriteIncludeFiles(os); + WriteOpenNamespace(os); + WriteUniqueRows(os); + WriteRowMap(os); + WriteClassMethods(os); + WriteCloseNamespace(os); +} + +void PeepholeActionTableWriter::WriteHeader(std::ostream& os) { + os << "// Copyright 2016 the V8 project authors. All rights reserved.\n" + << "// Use of this source code is governed by a BSD-style license that\n" + << "// can be found in the LICENSE file.\n\n" + << "// Autogenerated by " __FILE__ ". Do not edit.\n\n"; +} + +void PeepholeActionTableWriter::WriteIncludeFiles(std::ostream& os) { + os << "#include \"src/interpreter/bytecode-peephole-table.h\"\n\n"; +} + +void PeepholeActionTableWriter::WriteUniqueRows(std::ostream& os) { + os << "const PeepholeActionAndData PeepholeActionTable::row_data_[" + << table_.size() << "][" << kNumberOfBytecodes << "] = {\n"; + for (size_t i = 0; i < table_.size(); ++i) { + os << "{\n"; + WriteRow(os, i); + os << "},\n"; + } + os << "};\n\n"; +} + +void PeepholeActionTableWriter::WriteRowMap(std::ostream& os) { + os << "const PeepholeActionAndData* const PeepholeActionTable::row_[" + << kNumberOfBytecodes << "] = {\n"; + for (size_t i = 0; i < kNumberOfBytecodes; ++i) { + os << kIndent << " PeepholeActionTable::row_data_[" << row_map_[i] + << "], \n"; + } + os << "};\n\n"; +} + +void PeepholeActionTableWriter::WriteRow(std::ostream& os, size_t row_index) { + const Row row = table_.at(row_index); + for (PeepholeActionAndData action_data : row) { + os << kIndent << "{" << ActionName(action_data.action) << "," + << BytecodeName(action_data.bytecode) << "},\n"; + } +} + +void PeepholeActionTableWriter::WriteOpenNamespace(std::ostream& os) { + for (auto element : kNamespaceElements) { + os << "namespace " << element << " {\n"; + } + os << "\n"; +} + +void PeepholeActionTableWriter::WriteCloseNamespace(std::ostream& os) { + for (auto element : kNamespaceElements) { + os << "} // namespace " << element << "\n"; + } +} + +void PeepholeActionTableWriter::WriteClassMethods(std::ostream& os) { + os << "// static\n" + << "const PeepholeActionAndData*\n" + << "PeepholeActionTable::Lookup(Bytecode last, Bytecode current) {\n" + << kIndent + << "return &row_[Bytecodes::ToByte(last)][Bytecodes::ToByte(current)];\n" + << "}\n\n"; +} + +void PeepholeActionTableWriter::BuildTable() { + std::map hash_to_row_map; + Row row; + for (size_t i = 0; i < kNumberOfBytecodes; ++i) { + uint8_t byte_value = static_cast(i); + Bytecode last = Bytecodes::FromByte(byte_value); + BuildRow(last, &row); + size_t row_hash = HashRow(&row); + InsertRow(i, &row, row_hash, &hash_to_row_map); + } +} + +void PeepholeActionTableWriter::BuildRow(Bytecode last, Row* row) { + for (size_t i = 0; i < kNumberOfBytecodes; ++i) { + uint8_t byte_value = static_cast(i); + Bytecode current = Bytecodes::FromByte(byte_value); + PeepholeActionAndData action_data = LookupActionAndData(last, current); + row->at(i) = action_data; + } +} + +// static +bool PeepholeActionTableWriter::RowsEqual(const Row* const first, + const Row* const second) { + return memcmp(first, second, sizeof(*first)) == 0; +} + +// static +void PeepholeActionTableWriter::InsertRow( + size_t row_index, const Row* const row, size_t row_hash, + std::map* hash_to_row_map) { + // Insert row if no existing row matches, otherwise use existing row. + auto iter = hash_to_row_map->find(row_hash); + if (iter == hash_to_row_map->end()) { + row_map_[row_index] = table()->size(); + table()->push_back(*row); + } else { + row_map_[row_index] = iter->second; + + // If the following DCHECK fails, the HashRow() is not adequate. + DCHECK(RowsEqual(&table()->at(iter->second), row)); + } +} + +// static +size_t PeepholeActionTableWriter::HashRow(const Row* row) { + static const size_t kHashShift = 3; + std::size_t result = (1u << 31) - 1u; + const uint8_t* raw_data = reinterpret_cast(row); + for (size_t i = 0; i < sizeof(*row); ++i) { + size_t top_bits = result >> (kBitsPerByte * sizeof(size_t) - kHashShift); + result = (result << kHashShift) ^ top_bits ^ raw_data[i]; + } + return result; +} + +} // namespace interpreter +} // namespace internal +} // namespace v8 + +int main(int argc, const char* argv[]) { + CHECK_EQ(argc, 2); + + std::ofstream ofs(argv[1], std::ofstream::trunc); + v8::internal::interpreter::PeepholeActionTableWriter writer; + writer.BuildTable(); + writer.Write(ofs); + ofs.flush(); + ofs.close(); + + return 0; +} diff --git a/src/v8.gyp b/src/v8.gyp index bfc714309d..07434178e3 100644 --- a/src/v8.gyp +++ b/src/v8.gyp @@ -35,6 +35,7 @@ 'v8_extra_library_files%': [], 'v8_experimental_extra_library_files%': [], 'mksnapshot_exec': '<(PRODUCT_DIR)/<(EXECUTABLE_PREFIX)mksnapshot<(EXECUTABLE_SUFFIX)', + 'mkpeephole_exec': '<(PRODUCT_DIR)/<(EXECUTABLE_PREFIX)mkpeephole<(EXECUTABLE_SUFFIX)', }, 'includes': ['../gypfiles/toolchain.gypi', '../gypfiles/features.gypi'], 'targets': [ @@ -382,13 +383,31 @@ 'v8_libbase', 'v8_libsampler', ], + 'objs': ['foo.o'], 'variables': { 'optimize': 'max', }, 'include_dirs+': [ '..', '<(DEPTH)', + '<(SHARED_INTERMEDIATE_DIR)' ], + 'actions':[{ + 'action_name': 'run mkpeephole', + 'inputs': ['<(mkpeephole_exec)'], + 'outputs': ['<(INTERMEDIATE_DIR)/bytecode-peephole-table.cc'], + 'action': ['<(mkpeephole_exec)', '<(INTERMEDIATE_DIR)/bytecode-peephole-table.cc' ], + 'process_outputs_as_sources': 1, + 'conditions': [ + ['want_separate_host_toolset==1', { + 'dependencies': ['mkpeephole#host'], + 'toolsets': ['host'], + }, { + 'dependencies': ['mkpeephole'], + 'toolsets': ['target'], + }], + ], + }], 'sources': [ ### gcmole(all) ### '../include/v8-debug.h', '../include/v8-experimental.h', @@ -929,6 +948,7 @@ 'interpreter/bytecode-label.h', 'interpreter/bytecode-peephole-optimizer.cc', 'interpreter/bytecode-peephole-optimizer.h', + 'interpreter/bytecode-peephole-table.h', 'interpreter/bytecode-pipeline.cc', 'interpreter/bytecode-pipeline.h', 'interpreter/bytecode-register.cc', @@ -2315,5 +2335,26 @@ }], ], }, + { + 'target_name': 'mkpeephole', + 'type': 'executable', + 'dependencies': [ 'v8_libbase' ], + 'include_dirs+': [ + '..', + ], + 'sources': [ + 'interpreter/bytecode-peephole-table.h', + 'interpreter/bytecodes.h', + 'interpreter/bytecodes.cc', + 'interpreter/mkpeephole.cc' + ], + 'conditions': [ + ['want_separate_host_toolset==1', { + 'toolsets': ['host'], + }, { + 'toolsets': ['target'], + }], + ], + }, ], } diff --git a/test/cctest/interpreter/bytecode_expectations/ConstVariable.golden b/test/cctest/interpreter/bytecode_expectations/ConstVariable.golden index 4c511b53bc..70b0d6f271 100644 --- a/test/cctest/interpreter/bytecode_expectations/ConstVariable.golden +++ b/test/cctest/interpreter/bytecode_expectations/ConstVariable.golden @@ -59,7 +59,7 @@ snippet: " " frame size: 3 parameter count: 1 -bytecode array length: 33 +bytecode array length: 31 bytecodes: [ B(LdaTheHole), B(Star), R(0), @@ -73,7 +73,6 @@ bytecodes: [ /* 48 E> */ B(CallRuntime), U16(Runtime::kThrowReferenceError), R(2), U8(1), B(CallRuntime), U16(Runtime::kThrowConstAssignError), R(0), U8(0), B(Mov), R(1), R(0), - B(Ldar), R(0), B(LdaUndefined), /* 55 S> */ B(Return), ] diff --git a/test/cctest/interpreter/bytecode_expectations/LetVariable.golden b/test/cctest/interpreter/bytecode_expectations/LetVariable.golden index e54769c14d..4dbbdafd5e 100644 --- a/test/cctest/interpreter/bytecode_expectations/LetVariable.golden +++ b/test/cctest/interpreter/bytecode_expectations/LetVariable.golden @@ -59,7 +59,7 @@ snippet: " " frame size: 3 parameter count: 1 -bytecode array length: 28 +bytecode array length: 26 bytecodes: [ B(LdaTheHole), B(Star), R(0), @@ -72,7 +72,6 @@ bytecodes: [ B(Star), R(2), /* 45 E> */ B(CallRuntime), U16(Runtime::kThrowReferenceError), R(2), U8(1), B(Mov), R(1), R(0), - B(Ldar), R(0), B(LdaUndefined), /* 52 S> */ B(Return), ] diff --git a/test/unittests/interpreter/bytecode-peephole-optimizer-unittest.cc b/test/unittests/interpreter/bytecode-peephole-optimizer-unittest.cc index 168e0a7322..c3548252a7 100644 --- a/test/unittests/interpreter/bytecode-peephole-optimizer-unittest.cc +++ b/test/unittests/interpreter/bytecode-peephole-optimizer-unittest.cc @@ -134,12 +134,10 @@ TEST_F(BytecodePeepholeOptimizerTest, KeepStatementNop) { TEST_F(BytecodePeepholeOptimizerTest, KeepJumpIfToBooleanTrue) { BytecodeNode first(Bytecode::kLdaNull); BytecodeNode second(Bytecode::kJumpIfToBooleanTrue, 3); + BytecodeLabel label; optimizer()->Write(&first); CHECK_EQ(write_count(), 0); - optimizer()->Write(&second); - CHECK_EQ(write_count(), 1); - CHECK_EQ(last_written(), first); - Flush(); + optimizer()->WriteJump(&second, &label); CHECK_EQ(write_count(), 2); CHECK_EQ(last_written(), second); } @@ -147,15 +145,12 @@ TEST_F(BytecodePeepholeOptimizerTest, KeepJumpIfToBooleanTrue) { TEST_F(BytecodePeepholeOptimizerTest, ElideJumpIfToBooleanTrue) { BytecodeNode first(Bytecode::kLdaTrue); BytecodeNode second(Bytecode::kJumpIfToBooleanTrue, 3); + BytecodeLabel label; optimizer()->Write(&first); CHECK_EQ(write_count(), 0); - optimizer()->Write(&second); - CHECK_EQ(write_count(), 1); - CHECK_EQ(last_written(), first); - Flush(); + optimizer()->WriteJump(&second, &label); CHECK_EQ(write_count(), 2); - CHECK_EQ(last_written().bytecode(), Bytecode::kJumpIfTrue); - CHECK_EQ(last_written().operand(0), second.operand(0)); + CHECK_EQ(last_written(), second); } TEST_F(BytecodePeepholeOptimizerTest, KeepToBooleanLogicalNot) { @@ -204,12 +199,11 @@ TEST_F(BytecodePeepholeOptimizerTest, StarRxLdarRx) { BytecodeNode first(Bytecode::kStar, Register(0).ToOperand()); BytecodeNode second(Bytecode::kLdar, Register(0).ToOperand()); optimizer()->Write(&first); - CHECK_EQ(write_count(), 0); optimizer()->Write(&second); - CHECK_EQ(write_count(), 1); - CHECK_EQ(last_written(), first); + CHECK_EQ(write_count(), 0); Flush(); CHECK_EQ(write_count(), 1); + CHECK_EQ(last_written(), first); } TEST_F(BytecodePeepholeOptimizerTest, StarRxLdarRxStatement) { @@ -262,11 +256,10 @@ TEST_F(BytecodePeepholeOptimizerTest, ToNameToName) { BytecodeNode first(Bytecode::kToName); BytecodeNode second(Bytecode::kToName); optimizer()->Write(&first); - CHECK_EQ(write_count(), 0); optimizer()->Write(&second); - CHECK_EQ(write_count(), 1); - CHECK_EQ(last_written(), first); + CHECK_EQ(write_count(), 0); Flush(); + CHECK_EQ(last_written(), first); CHECK_EQ(write_count(), 1); } @@ -274,12 +267,11 @@ TEST_F(BytecodePeepholeOptimizerTest, TypeOfToName) { BytecodeNode first(Bytecode::kTypeOf); BytecodeNode second(Bytecode::kToName); optimizer()->Write(&first); - CHECK_EQ(write_count(), 0); optimizer()->Write(&second); - CHECK_EQ(write_count(), 1); - CHECK_EQ(last_written(), first); + CHECK_EQ(write_count(), 0); Flush(); CHECK_EQ(write_count(), 1); + CHECK_EQ(last_written(), first); } TEST_F(BytecodePeepholeOptimizerTest, LdaConstantStringToName) { @@ -289,12 +281,11 @@ TEST_F(BytecodePeepholeOptimizerTest, LdaConstantStringToName) { BytecodeNode first(Bytecode::kLdaConstant, static_cast(index)); BytecodeNode second(Bytecode::kToName); optimizer()->Write(&first); - CHECK_EQ(write_count(), 0); optimizer()->Write(&second); - CHECK_EQ(write_count(), 1); - CHECK_EQ(last_written(), first); + CHECK_EQ(write_count(), 0); Flush(); CHECK_EQ(write_count(), 1); + CHECK_EQ(last_written(), first); } TEST_F(BytecodePeepholeOptimizerTest, LdaConstantNumberToName) {