bytecode-peephole-optimizer.cc 11.3 KB
Newer Older
1
// Copyright 2016 the V8 project authors. All rights reserved.
2 3 4 5 6 7 8 9 10 11 12 13 14 15
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "src/interpreter/bytecode-peephole-optimizer.h"

#include "src/objects-inl.h"
#include "src/objects.h"

namespace v8 {
namespace internal {
namespace interpreter {

BytecodePeepholeOptimizer::BytecodePeepholeOptimizer(
    BytecodePipelineStage* next_stage)
16 17
    : next_stage_(next_stage),
      last_(BytecodeNode::Illegal(BytecodeSourceInfo())) {
18
  InvalidateLast();
19 20
}

21 22
// override
Handle<BytecodeArray> BytecodePeepholeOptimizer::ToBytecodeArray(
23
    Isolate* isolate, int register_count, int parameter_count,
24 25
    Handle<FixedArray> handler_table) {
  Flush();
26 27
  return next_stage_->ToBytecodeArray(isolate, register_count, parameter_count,
                                      handler_table);
28
}
29

30
// override
31 32 33
void BytecodePeepholeOptimizer::BindLabel(BytecodeLabel* label) {
  Flush();
  next_stage_->BindLabel(label);
34
}
35

36
// override
37
void BytecodePeepholeOptimizer::BindLabel(const BytecodeLabel& target,
38
                                          BytecodeLabel* label) {
39 40 41
  // There is no need to flush here, it will have been flushed when
  // |target| was bound.
  next_stage_->BindLabel(target, label);
42 43 44
}

// override
45 46 47 48 49 50 51
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);
52 53 54
}

// override
55 56 57 58
void BytecodePeepholeOptimizer::Write(BytecodeNode* node) {
  // Handlers for non-jump bytecodes run to completion emitting
  // bytecode to next stage as appropriate.
  ApplyPeepholeAction(node);
59 60 61
}

void BytecodePeepholeOptimizer::Flush() {
62 63 64 65 66 67
  if (LastIsValid()) {
    next_stage_->Write(&last_);
    InvalidateLast();
  }
}

68
void BytecodePeepholeOptimizer::InvalidateLast() {
69
  last_ = BytecodeNode::Illegal(BytecodeSourceInfo());
70
}
71

72 73 74 75 76
bool BytecodePeepholeOptimizer::LastIsValid() const {
  return last_.bytecode() != Bytecode::kIllegal;
}

void BytecodePeepholeOptimizer::SetLast(const BytecodeNode* const node) {
77 78 79 80
  // 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());
81
  last_ = *node;
82 83
}

84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105
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.
  //
106
  // The last bytecode could be elided for the MAYBE cases if the last
107 108
  // bytecode is known not to throw. If it throws, the system would
  // not have correct stack trace information. The appropriate check
109 110 111 112
  // 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.
113 114 115 116 117
  //
  return (!last_.source_info().is_valid() ||
          !current->source_info().is_valid());
}

118 119
namespace {

120 121 122
BytecodeNode TransformLdaSmiBinaryOpToBinaryOpWithSmi(
    Bytecode new_bytecode, BytecodeNode* const last,
    BytecodeNode* const current) {
123
  DCHECK_EQ(last->bytecode(), Bytecode::kLdaSmi);
124 125
  BytecodeNode node(new_bytecode, last->operand(0), current->operand(0),
                    current->operand(1), current->source_info());
126
  if (last->source_info().is_valid()) {
127
    node.set_source_info(last->source_info());
128
  }
129
  return node;
130 131
}

132 133 134
BytecodeNode TransformLdaZeroBinaryOpToBinaryOpWithZero(
    Bytecode new_bytecode, BytecodeNode* const last,
    BytecodeNode* const current) {
135
  DCHECK_EQ(last->bytecode(), Bytecode::kLdaZero);
136 137
  BytecodeNode node(new_bytecode, 0, current->operand(0), current->operand(1),
                    current->source_info());
138
  if (last->source_info().is_valid()) {
139
    node.set_source_info(last->source_info());
140
  }
141
  return node;
142 143
}

144 145 146
BytecodeNode TransformEqualityWithNullOrUndefined(Bytecode new_bytecode,
                                                  BytecodeNode* const last,
                                                  BytecodeNode* const current) {
147 148
  DCHECK((last->bytecode() == Bytecode::kLdaNull) ||
         (last->bytecode() == Bytecode::kLdaUndefined));
149 150 151
  DCHECK((current->bytecode() == Bytecode::kTestEqual) ||
         (current->bytecode() == Bytecode::kTestEqualStrict));
  BytecodeNode node(new_bytecode, current->operand(0), current->source_info());
152
  if (last->source_info().is_valid()) {
153
    node.set_source_info(last->source_info());
154
  }
155
  return node;
156 157
}

158 159
}  // namespace

160 161 162 163
void BytecodePeepholeOptimizer::DefaultAction(
    BytecodeNode* const node, const PeepholeActionAndData* action_data) {
  DCHECK(LastIsValid());
  DCHECK(!Bytecodes::IsJump(node->bytecode()));
164

165 166
  next_stage()->Write(last());
  SetLast(node);
167 168
}

169 170 171 172 173 174
void BytecodePeepholeOptimizer::UpdateLastAction(
    BytecodeNode* const node, const PeepholeActionAndData* action_data) {
  DCHECK(!LastIsValid());
  DCHECK(!Bytecodes::IsJump(node->bytecode()));

  SetLast(node);
175 176
}

177 178 179 180 181 182 183
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);
184
  }
185 186
}

187 188 189 190 191 192 193 194
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.
195 196
    BytecodeNode new_node(BytecodeNode::Nop(node->source_info()));
    DefaultAction(&new_node);
197 198 199
  } else {
    // Nothing to do, keep last and wait for next bytecode to pair with it.
  }
200 201
}

202 203 204 205 206 207 208
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);
209
  } else {
210
    DefaultAction(node);
211
  }
212
}
213

214 215 216 217 218 219 220 221 222 223
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()).
224
      node->set_source_info(last()->source_info());
225
    }
226 227 228
    SetLast(node);
  } else {
    DefaultAction(node);
229
  }
230
}
231

232 233 234 235 236
void BytecodePeepholeOptimizer::ChangeBytecodeAction(
    BytecodeNode* const node, const PeepholeActionAndData* action_data) {
  DCHECK(LastIsValid());
  DCHECK(!Bytecodes::IsJump(node->bytecode()));

237
  node->replace_bytecode(action_data->bytecode);
238 239 240 241 242 243 244
  DefaultAction(node);
}

void BytecodePeepholeOptimizer::TransformLdaSmiBinaryOpToBinaryOpWithSmiAction(
    BytecodeNode* const node, const PeepholeActionAndData* action_data) {
  DCHECK(LastIsValid());
  DCHECK(!Bytecodes::IsJump(node->bytecode()));
245

246 247
  if (!node->source_info().is_valid() || !last()->source_info().is_valid()) {
    // Fused last and current into current.
248 249 250
    BytecodeNode new_node(TransformLdaSmiBinaryOpToBinaryOpWithSmi(
        action_data->bytecode, last(), node));
    SetLast(&new_node);
251 252 253
  } else {
    DefaultAction(node);
  }
254 255
}

256 257 258 259 260 261 262
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.
263 264 265
    BytecodeNode new_node(TransformLdaZeroBinaryOpToBinaryOpWithZero(
        action_data->bytecode, last(), node));
    SetLast(&new_node);
266 267 268 269
  } else {
    DefaultAction(node);
  }
}
270

271 272
void BytecodePeepholeOptimizer::TransformEqualityWithNullOrUndefinedAction(
    BytecodeNode* const node, const PeepholeActionAndData* action_data) {
273 274 275
  DCHECK(LastIsValid());
  DCHECK(!Bytecodes::IsJump(node->bytecode()));
  // Fused last and current into current.
276 277
  BytecodeNode new_node(TransformEqualityWithNullOrUndefined(
      action_data->bytecode, last(), node));
278
  SetLast(&new_node);
279 280
}

281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302
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();
303
  node->replace_bytecode(action_data->bytecode);
304 305 306 307 308 309 310
}

void BytecodePeepholeOptimizer::ElideLastBeforeJumpAction(
    BytecodeNode* const node, const PeepholeActionAndData* action_data) {
  DCHECK(LastIsValid());
  DCHECK(Bytecodes::IsJump(node->bytecode()));

311
  if (!CanElideLastBasedOnSourcePosition(node)) {
312
    next_stage()->Write(last());
313
  } else if (!node->source_info().is_valid()) {
314
    node->set_source_info(last()->source_info());
315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336
  }
  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;
337 338 339
  }
}

340 341 342
}  // namespace interpreter
}  // namespace internal
}  // namespace v8