Home | History | Annotate | Download | only in interpreter
      1 // Copyright 2016 the V8 project authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "src/interpreter/bytecode-peephole-optimizer.h"
      6 
      7 #include "src/objects-inl.h"
      8 #include "src/objects.h"
      9 
     10 namespace v8 {
     11 namespace internal {
     12 namespace interpreter {
     13 
     14 BytecodePeepholeOptimizer::BytecodePeepholeOptimizer(
     15     BytecodePipelineStage* next_stage)
     16     : next_stage_(next_stage), last_(Bytecode::kIllegal, BytecodeSourceInfo()) {
     17   InvalidateLast();
     18 }
     19 
     20 // override
     21 Handle<BytecodeArray> BytecodePeepholeOptimizer::ToBytecodeArray(
     22     Isolate* isolate, int register_count, int parameter_count,
     23     Handle<FixedArray> handler_table) {
     24   Flush();
     25   return next_stage_->ToBytecodeArray(isolate, register_count, parameter_count,
     26                                       handler_table);
     27 }
     28 
     29 // override
     30 void BytecodePeepholeOptimizer::BindLabel(BytecodeLabel* label) {
     31   Flush();
     32   next_stage_->BindLabel(label);
     33 }
     34 
     35 // override
     36 void BytecodePeepholeOptimizer::BindLabel(const BytecodeLabel& target,
     37                                           BytecodeLabel* label) {
     38   // There is no need to flush here, it will have been flushed when
     39   // |target| was bound.
     40   next_stage_->BindLabel(target, label);
     41 }
     42 
     43 // override
     44 void BytecodePeepholeOptimizer::WriteJump(BytecodeNode* node,
     45                                           BytecodeLabel* label) {
     46   // Handlers for jump bytecodes do not emit |node| as WriteJump()
     47   // requires the |label| and having a label argument in all action
     48   // handlers results in dead work in the non-jump case.
     49   ApplyPeepholeAction(node);
     50   next_stage()->WriteJump(node, label);
     51 }
     52 
     53 // override
     54 void BytecodePeepholeOptimizer::Write(BytecodeNode* node) {
     55   // Handlers for non-jump bytecodes run to completion emitting
     56   // bytecode to next stage as appropriate.
     57   ApplyPeepholeAction(node);
     58 }
     59 
     60 void BytecodePeepholeOptimizer::Flush() {
     61   if (LastIsValid()) {
     62     next_stage_->Write(&last_);
     63     InvalidateLast();
     64   }
     65 }
     66 
     67 void BytecodePeepholeOptimizer::InvalidateLast() {
     68   last_.set_bytecode(Bytecode::kIllegal);
     69 }
     70 
     71 bool BytecodePeepholeOptimizer::LastIsValid() const {
     72   return last_.bytecode() != Bytecode::kIllegal;
     73 }
     74 
     75 void BytecodePeepholeOptimizer::SetLast(const BytecodeNode* const node) {
     76   // An action shouldn't leave a NOP as last bytecode unless it has
     77   // source position information. NOP without source information can
     78   // always be elided.
     79   DCHECK(node->bytecode() != Bytecode::kNop || node->source_info().is_valid());
     80   last_ = *node;
     81 }
     82 
     83 bool BytecodePeepholeOptimizer::CanElideLastBasedOnSourcePosition(
     84     const BytecodeNode* const current) const {
     85   //
     86   // The rules for allowing the elision of the last bytecode based
     87   // on source position are:
     88   //
     89   //                     C U R R E N T
     90   //              +--------+--------+--------+
     91   //              |  None  |  Expr  |  Stmt  |
     92   //  L  +--------+--------+--------+--------+
     93   //     |  None  |  YES   |  YES   |  YES   |
     94   //  A  +--------+--------+--------+--------+
     95   //     |  Expr  |  YES   | MAYBE  |  MAYBE |
     96   //  S  +--------+--------+--------+--------+
     97   //     |  Stmt  |  YES   |   NO   |   NO   |
     98   //  T  +--------+--------+--------+--------+
     99   //
    100   // The goal is not lose any statement positions and not lose useful
    101   // expression positions. Whenever the last bytecode is elided it's
    102   // source position information is applied to the current node
    103   // updating it if necessary.
    104   //
    105   // The last bytecode could be elided for the MAYBE cases if the last
    106   // bytecode is known not to throw. If it throws, the system would
    107   // not have correct stack trace information. The appropriate check
    108   // for this would be Bytecodes::IsWithoutExternalSideEffects(). By
    109   // default, the upstream bytecode generator filters out unneeded
    110   // expression position information so there is neglible benefit to
    111   // handling MAYBE specially. Hence MAYBE is treated the same as NO.
    112   //
    113   return (!last_.source_info().is_valid() ||
    114           !current->source_info().is_valid());
    115 }
    116 
    117 namespace {
    118 
    119 void TransformLdaSmiBinaryOpToBinaryOpWithSmi(Bytecode new_bytecode,
    120                                               BytecodeNode* const last,
    121                                               BytecodeNode* const current) {
    122   DCHECK_EQ(last->bytecode(), Bytecode::kLdaSmi);
    123   current->set_bytecode(new_bytecode, last->operand(0), current->operand(0),
    124                         current->operand(1));
    125   if (last->source_info().is_valid()) {
    126     current->set_source_info(last->source_info());
    127   }
    128 }
    129 
    130 void TransformLdaZeroBinaryOpToBinaryOpWithZero(Bytecode new_bytecode,
    131                                                 BytecodeNode* const last,
    132                                                 BytecodeNode* const current) {
    133   DCHECK_EQ(last->bytecode(), Bytecode::kLdaZero);
    134   current->set_bytecode(new_bytecode, 0, current->operand(0),
    135                         current->operand(1));
    136   if (last->source_info().is_valid()) {
    137     current->set_source_info(last->source_info());
    138   }
    139 }
    140 
    141 }  // namespace
    142 
    143 void BytecodePeepholeOptimizer::DefaultAction(
    144     BytecodeNode* const node, const PeepholeActionAndData* action_data) {
    145   DCHECK(LastIsValid());
    146   DCHECK(!Bytecodes::IsJump(node->bytecode()));
    147 
    148   next_stage()->Write(last());
    149   SetLast(node);
    150 }
    151 
    152 void BytecodePeepholeOptimizer::UpdateLastAction(
    153     BytecodeNode* const node, const PeepholeActionAndData* action_data) {
    154   DCHECK(!LastIsValid());
    155   DCHECK(!Bytecodes::IsJump(node->bytecode()));
    156 
    157   SetLast(node);
    158 }
    159 
    160 void BytecodePeepholeOptimizer::UpdateLastIfSourceInfoPresentAction(
    161     BytecodeNode* const node, const PeepholeActionAndData* action_data) {
    162   DCHECK(!LastIsValid());
    163   DCHECK(!Bytecodes::IsJump(node->bytecode()));
    164 
    165   if (node->source_info().is_valid()) {
    166     SetLast(node);
    167   }
    168 }
    169 
    170 void BytecodePeepholeOptimizer::ElideCurrentAction(
    171     BytecodeNode* const node, const PeepholeActionAndData* action_data) {
    172   DCHECK(LastIsValid());
    173   DCHECK(!Bytecodes::IsJump(node->bytecode()));
    174 
    175   if (node->source_info().is_valid()) {
    176     // Preserve the source information by replacing the node bytecode
    177     // with a no op bytecode.
    178     node->set_bytecode(Bytecode::kNop);
    179     DefaultAction(node);
    180   } else {
    181     // Nothing to do, keep last and wait for next bytecode to pair with it.
    182   }
    183 }
    184 
    185 void BytecodePeepholeOptimizer::ElideCurrentIfOperand0MatchesAction(
    186     BytecodeNode* const node, const PeepholeActionAndData* action_data) {
    187   DCHECK(LastIsValid());
    188   DCHECK(!Bytecodes::IsJump(node->bytecode()));
    189 
    190   if (last()->operand(0) == node->operand(0)) {
    191     ElideCurrentAction(node);
    192   } else {
    193     DefaultAction(node);
    194   }
    195 }
    196 
    197 void BytecodePeepholeOptimizer::ElideLastAction(
    198     BytecodeNode* const node, const PeepholeActionAndData* action_data) {
    199   DCHECK(LastIsValid());
    200   DCHECK(!Bytecodes::IsJump(node->bytecode()));
    201 
    202   if (CanElideLastBasedOnSourcePosition(node)) {
    203     if (last()->source_info().is_valid()) {
    204       // |node| can not have a valid source position if the source
    205       // position of last() is valid (per rules in
    206       // CanElideLastBasedOnSourcePosition()).
    207       node->set_source_info(last()->source_info());
    208     }
    209     SetLast(node);
    210   } else {
    211     DefaultAction(node);
    212   }
    213 }
    214 
    215 void BytecodePeepholeOptimizer::ChangeBytecodeAction(
    216     BytecodeNode* const node, const PeepholeActionAndData* action_data) {
    217   DCHECK(LastIsValid());
    218   DCHECK(!Bytecodes::IsJump(node->bytecode()));
    219 
    220   node->replace_bytecode(action_data->bytecode);
    221   DefaultAction(node);
    222 }
    223 
    224 void BytecodePeepholeOptimizer::TransformLdaSmiBinaryOpToBinaryOpWithSmiAction(
    225     BytecodeNode* const node, const PeepholeActionAndData* action_data) {
    226   DCHECK(LastIsValid());
    227   DCHECK(!Bytecodes::IsJump(node->bytecode()));
    228 
    229   if (!node->source_info().is_valid() || !last()->source_info().is_valid()) {
    230     // Fused last and current into current.
    231     TransformLdaSmiBinaryOpToBinaryOpWithSmi(action_data->bytecode, last(),
    232                                              node);
    233     SetLast(node);
    234   } else {
    235     DefaultAction(node);
    236   }
    237 }
    238 
    239 void BytecodePeepholeOptimizer::
    240     TransformLdaZeroBinaryOpToBinaryOpWithZeroAction(
    241         BytecodeNode* const node, const PeepholeActionAndData* action_data) {
    242   DCHECK(LastIsValid());
    243   DCHECK(!Bytecodes::IsJump(node->bytecode()));
    244   if (!node->source_info().is_valid() || !last()->source_info().is_valid()) {
    245     // Fused last and current into current.
    246     TransformLdaZeroBinaryOpToBinaryOpWithZero(action_data->bytecode, last(),
    247                                                node);
    248     SetLast(node);
    249   } else {
    250     DefaultAction(node);
    251   }
    252 }
    253 
    254 void BytecodePeepholeOptimizer::DefaultJumpAction(
    255     BytecodeNode* const node, const PeepholeActionAndData* action_data) {
    256   DCHECK(LastIsValid());
    257   DCHECK(Bytecodes::IsJump(node->bytecode()));
    258 
    259   next_stage()->Write(last());
    260   InvalidateLast();
    261 }
    262 
    263 void BytecodePeepholeOptimizer::UpdateLastJumpAction(
    264     BytecodeNode* const node, const PeepholeActionAndData* action_data) {
    265   DCHECK(!LastIsValid());
    266   DCHECK(Bytecodes::IsJump(node->bytecode()));
    267 }
    268 
    269 void BytecodePeepholeOptimizer::ChangeJumpBytecodeAction(
    270     BytecodeNode* const node, const PeepholeActionAndData* action_data) {
    271   DCHECK(LastIsValid());
    272   DCHECK(Bytecodes::IsJump(node->bytecode()));
    273 
    274   next_stage()->Write(last());
    275   InvalidateLast();
    276   node->set_bytecode(action_data->bytecode, node->operand(0));
    277 }
    278 
    279 void BytecodePeepholeOptimizer::ElideLastBeforeJumpAction(
    280     BytecodeNode* const node, const PeepholeActionAndData* action_data) {
    281   DCHECK(LastIsValid());
    282   DCHECK(Bytecodes::IsJump(node->bytecode()));
    283 
    284   if (!CanElideLastBasedOnSourcePosition(node)) {
    285     next_stage()->Write(last());
    286   } else if (!node->source_info().is_valid()) {
    287     node->set_source_info(last()->source_info());
    288   }
    289   InvalidateLast();
    290 }
    291 
    292 void BytecodePeepholeOptimizer::ApplyPeepholeAction(BytecodeNode* const node) {
    293   // A single table is used for looking up peephole optimization
    294   // matches as it is observed to have better performance. This is
    295   // inspite of the fact that jump bytecodes and non-jump bytecodes
    296   // have different processing logic, in particular a jump bytecode
    297   // always needs to emit the jump via WriteJump().
    298   const PeepholeActionAndData* const action_data =
    299       PeepholeActionTable::Lookup(last()->bytecode(), node->bytecode());
    300   switch (action_data->action) {
    301 #define CASE(Action)              \
    302   case PeepholeAction::k##Action: \
    303     Action(node, action_data);    \
    304     break;
    305     PEEPHOLE_ACTION_LIST(CASE)
    306 #undef CASE
    307     default:
    308       UNREACHABLE();
    309       break;
    310   }
    311 }
    312 
    313 }  // namespace interpreter
    314 }  // namespace internal
    315 }  // namespace v8
    316