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(¤t_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(¤t_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