Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(153)

Side by Side Diff: src/hydrogen-bounds-check-removal.cc

Issue 17568015: New array bounds check elimination pass (focused on induction variables and bitwise operations). (Closed) Base URL: https://v8.googlecode.com/svn/branches/bleeding_edge
Patch Set: Actually removed a method, and applied a small fix. Created 7 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
1 // Copyright 2013 the V8 project authors. All rights reserved.
2 // Redistribution and use in source and binary forms, with or without
3 // modification, are permitted provided that the following conditions are
4 // met:
5 //
6 // * Redistributions of source code must retain the above copyright
7 // notice, this list of conditions and the following disclaimer.
8 // * Redistributions in binary form must reproduce the above
9 // copyright notice, this list of conditions and the following
10 // disclaimer in the documentation and/or other materials provided
11 // with the distribution.
12 // * Neither the name of Google Inc. nor the names of its
13 // contributors may be used to endorse or promote products derived
14 // from this software without specific prior written permission.
15 //
16 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27
28 #include "hydrogen.h"
29
30 namespace v8 {
31 namespace internal {
32
33 /*
34 * This class is a table with one element for eack basic block.
35 *
36 * It is used to check if, inside one loop, all execution paths contain
37 * a bounds check for a particular [index, length] combination.
38 * The reason is that if there is a path that stays in the loop without
39 * executing a check then the check cannot be hoisted out of the loop (it
40 * would likely fail and cause a deopt for no good reason).
41 * We also check is there are paths that exit the loop early, and if yes we
42 * perform the hoisting only if graph()->use_optimistic_licm() is true.
43 * The reason is that such paths are realtively common and harmless (like in
44 * a "search" method that scans an array until an element is found), but in
45 * some cases they could cause a deopt if we hoist the check so this is a
46 * situation we need to detect.
47 *
48 * InitializeLoop() sets up the table for a given loop.
49 * ClearIterationData() prepares the table for a new check.
50 * LoopPathsAreChecked() explores the loop graph searching for paths that do
51 * not contain a check ahains a given induction variable.
52 * ProcessRelatedChecks() is the "main" method that processes all the checks
53 * related to a given induction variable inside its induction loop.
54 */
55 class InductionVariableBlocksTable BASE_EMBEDDED {
56 public:
57 class Element {
58 public:
59 static const int kNoBlock = -1;
60
61 HBasicBlock* block() { return block_; }
62 void set_block(HBasicBlock* block) { block_ = block; }
63 bool is_start() { return is_start_; }
64 bool is_proper_exit() { return is_proper_exit_; }
65 bool is_in_loop() { return is_in_loop_; }
66 bool has_check() { return has_check_; }
67 void set_has_check() { has_check_ = true; }
68 InductionVariableLimitUpdate* additional_limit() {
69 return &additional_limit_;
70 }
71
72 void InitializeLoop(InductionVariableData* data) {
73 ASSERT(data->limit() != NULL);
74 HLoopInformation* loop = data->phi()->block()->current_loop();
75 current_successor_ = kNoBlock;
76 backtrack_to_ = kNoBlock;
77 is_start_ = (block() == loop->loop_header());
78 is_proper_exit_ = (block() == data->induction_exit_target());
79 is_in_loop_ = loop->IsNestedInThisLoop(block()->current_loop());
80 has_check_ = false;
81 }
82
83 void ClearIterationData() {
84 current_successor_ = kNoBlock;
85 backtrack_to_ = kNoBlock;
86 }
87
88 int ComputeNextBlock(int from_block) {
89 if (current_successor_ == kNoBlock) {
90 backtrack_to_ = from_block;
91 }
92 current_successor_++;
93 if (CurrentSuccessorBlock() != kNoBlock) {
94 return CurrentSuccessorBlock();
95 } else {
96 return backtrack_to_;
97 }
98 }
99
100 int CurrentSuccessorBlock() {
101 if (current_successor_ < block()->end()->SuccessorCount()) {
102 return block()->end()->SuccessorAt(current_successor_)->block_id();
103 } else {
104 return kNoBlock;
105 }
106 }
107
108 Element()
109 : block_(NULL), current_successor_(kNoBlock), backtrack_to_(kNoBlock),
110 is_start_(false), is_proper_exit_(false), has_check_(false),
111 additional_limit_() {}
112
113 private:
114 HBasicBlock* block_;
115 int current_successor_;
116 int backtrack_to_;
117 bool is_start_;
118 bool is_proper_exit_;
119 bool is_in_loop_;
120 bool has_check_;
121 InductionVariableLimitUpdate additional_limit_;
122 };
123
124 HGraph* graph() { return graph_; }
125 HBasicBlock* loop_header() { return loop_header_; }
126 Element* at(int index) { return &(elements_.at(index)); }
127 Element* at(HBasicBlock* block) { return at(block->block_id()); }
128
129 void AddCheckAt(HBasicBlock* block) {
130 at(block->block_id())->set_has_check();
131 }
132
133 void InitializeLoop(InductionVariableData* data) {
134 for (int i = 0; i < graph()->blocks()->length(); i++) {
135 at(i)->InitializeLoop(data);
136 }
137 loop_header_ = data->phi()->block()->current_loop()->loop_header();
138 }
139
140 void ClearIterationData() {
141 ASSERT(loop_header() != NULL);
142 HLoopInformation* loop = loop_header()->loop_information();
143 for (int i = 0; i < loop->blocks()->length(); i++) {
144 at(loop->blocks()->at(i)->block_id())->ClearIterationData();
145 }
146 }
147
148 /*
149 * This method checks if it is appropriate to hoist the bounds checks on an
150 * induction variable out of the loop.
151 * The problem is that in the loop code graph there could be execution paths
152 * where the check is not performed, but hoisting the check has the same
153 * semantics as performing it at every loop iteration, which could cause
154 * unnecessary check failures (which would mean unnecessary deoptimizations).
155 * The method returns true if there are no paths that perform an iteration
156 * (loop back to the header) without meeting a check.
157 * "unsafe" is set if early exit paths are found.
158 */
159 bool LoopPathsAreChecked(bool* unsafe) {
160 *unsafe = false;
161 int previous_block = Element::kNoBlock;
162 int current_block = loop_header()->block_id();
163 while (current_block != Element::kNoBlock) {
164 Element* current_element = at(current_block);
165 int next_block = Element::kNoBlock;
166
167 if (!current_element->is_in_loop()) {
168 if (!current_element->is_proper_exit()) {
169 // We found a path that exits the loop early, and is not the exit
170 // related to the induction limit, therefore hoisting checks is
171 // an optimistic assumption.
172 *unsafe = true;
173 }
174 next_block = previous_block;
175 } else if (current_element->is_start() &&
176 previous_block != Element::kNoBlock &&
177 previous_block != current_element->CurrentSuccessorBlock()) {
178 // We found a path that does one loop iteration without meeting any
179 // check, therefore hoisting checks would be likely to cause
180 // unnecessary deopts.
181 return false;
182 } else if (current_element->has_check()) {
183 // We found a check so this path is safe and we can backtrack.
184 next_block = previous_block;
185 } else {
186 // No special condition, just find the next block in the traversal.
187 next_block = current_element->ComputeNextBlock(previous_block);
188 }
189
190 previous_block = current_block;
191 current_block = next_block;
192 }
193 return true;
194 }
195
196 explicit InductionVariableBlocksTable(HGraph* graph)
197 : graph_(graph), loop_header_(NULL),
198 elements_(graph->blocks()->length(), graph->zone()) {
199 for (int i = 0; i < graph->blocks()->length(); i++) {
200 Element element;
201 element.set_block(graph->blocks()->at(i));
202 elements_.Add(element, graph->zone());
203 ASSERT(at(i)->block()->block_id() == i);
204 }
205 }
206
207 // Tries to hoist a check out of its induction loop.
208 void ProcessRelatedChecks(
209 InductionVariableData::InductionVariableCheck* check,
210 InductionVariableData* data) {
211 HValue* length = check->check()->length();
212 ClearIterationData();
213 check->set_processed();
214 HBasicBlock* header =
215 data->phi()->block()->current_loop()->loop_header();
216 HBasicBlock* pre_header = header->predecessors()->at(0);
217 // Check that the limit is defined in the loop preheader.
218 if (!data->limit()->IsInteger32Constant()) {
219 HBasicBlock* limit_block = data->limit()->block();
220 if (limit_block != pre_header &&
221 !limit_block->Dominates(pre_header)) {
222 return;
223 }
224 }
225 // Check that the length and limit have compatible representations.
226 if (!(data->limit()->representation().Equals(
227 length->representation()) ||
228 data->limit()->IsInteger32Constant())) {
229 return;
230 }
231 // Check that the length is defined in the loop preheader.
232 if (check->check()->length()->block() != pre_header &&
233 !check->check()->length()->block()->Dominates(pre_header)) {
234 return;
235 }
236
237 // Add checks to the table.
238 for (InductionVariableData::InductionVariableCheck* current_check = check;
239 current_check != NULL;
240 current_check = current_check->next()) {
241 if (current_check->check()->length() != length) continue;
242
243 AddCheckAt(current_check->check()->block());
244 current_check->set_processed();
245 }
246
247 // Check that we will not cause unwanted deoptimizations.
248 bool unsafe;
249 bool failure = !LoopPathsAreChecked(&unsafe);
250 if (failure || (unsafe && !graph()->use_optimistic_licm())) {
251 return;
252 }
253
254 // We will do the hoisting, but we must see if the limit is "limit" or if
255 // all checks are done on constants: if all check are done against the same
256 // constant limit we will use that instead of the induction limit.
257 bool has_upper_constant_limit = true;
258 InductionVariableData::InductionVariableCheck* current_check = check;
259 int32_t upper_constant_limit =
260 current_check != NULL && current_check->HasUpperLimit() ?
261 current_check->upper_limit() : 0;
262 while (current_check != NULL) {
263 if (check->HasUpperLimit()) {
264 if (check->upper_limit() != upper_constant_limit) {
265 has_upper_constant_limit = false;
266 }
267 } else {
268 has_upper_constant_limit = false;
269 }
270
271 current_check->check()->set_skip_check();
272 current_check = current_check->next();
273 }
274
275 // Choose the appropriate limit.
276 HValue* limit = data->limit();
277 if (has_upper_constant_limit) {
278 HConstant* new_limit = new(pre_header->graph()->zone()) HConstant(
279 upper_constant_limit, length->representation());
280 new_limit->InsertBefore(pre_header->end());
281 limit = new_limit;
282 }
283
284 // If necessary, redefine the limit in the preheader.
285 if (limit->IsInteger32Constant() &&
286 limit->block() != pre_header &&
287 !limit->block()->Dominates(pre_header)) {
288 HConstant* new_limit = new(pre_header->graph()->zone()) HConstant(
289 limit->GetInteger32Constant(), length->representation());
290 new_limit->InsertBefore(pre_header->end());
291 limit = new_limit;
292 }
293
294 // Do the hoisting.
295 HBoundsCheck* hoisted_check = new(pre_header->zone()) HBoundsCheck(
296 limit, check->check()->length());
297 hoisted_check->InsertBefore(pre_header->end());
298 hoisted_check->set_allow_equality(true);
299 }
300
301 void CollectInductionVariableData(HBasicBlock* bb) {
302 bool additional_limit = false;
303
304 for (int i = 0; i < bb->phis()->length(); i++) {
305 HPhi* phi = bb->phis()->at(i);
306 phi->DetectInductionVariable();
307 }
308
309 additional_limit = InductionVariableData::ComputeInductionVariableLimit(
310 bb, at(bb)->additional_limit());
311
312 if (additional_limit) {
313 at(bb)->additional_limit()->updated_variable->
314 UpdateAdditionalLimit(at(bb)->additional_limit());
315 }
316
317 for (HInstruction* i = bb->first(); i != NULL; i = i->next()) {
318 if (!i->IsBoundsCheck()) continue;
319 HBoundsCheck* check = HBoundsCheck::cast(i);
320 InductionVariableData::BitwiseDecompositionResult decomposition;
321 InductionVariableData::DecomposeBitwise(check->index(), &decomposition);
322 if (!decomposition.base->IsPhi()) continue;
323 HPhi* phi = HPhi::cast(decomposition.base);
324
325 if (!phi->IsInductionVariable()) continue;
326 InductionVariableData* data = phi->induction_variable_data();
327
328 // For now ignore loops decrementing the index.
329 if (data->increment() <= 0) continue;
330 if (!data->lower_limit_is_non_negative_constant()) continue;
331
332 // TODO(mmassi): skip OSR values for check->length().
333 if (check->length() == data->limit() ||
334 check->length() == data->additional_upper_limit()) {
335 check->set_skip_check();
336 continue;
337 }
338
339 if (!phi->IsLimitedInductionVariable()) continue;
340
341 int32_t limit = data->ComputeUpperLimit(decomposition.and_mask,
342 decomposition.or_mask);
343 phi->induction_variable_data()->AddCheck(check, limit);
344 }
345
346 for (int i = 0; i < bb->dominated_blocks()->length(); i++) {
347 CollectInductionVariableData(bb->dominated_blocks()->at(i));
348 }
349
350 if (additional_limit) {
351 at(bb->block_id())->additional_limit()->updated_variable->
352 UpdateAdditionalLimit(at(bb->block_id())->additional_limit());
353 }
354 }
355
356 void EliminateRedundantBoundsChecks(HBasicBlock* bb) {
357 for (int i = 0; i < bb->phis()->length(); i++) {
358 HPhi* phi = bb->phis()->at(i);
359 if (!phi->IsLimitedInductionVariable()) continue;
360
361 InductionVariableData* induction_data = phi->induction_variable_data();
362 InductionVariableData::ChecksRelatedToLength* current_length_group =
363 induction_data->checks();
364 while (current_length_group != NULL) {
365 current_length_group->CloseCurrentBlock();
366 InductionVariableData::InductionVariableCheck* current_base_check =
367 current_length_group->checks();
368 InitializeLoop(induction_data);
369
370 while (current_base_check != NULL) {
371 ProcessRelatedChecks(current_base_check, induction_data);
372 while (current_base_check != NULL &&
373 current_base_check->processed()) {
374 current_base_check = current_base_check->next();
375 }
376 }
377
378 current_length_group = current_length_group->next();
379 }
380 }
381 }
382
383 private:
384 HGraph* graph_;
385 HBasicBlock* loop_header_;
386 ZoneList<Element> elements_;
387 };
388
389
390 void HGraph::EliminateRedundantBoundsChecksUsingInductionVariables() {
391 InductionVariableBlocksTable table(this);
392 table.CollectInductionVariableData(entry_block());
393 for (int i = 0; i < blocks()->length(); i++) {
394 table.EliminateRedundantBoundsChecks(blocks()->at(i));
395 }
396 }
397
398 } } // namespace v8::internal
399
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698