5#ifndef V8_COMPILER_TURBOSHAFT_GRAPH_H_
6#define V8_COMPILER_TURBOSHAFT_GRAPH_H_
27template <
class Reducers>
114 return OpIndex(
static_cast<uint32_t
>(
reinterpret_cast<Address>(ptr) -
160 void Grow(
size_t min_capacity) {
161 size_t size = this->
size();
164 while (new_capacity < min_capacity) new_capacity *= 2;
165 CHECK_LT(new_capacity, std::numeric_limits<uint32_t>::max() /
172 uint16_t* new_operation_sizes =
179 end_cap_ = new_buffer + new_capacity;
193template <
class Derived>
195template <
class Derived>
198template <
class Derived>
205 DCHECK_EQ(
static_cast<Derived*
>(
this)->len_ + 1, next->len_);
216 for (Derived* child =
last_child_; child !=
nullptr;
217 child = child->neighboring_child_) {
232template <
class Derived>
284 return !(*
this == other);
346 CheckPredecessorCount();
354 void CheckPredecessorCount()
const {
370 int pred_reverse_index = -1;
373 if (pred == target) {
375 pred_reverse_index = pred_count;
379 if (pred_reverse_index == -1) {
382 return pred_count - pred_reverse_index - 1;
422 origin->graph_generation_ + 1 == graph_generation_);
459 case Opcode::kBranch:
460 case Opcode::kSwitch:
461 case Opcode::kCheckException:
473 return gto->destination->index().id() <=
index().
id();
484 bool has_peeled_iteration()
const {
486 return has_peeled_iteration_;
488 void set_has_peeled_iteration() {
490 has_peeled_iteration_ =
true;
500 std::vector<const char*> tree_symbols = std::vector<const char*>(),
501 bool has_next =
false)
const;
512 custom_data_kind_for_debug_check_ = kind_for_debug_check;
517 DCHECK_EQ(custom_data_kind_for_debug_check_, kind_for_debug_check);
558 size_t graph_generation_ = 0;
560 bool has_peeled_iteration_ =
false;
564 template <
class Reducers>
566 template <
class Assembler>
612 block_type_refinement_.Reset();
658 result.set_generation_mod2(generation_mod2());
665 it = std::upper_bound(
667 [](
OpIndex value,
const Block* b) { return value < b->begin_; });
670 it = std::upper_bound(
672 [](
OpIndex value,
const Block* b) { return value < b->begin_; });
676 DCHECK((*it)->Contains(index));
677 return (*it)->index();
691 next.set_generation_mod2(generation_mod2());
698 prev.set_generation_mod2(generation_mod2());
714 if (
v8_flags.turboshaft_trace_emitted) {
715 std::cout <<
"/!\\ Removed last emitted operation /!\\\n";
720 template <
class Op,
class... Args>
725 Op& op = Op::New(
this,
args...);
730 for (
OpIndex input : op.inputs()) {
732 DCHECK(BelongsToThisGraph(input));
735 if (
v8_flags.turboshaft_trace_emitted) {
736 std::cout <<
"Emitted: " <<
result <<
" => " << op <<
"\n";
744 template <
class Op,
class... Args>
746 static_assert((std::is_base_of<Operation, Op>::value));
747 static_assert(std::is_trivially_destructible<Op>::value);
755 new_op = &Op::New(
this,
args...);
757 if (!std::is_same_v<Op, DeadOp>) {
758 new_op->saturated_use_count = old_uses;
777 result->graph_generation_ = generation_;
784 DCHECK_EQ(block->graph_generation_, generation_);
785 if (!
bound_blocks_.empty() && !block->HasPredecessors())
return false;
787 DCHECK(!block->begin_.valid());
792 uint32_t depth = block->ComputeDominator();
796 if (
v8_flags.turboshaft_trace_emitted) {
797 std::cout <<
"\nBound: " << block->index() <<
" [predecessors: ";
798 auto preds = block->Predecessors();
799 if (preds.size() >= 1) std::cout << preds[0]->index();
800 for (
size_t i = 1;
i < preds.
size();
i++) {
801 std::cout <<
", " << preds[
i]->index();
811 DCHECK(!block->end_.valid());
847 uint32_t number_of_operations = 0;
849 ++number_of_operations;
851 return number_of_operations;
860 begin.set_generation_mod2(generation_mod2());
867 end.set_generation_mod2(generation_mod2());
874 std::ptrdiff_t, OpIndex*, OpIndex> {
891 return index_ != other.index_;
900 template <
class OperationT,
typename GraphT>
902 :
public base::iterator<std::bidirectional_iterator_tag, OperationT> {
904 static_assert(std::is_same_v<std::remove_const_t<OperationT>,
Operation> &&
905 std::is_same_v<std::remove_const_t<GraphT>,
Graph>);
924 return index_ != other.index_;
949 const Block& block) {
953 const Block& block)
const {
958 const Block& block)
const {
1027 using TypeRefinements = std::vector<std::pair<OpIndex, Type>>;
1029 return block_type_refinement_;
1032 return block_type_refinement_;
1041 for (
size_t i = 0;
i < permutation.
size(); ++
i) {
1054 if (IsCreatedFromTurbofan())
companion_->SetCreatedFromTurbofan();
1075 std::swap(block_type_refinement_, companion.block_type_refinement_);
1077 DCHECK_EQ(generation_ + 1, companion.generation_);
1078 generation_ = companion.generation_++;
1086 size_t generation()
const {
return generation_; }
1087 int generation_mod2()
const {
return generation_ % 2; }
1089 bool BelongsToThisGraph(
OpIndex idx)
const {
1090 return idx.generation_mod2() == generation_mod2();
1093 void SetCreatedFromTurbofan() { graph_created_from_turbofan_ =
true; }
1094 bool IsCreatedFromTurbofan()
const {
return graph_created_from_turbofan_; }
1108 bool has_loop_unrolling_analyzer()
const {
1131 for (
OpIndex input : op.inputs()) {
1135 Get(input).saturated_use_count.Incr();
1141 for (
OpIndex input : op.inputs()) {
1145 Get(input).saturated_use_count.Decr();
1153 constexpr size_t kMinCapacity = 32;
1154 size_t next_capacity = std::max(kMinCapacity,
all_blocks_.size() * 2);
1155 size_t new_block_count = next_capacity -
all_blocks_.size();
1164 DCHECK_EQ(insert_begin + new_block_count, new_all_blocks.
end());
1165 for (
size_t i = 0;
i < new_block_count; ++
i) {
1166 insert_begin[
i] = &block_storage[
i];
1170 if (!old_all_blocks.
empty()) {
1200 bool graph_created_from_turbofan_ =
false;
1205 size_t generation_ = 1;
1227 size_t slot_count) {
1228 return graph->Allocate(slot_count);
1232 return graph.Get(index);
1236 DCHECK_EQ(graph_generation_, graph.generation());
1239 return graph.Get(
begin_);
1243 DCHECK_EQ(graph_generation_, graph.generation());
1244 return graph.Get(graph.PreviousIndex(
end()));
1248 DCHECK_EQ(graph_generation_, graph.generation());
1249 return graph.Get(graph.PreviousIndex(
end()));
1257 DCHECK_EQ(graph_generation_, graph.generation());
1258 for (
const auto& op : graph.operations(*
this)) {
1259 if (op.Is<
PhiOp>())
return true;
1269 : block(block),
block_id(block.index()) {}
1276 const Graph& graph);
1311template <
class Derived>
1313 jmp_ =
static_cast<Derived*
>(
this);
1319template <
class Derived>
1321 Derived* dominator) {
1326 Derived* t = dominator->jmp_;
1327 if (dominator->len_ - t->len_ == t->len_ - t->jmp_len_) {
1335 len_ = dominator->len_ + 1;
1336 jmp_len_ = jmp_->len_;
1337 dominator->AddChild(
static_cast<Derived*
>(
this));
1340template <
class Derived>
1345 if (b->
len_ > a->len_) {
1353 while (a->len_ != b->
len_) {
1355 if (a->jmp_len_ >= b->
len_) {
1366 if (a->jmp_ == b->
jmp_) {
1377 return static_cast<Derived*
>(
1385class std::iterator_traits<
1386 v8::internal::compiler::turboshaft::PredecessorIterator> {
union v8::internal::@341::BuiltinMetadata::KindSpecificData data
std::forward_iterator_tag iterator_category
constexpr bool empty() const
constexpr size_t size() const
constexpr T * data() const
T * AllocateArray(size_t length)
base::Vector< T > AllocateVector(size_t length)
void DeleteArray(T *pointer, size_t length)
static constexpr BlockIndex Invalid()
void ResetAllPredecessors()
void ResetLastPredecessor()
static constexpr int kInvalidPredecessorIndex
const Block * OriginForBlockEnd() const
void set_custom_data(uint32_t data, CustomDataKind kind_for_debug_check)
bool HasPredecessors() const
base::SmallVector< Block *, 8 > Predecessors() const
bool Contains(OpIndex op_idx) const
const Operation & FirstOperation(const Graph &graph) const
bool IsBranchTarget() const
Block * last_predecessor_
Block * NeighboringPredecessor() const
uint32_t predecessor_count_
Block * single_loop_predecessor_
bool HasBackedge(const Graph &graph) const
bool HasPhis(const Graph &graph) const
const Operation & LastOperation(const Graph &graph) const
void SetOrigin(const Block *origin)
bool EndsWithBranchingOp(const Graph &graph) const
NeighboringPredecessorIterable PredecessorsIterable() const
void PrintDominatorTree(std::vector< const char * > tree_symbols=std::vector< const char * >(), bool has_next=false) const
Block * neighboring_predecessor_
const Block * OriginForLoopHeader() const
Block * single_loop_predecessor() const
int PredecessorCount() const
uint32_t get_custom_data(CustomDataKind kind_for_debug_check) const
int GetPredecessorIndex(const Block *target) const
void SetSingleLoopPredecessor(Block *single_loop_predecessor)
uint32_t ComputeDominator()
Block * LastPredecessor() const
void AddPredecessor(Block *predecessor)
int OpCountUpperBound() const
bool IsLoopOrMerge() const
void AddChild(Derived *next)
Derived * neighboring_child_
base::SmallVector< Derived *, 8 > Children() const
Derived * NeighboringChild() const
Derived * LastChild() const
const Graph *const graph_
OpIndexIterator & operator--()
bool operator==(OpIndexIterator other) const
bool operator!=(OpIndexIterator other) const
OpIndexIterator(OpIndex index, const Graph *graph)
OpIndexIterator & operator++()
value_type operator*() const
bool operator==(OperationIterator other) const
OperationIterator(OpIndex index, GraphT *graph)
OperationIterator & operator++()
bool operator!=(OperationIterator other) const
value_type * operator->()
OperationIterator & operator--()
Graph(Zone *graph_zone, size_t initial_capacity=2048)
base::iterator_range< ConstOperationIterator > operations(const Block &block) const
GrowingOpIndexSidetable< OpIndex > operation_origins_
const Block & Get(BlockIndex i) const
V8_INLINE Operation & Get(OpIndex i)
Zone * graph_zone() const
void ReorderBlocks(base::Vector< uint32_t > permutation)
OpIndex PreviousIndex(const OpIndex idx) const
BlockIndex BlockOf(OpIndex index) const
GrowingOpIndexSidetable< SourcePosition > source_positions_
GrowingOpIndexSidetable< Type > & operation_types()
uint32_t op_id_capacity() const
const GrowingOpIndexSidetable< SourcePosition > & source_positions() const
base::iterator_range< OpIndexIterator > OperationIndices(OpIndex begin, OpIndex end) const
base::iterator_range< ConstOperationIterator > operations(OpIndex begin, OpIndex end) const
ZoneAbslFlatHashSet< uint32_t > stack_checks_to_remove_
V8_INLINE Block * NewBlock(Block::Kind kind, const Block *origin=nullptr)
BlockIndex BlockIndexOf(OpIndex op) const
base::iterator_range< MutableOperationIterator > AllOperations()
void clear_stack_checks_to_remove()
uint32_t block_count() const
LoopUnrollingAnalyzer * loop_unrolling_analyzer_
ZoneVector< Block * > bound_blocks_
void Replace(OpIndex replaced, Args... args)
ZoneAbslFlatHashSet< uint32_t > & stack_checks_to_remove()
V8_INLINE Block * NewLoopHeader(const Block *origin=nullptr)
uint32_t dominator_tree_depth_
base::iterator_range< base::DerefPtrIterator< const Block > > blocks() const
BlockIndex BlockIndexOf(const Operation &op) const
base::iterator_range< MutableOperationIterator > operations(OpIndex begin, OpIndex end)
V8_INLINE Block * NewBlock(const Block *origin=nullptr)
const Block & StartBlock() const
void clear_loop_unrolling_analyzer()
OperationStorageSlot * Allocate(size_t slot_count)
void KillOperation(OpIndex i)
base::iterator_range< MutableOperationIterator > operations(const Block &block)
OpIndex NextIndex(const OpIndex idx) const
const ZoneVector< Block * > & blocks_vector() const
V8_NOINLINE V8_PRESERVE_MOST void AllocateNewBlocks()
base::iterator_range< OpIndexIterator > AllOperationIndices() const
void DecrementInputUses(const Op &op)
V8_INLINE Op & Add(Args... args)
void IncrementInputUses(const Op &op)
base::iterator_range< OpIndexIterator > OperationIndices(const Block &block) const
base::Vector< Block * > all_blocks_
const ZoneAbslFlatHashSet< uint32_t > & stack_checks_to_remove() const
OperationIterator< Operation, Graph > MutableOperationIterator
LoopUnrollingAnalyzer * loop_unrolling_analyzer() const
GrowingOpIndexSidetable< Type > operation_types_
Graph & GetOrCreateCompanion()
base::iterator_range< base::DerefPtrIterator< Block > > blocks()
base::iterator_range< ConstOperationIterator > AllOperations() const
void TurnLoopIntoMerge(Block *loop)
OperationBuffer operations_
BlockIndex next_block_index() const
GrowingOpIndexSidetable< BlockIndex > op_to_block_
OpIndex Index(const Operation &op) const
GrowingOpIndexSidetable< OpIndex > & operation_origins()
void SetBlockOf(BlockIndex block, OpIndex op)
bool InputsValid(const Operation &op) const
const GrowingOpIndexSidetable< OpIndex > & operation_origins() const
bool IsLoopBackedge(const GotoOp &op) const
V8_INLINE bool Add(Block *block)
OpIndex LastOperation() const
ZoneVector< Block * > block_permutation_
bool IsValid(OpIndex i) const
Block & Get(BlockIndex i)
OpIndex BeginIndex() const
void set_loop_unrolling_analyzer(LoopUnrollingAnalyzer *loop_unrolling_analyzer)
const GrowingOpIndexSidetable< Type > & operation_types() const
V8_INLINE const Operation & Get(OpIndex i) const
OperationIterator< const Operation, const Graph > ConstOperationIterator
uint32_t DominatorTreeDepth() const
GrowingOpIndexSidetable< SourcePosition > & source_positions()
uint32_t NumberOfOperationsForDebugging() const
void Finalize(Block *block)
OpIndex next_operation_index() const
uint32_t op_id_count() const
PredecessorIterator end() const
NeighboringPredecessorIterable(const Block *begin)
PredecessorIterator begin() const
static constexpr OpIndex Invalid()
constexpr uint32_t id() const
constexpr bool valid() const
OperationStorageSlot * old_end_
ReplaceScope(OperationBuffer *buffer, OpIndex replaced)
OperationBuffer * buffer_
ReplaceScope(const ReplaceScope &)=delete
ReplaceScope & operator=(const ReplaceScope &)=delete
OperationBuffer(Zone *zone, size_t initial_capacity)
uint16_t SlotCount(OpIndex idx)
uint32_t capacity() const
OperationStorageSlot * Get(OpIndex idx)
OpIndex Previous(OpIndex idx) const
void Grow(size_t min_capacity)
OpIndex Index(const OperationStorageSlot *ptr) const
OperationStorageSlot * end_cap_
OpIndex BeginIndex() const
uint16_t * operation_sizes_
OperationStorageSlot * end_
OpIndex Next(OpIndex idx) const
const OperationStorageSlot * Get(OpIndex idx) const
OperationStorageSlot * Allocate(size_t slot_count)
OpIndex Index(const Operation &op) const
OperationStorageSlot * begin_
PredecessorIterator & operator++()
constexpr bool operator==(const PredecessorIterator &other) const
constexpr bool operator!=(const PredecessorIterator &other) const
PredecessorIterator(const Block *block)
const Block * operator*() const
Derived * GetCommonDominator(RandomAccessStackDominatorNode< Derived > *other) const
void SetDominator(Derived *dominator)
void SetAsDominatorRoot()
Derived * GetDominator() const
bool IsDominatedBy(const Derived *other) const
base::Vector< const DirectHandle< Object > > args
std::optional< TNode< JSArray > > a
ZoneVector< RpoNumber > & result
constexpr Vector< T > VectorOf(T *start, size_t size)
std::ostream & operator<<(std::ostream &os, PaddingSpace padding)
V8_INLINE const Operation & Get(const Graph &graph, OpIndex index)
uint64_t OperationStorageSlot
V8_INLINE OperationStorageSlot * AllocateOpStorage(Graph *graph, size_t slot_count)
constexpr size_t kSlotsPerId
constexpr std::underlying_type_t< Opcode > OpcodeIndex(Opcode x)
constexpr uint16_t kNumberOfOpcodes
base::SmallVector< Block *, 4 > SuccessorBlocks(const Block &block, const Graph &graph)
Node::Uses::const_iterator begin(const Node::Uses &uses)
bool TryCast(Tagged< From > value, Tagged< To > *out)
bool Is(IndirectHandle< U > value)
V8_EXPORT_PRIVATE FlagValues v8_flags
#define DCHECK_LE(v1, v2)
#define CHECK_LT(lhs, rhs)
#define DCHECK_NOT_NULL(val)
#define DCHECK_IMPLIES(v1, v2)
#define DCHECK_NE(v1, v2)
#define DCHECK_GE(v1, v2)
#define DCHECK(condition)
#define DCHECK_LT(v1, v2)
#define DCHECK_EQ(v1, v2)
#define DCHECK_GT(v1, v2)
#define V8_EXPORT_PRIVATE
base::Vector< const OpIndex > inputs() const
SaturatedUint8 saturated_use_count
#define V8_UNLIKELY(condition)