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

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: Reimplemented path scanning with dominator tree traversal (Thamks Ben!). 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
« no previous file with comments | « src/hydrogen.cc ('k') | src/hydrogen-instructions.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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 is_start_ = (block() == loop->loop_header());
76 is_proper_exit_ = (block() == data->induction_exit_target());
77 is_in_loop_ = loop->IsNestedInThisLoop(block()->current_loop());
78 has_check_ = false;
79 }
80
81
82 static const int kIsProcessed = 0;
83 bool IsProcessed() {
84 return current_successor_ == kIsProcessed;
85 }
86 void FlagAsProcessed() {
87 current_successor_ = kIsProcessed;
88 }
89 bool CheckLoopPathsRecursively(InductionVariableBlocksTable* table,
90 bool* unsafe) {
91 FlagAsProcessed();
92
93 if (has_check()) {
94 // We found a check so this path is safe and we can backtrack.
95 return true;
96 }
97
98 for (int i = 0; i < block()->end()->SuccessorCount(); i ++) {
99 Element* next = table->at(block()->end()->SuccessorAt(i));
100
101 if (!next->is_in_loop()) {
titzer 2013/07/22 13:03:10 This code is now redundant with CheckLoopPaths(Blo
Massi 2013/07/23 07:57:50 Done.
102 if (!next->is_proper_exit()) {
103 // We found a path that exits the loop early, and is not the exit
104 // related to the induction limit, therefore hoisting checks is
105 // an optimistic assumption.
106 *unsafe = true;
107 }
108 return true;
109 }
110
111 if (next->is_start()) {
112 // We found a path that does one loop iteration without meeting any
113 // check, therefore hoisting checks would be likely to cause
114 // unnecessary deopts.
115 return false;
116 }
117
118 // Continue the traversal on the current successor.
119 if (!next->CheckLoopPathsRecursively(table, unsafe)) {
120 // Propagate failure.
121 return false;
122 }
123 }
124
125 // We explored all successors with no failures so this path is ok.
126 return true;
127 }
128
129
130 Element()
131 : block_(NULL), current_successor_(kNoBlock), backtrack_to_(kNoBlock),
132 is_start_(false), is_proper_exit_(false), has_check_(false),
133 additional_limit_() {}
134
135 private:
136 HBasicBlock* block_;
137 int current_successor_;
138 int backtrack_to_;
139 bool is_start_;
140 bool is_proper_exit_;
141 bool is_in_loop_;
142 bool has_check_;
143 InductionVariableLimitUpdate additional_limit_;
144 };
145
146 HGraph* graph() { return graph_; }
147 HBasicBlock* loop_header() { return loop_header_; }
148 Element* at(int index) { return &(elements_.at(index)); }
149 Element* at(HBasicBlock* block) { return at(block->block_id()); }
150
151 void AddCheckAt(HBasicBlock* block) {
152 at(block->block_id())->set_has_check();
153 }
154
155 void InitializeLoop(InductionVariableData* data) {
156 for (int i = 0; i < graph()->blocks()->length(); i++) {
157 at(i)->InitializeLoop(data);
158 }
159 loop_header_ = data->phi()->block()->current_loop()->loop_header();
160 }
161
162
163 enum PathCheckResult {
titzer 2013/07/22 13:03:10 How about HOISTABLE, OPTIMISTICALLY_HOISTABLE, and
Massi 2013/07/23 07:57:50 Done.
164 OK,
165 UNSAFE,
166 FAILURE
167 };
168
169 /*
170 * This method checks if it is appropriate to hoist the bounds checks on an
171 * induction variable out of the loop.
172 * The problem is that in the loop code graph there could be execution paths
173 * where the check is not performed, but hoisting the check has the same
174 * semantics as performing it at every loop iteration, which could cause
175 * unnecessary check failures (which would mean unnecessary deoptimizations).
176 * The method returns OK if there are no paths that perform an iteration
177 * (loop back to the header) without meeting a check, or UNSAFE is set if
178 * early exit paths are found.
179 */
180 PathCheckResult CheckLoopPaths() {
titzer 2013/07/22 13:03:10 CheckHoistability?
Massi 2013/07/23 07:57:50 Done.
181 return CheckLoopPaths(loop_header());
182 }
183
184 explicit InductionVariableBlocksTable(HGraph* graph)
185 : graph_(graph), loop_header_(NULL),
186 elements_(graph->blocks()->length(), graph->zone()) {
187 for (int i = 0; i < graph->blocks()->length(); i++) {
188 Element element;
189 element.set_block(graph->blocks()->at(i));
190 elements_.Add(element, graph->zone());
191 ASSERT(at(i)->block()->block_id() == i);
192 }
193 }
194
195 // Tries to hoist a check out of its induction loop.
196 void ProcessRelatedChecks(
197 InductionVariableData::InductionVariableCheck* check,
198 InductionVariableData* data) {
199 HValue* length = check->check()->length();
200 check->set_processed();
201 HBasicBlock* header =
202 data->phi()->block()->current_loop()->loop_header();
203 HBasicBlock* pre_header = header->predecessors()->at(0);
204 // Check that the limit is defined in the loop preheader.
205 if (!data->limit()->IsInteger32Constant()) {
206 HBasicBlock* limit_block = data->limit()->block();
207 if (limit_block != pre_header &&
208 !limit_block->Dominates(pre_header)) {
209 return;
210 }
211 }
212 // Check that the length and limit have compatible representations.
213 if (!(data->limit()->representation().Equals(
214 length->representation()) ||
215 data->limit()->IsInteger32Constant())) {
216 return;
217 }
218 // Check that the length is defined in the loop preheader.
219 if (check->check()->length()->block() != pre_header &&
220 !check->check()->length()->block()->Dominates(pre_header)) {
221 return;
222 }
223
224 // Add checks to the table.
225 for (InductionVariableData::InductionVariableCheck* current_check = check;
226 current_check != NULL;
227 current_check = current_check->next()) {
228 if (current_check->check()->length() != length) continue;
229
230 AddCheckAt(current_check->check()->block());
231 current_check->set_processed();
232 }
233
234 // Check that we will not cause unwanted deoptimizations.
235 PathCheckResult check_result = CheckLoopPaths();
236 if (check_result == FAILURE ||
237 (check_result == UNSAFE && !graph()->use_optimistic_licm())) {
238 return;
239 }
240
241 // We will do the hoisting, but we must see if the limit is "limit" or if
242 // all checks are done on constants: if all check are done against the same
243 // constant limit we will use that instead of the induction limit.
244 bool has_upper_constant_limit = true;
245 InductionVariableData::InductionVariableCheck* current_check = check;
246 int32_t upper_constant_limit =
247 current_check != NULL && current_check->HasUpperLimit() ?
248 current_check->upper_limit() : 0;
249 while (current_check != NULL) {
250 if (check->HasUpperLimit()) {
251 if (check->upper_limit() != upper_constant_limit) {
252 has_upper_constant_limit = false;
253 }
254 } else {
255 has_upper_constant_limit = false;
256 }
257
258 current_check->check()->set_skip_check();
259 current_check = current_check->next();
260 }
261
262 // Choose the appropriate limit.
263 HValue* limit = data->limit();
264 if (has_upper_constant_limit) {
265 HConstant* new_limit = new(pre_header->graph()->zone()) HConstant(
266 upper_constant_limit, length->representation());
267 new_limit->InsertBefore(pre_header->end());
268 limit = new_limit;
269 }
270
271 // If necessary, redefine the limit in the preheader.
272 if (limit->IsInteger32Constant() &&
273 limit->block() != pre_header &&
274 !limit->block()->Dominates(pre_header)) {
275 HConstant* new_limit = new(pre_header->graph()->zone()) HConstant(
276 limit->GetInteger32Constant(), length->representation());
277 new_limit->InsertBefore(pre_header->end());
278 limit = new_limit;
279 }
280
281 // Do the hoisting.
282 HBoundsCheck* hoisted_check = new(pre_header->zone()) HBoundsCheck(
283 limit, check->check()->length());
284 hoisted_check->InsertBefore(pre_header->end());
285 hoisted_check->set_allow_equality(true);
286 }
287
288 void CollectInductionVariableData(HBasicBlock* bb) {
289 bool additional_limit = false;
290
291 for (int i = 0; i < bb->phis()->length(); i++) {
292 HPhi* phi = bb->phis()->at(i);
293 phi->DetectInductionVariable();
294 }
295
296 additional_limit = InductionVariableData::ComputeInductionVariableLimit(
297 bb, at(bb)->additional_limit());
298
299 if (additional_limit) {
300 at(bb)->additional_limit()->updated_variable->
301 UpdateAdditionalLimit(at(bb)->additional_limit());
302 }
303
304 for (HInstruction* i = bb->first(); i != NULL; i = i->next()) {
305 if (!i->IsBoundsCheck()) continue;
306 HBoundsCheck* check = HBoundsCheck::cast(i);
307 InductionVariableData::BitwiseDecompositionResult decomposition;
308 InductionVariableData::DecomposeBitwise(check->index(), &decomposition);
309 if (!decomposition.base->IsPhi()) continue;
310 HPhi* phi = HPhi::cast(decomposition.base);
311
312 if (!phi->IsInductionVariable()) continue;
313 InductionVariableData* data = phi->induction_variable_data();
314
315 // For now ignore loops decrementing the index.
316 if (data->increment() <= 0) continue;
titzer 2013/07/22 13:03:10 Haven't we already done a whole lot of work to dea
Massi 2013/07/23 07:57:50 It depends what we call "a whole lot of work". Wha
317 if (!data->LowerLimitIsNonNegativeConstant()) continue;
318
319 // TODO(mmassi): skip OSR values for check->length().
320 if (check->length() == data->limit() ||
321 check->length() == data->additional_upper_limit()) {
322 check->set_skip_check();
323 continue;
324 }
325
326 if (!phi->IsLimitedInductionVariable()) continue;
327
328 int32_t limit = data->ComputeUpperLimit(decomposition.and_mask,
329 decomposition.or_mask);
330 phi->induction_variable_data()->AddCheck(check, limit);
331 }
332
333 for (int i = 0; i < bb->dominated_blocks()->length(); i++) {
334 CollectInductionVariableData(bb->dominated_blocks()->at(i));
335 }
336
337 if (additional_limit) {
338 at(bb->block_id())->additional_limit()->updated_variable->
339 UpdateAdditionalLimit(at(bb->block_id())->additional_limit());
340 }
341 }
342
343 void EliminateRedundantBoundsChecks(HBasicBlock* bb) {
344 for (int i = 0; i < bb->phis()->length(); i++) {
345 HPhi* phi = bb->phis()->at(i);
346 if (!phi->IsLimitedInductionVariable()) continue;
347
348 InductionVariableData* induction_data = phi->induction_variable_data();
349 InductionVariableData::ChecksRelatedToLength* current_length_group =
350 induction_data->checks();
351 while (current_length_group != NULL) {
352 current_length_group->CloseCurrentBlock();
353 InductionVariableData::InductionVariableCheck* current_base_check =
354 current_length_group->checks();
355 InitializeLoop(induction_data);
356
357 while (current_base_check != NULL) {
358 ProcessRelatedChecks(current_base_check, induction_data);
359 while (current_base_check != NULL &&
360 current_base_check->processed()) {
361 current_base_check = current_base_check->next();
362 }
363 }
364
365 current_length_group = current_length_group->next();
366 }
367 }
368 }
369
370 private:
371 PathCheckResult CheckLoopPaths(HBasicBlock* block) {
titzer 2013/07/22 13:03:10 Much improved.
372 ASSERT(at(block)->is_in_loop());
373
374 // We found a check so this block is safe and we can backtrack.
375 if (at(block)->has_check()) {
376 return OK;
377 }
378
379 bool unsafe = false;
380 for (int i = 0; i < block->end()->SuccessorCount(); i ++) {
381 Element* successor = at(block->end()->SuccessorAt(i));
382
383 if (!successor->is_in_loop()) {
384 if (!successor->is_proper_exit()) {
385 // We found a path that exits the loop early, and is not the exit
386 // related to the induction limit, therefore hoisting checks is
387 // an optimistic assumption.
388 unsafe = true;
389 }
390 }
391
392 if (successor->is_start()) {
393 // We found a path that does one loop iteration without meeting any
394 // check, therefore hoisting checks would be likely to cause
395 // unnecessary deopts.
396 return FAILURE;
397 }
398 }
399
400 return unsafe ? UNSAFE : OK;
401 }
402
403 HGraph* graph_;
404 HBasicBlock* loop_header_;
405 ZoneList<Element> elements_;
406 };
407
408
409 void HGraph::EliminateRedundantBoundsChecksUsingInductionVariables() {
410 InductionVariableBlocksTable table(this);
411 table.CollectInductionVariableData(entry_block());
412 for (int i = 0; i < blocks()->length(); i++) {
413 table.EliminateRedundantBoundsChecks(blocks()->at(i));
414 }
415 }
416
417 } } // namespace v8::internal
418
OLDNEW
« no previous file with comments | « src/hydrogen.cc ('k') | src/hydrogen-instructions.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698