LCOV - code coverage report
Current view: top level - src/compiler - escape-analysis.cc (source / functions) Hit Total Coverage
Test: app.info Lines: 315 318 99.1 %
Date: 2019-04-17 Functions: 34 34 100.0 %

          Line data    Source code
       1             : // Copyright 2017 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/escape-analysis.h"
       6             : 
       7             : #include "src/bootstrapper.h"
       8             : #include "src/compiler/linkage.h"
       9             : #include "src/compiler/node-matchers.h"
      10             : #include "src/compiler/operator-properties.h"
      11             : #include "src/compiler/simplified-operator.h"
      12             : #include "src/handles-inl.h"
      13             : #include "src/objects/map-inl.h"
      14             : 
      15             : #ifdef DEBUG
      16             : #define TRACE(...)                                    \
      17             :   do {                                                \
      18             :     if (FLAG_trace_turbo_escape) PrintF(__VA_ARGS__); \
      19             :   } while (false)
      20             : #else
      21             : #define TRACE(...)
      22             : #endif
      23             : 
      24             : namespace v8 {
      25             : namespace internal {
      26             : namespace compiler {
      27             : 
      28             : template <class T>
      29             : class Sidetable {
      30             :  public:
      31             :   explicit Sidetable(Zone* zone) : map_(zone) {}
      32   122292483 :   T& operator[](const Node* node) {
      33             :     NodeId id = node->id();
      34   244584966 :     if (id >= map_.size()) {
      35     2418148 :       map_.resize(id + 1);
      36             :     }
      37   122292485 :     return map_[id];
      38             :   }
      39             : 
      40             :  private:
      41             :   ZoneVector<T> map_;
      42             : };
      43             : 
      44             : template <class T>
      45             : class SparseSidetable {
      46             :  public:
      47             :   explicit SparseSidetable(Zone* zone, T def_value = T())
      48      928157 :       : def_value_(std::move(def_value)), map_(zone) {}
      49    66231803 :   void Set(const Node* node, T value) {
      50   132464153 :     auto iter = map_.find(node->id());
      51    66232350 :     if (iter != map_.end()) {
      52     2214249 :       iter->second = std::move(value);
      53    64018040 :     } else if (value != def_value_) {
      54     6214433 :       map_.insert(iter, std::make_pair(node->id(), std::move(value)));
      55             :     }
      56    66232296 :   }
      57             :   const T& Get(const Node* node) const {
      58   344391776 :     auto iter = map_.find(node->id());
      59   172199125 :     return iter != map_.end() ? iter->second : def_value_;
      60             :   }
      61             : 
      62             :  private:
      63             :   T def_value_;
      64             :   ZoneUnorderedMap<NodeId, T> map_;
      65             : };
      66             : 
      67             : // Keeps track of the changes to the current node during reduction.
      68             : // Encapsulates the current state of the IR graph and the reducer state like
      69             : // side-tables. All access to the IR and the reducer state should happen through
      70             : // a ReduceScope to ensure that changes and dependencies are tracked and all
      71             : // necessary node revisitations happen.
      72             : class ReduceScope {
      73             :  public:
      74             :   using Reduction = EffectGraphReducer::Reduction;
      75             :   explicit ReduceScope(Node* node, Reduction* reduction)
      76    33117027 :       : current_node_(node), reduction_(reduction) {}
      77             : 
      78             :  protected:
      79             :   Node* current_node() const { return current_node_; }
      80             :   Reduction* reduction() { return reduction_; }
      81             : 
      82             :  private:
      83             :   Node* current_node_;
      84             :   Reduction* reduction_;
      85             : };
      86             : 
      87             : // A VariableTracker object keeps track of the values of variables at all points
      88             : // of the effect chain and introduces new phi nodes when necessary.
      89             : // Initially and by default, variables are mapped to nullptr, which means that
      90             : // the variable allocation point does not dominate the current point on the
      91             : // effect chain. We map variables that represent uninitialized memory to the
      92             : // Dead node to ensure it is not read.
      93             : // Unmapped values are impossible by construction, it is indistinguishable if a
      94             : // PersistentMap does not contain an element or maps it to the default element.
      95             : class VariableTracker {
      96             :  private:
      97             :   // The state of all variables at one point in the effect chain.
      98             :   class State {
      99             :    public:
     100             :     using Map = PersistentMap<Variable, Node*>;
     101             : 
     102             :     explicit State(Zone* zone) : map_(zone) {}
     103    10616570 :     Node* Get(Variable var) const {
     104    10616570 :       CHECK(var != Variable::Invalid());
     105    10616570 :       return map_.Get(var);
     106             :     }
     107     6047052 :     void Set(Variable var, Node* node) {
     108     6047052 :       CHECK(var != Variable::Invalid());
     109     6047052 :       return map_.Set(var, node);
     110             :     }
     111             :     Map::iterator begin() const { return map_.begin(); }
     112             :     Map::iterator end() const { return map_.end(); }
     113    64267102 :     bool operator!=(const State& other) const { return map_ != other.map_; }
     114             : 
     115             :    private:
     116             :     Map map_;
     117             :   };
     118             : 
     119             :  public:
     120             :   VariableTracker(JSGraph* graph, EffectGraphReducer* reducer, Zone* zone);
     121      763537 :   Variable NewVariable() { return Variable(next_variable_++); }
     122     1357902 :   Node* Get(Variable var, Node* effect) { return table_.Get(effect).Get(var); }
     123             :   Zone* zone() { return zone_; }
     124             : 
     125             :   class Scope : public ReduceScope {
     126             :    public:
     127             :     Scope(VariableTracker* tracker, Node* node, Reduction* reduction);
     128             :     ~Scope();
     129       25853 :     Maybe<Node*> Get(Variable var) {
     130       25853 :       Node* node = current_state_.Get(var);
     131       44707 :       if (node && node->opcode() == IrOpcode::kDead) {
     132             :         // TODO(tebbi): We use {Dead} as a sentinel for uninitialized memory.
     133             :         // Reading uninitialized memory can only happen in unreachable code. In
     134             :         // this case, we have to mark the object as escaping to avoid dead nodes
     135             :         // in the graph. This is a workaround that should be removed once we can
     136             :         // handle dead nodes everywhere.
     137             :         return Nothing<Node*>();
     138             :       }
     139             :       return Just(node);
     140             :     }
     141     2619260 :     void Set(Variable var, Node* node) { current_state_.Set(var, node); }
     142             : 
     143             :    private:
     144             :     VariableTracker* states_;
     145             :     State current_state_;
     146             :   };
     147             : 
     148             :  private:
     149             :   State MergeInputs(Node* effect_phi);
     150             :   Zone* zone_;
     151             :   JSGraph* graph_;
     152             :   SparseSidetable<State> table_;
     153             :   ZoneVector<Node*> buffer_;
     154             :   EffectGraphReducer* reducer_;
     155             :   int next_variable_ = 0;
     156             : 
     157             :   DISALLOW_COPY_AND_ASSIGN(VariableTracker);
     158             : };
     159             : 
     160             : // Encapsulates the current state of the escape analysis reducer to preserve
     161             : // invariants regarding changes and re-visitation.
     162             : class EscapeAnalysisTracker : public ZoneObject {
     163             :  public:
     164      464077 :   EscapeAnalysisTracker(JSGraph* jsgraph, EffectGraphReducer* reducer,
     165             :                         Zone* zone)
     166             :       : virtual_objects_(zone),
     167             :         replacements_(zone),
     168             :         variable_states_(jsgraph, reducer, zone),
     169             :         jsgraph_(jsgraph),
     170      464079 :         zone_(zone) {}
     171             : 
     172             :   class Scope : public VariableTracker::Scope {
     173             :    public:
     174             :     Scope(EffectGraphReducer* reducer, EscapeAnalysisTracker* tracker,
     175             :           Node* node, Reduction* reduction)
     176             :         : VariableTracker::Scope(&tracker->variable_states_, node, reduction),
     177             :           tracker_(tracker),
     178    33116128 :           reducer_(reducer) {}
     179     5301823 :     const VirtualObject* GetVirtualObject(Node* node) {
     180    10603675 :       VirtualObject* vobject = tracker_->virtual_objects_.Get(node);
     181     5301852 :       if (vobject) vobject->AddDependency(current_node());
     182     5301853 :       return vobject;
     183             :     }
     184             :     // Create or retrieve a virtual object for the current node.
     185      297133 :     const VirtualObject* InitVirtualObject(int size) {
     186             :       DCHECK_EQ(IrOpcode::kAllocate, current_node()->opcode());
     187      594267 :       VirtualObject* vobject = tracker_->virtual_objects_.Get(current_node());
     188      297134 :       if (vobject) {
     189      129444 :         CHECK(vobject->size() == size);
     190             :       } else {
     191      167690 :         vobject = tracker_->NewVirtualObject(size);
     192             :       }
     193      297134 :       if (vobject) vobject->AddDependency(current_node());
     194      297134 :       vobject_ = vobject;
     195      297134 :       return vobject;
     196             :     }
     197             : 
     198             :     void SetVirtualObject(Node* object) {
     199      341214 :       vobject_ = tracker_->virtual_objects_.Get(object);
     200             :     }
     201             : 
     202    22017495 :     void SetEscaped(Node* node) {
     203    44034864 :       if (VirtualObject* object = tracker_->virtual_objects_.Get(node)) {
     204     1367707 :         if (object->HasEscaped()) return;
     205             :         TRACE("Setting %s#%d to escaped because of use by %s#%d\n",
     206             :               node->op()->mnemonic(), node->id(),
     207             :               current_node()->op()->mnemonic(), current_node()->id());
     208             :         object->SetEscaped();
     209       83527 :         object->RevisitDependants(reducer_);
     210             :       }
     211             :     }
     212             :     // The inputs of the current node have to be accessed through the scope to
     213             :     // ensure that they respect the node replacements.
     214    21582634 :     Node* ValueInput(int i) {
     215    21582634 :       return tracker_->ResolveReplacement(
     216    21582604 :           NodeProperties::GetValueInput(current_node(), i));
     217             :     }
     218     3620815 :     Node* ContextInput() {
     219     3620815 :       return tracker_->ResolveReplacement(
     220     3620816 :           NodeProperties::GetContextInput(current_node()));
     221             :     }
     222             : 
     223     1018182 :     void SetReplacement(Node* replacement) {
     224     1018182 :       replacement_ = replacement;
     225             :       vobject_ =
     226     2031082 :           replacement ? tracker_->virtual_objects_.Get(replacement) : nullptr;
     227             :       if (replacement) {
     228             :         TRACE("Set %s#%d as replacement.\n", replacement->op()->mnemonic(),
     229             :               replacement->id());
     230             :       } else {
     231             :         TRACE("Set nullptr as replacement.\n");
     232             :       }
     233     1018176 :     }
     234             : 
     235      998131 :     void MarkForDeletion() { SetReplacement(tracker_->jsgraph_->Dead()); }
     236             : 
     237    66231783 :     ~Scope() {
     238    64701715 :       if (replacement_ != tracker_->replacements_[current_node()] ||
     239    63171552 :           vobject_ != tracker_->virtual_objects_.Get(current_node())) {
     240             :         reduction()->set_value_changed();
     241             :       }
     242    33115690 :       tracker_->replacements_[current_node()] = replacement_;
     243    33115567 :       tracker_->virtual_objects_.Set(current_node(), vobject_);
     244    33116093 :     }
     245             : 
     246             :    private:
     247             :     EscapeAnalysisTracker* tracker_;
     248             :     EffectGraphReducer* reducer_;
     249             :     VirtualObject* vobject_ = nullptr;
     250             :     Node* replacement_ = nullptr;
     251             :   };
     252             : 
     253    56079356 :   Node* GetReplacementOf(Node* node) { return replacements_[node]; }
     254             :   Node* ResolveReplacement(Node* node) {
     255    25203420 :     if (Node* replacement = GetReplacementOf(node)) {
     256             :       return replacement;
     257             :     }
     258             :     return node;
     259             :   }
     260             : 
     261             :  private:
     262             :   friend class EscapeAnalysisResult;
     263             :   static const size_t kMaxTrackedObjects = 100;
     264             : 
     265      167690 :   VirtualObject* NewVirtualObject(int size) {
     266      167690 :     if (next_object_id_ >= kMaxTrackedObjects) return nullptr;
     267      111675 :     return new (zone_)
     268      223350 :         VirtualObject(&variable_states_, next_object_id_++, size);
     269             :   }
     270             : 
     271             :   SparseSidetable<VirtualObject*> virtual_objects_;
     272             :   Sidetable<Node*> replacements_;
     273             :   VariableTracker variable_states_;
     274             :   VirtualObject::Id next_object_id_ = 0;
     275             :   JSGraph* const jsgraph_;
     276             :   Zone* const zone_;
     277             : 
     278             :   DISALLOW_COPY_AND_ASSIGN(EscapeAnalysisTracker);
     279             : };
     280             : 
     281      464079 : EffectGraphReducer::EffectGraphReducer(
     282             :     Graph* graph, std::function<void(Node*, Reduction*)> reduce, Zone* zone)
     283             :     : graph_(graph),
     284             :       state_(graph, kNumStates),
     285             :       revisit_(zone),
     286             :       stack_(zone),
     287     1392242 :       reduce_(std::move(reduce)) {}
     288             : 
     289      464082 : void EffectGraphReducer::ReduceFrom(Node* node) {
     290             :   // Perform DFS and eagerly trigger revisitation as soon as possible.
     291             :   // A stack element {node, i} indicates that input i of node should be visited
     292             :   // next.
     293             :   DCHECK(stack_.empty());
     294      927793 :   stack_.push({node, 0});
     295   139759725 :   while (!stack_.empty()) {
     296   139295642 :     Node* current = stack_.top().node;
     297             :     int& input_index = stack_.top().input_index;
     298   278591284 :     if (input_index < current->InputCount()) {
     299             :       Node* input = current->InputAt(input_index);
     300   106179637 :       input_index++;
     301   106179637 :       switch (state_.Get(input)) {
     302             :         case State::kVisited:
     303             :           // The input is already reduced.
     304             :           break;
     305             :         case State::kOnStack:
     306             :           // The input is on the DFS stack right now, so it will be revisited
     307             :           // later anyway.
     308             :           break;
     309             :         case State::kUnvisited:
     310             :         case State::kRevisit: {
     311             :           state_.Set(input, State::kOnStack);
     312    60691601 :           stack_.push({input, 0});
     313    30346039 :           break;
     314             :         }
     315             :       }
     316             :     } else {
     317             :       stack_.pop();
     318    33115961 :       Reduction reduction;
     319             :       reduce_(current, &reduction);
     320   240815287 :       for (Edge edge : current->use_edges()) {
     321             :         // Mark uses for revisitation.
     322             :         Node* use = edge.from();
     323   103849963 :         if (NodeProperties::IsEffectEdge(edge)) {
     324    15133723 :           if (reduction.effect_changed()) Revisit(use);
     325             :         } else {
     326    88719001 :           if (reduction.value_changed()) Revisit(use);
     327             :         }
     328             :       }
     329             :       state_.Set(current, State::kVisited);
     330             :       // Process the revisitation buffer immediately. This improves performance
     331             :       // of escape analysis. Using a stack for {revisit_} reverses the order in
     332             :       // which the revisitation happens. This also seems to improve performance.
     333    35423555 :       while (!revisit_.empty()) {
     334     2307655 :         Node* revisit = revisit_.top();
     335     2307655 :         if (state_.Get(revisit) == State::kRevisit) {
     336             :           state_.Set(revisit, State::kOnStack);
     337     4615643 :           stack_.push({revisit, 0});
     338             :         }
     339             :         revisit_.pop();
     340             :       }
     341             :     }
     342             :   }
     343      464083 : }
     344             : 
     345    10783630 : void EffectGraphReducer::Revisit(Node* node) {
     346    21567260 :   if (state_.Get(node) == State::kVisited) {
     347             :     TRACE("  Queueing for revisit: %s#%d\n", node->op()->mnemonic(),
     348             :           node->id());
     349             :     state_.Set(node, State::kRevisit);
     350             :     revisit_.push(node);
     351             :   }
     352    10783589 : }
     353             : 
     354      464080 : VariableTracker::VariableTracker(JSGraph* graph, EffectGraphReducer* reducer,
     355             :                                  Zone* zone)
     356             :     : zone_(zone),
     357             :       graph_(graph),
     358             :       table_(zone, State(zone)),
     359             :       buffer_(zone),
     360      928159 :       reducer_(reducer) {}
     361             : 
     362    33117027 : VariableTracker::Scope::Scope(VariableTracker* states, Node* node,
     363             :                               Reduction* reduction)
     364             :     : ReduceScope(node, reduction),
     365             :       states_(states),
     366    33117027 :       current_state_(states->zone_) {
     367    33117027 :   switch (node->opcode()) {
     368             :     case IrOpcode::kEffectPhi:
     369      378791 :       current_state_ = states_->MergeInputs(node);
     370      378757 :       break;
     371             :     default:
     372             :       int effect_inputs = node->op()->EffectInputCount();
     373    32738236 :       if (effect_inputs == 1) {
     374             :         current_state_ =
     375    28229591 :             states_->table_.Get(NodeProperties::GetEffectInput(node, 0));
     376             :       } else {
     377             :         DCHECK_EQ(0, effect_inputs);
     378             :       }
     379             :   }
     380    33116608 : }
     381             : 
     382    66232041 : VariableTracker::Scope::~Scope() {
     383    66232320 :   if (!reduction()->effect_changed() &&
     384    33116014 :       states_->table_.Get(current_node()) != current_state_) {
     385             :     reduction()->set_effect_changed();
     386             :   }
     387    33116306 :   states_->table_.Set(current_node(), current_state_);
     388    33116060 : }
     389             : 
     390      378796 : VariableTracker::State VariableTracker::MergeInputs(Node* effect_phi) {
     391             :   // A variable that is mapped to [nullptr] was not assigned a value on every
     392             :   // execution path to the current effect phi. Relying on the invariant that
     393             :   // every variable is initialized (at least with a sentinel like the Dead
     394             :   // node), this means that the variable initialization does not dominate the
     395             :   // current point. So for loop effect phis, we can keep nullptr for a variable
     396             :   // as long as the first input of the loop has nullptr for this variable. For
     397             :   // non-loop effect phis, we can even keep it nullptr as long as any input has
     398             :   // nullptr.
     399             :   DCHECK_EQ(IrOpcode::kEffectPhi, effect_phi->opcode());
     400             :   int arity = effect_phi->op()->EffectInputCount();
     401      378796 :   Node* control = NodeProperties::GetControlInput(effect_phi, 0);
     402             :   TRACE("control: %s#%d\n", control->op()->mnemonic(), control->id());
     403      378784 :   bool is_loop = control->opcode() == IrOpcode::kLoop;
     404      378784 :   buffer_.reserve(arity + 1);
     405             : 
     406      757535 :   State first_input = table_.Get(NodeProperties::GetEffectInput(effect_phi, 0));
     407      378763 :   State result = first_input;
     408     7292240 :   for (std::pair<Variable, Node*> var_value : first_input) {
     409     3461139 :     if (Node* value = var_value.second) {
     410     3462286 :       Variable var = var_value.first;
     411             :       TRACE("var %i:\n", var.id_);
     412             :       buffer_.clear();
     413     3462286 :       buffer_.push_back(value);
     414             :       bool identical_inputs = true;
     415             :       int num_defined_inputs = 1;
     416             :       TRACE("  input 0: %s#%d\n", value->op()->mnemonic(), value->id());
     417    16688139 :       for (int i = 1; i < arity; ++i) {
     418             :         Node* next_value =
     419    13299252 :             table_.Get(NodeProperties::GetEffectInput(effect_phi, i)).Get(var);
     420     6609580 :         if (next_value != value) identical_inputs = false;
     421     6609580 :         if (next_value != nullptr) {
     422     5517149 :           num_defined_inputs++;
     423             :           TRACE("  input %i: %s#%d\n", i, next_value->op()->mnemonic(),
     424             :                 next_value->id());
     425             :         } else {
     426             :           TRACE("  input %i: nullptr\n", i);
     427             :         }
     428     6609580 :         buffer_.push_back(next_value);
     429             :       }
     430             : 
     431     3445906 :       Node* old_value = table_.Get(effect_phi).Get(var);
     432             :       if (old_value) {
     433             :         TRACE("  old: %s#%d\n", old_value->op()->mnemonic(), old_value->id());
     434             :       } else {
     435             :         TRACE("  old: nullptr\n");
     436             :       }
     437             :       // Reuse a previously created phi node if possible.
     438     5022104 :       if (old_value && old_value->opcode() == IrOpcode::kPhi &&
     439      279087 :           NodeProperties::GetControlInput(old_value, 0) == control) {
     440             :         // Since a phi node can never dominate its control node,
     441             :         // [old_value] cannot originate from the inputs. Thus [old_value]
     442             :         // must have been created by a previous reduction of this [effect_phi].
     443      751576 :         for (int i = 0; i < arity; ++i) {
     444      316320 :           NodeProperties::ReplaceValueInput(
     445      632620 :               old_value, buffer_[i] ? buffer_[i] : graph_->Dead(), i);
     446             :           // This change cannot affect the rest of the reducer, so there is no
     447             :           // need to trigger additional revisitations.
     448             :         }
     449      118976 :         result.Set(var, old_value);
     450             :       } else {
     451     3316711 :         if (num_defined_inputs == 1 && is_loop) {
     452             :           // For loop effect phis, the variable initialization dominates iff it
     453             :           // dominates the first input.
     454             :           DCHECK_EQ(2, arity);
     455             :           DCHECK_EQ(value, buffer_[0]);
     456      261338 :           result.Set(var, value);
     457     3055373 :         } else if (num_defined_inputs < arity) {
     458             :           // If the variable is undefined on some input of this non-loop effect
     459             :           // phi, then its initialization does not dominate this point.
     460      200677 :           result.Set(var, nullptr);
     461             :         } else {
     462             :           DCHECK_EQ(num_defined_inputs, arity);
     463             :           // We only create a phi if the values are different.
     464     2854696 :           if (identical_inputs) {
     465     2701158 :             result.Set(var, value);
     466             :           } else {
     467             :             TRACE("Creating new phi\n");
     468      153538 :             buffer_.push_back(control);
     469      153538 :             Node* phi = graph_->graph()->NewNode(
     470      153538 :                 graph_->common()->Phi(MachineRepresentation::kTagged, arity),
     471      153538 :                 arity + 1, &buffer_.front());
     472             :             // TODO(tebbi): Computing precise types here is tricky, because of
     473             :             // the necessary revisitations. If we really need this, we should
     474             :             // probably do it afterwards.
     475             :             NodeProperties::SetType(phi, Type::Any());
     476      153538 :             reducer_->AddRoot(phi);
     477      153538 :             result.Set(var, phi);
     478             :           }
     479             :         }
     480             :       }
     481             : #ifdef DEBUG
     482             :       if (Node* result_node = result.Get(var)) {
     483             :         TRACE("  result: %s#%d\n", result_node->op()->mnemonic(),
     484             :               result_node->id());
     485             :       } else {
     486             :         TRACE("  result: nullptr\n");
     487             :       }
     488             : #endif
     489             :     }
     490             :   }
     491      378759 :   return result;
     492             : }
     493             : 
     494             : namespace {
     495             : 
     496             : int OffsetOfFieldAccess(const Operator* op) {
     497             :   DCHECK(op->opcode() == IrOpcode::kLoadField ||
     498             :          op->opcode() == IrOpcode::kStoreField);
     499      966353 :   FieldAccess access = FieldAccessOf(op);
     500             :   return access.offset;
     501             : }
     502             : 
     503             : int OffsetOfElementAt(ElementAccess const& access, int index) {
     504             :   DCHECK_GE(index, 0);
     505             :   DCHECK_GE(ElementSizeLog2Of(access.machine_type.representation()),
     506             :             kTaggedSizeLog2);
     507       47375 :   return access.header_size +
     508       47519 :          (index << ElementSizeLog2Of(access.machine_type.representation()));
     509             : }
     510             : 
     511       46883 : Maybe<int> OffsetOfElementsAccess(const Operator* op, Node* index_node) {
     512             :   DCHECK(op->opcode() == IrOpcode::kLoadElement ||
     513             :          op->opcode() == IrOpcode::kStoreElement);
     514       46883 :   Type index_type = NodeProperties::GetType(index_node);
     515       46883 :   if (!index_type.Is(Type::OrderedNumber())) return Nothing<int>();
     516       46883 :   double max = index_type.Max();
     517       46883 :   double min = index_type.Min();
     518       46883 :   int index = static_cast<int>(min);
     519       46883 :   if (index < 0 || index != min || index != max) return Nothing<int>();
     520       45852 :   return Just(OffsetOfElementAt(ElementAccessOf(op), index));
     521             : }
     522             : 
     523         118 : Node* LowerCompareMapsWithoutLoad(Node* checked_map,
     524             :                                   ZoneHandleSet<Map> const& checked_against,
     525             :                                   JSGraph* jsgraph) {
     526         118 :   Node* true_node = jsgraph->TrueConstant();
     527         118 :   Node* false_node = jsgraph->FalseConstant();
     528             :   Node* replacement = false_node;
     529         236 :   for (Handle<Map> map : checked_against) {
     530         118 :     Node* map_node = jsgraph->HeapConstant(map);
     531             :     // We cannot create a HeapConstant type here as we are off-thread.
     532             :     NodeProperties::SetType(map_node, Type::Internal());
     533         118 :     Node* comparison = jsgraph->graph()->NewNode(
     534             :         jsgraph->simplified()->ReferenceEqual(), checked_map, map_node);
     535             :     NodeProperties::SetType(comparison, Type::Boolean());
     536         118 :     if (replacement == false_node) {
     537             :       replacement = comparison;
     538             :     } else {
     539           0 :       replacement = jsgraph->graph()->NewNode(
     540             :           jsgraph->common()->Select(MachineRepresentation::kTaggedPointer),
     541             :           comparison, true_node, replacement);
     542             :       NodeProperties::SetType(replacement, Type::Boolean());
     543             :     }
     544             :   }
     545         118 :   return replacement;
     546             : }
     547             : 
     548    33116612 : void ReduceNode(const Operator* op, EscapeAnalysisTracker::Scope* current,
     549             :                 JSGraph* jsgraph) {
     550    33116612 :   switch (op->opcode()) {
     551             :     case IrOpcode::kAllocate: {
     552      297133 :       NumberMatcher size(current->ValueInput(0));
     553      297131 :       if (!size.HasValue()) break;
     554      297133 :       int size_int = static_cast<int>(size.Value());
     555      297133 :       if (size_int != size.Value()) break;
     556      297133 :       if (const VirtualObject* vobject = current->InitVirtualObject(size_int)) {
     557             :         // Initialize with dead nodes as a sentinel for uninitialized memory.
     558     1866717 :         for (Variable field : *vobject) {
     559     1625597 :           current->Set(field, jsgraph->Dead());
     560             :         }
     561             :       }
     562             :       break;
     563             :     }
     564             :     case IrOpcode::kFinishRegion:
     565      310204 :       current->SetVirtualObject(current->ValueInput(0));
     566             :       break;
     567             :     case IrOpcode::kStoreField: {
     568     3365199 :       Node* object = current->ValueInput(0);
     569     3365199 :       Node* value = current->ValueInput(1);
     570     3365192 :       const VirtualObject* vobject = current->GetVirtualObject(object);
     571             :       Variable var;
     572     4313312 :       if (vobject && !vobject->HasEscaped() &&
     573      948120 :           vobject->FieldAt(OffsetOfFieldAccess(op)).To(&var)) {
     574             :         current->Set(var, value);
     575      948116 :         current->MarkForDeletion();
     576             :       } else {
     577     2417077 :         current->SetEscaped(object);
     578     2417077 :         current->SetEscaped(value);
     579             :       }
     580             :       break;
     581             :     }
     582             :     case IrOpcode::kStoreElement: {
     583      103672 :       Node* object = current->ValueInput(0);
     584      103672 :       Node* index = current->ValueInput(1);
     585      103672 :       Node* value = current->ValueInput(2);
     586      103672 :       const VirtualObject* vobject = current->GetVirtualObject(object);
     587             :       int offset;
     588             :       Variable var;
     589      242390 :       if (vobject && !vobject->HasEscaped() &&
     590      194777 :           OffsetOfElementsAccess(op, index).To(&offset) &&
     591       45545 :           vobject->FieldAt(offset).To(&var)) {
     592             :         current->Set(var, value);
     593       45545 :         current->MarkForDeletion();
     594             :       } else {
     595       58127 :         current->SetEscaped(value);
     596       58127 :         current->SetEscaped(object);
     597             :       }
     598             :       break;
     599             :     }
     600             :     case IrOpcode::kLoadField: {
     601     1324636 :       Node* object = current->ValueInput(0);
     602     1324636 :       const VirtualObject* vobject = current->GetVirtualObject(object);
     603             :       Variable var;
     604             :       Node* value;
     605     1440918 :       if (vobject && !vobject->HasEscaped() &&
     606     1361080 :           vobject->FieldAt(OffsetOfFieldAccess(op)).To(&var) &&
     607       18215 :           current->Get(var).To(&value)) {
     608       18215 :         current->SetReplacement(value);
     609             :       } else {
     610     1306414 :         current->SetEscaped(object);
     611             :       }
     612             :       break;
     613             :     }
     614             :     case IrOpcode::kLoadElement: {
     615       26685 :       Node* object = current->ValueInput(0);
     616       26685 :       Node* index = current->ValueInput(1);
     617       26685 :       const VirtualObject* vobject = current->GetVirtualObject(object);
     618             :       int offset;
     619             :       Variable var;
     620             :       Node* value;
     621       30229 :       if (vobject && !vobject->HasEscaped() &&
     622        1630 :           OffsetOfElementsAccess(op, index).To(&offset) &&
     623       27590 :           vobject->FieldAt(offset).To(&var) && current->Get(var).To(&value)) {
     624         299 :         current->SetReplacement(value);
     625       26386 :       } else if (vobject && !vobject->HasEscaped()) {
     626             :         // Compute the known length (aka the number of elements) of {object}
     627             :         // based on the virtual object information.
     628        1024 :         ElementAccess const& access = ElementAccessOf(op);
     629             :         int const length =
     630        1024 :             (vobject->size() - access.header_size) >>
     631        1024 :             ElementSizeLog2Of(access.machine_type.representation());
     632             :         Variable var0, var1;
     633             :         Node* value0;
     634             :         Node* value1;
     635        1168 :         if (length == 1 &&
     636         288 :             vobject->FieldAt(OffsetOfElementAt(access, 0)).To(&var) &&
     637        1312 :             current->Get(var).To(&value) &&
     638          98 :             (value == nullptr ||
     639        1122 :              NodeProperties::GetType(value).Is(access.type))) {
     640             :           // The {object} has no elements, and we know that the LoadElement
     641             :           // {index} must be within bounds, thus it must always yield this
     642             :           // one element of {object}.
     643         144 :           current->SetReplacement(value);
     644         144 :           break;
     645        1645 :         } else if (length == 2 &&
     646        1530 :                    vobject->FieldAt(OffsetOfElementAt(access, 0)).To(&var0) &&
     647        1530 :                    current->Get(var0).To(&value0) &&
     648         458 :                    (value0 == nullptr ||
     649        2096 :                     NodeProperties::GetType(value0).Is(access.type)) &&
     650        1516 :                    vobject->FieldAt(OffsetOfElementAt(access, 1)).To(&var1) &&
     651        2396 :                    current->Get(var1).To(&value1) &&
     652         451 :                    (value1 == nullptr ||
     653        1331 :                     NodeProperties::GetType(value1).Is(access.type))) {
     654         758 :           if (value0 && value1) {
     655             :             // The {object} has exactly two elements, so the LoadElement
     656             :             // must return one of them (i.e. either the element at index
     657             :             // 0 or the one at index 1). So we can turn the LoadElement
     658             :             // into a Select operation instead (still allowing the {object}
     659             :             // to be scalar replaced). We must however mark the elements
     660             :             // of the {object} itself as escaping.
     661             :             Node* check =
     662         451 :                 jsgraph->graph()->NewNode(jsgraph->simplified()->NumberEqual(),
     663             :                                           index, jsgraph->ZeroConstant());
     664             :             NodeProperties::SetType(check, Type::Boolean());
     665         451 :             Node* select = jsgraph->graph()->NewNode(
     666             :                 jsgraph->common()->Select(access.machine_type.representation()),
     667             :                 check, value0, value1);
     668             :             NodeProperties::SetType(select, access.type);
     669         451 :             current->SetReplacement(select);
     670         451 :             current->SetEscaped(value0);
     671         451 :             current->SetEscaped(value1);
     672         451 :             break;
     673             :           } else {
     674             :             // If the variables have no values, we have
     675             :             // not reached the fixed-point yet.
     676             :             break;
     677             :           }
     678             :         }
     679             :       }
     680       25783 :       current->SetEscaped(object);
     681       25783 :       break;
     682             :     }
     683             :     case IrOpcode::kTypeGuard: {
     684       31011 :       current->SetVirtualObject(current->ValueInput(0));
     685             :       break;
     686             :     }
     687             :     case IrOpcode::kReferenceEqual: {
     688      199019 :       Node* left = current->ValueInput(0);
     689      199019 :       Node* right = current->ValueInput(1);
     690      199019 :       const VirtualObject* left_object = current->GetVirtualObject(left);
     691      199019 :       const VirtualObject* right_object = current->GetVirtualObject(right);
     692             :       Node* replacement = nullptr;
     693      199019 :       if (left_object && !left_object->HasEscaped()) {
     694         256 :         if (right_object && !right_object->HasEscaped() &&
     695             :             left_object->id() == right_object->id()) {
     696           4 :           replacement = jsgraph->TrueConstant();
     697             :         } else {
     698         252 :           replacement = jsgraph->FalseConstant();
     699             :         }
     700      198763 :       } else if (right_object && !right_object->HasEscaped()) {
     701         471 :         replacement = jsgraph->FalseConstant();
     702             :       }
     703      199019 :       if (replacement) {
     704             :         // TODO(tebbi) This is a workaround for uninhabited types. If we
     705             :         // replaced a value of uninhabited type with a constant, we would
     706             :         // widen the type of the node. This could produce inconsistent
     707             :         // types (which might confuse representation selection). We get
     708             :         // around this by refusing to constant-fold and escape-analyze
     709             :         // if the type is not inhabited.
     710         727 :         if (!NodeProperties::GetType(left).IsNone() &&
     711             :             !NodeProperties::GetType(right).IsNone()) {
     712         727 :           current->SetReplacement(replacement);
     713             :         } else {
     714           0 :           current->SetEscaped(left);
     715           0 :           current->SetEscaped(right);
     716             :         }
     717             :       }
     718             :       break;
     719             :     }
     720             :     case IrOpcode::kCheckMaps: {
     721       66308 :       CheckMapsParameters params = CheckMapsParametersOf(op);
     722       66308 :       Node* checked = current->ValueInput(0);
     723       66308 :       const VirtualObject* vobject = current->GetVirtualObject(checked);
     724             :       Variable map_field;
     725             :       Node* map;
     726       83177 :       if (vobject && !vobject->HasEscaped() &&
     727       77402 :           vobject->FieldAt(HeapObject::kMapOffset).To(&map_field) &&
     728        5547 :           current->Get(map_field).To(&map)) {
     729        5547 :         if (map) {
     730        4447 :           Type const map_type = NodeProperties::GetType(map);
     731             :           AllowHandleDereference handle_dereference;
     732        8885 :           if (map_type.IsHeapConstant() &&
     733        4438 :               params.maps().contains(
     734             :                   Handle<Map>::cast(map_type.AsHeapConstant()->Value()))) {
     735        4345 :             current->MarkForDeletion();
     736        4345 :             break;
     737             :           }
     738             :         } else {
     739             :           // If the variable has no value, we have not reached the fixed-point
     740             :           // yet.
     741             :           break;
     742             :         }
     743             :       }
     744       60863 :       current->SetEscaped(checked);
     745       60863 :       break;
     746             :     }
     747             :     case IrOpcode::kCompareMaps: {
     748        9241 :       Node* object = current->ValueInput(0);
     749        9241 :       const VirtualObject* vobject = current->GetVirtualObject(object);
     750             :       Variable map_field;
     751             :       Node* object_map;
     752        9598 :       if (vobject && !vobject->HasEscaped() &&
     753        9491 :           vobject->FieldAt(HeapObject::kMapOffset).To(&map_field) &&
     754         125 :           current->Get(map_field).To(&object_map)) {
     755         125 :         if (object_map) {
     756         118 :           current->SetReplacement(LowerCompareMapsWithoutLoad(
     757         236 :               object_map, CompareMapsParametersOf(op), jsgraph));
     758         118 :           break;
     759             :         } else {
     760             :           // If the variable has no value, we have not reached the fixed-point
     761             :           // yet.
     762             :           break;
     763             :         }
     764             :       }
     765        9116 :       current->SetEscaped(object);
     766        9116 :       break;
     767             :     }
     768             :     case IrOpcode::kCheckHeapObject: {
     769       31415 :       Node* checked = current->ValueInput(0);
     770       31415 :       switch (checked->opcode()) {
     771             :         case IrOpcode::kAllocate:
     772             :         case IrOpcode::kFinishRegion:
     773             :         case IrOpcode::kHeapConstant:
     774          98 :           current->SetReplacement(checked);
     775          98 :           break;
     776             :         default:
     777       31317 :           current->SetEscaped(checked);
     778       31317 :           break;
     779             :       }
     780             :       break;
     781             :     }
     782             :     case IrOpcode::kMapGuard: {
     783        8095 :       Node* object = current->ValueInput(0);
     784        8095 :       const VirtualObject* vobject = current->GetVirtualObject(object);
     785        8095 :       if (vobject && !vobject->HasEscaped()) {
     786         125 :         current->MarkForDeletion();
     787             :       }
     788             :       break;
     789             :     }
     790             :     case IrOpcode::kStateValues:
     791             :     case IrOpcode::kFrameState:
     792             :       // These uses are always safe.
     793             :       break;
     794             :     default: {
     795             :       // For unknown nodes, treat all value inputs as escaping.
     796             :       int value_input_count = op->ValueInputCount();
     797    42998539 :       for (int i = 0; i < value_input_count; ++i) {
     798    12012155 :         Node* input = current->ValueInput(i);
     799    12012112 :         current->SetEscaped(input);
     800             :       }
     801    18974413 :       if (OperatorProperties::HasContextInput(op)) {
     802     3620816 :         current->SetEscaped(current->ContextInput());
     803             :       }
     804             :       break;
     805             :     }
     806             :   }
     807    33116331 : }
     808             : 
     809             : }  // namespace
     810             : 
     811    33116128 : void EscapeAnalysis::Reduce(Node* node, Reduction* reduction) {
     812             :   const Operator* op = node->op();
     813             :   TRACE("Reducing %s#%d\n", op->mnemonic(), node->id());
     814             : 
     815    66232244 :   EscapeAnalysisTracker::Scope current(this, tracker_, node, reduction);
     816    33115877 :   ReduceNode(op, &current, jsgraph());
     817    33116066 : }
     818             : 
     819      464079 : EscapeAnalysis::EscapeAnalysis(JSGraph* jsgraph, Zone* zone)
     820             :     : EffectGraphReducer(
     821             :           jsgraph->graph(),
     822    33115920 :           [this](Node* node, Reduction* reduction) { Reduce(node, reduction); },
     823             :           zone),
     824      464079 :       tracker_(new (zone) EscapeAnalysisTracker(jsgraph, this, zone)),
     825     1392240 :       jsgraph_(jsgraph) {}
     826             : 
     827    30875931 : Node* EscapeAnalysisResult::GetReplacementOf(Node* node) {
     828    30875931 :   Node* replacement = tracker_->GetReplacementOf(node);
     829             :   // Replacements cannot have replacements. This is important to ensure
     830             :   // re-visitation: If a replacement is replaced, then all nodes accessing
     831             :   // the replacement have to be updated.
     832             :   if (replacement) DCHECK_NULL(tracker_->GetReplacementOf(replacement));
     833    30876018 :   return replacement;
     834             : }
     835             : 
     836      678951 : Node* EscapeAnalysisResult::GetVirtualObjectField(const VirtualObject* vobject,
     837             :                                                   int field, Node* effect) {
     838     2036853 :   return tracker_->variable_states_.Get(vobject->FieldAt(field).FromJust(),
     839     1357902 :                                         effect);
     840             : }
     841             : 
     842    53273677 : const VirtualObject* EscapeAnalysisResult::GetVirtualObject(Node* node) {
     843   106547400 :   return tracker_->virtual_objects_.Get(node);
     844             : }
     845             : 
     846      111675 : VirtualObject::VirtualObject(VariableTracker* var_states, VirtualObject::Id id,
     847             :                              int size)
     848      111675 :     : Dependable(var_states->zone()), id_(id), fields_(var_states->zone()) {
     849             :   DCHECK(IsAligned(size, kTaggedSize));
     850             :   TRACE("Creating VirtualObject id:%d size:%d\n", id, size);
     851      111675 :   int num_fields = size / kTaggedSize;
     852      111675 :   fields_.reserve(num_fields);
     853     1638749 :   for (int i = 0; i < num_fields; ++i) {
     854     1527074 :     fields_.push_back(var_states->NewVariable());
     855             :   }
     856      111675 : }
     857             : 
     858             : #undef TRACE
     859             : 
     860             : }  // namespace compiler
     861             : }  // namespace internal
     862      121996 : }  // namespace v8

Generated by: LCOV version 1.10