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

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: Recursive traversal implementation. 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.
titzer 2013/07/18 12:22:58 These comments are probably better located on thei
Massi 2013/07/23 07:57:50 Done.
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
109 static const int kIsProcessed = 0;
110 bool IsProcessed() {
111 return current_successor_ == kIsProcessed;
112 }
113 void FlagAsProcessed() {
114 current_successor_ = kIsProcessed;
115 }
116 bool CheckLoopPathsRecursively(InductionVariableBlocksTable* table,
117 bool* unsafe) {
titzer 2013/07/18 12:22:58 This is much more readable, good. I think we can d
Massi 2013/07/23 07:57:50 We are going towards a solution where we iterate t
118 FlagAsProcessed();
119
120 if (has_check()) {
121 // We found a check so this path is safe and we can backtrack.
122 return true;
123 }
124
125 for (int i = 0; i < block()->end()->SuccessorCount(); i ++) {
126 Element* next = table->at(block()->end()->SuccessorAt(i));
127
128 if (!next->is_in_loop()) {
129 if (!next->is_proper_exit()) {
130 // We found a path that exits the loop early, and is not the exit
131 // related to the induction limit, therefore hoisting checks is
132 // an optimistic assumption.
133 *unsafe = true;
134 }
135 return true;
136 }
137
138 if (next->is_start()) {
139 // We found a path that does one loop iteration without meeting any
140 // check, therefore hoisting checks would be likely to cause
141 // unnecessary deopts.
142 return false;
143 }
144
145 // Continue the traversal on the current successor.
146 if (!next->CheckLoopPathsRecursively(table, unsafe)) {
147 // Propagate failure.
148 return false;
149 }
150 }
151
152 // We explored all successors with no failures so this path is ok.
153 return true;
154 }
155
156
157 Element()
158 : block_(NULL), current_successor_(kNoBlock), backtrack_to_(kNoBlock),
159 is_start_(false), is_proper_exit_(false), has_check_(false),
160 additional_limit_() {}
161
162 private:
163 HBasicBlock* block_;
164 int current_successor_;
165 int backtrack_to_;
166 bool is_start_;
167 bool is_proper_exit_;
168 bool is_in_loop_;
169 bool has_check_;
170 InductionVariableLimitUpdate additional_limit_;
171 };
172
173 HGraph* graph() { return graph_; }
174 HBasicBlock* loop_header() { return loop_header_; }
175 Element* at(int index) { return &(elements_.at(index)); }
176 Element* at(HBasicBlock* block) { return at(block->block_id()); }
177
178 void AddCheckAt(HBasicBlock* block) {
179 at(block->block_id())->set_has_check();
180 }
181
182 void InitializeLoop(InductionVariableData* data) {
183 for (int i = 0; i < graph()->blocks()->length(); i++) {
184 at(i)->InitializeLoop(data);
185 }
186 loop_header_ = data->phi()->block()->current_loop()->loop_header();
187 }
188
189 void ClearIterationData() {
190 ASSERT(loop_header() != NULL);
191 HLoopInformation* loop = loop_header()->loop_information();
192 for (int i = 0; i < loop->blocks()->length(); i++) {
193 at(loop->blocks()->at(i)->block_id())->ClearIterationData();
194 }
195 }
196
197 /*
198 * This method checks if it is appropriate to hoist the bounds checks on an
199 * induction variable out of the loop.
200 * The problem is that in the loop code graph there could be execution paths
201 * where the check is not performed, but hoisting the check has the same
202 * semantics as performing it at every loop iteration, which could cause
203 * unnecessary check failures (which would mean unnecessary deoptimizations).
204 * The method returns true if there are no paths that perform an iteration
205 * (loop back to the header) without meeting a check.
206 * "unsafe" is set if early exit paths are found.
207 */
208 bool LoopPathsAreCheckedIteratively(bool* unsafe) {
titzer 2013/07/18 12:22:58 When you convert this method to use an iterator, p
Massi 2013/07/23 07:57:50 Done.
209 *unsafe = false;
210 int previous_block = Element::kNoBlock;
211 int current_block = loop_header()->block_id();
212 while (current_block != Element::kNoBlock) {
213 Element* current_element = at(current_block);
214 int next_block = Element::kNoBlock;
215
216 if (!current_element->is_in_loop()) {
217 if (!current_element->is_proper_exit()) {
218 // We found a path that exits the loop early, and is not the exit
219 // related to the induction limit, therefore hoisting checks is
220 // an optimistic assumption.
221 *unsafe = true;
222 }
223 next_block = previous_block;
224 } else if (current_element->is_start() &&
225 previous_block != Element::kNoBlock &&
226 previous_block != current_element->CurrentSuccessorBlock()) {
227 // We found a path that does one loop iteration without meeting any
228 // check, therefore hoisting checks would be likely to cause
229 // unnecessary deopts.
230 return false;
231 } else if (current_element->has_check()) {
232 // We found a check so this path is safe and we can backtrack.
233 next_block = previous_block;
234 } else {
235 // No special condition, just find the next block in the traversal.
236 next_block = current_element->ComputeNextBlock(previous_block);
237 }
238
239 previous_block = current_block;
240 current_block = next_block;
241 }
242 return true;
243 }
244
245 bool LoopPathsAreCheckedRecursively(bool* unsafe) {
246 return at(loop_header()->block_id())->CheckLoopPathsRecursively(
247 this, unsafe);
248 }
249
250 bool LoopPathsAreChecked(bool* unsafe) {
251 // return LoopPathsAreCheckedIteratively(unsafe);
252 return LoopPathsAreCheckedRecursively(unsafe);
253 }
254
255
256 explicit InductionVariableBlocksTable(HGraph* graph)
257 : graph_(graph), loop_header_(NULL),
258 elements_(graph->blocks()->length(), graph->zone()) {
259 for (int i = 0; i < graph->blocks()->length(); i++) {
260 Element element;
261 element.set_block(graph->blocks()->at(i));
262 elements_.Add(element, graph->zone());
263 ASSERT(at(i)->block()->block_id() == i);
264 }
265 }
266
267 // Tries to hoist a check out of its induction loop.
268 void ProcessRelatedChecks(
269 InductionVariableData::InductionVariableCheck* check,
270 InductionVariableData* data) {
271 HValue* length = check->check()->length();
272 ClearIterationData();
273 check->set_processed();
274 HBasicBlock* header =
275 data->phi()->block()->current_loop()->loop_header();
276 HBasicBlock* pre_header = header->predecessors()->at(0);
277 // Check that the limit is defined in the loop preheader.
278 if (!data->limit()->IsInteger32Constant()) {
279 HBasicBlock* limit_block = data->limit()->block();
280 if (limit_block != pre_header &&
281 !limit_block->Dominates(pre_header)) {
282 return;
283 }
284 }
285 // Check that the length and limit have compatible representations.
286 if (!(data->limit()->representation().Equals(
287 length->representation()) ||
288 data->limit()->IsInteger32Constant())) {
289 return;
290 }
291 // Check that the length is defined in the loop preheader.
292 if (check->check()->length()->block() != pre_header &&
293 !check->check()->length()->block()->Dominates(pre_header)) {
294 return;
295 }
296
297 // Add checks to the table.
298 for (InductionVariableData::InductionVariableCheck* current_check = check;
299 current_check != NULL;
300 current_check = current_check->next()) {
301 if (current_check->check()->length() != length) continue;
302
303 AddCheckAt(current_check->check()->block());
304 current_check->set_processed();
305 }
306
307 // Check that we will not cause unwanted deoptimizations.
308 bool unsafe;
309 bool failure = !LoopPathsAreChecked(&unsafe);
titzer 2013/07/18 12:22:58 If this method just returns a value, then you don'
Massi 2013/07/23 07:57:50 Done.
310 if (failure || (unsafe && !graph()->use_optimistic_licm())) {
311 return;
312 }
313
314 // We will do the hoisting, but we must see if the limit is "limit" or if
315 // all checks are done on constants: if all check are done against the same
316 // constant limit we will use that instead of the induction limit.
317 bool has_upper_constant_limit = true;
318 InductionVariableData::InductionVariableCheck* current_check = check;
319 int32_t upper_constant_limit =
320 current_check != NULL && current_check->HasUpperLimit() ?
321 current_check->upper_limit() : 0;
322 while (current_check != NULL) {
323 if (check->HasUpperLimit()) {
324 if (check->upper_limit() != upper_constant_limit) {
325 has_upper_constant_limit = false;
326 }
327 } else {
328 has_upper_constant_limit = false;
329 }
330
331 current_check->check()->set_skip_check();
332 current_check = current_check->next();
333 }
334
335 // Choose the appropriate limit.
336 HValue* limit = data->limit();
337 if (has_upper_constant_limit) {
338 HConstant* new_limit = new(pre_header->graph()->zone()) HConstant(
339 upper_constant_limit, length->representation());
340 new_limit->InsertBefore(pre_header->end());
341 limit = new_limit;
342 }
343
344 // If necessary, redefine the limit in the preheader.
345 if (limit->IsInteger32Constant() &&
346 limit->block() != pre_header &&
347 !limit->block()->Dominates(pre_header)) {
348 HConstant* new_limit = new(pre_header->graph()->zone()) HConstant(
349 limit->GetInteger32Constant(), length->representation());
350 new_limit->InsertBefore(pre_header->end());
351 limit = new_limit;
352 }
353
354 // Do the hoisting.
355 HBoundsCheck* hoisted_check = new(pre_header->zone()) HBoundsCheck(
356 limit, check->check()->length());
357 hoisted_check->InsertBefore(pre_header->end());
358 hoisted_check->set_allow_equality(true);
359 }
360
361 void CollectInductionVariableData(HBasicBlock* bb) {
362 bool additional_limit = false;
363
364 for (int i = 0; i < bb->phis()->length(); i++) {
365 HPhi* phi = bb->phis()->at(i);
366 phi->DetectInductionVariable();
367 }
368
369 additional_limit = InductionVariableData::ComputeInductionVariableLimit(
370 bb, at(bb)->additional_limit());
371
372 if (additional_limit) {
373 at(bb)->additional_limit()->updated_variable->
374 UpdateAdditionalLimit(at(bb)->additional_limit());
375 }
376
377 for (HInstruction* i = bb->first(); i != NULL; i = i->next()) {
378 if (!i->IsBoundsCheck()) continue;
379 HBoundsCheck* check = HBoundsCheck::cast(i);
380 InductionVariableData::BitwiseDecompositionResult decomposition;
381 InductionVariableData::DecomposeBitwise(check->index(), &decomposition);
382 if (!decomposition.base->IsPhi()) continue;
383 HPhi* phi = HPhi::cast(decomposition.base);
384
385 if (!phi->IsInductionVariable()) continue;
386 InductionVariableData* data = phi->induction_variable_data();
387
388 // For now ignore loops decrementing the index.
389 if (data->increment() <= 0) continue;
390 if (!data->LowerLimitIsNonNegativeConstant()) continue;
391
392 // TODO(mmassi): skip OSR values for check->length().
393 if (check->length() == data->limit() ||
394 check->length() == data->additional_upper_limit()) {
395 check->set_skip_check();
396 continue;
397 }
398
399 if (!phi->IsLimitedInductionVariable()) continue;
400
401 int32_t limit = data->ComputeUpperLimit(decomposition.and_mask,
402 decomposition.or_mask);
403 phi->induction_variable_data()->AddCheck(check, limit);
404 }
405
406 for (int i = 0; i < bb->dominated_blocks()->length(); i++) {
407 CollectInductionVariableData(bb->dominated_blocks()->at(i));
408 }
409
410 if (additional_limit) {
411 at(bb->block_id())->additional_limit()->updated_variable->
412 UpdateAdditionalLimit(at(bb->block_id())->additional_limit());
413 }
414 }
415
416 void EliminateRedundantBoundsChecks(HBasicBlock* bb) {
417 for (int i = 0; i < bb->phis()->length(); i++) {
418 HPhi* phi = bb->phis()->at(i);
419 if (!phi->IsLimitedInductionVariable()) continue;
420
421 InductionVariableData* induction_data = phi->induction_variable_data();
422 InductionVariableData::ChecksRelatedToLength* current_length_group =
423 induction_data->checks();
424 while (current_length_group != NULL) {
425 current_length_group->CloseCurrentBlock();
426 InductionVariableData::InductionVariableCheck* current_base_check =
427 current_length_group->checks();
428 InitializeLoop(induction_data);
429
430 while (current_base_check != NULL) {
431 ProcessRelatedChecks(current_base_check, induction_data);
432 while (current_base_check != NULL &&
433 current_base_check->processed()) {
434 current_base_check = current_base_check->next();
435 }
436 }
437
438 current_length_group = current_length_group->next();
439 }
440 }
441 }
442
443 private:
444 HGraph* graph_;
445 HBasicBlock* loop_header_;
446 ZoneList<Element> elements_;
447 };
448
449
450 void HGraph::EliminateRedundantBoundsChecksUsingInductionVariables() {
451 InductionVariableBlocksTable table(this);
452 table.CollectInductionVariableData(entry_block());
453 for (int i = 0; i < blocks()->length(); i++) {
454 table.EliminateRedundantBoundsChecks(blocks()->at(i));
455 }
456 }
457
458 } } // namespace v8::internal
459
OLDNEW
« no previous file with comments | « src/hydrogen.cc ('k') | src/hydrogen-instructions.h » ('j') | src/hydrogen-instructions.h » ('J')

Powered by Google App Engine
This is Rietveld 408576698