v8  3.25.30(node0.11.13)
V8 is Google's open source JavaScript engine
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Pages
hydrogen-bch.cc
Go to the documentation of this file.
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; }
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 
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() const { return graph_; }
106  Counters* counters() const { return graph()->isolate()->counters(); }
107  HBasicBlock* loop_header() const { return loop_header_; }
108  Element* at(int index) const { return &(elements_.at(index)); }
109  Element* at(HBasicBlock* block) const { return at(block->block_id()); }
110 
111  void AddCheckAt(HBasicBlock* block) {
112  at(block->block_id())->set_has_check();
113  }
114 
115  /*
116  * Initializes the table for a given loop (identified by its induction
117  * variable).
118  */
119  void InitializeLoop(InductionVariableData* data) {
120  for (int i = 0; i < graph()->blocks()->length(); i++) {
121  at(i)->InitializeLoop(data);
122  }
123  loop_header_ = data->phi()->block()->current_loop()->loop_header();
124  }
125 
126 
130  NOT_HOISTABLE
131  };
132 
133  /*
134  * This method checks if it is appropriate to hoist the bounds checks on an
135  * induction variable out of the loop.
136  * The problem is that in the loop code graph there could be execution paths
137  * where the check is not performed, but hoisting the check has the same
138  * semantics as performing it at every loop iteration, which could cause
139  * unnecessary check failures (which would mean unnecessary deoptimizations).
140  * The method returns OK if there are no paths that perform an iteration
141  * (loop back to the header) without meeting a check, or UNSAFE is set if
142  * early exit paths are found.
143  */
145  for (int i = 0; i < elements_.length(); i++) {
146  at(i)->ResetCurrentDominatedBlock();
147  }
148  bool unsafe = false;
149 
150  HBasicBlock* current = loop_header();
151  while (current != NULL) {
152  HBasicBlock* next;
153 
154  if (at(current)->has_check() || !at(current)->is_in_loop()) {
155  // We found a check or we reached a dominated block out of the loop,
156  // therefore this block is safe and we can backtrack.
157  next = NULL;
158  } else {
159  for (int i = 0; i < current->end()->SuccessorCount(); i ++) {
160  Element* successor = at(current->end()->SuccessorAt(i));
161 
162  if (!successor->is_in_loop()) {
163  if (!successor->is_proper_exit()) {
164  // We found a path that exits the loop early, and is not the exit
165  // related to the induction limit, therefore hoisting checks is
166  // an optimistic assumption.
167  unsafe = true;
168  }
169  }
170 
171  if (successor->is_start()) {
172  // We found a path that does one loop iteration without meeting any
173  // check, therefore hoisting checks would be likely to cause
174  // unnecessary deopts.
175  return NOT_HOISTABLE;
176  }
177  }
178 
179  next = at(current)->NextDominatedBlock();
180  }
181 
182  // If we have no next block we need to backtrack the tree traversal.
183  while (next == NULL) {
184  current = current->dominator();
185  if (current != NULL) {
186  next = at(current)->NextDominatedBlock();
187  } else {
188  // We reached the root: next stays NULL.
189  next = NULL;
190  break;
191  }
192  }
193 
194  current = next;
195  }
196 
197  return unsafe ? OPTIMISTICALLY_HOISTABLE : HOISTABLE;
198  }
199 
200  explicit InductionVariableBlocksTable(HGraph* graph)
201  : graph_(graph), loop_header_(NULL),
202  elements_(graph->blocks()->length(), graph->zone()) {
203  for (int i = 0; i < graph->blocks()->length(); i++) {
204  Element element;
205  element.set_block(graph->blocks()->at(i));
206  elements_.Add(element, graph->zone());
207  ASSERT(at(i)->block()->block_id() == i);
208  }
209  }
210 
211  // Tries to hoist a check out of its induction loop.
213  InductionVariableData::InductionVariableCheck* check,
214  InductionVariableData* data) {
215  HValue* length = check->check()->length();
216  check->set_processed();
217  HBasicBlock* header =
218  data->phi()->block()->current_loop()->loop_header();
219  HBasicBlock* pre_header = header->predecessors()->at(0);
220  // Check that the limit is defined in the loop preheader.
221  if (!data->limit()->IsInteger32Constant()) {
222  HBasicBlock* limit_block = data->limit()->block();
223  if (limit_block != pre_header &&
224  !limit_block->Dominates(pre_header)) {
225  return;
226  }
227  }
228  // Check that the length and limit have compatible representations.
229  if (!(data->limit()->representation().Equals(
230  length->representation()) ||
231  data->limit()->IsInteger32Constant())) {
232  return;
233  }
234  // Check that the length is defined in the loop preheader.
235  if (check->check()->length()->block() != pre_header &&
236  !check->check()->length()->block()->Dominates(pre_header)) {
237  return;
238  }
239 
240  // Add checks to the table.
241  for (InductionVariableData::InductionVariableCheck* current_check = check;
242  current_check != NULL;
243  current_check = current_check->next()) {
244  if (current_check->check()->length() != length) continue;
245 
246  AddCheckAt(current_check->check()->block());
247  current_check->set_processed();
248  }
249 
250  // Check that we will not cause unwanted deoptimizations.
251  Hoistability hoistability = CheckHoistability();
252  if (hoistability == NOT_HOISTABLE ||
253  (hoistability == OPTIMISTICALLY_HOISTABLE &&
254  !graph()->use_optimistic_licm())) {
255  return;
256  }
257 
258  // We will do the hoisting, but we must see if the limit is "limit" or if
259  // all checks are done on constants: if all check are done against the same
260  // constant limit we will use that instead of the induction limit.
261  bool has_upper_constant_limit = true;
262  int32_t upper_constant_limit =
263  check != NULL && check->HasUpperLimit() ? check->upper_limit() : 0;
264  for (InductionVariableData::InductionVariableCheck* current_check = check;
265  current_check != NULL;
266  current_check = current_check->next()) {
267  has_upper_constant_limit =
268  has_upper_constant_limit &&
269  check->HasUpperLimit() &&
270  check->upper_limit() == upper_constant_limit;
271  counters()->bounds_checks_eliminated()->Increment();
272  current_check->check()->set_skip_check();
273  }
274 
275  // Choose the appropriate limit.
276  Zone* zone = graph()->zone();
277  HValue* context = graph()->GetInvalidContext();
278  HValue* limit = data->limit();
279  if (has_upper_constant_limit) {
280  HConstant* new_limit = HConstant::New(zone, context,
281  upper_constant_limit);
282  new_limit->InsertBefore(pre_header->end());
283  limit = new_limit;
284  }
285 
286  // If necessary, redefine the limit in the preheader.
287  if (limit->IsInteger32Constant() &&
288  limit->block() != pre_header &&
289  !limit->block()->Dominates(pre_header)) {
290  HConstant* new_limit = HConstant::New(zone, context,
291  limit->GetInteger32Constant());
292  new_limit->InsertBefore(pre_header->end());
293  limit = new_limit;
294  }
295 
296  // Do the hoisting.
297  HBoundsCheck* hoisted_check = HBoundsCheck::New(
298  zone, context, limit, check->check()->length());
299  hoisted_check->InsertBefore(pre_header->end());
300  hoisted_check->set_allow_equality(true);
301  counters()->bounds_checks_hoisted()->Increment();
302  }
303 
304  void CollectInductionVariableData(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->LowerLimitIsNonNegativeConstant()) 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  counters()->bounds_checks_eliminated()->Increment();
339  check->set_skip_check();
340  continue;
341  }
342 
343  if (!phi->IsLimitedInductionVariable()) continue;
344 
345  int32_t limit = data->ComputeUpperLimit(decomposition.and_mask,
346  decomposition.or_mask);
347  phi->induction_variable_data()->AddCheck(check, limit);
348  }
349 
350  for (int i = 0; i < bb->dominated_blocks()->length(); i++) {
351  CollectInductionVariableData(bb->dominated_blocks()->at(i));
352  }
353 
354  if (additional_limit) {
355  at(bb->block_id())->additional_limit()->updated_variable->
356  UpdateAdditionalLimit(at(bb->block_id())->additional_limit());
357  }
358  }
359 
360  void EliminateRedundantBoundsChecks(HBasicBlock* bb) {
361  for (int i = 0; i < bb->phis()->length(); i++) {
362  HPhi* phi = bb->phis()->at(i);
363  if (!phi->IsLimitedInductionVariable()) continue;
364 
365  InductionVariableData* induction_data = phi->induction_variable_data();
366  InductionVariableData::ChecksRelatedToLength* current_length_group =
367  induction_data->checks();
368  while (current_length_group != NULL) {
369  current_length_group->CloseCurrentBlock();
370  InductionVariableData::InductionVariableCheck* current_base_check =
371  current_length_group->checks();
372  InitializeLoop(induction_data);
373 
374  while (current_base_check != NULL) {
375  ProcessRelatedChecks(current_base_check, induction_data);
376  while (current_base_check != NULL &&
377  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  private:
388  HGraph* graph_;
389  HBasicBlock* loop_header_;
390  ZoneList<Element> elements_;
391 };
392 
393 
394 void HBoundsCheckHoistingPhase::HoistRedundantBoundsChecks() {
395  InductionVariableBlocksTable table(graph());
396  table.CollectInductionVariableData(graph()->entry_block());
397  for (int i = 0; i < graph()->blocks()->length(); i++) {
398  table.EliminateRedundantBoundsChecks(graph()->blocks()->at(i));
399  }
400 }
401 
402 } } // namespace v8::internal
enable upcoming ES6 features enable harmony block scoping enable harmony enable harmony proxies enable harmony generators enable harmony numeric enable harmony string enable harmony math functions harmony_scoping harmony_symbols harmony_collections harmony_iteration harmony_strings harmony_scoping harmony_maths tracks arrays with only smi values Optimize object Array DOM strings and string pretenure call new trace pretenuring decisions of HAllocate instructions track fields with only smi values track fields with heap values track_fields track_fields Enables optimizations which favor memory size over execution speed use string slices optimization filter maximum number of GVN fix point iterations use function inlining use allocation folding eliminate write barriers targeting allocations in optimized code maximum source size in bytes considered for a single inlining maximum cumulative number of AST nodes considered for inlining crankshaft harvests type feedback from stub cache trace check elimination phase hydrogen tracing filter NULL
Definition: flags.cc:269
void set_block(HBasicBlock *block)
Definition: hydrogen-bch.cc:55
Element * at(HBasicBlock *block) const
HBasicBlock * block() const
void AddCheckAt(HBasicBlock *block)
Hoistability CheckHoistability()
InductionVariableBlocksTable(HGraph *graph)
int int32_t
Definition: unicode.cc:47
#define ASSERT(condition)
Definition: checks.h:329
HGraph * graph() const
Definition: hydrogen.h:2695
Counters * counters() const
Representation representation() const
void check(i::Vector< const uint8_t > string)
void CollectInductionVariableData(HBasicBlock *bb)
void InitializeLoop(InductionVariableData *data)
Definition: hydrogen-bch.cc:69
#define BASE_EMBEDDED
Definition: allocation.h:68
Element * at(int index) const
void EliminateRedundantBoundsChecks(HBasicBlock *bb)
InductionVariableLimitUpdate * additional_limit()
Definition: hydrogen-bch.cc:61
HBasicBlock * loop_header() const
void InitializeLoop(InductionVariableData *data)
void ProcessRelatedChecks(InductionVariableData::InductionVariableCheck *check, InductionVariableData *data)