23#define OFFSET(x) ((x)&0x1F)
24#define BIT(x) (1u << OFFSET(x))
25#define INDEX(x) ((x) >> 5)
68 info_(graph->NodeCount(), {
nullptr,
nullptr,
false}, zone),
87 if (ni.node ==
nullptr)
continue;
92 if (marked_forward && marked_backward) {
94 }
else if (marked_forward) {
96 }
else if (marked_backward) {
102 PrintF(
" #%d:%s\n", ni.node->id(), ni.node->op()->mnemonic());
107 PrintF(
"Loop %d headed at #%d\n",
i, li.header->
id());
137 if (from == to)
return false;
142 uint32_t
mask =
i ==
INDEX(loop_filter) ? ~BIT(loop_filter) : 0xFFFFFFFF;
143 uint32_t prev = tp[
i];
144 uint32_t next = prev | (fp[
i] &
mask);
146 if (!change && (prev != next)) change =
true;
154 uint32_t prev = tp[0];
155 uint32_t next = prev |
BIT(loop_num);
163 uint32_t prev = tp[0];
164 uint32_t next = prev |
BIT(loop_num);
171 if (from == to)
return false;
173 int findex = from->id() *
width_;
174 int tindex = to->id() *
width_;
178 uint32_t next = prev | marks;
180 if (!change && (prev != next)) change =
true;
199 info(node).backwards_visited =
true;
205 if (node->opcode() == IrOpcode::kLoop) {
210 Node* merge = node->
InputAt(node->InputCount() - 1);
211 if (merge->
opcode() == IrOpcode::kLoop) {
214 }
else if (node->opcode() == IrOpcode::kLoopExit) {
218 }
else if (node->opcode() == IrOpcode::kLoopExitValue ||
219 node->opcode() == IrOpcode::kLoopExitEffect) {
227 for (
int i = 0;
i < node->InputCount();
i++) {
232 !
info(input).backwards_visited) {
240 !
info(input).backwards_visited) {
250 DCHECK_EQ(IrOpcode::kLoop, node->opcode());
252 if (loop_num > 0)
return loop_num;
258 loops_.push_back({
node,
nullptr,
nullptr,
nullptr,
nullptr});
271 DCHECK_EQ(IrOpcode::kLoop, node->opcode());
279 if (node->InputCount() <= 1)
continue;
281 if (use->opcode() == IrOpcode::kLoopExit) {
283 for (
Node* exit_use : use->
uses()) {
284 if (exit_use->opcode() == IrOpcode::kLoopExitValue ||
285 exit_use->opcode() == IrOpcode::kLoopExitEffect) {
294 int new_width =
width_ + 1;
297 memset(new_backward, 0, new_width * max *
sizeof(uint32_t));
299 for (
int i = 0;
i < max;
i++) {
300 uint32_t* np = &new_backward[
i * new_width];
302 for (
int j = 0; j <
width_; j++) np[j] = op[j];
328 for (
Edge edge : node->use_edges()) {
329 Node* use = edge.from();
342 return node->opcode() == IrOpcode::kLoopExit ||
343 node->opcode() == IrOpcode::kLoopExitValue ||
344 node->opcode() == IrOpcode::kLoopExitEffect;
348 if (
LoopNum(use) <= 0)
return false;
352 }
else if (use->opcode() == IrOpcode::kLoop) {
403 if (ni.node ==
nullptr)
continue;
406 int innermost_index = 0;
412 for (
int j = 0; j < 32; j++) {
413 if (marks & (1u << j)) {
414 int loop_num =
i * 32 + j;
415 if (loop_num == 0)
continue;
417 if (innermost ==
nullptr ||
420 innermost_index = loop_num;
425 if (innermost ==
nullptr)
continue;
428 CHECK(ni.node->opcode() != IrOpcode::kReturn);
449 if (ni.node ==
nullptr || !
IsInLoop(ni.node, 1))
continue;
452 CHECK(ni.node->opcode() != IrOpcode::kReturn);
499 if (li.
loop !=
nullptr)
return li.
loop;
504 if (
i == loop_num)
continue;
508 if (parent ==
nullptr || upper->
depth_ > parent->
depth_) {
522 while (i < loop->body_start_) {
525 while (i < loop->exits_start_) {
528 while (i < loop->exits_end_) {
548#if V8_ENABLE_WEBASSEMBLY
554 std::vector<Node*> queue;
558 queue.push_back(loop_header);
559 visited->insert(loop_header);
561#define ENQUEUE_USES(use_name, condition) \
562 for (Node * use_name : node->uses()) { \
563 if (condition && visited->count(use_name) == 0) { \
564 visited->insert(use_name); \
565 queue.push_back(use_name); \
568 bool has_instruction_worth_peeling =
false;
569 while (!queue.empty()) {
570 Node* node = queue.back();
572 if (node->opcode() == IrOpcode::kEnd) {
574 visited->erase(node);
577 if (visited->size() > max_size)
return nullptr;
578 switch (node->opcode()) {
579 case IrOpcode::kLoop:
581 if (node != loop_header)
return nullptr;
582 ENQUEUE_USES(use,
true);
584 case IrOpcode::kLoopExit:
586 if (node->InputAt(1) != loop_header)
return nullptr;
588 ENQUEUE_USES(use, (use->opcode() == IrOpcode::kLoopExitEffect ||
589 use->opcode() == IrOpcode::kLoopExitValue))
591 case IrOpcode::kLoopExitEffect:
592 case IrOpcode::kLoopExitValue:
601 case IrOpcode::kTailCall:
602 case IrOpcode::kJSWasmCall:
603 case IrOpcode::kJSCall:
604 if (purpose == Purpose::kLoopUnrolling)
return nullptr;
605 ENQUEUE_USES(use,
true)
607 case IrOpcode::
kCall: {
608 if (purpose == Purpose::kLoopPeeling) {
609 ENQUEUE_USES(use,
true);
612 Node* callee = node->InputAt(0);
613 if (callee->opcode() != IrOpcode::kRelocatableInt32Constant &&
614 callee->opcode() != IrOpcode::kRelocatableInt64Constant) {
619 constexpr Builtin unrollable_builtins[] = {
621 Builtin::kWasmStackGuard,
623 Builtin::kWasmTableGet, Builtin::kWasmTableSet,
624 Builtin::kWasmTableGetFuncRef, Builtin::kWasmTableSetFuncRef,
625 Builtin::kWasmTableGrow,
627 Builtin::kWasmI32AtomicWait, Builtin::kWasmI64AtomicWait,
629 Builtin::kWasmAllocateFixedArray, Builtin::kWasmThrow,
630 Builtin::kWasmRethrow, Builtin::kWasmRethrowExplicitContext,
632 Builtin::kWasmRefFunc,
635 Builtin::kWasmStringViewWtf16GetCodeUnit};
636 if (std::count(std::begin(unrollable_builtins),
637 std::end(unrollable_builtins), builtin) == 0) {
640 ENQUEUE_USES(use,
true)
643 case IrOpcode::kWasmStructGet: {
646 Node*
object = node->InputAt(0);
647 if (object->opcode() == IrOpcode::kWasmStructGet &&
648 visited->find(
object) != visited->end()) {
649 has_instruction_worth_peeling =
true;
651 ENQUEUE_USES(use,
true);
654 case IrOpcode::kWasmArrayGet:
658 case IrOpcode::kStringPrepareForGetCodeunit:
661 has_instruction_worth_peeling =
true;
664 ENQUEUE_USES(use,
true)
675 for (Node* node : *visited) {
677 if (node == loop_header)
continue;
679 if (!all_nodes.
IsLive(node))
continue;
681 for (Edge edge : node->input_edges()) {
682 Node* input = edge.to();
683 if (NodeProperties::IsControlEdge(edge) && visited->count(input) == 0 &&
684 input->opcode() != IrOpcode::kStart) {
686 "Floating control detected in wasm turbofan graph: Node #%d:%s is "
687 "inside loop headed by #%d, but its control dependency #%d:%s is "
689 node->id(), node->op()->mnemonic(), loop_header->id(), input->id(),
690 input->op()->mnemonic());
698 if (purpose == Purpose::kLoopPeeling && !has_instruction_worth_peeling) {
712 if (!loop_tree->
Contains(loop, use)) {
714 switch (node->opcode()) {
715 case IrOpcode::kLoopExit:
716 unmarked_exit = (node->InputAt(1) != loop_node);
718 case IrOpcode::kLoopExitValue:
719 case IrOpcode::kLoopExitEffect:
720 unmarked_exit = (node->InputAt(1)->InputAt(1) != loop_node);
723 unmarked_exit = (use->opcode() != IrOpcode::kTerminate);
728 "Cannot peel loop %i. Loop exit without explicit mark: Node %i "
729 "(%s) is inside loop, but its use %i (%s) is outside.\n",
730 loop_node->
id(), node->id(), node->op()->mnemonic(), use->id(),
731 use->op()->mnemonic());
742 Node* first = *HeaderNodes(loop).begin();
743 if (first->opcode() == IrOpcode::kLoop)
return first;
752 if (node_map_.Get(node) == 0)
return node;
753 return copies_->at(node_map_.Get(node) + copy_index);
758 node_map_.Set(original, copies_->size() + 1);
759 copies_->push_back(original);
760 copies_->insert(copies_->end(), new_copies.
begin(), new_copies.
end());
765 node_map_.Set(original, copies_->size() + 1);
766 copies_->push_back(original);
767 copies_->push_back(copy);
void TickAndMaybeEnterSafepoint()
void reserve(size_t new_cap)
void push_back(const T &value)
T * AllocateArray(size_t length)
bool IsLive(const Node *node) const
static bool IsPhiOpcode(Value value)
void PrintLoop(LoopTree::Loop *loop)
int CreateLoopInfo(Node *node)
bool PropagateForwardMarks(Node *from, Node *to)
void AddNodeToLoop(NodeInfo *node_info, TempLoopInfo *loop, int loop_num)
void SetLoopMark(Node *node, int loop_num)
bool IsLoopExitNode(Node *node)
bool IsInLoop(Node *node, int loop_num)
ZoneVector< int > loop_num_
void ResizeForwardMarks()
bool PropagateBackwardMarks(Node *from, Node *to, int loop_filter)
void SetLoopMarkForLoopHeader(Node *node, int loop_num)
bool IsBackedge(Node *use, int index)
LoopTree::Loop * ConnectLoopTree(int loop_num)
NodeInfo & info(Node *node)
LoopFinderImpl(TFGraph *graph, LoopTree *loop_tree, TickCounter *tick_counter, Zone *zone)
ZoneVector< TempLoopInfo > loops_
bool IsLoopHeaderNode(Node *node)
ZoneVector< NodeInfo > info_
TickCounter *const tick_counter_
bool SetBackwardMark(Node *to, int loop_num)
void SerializeLoop(LoopTree::Loop *loop)
void ResizeBackwardMarks()
bool SetForwardMark(Node *to, int loop_num)
NodeMarker< bool > queued_
static bool HasMarkedExits(LoopTree *loop_tree, const LoopTree::Loop *loop)
static LoopTree * BuildLoopTree(TFGraph *graph, TickCounter *tick_counter, Zone *temp_zone)
ZoneVector< Loop * > children_
Node * GetLoopControl(const Loop *loop)
ZoneVector< Loop * > outer_loops_
int LoopNum(const Loop *loop) const
ZoneVector< Node * > loop_nodes_
void SetParent(Loop *parent, Loop *child)
ZoneVector< int > node_to_loop_num_
Node * HeaderNode(const Loop *loop)
NodeRange LoopNodes(const Loop *loop)
bool Contains(const Loop *loop, Node *node)
ZoneVector< Loop > all_loops_
Node * map(Node *node, uint32_t copy_index)
void Insert(Node *original, const NodeVector &new_copies)
V8_INLINE void Set(Node *node, State state)
V8_INLINE State Get(const Node *node)
static int FirstControlIndex(Node *node)
static bool IsPhi(Node *node)
static Node * GetControlInput(Node *node, int index=0)
constexpr IrOpcode::Value opcode() const
Node * InputAt(int index) const
Handle< SharedFunctionInfo > info
static const int kAssumedLoopEntryIndex
T const & OpParameter(const Operator *op)
void PrintF(const char *format,...)
V8_EXPORT_PRIVATE FlagValues v8_flags
#define DCHECK(condition)
#define DCHECK_LT(v1, v2)
#define DCHECK_EQ(v1, v2)