summaryrefslogtreecommitdiff
path: root/deps/v8/src/torque/cfg.h
blob: 67b64bbc04a0d4cec5b5e7c8d007ebd3f4c54f8b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
// Copyright 2018 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_TORQUE_CFG_H_
#define V8_TORQUE_CFG_H_

#include <list>
#include <memory>
#include <unordered_map>
#include <vector>

#include "src/torque/ast.h"
#include "src/torque/instructions.h"
#include "src/torque/source-positions.h"
#include "src/torque/types.h"

namespace v8 {
namespace internal {
namespace torque {

class ControlFlowGraph;

class Block {
 public:
  explicit Block(ControlFlowGraph* cfg, size_t id,
                 base::Optional<Stack<const Type*>> input_types,
                 bool is_deferred)
      : cfg_(cfg),
        input_types_(std::move(input_types)),
        id_(id),
        is_deferred_(is_deferred) {}
  void Add(Instruction instruction) {
    DCHECK(!IsComplete());
    instructions_.push_back(std::move(instruction));
  }

  bool HasInputTypes() const { return input_types_ != base::nullopt; }
  const Stack<const Type*>& InputTypes() const { return *input_types_; }
  void SetInputTypes(const Stack<const Type*>& input_types);
  void Retype() {
    Stack<const Type*> current_stack = InputTypes();
    for (const Instruction& instruction : instructions()) {
      instruction.TypeInstruction(&current_stack, cfg_);
    }
  }

  const std::vector<Instruction>& instructions() const { return instructions_; }
  bool IsComplete() const {
    return !instructions_.empty() && instructions_.back()->IsBlockTerminator();
  }
  size_t id() const { return id_; }
  bool IsDeferred() const { return is_deferred_; }

 private:
  ControlFlowGraph* cfg_;
  std::vector<Instruction> instructions_;
  base::Optional<Stack<const Type*>> input_types_;
  const size_t id_;
  bool is_deferred_;
};

class ControlFlowGraph {
 public:
  explicit ControlFlowGraph(Stack<const Type*> input_types) {
    start_ = NewBlock(std::move(input_types), false);
    PlaceBlock(start_);
  }

  Block* NewBlock(base::Optional<Stack<const Type*>> input_types,
                  bool is_deferred) {
    blocks_.emplace_back(this, next_block_id_++, std::move(input_types),
                         is_deferred);
    return &blocks_.back();
  }
  void PlaceBlock(Block* block) { placed_blocks_.push_back(block); }
  Block* start() const { return start_; }
  base::Optional<Block*> end() const { return end_; }
  void set_end(Block* end) { end_ = end; }
  void SetReturnType(const Type* t) {
    if (!return_type_) {
      return_type_ = t;
      return;
    }
    if (t != *return_type_) {
      ReportError("expected return type ", **return_type_, " instead of ", *t);
    }
  }
  const std::vector<Block*>& blocks() const { return placed_blocks_; }

 private:
  std::list<Block> blocks_;
  Block* start_;
  std::vector<Block*> placed_blocks_;
  base::Optional<Block*> end_;
  base::Optional<const Type*> return_type_;
  size_t next_block_id_ = 0;
};

class CfgAssembler {
 public:
  explicit CfgAssembler(Stack<const Type*> input_types)
      : current_stack_(std::move(input_types)), cfg_(current_stack_) {}

  const ControlFlowGraph& Result() {
    if (!CurrentBlockIsComplete()) {
      cfg_.set_end(current_block_);
    }
    return cfg_;
  }

  Block* NewBlock(
      base::Optional<Stack<const Type*>> input_types = base::nullopt,
      bool is_deferred = false) {
    return cfg_.NewBlock(std::move(input_types), is_deferred);
  }

  bool CurrentBlockIsComplete() const { return current_block_->IsComplete(); }

  void Emit(Instruction instruction) {
    instruction.TypeInstruction(&current_stack_, &cfg_);
    current_block_->Add(std::move(instruction));
  }

  const Stack<const Type*>& CurrentStack() const { return current_stack_; }

  StackRange TopRange(size_t slot_count) const {
    return CurrentStack().TopRange(slot_count);
  }

  void Bind(Block* block);
  void Goto(Block* block);
  // Goto block while keeping {preserved_slots} many slots on the top and
  // deleting additional the slots below these to match the input type of the
  // target block.
  // Returns the StackRange of the preserved slots in the target block.
  StackRange Goto(Block* block, size_t preserved_slots);
  // The condition must be of type bool and on the top of stack. It is removed
  // from the stack before branching.
  void Branch(Block* if_true, Block* if_false);
  // Delete the specified range of slots, moving upper slots to fill the gap.
  void DeleteRange(StackRange range);
  void DropTo(BottomOffset new_level);
  StackRange Peek(StackRange range, base::Optional<const Type*> type);
  void Poke(StackRange destination, StackRange origin,
            base::Optional<const Type*> type);
  void Print(std::string s);
  void AssertionFailure(std::string message);
  void Unreachable();
  void DebugBreak();

  void PrintCurrentStack(std::ostream& s) { s << "stack: " << current_stack_; }

 private:
  friend class CfgAssemblerScopedTemporaryBlock;
  Stack<const Type*> current_stack_;
  ControlFlowGraph cfg_;
  Block* current_block_ = cfg_.start();
};

class CfgAssemblerScopedTemporaryBlock {
 public:
  CfgAssemblerScopedTemporaryBlock(CfgAssembler* assembler, Block* block)
      : assembler_(assembler), saved_block_(block) {
    saved_stack_ = block->InputTypes();
    DCHECK(!assembler->CurrentBlockIsComplete());
    std::swap(saved_block_, assembler->current_block_);
    std::swap(saved_stack_, assembler->current_stack_);
    assembler->cfg_.PlaceBlock(block);
  }

  ~CfgAssemblerScopedTemporaryBlock() {
    DCHECK(assembler_->CurrentBlockIsComplete());
    std::swap(saved_block_, assembler_->current_block_);
    std::swap(saved_stack_, assembler_->current_stack_);
  }

 private:
  CfgAssembler* assembler_;
  Stack<const Type*> saved_stack_;
  Block* saved_block_;
};

}  // namespace torque
}  // namespace internal
}  // namespace v8

#endif  // V8_TORQUE_CFG_H_