OLD | NEW |
---|---|
(Empty) | |
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 | |
5 #include "src/compiler/common-operator.h" | |
6 #include "src/compiler/graph.h" | |
7 #include "src/compiler/loop-peeling.h" | |
8 #include "src/compiler/node.h" | |
9 #include "src/compiler/node-marker.h" | |
10 #include "src/compiler/node-properties-inl.h" | |
11 #include "src/zone.h" | |
12 | |
13 namespace v8 { | |
14 namespace internal { | |
15 namespace compiler { | |
16 | |
17 struct Peeling { | |
18 // Maps a node to its index in the {pairs} vector. | |
19 NodeMarker<size_t> node_map; | |
20 // The vector which contains the mapped nodes. | |
21 NodeVector* pairs; | |
22 | |
23 Peeling(Graph* graph, Zone* tmp_zone, size_t max, NodeVector* p) | |
24 : node_map(graph, max), pairs(p) {} | |
25 | |
26 Node* map(Node* node) { | |
27 if (node_map.Get(node) == 0) return node; | |
28 return pairs->at(node_map.Get(node)); | |
29 } | |
30 | |
31 void Insert(Node* original, Node* copy) { | |
32 node_map.Set(original, 1 + pairs->size()); | |
33 pairs->push_back(original); | |
34 pairs->push_back(copy); | |
35 } | |
36 | |
37 void CopyNodes(Graph* graph, Zone* tmp_zone, Node* dead, NodeRange nodes) { | |
38 NodeVector inputs(tmp_zone); | |
39 // Copy all the nodes first. | |
40 for (Node* node : nodes) { | |
41 inputs.clear(); | |
42 for (Node* input : node->inputs()) inputs.push_back(map(input)); | |
43 Insert(node, graph->NewNode(node->op(), node->InputCount(), &inputs[0])); | |
44 } | |
45 | |
46 // Fix remaining inputs of the copies. | |
47 for (Node* original : nodes) { | |
48 Node* copy = pairs->at(node_map.Get(original)); | |
49 for (int i = 0; i < copy->InputCount(); i++) { | |
50 copy->ReplaceInput(i, map(original->InputAt(i))); | |
51 } | |
52 } | |
53 } | |
54 | |
55 bool Marked(Node* node) { return node_map.Get(node) > 0; } | |
56 }; | |
57 | |
58 | |
59 class PeeledIterationImpl : public PeeledIteration { | |
60 public: | |
61 NodeVector node_pairs_; | |
62 explicit PeeledIterationImpl(Zone* zone) : node_pairs_(zone) {} | |
63 }; | |
64 | |
65 | |
66 Node* PeeledIteration::map(Node* node) { | |
67 // TODO(turbofan): we use a simple linear search, since the peeled iteration | |
68 // is really only used in testing. | |
69 PeeledIterationImpl* impl = static_cast<PeeledIterationImpl*>(this); | |
Benedikt Meurer
2015/01/16 17:55:36
How about avoiding the PeeledIterationImpl and jus
titzer
2015/01/19 09:47:12
Was hoping to hide the implementation for now, sin
| |
70 for (size_t i = 0; i < impl->node_pairs_.size(); i += 2) { | |
71 if (impl->node_pairs_[i] == node) return impl->node_pairs_[i + 1]; | |
72 } | |
73 return node; | |
74 } | |
75 | |
76 | |
77 PeeledIteration* LoopPeeler::Peel(Graph* graph, CommonOperatorBuilder* common, | |
78 LoopTree* loop_tree, LoopTree::Loop* loop, | |
79 Zone* tmp_zone) { | |
80 PeeledIterationImpl* iter = new (tmp_zone) PeeledIterationImpl(tmp_zone); | |
81 Peeling peeling(graph, tmp_zone, loop->TotalSize() * 2 + 2, | |
82 &iter->node_pairs_); | |
83 | |
84 //============================================================================ | |
85 // Construct the peeled iteration. | |
86 //============================================================================ | |
87 Node* dead = graph->NewNode(common->Dead()); | |
88 | |
89 // Map the loop header nodes to their entry values. | |
90 for (Node* node : loop_tree->HeaderNodes(loop)) { | |
91 // TODO(titzer): assuming loop entry at index 0. | |
92 peeling.Insert(node, node->InputAt(0)); | |
93 } | |
94 | |
95 // Copy all the nodes of loop body for the peeled iteration. | |
96 peeling.CopyNodes(graph, tmp_zone, dead, loop_tree->BodyNodes(loop)); | |
97 | |
98 //============================================================================ | |
99 // Replace the entry to the loop with the output of the peeled iteration. | |
100 //============================================================================ | |
101 Node* loop_node = loop_tree->GetLoopControl(loop); | |
102 Node* new_entry; | |
103 int backedges = loop_node->InputCount() - 1; | |
104 if (backedges > 1) { | |
105 // Multiple backedges from original loop, therefore multiple output edges | |
106 // from the peeled iteration. | |
107 NodeVector inputs(tmp_zone); | |
108 for (int i = 1; i < loop_node->InputCount(); i++) { | |
109 inputs.push_back(peeling.map(loop_node->InputAt(i))); | |
110 } | |
111 Node* merge = | |
112 graph->NewNode(common->Merge(backedges), backedges, &inputs[0]); | |
113 | |
114 // Merge values from the multiple output edges of the peeled iteration. | |
115 for (Node* node : loop_tree->HeaderNodes(loop)) { | |
116 if (node->opcode() == IrOpcode::kLoop) continue; // already done. | |
117 inputs.clear(); | |
118 for (int i = 0; i < backedges; i++) { | |
119 inputs.push_back(peeling.map(node->InputAt(1 + i))); | |
120 } | |
121 for (Node* input : inputs) { | |
122 if (input != inputs[0]) { // Non-redundant phi. | |
123 inputs.push_back(merge); | |
124 const Operator* op = common->ResizeMergeOrPhi(node->op(), backedges); | |
125 Node* phi = graph->NewNode(op, backedges + 1, &inputs[0]); | |
126 node->ReplaceInput(0, phi); | |
127 break; | |
128 } | |
129 } | |
130 } | |
131 new_entry = merge; | |
132 } else { | |
133 // Only one backedge, simply replace the input to loop with output of | |
134 // peeling. | |
135 for (Node* node : loop_tree->HeaderNodes(loop)) { | |
136 node->ReplaceInput(0, peeling.map(node->InputAt(0))); | |
137 } | |
138 new_entry = peeling.map(loop_node->InputAt(1)); | |
139 } | |
140 loop_node->ReplaceInput(0, new_entry); | |
141 | |
142 //============================================================================ | |
143 // Find the loop exit region. | |
144 //============================================================================ | |
145 NodeVector exits(tmp_zone); | |
146 Node* end = NULL; | |
147 for (Node* node : loop_tree->LoopNodes(loop)) { | |
148 for (Node* use : node->uses()) { | |
149 if (!loop_tree->Contains(loop, use)) { | |
150 if (node->opcode() == IrOpcode::kBranch && | |
151 (use->opcode() == IrOpcode::kIfTrue || | |
152 use->opcode() == IrOpcode::kIfFalse)) { | |
153 // This is a branch from inside the loop to outside the loop. | |
154 exits.push_back(use); | |
155 } | |
156 } | |
157 } | |
158 } | |
159 | |
160 if (exits.size() == 0) return iter; // no exits => NTL | |
161 | |
162 if (exits.size() == 1) { | |
163 // Only one exit, so {end} is that exit. | |
164 end = exits[0]; | |
165 } else { | |
166 // {end} should be the common merge from the exits. | |
167 NodeVector rets(tmp_zone); | |
168 for (Node* exit : exits) { | |
169 Node* found = NULL; | |
170 for (Node* use : exit->uses()) { | |
171 if (use->opcode() == IrOpcode::kMerge) { | |
172 found = use; | |
173 if (end == NULL) { | |
174 end = found; | |
175 } else { | |
176 CHECK_EQ(end, found); // it should be unique! | |
177 } | |
178 } else if (use->opcode() == IrOpcode::kReturn) { | |
179 found = use; | |
180 rets.push_back(found); | |
181 } | |
182 } | |
183 // There should be a merge or a return for each exit. | |
184 CHECK_NE(NULL, found); | |
185 } | |
186 // Return nodes, the end merge, and the phis associated with the end merge | |
187 // must be duplicated as well. | |
188 for (Node* node : rets) exits.push_back(node); | |
189 if (end != NULL) { | |
190 exits.push_back(end); | |
191 for (Node* use : end->uses()) { | |
192 if (IrOpcode::IsPhiOpcode(use->opcode())) exits.push_back(use); | |
193 } | |
194 } | |
195 } | |
196 | |
197 //============================================================================ | |
198 // Duplicate the loop exit region and add a merge. | |
199 //============================================================================ | |
200 NodeRange exit_range(&exits[0], &exits[0] + exits.size()); | |
201 peeling.CopyNodes(graph, tmp_zone, dead, exit_range); | |
202 | |
203 Node* merge = graph->NewNode(common->Merge(2), end, peeling.map(end)); | |
204 end->ReplaceUses(merge); | |
205 merge->ReplaceInput(0, end); // HULK SMASH!! | |
206 | |
207 // Find and update all the edges into either the loop or exit region. | |
208 for (int i = 0; i < 2; i++) { | |
209 NodeRange range = i == 0 ? loop_tree->LoopNodes(loop) : exit_range; | |
210 ZoneVector<Edge> value_edges(tmp_zone); | |
211 ZoneVector<Edge> effect_edges(tmp_zone); | |
212 | |
213 for (Node* node : range) { | |
214 // Gather value and effect edges from outside the region. | |
215 for (Edge edge : node->use_edges()) { | |
216 if (!peeling.Marked(edge.from())) { | |
217 // Edge from outside the loop into the region. | |
218 if (NodeProperties::IsValueEdge(edge) || | |
219 NodeProperties::IsContextEdge(edge)) { | |
220 value_edges.push_back(edge); | |
221 } else if (NodeProperties::IsEffectEdge(edge)) { | |
222 effect_edges.push_back(edge); | |
223 } else { | |
224 // don't do anything for control edges. | |
225 // TODO(titzer): should update control edges to peeled? | |
226 } | |
227 } | |
228 } | |
229 | |
230 // Update all the value and effect edges at once. | |
231 if (!value_edges.empty()) { | |
232 // TODO(titzer): machine type is wrong here. | |
233 Node* phi = graph->NewNode(common->Phi(kMachAnyTagged, 2), node, | |
234 peeling.map(node), merge); | |
235 for (Edge edge : value_edges) edge.UpdateTo(phi); | |
236 value_edges.clear(); | |
237 } | |
238 if (!effect_edges.empty()) { | |
239 Node* effect_phi = graph->NewNode(common->EffectPhi(2), node, | |
240 peeling.map(node), merge); | |
241 for (Edge edge : effect_edges) edge.UpdateTo(effect_phi); | |
242 effect_edges.clear(); | |
243 } | |
244 } | |
245 } | |
246 | |
247 return iter; | |
248 } | |
249 | |
250 } // namespace compiler | |
251 } // namespace internal | |
252 } // namespace v8 | |
OLD | NEW |