1 // Copyright 2021 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 #include "src/compiler/loop-unrolling.h"
6 
7 #include "src/base/small-vector.h"
8 #include "src/codegen/tick-counter.h"
9 #include "src/compiler/common-operator.h"
10 #include "src/compiler/loop-analysis.h"
11 #include "src/compiler/loop-peeling.h"
12 
13 namespace v8 {
14 namespace internal {
15 namespace compiler {
16 
UnrollLoop(Node* loop_node, ZoneUnorderedSet<Node*>* loop, uint32_t depth, Graph* graph, CommonOperatorBuilder* common, Zone* tmp_zone, SourcePositionTable* source_positions, NodeOriginTable* node_origins)17 void UnrollLoop(Node* loop_node, ZoneUnorderedSet<Node*>* loop, uint32_t depth,
18                 Graph* graph, CommonOperatorBuilder* common, Zone* tmp_zone,
19                 SourcePositionTable* source_positions,
20                 NodeOriginTable* node_origins) {
21   DCHECK_EQ(loop_node->opcode(), IrOpcode::kLoop);
22   DCHECK_NOT_NULL(loop);
23   // No back-jump to the loop header means this is not really a loop.
24   if (loop_node->InputCount() < 2) return;
25 
26   uint32_t unrolling_count =
27       unrolling_count_heuristic(static_cast<uint32_t>(loop->size()), depth);
28   if (unrolling_count == 0) return;
29 
30   uint32_t iteration_count = unrolling_count + 1;
31 
32   uint32_t copied_size = static_cast<uint32_t>(loop->size()) * iteration_count;
33 
34   NodeVector copies(tmp_zone);
35 
36   NodeCopier copier(graph, copied_size, &copies, unrolling_count);
37   source_positions->AddDecorator();
38   copier.CopyNodes(graph, tmp_zone, graph->NewNode(common->Dead()),
39                    base::make_iterator_range(loop->begin(), loop->end()),
40                    source_positions, node_origins);
41   source_positions->RemoveDecorator();
42 
43   // The terminator nodes in the copies need to get connected to the graph's end
44   // node, except Terminate nodes which will be deleted anyway.
45   for (Node* node : copies) {
46     if (IrOpcode::IsGraphTerminator(node->opcode()) &&
47         node->opcode() != IrOpcode::kTerminate && node->UseCount() == 0) {
48       NodeProperties::MergeControlToEnd(graph, common, node);
49     }
50   }
51 
52 #define COPY(node, n) copier.map(node, n)
53 #define FOREACH_COPY_INDEX(i) for (uint32_t i = 0; i < unrolling_count; i++)
54 
55   for (Node* node : loop_node->uses()) {
56     switch (node->opcode()) {
57       case IrOpcode::kBranch: {
58         /*** Step 1: Remove stack checks from all but the first iteration of the
59              loop. ***/
60         Node* stack_check = node->InputAt(0);
61         if (stack_check->opcode() != IrOpcode::kStackPointerGreaterThan) {
62           break;
63         }
64         FOREACH_COPY_INDEX(i) {
65           COPY(node, i)->ReplaceInput(0,
66                                       graph->NewNode(common->Int32Constant(1)));
67         }
68         for (Node* use : stack_check->uses()) {
69           if (use->opcode() == IrOpcode::kEffectPhi) {
70             // We now need to remove stack check and the related function call
71             // from the effect chain.
72             // The effect chain looks like this (* stand for irrelevant nodes):
73             //
74             // {replacing_effect} (effect before stack check)
75             //   *  *  |  *
76             //   |  |  |  |
77             // ( LoadFromObject )
78             //   |  |
79             // {stack_check}
80             // |   |
81             // |   *
82             // |
83             // | * *
84             // | | |
85             // {use}: EffectPhi (stack check effect that we need to replace)
86             DCHECK_EQ(use->opcode(), IrOpcode::kEffectPhi);
87             DCHECK_EQ(NodeProperties::GetEffectInput(use), stack_check);
88             DCHECK_EQ(NodeProperties::GetEffectInput(stack_check)->opcode(),
89                       IrOpcode::kLoadFromObject);
90             Node* replacing_effect = NodeProperties::GetEffectInput(
91                 NodeProperties::GetEffectInput(stack_check));
92             FOREACH_COPY_INDEX(i) {
93               COPY(use, i)->ReplaceUses(COPY(replacing_effect, i));
94             }
95           }
96         }
97         break;
98       }
99 
100       case IrOpcode::kLoopExit: {
101         /*** Step 2: Create merges for loop exits. ***/
102         if (node->InputAt(1) == loop_node) {
103           // Create a merge node from all iteration exits.
104           Node** merge_inputs = tmp_zone->NewArray<Node*>(iteration_count);
105           merge_inputs[0] = node;
106           for (uint32_t i = 1; i < iteration_count; i++) {
107             merge_inputs[i] = COPY(node, i - 1);
108           }
109           Node* merge_node = graph->NewNode(common->Merge(iteration_count),
110                                             iteration_count, merge_inputs);
111           // Replace all uses of the loop exit with the merge node.
112           for (Edge use_edge : node->use_edges()) {
113             Node* use = use_edge.from();
114             if (loop->count(use) == 1) {
115               // Uses within the loop will be LoopExitEffects and
116               // LoopExitValues. We need to create a phi from all loop
117               // iterations. Its merge will be the merge node for LoopExits.
118               const Operator* phi_operator;
119               if (use->opcode() == IrOpcode::kLoopExitEffect) {
120                 phi_operator = common->EffectPhi(iteration_count);
121               } else {
122                 DCHECK(use->opcode() == IrOpcode::kLoopExitValue);
123                 phi_operator = common->Phi(
124                     LoopExitValueRepresentationOf(use->op()), iteration_count);
125               }
126               Node** phi_inputs =
127                   tmp_zone->NewArray<Node*>(iteration_count + 1);
128               phi_inputs[0] = use;
129               for (uint32_t i = 1; i < iteration_count; i++) {
130                 phi_inputs[i] = COPY(use, i - 1);
131               }
132               phi_inputs[iteration_count] = merge_node;
133               Node* phi =
134                   graph->NewNode(phi_operator, iteration_count + 1, phi_inputs);
135               use->ReplaceUses(phi);
136               // Repair phi which we just broke.
137               phi->ReplaceInput(0, use);
138             } else if (use != merge_node) {
139               // For uses outside the loop, simply redirect them to the merge.
140               use->ReplaceInput(use_edge.index(), merge_node);
141             }
142           }
143         }
144         break;
145       }
146 
147       case IrOpcode::kTerminate: {
148         // We only need to keep the Terminate node for the loop header of the
149         // first iteration.
150         FOREACH_COPY_INDEX(i) { COPY(node, i)->Kill(); }
151         break;
152       }
153 
154       default:
155         break;
156     }
157   }
158 
159   /*** Step 3: Rewire the iterations of the loop. Each iteration should flow
160        into the next one, and the last should flow into the first. ***/
161 
162   // 3a) Rewire control.
163 
164   // We start at index=1 assuming that index=0 is the (non-recursive) loop
165   // entry.
166   for (int input_index = 1; input_index < loop_node->InputCount();
167        input_index++) {
168     Node* last_iteration_input =
169         COPY(loop_node, unrolling_count - 1)->InputAt(input_index);
170     for (uint32_t copy_index = unrolling_count - 1; copy_index > 0;
171          copy_index--) {
172       COPY(loop_node, copy_index)
173           ->ReplaceInput(input_index,
174                          COPY(loop_node, copy_index - 1)->InputAt(input_index));
175     }
176     COPY(loop_node, 0)
177         ->ReplaceInput(input_index, loop_node->InputAt(input_index));
178     loop_node->ReplaceInput(input_index, last_iteration_input);
179   }
180   // The loop of each following iteration will become a merge. We need to remove
181   // its non-recursive input.
182   FOREACH_COPY_INDEX(i) {
183     COPY(loop_node, i)->RemoveInput(0);
184     NodeProperties::ChangeOp(COPY(loop_node, i),
185                              common->Merge(loop_node->InputCount() - 1));
186   }
187 
188   // 3b) Rewire phis and loop exits.
189   for (Node* use : loop_node->uses()) {
190     if (NodeProperties::IsPhi(use)) {
191       int count = use->opcode() == IrOpcode::kPhi
192                       ? use->op()->ValueInputCount()
193                       : use->op()->EffectInputCount();
194       // Phis depending on the loop header should take their input from the
195       // previous iteration instead.
196       for (int input_index = 1; input_index < count; input_index++) {
197         Node* last_iteration_input =
198             COPY(use, unrolling_count - 1)->InputAt(input_index);
199         for (uint32_t copy_index = unrolling_count - 1; copy_index > 0;
200              copy_index--) {
201           COPY(use, copy_index)
202               ->ReplaceInput(input_index,
203                              COPY(use, copy_index - 1)->InputAt(input_index));
204         }
205         COPY(use, 0)->ReplaceInput(input_index, use->InputAt(input_index));
206         use->ReplaceInput(input_index, last_iteration_input);
207       }
208 
209       // Phis in each following iteration should not depend on the
210       // (non-recursive) entry to the loop. Remove their first input.
211       FOREACH_COPY_INDEX(i) {
212         COPY(use, i)->RemoveInput(0);
213         NodeProperties::ChangeOp(
214             COPY(use, i), common->ResizeMergeOrPhi(use->op(), count - 1));
215       }
216     }
217 
218     // Loop exits should point to the loop header.
219     if (use->opcode() == IrOpcode::kLoopExit) {
220       FOREACH_COPY_INDEX(i) { COPY(use, i)->ReplaceInput(1, loop_node); }
221     }
222   }
223 }
224 
225 #undef COPY
226 #undef FOREACH_COPY_INDEX
227 
228 }  // namespace compiler
229 }  // namespace internal
230 }  // namespace v8
231