xref: /third_party/node/deps/v8/src/torque/cfg.h (revision 1cb0ef41)
1// Copyright 2018 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#ifndef V8_TORQUE_CFG_H_
6#define V8_TORQUE_CFG_H_
7
8#include <list>
9#include <memory>
10#include <unordered_map>
11#include <vector>
12
13#include "src/torque/ast.h"
14#include "src/torque/instructions.h"
15#include "src/torque/source-positions.h"
16#include "src/torque/types.h"
17
18namespace v8 {
19namespace internal {
20namespace torque {
21
22class ControlFlowGraph;
23
24class Block {
25 public:
26  explicit Block(ControlFlowGraph* cfg, size_t id,
27                 base::Optional<Stack<const Type*>> input_types,
28                 bool is_deferred)
29      : cfg_(cfg),
30        input_types_(std::move(input_types)),
31        id_(id),
32        is_deferred_(is_deferred) {}
33  void Add(Instruction instruction) {
34    DCHECK(!IsComplete());
35    instructions_.push_back(std::move(instruction));
36  }
37
38  bool HasInputTypes() const { return input_types_ != base::nullopt; }
39  const Stack<const Type*>& InputTypes() const { return *input_types_; }
40  void SetInputTypes(const Stack<const Type*>& input_types);
41  void Retype() {
42    Stack<const Type*> current_stack = InputTypes();
43    for (const Instruction& instruction : instructions()) {
44      instruction.TypeInstruction(&current_stack, cfg_);
45    }
46  }
47
48  std::vector<Instruction>& instructions() { return instructions_; }
49  const std::vector<Instruction>& instructions() const { return instructions_; }
50  bool IsComplete() const {
51    return !instructions_.empty() && instructions_.back()->IsBlockTerminator();
52  }
53  size_t id() const { return id_; }
54  bool IsDeferred() const { return is_deferred_; }
55
56  void MergeInputDefinitions(const Stack<DefinitionLocation>& input_definitions,
57                             Worklist<Block*>* worklist) {
58    if (!input_definitions_) {
59      input_definitions_ = input_definitions;
60      if (worklist) worklist->Enqueue(this);
61      return;
62    }
63
64    DCHECK_EQ(input_definitions_->Size(), input_definitions.Size());
65    bool changed = false;
66    for (BottomOffset i = {0}; i < input_definitions.AboveTop(); ++i) {
67      auto& current = input_definitions_->Peek(i);
68      auto& input = input_definitions.Peek(i);
69      if (current == input) continue;
70      if (current == DefinitionLocation::Phi(this, i.offset)) continue;
71      input_definitions_->Poke(i, DefinitionLocation::Phi(this, i.offset));
72      changed = true;
73    }
74
75    if (changed && worklist) worklist->Enqueue(this);
76  }
77  bool HasInputDefinitions() const {
78    return input_definitions_ != base::nullopt;
79  }
80  const Stack<DefinitionLocation>& InputDefinitions() const {
81    DCHECK(HasInputDefinitions());
82    return *input_definitions_;
83  }
84
85  bool IsDead() const { return !HasInputDefinitions(); }
86
87 private:
88  ControlFlowGraph* cfg_;
89  std::vector<Instruction> instructions_;
90  base::Optional<Stack<const Type*>> input_types_;
91  base::Optional<Stack<DefinitionLocation>> input_definitions_;
92  const size_t id_;
93  bool is_deferred_;
94};
95
96class ControlFlowGraph {
97 public:
98  explicit ControlFlowGraph(Stack<const Type*> input_types) {
99    start_ = NewBlock(std::move(input_types), false);
100    PlaceBlock(start_);
101  }
102
103  Block* NewBlock(base::Optional<Stack<const Type*>> input_types,
104                  bool is_deferred) {
105    blocks_.emplace_back(this, next_block_id_++, std::move(input_types),
106                         is_deferred);
107    return &blocks_.back();
108  }
109  void PlaceBlock(Block* block) { placed_blocks_.push_back(block); }
110  template <typename UnaryPredicate>
111  void UnplaceBlockIf(UnaryPredicate&& predicate) {
112    auto newEnd = std::remove_if(placed_blocks_.begin(), placed_blocks_.end(),
113                                 std::forward<UnaryPredicate>(predicate));
114    placed_blocks_.erase(newEnd, placed_blocks_.end());
115  }
116  Block* start() const { return start_; }
117  base::Optional<Block*> end() const { return end_; }
118  void set_end(Block* end) { end_ = end; }
119  void SetReturnType(TypeVector t) {
120    if (!return_type_) {
121      return_type_ = t;
122      return;
123    }
124    if (t != *return_type_) {
125      std::stringstream message;
126      message << "expected return type ";
127      PrintCommaSeparatedList(message, *return_type_);
128      message << " instead of ";
129      PrintCommaSeparatedList(message, t);
130      ReportError(message.str());
131    }
132  }
133  const std::vector<Block*>& blocks() const { return placed_blocks_; }
134  size_t NumberOfBlockIds() const { return next_block_id_; }
135  std::size_t ParameterCount() const {
136    return start_ ? start_->InputTypes().Size() : 0;
137  }
138
139 private:
140  std::list<Block> blocks_;
141  Block* start_;
142  std::vector<Block*> placed_blocks_;
143  base::Optional<Block*> end_;
144  base::Optional<TypeVector> return_type_;
145  size_t next_block_id_ = 0;
146};
147
148class CfgAssembler {
149 public:
150  explicit CfgAssembler(Stack<const Type*> input_types)
151      : current_stack_(std::move(input_types)), cfg_(current_stack_) {}
152
153  const ControlFlowGraph& Result() {
154    if (!CurrentBlockIsComplete()) {
155      cfg_.set_end(current_block_);
156    }
157    OptimizeCfg();
158    DCHECK(CfgIsComplete());
159    ComputeInputDefinitions();
160    return cfg_;
161  }
162
163  Block* NewBlock(
164      base::Optional<Stack<const Type*>> input_types = base::nullopt,
165      bool is_deferred = false) {
166    return cfg_.NewBlock(std::move(input_types), is_deferred);
167  }
168
169  bool CurrentBlockIsComplete() const { return current_block_->IsComplete(); }
170  bool CfgIsComplete() const {
171    return std::all_of(
172        cfg_.blocks().begin(), cfg_.blocks().end(), [this](Block* block) {
173          return (cfg_.end() && *cfg_.end() == block) || block->IsComplete();
174        });
175  }
176
177  void Emit(Instruction instruction) {
178    instruction.TypeInstruction(&current_stack_, &cfg_);
179    current_block_->Add(std::move(instruction));
180  }
181
182  const Stack<const Type*>& CurrentStack() const { return current_stack_; }
183
184  StackRange TopRange(size_t slot_count) const {
185    return CurrentStack().TopRange(slot_count);
186  }
187
188  void Bind(Block* block);
189  void Goto(Block* block);
190  // Goto block while keeping {preserved_slots} many slots on the top and
191  // deleting additional the slots below these to match the input type of the
192  // target block.
193  // Returns the StackRange of the preserved slots in the target block.
194  StackRange Goto(Block* block, size_t preserved_slots);
195  // The condition must be of type bool and on the top of stack. It is removed
196  // from the stack before branching.
197  void Branch(Block* if_true, Block* if_false);
198  // Delete the specified range of slots, moving upper slots to fill the gap.
199  void DeleteRange(StackRange range);
200  void DropTo(BottomOffset new_level);
201  StackRange Peek(StackRange range, base::Optional<const Type*> type);
202  void Poke(StackRange destination, StackRange origin,
203            base::Optional<const Type*> type);
204  void Print(std::string s);
205  void AssertionFailure(std::string message);
206  void Unreachable();
207  void DebugBreak();
208
209  void PrintCurrentStack(std::ostream& s) { s << "stack: " << current_stack_; }
210  void OptimizeCfg();
211  void ComputeInputDefinitions();
212
213 private:
214  friend class CfgAssemblerScopedTemporaryBlock;
215  Stack<const Type*> current_stack_;
216  ControlFlowGraph cfg_;
217  Block* current_block_ = cfg_.start();
218};
219
220class V8_NODISCARD CfgAssemblerScopedTemporaryBlock {
221 public:
222  CfgAssemblerScopedTemporaryBlock(CfgAssembler* assembler, Block* block)
223      : assembler_(assembler), saved_block_(block) {
224    saved_stack_ = block->InputTypes();
225    DCHECK(!assembler->CurrentBlockIsComplete());
226    std::swap(saved_block_, assembler->current_block_);
227    std::swap(saved_stack_, assembler->current_stack_);
228    assembler->cfg_.PlaceBlock(block);
229  }
230
231  ~CfgAssemblerScopedTemporaryBlock() {
232    DCHECK(assembler_->CurrentBlockIsComplete());
233    std::swap(saved_block_, assembler_->current_block_);
234    std::swap(saved_stack_, assembler_->current_stack_);
235  }
236
237 private:
238  CfgAssembler* assembler_;
239  Stack<const Type*> saved_stack_;
240  Block* saved_block_;
241};
242
243}  // namespace torque
244}  // namespace internal
245}  // namespace v8
246
247#endif  // V8_TORQUE_CFG_H_
248