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