control-flow-builders.h 10.4 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-generator.h"
13
#include "src/interpreter/bytecode-label.h"
14
#include "src/zone/zone-containers.h"
15 16 17 18 19

namespace v8 {
namespace internal {
namespace interpreter {

20
class V8_EXPORT_PRIVATE ControlFlowBuilder {
21 22 23
 public:
  explicit ControlFlowBuilder(BytecodeArrayBuilder* builder)
      : builder_(builder) {}
24 25
  ControlFlowBuilder(const ControlFlowBuilder&) = delete;
  ControlFlowBuilder& operator=(const ControlFlowBuilder&) = delete;
26
  virtual ~ControlFlowBuilder() = default;
27 28 29 30 31 32 33 34

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

 private:
  BytecodeArrayBuilder* builder_;
};

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

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

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

62 63
 protected:
  void EmitJump(BytecodeLabels* labels);
64 65 66 67
  void EmitJumpIfTrue(BytecodeArrayBuilder::ToBooleanMode mode,
                      BytecodeLabels* labels);
  void EmitJumpIfFalse(BytecodeArrayBuilder::ToBooleanMode mode,
                       BytecodeLabels* labels);
68 69
  void EmitJumpIfUndefined(BytecodeLabels* labels);
  void EmitJumpIfNull(BytecodeLabels* labels);
70

71 72 73
  // Called from the destructor to update sites that emit jumps for break.
  void BindBreakTarget();

74
  // Unbound labels that identify jumps for break statements in the code.
75
  BytecodeLabels break_labels_;
76 77 78

  // A continuation counter (for block coverage) is needed e.g. when
  // encountering a break statement.
79 80
  AstNode* node_;
  BlockCoverageBuilder* block_coverage_builder_;
81 82
};

83
// Class to track control flow for block statements (which can break in JS).
84 85
class V8_EXPORT_PRIVATE BlockBuilder final
    : public BreakableControlFlowBuilder {
86
 public:
87 88 89
  BlockBuilder(BytecodeArrayBuilder* builder,
               BlockCoverageBuilder* block_coverage_builder,
               BreakableStatement* statement)
90 91
      : BreakableControlFlowBuilder(builder, block_coverage_builder,
                                    statement) {}
92 93
};

94 95
// A class to help with co-ordinating break and continue statements with
// their loop.
96
class V8_EXPORT_PRIVATE LoopBuilder final : public BreakableControlFlowBuilder {
97
 public:
98
  LoopBuilder(BytecodeArrayBuilder* builder,
99
              BlockCoverageBuilder* block_coverage_builder, AstNode* node)
100
      : BreakableControlFlowBuilder(builder, block_coverage_builder, node),
101 102
        continue_labels_(builder->zone()),
        end_labels_(builder->zone()) {
103 104
    if (block_coverage_builder_ != nullptr) {
      block_coverage_body_slot_ =
105 106
          block_coverage_builder_->AllocateBlockCoverageSlot(
              node, SourceRangeKind::kBody);
107
    }
108
    source_position_ = node ? node->position() : kNoSourcePosition;
109
  }
110
  ~LoopBuilder() override;
111

112
  void LoopHeader();
113
  void LoopBody();
114
  void JumpToHeader(int loop_depth, LoopBuilder* const parent_loop);
115
  void BindContinueTarget();
116 117

  // This method is called when visiting continue statements in the AST.
118
  // Inserts a jump to an unbound label that is patched when BindContinueTarget
119
  // is called.
120 121 122 123
  void Continue() { EmitJump(&continue_labels_); }
  void ContinueIfUndefined() { EmitJumpIfUndefined(&continue_labels_); }
  void ContinueIfNull() { EmitJumpIfNull(&continue_labels_); }

124
 private:
125 126 127 128 129 130
  // Emit a Jump to our parent_loop_'s end label which could be a JumpLoop or,
  // iff they are a nested inner loop with the same loop header bytecode offset
  // as their parent's, a Jump to its parent's end label.
  void JumpToLoopEnd() { EmitJump(&end_labels_); }
  void BindLoopEnd();

131
  BytecodeLoopHeader loop_header_;
132

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 138 139 140 141 142
  // Unbound labels that identify jumps for nested inner loops which share the
  // same header offset as this loop. Said inner loops will Jump to our end
  // label, which could be a JumpLoop or, iff we are a nested inner loop too, a
  // Jump to our parent's end label.
  BytecodeLabels end_labels_;

143
  int block_coverage_body_slot_;
144 145

  int source_position_;
146 147 148
};

// A class to help with co-ordinating break statements with their switch.
149 150
class V8_EXPORT_PRIVATE SwitchBuilder final
    : public BreakableControlFlowBuilder {
151
 public:
152 153 154 155
  SwitchBuilder(BytecodeArrayBuilder* builder,
                BlockCoverageBuilder* block_coverage_builder,
                SwitchStatement* statement, int number_of_cases)
      : BreakableControlFlowBuilder(builder, block_coverage_builder, statement),
156 157 158
        case_sites_(builder->zone()) {
    case_sites_.resize(number_of_cases);
  }
159
  ~SwitchBuilder() override;  // NOLINT (modernize-use-equals-default)
160 161 162

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

  // This method is called when visiting case comparison operation for |index|.
166 167 168 169 170
  // 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));
  }
171 172 173 174

  // 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.
175
  void DefaultAt(int index) { builder()->Jump(&case_sites_.at(index)); }
176 177 178 179 180 181

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

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

195
  ~TryCatchBuilder() override;
196 197 198 199 200 201 202

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

 private:
  int handler_id_;
203
  HandlerTable::CatchPrediction catch_prediction_;
204
  BytecodeLabel exit_;
205 206 207

  BlockCoverageBuilder* block_coverage_builder_;
  TryCatchStatement* statement_;
208 209 210
};

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

224
  ~TryFinallyBuilder() override;
225 226

  void BeginTry(Register context);
227
  void LeaveTry();
228
  void EndTry();
229 230
  void BeginHandler();
  void BeginFinally();
231 232 233 234
  void EndFinally();

 private:
  int handler_id_;
235
  HandlerTable::CatchPrediction catch_prediction_;
236
  BytecodeLabel handler_;
237 238

  // Unbound labels that identify jumps to the finally block in the code.
239
  BytecodeLabels finalization_sites_;
240 241 242

  BlockCoverageBuilder* block_coverage_builder_;
  TryFinallyStatement* statement_;
243 244
};

245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266
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);
    }
  }
267
  ~ConditionalControlFlowBuilder() override;
268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287

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

288 289 290 291 292
}  // namespace interpreter
}  // namespace internal
}  // namespace v8

#endif  // V8_INTERPRETER_CONTROL_FLOW_BUILDERS_H_