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

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

Powered by Google App Engine
This is Rietveld 408576698