v8
V8 is Google’s open source high-performance JavaScript and WebAssembly engine, written in C++.
Loading...
Searching...
No Matches
loop-peeling.cc
Go to the documentation of this file.
1// Copyright 2015 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
13#include "src/compiler/node.h"
15#include "src/zone/zone.h"
16
17// Loop peeling is an optimization that copies the body of a loop, creating
18// a new copy of the body called the "peeled iteration" that represents the
19// first iteration. Beginning with a loop as follows:
20
21// E
22// | A
23// | | (backedges)
24// | +---------------|---------------------------------+
25// | | +-------------|-------------------------------+ |
26// | | | | +--------+ | |
27// | | | | | +----+ | | |
28// | | | | | | | | | |
29// ( Loop )<-------- ( phiA ) | | | |
30// | | | | | |
31// ((======P=================U=======|=|=====)) | |
32// (( | | )) | |
33// (( X <---------------------+ | )) | |
34// (( | )) | |
35// (( body | )) | |
36// (( | )) | |
37// (( Y <-----------------------+ )) | |
38// (( )) | |
39// ((===K====L====M==========================)) | |
40// | | | | |
41// | | +-----------------------------------------+ |
42// | +------------------------------------------------+
43// |
44// exit
45
46// The body of the loop is duplicated so that all nodes considered "inside"
47// the loop (e.g. {P, U, X, Y, K, L, M}) have a corresponding copies in the
48// peeled iteration (e.g. {P', U', X', Y', K', L', M'}). What were considered
49// backedges of the loop correspond to edges from the peeled iteration to
50// the main loop body, with multiple backedges requiring a merge.
51
52// Similarly, any exits from the loop body need to be merged with "exits"
53// from the peeled iteration, resulting in the graph as follows:
54
55// E
56// | A
57// | |
58// ((=====P'================U'===============))
59// (( ))
60// (( X'<-------------+ ))
61// (( | ))
62// (( peeled iteration | ))
63// (( | ))
64// (( Y'<-----------+ | ))
65// (( | | ))
66// ((===K'===L'====M'======|=|===============))
67// | | | | |
68// +--------+ +-+ +-+ | |
69// | | | | |
70// | Merge <------phi
71// | | |
72// | +-----+ |
73// | | | (backedges)
74// | | +---------------|---------------------------------+
75// | | | +-------------|-------------------------------+ |
76// | | | | | +--------+ | |
77// | | | | | | +----+ | | |
78// | | | | | | | | | | |
79// | ( Loop )<-------- ( phiA ) | | | |
80// | | | | | | |
81// | ((======P=================U=======|=|=====)) | |
82// | (( | | )) | |
83// | (( X <---------------------+ | )) | |
84// | (( | )) | |
85// | (( body | )) | |
86// | (( | )) | |
87// | (( Y <-----------------------+ )) | |
88// | (( )) | |
89// | ((===K====L====M==========================)) | |
90// | | | | | |
91// | | | +-----------------------------------------+ |
92// | | +------------------------------------------------+
93// | |
94// | |
95// +----+ +-+
96// | |
97// Merge
98// |
99// exit
100
101// Note that the boxes ((===)) above are not explicitly represented in the
102// graph, but are instead computed by the {LoopFinder}.
103
104namespace v8 {
105namespace internal {
106namespace compiler {
107
109 public:
111 explicit PeeledIterationImpl(Zone* zone) : node_pairs_(zone) {}
112};
113
114
116 // TODO(turbofan): we use a simple linear search, since the peeled iteration
117 // is really only used in testing.
118 PeeledIterationImpl* impl = static_cast<PeeledIterationImpl*>(this);
119 for (size_t i = 0; i < impl->node_pairs_.size(); i += 2) {
120 if (impl->node_pairs_[i] == node) return impl->node_pairs_[i + 1];
121 }
122 return node;
123}
124
126 if (!CanPeel(loop)) return nullptr;
127
128 //============================================================================
129 // Construct the peeled iteration.
130 //============================================================================
132 uint32_t estimated_peeled_size = 5 + loop->TotalSize() * 2;
133 NodeCopier copier(graph_, estimated_peeled_size, &iter->node_pairs_, 1);
134
135 Node* dead = graph_->NewNode(common_->Dead());
136
137 // Map the loop header nodes to their entry values.
138 for (Node* node : loop_tree_->HeaderNodes(loop)) {
139 copier.Insert(node, node->InputAt(kAssumedLoopEntryIndex));
140 }
141
142 // Copy all the nodes of loop body for the peeled iteration.
143 copier.CopyNodes(graph_, tmp_zone_, dead, loop_tree_->BodyNodes(loop),
145
146 //============================================================================
147 // Replace the entry to the loop with the output of the peeled iteration.
148 //============================================================================
149 Node* loop_node = loop_tree_->GetLoopControl(loop);
150 Node* new_entry;
151 int backedges = loop_node->InputCount() - 1;
152 if (backedges > 1) {
153 // Multiple backedges from original loop, therefore multiple output edges
154 // from the peeled iteration.
155 NodeVector inputs(tmp_zone_);
156 for (int i = 1; i < loop_node->InputCount(); i++) {
157 inputs.push_back(copier.map(loop_node->InputAt(i)));
158 }
159 Node* merge =
160 graph_->NewNode(common_->Merge(backedges), backedges, &inputs[0]);
161
162 // Merge values from the multiple output edges of the peeled iteration.
163 for (Node* node : loop_tree_->HeaderNodes(loop)) {
164 if (node->opcode() == IrOpcode::kLoop) continue; // already done.
165 inputs.clear();
166 for (int i = 0; i < backedges; i++) {
167 inputs.push_back(copier.map(node->InputAt(1 + i)));
168 }
169 for (Node* input : inputs) {
170 if (input != inputs[0]) { // Non-redundant phi.
171 inputs.push_back(merge);
172 const Operator* op = common_->ResizeMergeOrPhi(node->op(), backedges);
173 Node* phi = graph_->NewNode(op, backedges + 1, &inputs[0]);
174 node->ReplaceInput(0, phi);
175 break;
176 }
177 }
178 }
179 new_entry = merge;
180 } else {
181 // Only one backedge, simply replace the input to loop with output of
182 // peeling.
183 for (Node* node : loop_tree_->HeaderNodes(loop)) {
184 node->ReplaceInput(0, copier.map(node->InputAt(1)));
185 }
186 new_entry = copier.map(loop_node->InputAt(1));
187 }
188 loop_node->ReplaceInput(0, new_entry);
189
190 //============================================================================
191 // Change the exit and exit markers to merge/phi/effect-phi.
192 //============================================================================
193 for (Node* exit : loop_tree_->ExitNodes(loop)) {
194 switch (exit->opcode()) {
195 case IrOpcode::kLoopExit:
196 // Change the loop exit node to a merge node.
197 exit->ReplaceInput(1, copier.map(exit->InputAt(0)));
199 break;
200 case IrOpcode::kLoopExitValue:
201 // Change exit marker to phi.
202 exit->InsertInput(graph_->zone(), 1, copier.map(exit->InputAt(0)));
204 exit, common_->Phi(LoopExitValueRepresentationOf(exit->op()), 2));
205 break;
206 case IrOpcode::kLoopExitEffect:
207 // Change effect exit marker to effect phi.
208 exit->InsertInput(graph_->zone(), 1, copier.map(exit->InputAt(0)));
210 break;
211 default:
212 break;
213 }
214 }
215 return iter;
216}
217
219 // If the loop has nested loops, peel inside those.
220 if (!loop->children().empty()) {
221 for (LoopTree::Loop* inner_loop : loop->children()) {
222 PeelInnerLoops(inner_loop);
223 }
224 return;
225 }
226 // Only peel small-enough loops.
227 if (loop->TotalSize() > LoopPeeler::kMaxPeeledNodes) return;
228 if (v8_flags.trace_turbo_loop) {
229 PrintF("Peeling loop with header: ");
230 for (Node* node : loop_tree_->HeaderNodes(loop)) {
231 PrintF("%i ", node->id());
232 }
233 PrintF("\n");
234 }
235
236 Peel(loop);
237}
238
240 DCHECK_EQ(IrOpcode::kLoopExit, node->opcode());
241 // The exit markers take the loop exit as input. We iterate over uses
242 // and remove all the markers from the graph.
243 for (Edge edge : node->use_edges()) {
245 Node* marker = edge.from();
246 if (marker->opcode() == IrOpcode::kLoopExitValue) {
247 NodeProperties::ReplaceUses(marker, marker->InputAt(0));
248 marker->Kill();
249 } else if (marker->opcode() == IrOpcode::kLoopExitEffect) {
250 NodeProperties::ReplaceUses(marker, nullptr,
252 marker->Kill();
253 }
254 }
255 }
256 NodeProperties::ReplaceUses(node, nullptr, nullptr,
258 node->Kill();
259}
260
268
269// static
271 ZoneQueue<Node*> queue(tmp_zone);
272 BitVector visited(static_cast<int>(graph->NodeCount()), tmp_zone);
273 queue.push(graph->end());
274 while (!queue.empty()) {
275 Node* node = queue.front();
276 queue.pop();
277
278 if (node->opcode() == IrOpcode::kLoopExit) {
279 Node* control = NodeProperties::GetControlInput(node);
280 EliminateLoopExit(node);
281 if (!visited.Contains(control->id())) {
282 visited.Add(control->id());
283 queue.push(control);
284 }
285 } else {
286 for (int i = 0; i < node->op()->ControlInputCount(); i++) {
287 Node* control = NodeProperties::GetControlInput(node, i);
288 if (!visited.Contains(control->id())) {
289 visited.Add(control->id());
290 queue.push(control);
291 }
292 }
293 }
294 }
295}
296
297} // namespace compiler
298} // namespace internal
299} // namespace v8
bool Contains(int i) const
Definition bit-vector.h:180
void push_back(const T &value)
T * New(Args &&... args)
Definition zone.h:114
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 void EliminateLoopExit(Node *loop)
PeeledIteration * Peel(LoopTree::Loop *loop)
CommonOperatorBuilder *const common_
SourcePositionTable *const source_positions_
static void EliminateLoopExits(TFGraph *graph, Zone *tmp_zone)
static const size_t kMaxPeeledNodes
NodeOriginTable *const node_origins_
bool CanPeel(LoopTree::Loop *loop)
void PeelInnerLoops(LoopTree::Loop *loop)
const ZoneVector< Loop * > & children() const
Node * GetLoopControl(const Loop *loop)
NodeRange HeaderNodes(const Loop *loop)
const ZoneVector< Loop * > & outer_loops() const
NodeRange ExitNodes(const Loop *loop)
NodeRange BodyNodes(const Loop *loop)
void CopyNodes(TFGraph *graph, Zone *tmp_zone_, Node *dead, base::iterator_range< InputIterator > nodes, SourcePositionTable *source_positions, NodeOriginTable *node_origins)
Node * map(Node *node, uint32_t copy_index)
void Insert(Node *original, const NodeVector &new_copies)
static void ChangeOp(Node *node, const Operator *new_op)
static void ReplaceUses(Node *node, Node *value, Node *effect=nullptr, Node *success=nullptr, Node *exception=nullptr)
static Node * GetEffectInput(Node *node, int index=0)
static Node * GetControlInput(Node *node, int index=0)
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
NodeId id() const
Definition node.h:57
Node * NewNode(const Operator *op, int input_count, Node *const *inputs, bool incomplete=false)
Node * node
static const int kAssumedLoopEntryIndex
MachineRepresentation LoopExitValueRepresentationOf(const Operator *const op)
void PrintF(const char *format,...)
Definition utils.cc:39
V8_EXPORT_PRIVATE FlagValues v8_flags
#define DCHECK_EQ(v1, v2)
Definition logging.h:485