blob: 4cacb58edb3a649219c7032ff08b0624e3ef6aa3 [file] [log] [blame]
Kaido Kertf309f9a2021-04-30 12:09:15 -07001// 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(const Type* t) {
120 if (!return_type_) {
121 return_type_ = t;
122 return;
123 }
124 if (t != *return_type_) {
125 ReportError("expected return type ", **return_type_, " instead of ", *t);
126 }
127 }
128 const std::vector<Block*>& blocks() const { return placed_blocks_; }
129 size_t NumberOfBlockIds() const { return next_block_id_; }
130 std::size_t ParameterCount() const {
131 return start_ ? start_->InputTypes().Size() : 0;
132 }
133
134 private:
135 std::list<Block> blocks_;
136 Block* start_;
137 std::vector<Block*> placed_blocks_;
138 base::Optional<Block*> end_;
139 base::Optional<const Type*> return_type_;
140 size_t next_block_id_ = 0;
141};
142
143class CfgAssembler {
144 public:
145 explicit CfgAssembler(Stack<const Type*> input_types)
146 : current_stack_(std::move(input_types)), cfg_(current_stack_) {}
147
148 const ControlFlowGraph& Result() {
149 if (!CurrentBlockIsComplete()) {
150 cfg_.set_end(current_block_);
151 }
152 OptimizeCfg();
153 DCHECK(CfgIsComplete());
154 ComputeInputDefinitions();
155 return cfg_;
156 }
157
158 Block* NewBlock(
159 base::Optional<Stack<const Type*>> input_types = base::nullopt,
160 bool is_deferred = false) {
161 return cfg_.NewBlock(std::move(input_types), is_deferred);
162 }
163
164 bool CurrentBlockIsComplete() const { return current_block_->IsComplete(); }
165 bool CfgIsComplete() const {
166 return std::all_of(
167 cfg_.blocks().begin(), cfg_.blocks().end(), [this](Block* block) {
168 return (cfg_.end() && *cfg_.end() == block) || block->IsComplete();
169 });
170 }
171
172 void Emit(Instruction instruction) {
173 instruction.TypeInstruction(&current_stack_, &cfg_);
174 current_block_->Add(std::move(instruction));
175 }
176
177 const Stack<const Type*>& CurrentStack() const { return current_stack_; }
178
179 StackRange TopRange(size_t slot_count) const {
180 return CurrentStack().TopRange(slot_count);
181 }
182
183 void Bind(Block* block);
184 void Goto(Block* block);
185 // Goto block while keeping {preserved_slots} many slots on the top and
186 // deleting additional the slots below these to match the input type of the
187 // target block.
188 // Returns the StackRange of the preserved slots in the target block.
189 StackRange Goto(Block* block, size_t preserved_slots);
190 // The condition must be of type bool and on the top of stack. It is removed
191 // from the stack before branching.
192 void Branch(Block* if_true, Block* if_false);
193 // Delete the specified range of slots, moving upper slots to fill the gap.
194 void DeleteRange(StackRange range);
195 void DropTo(BottomOffset new_level);
196 StackRange Peek(StackRange range, base::Optional<const Type*> type);
197 void Poke(StackRange destination, StackRange origin,
198 base::Optional<const Type*> type);
199 void Print(std::string s);
200 void AssertionFailure(std::string message);
201 void Unreachable();
202 void DebugBreak();
203
204 void PrintCurrentStack(std::ostream& s) { s << "stack: " << current_stack_; }
205 void OptimizeCfg();
206 void ComputeInputDefinitions();
207
208 private:
209 friend class CfgAssemblerScopedTemporaryBlock;
210 Stack<const Type*> current_stack_;
211 ControlFlowGraph cfg_;
212 Block* current_block_ = cfg_.start();
213};
214
215class CfgAssemblerScopedTemporaryBlock {
216 public:
217 CfgAssemblerScopedTemporaryBlock(CfgAssembler* assembler, Block* block)
218 : assembler_(assembler), saved_block_(block) {
219 saved_stack_ = block->InputTypes();
220 DCHECK(!assembler->CurrentBlockIsComplete());
221 std::swap(saved_block_, assembler->current_block_);
222 std::swap(saved_stack_, assembler->current_stack_);
223 assembler->cfg_.PlaceBlock(block);
224 }
225
226 ~CfgAssemblerScopedTemporaryBlock() {
227 DCHECK(assembler_->CurrentBlockIsComplete());
228 std::swap(saved_block_, assembler_->current_block_);
229 std::swap(saved_stack_, assembler_->current_stack_);
230 }
231
232 private:
233 CfgAssembler* assembler_;
234 Stack<const Type*> saved_stack_;
235 Block* saved_block_;
236};
237
238} // namespace torque
239} // namespace internal
240} // namespace v8
241
242#endif // V8_TORQUE_CFG_H_