LCOV - code coverage report
Current view: top level - src/compiler - value-numbering-reducer.cc (source / functions) Hit Total Coverage
Test: app.info Lines: 75 75 100.0 %
Date: 2017-04-26 Functions: 7 8 87.5 %

          Line data    Source code
       1             : // Copyright 2014 the V8 project authors. All rights reserved.
       2             : // Use of this source code is governed by a BSD-style license that can be
       3             : // found in the LICENSE file.
       4             : 
       5             : #include "src/compiler/value-numbering-reducer.h"
       6             : 
       7             : #include <cstring>
       8             : 
       9             : #include "src/base/functional.h"
      10             : #include "src/compiler/node-properties.h"
      11             : #include "src/compiler/node.h"
      12             : 
      13             : namespace v8 {
      14             : namespace internal {
      15             : namespace compiler {
      16             : 
      17             : namespace {
      18             : 
      19   175522360 : size_t HashCode(Node* node) {
      20    87761180 :   size_t h = base::hash_combine(node->op()->HashCode(), node->InputCount());
      21   446409840 :   for (Node* input : node->inputs()) {
      22             :     h = base::hash_combine(h, input->id());
      23             :   }
      24    87753418 :   return h;
      25             : }
      26             : 
      27             : 
      28   145367070 : bool Equals(Node* a, Node* b) {
      29             :   DCHECK_NOT_NULL(a);
      30             :   DCHECK_NOT_NULL(b);
      31             :   DCHECK_NOT_NULL(a->op());
      32             :   DCHECK_NOT_NULL(b->op());
      33   145367070 :   if (!a->op()->Equals(b->op())) return false;
      34     3663008 :   if (a->InputCount() != b->InputCount()) return false;
      35             :   Node::Inputs aInputs = a->inputs();
      36             :   Node::Inputs bInputs = b->inputs();
      37             : 
      38             :   auto aIt = aInputs.begin();
      39             :   auto bIt = bInputs.begin();
      40             :   auto aEnd = aInputs.end();
      41             : 
      42     7414714 :   for (; aIt != aEnd; ++aIt, ++bIt) {
      43             :     DCHECK_NOT_NULL(*aIt);
      44             :     DCHECK_NOT_NULL(*bIt);
      45    17444010 :     if ((*aIt)->id() != (*bIt)->id()) return false;
      46             :   }
      47             :   return true;
      48             : }
      49             : 
      50             : }  // namespace
      51             : 
      52     1254335 : ValueNumberingReducer::ValueNumberingReducer(Zone* temp_zone, Zone* graph_zone)
      53             :     : entries_(nullptr),
      54             :       capacity_(0),
      55             :       size_(0),
      56             :       temp_zone_(temp_zone),
      57     1254335 :       graph_zone_(graph_zone) {}
      58             : 
      59     2508492 : ValueNumberingReducer::~ValueNumberingReducer() {}
      60             : 
      61             : 
      62   124223710 : Reduction ValueNumberingReducer::Reduce(Node* node) {
      63   122969464 :   if (!node->op()->HasProperty(Operator::kIdempotent)) return NoChange();
      64             : 
      65    65442175 :   const size_t hash = HashCode(node);
      66    65438932 :   if (!entries_) {
      67             :     DCHECK(size_ == 0);
      68             :     DCHECK(capacity_ == 0);
      69             :     // Allocate the initial entries and insert the first entry.
      70     1254246 :     capacity_ = kInitialCapacity;
      71     1254293 :     entries_ = temp_zone()->NewArray<Node*>(kInitialCapacity);
      72             :     memset(entries_, 0, sizeof(*entries_) * kInitialCapacity);
      73     1254293 :     entries_[hash & (kInitialCapacity - 1)] = node;
      74     1254293 :     size_ = 1;
      75             :     return NoChange();
      76             :   }
      77             : 
      78             :   DCHECK(size_ < capacity_);
      79             :   DCHECK(size_ + size_ / 4 < capacity_);
      80             : 
      81    64184686 :   const size_t mask = capacity_ - 1;
      82             :   size_t dead = capacity_;
      83             : 
      84   126993210 :   for (size_t i = hash & mask;; i = (i + 1) & mask) {
      85   126993210 :     Node* entry = entries_[i];
      86   126993210 :     if (!entry) {
      87    59854825 :       if (dead != capacity_) {
      88             :         // Reuse dead entry that we discovered on the way.
      89       20093 :         entries_[dead] = node;
      90             :       } else {
      91             :         // Have to insert a new entry.
      92    59834732 :         entries_[i] = node;
      93    59834732 :         size_++;
      94             : 
      95             :         // Resize to keep load factor below 80%
      96    59834732 :         if (size_ + size_ / 4 >= capacity_) Grow();
      97             :       }
      98             :       DCHECK(size_ + size_ / 4 < capacity_);
      99             :       return NoChange();
     100             :     }
     101             : 
     102    67138385 :     if (entry == node) {
     103             :       // We need to check for a certain class of collisions here. Imagine the
     104             :       // following scenario:
     105             :       //
     106             :       //  1. We insert node1 with op1 and certain inputs at index i.
     107             :       //  2. We insert node2 with op2 and certain inputs at index i+1.
     108             :       //  3. Some other reducer changes node1 to op2 and the inputs from node2.
     109             :       //
     110             :       // Now we are called again to reduce node1, and we would return NoChange
     111             :       // in this case because we find node1 first, but what we should actually
     112             :       // do is return Replace(node2) instead.
     113    11036923 :       for (size_t j = (i + 1) & mask;; j = (j + 1) & mask) {
     114    11036923 :         Node* entry = entries_[j];
     115    11036923 :         if (!entry) {
     116             :           // No collision, {node} is fine.
     117             :           return NoChange();
     118             :         }
     119     8306259 :         if (entry->IsDead()) {
     120             :           continue;
     121             :         }
     122     8301332 :         if (entry == node) {
     123             :           // Collision with ourselves, doesn't count as a real collision.
     124             :           // Opportunistically clean-up the duplicate entry if we're at the end
     125             :           // of a bucket.
     126          41 :           if (!entries_[(j + 1) & mask]) {
     127           3 :             entries_[j] = nullptr;
     128           3 :             size_--;
     129             :             return NoChange();
     130             :           }
     131             :           // Otherwise, keep searching for another collision.
     132             :           continue;
     133             :         }
     134     8301291 :         if (Equals(entry, node)) {
     135         366 :           Reduction reduction = ReplaceIfTypesMatch(node, entry);
     136         366 :           if (reduction.Changed()) {
     137             :             // Overwrite the colliding entry with the actual entry.
     138         366 :             entries_[i] = entry;
     139             :             // Opportunistically clean-up the duplicate entry if we're at the
     140             :             // end of a bucket.
     141         366 :             if (!entries_[(j + 1) & mask]) {
     142         128 :               entries_[j] = nullptr;
     143         128 :               size_--;
     144             :             }
     145             :           }
     146         366 :           return reduction;
     147             :         }
     148     8305889 :       }
     149             :     }
     150             : 
     151             :     // Skip dead entries, but remember their indices so we can reuse them.
     152    64407351 :     if (entry->IsDead()) {
     153             :       dead = i;
     154             :       continue;
     155             :     }
     156    64383521 :     if (Equals(entry, node)) {
     157     1599688 :       return ReplaceIfTypesMatch(node, entry);
     158             :     }
     159    62808524 :   }
     160             : }
     161             : 
     162     1600054 : Reduction ValueNumberingReducer::ReplaceIfTypesMatch(Node* node,
     163             :                                                      Node* replacement) {
     164             :   // Make sure the replacement has at least as good type as the original node.
     165     2356213 :   if (NodeProperties::IsTyped(replacement) && NodeProperties::IsTyped(node)) {
     166             :     Type* replacement_type = NodeProperties::GetType(replacement);
     167             :     Type* node_type = NodeProperties::GetType(node);
     168      748274 :     if (!replacement_type->Is(node_type)) {
     169             :       // Ideally, we would set an intersection of {replacement_type} and
     170             :       // {node_type} here. However, typing of NumberConstants assigns different
     171             :       // types to constants with the same value (it creates a fresh heap
     172             :       // number), which would make the intersection empty. To be safe, we use
     173             :       // the smaller type if the types are comparable.
     174         477 :       if (node_type->Is(replacement_type)) {
     175             :         NodeProperties::SetType(replacement, node_type);
     176             :       } else {
     177             :         // Types are not comparable => do not replace.
     178             :         return NoChange();
     179             :       }
     180             :     }
     181             :   }
     182             :   return Replace(replacement);
     183             : }
     184             : 
     185             : 
     186      126396 : void ValueNumberingReducer::Grow() {
     187             :   // Allocate a new block of entries double the previous capacity.
     188       63198 :   Node** const old_entries = entries_;
     189       63198 :   size_t const old_capacity = capacity_;
     190       63198 :   capacity_ *= 2;
     191       63286 :   entries_ = temp_zone()->NewArray<Node*>(capacity_);
     192       63286 :   memset(entries_, 0, sizeof(*entries_) * capacity_);
     193       63286 :   size_ = 0;
     194       63286 :   size_t const mask = capacity_ - 1;
     195             : 
     196             :   // Insert the old entries into the new block (skipping dead nodes).
     197    27958857 :   for (size_t i = 0; i < old_capacity; ++i) {
     198    27895659 :     Node* const old_entry = old_entries[i];
     199    50228743 :     if (!old_entry || old_entry->IsDead()) continue;
     200    29685605 :     for (size_t j = HashCode(old_entry) & mask;; j = (j + 1) & mask) {
     201    29685517 :       Node* const entry = entries_[j];
     202    29685517 :       if (entry == old_entry) {
     203             :         // Skip duplicate of the old entry.
     204             :         break;
     205             :       }
     206    29641640 :       if (!entry) {
     207    22278705 :         entries_[j] = old_entry;
     208    22278705 :         size_++;
     209    22278705 :         break;
     210             :       }
     211     7362935 :     }
     212             :   }
     213       63198 : }
     214             : 
     215             : }  // namespace compiler
     216             : }  // namespace internal
     217             : }  // namespace v8

Generated by: LCOV version 1.10