control-flow-builders.h 9.68 KB
Newer Older
1 2 3 4 5 6 7 8 9
// Copyright 2015 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_CONTROL_FLOW_BUILDERS_H_
#define V8_INTERPRETER_CONTROL_FLOW_BUILDERS_H_

#include "src/interpreter/bytecode-array-builder.h"

10
#include "src/ast/ast-source-ranges.h"
11
#include "src/interpreter/block-coverage-builder.h"
12
#include "src/interpreter/bytecode-label.h"
13
#include "src/zone/zone-containers.h"
14 15 16 17 18

namespace v8 {
namespace internal {
namespace interpreter {

19
class V8_EXPORT_PRIVATE ControlFlowBuilder BASE_EMBEDDED {
20 21 22 23 24 25 26 27 28 29 30 31 32 33
 public:
  explicit ControlFlowBuilder(BytecodeArrayBuilder* builder)
      : builder_(builder) {}
  virtual ~ControlFlowBuilder() {}

 protected:
  BytecodeArrayBuilder* builder() const { return builder_; }

 private:
  BytecodeArrayBuilder* builder_;

  DISALLOW_COPY_AND_ASSIGN(ControlFlowBuilder);
};

34 35
class V8_EXPORT_PRIVATE BreakableControlFlowBuilder
    : public ControlFlowBuilder {
36
 public:
37 38 39 40 41 42 43
  BreakableControlFlowBuilder(BytecodeArrayBuilder* builder,
                              BlockCoverageBuilder* block_coverage_builder,
                              AstNode* node)
      : ControlFlowBuilder(builder),
        break_labels_(builder->zone()),
        node_(node),
        block_coverage_builder_(block_coverage_builder) {}
44
  virtual ~BreakableControlFlowBuilder();
45

46
  // This method is called when visiting break statements in the AST.
47 48 49
  // Inserts a jump to an unbound label that is patched when the corresponding
  // BindBreakTarget is called.
  void Break() { EmitJump(&break_labels_); }
50 51 52 53 54 55
  void BreakIfTrue(BytecodeArrayBuilder::ToBooleanMode mode) {
    EmitJumpIfTrue(mode, &break_labels_);
  }
  void BreakIfFalse(BytecodeArrayBuilder::ToBooleanMode mode) {
    EmitJumpIfFalse(mode, &break_labels_);
  }
56 57
  void BreakIfUndefined() { EmitJumpIfUndefined(&break_labels_); }
  void BreakIfNull() { EmitJumpIfNull(&break_labels_); }
58

59
  BytecodeLabels* break_labels() { return &break_labels_; }
60

61
  void set_needs_continuation_counter() { needs_continuation_counter_ = true; }
62 63 64
  bool needs_continuation_counter() const {
    return needs_continuation_counter_;
  }
65

66 67
 protected:
  void EmitJump(BytecodeLabels* labels);
68 69 70 71
  void EmitJumpIfTrue(BytecodeArrayBuilder::ToBooleanMode mode,
                      BytecodeLabels* labels);
  void EmitJumpIfFalse(BytecodeArrayBuilder::ToBooleanMode mode,
                       BytecodeLabels* labels);
72 73
  void EmitJumpIfUndefined(BytecodeLabels* labels);
  void EmitJumpIfNull(BytecodeLabels* labels);
74

75 76 77
  // Called from the destructor to update sites that emit jumps for break.
  void BindBreakTarget();

78
  // Unbound labels that identify jumps for break statements in the code.
79
  BytecodeLabels break_labels_;
80 81 82

  // A continuation counter (for block coverage) is needed e.g. when
  // encountering a break statement.
83
  AstNode* node_;
84
  bool needs_continuation_counter_ = false;
85
  BlockCoverageBuilder* block_coverage_builder_;
86 87
};

88 89

// Class to track control flow for block statements (which can break in JS).
90 91
class V8_EXPORT_PRIVATE BlockBuilder final
    : public BreakableControlFlowBuilder {
92
 public:
93 94 95
  BlockBuilder(BytecodeArrayBuilder* builder,
               BlockCoverageBuilder* block_coverage_builder,
               BreakableStatement* statement)
96 97
      : BreakableControlFlowBuilder(builder, block_coverage_builder,
                                    statement) {}
98 99 100
};


101 102
// A class to help with co-ordinating break and continue statements with
// their loop.
103
class V8_EXPORT_PRIVATE LoopBuilder final : public BreakableControlFlowBuilder {
104
 public:
105
  LoopBuilder(BytecodeArrayBuilder* builder,
106
              BlockCoverageBuilder* block_coverage_builder, AstNode* node)
107
      : BreakableControlFlowBuilder(builder, block_coverage_builder, node),
108
        continue_labels_(builder->zone()) {
109
    if (block_coverage_builder_ != nullptr) {
110
      set_needs_continuation_counter();
111
      block_coverage_body_slot_ =
112 113
          block_coverage_builder_->AllocateBlockCoverageSlot(
              node, SourceRangeKind::kBody);
114 115
    }
  }
116 117
  ~LoopBuilder();

118
  void LoopHeader();
119
  void LoopBody();
120
  void JumpToHeader(int loop_depth);
121
  void BindContinueTarget();
122 123

  // This method is called when visiting continue statements in the AST.
124
  // Inserts a jump to an unbound label that is patched when BindContinueTarget
125
  // is called.
126 127 128 129
  void Continue() { EmitJump(&continue_labels_); }
  void ContinueIfUndefined() { EmitJumpIfUndefined(&continue_labels_); }
  void ContinueIfNull() { EmitJumpIfNull(&continue_labels_); }

130
 private:
131 132
  BytecodeLabel loop_header_;

133 134 135
  // Unbound labels that identify jumps for continue statements in the code and
  // jumps from checking the loop condition to the header for do-while loops.
  BytecodeLabels continue_labels_;
136

137
  int block_coverage_body_slot_;
138 139
};

140

141
// A class to help with co-ordinating break statements with their switch.
142 143
class V8_EXPORT_PRIVATE SwitchBuilder final
    : public BreakableControlFlowBuilder {
144
 public:
145 146 147 148
  SwitchBuilder(BytecodeArrayBuilder* builder,
                BlockCoverageBuilder* block_coverage_builder,
                SwitchStatement* statement, int number_of_cases)
      : BreakableControlFlowBuilder(builder, block_coverage_builder, statement),
149 150 151 152 153 154 155
        case_sites_(builder->zone()) {
    case_sites_.resize(number_of_cases);
  }
  ~SwitchBuilder();

  // This method should be called by the SwitchBuilder owner when the case
  // statement with |index| is emitted to update the case jump site.
156
  void SetCaseTarget(int index, CaseClause* clause);
157 158

  // This method is called when visiting case comparison operation for |index|.
159 160 161 162 163
  // Inserts a JumpIfTrue with ToBooleanMode |mode| to a unbound label that is
  // patched when the corresponding SetCaseTarget is called.
  void Case(BytecodeArrayBuilder::ToBooleanMode mode, int index) {
    builder()->JumpIfTrue(mode, &case_sites_.at(index));
  }
164 165 166 167

  // This method is called when all cases comparisons have been emitted if there
  // is a default case statement. Inserts a Jump to a unbound label that is
  // patched when the corresponding SetCaseTarget is called.
168
  void DefaultAt(int index) { builder()->Jump(&case_sites_.at(index)); }
169 170 171 172 173 174

 private:
  // Unbound labels that identify jumps for case statements in the code.
  ZoneVector<BytecodeLabel> case_sites_;
};

175 176

// A class to help with co-ordinating control flow in try-catch statements.
177
class V8_EXPORT_PRIVATE TryCatchBuilder final : public ControlFlowBuilder {
178
 public:
179
  TryCatchBuilder(BytecodeArrayBuilder* builder,
180 181
                  BlockCoverageBuilder* block_coverage_builder,
                  TryCatchStatement* statement,
182
                  HandlerTable::CatchPrediction catch_prediction)
183 184
      : ControlFlowBuilder(builder),
        handler_id_(builder->NewHandlerEntry()),
185 186 187 188 189
        catch_prediction_(catch_prediction),
        block_coverage_builder_(block_coverage_builder),
        statement_(statement) {}

  ~TryCatchBuilder();
190 191 192 193 194 195 196

  void BeginTry(Register context);
  void EndTry();
  void EndCatch();

 private:
  int handler_id_;
197
  HandlerTable::CatchPrediction catch_prediction_;
198 199
  BytecodeLabel handler_;
  BytecodeLabel exit_;
200 201 202

  BlockCoverageBuilder* block_coverage_builder_;
  TryCatchStatement* statement_;
203 204 205 206
};


// A class to help with co-ordinating control flow in try-finally statements.
207
class V8_EXPORT_PRIVATE TryFinallyBuilder final : public ControlFlowBuilder {
208
 public:
209
  TryFinallyBuilder(BytecodeArrayBuilder* builder,
210 211
                    BlockCoverageBuilder* block_coverage_builder,
                    TryFinallyStatement* statement,
212
                    HandlerTable::CatchPrediction catch_prediction)
213 214
      : ControlFlowBuilder(builder),
        handler_id_(builder->NewHandlerEntry()),
215
        catch_prediction_(catch_prediction),
216 217 218 219 220
        finalization_sites_(builder->zone()),
        block_coverage_builder_(block_coverage_builder),
        statement_(statement) {}

  ~TryFinallyBuilder();
221 222

  void BeginTry(Register context);
223
  void LeaveTry();
224
  void EndTry();
225 226
  void BeginHandler();
  void BeginFinally();
227 228 229 230
  void EndFinally();

 private:
  int handler_id_;
231
  HandlerTable::CatchPrediction catch_prediction_;
232
  BytecodeLabel handler_;
233 234

  // Unbound labels that identify jumps to the finally block in the code.
235
  BytecodeLabels finalization_sites_;
236 237 238

  BlockCoverageBuilder* block_coverage_builder_;
  TryFinallyStatement* statement_;
239 240
};

241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283
class V8_EXPORT_PRIVATE ConditionalControlFlowBuilder final
    : public ControlFlowBuilder {
 public:
  ConditionalControlFlowBuilder(BytecodeArrayBuilder* builder,
                                BlockCoverageBuilder* block_coverage_builder,
                                AstNode* node)
      : ControlFlowBuilder(builder),
        end_labels_(builder->zone()),
        then_labels_(builder->zone()),
        else_labels_(builder->zone()),
        node_(node),
        block_coverage_builder_(block_coverage_builder) {
    DCHECK(node->IsIfStatement() || node->IsConditional());
    if (block_coverage_builder != nullptr) {
      block_coverage_then_slot_ =
          block_coverage_builder->AllocateBlockCoverageSlot(
              node, SourceRangeKind::kThen);
      block_coverage_else_slot_ =
          block_coverage_builder->AllocateBlockCoverageSlot(
              node, SourceRangeKind::kElse);
    }
  }
  ~ConditionalControlFlowBuilder();

  BytecodeLabels* then_labels() { return &then_labels_; }
  BytecodeLabels* else_labels() { return &else_labels_; }

  void Then();
  void Else();

  void JumpToEnd();

 private:
  BytecodeLabels end_labels_;
  BytecodeLabels then_labels_;
  BytecodeLabels else_labels_;

  AstNode* node_;
  int block_coverage_then_slot_;
  int block_coverage_else_slot_;
  BlockCoverageBuilder* block_coverage_builder_;
};

284 285 286 287 288
}  // namespace interpreter
}  // namespace internal
}  // namespace v8

#endif  // V8_INTERPRETER_CONTROL_FLOW_BUILDERS_H_