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
30namespace v8 {
31namespace 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 */
48class 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    Zone* zone = graph()->zone();
282    HValue* context = graph()->GetInvalidContext();
283    HValue* limit = data->limit();
284    if (has_upper_constant_limit) {
285      HConstant* new_limit = HConstant::New(zone, context,
286                                            upper_constant_limit);
287      new_limit->InsertBefore(pre_header->end());
288      limit = new_limit;
289    }
290
291    // If necessary, redefine the limit in the preheader.
292    if (limit->IsInteger32Constant() &&
293        limit->block() != pre_header &&
294        !limit->block()->Dominates(pre_header)) {
295      HConstant* new_limit = HConstant::New(zone, context,
296                                            limit->GetInteger32Constant());
297      new_limit->InsertBefore(pre_header->end());
298      limit = new_limit;
299    }
300
301    // Do the hoisting.
302    HBoundsCheck* hoisted_check = HBoundsCheck::New(
303        zone, context, limit, check->check()->length());
304    hoisted_check->InsertBefore(pre_header->end());
305    hoisted_check->set_allow_equality(true);
306    hoisted_check->block()->graph()->isolate()->counters()->
307        bounds_checks_hoisted()->Increment();
308  }
309
310  void CollectInductionVariableData(HBasicBlock* bb) {
311    bool additional_limit = false;
312
313    for (int i = 0; i < bb->phis()->length(); i++) {
314      HPhi* phi = bb->phis()->at(i);
315      phi->DetectInductionVariable();
316    }
317
318    additional_limit = InductionVariableData::ComputeInductionVariableLimit(
319        bb, at(bb)->additional_limit());
320
321    if (additional_limit) {
322      at(bb)->additional_limit()->updated_variable->
323          UpdateAdditionalLimit(at(bb)->additional_limit());
324    }
325
326    for (HInstruction* i = bb->first(); i != NULL; i = i->next()) {
327      if (!i->IsBoundsCheck()) continue;
328      HBoundsCheck* check = HBoundsCheck::cast(i);
329      InductionVariableData::BitwiseDecompositionResult decomposition;
330      InductionVariableData::DecomposeBitwise(check->index(), &decomposition);
331      if (!decomposition.base->IsPhi()) continue;
332      HPhi* phi = HPhi::cast(decomposition.base);
333
334      if (!phi->IsInductionVariable()) continue;
335      InductionVariableData* data = phi->induction_variable_data();
336
337      // For now ignore loops decrementing the index.
338      if (data->increment() <= 0) continue;
339      if (!data->LowerLimitIsNonNegativeConstant()) continue;
340
341      // TODO(mmassi): skip OSR values for check->length().
342      if (check->length() == data->limit() ||
343          check->length() == data->additional_upper_limit()) {
344        check->block()->graph()->isolate()->counters()->
345            bounds_checks_eliminated()->Increment();
346        check->set_skip_check();
347        continue;
348      }
349
350      if (!phi->IsLimitedInductionVariable()) continue;
351
352      int32_t limit = data->ComputeUpperLimit(decomposition.and_mask,
353                                              decomposition.or_mask);
354      phi->induction_variable_data()->AddCheck(check, limit);
355    }
356
357    for (int i = 0; i < bb->dominated_blocks()->length(); i++) {
358      CollectInductionVariableData(bb->dominated_blocks()->at(i));
359    }
360
361    if (additional_limit) {
362      at(bb->block_id())->additional_limit()->updated_variable->
363          UpdateAdditionalLimit(at(bb->block_id())->additional_limit());
364    }
365  }
366
367  void EliminateRedundantBoundsChecks(HBasicBlock* bb) {
368    for (int i = 0; i < bb->phis()->length(); i++) {
369      HPhi* phi = bb->phis()->at(i);
370      if (!phi->IsLimitedInductionVariable()) continue;
371
372      InductionVariableData* induction_data = phi->induction_variable_data();
373      InductionVariableData::ChecksRelatedToLength* current_length_group =
374          induction_data->checks();
375      while (current_length_group != NULL) {
376        current_length_group->CloseCurrentBlock();
377        InductionVariableData::InductionVariableCheck* current_base_check =
378            current_length_group->checks();
379        InitializeLoop(induction_data);
380
381        while (current_base_check != NULL) {
382          ProcessRelatedChecks(current_base_check, induction_data);
383          while (current_base_check != NULL &&
384                 current_base_check->processed()) {
385            current_base_check = current_base_check->next();
386          }
387        }
388
389        current_length_group = current_length_group->next();
390      }
391    }
392  }
393
394 private:
395  HGraph* graph_;
396  HBasicBlock* loop_header_;
397  ZoneList<Element> elements_;
398};
399
400
401void HBoundsCheckHoistingPhase::HoistRedundantBoundsChecks() {
402  InductionVariableBlocksTable table(graph());
403  table.CollectInductionVariableData(graph()->entry_block());
404  for (int i = 0; i < graph()->blocks()->length(); i++) {
405    table.EliminateRedundantBoundsChecks(graph()->blocks()->at(i));
406  }
407}
408
409} }  // namespace v8::internal
410
411