• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
18 namespace v8 {
19 namespace internal {
20 namespace torque {
21 
22 class ControlFlowGraph;
23 
24 class Block {
25  public:
Block(ControlFlowGraph * cfg,size_t id,base::Optional<Stack<const Type * >> input_types,bool is_deferred)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) {}
Add(Instruction instruction)33   void Add(Instruction instruction) {
34     DCHECK(!IsComplete());
35     instructions_.push_back(std::move(instruction));
36   }
37 
HasInputTypes()38   bool HasInputTypes() const { return input_types_ != base::nullopt; }
InputTypes()39   const Stack<const Type*>& InputTypes() const { return *input_types_; }
40   void SetInputTypes(const Stack<const Type*>& input_types);
Retype()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 
instructions()48   std::vector<Instruction>& instructions() { return instructions_; }
instructions()49   const std::vector<Instruction>& instructions() const { return instructions_; }
IsComplete()50   bool IsComplete() const {
51     return !instructions_.empty() && instructions_.back()->IsBlockTerminator();
52   }
id()53   size_t id() const { return id_; }
IsDeferred()54   bool IsDeferred() const { return is_deferred_; }
55 
MergeInputDefinitions(const Stack<DefinitionLocation> & input_definitions,Worklist<Block * > * worklist)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   }
HasInputDefinitions()77   bool HasInputDefinitions() const {
78     return input_definitions_ != base::nullopt;
79   }
InputDefinitions()80   const Stack<DefinitionLocation>& InputDefinitions() const {
81     DCHECK(HasInputDefinitions());
82     return *input_definitions_;
83   }
84 
IsDead()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 
96 class ControlFlowGraph {
97  public:
ControlFlowGraph(Stack<const Type * > input_types)98   explicit ControlFlowGraph(Stack<const Type*> input_types) {
99     start_ = NewBlock(std::move(input_types), false);
100     PlaceBlock(start_);
101   }
102 
NewBlock(base::Optional<Stack<const Type * >> input_types,bool is_deferred)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   }
PlaceBlock(Block * block)109   void PlaceBlock(Block* block) { placed_blocks_.push_back(block); }
110   template <typename UnaryPredicate>
UnplaceBlockIf(UnaryPredicate && predicate)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   }
start()116   Block* start() const { return start_; }
end()117   base::Optional<Block*> end() const { return end_; }
set_end(Block * end)118   void set_end(Block* end) { end_ = end; }
SetReturnType(TypeVector t)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   }
blocks()133   const std::vector<Block*>& blocks() const { return placed_blocks_; }
NumberOfBlockIds()134   size_t NumberOfBlockIds() const { return next_block_id_; }
ParameterCount()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 
148 class CfgAssembler {
149  public:
CfgAssembler(Stack<const Type * > input_types)150   explicit CfgAssembler(Stack<const Type*> input_types)
151       : current_stack_(std::move(input_types)), cfg_(current_stack_) {}
152 
Result()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 
CurrentBlockIsComplete()169   bool CurrentBlockIsComplete() const { return current_block_->IsComplete(); }
CfgIsComplete()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 
Emit(Instruction instruction)177   void Emit(Instruction instruction) {
178     instruction.TypeInstruction(&current_stack_, &cfg_);
179     current_block_->Add(std::move(instruction));
180   }
181 
CurrentStack()182   const Stack<const Type*>& CurrentStack() const { return current_stack_; }
183 
TopRange(size_t slot_count)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 
PrintCurrentStack(std::ostream & s)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 
220 class V8_NODISCARD CfgAssemblerScopedTemporaryBlock {
221  public:
CfgAssemblerScopedTemporaryBlock(CfgAssembler * assembler,Block * block)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 
~CfgAssemblerScopedTemporaryBlock()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