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

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: Addressed review comments (3). 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 has_check() { return has_check_; }
64 void set_has_check() { has_check_ = true; }
65 InductionVariableLimitUpdate* additional_limit() {
66 return &additional_limit_;
67 }
68
69 void InitializeLoop(InductionVariableData* data) {
70 ASSERT(data->limit() != NULL);
71 HLoopInformation* loop = data->phi()->block()->current_loop();
72 current_successor_ = kNoBlock;
73 backtrack_to_ = kNoBlock;
74 is_start_ = (block() == loop->loop_header());
75 is_proper_exit_ = (block() == data->induction_exit_target());
76 is_in_loop_ = loop->IsNestedInThisLoop(block()->current_loop());
77 has_check_ = false;
78 }
79
80 void ClearIterationData() {
81 current_successor_ = kNoBlock;
82 backtrack_to_ = kNoBlock;
83 }
84
85 int CurrentSuccessorBlock() {
86 if (current_successor_ < block()->end()->SuccessorCount()) {
87 return block()->end()->SuccessorAt(current_successor_)->block_id();
88 } else {
89 return kNoBlock;
90 }
91 }
92
93 /*
94 * Perform one step of the loop graph traversal.
95 * from_block: The index of the block we are coming from.
96 * failure: Set this if we found a path that comes back to the loop header
97 * without passing through a check first.
98 * unsafe: Set this if we found an exit from the loop without passing
99 * through a check first.
100 * returns: The index of the next block to be processed.
101 */
titzer 2013/07/10 16:34:18 It is still not clear to me what order the code vi
Massi 2013/07/11 11:15:22 Done.
102 int PerformStep(int from_block, bool* failure, bool* unsafe) {
103 if (!is_in_loop_) {
104 if (!is_proper_exit_) {
105 *unsafe = true;
106 }
107 return from_block;
108 }
109
110 if (is_start_ &&
111 from_block != kNoBlock &&
112 from_block != CurrentSuccessorBlock()) {
113 *failure = true;
114 return from_block;
115 }
116
117 if (has_check()) {
118 return from_block;
119 }
120
121 if (current_successor_ == kNoBlock) {
122 backtrack_to_ = from_block;
123 }
124 current_successor_++;
125
126 if (CurrentSuccessorBlock() != kNoBlock) {
127 return CurrentSuccessorBlock();
128 } else {
129 return backtrack_to_;
130 }
131 }
132
133 Element()
134 : block_(NULL), current_successor_(kNoBlock), backtrack_to_(kNoBlock),
135 is_start_(false), is_proper_exit_(false), has_check_(false),
136 additional_limit_() {}
137
138 private:
139 HBasicBlock* block_;
140 int current_successor_;
141 int backtrack_to_;
142 bool is_start_;
143 bool is_proper_exit_;
144 bool is_in_loop_;
145 bool has_check_;
146 InductionVariableLimitUpdate additional_limit_;
147 };
148
149 HGraph* graph() { return graph_; }
150 HBasicBlock* loop_header() { return loop_header_; }
151 Element* at(int index) { return &(elements_.at(index)); }
152 Element* at(HBasicBlock* block) { return at(block->block_id()); }
153
154 void AddCheckAt(HBasicBlock* block) {
155 at(block->block_id())->set_has_check();
156 }
157
158 void InitializeLoop(InductionVariableData* data) {
159 for (int i = 0; i < graph()->blocks()->length(); i++) {
160 at(i)->InitializeLoop(data);
161 }
162 loop_header_ = data->phi()->block()->current_loop()->loop_header();
163 }
164
165 void ClearIterationData() {
166 ASSERT(loop_header() != NULL);
167 HLoopInformation* loop = loop_header()->loop_information();
168 for (int i = 0; i < loop->blocks()->length(); i++) {
169 at(loop->blocks()->at(i)->block_id())->ClearIterationData();
170 }
171 }
172
173 bool LoopPathsAreChecked(bool* unsafe) {
174 bool failure = false;
175 *unsafe = false;
176 int previous_block = Element::kNoBlock;
177 int current_block = loop_header()->block_id();
178 while (current_block != Element::kNoBlock) {
179 int next_block = at(current_block)->PerformStep(previous_block,
180 &failure, unsafe);
titzer 2013/07/10 16:34:18 It might actually be clearer to inline this Perfor
Massi 2013/07/11 11:15:22 Done.
181 previous_block = current_block;
182 current_block = next_block;
183 if (failure) return false;
184 }
185 return true;
186 }
187
188 explicit InductionVariableBlocksTable(HGraph* graph)
189 : graph_(graph), loop_header_(NULL),
190 elements_(graph->blocks()->length(), graph->zone()) {
191 for (int i = 0; i < graph->blocks()->length(); i++) {
192 Element element;
193 element.set_block(graph->blocks()->at(i));
194 elements_.Add(element, graph->zone());
195 ASSERT(at(i)->block()->block_id() == i);
196 }
197 }
198
199 // Tries to hoist a check out of its induction loop.
200 void ProcessRelatedChecks(
201 InductionVariableData::InductionVariableCheck* check,
202 InductionVariableData* data) {
203 HValue* length = check->check()->length();
204 ClearIterationData();
205 check->set_processed();
206 HBasicBlock* header =
207 data->phi()->block()->current_loop()->loop_header();
208 HBasicBlock* pre_header = header->predecessors()->at(0);
209 // Check that the limit is defined in the loop preheader.
210 if (!data->limit()->IsInteger32Constant()) {
211 HBasicBlock* limit_block = data->limit()->block();
212 if (limit_block != pre_header &&
213 !limit_block->Dominates(pre_header)) {
214 return;
215 }
216 }
217 // Check that the length and limit have compatible representations.
218 if (!(data->limit()->representation().Equals(
219 length->representation()) ||
220 data->limit()->IsInteger32Constant())) {
221 return;
222 }
223 // Check that the length is defined in the loop preheader.
224 if (check->check()->length()->block() != pre_header &&
225 !check->check()->length()->block()->Dominates(pre_header)) {
226 return;
227 }
228
229 // Add checks to the table.
230 for (InductionVariableData::InductionVariableCheck* current_check = check;
231 current_check != NULL;
232 current_check = current_check->next()) {
233 if (current_check->check()->length() != length) continue;
234
235 AddCheckAt(current_check->check()->block());
236 current_check->set_processed();
237 }
238
239 // Check that we will not cause unwanted deoptimizations.
240 bool unsafe;
241 bool failure = !LoopPathsAreChecked(&unsafe);
242 if (failure || (unsafe && !graph()->use_optimistic_licm())) {
243 return;
244 }
245
246 // We will do the hoisting, but we must see if the limit is "limit" or if
247 // all checks are done on constants: if all check are done against the same
248 // constant limit we will use that instead of the induction limit.
249 bool has_upper_constant_limit = true;
250 InductionVariableData::InductionVariableCheck* current_check = check;
251 int32_t upper_constant_limit =
252 current_check != NULL && current_check->HasUpperLimit() ?
253 current_check->upper_limit() : 0;
254 while (current_check != NULL) {
255 if (check->HasUpperLimit()) {
256 if (check->upper_limit() != upper_constant_limit) {
257 has_upper_constant_limit = false;
258 }
259 } else {
260 has_upper_constant_limit = false;
261 }
262
263 current_check->check()->set_skip_check();
264 current_check = current_check->next();
265 }
266
267 // Choose the appropriate limit.
268 HValue* limit = data->limit();
269 if (has_upper_constant_limit) {
270 HConstant* new_limit = new(pre_header->graph()->zone()) HConstant(
271 upper_constant_limit, length->representation());
272 new_limit->InsertBefore(pre_header->end());
273 limit = new_limit;
274 }
275
276 // If necessary, redefine the limit in the preheader.
277 if (limit->IsInteger32Constant() &&
278 limit->block() != pre_header &&
279 !limit->block()->Dominates(pre_header)) {
280 HConstant* new_limit = new(pre_header->graph()->zone()) HConstant(
281 limit->GetInteger32Constant(), length->representation());
282 new_limit->InsertBefore(pre_header->end());
283 limit = new_limit;
284 }
285
286 // Do the hoisting.
287 HBoundsCheck* hoisted_check = new(pre_header->zone()) HBoundsCheck(
288 limit, check->check()->length());
289 hoisted_check->InsertBefore(pre_header->end());
290 hoisted_check->set_allow_equality(true);
291 }
292
293 void CollectInductionVariableData(HBasicBlock* bb);
titzer 2013/07/10 16:34:18 It's OK IMO to go ahead and pull up these other me
Massi 2013/07/11 11:15:22 Done.
294 void EliminateRedundantBoundsChecks(HBasicBlock* bb);
295
296 private:
297 HGraph* graph_;
298 HBasicBlock* loop_header_;
299 ZoneList<Element> elements_;
300 };
301
302
303 void InductionVariableBlocksTable::CollectInductionVariableData(
304 HBasicBlock* bb) {
305 bool additional_limit = false;
306
307 for (int i = 0; i < bb->phis()->length(); i++) {
308 HPhi* phi = bb->phis()->at(i);
309 phi->DetectInductionVariable();
310 }
311
312 additional_limit = InductionVariableData::ComputeInductionVariableLimit(
313 bb, at(bb)->additional_limit());
314
315 if (additional_limit) {
316 at(bb)->additional_limit()->updated_variable->
317 UpdateAdditionalLimit(at(bb)->additional_limit());
318 }
319
320 for (HInstruction* i = bb->first(); i != NULL; i = i->next()) {
321 if (!i->IsBoundsCheck()) continue;
322 HBoundsCheck* check = HBoundsCheck::cast(i);
323 InductionVariableData::BitwiseDecompositionResult decomposition;
324 InductionVariableData::DecomposeBitwise(check->index(), &decomposition);
325 if (!decomposition.base->IsPhi()) continue;
326 HPhi* phi = HPhi::cast(decomposition.base);
327
328 if (!phi->IsInductionVariable()) continue;
329 InductionVariableData* data = phi->induction_variable_data();
330
331 // For now ignore loops decrementing the index.
332 if (data->increment() <= 0) continue;
333 if (!data->lower_limit_is_non_negative_constant()) continue;
334
335 // TODO(mmassi): skip OSR values for check->length().
336 if (check->length() == data->limit() ||
337 check->length() == data->additional_upper_limit()) {
338 check->set_skip_check();
339 continue;
340 }
341
342 if (!phi->IsLimitedInductionVariable()) continue;
343
344 int32_t limit = data->ComputeUpperLimit(decomposition.and_mask,
345 decomposition.or_mask);
346 phi->induction_variable_data()->AddCheck(check, limit);
347 }
348
349 for (int i = 0; i < bb->dominated_blocks()->length(); i++) {
350 CollectInductionVariableData(bb->dominated_blocks()->at(i));
351 }
352
353 if (additional_limit) {
354 at(bb->block_id())->additional_limit()->updated_variable->
355 UpdateAdditionalLimit(at(bb->block_id())->additional_limit());
356 }
357 }
358
359
360 void InductionVariableBlocksTable::EliminateRedundantBoundsChecks(
361 HBasicBlock* bb) {
362 for (int i = 0; i < bb->phis()->length(); i++) {
363 HPhi* phi = bb->phis()->at(i);
364 if (!phi->IsLimitedInductionVariable()) continue;
365
366 InductionVariableData* induction_data = phi->induction_variable_data();
367 InductionVariableData::ChecksRelatedToLength* current_length_group =
368 induction_data->checks();
369 while (current_length_group != NULL) {
370 current_length_group->CloseCurrentBlock();
371 InductionVariableData::InductionVariableCheck* current_base_check =
372 current_length_group->checks();
373 InitializeLoop(induction_data);
374
375 while (current_base_check != NULL) {
376 ProcessRelatedChecks(current_base_check, induction_data);
377 while (current_base_check != NULL && current_base_check->processed()) {
378 current_base_check = current_base_check->next();
379 }
380 }
381
382 current_length_group = current_length_group->next();
383 }
384 }
385 }
386
387
388 void HGraph::EliminateRedundantBoundsChecksUsingInductionVariables() {
389 InductionVariableBlocksTable table(this);
390 table.CollectInductionVariableData(entry_block());
391 for (int i = 0; i < blocks()->length(); i++) {
392 table.EliminateRedundantBoundsChecks(blocks()->at(i));
393 }
394 }
395
396 } } // namespace v8::internal
397
OLDNEW
« no previous file with comments | « src/hydrogen.cc ('k') | src/hydrogen-instructions.h » ('j') | src/hydrogen-instructions.cc » ('J')

Powered by Google App Engine
This is Rietveld 408576698