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