v8
V8 is Google’s open source high-performance JavaScript and WebAssembly engine, written in C++.
Loading...
Searching...
No Matches
loop-unrolling.cc
Go to the documentation of this file.
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
6
12
13namespace v8 {
14namespace internal {
15namespace compiler {
16
17void UnrollLoop(Node* loop_node, ZoneUnorderedSet<Node*>* loop, uint32_t depth,
18 TFGraph* graph, CommonOperatorBuilder* common, Zone* tmp_zone,
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);
38 copier.CopyNodes(graph, tmp_zone, graph->NewNode(common->Dead()),
39 base::make_iterator_range(loop->begin(), loop->end()),
40 source_positions, node_origins);
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 // Replace value uses of the stack check with {true}, and remove the
65 // stack check from the effect chain.
67 for (Edge use_edge : COPY(stack_check, i)->use_edges()) {
68 if (NodeProperties::IsValueEdge(use_edge)) {
69 use_edge.UpdateTo(graph->NewNode(common->Int32Constant(1)));
70 } else if (NodeProperties::IsEffectEdge(use_edge)) {
71 use_edge.UpdateTo(
73 } else {
75 }
76 }
77 }
78 break;
79 }
80
81 case IrOpcode::kLoopExit: {
82 /*** Step 2: Create merges for loop exits. ***/
83 if (node->InputAt(1) == loop_node) {
84 // Create a merge node from all iteration exits.
85 Node** merge_inputs = tmp_zone->AllocateArray<Node*>(iteration_count);
86 merge_inputs[0] = node;
87 for (uint32_t i = 1; i < iteration_count; i++) {
88 merge_inputs[i] = COPY(node, i - 1);
89 }
90 Node* merge_node = graph->NewNode(common->Merge(iteration_count),
91 iteration_count, merge_inputs);
92 // Replace all uses of the loop exit with the merge node.
93 for (Edge use_edge : node->use_edges()) {
94 Node* use = use_edge.from();
95 if (loop->count(use) == 1) {
96 // Uses within the loop will be LoopExitEffects and
97 // LoopExitValues. We need to create a phi from all loop
98 // iterations. Its merge will be the merge node for LoopExits.
99 const Operator* phi_operator;
100 if (use->opcode() == IrOpcode::kLoopExitEffect) {
101 phi_operator = common->EffectPhi(iteration_count);
102 } else {
103 DCHECK(use->opcode() == IrOpcode::kLoopExitValue);
104 phi_operator = common->Phi(
105 LoopExitValueRepresentationOf(use->op()), iteration_count);
106 }
107 Node** phi_inputs =
108 tmp_zone->AllocateArray<Node*>(iteration_count + 1);
109 phi_inputs[0] = use;
110 for (uint32_t i = 1; i < iteration_count; i++) {
111 phi_inputs[i] = COPY(use, i - 1);
112 }
113 phi_inputs[iteration_count] = merge_node;
114 Node* phi =
115 graph->NewNode(phi_operator, iteration_count + 1, phi_inputs);
116 use->ReplaceUses(phi);
117 // Repair phi which we just broke.
118 phi->ReplaceInput(0, use);
119 } else if (use != merge_node) {
120 // For uses outside the loop, simply redirect them to the merge.
121 use->ReplaceInput(use_edge.index(), merge_node);
122 }
123 }
124 }
125 break;
126 }
127
128 case IrOpcode::kTerminate: {
129 // We only need to keep the Terminate node for the loop header of the
130 // first iteration.
131 FOREACH_COPY_INDEX(i) { COPY(node, i)->Kill(); }
132 break;
133 }
134
135 default:
136 break;
137 }
138 }
139
140 /*** Step 3: Rewire the iterations of the loop. Each iteration should flow
141 into the next one, and the last should flow into the first. ***/
142
143 // 3a) Rewire control.
144
145 // We start at index=1 assuming that index=0 is the (non-recursive) loop
146 // entry.
147 for (int input_index = 1; input_index < loop_node->InputCount();
148 input_index++) {
149 Node* last_iteration_input =
150 COPY(loop_node, unrolling_count - 1)->InputAt(input_index);
151 for (uint32_t copy_index = unrolling_count - 1; copy_index > 0;
152 copy_index--) {
153 COPY(loop_node, copy_index)
154 ->ReplaceInput(input_index,
155 COPY(loop_node, copy_index - 1)->InputAt(input_index));
156 }
157 COPY(loop_node, 0)
158 ->ReplaceInput(input_index, loop_node->InputAt(input_index));
159 loop_node->ReplaceInput(input_index, last_iteration_input);
160 }
161 // The loop of each following iteration will become a merge. We need to remove
162 // its non-recursive input.
164 COPY(loop_node, i)->RemoveInput(0);
165 NodeProperties::ChangeOp(COPY(loop_node, i),
166 common->Merge(loop_node->InputCount() - 1));
167 }
168
169 // 3b) Rewire phis and loop exits.
170 for (Node* use : loop_node->uses()) {
171 if (NodeProperties::IsPhi(use)) {
172 int count = use->opcode() == IrOpcode::kPhi
173 ? use->op()->ValueInputCount()
174 : use->op()->EffectInputCount();
175 // Phis depending on the loop header should take their input from the
176 // previous iteration instead.
177 for (int input_index = 1; input_index < count; input_index++) {
178 Node* last_iteration_input =
179 COPY(use, unrolling_count - 1)->InputAt(input_index);
180 for (uint32_t copy_index = unrolling_count - 1; copy_index > 0;
181 copy_index--) {
182 COPY(use, copy_index)
183 ->ReplaceInput(input_index,
184 COPY(use, copy_index - 1)->InputAt(input_index));
185 }
186 COPY(use, 0)->ReplaceInput(input_index, use->InputAt(input_index));
187 use->ReplaceInput(input_index, last_iteration_input);
188 }
189
190 // Phis in each following iteration should not depend on the
191 // (non-recursive) entry to the loop. Remove their first input.
193 COPY(use, i)->RemoveInput(0);
195 COPY(use, i), common->ResizeMergeOrPhi(use->op(), count - 1));
196 }
197 }
198
199 // Loop exits should point to the loop header.
200 if (use->opcode() == IrOpcode::kLoopExit) {
201 FOREACH_COPY_INDEX(i) { COPY(use, i)->ReplaceInput(1, loop_node); }
202 }
203 }
204}
205
206#undef COPY
207#undef FOREACH_COPY_INDEX
208
209} // namespace compiler
210} // namespace internal
211} // namespace v8
T * AllocateArray(size_t length)
Definition zone.h:127
const Operator * Phi(MachineRepresentation representation, int value_input_count)
const Operator * EffectPhi(int effect_input_count)
const Operator * ResizeMergeOrPhi(const Operator *op, int size)
const Operator * Merge(int control_input_count)
static bool IsGraphTerminator(Value value)
Definition opcodes.h:1415
void CopyNodes(TFGraph *graph, Zone *tmp_zone_, Node *dead, base::iterator_range< InputIterator > nodes, SourcePositionTable *source_positions, NodeOriginTable *node_origins)
static void ChangeOp(Node *node, const Operator *new_op)
static Node * GetEffectInput(Node *node, int index=0)
static void MergeControlToEnd(TFGraph *graph, CommonOperatorBuilder *common, Node *node)
constexpr IrOpcode::Value opcode() const
Definition node.h:52
int InputCount() const
Definition node.h:59
void ReplaceInput(int index, Node *new_to)
Definition node.h:76
Node * InputAt(int index) const
Definition node.h:70
void ReplaceUses(Node *replace_to)
Definition node.cc:306
uint32_t count
SourcePositionTable * source_positions
Node * node
#define FOREACH_COPY_INDEX(i)
#define COPY(node, n)
auto make_iterator_range(ForwardIterator begin, ForwardIterator end)
Definition iterator.h:65
V8_INLINE uint32_t unrolling_count_heuristic(uint32_t size, uint32_t depth)
void UnrollLoop(Node *loop_node, ZoneUnorderedSet< Node * > *loop, uint32_t depth, TFGraph *graph, CommonOperatorBuilder *common, Zone *tmp_zone, SourcePositionTable *source_positions, NodeOriginTable *node_origins)
MachineRepresentation LoopExitValueRepresentationOf(const Operator *const op)
other heap size generate builtins concurrently on separate threads in mksnapshot track concurrent recompilation artificial compilation delay in ms max number of threads that concurrent Turbofan can use(0 for unbounded)") DEFINE_BOOL( stress_concurrent_inlining
#define DCHECK_NOT_NULL(val)
Definition logging.h:492
#define DCHECK(condition)
Definition logging.h:482
#define DCHECK_EQ(v1, v2)
Definition logging.h:485